diff options
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r-- | drivers/iio/adc/ad7949.c | 33 | ||||
-rw-r--r-- | drivers/iio/adc/ad_sigma_delta.c | 3 | ||||
-rw-r--r-- | drivers/iio/adc/ingenic-adc.c | 149 | ||||
-rw-r--r-- | drivers/iio/adc/sc27xx_adc.c | 16 |
4 files changed, 136 insertions, 65 deletions
diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index ac0ffff6c5ae..5c2b3446fa4a 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -54,38 +54,20 @@ struct ad7949_adc_chip { u8 resolution; u16 cfg; unsigned int current_channel; - u32 buffer ____cacheline_aligned; + u16 buffer ____cacheline_aligned; }; -static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc) -{ - if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK)) - return true; - - return false; -} - -static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc) -{ - int ret = ad7949_adc->resolution; - - if (ad7949_spi_cfg_is_read_back(ad7949_adc)) - ret += AD7949_CFG_REG_SIZE_BITS; - - return ret; -} - static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, u16 mask) { int ret; - int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int bits_per_word = ad7949_adc->resolution; int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; struct spi_message msg; struct spi_transfer tx[] = { { .tx_buf = &ad7949_adc->buffer, - .len = 4, + .len = 2, .bits_per_word = bits_per_word, }, }; @@ -107,13 +89,13 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, unsigned int channel) { int ret; - int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int bits_per_word = ad7949_adc->resolution; int mask = GENMASK(ad7949_adc->resolution, 0); struct spi_message msg; struct spi_transfer tx[] = { { .rx_buf = &ad7949_adc->buffer, - .len = 4, + .len = 2, .bits_per_word = bits_per_word, }, }; @@ -138,10 +120,7 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, ad7949_adc->current_channel = channel; - if (ad7949_spi_cfg_is_read_back(ad7949_adc)) - *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask; - else - *val = ad7949_adc->buffer & mask; + *val = ad7949_adc->buffer & mask; return 0; } diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 2640b75fb774..8ba90486c787 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -205,7 +205,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, } EXPORT_SYMBOL_GPL(ad_sd_reset); -static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, +int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { int ret; @@ -242,6 +242,7 @@ out: return ret; } +EXPORT_SYMBOL_GPL(ad_sd_calibrate); /** * ad_sd_calibrate_all() - Performs channel calibration 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); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index a6c046575ec3..66b387f9b36d 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -477,13 +477,6 @@ static void sc27xx_adc_disable(void *_data) SC27XX_MODULE_ADC_EN, 0); } -static void sc27xx_adc_free_hwlock(void *_data) -{ - struct hwspinlock *hwlock = _data; - - hwspin_lock_free(hwlock); -} - static int sc27xx_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -520,19 +513,12 @@ static int sc27xx_adc_probe(struct platform_device *pdev) return ret; } - sc27xx_data->hwlock = hwspin_lock_request_specific(ret); + sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); if (!sc27xx_data->hwlock) { dev_err(dev, "failed to request hwspinlock\n"); return -ENXIO; } - ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock, - sc27xx_data->hwlock); - if (ret) { - dev_err(dev, "failed to add hwspinlock action\n"); - return ret; - } - sc27xx_data->dev = dev; ret = sc27xx_adc_enable(sc27xx_data); |