diff options
Diffstat (limited to 'drivers/staging/iio/adc/ad7192.c')
-rw-r--r-- | drivers/staging/iio/adc/ad7192.c | 244 |
1 files changed, 152 insertions, 92 deletions
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index acdbc07fd259..b6d12fe7c12a 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -1,12 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* * AD7190 AD7192 AD7193 AD7195 SPI ADC driver * * Copyright 2011-2015 Analog Devices Inc. - * - * Licensed under the GPL-2. */ #include <linux/interrupt.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -109,10 +109,10 @@ #define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */ #define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */ -#define AD7193_CH_AIN1P_AIN2M 0x000 /* AIN1(+) - AIN2(-) */ -#define AD7193_CH_AIN3P_AIN4M 0x001 /* AIN3(+) - AIN4(-) */ -#define AD7193_CH_AIN5P_AIN6M 0x002 /* AIN5(+) - AIN6(-) */ -#define AD7193_CH_AIN7P_AIN8M 0x004 /* AIN7(+) - AIN8(-) */ +#define AD7193_CH_AIN1P_AIN2M 0x001 /* AIN1(+) - AIN2(-) */ +#define AD7193_CH_AIN3P_AIN4M 0x002 /* AIN3(+) - AIN4(-) */ +#define AD7193_CH_AIN5P_AIN6M 0x004 /* AIN5(+) - AIN6(-) */ +#define AD7193_CH_AIN7P_AIN8M 0x008 /* AIN7(+) - AIN8(-) */ #define AD7193_CH_TEMP 0x100 /* Temp senseor */ #define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */ #define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */ @@ -156,14 +156,16 @@ struct ad7192_state { struct regulator *avdd; struct regulator *dvdd; + struct clk *mclk; u16 int_vref_mv; - u32 mclk; + u32 fclk; u32 f_order; u32 mode; u32 conf; u32 scale_avail[8][2]; u8 gpocon; u8 devid; + u8 clock_sel; struct mutex lock; /* protect sensor state */ struct ad_sigma_delta sd; @@ -216,8 +218,8 @@ static const struct ad_sd_calib_data ad7192_calib_arr[8] = { static int ad7192_calibrate_all(struct ad7192_state *st) { - return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr, - ARRAY_SIZE(ad7192_calib_arr)); + return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr, + ARRAY_SIZE(ad7192_calib_arr)); } static inline bool ad7192_valid_external_frequency(u32 freq) @@ -226,23 +228,45 @@ static inline bool ad7192_valid_external_frequency(u32 freq) freq <= AD7192_EXT_FREQ_MHZ_MAX); } -static int ad7192_setup(struct ad7192_state *st, - const struct ad7192_platform_data *pdata) +static int ad7192_of_clock_select(struct ad7192_state *st) +{ + struct device_node *np = st->sd.spi->dev.of_node; + unsigned int clock_sel; + + clock_sel = AD7192_CLK_INT; + + /* use internal clock */ + if (PTR_ERR(st->mclk) == -ENOENT) { + if (of_property_read_bool(np, "adi,int-clock-output-enable")) + clock_sel = AD7192_CLK_INT_CO; + } else { + if (of_property_read_bool(np, "adi,clock-xtal")) + clock_sel = AD7192_CLK_EXT_MCLK1_2; + else + clock_sel = AD7192_CLK_EXT_MCLK2; + } + + return clock_sel; +} + +static int ad7192_setup(struct ad7192_state *st, struct device_node *np) { struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi); + bool rej60_en, sinc3_en, refin2_en, chop_en; + bool buf_en, bipolar, burnout_curr_en; unsigned long long scale_uv; int i, ret, id; /* reset the serial interface */ ret = ad_sd_reset(&st->sd, 48); if (ret < 0) - goto out; + return ret; usleep_range(500, 1000); /* Wait for at least 500us */ /* write/read test for device presence */ ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id); if (ret) - goto out; + return ret; id &= AD7192_ID_MASK; @@ -250,44 +274,28 @@ static int ad7192_setup(struct ad7192_state *st, dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n", id); - switch (pdata->clock_source_sel) { - case AD7192_CLK_INT: - case AD7192_CLK_INT_CO: - st->mclk = AD7192_INT_FREQ_MHZ; - break; - case AD7192_CLK_EXT_MCLK1_2: - case AD7192_CLK_EXT_MCLK2: - if (ad7192_valid_external_frequency(pdata->ext_clk_hz)) { - st->mclk = pdata->ext_clk_hz; - break; - } - dev_err(&st->sd.spi->dev, "Invalid frequency setting %u\n", - pdata->ext_clk_hz); - ret = -EINVAL; - goto out; - default: - ret = -EINVAL; - goto out; - } - st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) | - AD7192_MODE_CLKSRC(pdata->clock_source_sel) | + AD7192_MODE_CLKSRC(st->clock_sel) | AD7192_MODE_RATE(480); st->conf = AD7192_CONF_GAIN(0); - if (pdata->rej60_en) + rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable"); + if (rej60_en) st->mode |= AD7192_MODE_REJ60; - if (pdata->sinc3_en) + sinc3_en = of_property_read_bool(np, "adi,sinc3-filter-enable"); + if (sinc3_en) st->mode |= AD7192_MODE_SINC3; - if (pdata->refin2_en && st->devid != ID_AD7195) + refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable"); + if (refin2_en && st->devid != ID_AD7195) st->conf |= AD7192_CONF_REFSEL; - if (pdata->chop_en) { + chop_en = of_property_read_bool(np, "adi,chop-enable"); + if (chop_en) { st->conf |= AD7192_CONF_CHOP; - if (pdata->sinc3_en) + if (sinc3_en) st->f_order = 3; /* SINC 3rd order */ else st->f_order = 4; /* SINC 4th order */ @@ -295,30 +303,34 @@ static int ad7192_setup(struct ad7192_state *st, st->f_order = 1; } - if (pdata->buf_en) + buf_en = of_property_read_bool(np, "adi,buffer-enable"); + if (buf_en) st->conf |= AD7192_CONF_BUF; - if (pdata->unipolar_en) + bipolar = of_property_read_bool(np, "bipolar"); + if (!bipolar) st->conf |= AD7192_CONF_UNIPOLAR; - if (pdata->burnout_curr_en && pdata->buf_en && !pdata->chop_en) { + burnout_curr_en = of_property_read_bool(np, + "adi,burnout-currents-enable"); + if (burnout_curr_en && buf_en && !chop_en) { st->conf |= AD7192_CONF_BURN; - } else if (pdata->burnout_curr_en) { + } else if (burnout_curr_en) { dev_warn(&st->sd.spi->dev, "Can't enable burnout currents: see CHOP or buffer\n"); } ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); if (ret) - goto out; + return ret; ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); if (ret) - goto out; + return ret; ret = ad7192_calibrate_all(st); if (ret) - goto out; + return ret; /* Populate available ADC input ranges */ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { @@ -332,35 +344,8 @@ static int ad7192_setup(struct ad7192_state *st, } return 0; -out: - dev_err(&st->sd.spi->dev, "setup failed\n"); - return ret; } -static ssize_t -ad7192_show_scale_available(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7192_state *st = iio_priv(indio_dev); - int i, len = 0; - - for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) - len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0], - st->scale_avail[i][1]); - - len += sprintf(buf + len, "\n"); - - return len; -} - -static IIO_DEVICE_ATTR_NAMED(in_v_m_v_scale_available, - in_voltage-voltage_scale_available, - 0444, ad7192_show_scale_available, NULL, 0); - -static IIO_DEVICE_ATTR(in_voltage_scale_available, 0444, - ad7192_show_scale_available, NULL, 0); - static ssize_t ad7192_show_ac_excitation(struct device *dev, struct device_attribute *attr, char *buf) @@ -435,8 +420,6 @@ static IIO_DEVICE_ATTR(ac_excitation_en, 0644, AD7192_REG_MODE); static struct attribute *ad7192_attributes[] = { - &iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, &iio_dev_attr_bridge_switch_en.dev_attr.attr, &iio_dev_attr_ac_excitation_en.dev_attr.attr, NULL @@ -447,8 +430,6 @@ static const struct attribute_group ad7192_attribute_group = { }; static struct attribute *ad7195_attributes[] = { - &iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr, - &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, &iio_dev_attr_bridge_switch_en.dev_attr.attr, NULL }; @@ -499,7 +480,7 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, *val -= 273 * ad7192_get_temp_scale(unipolar); return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: - *val = st->mclk / + *val = st->fclk / (st->f_order * 1024 * AD7192_MODE_RATE(st->mode)); return IIO_VAL_INT; } @@ -546,7 +527,7 @@ static int ad7192_write_raw(struct iio_dev *indio_dev, break; } - div = st->mclk / (val * st->f_order * 1024); + div = st->fclk / (val * st->f_order * 1024); if (div < 1 || div > 1023) { ret = -EINVAL; break; @@ -579,10 +560,31 @@ static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev, } } +static int ad7192_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(st->scale_avail) * 2; + + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + static const struct iio_info ad7192_info = { .read_raw = ad7192_read_raw, .write_raw = ad7192_write_raw, .write_raw_get_fmt = ad7192_write_raw_get_fmt, + .read_avail = ad7192_read_avail, .attrs = &ad7192_attribute_group, .validate_trigger = ad_sd_validate_trigger, }; @@ -591,6 +593,7 @@ static const struct iio_info ad7195_info = { .read_raw = ad7192_read_raw, .write_raw = ad7192_write_raw, .write_raw_get_fmt = ad7192_write_raw_get_fmt, + .read_avail = ad7192_read_avail, .attrs = &ad7195_attribute_group, .validate_trigger = ad_sd_validate_trigger, }; @@ -625,6 +628,42 @@ static const struct iio_chan_spec ad7193_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(14), }; +static int ad7192_channels_config(struct iio_dev *indio_dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + const struct iio_chan_spec *channels; + struct iio_chan_spec *chan; + int i; + + switch (st->devid) { + case ID_AD7193: + channels = ad7193_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7193_channels); + break; + default: + channels = ad7192_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7192_channels); + break; + } + + chan = devm_kcalloc(indio_dev->dev.parent, indio_dev->num_channels, + sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + indio_dev->channels = chan; + + for (i = 0; i < indio_dev->num_channels; i++) { + *chan = channels[i]; + if (chan->type != IIO_TEMP) + chan->info_mask_shared_by_type_available |= + BIT(IIO_CHAN_INFO_SCALE); + chan++; + } + + return 0; +} + static int ad7192_probe(struct spi_device *spi) { const struct ad7192_platform_data *pdata = dev_get_platdata(&spi->dev); @@ -687,16 +726,9 @@ static int ad7192_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; - switch (st->devid) { - case ID_AD7193: - indio_dev->channels = ad7193_channels; - indio_dev->num_channels = ARRAY_SIZE(ad7193_channels); - break; - default: - indio_dev->channels = ad7192_channels; - indio_dev->num_channels = ARRAY_SIZE(ad7192_channels); - break; - } + ret = ad7192_channels_config(indio_dev); + if (ret < 0) + goto error_disable_dvdd; if (st->devid == ID_AD7195) indio_dev->info = &ad7195_info; @@ -709,15 +741,42 @@ static int ad7192_probe(struct spi_device *spi) if (ret) goto error_disable_dvdd; - ret = ad7192_setup(st, pdata); - if (ret) + st->fclk = AD7192_INT_FREQ_MHZ; + + st->mclk = devm_clk_get(&st->sd.spi->dev, "mclk"); + if (IS_ERR(st->mclk) && PTR_ERR(st->mclk) != -ENOENT) { + ret = PTR_ERR(st->mclk); goto error_remove_trigger; + } + + st->clock_sel = ad7192_of_clock_select(st); + + if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || + st->clock_sel == AD7192_CLK_EXT_MCLK2) { + ret = clk_prepare_enable(st->mclk); + if (ret < 0) + goto error_remove_trigger; + + st->fclk = clk_get_rate(st->mclk); + if (!ad7192_valid_external_frequency(st->fclk)) { + ret = -EINVAL; + dev_err(&spi->dev, + "External clock frequency out of bounds\n"); + goto error_disable_clk; + } + } + + ret = ad7192_setup(st, spi->dev.of_node); + if (ret) + goto error_disable_clk; ret = iio_device_register(indio_dev); if (ret < 0) - goto error_remove_trigger; + goto error_disable_clk; return 0; +error_disable_clk: + clk_disable_unprepare(st->mclk); error_remove_trigger: ad_sd_cleanup_buffer_and_trigger(indio_dev); error_disable_dvdd: @@ -734,6 +793,7 @@ static int ad7192_remove(struct spi_device *spi) struct ad7192_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); + clk_disable_unprepare(st->mclk); ad_sd_cleanup_buffer_and_trigger(indio_dev); regulator_disable(st->dvdd); |