static int aspeed_adc_probe()

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;
}