Re: [PATCH v2 3/3] IIO: Ingenic JZ47xx: Add support for JZ4770 SoC ADC.

From: Jonathan Cameron
Date: Sun Sep 08 2019 - 08:25:27 EST


On Sat, 27 Jul 2019 21:59:40 +0200
Artur Rojek <contact@xxxxxxxxxxxxxx> wrote:

> Add support for the ADC hardware present on Ingenic JZ4770 SoC.
>
> Signed-off-by: Artur Rojek <contact@xxxxxxxxxxxxxx>
> Tested-by: Paul Cercueil <paul@xxxxxxxxxxxxxxx>

Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to play with it.

Thanks,

Jonathan

> ---
>
> Changes:
>
> v2: - move the IIO_CHAN_INFO_RAW block into a utility function,
> - unconditionally lock aux_lock for reduced complexity
>
> drivers/iio/adc/ingenic-adc.c | 149 +++++++++++++++++++++++++++++-----
> 1 file changed, 127 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
> index e234970b7150..7a53c2f8d438 100644
> --- a/drivers/iio/adc/ingenic-adc.c
> +++ b/drivers/iio/adc/ingenic-adc.c
> @@ -25,9 +25,13 @@
> #define JZ_ADC_REG_ADSDAT 0x20
> #define JZ_ADC_REG_ADCLK 0x28
>
> +#define JZ_ADC_REG_ENABLE_PD BIT(7)
> +#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
> #define JZ_ADC_REG_CFG_BAT_MD BIT(4)
> #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
> -#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB 16
> +#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
> +#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
> +#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16
>
> #define JZ_ADC_AUX_VREF 3300
> #define JZ_ADC_AUX_VREF_BITS 12
> @@ -37,6 +41,8 @@
> #define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
> #define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
> #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
> +#define JZ4770_ADC_BATTERY_VREF 6600
> +#define JZ4770_ADC_BATTERY_VREF_BITS 12
>
> struct ingenic_adc;
>
> @@ -47,6 +53,8 @@ struct ingenic_adc_soc_data {
> size_t battery_raw_avail_size;
> const int *battery_scale_avail;
> size_t battery_scale_avail_size;
> + unsigned int battery_vref_mode: 1;
> + unsigned int has_aux2: 1;
> int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
> };
>
> @@ -54,6 +62,7 @@ struct ingenic_adc {
> void __iomem *base;
> struct clk *clk;
> struct mutex lock;
> + struct mutex aux_lock;
> const struct ingenic_adc_soc_data *soc_data;
> bool low_vref_mode;
> };
> @@ -120,6 +129,8 @@ static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
> case IIO_CHAN_INFO_SCALE:
> switch (chan->channel) {
> case INGENIC_ADC_BATTERY:
> + if (!adc->soc_data->battery_vref_mode)
> + return -EINVAL;
> if (val > JZ_ADC_BATTERY_LOW_VREF) {
> ingenic_adc_set_config(adc,
> JZ_ADC_REG_CFG_BAT_MD,
> @@ -158,6 +169,14 @@ static const int jz4740_adc_battery_scale_avail[] = {
> JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
> };
>
> +static const int jz4770_adc_battery_raw_avail[] = {
> + 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1,
> +};
> +
> +static const int jz4770_adc_battery_scale_avail[] = {
> + JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS,
> +};
> +
> static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
> {
> struct clk *parent_clk;
> @@ -187,7 +206,45 @@ static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
> /* We also need a divider that produces a 10us clock. */
> div_10us = DIV_ROUND_UP(rate, 100000);
>
> - writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) |
> + writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) |
> + (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
> + adc->base + JZ_ADC_REG_ADCLK);
> +
> + return 0;
> +}
> +
> +static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
> +{
> + struct clk *parent_clk;
> + unsigned long parent_rate, rate;
> + unsigned int div_main, div_ms, div_10us;
> +
> + parent_clk = clk_get_parent(adc->clk);
> + if (!parent_clk) {
> + dev_err(dev, "ADC clock has no parent\n");
> + return -ENODEV;
> + }
> + parent_rate = clk_get_rate(parent_clk);
> +
> + /*
> + * The JZ4770 ADC works at 20 kHz to 200 kHz.
> + * We pick the highest rate possible.
> + */
> + div_main = DIV_ROUND_UP(parent_rate, 200000);
> + div_main = clamp(div_main, 1u, 256u);
> + rate = parent_rate / div_main;
> + if (rate < 20000 || rate > 200000) {
> + dev_err(dev, "No valid divider for ADC main clock\n");
> + return -EINVAL;
> + }
> +
> + /* We also need a divider that produces a 10us clock. */
> + div_10us = DIV_ROUND_UP(rate, 10000);
> + /* And another, which produces a 1ms clock. */
> + div_ms = DIV_ROUND_UP(rate, 1000);
> +
> + writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) |
> + ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) |
> (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
> adc->base + JZ_ADC_REG_ADCLK);
>
> @@ -201,6 +258,8 @@ static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
> .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
> .battery_scale_avail = jz4725b_adc_battery_scale_avail,
> .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
> + .battery_vref_mode = true,
> + .has_aux2 = false,
> .init_clk_div = jz4725b_adc_init_clk_div,
> };
>
> @@ -211,9 +270,23 @@ static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
> .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
> .battery_scale_avail = jz4740_adc_battery_scale_avail,
> .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
> + .battery_vref_mode = true,
> + .has_aux2 = false,
> .init_clk_div = NULL, /* no ADCLK register on JZ4740 */
> };
>
> +static const struct ingenic_adc_soc_data jz4770_adc_soc_data = {
> + .battery_high_vref = JZ4770_ADC_BATTERY_VREF,
> + .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS,
> + .battery_raw_avail = jz4770_adc_battery_raw_avail,
> + .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail),
> + .battery_scale_avail = jz4770_adc_battery_scale_avail,
> + .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail),
> + .battery_vref_mode = false,
> + .has_aux2 = true,
> + .init_clk_div = jz4770_adc_init_clk_div,
> +};
> +
> static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
> struct iio_chan_spec const *chan,
> const int **vals,
> @@ -239,6 +312,42 @@ static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
> };
> }
>
> +static int ingenic_adc_read_chan_info_raw(struct ingenic_adc *adc,
> + struct iio_chan_spec const *chan,
> + int *val)
> +{
> + int bit, ret, engine = (chan->channel == INGENIC_ADC_BATTERY);
> +
> + /* We cannot sample AUX/AUX2 in parallel. */
> + mutex_lock(&adc->aux_lock);
> + if (adc->soc_data->has_aux2 && engine == 0) {
> + bit = BIT(chan->channel == INGENIC_ADC_AUX2);
> + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, bit);
> + }
> +
> + clk_enable(adc->clk);
> + ret = ingenic_adc_capture(adc, engine);
> + if (ret)
> + goto out;
> +
> + switch (chan->channel) {
> + case INGENIC_ADC_AUX:
> + case INGENIC_ADC_AUX2:
> + *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
> + break;
> + case INGENIC_ADC_BATTERY:
> + *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
> + break;
> + }
> +
> + ret = IIO_VAL_INT;
> +out:
> + clk_disable(adc->clk);
> + mutex_unlock(&adc->aux_lock);
> +
> + return ret;
> +}
> +
> static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
> struct iio_chan_spec const *chan,
> int *val,
> @@ -246,32 +355,14 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
> long m)
> {
> struct ingenic_adc *adc = iio_priv(iio_dev);
> - int ret;
>
> switch (m) {
> case IIO_CHAN_INFO_RAW:
> - clk_enable(adc->clk);
> - ret = ingenic_adc_capture(adc, chan->channel);
> - if (ret) {
> - clk_disable(adc->clk);
> - return ret;
> - }
> -
> - switch (chan->channel) {
> - case INGENIC_ADC_AUX:
> - *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
> - break;
> - case INGENIC_ADC_BATTERY:
> - *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
> - break;
> - }
> -
> - clk_disable(adc->clk);
> -
> - return IIO_VAL_INT;
> + return ingenic_adc_read_chan_info_raw(adc, chan, val);
> case IIO_CHAN_INFO_SCALE:
> switch (chan->channel) {
> case INGENIC_ADC_AUX:
> + case INGENIC_ADC_AUX2:
> *val = JZ_ADC_AUX_VREF;
> *val2 = JZ_ADC_AUX_VREF_BITS;
> break;
> @@ -322,6 +413,14 @@ static const struct iio_chan_spec ingenic_channels[] = {
> .indexed = 1,
> .channel = INGENIC_ADC_BATTERY,
> },
> + { /* Must always be last in the array. */
> + .extend_name = "aux2",
> + .type = IIO_VOLTAGE,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE),
> + .indexed = 1,
> + .channel = INGENIC_ADC_AUX2,
> + },
> };
>
> static int ingenic_adc_probe(struct platform_device *pdev)
> @@ -343,6 +442,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
>
> adc = iio_priv(iio_dev);
> mutex_init(&adc->lock);
> + mutex_init(&adc->aux_lock);
> adc->soc_data = soc_data;
>
> mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> @@ -374,6 +474,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
> /* Put hardware in a known passive state. */
> writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
> writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
> + usleep_range(2000, 3000); /* Must wait at least 2ms. */
> clk_disable(adc->clk);
>
> ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
> @@ -387,6 +488,9 @@ static int ingenic_adc_probe(struct platform_device *pdev)
> iio_dev->modes = INDIO_DIRECT_MODE;
> iio_dev->channels = ingenic_channels;
> iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
> + /* Remove AUX2 from the list of supported channels. */
> + if (!adc->soc_data->has_aux2)
> + iio_dev->num_channels -= 1;
> iio_dev->info = &ingenic_adc_info;
>
> ret = devm_iio_device_register(dev, iio_dev);
> @@ -400,6 +504,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
> static const struct of_device_id ingenic_adc_of_match[] = {
> { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
> { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
> + { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, },
> { },
> };
> MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);