in adc/aspeed_adc.c [480:644]
static int aspeed_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct aspeed_adc_data *data;
int ret;
u32 adc_engine_control_reg_val;
unsigned long scaler_flags = 0;
char clk_name[32], clk_parent_name[32];
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = &pdev->dev;
data->model_data = of_device_get_match_data(&pdev->dev);
platform_set_drvdata(pdev, indio_dev);
data->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
/* Register ADC clock prescaler with source specified by device tree. */
spin_lock_init(&data->clk_lock);
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
of_clk_get_parent_name(pdev->dev.of_node, 0));
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
data->model_data->model_name);
data->fixed_div_clk = clk_hw_register_fixed_factor(
&pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
if (IS_ERR(data->fixed_div_clk))
return PTR_ERR(data->fixed_div_clk);
ret = devm_add_action_or_reset(data->dev,
aspeed_adc_unregister_fixed_divider,
data->fixed_div_clk);
if (ret)
return ret;
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name);
if (data->model_data->need_prescaler) {
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
data->model_data->model_name);
data->clk_prescaler = devm_clk_hw_register_divider(
&pdev->dev, clk_name, clk_parent_name, 0,
data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
&data->clk_lock);
if (IS_ERR(data->clk_prescaler))
return PTR_ERR(data->clk_prescaler);
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name),
clk_name);
scaler_flags = CLK_SET_RATE_PARENT;
}
/*
* Register ADC clock scaler downstream from the prescaler. Allow rate
* setting to adjust the prescaler as well.
*/
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
data->model_data->model_name);
data->clk_scaler = devm_clk_hw_register_divider(
&pdev->dev, clk_name, clk_parent_name, scaler_flags,
data->base + ASPEED_REG_CLOCK_CONTROL, 0,
data->model_data->scaler_bit_width, 0, &data->clk_lock);
if (IS_ERR(data->clk_scaler))
return PTR_ERR(data->clk_scaler);
data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
if (IS_ERR(data->rst)) {
dev_err(&pdev->dev,
"invalid or missing reset controller device tree entry");
return PTR_ERR(data->rst);
}
reset_control_deassert(data->rst);
ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
data->rst);
if (ret)
return ret;
ret = aspeed_adc_vref_config(indio_dev);
if (ret)
return ret;
if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid",
NULL)) {
ret = aspeed_adc_set_trim_data(indio_dev);
if (ret)
return ret;
}
if (of_find_property(data->dev->of_node, "aspeed,battery-sensing",
NULL)) {
if (data->model_data->bat_sense_sup) {
data->battery_sensing = 1;
if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
ASPEED_ADC_BAT_SENSING_DIV) {
data->battery_mode_gain.mult = 3;
data->battery_mode_gain.div = 1;
} else {
data->battery_mode_gain.mult = 3;
data->battery_mode_gain.div = 2;
}
} else
dev_warn(&pdev->dev,
"Failed to enable battery-sensing mode\n");
}
ret = clk_prepare_enable(data->clk_scaler->clk);
if (ret)
return ret;
ret = devm_add_action_or_reset(data->dev,
aspeed_adc_clk_disable_unprepare,
data->clk_scaler->clk);
if (ret)
return ret;
ret = aspeed_adc_set_sampling_rate(indio_dev,
ASPEED_ADC_DEF_SAMPLING_RATE);
if (ret)
return ret;
adc_engine_control_reg_val =
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
adc_engine_control_reg_val |=
FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
ASPEED_ADC_ENGINE_ENABLE;
/* Enable engine in normal mode. */
writel(adc_engine_control_reg_val,
data->base + ASPEED_REG_ENGINE_CONTROL);
ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
data);
if (ret)
return ret;
if (data->model_data->wait_init_sequence) {
/* Wait for initial sequence complete. */
ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL,
adc_engine_control_reg_val,
adc_engine_control_reg_val &
ASPEED_ADC_CTRL_INIT_RDY,
ASPEED_ADC_INIT_POLLING_TIME,
ASPEED_ADC_INIT_TIMEOUT);
if (ret)
return ret;
}
aspeed_adc_compensation(indio_dev);
/* Start all channels in normal mode. */
adc_engine_control_reg_val =
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
writel(adc_engine_control_reg_val,
data->base + ASPEED_REG_ENGINE_CONTROL);
indio_dev->name = data->model_data->model_name;
indio_dev->info = &aspeed_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = data->battery_sensing ?
aspeed_adc_iio_bat_channels :
aspeed_adc_iio_channels;
indio_dev->num_channels = data->model_data->num_channels;
ret = devm_iio_device_register(data->dev, indio_dev);
return ret;
}