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