From 8eebe6281ac1062764db23d181e3feb3305a3690 Mon Sep 17 00:00:00 2001 From: André Gustavo Nakagomi Lopez Date: Mon, 25 Oct 2021 09:19:50 -0300 Subject: iio: adc: lpc18xx_adc: Reorder clk_get_rate() function call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clk_get_rate() is not guaranteed to work if called before clk_prepare_enable(). Reorder clk_get_rate(), so it's called after clk_prepare_enable() and after devm_add_action_or_reset() of lpc18xx_clk_disable(). Not that this is not a problem on this particular device, but it is good to remove a case that might get copied elsewhere. Suggested-by: Jonathan Cameron Acked-by: Vladimir Zapolskiy Signed-off-by: André Gustavo Nakagomi Lopez Link: https://lore.kernel.org/r/YXag5l4xBkGQH3tq@Andryuu.br Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc18xx_adc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index ceefa4d793cf..ae9c9384f23e 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -157,9 +157,6 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk), "error getting clock\n"); - rate = clk_get_rate(adc->clk); - clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); - adc->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(adc->vref)) return dev_err_probe(&pdev->dev, PTR_ERR(adc->vref), @@ -192,6 +189,9 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) if (ret) return ret; + rate = clk_get_rate(adc->clk); + clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); + adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) | LPC18XX_ADC_CR_PDN; writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR); -- cgit From e12653eb77b90fc33ff3e0b9caf21b02b026f552 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 25 Oct 2021 21:50:07 +0200 Subject: iio: accel: mma7660: Warn about failure to put device in stand-by in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whan an i2c driver's remove function returns a non-zero error code nothing happens apart from emitting a generic error message. Make this error message more device specific and return zero instead. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211025195007.84541-1-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma7660.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index cd6cdf2c51b0..24b83ccdb950 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -210,10 +210,16 @@ static int mma7660_probe(struct i2c_client *client, static int mma7660_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + int ret; iio_device_unregister(indio_dev); - return mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); + ret = mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); + if (ret) + dev_warn(&client->dev, "Failed to put device in stand-by mode (%pe), ignoring\n", + ERR_PTR(ret)); + + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit From fb45c7a31ec1f772502867ea87a2315b57a9f439 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 21 Oct 2021 14:59:50 +0200 Subject: iio: xilinx-xadc: Make IRQ optional In some setups the IRQ signal of the XADC might not be wired to the host system. The driver currently requires that an interrupt is specified. Make the interrupt optional so the driver can be used in such setups where the interrupt is not connected. Since both the internal triggers as well as events depend on the interrupt being connected both are not available when the interrupt is not connected. Buffered access is still supported even without an interrupt since an external trigger can be used. The IRQ is only optional when using the AXI interface, since the PCAP interface needs the IRQ for reading and writing registers. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211021125950.28707-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 62 ++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 22 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 83bea5ef765d..2aa4278ecba7 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -107,6 +107,7 @@ static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500; #define XADC_AXI_INT_ALARM_MASK 0x3c0f #define XADC_FLAGS_BUFFERED BIT(0) +#define XADC_FLAGS_IRQ_OPTIONAL BIT(1) /* * The XADC hardware supports a samplerate of up to 1MSPS. Unfortunately it does @@ -562,7 +563,7 @@ static const struct xadc_ops xadc_7s_axi_ops = { .get_dclk_rate = xadc_axi_get_dclk, .update_alarm = xadc_axi_update_alarm, .interrupt_handler = xadc_axi_interrupt_handler, - .flags = XADC_FLAGS_BUFFERED, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, .type = XADC_TYPE_S7, }; @@ -573,7 +574,7 @@ static const struct xadc_ops xadc_us_axi_ops = { .get_dclk_rate = xadc_axi_get_dclk, .update_alarm = xadc_axi_update_alarm, .interrupt_handler = xadc_axi_interrupt_handler, - .flags = XADC_FLAGS_BUFFERED, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, .type = XADC_TYPE_US, }; @@ -1182,7 +1183,7 @@ static const struct of_device_id xadc_of_match_table[] = { MODULE_DEVICE_TABLE(of, xadc_of_match_table); static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, - unsigned int *conf) + unsigned int *conf, int irq) { struct device *dev = indio_dev->dev.parent; struct xadc *xadc = iio_priv(indio_dev); @@ -1195,6 +1196,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, u32 ext_mux_chan; u32 reg; int ret; + int i; *conf = 0; @@ -1273,6 +1275,14 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, } of_node_put(chan_node); + /* No IRQ => no events */ + if (irq <= 0) { + for (i = 0; i < num_channels; i++) { + channels[i].event_spec = NULL; + channels[i].num_event_specs = 0; + } + } + indio_dev->num_channels = num_channels; indio_dev->channels = devm_krealloc(dev, channels, sizeof(*channels) * num_channels, @@ -1307,6 +1317,7 @@ static int xadc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct of_device_id *id; + const struct xadc_ops *ops; struct iio_dev *indio_dev; unsigned int bipolar_mask; unsigned int conf0; @@ -1322,9 +1333,12 @@ static int xadc_probe(struct platform_device *pdev) if (!id) return -EINVAL; - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -ENXIO; + ops = id->data; + + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && + (irq != -ENXIO || !(ops->flags & XADC_FLAGS_IRQ_OPTIONAL))) + return irq; indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc)); if (!indio_dev) @@ -1345,7 +1359,7 @@ static int xadc_probe(struct platform_device *pdev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &xadc_info; - ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0); + ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0, irq); if (ret) return ret; @@ -1357,14 +1371,16 @@ static int xadc_probe(struct platform_device *pdev) if (ret) return ret; - xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); - if (IS_ERR(xadc->convst_trigger)) - return PTR_ERR(xadc->convst_trigger); + if (irq > 0) { + xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); + if (IS_ERR(xadc->convst_trigger)) + return PTR_ERR(xadc->convst_trigger); - xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, - "samplerate"); - if (IS_ERR(xadc->samplerate_trigger)) - return PTR_ERR(xadc->samplerate_trigger); + xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, + "samplerate"); + if (IS_ERR(xadc->samplerate_trigger)) + return PTR_ERR(xadc->samplerate_trigger); + } } xadc->clk = devm_clk_get(dev, NULL); @@ -1396,15 +1412,17 @@ static int xadc_probe(struct platform_device *pdev) } } - ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, 0, - dev_name(dev), indio_dev); - if (ret) - return ret; + if (irq > 0) { + ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, + 0, dev_name(dev), indio_dev); + if (ret) + return ret; - ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, - &xadc->zynq_unmask_work); - if (ret) - return ret; + ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, + &xadc->zynq_unmask_work); + if (ret) + return ret; + } ret = xadc->ops->setup(pdev, indio_dev, irq); if (ret) -- cgit From 8cf524be72fa8205754ed0ddc11a32aaf156c39f Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Thu, 21 Oct 2021 08:18:23 -0400 Subject: iio: adc: stm32-adc: Fix of_node_put() issue in stm32-adc Fix following coccicheck warning: ./drivers/iio/adc/stm32-adc.c:2014:1-33: WARNING: Function for_each_available_child_of_node should have of_node_put() before return. Early exits from for_each_available_child_of_node should decrement the node reference counter. Replace return by goto here. Reviewed-by: Fabrice Gasnier Signed-off-by: Wan Jiabing Link: https://lore.kernel.org/r/20211021121826.6339-1-wanjiabing@vivo.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 6245434f8377..7f1fb36c747c 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2024,7 +2024,8 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, if (strlen(name) >= STM32_ADC_CH_SZ) { dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n", name, STM32_ADC_CH_SZ); - return -EINVAL; + ret = -EINVAL; + goto err; } strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); ret = stm32_adc_populate_int_ch(indio_dev, name, val); -- cgit From 4498863cad7befbbd14006e033ae84784a33ae53 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Oct 2021 10:53:49 +0200 Subject: iio: st-sensors: Use dev_to_iio_dev() in sysfs callbacks Using `dev_get_drvdata()` in IIO sysfs callbacks to get a pointer to the IIO device is a relic from the very early days of IIO. The IIO core as well as most other drivers have switched over to using `dev_to_iio_dev()` instead. This driver is one of the last few drivers remaining that uses the outdated idiom, update it. This will allow to eventually update the IIO core to no longer set the drvdata for the IIO device and free it up for driver usage. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211020085349.16178-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 1de395bda03e..eb452d0c423c 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -638,7 +638,7 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, struct device_attribute *attr, char *buf) { int i, len = 0; - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct st_sensor_data *sdata = iio_priv(indio_dev); mutex_lock(&indio_dev->mlock); @@ -660,7 +660,7 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) { int i, len = 0, q, r; - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct st_sensor_data *sdata = iio_priv(indio_dev); mutex_lock(&indio_dev->mlock); -- cgit From ba1287e73182f2521d4fc5c0809620ed06652796 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Oct 2021 10:57:54 +0200 Subject: iio: imx7d_adc: Don't pass IIO device to imx7d_adc_{enable,disable}() The `imx7d_adc_enable()` and `imx7d_adc_disable()` functions are used as the suspend and resume callbacks for the device. When called as suspend/resume functions they are called with the platform_device's device as their parameter. In addition the functions are called on device probe and remove. In this case they are passed the struct device of the IIO device that the driver registers. This works because in the `imx7d_adc_{enable,disable}()` functions the passed struct device is only ever used as a parameter to `dev_get_drvdata()` and `dev_get_drvdata()` returns the same value for the platform device and the IIO device. But for consistency we should pass the same struct device to the `imx7d_adc_{enable,disable}()` in all cases. This will avoid accidental breakage if the device is ever used for something more than `dev_get_drvdata()`. Another motivation is that `dev_get_drvdata()` on the IIO device relies on the IIO core calling `dev_set_drvdata()`. Something we want to remove. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211020085754.16654-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 092f8d296527..12f5b8e34c84 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -522,12 +522,11 @@ static int imx7d_adc_probe(struct platform_device *pdev) imx7d_adc_feature_config(info); - ret = imx7d_adc_enable(&indio_dev->dev); + ret = imx7d_adc_enable(dev); if (ret) return ret; - ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, - &indio_dev->dev); + ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, dev); if (ret) return ret; -- cgit From dc19fa63ad80a636fdbc1a02153d1ab140cb901f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Oct 2021 16:21:10 +0200 Subject: iio: ms5611: Simplify IO callback parameters The ms5611 passes &indio_dev->dev as a parameter to all its IO callbacks only to directly cast the struct device back to struct iio_dev. And the struct iio_dev is then only used to get the drivers state struct. Simplify this a bit by passing the state struct directly. This makes it a bit easier to follow what the code is doing. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211020142110.7060-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611.h | 6 +++--- drivers/iio/pressure/ms5611_core.c | 7 +++---- drivers/iio/pressure/ms5611_i2c.c | 11 ++++------- drivers/iio/pressure/ms5611_spi.c | 17 +++++++---------- 4 files changed, 17 insertions(+), 24 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index 86b1c4b1820d..cbc9349c342a 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -50,9 +50,9 @@ struct ms5611_state { const struct ms5611_osr *pressure_osr; const struct ms5611_osr *temp_osr; - int (*reset)(struct device *dev); - int (*read_prom_word)(struct device *dev, int index, u16 *word); - int (*read_adc_temp_and_pressure)(struct device *dev, + int (*reset)(struct ms5611_state *st); + int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word); + int (*read_adc_temp_and_pressure)(struct ms5611_state *st, s32 *temp, s32 *pressure); struct ms5611_chip_info *chip_info; diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index ee75f08655c9..a4d0b54cde9b 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -85,8 +85,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev) struct ms5611_state *st = iio_priv(indio_dev); for (i = 0; i < MS5611_PROM_WORDS_NB; i++) { - ret = st->read_prom_word(&indio_dev->dev, - i, &st->chip_info->prom[i]); + ret = st->read_prom_word(st, i, &st->chip_info->prom[i]); if (ret < 0) { dev_err(&indio_dev->dev, "failed to read prom at %d\n", i); @@ -108,7 +107,7 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev, int ret; struct ms5611_state *st = iio_priv(indio_dev); - ret = st->read_adc_temp_and_pressure(&indio_dev->dev, temp, pressure); + ret = st->read_adc_temp_and_pressure(st, temp, pressure); if (ret < 0) { dev_err(&indio_dev->dev, "failed to read temperature and pressure\n"); @@ -196,7 +195,7 @@ static int ms5611_reset(struct iio_dev *indio_dev) int ret; struct ms5611_state *st = iio_priv(indio_dev); - ret = st->reset(&indio_dev->dev); + ret = st->reset(st); if (ret < 0) { dev_err(&indio_dev->dev, "failed to reset device\n"); return ret; diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 5c82d80f85b6..1047a85527a9 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -20,17 +20,15 @@ #include "ms5611.h" -static int ms5611_i2c_reset(struct device *dev) +static int ms5611_i2c_reset(struct ms5611_state *st) { - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); - return i2c_smbus_write_byte(st->client, MS5611_RESET); } -static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word) +static int ms5611_i2c_read_prom_word(struct ms5611_state *st, int index, + u16 *word) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); ret = i2c_smbus_read_word_swapped(st->client, MS5611_READ_PROM_WORD + (index << 1)); @@ -57,11 +55,10 @@ static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val) return 0; } -static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev, +static int ms5611_i2c_read_adc_temp_and_pressure(struct ms5611_state *st, s32 *temp, s32 *pressure) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); const struct ms5611_osr *osr = st->temp_osr; ret = i2c_smbus_write_byte(st->client, osr->cmd); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 79bed64c9b68..9fa2dcd71760 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -15,18 +15,17 @@ #include "ms5611.h" -static int ms5611_spi_reset(struct device *dev) +static int ms5611_spi_reset(struct ms5611_state *st) { u8 cmd = MS5611_RESET; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); return spi_write_then_read(st->client, &cmd, 1, NULL, 0); } -static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word) +static int ms5611_spi_read_prom_word(struct ms5611_state *st, int index, + u16 *word) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1)); if (ret < 0) @@ -37,11 +36,10 @@ static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word) return 0; } -static int ms5611_spi_read_adc(struct device *dev, s32 *val) +static int ms5611_spi_read_adc(struct ms5611_state *st, s32 *val) { int ret; u8 buf[3] = { MS5611_READ_ADC }; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); ret = spi_write_then_read(st->client, buf, 1, buf, 3); if (ret < 0) @@ -52,11 +50,10 @@ static int ms5611_spi_read_adc(struct device *dev, s32 *val) return 0; } -static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, +static int ms5611_spi_read_adc_temp_and_pressure(struct ms5611_state *st, s32 *temp, s32 *pressure) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); const struct ms5611_osr *osr = st->temp_osr; /* @@ -68,7 +65,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, return ret; usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL)); - ret = ms5611_spi_read_adc(dev, temp); + ret = ms5611_spi_read_adc(st, temp); if (ret < 0) return ret; @@ -78,7 +75,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, return ret; usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL)); - return ms5611_spi_read_adc(dev, pressure); + return ms5611_spi_read_adc(st, pressure); } static int ms5611_spi_probe(struct spi_device *spi) -- cgit From 4bdc3e967dc6c22e32da0ddba099828415ac4b0e Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 21 Oct 2021 20:42:53 +0800 Subject: iio: adc: ina2xx: Make use of the helper macro kthread_run() Repalce kthread_create/wake_up_process() with kthread_run() to simplify the code. Reviewed-by: Lars-Peter Clausen Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211021124254.3247-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index a4b2ff9e0dd5..360d7a00f60d 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -842,15 +842,14 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", chip->allow_async_readout); - task = kthread_create(ina2xx_capture_thread, (void *)indio_dev, - "%s:%d-%uus", indio_dev->name, - iio_device_id(indio_dev), - sampling_us); + task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, + "%s:%d-%uus", indio_dev->name, + iio_device_id(indio_dev), + sampling_us); if (IS_ERR(task)) return PTR_ERR(task); get_task_struct(task); - wake_up_process(task); chip->task = task; return 0; -- cgit From 2c4ce5041cd5d66875137a854b5e19672dce19a5 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 21 Oct 2021 20:42:54 +0800 Subject: iio: adc: ina2xx: Avoid double reference counting from get_task_struct/put_task_struct() kthread_run() and kthread_stop() already do reference counting of the task, so remove get_task_struct/put_task_struct() to avoid double reference counting. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211021124254.3247-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 360d7a00f60d..352f27657238 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -849,7 +849,6 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) if (IS_ERR(task)) return PTR_ERR(task); - get_task_struct(task); chip->task = task; return 0; @@ -861,7 +860,6 @@ static int ina2xx_buffer_disable(struct iio_dev *indio_dev) if (chip->task) { kthread_stop(chip->task); - put_task_struct(chip->task); chip->task = NULL; } -- cgit From 6bb835f3d00467c9a5e35f4955afa29df96a404e Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Sun, 24 Oct 2021 12:16:26 +0300 Subject: iio: core: Introduce IIO_VAL_INT_64. Introduce IIO_VAL_INT_64 to read 64-bit value for channel attribute. Val is used as lower 32 bits. Signed-off-by: Andriy Tryshnivskyy Link: https://lore.kernel.org/r/20211024091627.28031-2-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 463a63d5bf56..d94d26b11473 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -702,6 +702,9 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, } case IIO_VAL_CHAR: return sysfs_emit_at(buf, offset, "%c", (char)vals[0]); + case IIO_VAL_INT_64: + tmp2 = (s64)((((u64)vals[1]) << 32) | (u32)vals[0]); + return sysfs_emit_at(buf, offset, "%lld", tmp2); default: return 0; } -- cgit From 1fd85607e1e52dc6f3ac1a993f9ab57e416aa9ab Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Sun, 24 Oct 2021 12:16:27 +0300 Subject: iio/scmi: Add reading "raw" attribute. Add IIO_CHAN_INFO_RAW to the mask and implement corresponding reading "raw" attribute in scmi_iio_read_raw. Signed-off-by: Andriy Tryshnivskyy Acked-by: Jyoti Bhayana Link: https://lore.kernel.org/r/20211024091627.28031-3-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/common/scmi_sensors/scmi_iio.c | 57 +++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index 7cf2bf282cef..d538bf3ab1ef 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -279,6 +279,52 @@ static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2) return 0; } +static int scmi_iio_read_channel_data(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, int *val, int *val2) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_config; + struct scmi_sensor_reading readings[SCMI_IIO_NUM_OF_AXIS]; + int err; + + sensor_config = FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_ENABLE); + err = sensor->sensor_ops->config_set( + sensor->ph, sensor->sensor_info->id, sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in enabling sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + err = sensor->sensor_ops->reading_get_timestamped( + sensor->ph, sensor->sensor_info->id, + sensor->sensor_info->num_axis, readings); + if (err) { + dev_err(&iio_dev->dev, + "Error in reading raw attribute for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + sensor_config = FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_DISABLE); + err = sensor->sensor_ops->config_set( + sensor->ph, sensor->sensor_info->id, sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in disabling sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + *val = lower_32_bits(readings[ch->scan_index].value); + *val2 = upper_32_bits(readings[ch->scan_index].value); + + return IIO_VAL_INT_64; +} + static int scmi_iio_read_raw(struct iio_dev *iio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -300,6 +346,14 @@ static int scmi_iio_read_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_SAMP_FREQ: ret = scmi_iio_get_odr_val(iio_dev, val, val2); return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + ret = scmi_iio_read_channel_data(iio_dev, ch, val, val2); + iio_device_release_direct_mode(iio_dev); + return ret; default: return -EINVAL; } @@ -381,7 +435,8 @@ static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan, iio_chan->type = type; iio_chan->modified = 1; iio_chan->channel2 = mod; - iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE); + iio_chan->info_mask_separate = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_RAW); iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); iio_chan->info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ); -- cgit From 3c33b7b8267f0795d74f407e97f3eeec2acb0165 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 09:04:21 +0100 Subject: iio: Mark iio_device_type as const The iio_device_type struct is never modified, mark it as const. This allows it to be placed in a read-only memory section, which will protect against accidental or deliberate modification. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031080421.2086-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 2 +- drivers/iio/industrialio-core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 61e318431de9..501e286702ef 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -16,7 +16,7 @@ struct iio_buffer; struct iio_chan_spec; struct iio_dev; -extern struct device_type iio_device_type; +extern const struct device_type iio_device_type; struct iio_dev_buffer_pair { struct iio_dev *indio_dev; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index d94d26b11473..20d5178ca073 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1622,7 +1622,7 @@ static void iio_dev_release(struct device *device) kfree(iio_dev_opaque); } -struct device_type iio_device_type = { +const struct device_type iio_device_type = { .name = "iio_device", .release = iio_dev_release, }; -- cgit From 2d323927519c3ffbf4b0700459333bcc5528bb96 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:22 +0100 Subject: iio: interrupt-trigger: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/iio-trig-interrupt.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index f746c460bf2a..5f49cd105fae 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -25,9 +25,6 @@ static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) return IRQ_HANDLED; } -static const struct iio_trigger_ops iio_interrupt_trigger_ops = { -}; - static int iio_interrupt_trigger_probe(struct platform_device *pdev) { struct iio_interrupt_trigger_info *trig_info; @@ -58,7 +55,6 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) } iio_trigger_set_drvdata(trig, trig_info); trig_info->irq = irq; - trig->ops = &iio_interrupt_trigger_ops; ret = request_irq(irq, iio_interrupt_trigger_poll, irqflags, trig->name, trig); if (ret) { -- cgit From e28309ad8a06da6b5bdd210b3c98efc3149f862c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:23 +0100 Subject: iio: sysfs-trigger: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-2-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/iio-trig-sysfs.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index e9adfff45b39..2a4b75897910 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -124,9 +124,6 @@ static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = { NULL }; -static const struct iio_trigger_ops iio_sysfs_trigger_ops = { -}; - static int iio_sysfs_trigger_probe(int id) { struct iio_sysfs_trig *t; @@ -156,7 +153,6 @@ static int iio_sysfs_trigger_probe(int id) } t->trig->dev.groups = iio_sysfs_trigger_attr_groups; - t->trig->ops = &iio_sysfs_trigger_ops; iio_trigger_set_drvdata(t->trig, t); t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work); -- cgit From a3ab9c0622511bc8330ba9da0b406de6c7a0d645 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:24 +0100 Subject: iio: ad_sigma_delta: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-3-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 1d652d9b2f5c..cd418bd8bd87 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -467,9 +467,6 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) } EXPORT_SYMBOL_GPL(ad_sd_validate_trigger); -static const struct iio_trigger_ops ad_sd_trigger_ops = { -}; - static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); @@ -486,7 +483,6 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de if (sigma_delta->trig == NULL) return -ENOMEM; - sigma_delta->trig->ops = &ad_sd_trigger_ops; init_completion(&sigma_delta->completion); sigma_delta->irq_dis = true; -- cgit From 26ae5ed3fcda509ca46e28447bf0aa0fbff5bb88 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:25 +0100 Subject: iio: afe4403: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-4-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 97b82f9a8e45..273f16dcaff8 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -345,9 +345,6 @@ err: return IRQ_HANDLED; } -static const struct iio_trigger_ops afe4403_trigger_ops = { -}; - #define AFE4403_TIMING_PAIRS \ { AFE440X_LED2STC, 0x000050 }, \ { AFE440X_LED2ENDC, 0x0003e7 }, \ @@ -530,8 +527,6 @@ static int afe4403_probe(struct spi_device *spi) iio_trigger_set_drvdata(afe->trig, indio_dev); - afe->trig->ops = &afe4403_trigger_ops; - ret = iio_trigger_register(afe->trig); if (ret) { dev_err(afe->dev, "Unable to register IIO trigger\n"); -- cgit From 35ce398a554c9851f83730c186c7c325bb127e40 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:26 +0100 Subject: iio: afe4404: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-5-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4404.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 7ef3f5e34de5..aa9311e1e655 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -347,9 +347,6 @@ err: return IRQ_HANDLED; } -static const struct iio_trigger_ops afe4404_trigger_ops = { -}; - /* Default timings from data-sheet */ #define AFE4404_TIMING_PAIRS \ { AFE440X_PRPCOUNT, 39999 }, \ @@ -537,8 +534,6 @@ static int afe4404_probe(struct i2c_client *client, iio_trigger_set_drvdata(afe->trig, indio_dev); - afe->trig->ops = &afe4404_trigger_ops; - ret = iio_trigger_register(afe->trig); if (ret) { dev_err(afe->dev, "Unable to register IIO trigger\n"); -- cgit From 44c3bf8c1a4838115f5de5b66f84370b7aff2e21 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:27 +0100 Subject: iio: as3935: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-6-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 3797a8f54276..d62766b6b39e 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -238,9 +238,6 @@ err_read: return IRQ_HANDLED; } -static const struct iio_trigger_ops iio_interrupt_trigger_ops = { -}; - static void as3935_event_work(struct work_struct *work) { struct as3935_state *st; @@ -417,7 +414,6 @@ static int as3935_probe(struct spi_device *spi) st->trig = trig; st->noise_tripped = jiffies - HZ; iio_trigger_set_drvdata(trig, indio_dev); - trig->ops = &iio_interrupt_trigger_ops; ret = devm_iio_trigger_register(dev, trig); if (ret) { -- cgit From f3df6c739a8513ab81dd9d49c0329933620cfaa7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:28 +0100 Subject: iio: atlas-sensor: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-7-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 9cb99585b6ff..04b44a327614 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -434,9 +434,6 @@ static int atlas_buffer_predisable(struct iio_dev *indio_dev) return 0; } -static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { -}; - static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { .postenable = atlas_buffer_postenable, .predisable = atlas_buffer_predisable, @@ -645,7 +642,6 @@ static int atlas_probe(struct i2c_client *client, data->client = client; data->trig = trig; data->chip = chip; - trig->ops = &atlas_interrupt_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); i2c_set_clientdata(client, indio_dev); -- cgit From 9662afc9059b79ed4efb8b90b2865f9c919c7fe4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:29 +0100 Subject: iio: gp2ap020a00f: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-8-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index d1d9f2d319e4..b820041159f7 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1467,9 +1467,6 @@ static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { .predisable = &gp2ap020a00f_buffer_predisable, }; -static const struct iio_trigger_ops gp2ap020a00f_trigger_ops = { -}; - static int gp2ap020a00f_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1550,8 +1547,6 @@ static int gp2ap020a00f_probe(struct i2c_client *client, goto error_uninit_buffer; } - data->trig->ops = &gp2ap020a00f_trigger_ops; - init_irq_work(&data->work, gp2ap020a00f_iio_trigger_work); err = iio_trigger_register(data->trig); -- cgit From 6a9a90364914d41a1a7456dd964af8dc2ab3ed4b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:30 +0100 Subject: iio: lmp91000: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-9-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/potentiostat/lmp91000.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index ed30bdaa10ec..fe514f0b5506 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -271,9 +271,6 @@ static int lmp91000_buffer_cb(const void *val, void *private) return 0; } -static const struct iio_trigger_ops lmp91000_trigger_ops = { -}; - static int lmp91000_buffer_postenable(struct iio_dev *indio_dev) { struct lmp91000_data *data = iio_priv(indio_dev); @@ -330,7 +327,6 @@ static int lmp91000_probe(struct i2c_client *client, return -ENOMEM; } - data->trig->ops = &lmp91000_trigger_ops; init_completion(&data->completion); ret = lmp91000_read_config(data); -- cgit From eb0469894ba788ffdc81097b7dea822432e479d9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 1 Nov 2021 11:27:34 +0100 Subject: iio: mma8452: Use correct type for return variable in IRQ handler The IRQ handler's return type is irqreturn_t. The mma8452 uses a variable to store the return value, but the variable is of type int. Change this to irqreturn_t. This makes it easier to verify that the code is correct. Signed-off-by: Lars-Peter Clausen Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211101102734.32291-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 715b8138fb71..4ac4c06e9707 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1053,7 +1053,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) { struct iio_dev *indio_dev = p; struct mma8452_data *data = iio_priv(indio_dev); - int ret = IRQ_NONE; + irqreturn_t ret = IRQ_NONE; int src; src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC); -- cgit From 907b2ad8c9acad39ac1f0ccdbbe66c63856055e3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Oct 2021 10:29:28 +0200 Subject: iio: at91-sama5d2: Fix incorrect cast to platform_device The at91-sama5d2 driver calls `to_platform_device()` on a struct device that is part of a IIO device. This is incorrect since `to_platform_device()` must only be called on a struct device that is part of a platform device. The code still works by accident because non of the struct platform_device specific fields are accessed. Refactor the code a bit so that it behaves identically, but does not use the incorrect cast. This avoids accidentally adding undefined behavior in the future by assuming the `struct platform_device` is actually valid. Signed-off-by: Lars-Peter Clausen Tested-by: Eugen Hristev Link: https://lore.kernel.org/r/20211019082929.30503-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 4c922ef634f8..3841e7b6c81d 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1661,10 +1661,9 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, } } -static void at91_adc_dma_init(struct platform_device *pdev) +static void at91_adc_dma_init(struct at91_adc_state *st) { - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct at91_adc_state *st = iio_priv(indio_dev); + struct device *dev = &st->indio_dev->dev; struct dma_slave_config config = {0}; /* we have 2 bytes for each channel */ unsigned int sample_size = st->soc_info.platform->nr_channels * 2; @@ -1679,9 +1678,9 @@ static void at91_adc_dma_init(struct platform_device *pdev) if (st->dma_st.dma_chan) return; - st->dma_st.dma_chan = dma_request_chan(&pdev->dev, "rx"); + st->dma_st.dma_chan = dma_request_chan(dev, "rx"); if (IS_ERR(st->dma_st.dma_chan)) { - dev_info(&pdev->dev, "can't get DMA channel\n"); + dev_info(dev, "can't get DMA channel\n"); st->dma_st.dma_chan = NULL; goto dma_exit; } @@ -1691,7 +1690,7 @@ static void at91_adc_dma_init(struct platform_device *pdev) &st->dma_st.rx_dma_buf, GFP_KERNEL); if (!st->dma_st.rx_buf) { - dev_info(&pdev->dev, "can't allocate coherent DMA area\n"); + dev_info(dev, "can't allocate coherent DMA area\n"); goto dma_chan_disable; } @@ -1704,11 +1703,11 @@ static void at91_adc_dma_init(struct platform_device *pdev) config.dst_maxburst = 1; if (dmaengine_slave_config(st->dma_st.dma_chan, &config)) { - dev_info(&pdev->dev, "can't configure DMA slave\n"); + dev_info(dev, "can't configure DMA slave\n"); goto dma_free_area; } - dev_info(&pdev->dev, "using %s for rx DMA transfers\n", + dev_info(dev, "using %s for rx DMA transfers\n", dma_chan_name(st->dma_st.dma_chan)); return; @@ -1720,13 +1719,12 @@ dma_chan_disable: dma_release_channel(st->dma_st.dma_chan); st->dma_st.dma_chan = NULL; dma_exit: - dev_info(&pdev->dev, "continuing without DMA support\n"); + dev_info(dev, "continuing without DMA support\n"); } -static void at91_adc_dma_disable(struct platform_device *pdev) +static void at91_adc_dma_disable(struct at91_adc_state *st) { - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct at91_adc_state *st = iio_priv(indio_dev); + struct device *dev = &st->indio_dev->dev; /* we have 2 bytes for each channel */ unsigned int sample_size = st->soc_info.platform->nr_channels * 2; unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * @@ -1744,7 +1742,7 @@ static void at91_adc_dma_disable(struct platform_device *pdev) dma_release_channel(st->dma_st.dma_chan); st->dma_st.dma_chan = NULL; - dev_info(&pdev->dev, "continuing without DMA support\n"); + dev_info(dev, "continuing without DMA support\n"); } static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) @@ -1770,9 +1768,9 @@ static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) */ if (val == 1) - at91_adc_dma_disable(to_platform_device(&indio_dev->dev)); + at91_adc_dma_disable(st); else if (val > 1) - at91_adc_dma_init(to_platform_device(&indio_dev->dev)); + at91_adc_dma_init(st); /* * We can start the DMA only after setting the watermark and @@ -1780,7 +1778,7 @@ static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) */ ret = at91_adc_buffer_prepare(indio_dev); if (ret) - at91_adc_dma_disable(to_platform_device(&indio_dev->dev)); + at91_adc_dma_disable(st); return ret; } @@ -2077,7 +2075,7 @@ static int at91_adc_probe(struct platform_device *pdev) return 0; dma_disable: - at91_adc_dma_disable(pdev); + at91_adc_dma_disable(st); per_clk_disable_unprepare: clk_disable_unprepare(st->per_clk); vref_disable: @@ -2094,7 +2092,7 @@ static int at91_adc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); - at91_adc_dma_disable(pdev); + at91_adc_dma_disable(st); clk_disable_unprepare(st->per_clk); -- cgit From 0d376dc9febb78eda0bc3121f66d4e4d868880c0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Oct 2021 10:29:29 +0200 Subject: iio: at91-sama5d2: Use dev_to_iio_dev() in sysfs callbacks Using `dev_get_drvdata()` in IIO sysfs callbacks to get a pointer to the IIO device is a relic from the very early days of IIO. The IIO core as well as most other drivers have switched over to using `dev_to_iio_dev()` instead. This driver is one of the last few drivers remaining that uses the outdated idiom, update it. This will allow to eventually update the IIO core to no longer set the drvdata for the IIO device and free it up for driver usage. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211019082929.30503-2-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 3841e7b6c81d..a2c406276329 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1825,7 +1825,7 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) static ssize_t at91_adc_get_fifo_state(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan); @@ -1834,7 +1834,7 @@ static ssize_t at91_adc_get_fifo_state(struct device *dev, static ssize_t at91_adc_get_watermark(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark); -- cgit From f905772e8b16cde9858b9d775b215757d4d8db27 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:01 -0700 Subject: iio: bma220: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-2-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma220_spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index bc4c626e454d..74024d7ce5ac 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -27,7 +27,6 @@ #define BMA220_CHIP_ID 0xDD #define BMA220_READ_MASK BIT(7) #define BMA220_RANGE_MASK GENMASK(1, 0) -#define BMA220_DATA_SHIFT 2 #define BMA220_SUSPEND_SLEEP 0xFF #define BMA220_SUSPEND_WAKE 0x00 @@ -45,7 +44,7 @@ .sign = 's', \ .realbits = 6, \ .storagebits = 8, \ - .shift = BMA220_DATA_SHIFT, \ + .shift = 2, \ .endianness = IIO_CPU, \ }, \ } @@ -125,7 +124,8 @@ static int bma220_read_raw(struct iio_dev *indio_dev, ret = bma220_read_reg(data->spi_device, chan->address); if (ret < 0) return -EINVAL; - *val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE); -- cgit From 9105079db67a64fa58c10e699aabfe4703f5ac3f Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:02 -0700 Subject: iio: kxcjk-1013: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-3-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxcjk-1013.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index a51fdd3c9b5b..88cf0c276893 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -927,7 +927,8 @@ static int kxcjk1013_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->mutex); return ret; } - *val = sign_extend32(ret >> 4, 11); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); ret = kxcjk1013_set_power_state(data, false); } mutex_unlock(&data->mutex); -- cgit From 1aa2f96abbcc7e68a13ce53deaf41cb2fd2debfa Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:03 -0700 Subject: iio: mma7455: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-4-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma7455_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index 777c6c384b09..e6739ba74edf 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -134,7 +134,8 @@ static int mma7455_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - *val = sign_extend32(le16_to_cpu(data), 9); + *val = sign_extend32(le16_to_cpu(data), + chan->scan_type.realbits - 1); return IIO_VAL_INT; -- cgit From 5405c9b4074a93f01977f8b070c4d999aea00754 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:04 -0700 Subject: iio: sca3000: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-5-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index c6b75308148a..43ecacbdc95a 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -534,6 +534,13 @@ static const struct iio_chan_spec sca3000_channels_with_temp[] = { BIT(IIO_CHAN_INFO_OFFSET), /* No buffer support */ .scan_index = -1, + .scan_type = { + .sign = 'u', + .realbits = 9, + .storagebits = 16, + .shift = 5, + .endianness = IIO_BE, + }, }, { .type = IIO_ACCEL, @@ -730,8 +737,9 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, mutex_unlock(&st->lock); return ret; } - *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF; - *val = sign_extend32(*val, 12); + *val = sign_extend32(be16_to_cpup((__be16 *)st->rx) >> + chan->scan_type.shift, + chan->scan_type.realbits - 1); } else { /* get the temperature when available */ ret = sca3000_read_data_short(st, @@ -741,8 +749,9 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, mutex_unlock(&st->lock); return ret; } - *val = ((st->rx[0] & 0x3F) << 3) | - ((st->rx[1] & 0xE0) >> 5); + *val = (be16_to_cpup((__be16 *)st->rx) >> + chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); } mutex_unlock(&st->lock); return IIO_VAL_INT; -- cgit From 571f8d006f39d8159d80f65eeb15603e649f6611 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:05 -0700 Subject: iio: stk8312: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-6-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/stk8312.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 43c621d0f11e..de0cdf8c1f94 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -355,7 +355,7 @@ static int stk8312_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return ret; } - *val = sign_extend32(ret, 7); + *val = sign_extend32(ret, chan->scan_type.realbits - 1); ret = stk8312_set_mode(data, data->mode & (~STK8312_MODE_ACTIVE)); mutex_unlock(&data->lock); -- cgit From ded408b1135437540d27012da7b6f1afb4f4bf65 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:06 -0700 Subject: iio: stk8ba50: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-7-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/stk8ba50.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index e137a34b5c9a..517c57ed9e94 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -227,7 +227,8 @@ static int stk8ba50_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return -EINVAL; } - *val = sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND); mutex_unlock(&data->lock); return IIO_VAL_INT; -- cgit From 4e9f4c12f1863b890965bfbf81d8d9bc85c12edb Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:07 -0700 Subject: iio: ad7266: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-8-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7266.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index a8ec3efd659e..1d345d66742d 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -159,7 +159,8 @@ static int ad7266_read_raw(struct iio_dev *indio_dev, *val = (*val >> 2) & 0xfff; if (chan->scan_type.sign == 's') - *val = sign_extend32(*val, 11); + *val = sign_extend32(*val, + chan->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: -- cgit From a5cd0e7f5b3cda94b9f4029b8baef817a7a97226 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:09 -0700 Subject: iio: ti-adc12138: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-10-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc12138.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index fcd5d39dd03e..5b5d45210539 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -239,7 +239,8 @@ static int adc12138_read_raw(struct iio_dev *iio, if (ret) return ret; - *value = sign_extend32(be16_to_cpu(data) >> 3, 12); + *value = sign_extend32(be16_to_cpu(data) >> channel->scan_type.shift, + channel->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: -- cgit From 4d57fb548a1b086fc216c94cc186fba03c396190 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:10 -0700 Subject: iio: mag3110: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-11-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mag3110.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index c96415a1aead..17c62d806218 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -291,7 +291,8 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, if (ret < 0) goto release; *val = sign_extend32( - be16_to_cpu(buffer[chan->scan_index]), 15); + be16_to_cpu(buffer[chan->scan_index]), + chan->scan_type.realbits - 1); ret = IIO_VAL_INT; break; case IIO_TEMP: /* in 1 C / LSB */ @@ -306,7 +307,8 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); if (ret < 0) goto release; - *val = sign_extend32(ret, 7); + *val = sign_extend32(ret, + chan->scan_type.realbits - 1); ret = IIO_VAL_INT; break; default: -- cgit From aad54091e1b50d725baa31c11358e6d6dcf44cf0 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:11 -0700 Subject: iio: ti-ads1015: Remove shift variable ads1015_read_raw By using scan_type.realbits when processing raw data, we use scan_type.shit only once, thus we don't need to define a local variable for it anymore. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-12-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index b0352e91ac16..b92d4cd1b823 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -464,9 +464,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, mutex_lock(&data->lock); switch (mask) { - case IIO_CHAN_INFO_RAW: { - int shift = chan->scan_type.shift; - + case IIO_CHAN_INFO_RAW: ret = iio_device_claim_direct_mode(indio_dev); if (ret) break; @@ -487,7 +485,8 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, goto release_direct; } - *val = sign_extend32(*val >> shift, 15 - shift); + *val = sign_extend32(*val >> chan->scan_type.shift, + chan->scan_type.realbits - 1); ret = ads1015_set_power_state(data, false); if (ret < 0) @@ -497,7 +496,6 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, release_direct: iio_device_release_direct_mode(indio_dev); break; - } case IIO_CHAN_INFO_SCALE: idx = data->channel_data[chan->address].pga; *val = ads1015_fullscale_range[idx]; -- cgit From fb3e8bb47806a3e41d200841518726a9e700e283 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:12 -0700 Subject: iio: xilinx-xadc-core: Use local variable in xadc_read_raw Minor cleanup: bit is already defined as chan->scan_type.realbits, use bit when needed. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-13-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 2aa4278ecba7..823c8e5f9809 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -944,7 +944,7 @@ static int xadc_read_raw(struct iio_dev *indio_dev, *val = 1000; break; } - *val2 = chan->scan_type.realbits; + *val2 = bits; return IIO_VAL_FRACTIONAL_LOG2; case IIO_TEMP: /* Temp in C = (val * 503.975) / 2**bits - 273.15 */ -- cgit From 7721c73d8018ee8a8588ab165a34032bec27de4d Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:13 -0700 Subject: iio: mpl3115: Use scan_type.shift and realbit in mpl3115_read_raw When processing raw data using channel scan_type.shift as source of trust to shift data appropriately. When processing the temperature channel, use a 16bit big endian variable as buffer to increase conversion readability. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-14-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/mpl3115.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index 1eb9e7b29e05..e95b9a5475b4 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -74,7 +74,6 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct mpl3115_data *data = iio_priv(indio_dev); - __be32 tmp = 0; int ret; switch (mask) { @@ -84,7 +83,9 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, return ret; switch (chan->type) { - case IIO_PRESSURE: /* in 0.25 pascal / LSB */ + case IIO_PRESSURE: { /* in 0.25 pascal / LSB */ + __be32 tmp = 0; + mutex_lock(&data->lock); ret = mpl3115_request(data); if (ret < 0) { @@ -96,10 +97,13 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); if (ret < 0) break; - *val = be32_to_cpu(tmp) >> 12; + *val = be32_to_cpu(tmp) >> chan->scan_type.shift; ret = IIO_VAL_INT; break; - case IIO_TEMP: /* in 0.0625 celsius / LSB */ + } + case IIO_TEMP: { /* in 0.0625 celsius / LSB */ + __be16 tmp; + mutex_lock(&data->lock); ret = mpl3115_request(data); if (ret < 0) { @@ -111,9 +115,11 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); if (ret < 0) break; - *val = sign_extend32(be32_to_cpu(tmp) >> 20, 11); + *val = sign_extend32(be16_to_cpu(tmp) >> chan->scan_type.shift, + chan->scan_type.realbits - 1); ret = IIO_VAL_INT; break; + } default: ret = -EINVAL; break; -- cgit From 471d040defb243e59a2cee42069ca4e8d6d3e94b Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Fri, 5 Nov 2021 01:55:04 +0000 Subject: iio: adc: rzg2l_adc: Remove unnecessary print function dev_err() The print function dev_err() is redundant because platform_get_irq() already prints an error. Signed-off-by: Xu Wang Reviewed-by: Geert Uytterhoeven Reviewed-by: Lad Prabhakar Link: https://lore.kernel.org/r/20211105015504.39226-1-vulab@iscas.ac.cn Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rzg2l_adc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c index 32fbf57c362f..9d5be52bd948 100644 --- a/drivers/iio/adc/rzg2l_adc.c +++ b/drivers/iio/adc/rzg2l_adc.c @@ -506,10 +506,8 @@ static int rzg2l_adc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no irq resource\n"); + if (irq < 0) return irq; - } ret = devm_request_irq(dev, irq, rzg2l_adc_isr, 0, dev_name(dev), adc); -- cgit From 7d71d289e1ba86838bc908d5ce216a208815fd01 Mon Sep 17 00:00:00 2001 From: Maslov Dmitry Date: Sat, 6 Nov 2021 18:41:37 +0100 Subject: iio: light: ltr501: Added ltr303 driver support Previously ltr501 driver supported a number of light and, proximity sensors including ltr501, ltr559 and ltr301. This adds support for another light sensor ltr303 used in Seeed Studio reTerminal, a carrier board for Raspberry Pi 4 CM. Signed-off-by: Maslov Dmitry Link: https://lore.kernel.org/r/20211106174137.6783-1-maslovdmitry@seeed.cc Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 7e51aaac0bf8..bab5b78f2e30 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor + * Support for Lite-On LTR501 and similar ambient light and proximity sensors. * * Copyright 2014 Peter Meerwald * @@ -98,6 +98,7 @@ enum { ltr501 = 0, ltr559, ltr301, + ltr303, }; struct ltr501_gain { @@ -1231,6 +1232,18 @@ static const struct ltr501_chip_info ltr501_chip_info_tbl[] = { .channels = ltr301_channels, .no_channels = ARRAY_SIZE(ltr301_channels), }, + [ltr303] = { + .partid = 0x0A, + .als_gain = ltr559_als_gain_tbl, + .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), + .als_mode_active = BIT(0), + .als_gain_mask = BIT(2) | BIT(3) | BIT(4), + .als_gain_shift = 2, + .info = <r301_info, + .info_no_irq = <r301_info_no_irq, + .channels = ltr301_channels, + .no_channels = ARRAY_SIZE(ltr301_channels), + }, }; static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val) @@ -1605,6 +1618,7 @@ static const struct i2c_device_id ltr501_id[] = { { "ltr501", ltr501}, { "ltr559", ltr559}, { "ltr301", ltr301}, + { "ltr303", ltr303}, { } }; MODULE_DEVICE_TABLE(i2c, ltr501_id); @@ -1613,6 +1627,7 @@ static const struct of_device_id ltr501_of_match[] = { { .compatible = "liteon,ltr501", }, { .compatible = "liteon,ltr559", }, { .compatible = "liteon,ltr301", }, + { .compatible = "liteon,ltr303", }, {} }; MODULE_DEVICE_TABLE(of, ltr501_of_match); -- cgit From e5cc9840f08be46c701d88b81f06d37db516fe32 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 13 Oct 2021 12:49:23 +0300 Subject: iio: buffer: Use dedicated variable in iio_buffers_alloc_sysfs_and_mask() Use dedicated variable for index in the loop in the iio_buffers_alloc_sysfs_and_mask(). This will make code cleaner and less error prone as proved by previous changes done in this function. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211013094923.2473-3-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index e180728914c0..94eb9f6cf128 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1727,8 +1727,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); const struct iio_chan_spec *channels; struct iio_buffer *buffer; - int unwind_idx; - int ret, i; + int ret, i, idx; size_t sz; channels = indio_dev->channels; @@ -1743,15 +1742,12 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) if (!iio_dev_opaque->attached_buffers_cnt) return 0; - for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { - buffer = iio_dev_opaque->attached_buffers[i]; - ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, i); - if (ret) { - unwind_idx = i - 1; + for (idx = 0; idx < iio_dev_opaque->attached_buffers_cnt; idx++) { + buffer = iio_dev_opaque->attached_buffers[idx]; + ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, idx); + if (ret) goto error_unwind_sysfs_and_mask; - } } - unwind_idx = iio_dev_opaque->attached_buffers_cnt - 1; sz = sizeof(*(iio_dev_opaque->buffer_ioctl_handler)); iio_dev_opaque->buffer_ioctl_handler = kzalloc(sz, GFP_KERNEL); @@ -1767,9 +1763,9 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) return 0; error_unwind_sysfs_and_mask: - for (; unwind_idx >= 0; unwind_idx--) { - buffer = iio_dev_opaque->attached_buffers[unwind_idx]; - __iio_buffer_free_sysfs_and_mask(buffer, indio_dev, unwind_idx); + while (idx--) { + buffer = iio_dev_opaque->attached_buffers[idx]; + __iio_buffer_free_sysfs_and_mask(buffer, indio_dev, idx); } return ret; } -- cgit From ab1fb45579d876aee70eb736d3d9e6a9bacc798d Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 15 Nov 2021 14:19:13 +0000 Subject: iio: buffer-dma: Use round_down() instead of rounddown() We know that the buffer's alignment will always be a power of two; therefore, we can use the faster round_down() macro. Signed-off-by: Paul Cercueil Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211115141925.60164-4-paul@crapouillou.net Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 1ac94c4e9792..f8ce26a24c57 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -67,7 +67,7 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, dma_cookie_t cookie; block->bytes_used = min(block->size, dmaengine_buffer->max_size); - block->bytes_used = rounddown(block->bytes_used, + block->bytes_used = round_down(block->bytes_used, dmaengine_buffer->align); desc = dmaengine_prep_slave_single(dmaengine_buffer->chan, -- cgit From ffc7c5172a6d1f7ec468066a7172ce65baf1e3e1 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 19 Nov 2021 10:56:27 +0200 Subject: iio: expose shared parameter in IIO_ENUM_AVAILABLE The shared parameter should be configurable based on its usage, and not constrained to IIO_SHARED_BY_TYPE. This patch aims to improve the flexibility in using the IIO_ENUM_AVAILABLE define and avoid redefining custom iio enums that expose the shared parameter. An example is the ad5766.c driver where IIO_ENUM_AVAILABLE_SHARED was defined in order to achieve `shared` parameter customization. The current state of the IIO_ENUM_AVAILABLE implementation will imply similar redefinitions each time a driver will require access to the `shared` parameter. An example would be admv1013 driver which will require custom device attribute for the frequency translation modes: Quadrature I/Q mode and Intermediate Frequency mode. Signed-off-by: Antoniu Miclaus Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211119085627.6348-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma180.c | 2 +- drivers/iio/accel/mma9553.c | 2 +- drivers/iio/adc/ad7192.c | 3 ++- drivers/iio/adc/hi8435.c | 2 +- drivers/iio/dac/ad5064.c | 4 ++-- drivers/iio/dac/ad5380.c | 2 +- drivers/iio/dac/ad5446.c | 2 +- drivers/iio/dac/ad5504.c | 2 +- drivers/iio/dac/ad5624r_spi.c | 2 +- drivers/iio/dac/ad5686.c | 2 +- drivers/iio/dac/ad5766.c | 13 ++----------- drivers/iio/dac/ad5791.c | 2 +- drivers/iio/dac/max5821.c | 2 +- drivers/iio/dac/mcp4725.c | 8 ++++---- drivers/iio/dac/stm32-dac.c | 2 +- drivers/iio/dac/ti-dac082s085.c | 2 +- drivers/iio/dac/ti-dac5571.c | 2 +- drivers/iio/dac/ti-dac7311.c | 2 +- drivers/iio/magnetometer/hmc5843_core.c | 4 ++-- drivers/iio/trigger/stm32-timer-trigger.c | 4 ++-- 20 files changed, 28 insertions(+), 36 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 2edfcb4819b7..09496f358ad9 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -658,7 +658,7 @@ static const struct iio_chan_spec_ext_info bma023_ext_info[] = { static const struct iio_chan_spec_ext_info bma180_ext_info[] = { IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE, &bma180_power_mode_enum), - IIO_ENUM_AVAILABLE("power_mode", &bma180_power_mode_enum), + IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE, &bma180_power_mode_enum), IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma180_accel_get_mount_matrix), { } }; diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index ba3ecb3b57dc..0570ab1cc064 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -917,7 +917,7 @@ static const struct iio_enum mma9553_calibgender_enum = { static const struct iio_chan_spec_ext_info mma9553_ext_info[] = { IIO_ENUM("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), - IIO_ENUM_AVAILABLE("calibgender", &mma9553_calibgender_enum), + IIO_ENUM_AVAILABLE("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), {}, }; diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 2121a812b0c3..cc990205f306 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -257,7 +257,8 @@ static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = { }, IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, &ad7192_syscalib_mode_enum), - IIO_ENUM_AVAILABLE("sys_calibration_mode", &ad7192_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7192_syscalib_mode_enum), {} }; diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index 8b353e26668e..e665e14c6e54 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -350,7 +350,7 @@ static const struct iio_enum hi8435_sensing_mode = { static const struct iio_chan_spec_ext_info hi8435_ext_info[] = { IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode), - IIO_ENUM_AVAILABLE("sensing_mode", &hi8435_sensing_mode), + IIO_ENUM_AVAILABLE("sensing_mode", IIO_SHARED_BY_TYPE, &hi8435_sensing_mode), {}, }; diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index fd9cac4f6321..27ee2c63c5d4 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -377,7 +377,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5064_powerdown_mode_enum), { }, }; @@ -389,7 +389,7 @@ static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, <c2617_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", <c2617_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, <c2617_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 8ca26bb4b62f..e38860a6a9f3 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -249,7 +249,7 @@ static const struct iio_chan_spec_ext_info ad5380_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 3cc5513a6cbf..1c9b54c012a7 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -142,7 +142,7 @@ static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5446_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 19cdf9890d02..b631261efa97 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -241,7 +241,7 @@ static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5504_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5504_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 530529feebb5..3c98941b9f99 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -159,7 +159,7 @@ static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5624r_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5624r_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 8f001db775f4..e592a995f404 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -184,7 +184,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5686_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index b0d220c3a126..43189af2fb1f 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -426,14 +426,6 @@ static ssize_t ad5766_write_ext(struct iio_dev *indio_dev, .shared = _shared, \ } -#define IIO_ENUM_AVAILABLE_SHARED(_name, _shared, _e) \ -{ \ - .name = (_name "_available"), \ - .shared = _shared, \ - .read = iio_enum_available_read, \ - .private = (uintptr_t)(_e), \ -} - static const struct iio_chan_spec_ext_info ad5766_ext_info[] = { _AD5766_CHAN_EXT_INFO("dither_enable", AD5766_DITHER_ENABLE, @@ -443,9 +435,8 @@ static const struct iio_chan_spec_ext_info ad5766_ext_info[] = { _AD5766_CHAN_EXT_INFO("dither_source", AD5766_DITHER_SOURCE, IIO_SEPARATE), IIO_ENUM("dither_scale", IIO_SEPARATE, &ad5766_dither_scale_enum), - IIO_ENUM_AVAILABLE_SHARED("dither_scale", - IIO_SEPARATE, - &ad5766_dither_scale_enum), + IIO_ENUM_AVAILABLE("dither_scale", IIO_SEPARATE, + &ad5766_dither_scale_enum), {} }; diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index a0923b76e8b6..7b4579d73d18 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -285,7 +285,7 @@ static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5791_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5791_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index 7da4710a6408..fce640b7f1c8 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -137,7 +137,7 @@ static const struct iio_chan_spec_ext_info max5821_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &max5821_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &max5821_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &max5821_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 34b14aafb630..98b2c2f10bf3 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -221,8 +221,8 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp472x_powerdown_mode_enum[MCP4725]), - IIO_ENUM_AVAILABLE("powerdown_mode", - &mcp472x_powerdown_mode_enum[MCP4725]), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &mcp472x_powerdown_mode_enum[MCP4725]), { }, }; @@ -235,8 +235,8 @@ static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp472x_powerdown_mode_enum[MCP4726]), - IIO_ENUM_AVAILABLE("powerdown_mode", - &mcp472x_powerdown_mode_enum[MCP4726]), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &mcp472x_powerdown_mode_enum[MCP4726]), { }, }; diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index dd2e306824e7..cd71cc4553a7 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -246,7 +246,7 @@ static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en), - IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &stm32_dac_powerdown_mode_en), {}, }; diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c index 5c14bfb16521..6beda2193683 100644 --- a/drivers/iio/dac/ti-dac082s085.c +++ b/drivers/iio/dac/ti-dac082s085.c @@ -160,7 +160,7 @@ static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { .shared = IIO_SHARED_BY_TYPE, }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), - IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), { }, }; diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index 546a4cf6c5ef..4a3b8d875518 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -212,7 +212,7 @@ static const struct iio_chan_spec_ext_info dac5571_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &dac5571_powerdown_mode), - IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode), {}, }; diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 09218c3029f0..99f275829ec2 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -146,7 +146,7 @@ static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { .shared = IIO_SHARED_BY_TYPE, }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), - IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), { }, }; diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index f08726bf5ec3..5a730d9bdbb0 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -246,7 +246,7 @@ static const struct iio_enum hmc5843_meas_conf_enum = { static const struct iio_chan_spec_ext_info hmc5843_ext_info[] = { IIO_ENUM("meas_conf", IIO_SHARED_BY_TYPE, &hmc5843_meas_conf_enum), - IIO_ENUM_AVAILABLE("meas_conf", &hmc5843_meas_conf_enum), + IIO_ENUM_AVAILABLE("meas_conf", IIO_SHARED_BY_TYPE, &hmc5843_meas_conf_enum), IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix), { } }; @@ -260,7 +260,7 @@ static const struct iio_enum hmc5983_meas_conf_enum = { static const struct iio_chan_spec_ext_info hmc5983_ext_info[] = { IIO_ENUM("meas_conf", IIO_SHARED_BY_TYPE, &hmc5983_meas_conf_enum), - IIO_ENUM_AVAILABLE("meas_conf", &hmc5983_meas_conf_enum), + IIO_ENUM_AVAILABLE("meas_conf", IIO_SHARED_BY_TYPE, &hmc5983_meas_conf_enum), IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix), { } }; diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 33083877cd19..02b87b0f9d70 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -696,9 +696,9 @@ static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { .write = stm32_count_set_preset }, IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum), - IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum), + IIO_ENUM_AVAILABLE("enable_mode", IIO_SHARED_BY_TYPE, &stm32_enable_mode_enum), IIO_ENUM("trigger_mode", IIO_SEPARATE, &stm32_trigger_mode_enum), - IIO_ENUM_AVAILABLE("trigger_mode", &stm32_trigger_mode_enum), + IIO_ENUM_AVAILABLE("trigger_mode", IIO_SHARED_BY_TYPE, &stm32_trigger_mode_enum), {} }; -- cgit From ee8ec048e091bfe36cc463e7a30eefbe5fef3e75 Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Fri, 5 Nov 2021 12:05:00 +0200 Subject: iio: test: Add check against NULL for buffer in tests. Add KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf) for every test. Also use ARRAY_SIZE(values) where it is possible. Signed-off-by: Andriy Tryshnivskyy Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211105100501.1904-2-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/test/iio-test-format.c | 69 +++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 27 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index f1e951eddb43..b746d00bc0ea 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -14,10 +14,13 @@ static void iio_test_iio_format_value_integer(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + char *buf; int val; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + val = 42; ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "42\n"); @@ -41,142 +44,154 @@ static void iio_test_iio_format_value_integer(struct kunit *test) static void iio_test_iio_format_value_fixedpoint(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[2]; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + /* positive >= 1 */ values[0] = 1; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000010\n"); /* positive < 1 */ values[0] = 0; values[1] = 12; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000012\n"); /* negative <= -1 */ values[0] = -1; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000000010\n"); /* negative > -1 */ values[0] = 0; values[1] = -123; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000123\n"); } static void iio_test_iio_format_value_fractional(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[2]; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + /* positive < 1 */ values[0] = 1; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.100000000\n"); /* positive >= 1 */ values[0] = 100; values[1] = 3; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "33.333333333\n"); /* negative > -1 */ values[0] = -1; values[1] = 1000000000; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000001\n"); /* negative <= -1 */ values[0] = -200; values[1] = 3; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-66.666666666\n"); /* Zero */ values[0] = 0; values[1] = -10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); } static void iio_test_iio_format_value_fractional_log2(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[2]; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + /* positive < 1 */ values[0] = 123; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.120117187\n"); /* positive >= 1 */ values[0] = 1234567; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1205.631835937\n"); /* negative > -1 */ values[0] = -123; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.120117187\n"); /* negative <= -1 */ values[0] = -1234567; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1205.631835937\n"); /* Zero */ values[0] = 0; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); } static void iio_test_iio_format_value_multiple(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[] = {1, -2, 3, -4, 5}; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + ret = iio_format_value(buf, IIO_VAL_INT_MULTIPLE, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1 -2 3 -4 5 \n"); -- cgit From 1d9b750c92d738cb84eb1531dd85147466202b86 Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Fri, 5 Nov 2021 12:05:01 +0200 Subject: iio: test: Add test for IIO_VAL_INT_64. Add test for newly introduced type IIO_VAL_INT_64. Signed-off-by: Andriy Tryshnivskyy Reported-by: kernel test robot Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211105100501.1904-3-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/test/iio-test-format.c | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index b746d00bc0ea..237321436b83 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -197,12 +197,66 @@ static void iio_test_iio_format_value_multiple(struct kunit *test) IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1 -2 3 -4 5 \n"); } +static void iio_test_iio_format_value_integer_64(struct kunit *test) +{ + int values[2]; + s64 value; + char *buf; + int ret; + + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + value = 24; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "24\n"); + + value = -24; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-24\n"); + + value = 0; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n"); + + value = UINT_MAX; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "4294967295\n"); + + value = -((s64)UINT_MAX); + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-4294967295\n"); + + value = LLONG_MAX; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854775807\n"); + + value = LLONG_MIN; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854775808\n"); +} + static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_integer), KUNIT_CASE(iio_test_iio_format_value_fixedpoint), KUNIT_CASE(iio_test_iio_format_value_fractional), KUNIT_CASE(iio_test_iio_format_value_fractional_log2), KUNIT_CASE(iio_test_iio_format_value_multiple), + KUNIT_CASE(iio_test_iio_format_value_integer_64), {} }; -- cgit From 35619155d044830357f06f1d2c8188c4530b4d7a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 13 Nov 2021 16:23:14 +0100 Subject: iio: imu: st_lsm6dsx: add dts property to disable sensor-hub Introduce the capability to disable sensorhub through a device-tree property since there are some configurations where users want to explicitly disable sensor-hub auto-probing at bootstrap. A typical configuration is when the sensorhub clock/data lines are connected to a pull-up resistor since no slave sensors are connected to the i2c master. If SDO/SA0 line is connected to the same pull-up resistor, when the driver tries to probe slave devices connected on sensor-hub, it will force SDO/SA0 line to low, modifying the device i2c address. Tested-by: Mario Tesi Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/ad7894e7b1c6fb3427fab3f623bb942860ad45cf.1636816719.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index f2cbbc756459..727b4b6ac696 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2244,7 +2244,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; hub_settings = &hw->settings->shub_settings; - if (hub_settings->master_en.addr) { + if (hub_settings->master_en.addr && + (!dev_fwnode(dev) || + !device_property_read_bool(dev, "st,disable-sensor-hub"))) { err = st_lsm6dsx_shub_probe(hw, name); if (err < 0) return err; -- cgit From 4da5f2d6f2e3286262d32db901ec735a6a5a51b9 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Thu, 18 Nov 2021 17:12:32 +0300 Subject: iio:adc:axp20x: add support for NTC thermistor Most AXPxxx-based reference designs place a 10k NTC thermistor on a TS pin. When appropriately configured, AXP PMICs will inject fixed current (80uA by default) into TS pin and measure the voltage across a thermistor. The PMIC itself will by default compare this voltage with predefined thresholds and disable battery charging whenever the battery is too hot or too cold. Alternatively, the TS pin can be configured as general-purpose ADC input. This mode is not supported by the driver. This patch allows reading the voltage on the TS pin. It can be then either processed by userspace or used by kernel consumer like hwmon ntc thermistor driver. Signed-off-by: Evgeny Boger Acked-by: Maxime Ripard Reviewed-by: Quentin Schulz Link: https://lore.kernel.org/r/20211118141233.247907-2-boger@wirenboard.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 45 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 3e0c0233b431..12d469a52cea 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -186,6 +186,8 @@ static const struct iio_chan_spec axp20x_adc_channels[] = { AXP20X_BATT_CHRG_I_H), AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP20X_TS_IN, "ts_v", IIO_VOLTAGE, + AXP20X_TS_IN_H), }; static const struct iio_chan_spec axp22x_adc_channels[] = { @@ -203,6 +205,8 @@ static const struct iio_chan_spec axp22x_adc_channels[] = { AXP20X_BATT_CHRG_I_H), AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_TS_IN, "ts_v", IIO_VOLTAGE, + AXP22X_TS_ADC_H), }; static const struct iio_chan_spec axp813_adc_channels[] = { @@ -222,6 +226,8 @@ static const struct iio_chan_spec axp813_adc_channels[] = { AXP20X_BATT_CHRG_I_H), AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP813_TS_IN, "ts_v", IIO_VOLTAGE, + AXP288_TS_ADC_H), }; static int axp20x_adc_raw(struct iio_dev *indio_dev, @@ -307,11 +313,36 @@ static int axp20x_adc_scale_voltage(int channel, int *val, int *val2) *val2 = 400000; return IIO_VAL_INT_PLUS_MICRO; + case AXP20X_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + default: return -EINVAL; } } +static int axp22x_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP22X_BATT_V: + /* 1.1 mV per LSB */ + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP22X_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} static int axp813_adc_scale_voltage(int channel, int *val, int *val2) { switch (channel) { @@ -325,6 +356,12 @@ static int axp813_adc_scale_voltage(int channel, int *val, int *val2) *val2 = 100000; return IIO_VAL_INT_PLUS_MICRO; + case AXP813_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + default: return -EINVAL; } @@ -378,12 +415,7 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val, { switch (chan->type) { case IIO_VOLTAGE: - if (chan->channel != AXP22X_BATT_V) - return -EINVAL; - - *val = 1; - *val2 = 100000; - return IIO_VAL_INT_PLUS_MICRO; + return axp22x_adc_scale_voltage(chan->channel, val, val2); case IIO_CURRENT: *val = 0; @@ -488,6 +520,7 @@ static int axp22x_read_raw(struct iio_dev *indio_dev, { switch (mask) { case IIO_CHAN_INFO_OFFSET: + /* For PMIC temp only */ *val = -2677; return IIO_VAL_INT; -- cgit From 4114835810aecec94b4163b8b1086dd953476391 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Thu, 25 Nov 2021 17:56:46 +0500 Subject: iio: ltr501: Export near level property for proximity sensor Userspace tools like iio-sensor-proxy need to know the proximity level that should be considered "near". This value is hardware-specific and can be defined via the devicetree. Allow the driver to export the near level. Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20211125125646.54831-2-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index bab5b78f2e30..902b9c7a96a7 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -166,6 +166,7 @@ struct ltr501_data { struct regmap_field *reg_ps_rate; struct regmap_field *reg_als_prst; struct regmap_field *reg_ps_prst; + uint32_t near_level; }; static const struct ltr501_samp_table ltr501_als_samp_table[] = { @@ -525,6 +526,25 @@ static int ltr501_write_intr_prst(struct ltr501_data *data, return -EINVAL; } +static ssize_t ltr501_read_near_level(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ltr501_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", data->near_level); +} + +static const struct iio_chan_spec_ext_info ltr501_ext_info[] = { + { + .name = "nearlevel", + .shared = IIO_SEPARATE, + .read = ltr501_read_near_level, + }, + { /* sentinel */ } +}; + static const struct iio_event_spec ltr501_als_event_spec[] = { { .type = IIO_EV_TYPE_THRESH, @@ -609,6 +629,7 @@ static const struct iio_chan_spec ltr501_channels[] = { }, .event_spec = ltr501_pxs_event_spec, .num_event_specs = ARRAY_SIZE(ltr501_pxs_event_spec), + .ext_info = ltr501_ext_info, }, IIO_CHAN_SOFT_TIMESTAMP(3), }; @@ -1531,6 +1552,10 @@ static int ltr501_probe(struct i2c_client *client, if ((partid >> 4) != data->chip_info->partid) return -ENODEV; + if (device_property_read_u32(&client->dev, "proximity-near-level", + &data->near_level)) + data->near_level = 0; + indio_dev->info = data->chip_info->info; indio_dev->channels = data->chip_info->channels; indio_dev->num_channels = data->chip_info->no_channels; -- cgit From 0bb12606c05fe9737e3056fe76d6e4b9c2a87b57 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 2 Dec 2021 17:08:18 +0200 Subject: iio:dac:ad7293: add support for AD7293 The AD7293 is a Power Amplifier drain current controller containing functionality for general-purpose monitoring and control of current, voltage, and temperature, integrated into a single chip solution with an SPI-compatible interface. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/AD7293.pdf Signed-off-by: Antoniu Miclaus Reviewed-by: Cai Huoqing Link: https://lore.kernel.org/r/20211202150819.24832-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 11 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad7293.c | 934 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 946 insertions(+) create mode 100644 drivers/iio/dac/ad7293.c (limited to 'drivers/iio') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 75e1f2b48638..6206b90fc08f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -221,6 +221,17 @@ config AD5791 To compile this driver as a module, choose M here: the module will be called ad5791. +config AD7293 + tristate "Analog Devices AD7293 Power Amplifier Current Controller" + depends on SPI + help + Say yes here to build support for Analog Devices AD7293 + Power Amplifier Current Controller with + ADC, DACs, and Temperature and Current Sensors + + To compile this driver as a module, choose M here: the + module will be called ad7293. + config AD7303 tristate "Analog Devices AD7303 DAC driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 33e16f14902a..3c17246ee89b 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o +obj-$(CONFIG_AD7293) += ad7293.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c new file mode 100644 index 000000000000..59a38ca4c3c7 --- /dev/null +++ b/drivers/iio/dac/ad7293.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7293 driver + * + * Copyright 2021 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD7293_R1B BIT(16) +#define AD7293_R2B BIT(17) +#define AD7293_PAGE_ADDR_MSK GENMASK(15, 8) +#define AD7293_PAGE(x) FIELD_PREP(AD7293_PAGE_ADDR_MSK, x) + +/* AD7293 Register Map Common */ +#define AD7293_REG_NO_OP (AD7293_R1B | AD7293_PAGE(0x0) | 0x0) +#define AD7293_REG_PAGE_SELECT (AD7293_R1B | AD7293_PAGE(0x0) | 0x1) +#define AD7293_REG_CONV_CMD (AD7293_R2B | AD7293_PAGE(0x0) | 0x2) +#define AD7293_REG_RESULT (AD7293_R1B | AD7293_PAGE(0x0) | 0x3) +#define AD7293_REG_DAC_EN (AD7293_R1B | AD7293_PAGE(0x0) | 0x4) +#define AD7293_REG_DEVICE_ID (AD7293_R2B | AD7293_PAGE(0x0) | 0xC) +#define AD7293_REG_SOFT_RESET (AD7293_R2B | AD7293_PAGE(0x0) | 0xF) + +/* AD7293 Register Map Page 0x0 */ +#define AD7293_REG_VIN0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x10) +#define AD7293_REG_VIN1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x11) +#define AD7293_REG_VIN2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x12) +#define AD7293_REG_VIN3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x13) +#define AD7293_REG_TSENSE_INT (AD7293_R2B | AD7293_PAGE(0x0) | 0x20) +#define AD7293_REG_TSENSE_D0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x21) +#define AD7293_REG_TSENSE_D1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x22) +#define AD7293_REG_ISENSE_0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x28) +#define AD7293_REG_ISENSE_1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x29) +#define AD7293_REG_ISENSE_2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x2A) +#define AD7293_REG_ISENSE_3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x2B) +#define AD7293_REG_UNI_VOUT0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x30) +#define AD7293_REG_UNI_VOUT1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x31) +#define AD7293_REG_UNI_VOUT2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x32) +#define AD7293_REG_UNI_VOUT3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x33) +#define AD7293_REG_BI_VOUT0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x34) +#define AD7293_REG_BI_VOUT1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x35) +#define AD7293_REG_BI_VOUT2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x36) +#define AD7293_REG_BI_VOUT3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x37) + +/* AD7293 Register Map Page 0x2 */ +#define AD7293_REG_DIGITAL_OUT_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x11) +#define AD7293_REG_DIGITAL_INOUT_FUNC (AD7293_R2B | AD7293_PAGE(0x2) | 0x12) +#define AD7293_REG_DIGITAL_FUNC_POL (AD7293_R2B | AD7293_PAGE(0x2) | 0x13) +#define AD7293_REG_GENERAL (AD7293_R2B | AD7293_PAGE(0x2) | 0x14) +#define AD7293_REG_VINX_RANGE0 (AD7293_R2B | AD7293_PAGE(0x2) | 0x15) +#define AD7293_REG_VINX_RANGE1 (AD7293_R2B | AD7293_PAGE(0x2) | 0x16) +#define AD7293_REG_VINX_DIFF_SE (AD7293_R2B | AD7293_PAGE(0x2) | 0x17) +#define AD7293_REG_VINX_FILTER (AD7293_R2B | AD7293_PAGE(0x2) | 0x18) +#define AD7293_REG_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x19) +#define AD7293_REG_CONV_DELAY (AD7293_R2B | AD7293_PAGE(0x2) | 0x1A) +#define AD7293_REG_TSENSE_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x1B) +#define AD7293_REG_ISENSE_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x1C) +#define AD7293_REG_ISENSE_GAIN (AD7293_R2B | AD7293_PAGE(0x2) | 0x1D) +#define AD7293_REG_DAC_SNOOZE_O (AD7293_R2B | AD7293_PAGE(0x2) | 0x1F) +#define AD7293_REG_DAC_SNOOZE_1 (AD7293_R2B | AD7293_PAGE(0x2) | 0x20) +#define AD7293_REG_RSX_MON_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x23) +#define AD7293_REG_INTEGR_CL (AD7293_R2B | AD7293_PAGE(0x2) | 0x28) +#define AD7293_REG_PA_ON_CTRL (AD7293_R2B | AD7293_PAGE(0x2) | 0x29) +#define AD7293_REG_RAMP_TIME_0 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2A) +#define AD7293_REG_RAMP_TIME_1 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2B) +#define AD7293_REG_RAMP_TIME_2 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2C) +#define AD7293_REG_RAMP_TIME_3 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2D) +#define AD7293_REG_CL_FR_IT (AD7293_R2B | AD7293_PAGE(0x2) | 0x2E) +#define AD7293_REG_INTX_AVSS_AVDD (AD7293_R2B | AD7293_PAGE(0x2) | 0x2F) + +/* AD7293 Register Map Page 0x3 */ +#define AD7293_REG_VINX_SEQ (AD7293_R2B | AD7293_PAGE(0x3) | 0x10) +#define AD7293_REG_ISENSEX_TSENSEX_SEQ (AD7293_R2B | AD7293_PAGE(0x3) | 0x11) +#define AD7293_REG_RSX_MON_BI_VOUTX_SEQ (AD7293_R2B | AD7293_PAGE(0x3) | 0x12) + +/* AD7293 Register Map Page 0xE */ +#define AD7293_REG_VIN0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x10) +#define AD7293_REG_VIN1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x11) +#define AD7293_REG_VIN2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x12) +#define AD7293_REG_VIN3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x13) +#define AD7293_REG_TSENSE_INT_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x20) +#define AD7293_REG_TSENSE_D0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x21) +#define AD7293_REG_TSENSE_D1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x22) +#define AD7293_REG_ISENSE0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x28) +#define AD7293_REG_ISENSE1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x29) +#define AD7293_REG_ISENSE2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x2A) +#define AD7293_REG_ISENSE3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x2B) +#define AD7293_REG_UNI_VOUT0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x30) +#define AD7293_REG_UNI_VOUT1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x31) +#define AD7293_REG_UNI_VOUT2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x32) +#define AD7293_REG_UNI_VOUT3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x33) +#define AD7293_REG_BI_VOUT0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x34) +#define AD7293_REG_BI_VOUT1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x35) +#define AD7293_REG_BI_VOUT2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x36) +#define AD7293_REG_BI_VOUT3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x37) + +/* AD7293 Miscellaneous Definitions */ +#define AD7293_READ BIT(7) +#define AD7293_TRANSF_LEN_MSK GENMASK(17, 16) + +#define AD7293_REG_ADDR_MSK GENMASK(7, 0) +#define AD7293_REG_VOUT_OFFSET_MSK GENMASK(5, 4) +#define AD7293_REG_DATA_RAW_MSK GENMASK(15, 4) +#define AD7293_REG_VINX_RANGE_GET_CH_MSK(x, ch) (((x) >> (ch)) & 0x1) +#define AD7293_REG_VINX_RANGE_SET_CH_MSK(x, ch) (((x) & 0x1) << (ch)) +#define AD7293_CHIP_ID 0x18 + +enum ad7293_ch_type { + AD7293_ADC_VINX, + AD7293_ADC_TSENSE, + AD7293_ADC_ISENSE, + AD7293_DAC, +}; + +enum ad7293_max_offset { + AD7293_TSENSE_MIN_OFFSET_CH = 4, + AD7293_ISENSE_MIN_OFFSET_CH = 7, + AD7293_VOUT_MIN_OFFSET_CH = 11, + AD7293_VOUT_MAX_OFFSET_CH = 18, +}; + +static const int dac_offset_table[] = {0, 1, 2}; + +static const int isense_gain_table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + +static const int adc_range_table[] = {0, 1, 2, 3}; + +struct ad7293_state { + struct spi_device *spi; + /* Protect against concurrent accesses to the device, page selection and data content */ + struct mutex lock; + struct gpio_desc *gpio_reset; + struct regulator *reg_avdd; + struct regulator *reg_vdrive; + u8 page_select; + u8 data[3] ____cacheline_aligned; +}; + +static int ad7293_page_select(struct ad7293_state *st, unsigned int reg) +{ + int ret; + + if (st->page_select != FIELD_GET(AD7293_PAGE_ADDR_MSK, reg)) { + st->data[0] = FIELD_GET(AD7293_REG_ADDR_MSK, AD7293_REG_PAGE_SELECT); + st->data[1] = FIELD_GET(AD7293_PAGE_ADDR_MSK, reg); + + ret = spi_write(st->spi, &st->data[0], 2); + if (ret) + return ret; + + st->page_select = FIELD_GET(AD7293_PAGE_ADDR_MSK, reg); + } + + return 0; +} + +static int __ad7293_spi_read(struct ad7293_state *st, unsigned int reg, + u16 *val) +{ + int ret; + unsigned int length; + struct spi_transfer t = {0}; + + length = FIELD_GET(AD7293_TRANSF_LEN_MSK, reg); + + ret = ad7293_page_select(st, reg); + if (ret) + return ret; + + st->data[0] = AD7293_READ | FIELD_GET(AD7293_REG_ADDR_MSK, reg); + st->data[1] = 0x0; + st->data[2] = 0x0; + + t.tx_buf = &st->data[0]; + t.rx_buf = &st->data[0]; + t.len = length + 1; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + if (length == 1) + *val = st->data[1]; + else + *val = get_unaligned_be16(&st->data[1]); + + return 0; +} + +static int ad7293_spi_read(struct ad7293_state *st, unsigned int reg, + u16 *val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __ad7293_spi_read(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __ad7293_spi_write(struct ad7293_state *st, unsigned int reg, + u16 val) +{ + int ret; + unsigned int length; + + length = FIELD_GET(AD7293_TRANSF_LEN_MSK, reg); + + ret = ad7293_page_select(st, reg); + if (ret) + return ret; + + st->data[0] = FIELD_GET(AD7293_REG_ADDR_MSK, reg); + + if (length == 1) + st->data[1] = val; + else + put_unaligned_be16(val, &st->data[1]); + + return spi_write(st->spi, &st->data[0], length + 1); +} + +static int ad7293_spi_write(struct ad7293_state *st, unsigned int reg, + u16 val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __ad7293_spi_write(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __ad7293_spi_update_bits(struct ad7293_state *st, unsigned int reg, + u16 mask, u16 val) +{ + int ret; + u16 data, temp; + + ret = __ad7293_spi_read(st, reg, &data); + if (ret) + return ret; + + temp = (data & ~mask) | (val & mask); + + return __ad7293_spi_write(st, reg, temp); +} + +static int ad7293_spi_update_bits(struct ad7293_state *st, unsigned int reg, + u16 mask, u16 val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __ad7293_spi_update_bits(st, reg, mask, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_adc_get_scale(struct ad7293_state *st, unsigned int ch, + u16 *range) +{ + int ret; + u16 data; + + mutex_lock(&st->lock); + + ret = __ad7293_spi_read(st, AD7293_REG_VINX_RANGE1, &data); + if (ret) + goto exit; + + *range = AD7293_REG_VINX_RANGE_GET_CH_MSK(data, ch); + + ret = __ad7293_spi_read(st, AD7293_REG_VINX_RANGE0, &data); + if (ret) + goto exit; + + *range |= AD7293_REG_VINX_RANGE_GET_CH_MSK(data, ch) << 1; + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_adc_set_scale(struct ad7293_state *st, unsigned int ch, + u16 range) +{ + int ret; + unsigned int ch_msk = BIT(ch); + + mutex_lock(&st->lock); + ret = __ad7293_spi_update_bits(st, AD7293_REG_VINX_RANGE1, ch_msk, + AD7293_REG_VINX_RANGE_SET_CH_MSK(range, ch)); + if (ret) + goto exit; + + ret = __ad7293_spi_update_bits(st, AD7293_REG_VINX_RANGE0, ch_msk, + AD7293_REG_VINX_RANGE_SET_CH_MSK((range >> 1), ch)); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_get_offset(struct ad7293_state *st, unsigned int ch, + u16 *offset) +{ + if (ch < AD7293_TSENSE_MIN_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_VIN0_OFFSET + ch, offset); + else if (ch < AD7293_ISENSE_MIN_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_TSENSE_INT_OFFSET + (ch - 4), offset); + else if (ch < AD7293_VOUT_MIN_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_ISENSE0_OFFSET + (ch - 7), offset); + else if (ch <= AD7293_VOUT_MAX_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_UNI_VOUT0_OFFSET + (ch - 11), offset); + + return -EINVAL; +} + +static int ad7293_set_offset(struct ad7293_state *st, unsigned int ch, + u16 offset) +{ + if (ch < AD7293_TSENSE_MIN_OFFSET_CH) + return ad7293_spi_write(st, AD7293_REG_VIN0_OFFSET + ch, + offset); + else if (ch < AD7293_ISENSE_MIN_OFFSET_CH) + return ad7293_spi_write(st, + AD7293_REG_TSENSE_INT_OFFSET + + (ch - AD7293_TSENSE_MIN_OFFSET_CH), + offset); + else if (ch < AD7293_VOUT_MIN_OFFSET_CH) + return ad7293_spi_write(st, + AD7293_REG_ISENSE0_OFFSET + + (ch - AD7293_ISENSE_MIN_OFFSET_CH), + offset); + else if (ch <= AD7293_VOUT_MAX_OFFSET_CH) + return ad7293_spi_update_bits(st, + AD7293_REG_UNI_VOUT0_OFFSET + + (ch - AD7293_VOUT_MIN_OFFSET_CH), + AD7293_REG_VOUT_OFFSET_MSK, + FIELD_PREP(AD7293_REG_VOUT_OFFSET_MSK, offset)); + + return -EINVAL; +} + +static int ad7293_isense_set_scale(struct ad7293_state *st, unsigned int ch, + u16 gain) +{ + unsigned int ch_msk = (0xf << (4 * ch)); + + return ad7293_spi_update_bits(st, AD7293_REG_ISENSE_GAIN, ch_msk, + gain << (4 * ch)); +} + +static int ad7293_isense_get_scale(struct ad7293_state *st, unsigned int ch, + u16 *gain) +{ + int ret; + + ret = ad7293_spi_read(st, AD7293_REG_ISENSE_GAIN, gain); + if (ret) + return ret; + + *gain = (*gain >> (4 * ch)) & 0xf; + + return ret; +} + +static int ad7293_dac_write_raw(struct ad7293_state *st, unsigned int ch, + u16 raw) +{ + int ret; + + mutex_lock(&st->lock); + + ret = __ad7293_spi_update_bits(st, AD7293_REG_DAC_EN, BIT(ch), BIT(ch)); + if (ret) + goto exit; + + ret = __ad7293_spi_write(st, AD7293_REG_UNI_VOUT0 + ch, + FIELD_PREP(AD7293_REG_DATA_RAW_MSK, raw)); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_ch_read_raw(struct ad7293_state *st, enum ad7293_ch_type type, + unsigned int ch, u16 *raw) +{ + int ret; + unsigned int reg_wr, reg_rd, data_wr; + + switch (type) { + case AD7293_ADC_VINX: + reg_wr = AD7293_REG_VINX_SEQ; + reg_rd = AD7293_REG_VIN0 + ch; + data_wr = BIT(ch); + + break; + case AD7293_ADC_TSENSE: + reg_wr = AD7293_REG_ISENSEX_TSENSEX_SEQ; + reg_rd = AD7293_REG_TSENSE_INT + ch; + data_wr = BIT(ch); + + break; + case AD7293_ADC_ISENSE: + reg_wr = AD7293_REG_ISENSEX_TSENSEX_SEQ; + reg_rd = AD7293_REG_ISENSE_0 + ch; + data_wr = BIT(ch) << 8; + + break; + case AD7293_DAC: + reg_rd = AD7293_REG_UNI_VOUT0 + ch; + + break; + default: + return -EINVAL; + } + + mutex_lock(&st->lock); + + if (type != AD7293_DAC) { + if (type == AD7293_ADC_TSENSE) { + ret = __ad7293_spi_write(st, AD7293_REG_TSENSE_BG_EN, + BIT(ch)); + if (ret) + goto exit; + + usleep_range(9000, 9900); + } else if (type == AD7293_ADC_ISENSE) { + ret = __ad7293_spi_write(st, AD7293_REG_ISENSE_BG_EN, + BIT(ch)); + if (ret) + goto exit; + + usleep_range(2000, 7000); + } + + ret = __ad7293_spi_write(st, reg_wr, data_wr); + if (ret) + goto exit; + + ret = __ad7293_spi_write(st, AD7293_REG_CONV_CMD, 0x82); + if (ret) + goto exit; + } + + ret = __ad7293_spi_read(st, reg_rd, raw); + + *raw = FIELD_GET(AD7293_REG_DATA_RAW_MSK, *raw); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7293_state *st = iio_priv(indio_dev); + int ret; + u16 data; + + switch (info) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) + ret = ad7293_ch_read_raw(st, AD7293_DAC, + chan->channel, &data); + else + ret = ad7293_ch_read_raw(st, AD7293_ADC_VINX, + chan->channel, &data); + + break; + case IIO_CURRENT: + ret = ad7293_ch_read_raw(st, AD7293_ADC_ISENSE, + chan->channel, &data); + + break; + case IIO_TEMP: + ret = ad7293_ch_read_raw(st, AD7293_ADC_TSENSE, + chan->channel, &data); + + break; + default: + return -EINVAL; + } + + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) { + ret = ad7293_get_offset(st, + chan->channel + AD7293_VOUT_MIN_OFFSET_CH, + &data); + + data = FIELD_GET(AD7293_REG_VOUT_OFFSET_MSK, data); + } else { + ret = ad7293_get_offset(st, chan->channel, &data); + } + + break; + case IIO_CURRENT: + ret = ad7293_get_offset(st, + chan->channel + AD7293_ISENSE_MIN_OFFSET_CH, + &data); + + break; + case IIO_TEMP: + ret = ad7293_get_offset(st, + chan->channel + AD7293_TSENSE_MIN_OFFSET_CH, + &data); + + break; + default: + return -EINVAL; + } + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + ret = ad7293_adc_get_scale(st, chan->channel, &data); + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_CURRENT: + ret = ad7293_isense_get_scale(st, chan->channel, &data); + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_TEMP: + *val = 1; + *val2 = 8; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad7293_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7293_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + if (!chan->output) + return -EINVAL; + + return ad7293_dac_write_raw(st, chan->channel, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) + return ad7293_set_offset(st, + chan->channel + + AD7293_VOUT_MIN_OFFSET_CH, + val); + else + return ad7293_set_offset(st, chan->channel, val); + case IIO_CURRENT: + return ad7293_set_offset(st, + chan->channel + + AD7293_ISENSE_MIN_OFFSET_CH, + val); + case IIO_TEMP: + return ad7293_set_offset(st, + chan->channel + + AD7293_TSENSE_MIN_OFFSET_CH, + val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + return ad7293_adc_set_scale(st, chan->channel, val); + case IIO_CURRENT: + return ad7293_isense_set_scale(st, chan->channel, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad7293_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct ad7293_state *st = iio_priv(indio_dev); + int ret; + + if (read_val) { + u16 temp; + ret = ad7293_spi_read(st, reg, &temp); + *read_val = temp; + } else { + ret = ad7293_spi_write(st, reg, (u16)write_val); + } + + return ret; +} + +static int ad7293_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_OFFSET: + *vals = dac_offset_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(dac_offset_table); + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT; + + switch (chan->type) { + case IIO_VOLTAGE: + *vals = adc_range_table; + *length = ARRAY_SIZE(adc_range_table); + return IIO_AVAIL_LIST; + case IIO_CURRENT: + *vals = isense_gain_table; + *length = ARRAY_SIZE(isense_gain_table); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +#define AD7293_CHAN_ADC(_channel) { \ + .type = IIO_VOLTAGE, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) \ +} + +#define AD7293_CHAN_DAC(_channel) { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OFFSET) \ +} + +#define AD7293_CHAN_ISENSE(_channel) { \ + .type = IIO_CURRENT, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) \ +} + +#define AD7293_CHAN_TEMP(_channel) { \ + .type = IIO_TEMP, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ +} + +static const struct iio_chan_spec ad7293_channels[] = { + AD7293_CHAN_ADC(0), + AD7293_CHAN_ADC(1), + AD7293_CHAN_ADC(2), + AD7293_CHAN_ADC(3), + AD7293_CHAN_ISENSE(0), + AD7293_CHAN_ISENSE(1), + AD7293_CHAN_ISENSE(2), + AD7293_CHAN_ISENSE(3), + AD7293_CHAN_TEMP(0), + AD7293_CHAN_TEMP(1), + AD7293_CHAN_TEMP(2), + AD7293_CHAN_DAC(0), + AD7293_CHAN_DAC(1), + AD7293_CHAN_DAC(2), + AD7293_CHAN_DAC(3), + AD7293_CHAN_DAC(4), + AD7293_CHAN_DAC(5), + AD7293_CHAN_DAC(6), + AD7293_CHAN_DAC(7) +}; + +static int ad7293_soft_reset(struct ad7293_state *st) +{ + int ret; + + ret = __ad7293_spi_write(st, AD7293_REG_SOFT_RESET, 0x7293); + if (ret) + return ret; + + return __ad7293_spi_write(st, AD7293_REG_SOFT_RESET, 0x0000); +} + +static int ad7293_reset(struct ad7293_state *st) +{ + if (st->gpio_reset) { + gpiod_set_value(st->gpio_reset, 0); + usleep_range(100, 1000); + gpiod_set_value(st->gpio_reset, 1); + usleep_range(100, 1000); + + return 0; + } + + /* Perform a software reset */ + return ad7293_soft_reset(st); +} + +static int ad7293_properties_parse(struct ad7293_state *st) +{ + struct spi_device *spi = st->spi; + + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_reset), + "failed to get the reset GPIO\n"); + + st->reg_avdd = devm_regulator_get(&spi->dev, "avdd"); + if (IS_ERR(st->reg_avdd)) + return dev_err_probe(&spi->dev, PTR_ERR(st->reg_avdd), + "failed to get the AVDD voltage\n"); + + st->reg_vdrive = devm_regulator_get(&spi->dev, "vdrive"); + if (IS_ERR(st->reg_vdrive)) + return dev_err_probe(&spi->dev, PTR_ERR(st->reg_vdrive), + "failed to get the VDRIVE voltage\n"); + + return 0; +} + +static void ad7293_reg_disable(void *data) +{ + regulator_disable(data); +} + +static int ad7293_init(struct ad7293_state *st) +{ + int ret; + u16 chip_id; + struct spi_device *spi = st->spi; + + ret = ad7293_properties_parse(st); + if (ret) + return ret; + + ret = ad7293_reset(st); + if (ret) + return ret; + + ret = regulator_enable(st->reg_avdd); + if (ret) { + dev_err(&spi->dev, + "Failed to enable specified AVDD Voltage!\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, + st->reg_avdd); + if (ret) + return ret; + + ret = regulator_enable(st->reg_vdrive); + if (ret) { + dev_err(&spi->dev, + "Failed to enable specified VDRIVE Voltage!\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, + st->reg_vdrive); + if (ret) + return ret; + + ret = regulator_get_voltage(st->reg_avdd); + if (ret < 0) { + dev_err(&spi->dev, "Failed to read avdd regulator: %d\n", ret); + return ret; + } + + if (ret > 5500000 || ret < 4500000) + return -EINVAL; + + ret = regulator_get_voltage(st->reg_vdrive); + if (ret < 0) { + dev_err(&spi->dev, + "Failed to read vdrive regulator: %d\n", ret); + return ret; + } + if (ret > 5500000 || ret < 1700000) + return -EINVAL; + + /* Check Chip ID */ + ret = __ad7293_spi_read(st, AD7293_REG_DEVICE_ID, &chip_id); + if (ret) + return ret; + + if (chip_id != AD7293_CHIP_ID) { + dev_err(&spi->dev, "Invalid Chip ID.\n"); + return -EINVAL; + } + + return 0; +} + +static const struct iio_info ad7293_info = { + .read_raw = ad7293_read_raw, + .write_raw = ad7293_write_raw, + .read_avail = &ad7293_read_avail, + .debugfs_reg_access = &ad7293_reg_access, +}; + +static int ad7293_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad7293_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + indio_dev->info = &ad7293_info; + indio_dev->name = "ad7293"; + indio_dev->channels = ad7293_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7293_channels); + + st->spi = spi; + st->page_select = 0; + + mutex_init(&st->lock); + + ret = ad7293_init(st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7293_id[] = { + { "ad7293", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7293_id); + +static const struct of_device_id ad7293_of_match[] = { + { .compatible = "adi,ad7293" }, + {} +}; +MODULE_DEVICE_TABLE(of, ad7293_of_match); + +static struct spi_driver ad7293_driver = { + .driver = { + .name = "ad7293", + .of_match_table = ad7293_of_match, + }, + .probe = ad7293_probe, + .id_table = ad7293_id, +}; +module_spi_driver(ad7293_driver); + +MODULE_AUTHOR("Antoniu Miclaus Date: Sun, 5 Dec 2021 13:40:43 +0200 Subject: iio: add addac subdirectory For IIO devices that expose both ADC and DAC functionality. Signed-off-by: Cosmin Tanislav Link: https://lore.kernel.org/r/20211205114045.173612-2-cosmin.tanislav@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/addac/Kconfig | 8 ++++++++ drivers/iio/addac/Makefile | 6 ++++++ 4 files changed, 16 insertions(+) create mode 100644 drivers/iio/addac/Kconfig create mode 100644 drivers/iio/addac/Makefile (limited to 'drivers/iio') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 2334ad249b46..4fb4321a72cb 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -70,6 +70,7 @@ config IIO_TRIGGERED_EVENT source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" +source "drivers/iio/addac/Kconfig" source "drivers/iio/afe/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/cdc/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 65e39bd4f934..8d48c70fee4d 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o obj-y += accel/ obj-y += adc/ +obj-y += addac/ obj-y += afe/ obj-y += amplifiers/ obj-y += buffer/ diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig new file mode 100644 index 000000000000..2e64d7755d5e --- /dev/null +++ b/drivers/iio/addac/Kconfig @@ -0,0 +1,8 @@ +# +# ADC DAC drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Analog to digital and digital to analog converters" + +endmenu diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile new file mode 100644 index 000000000000..b888b9ee12da --- /dev/null +++ b/drivers/iio/addac/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O ADDAC drivers +# + +# When adding new entries keep the list in alphabetical order -- cgit From fea251b6a5dbdf8ba8af64abcd013d66ab6b05ee Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 5 Dec 2021 13:40:45 +0200 Subject: iio: addac: add AD74413R driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AD74412R and AD74413R are quad-channel, software configurable, input/output solutions for building and process control applications. They contain functionality for analog output, analog input, digital input, resistance temperature detector, and thermocouple measurements integrated into a single chip solution with an SPI interface. The devices feature a 16-bit ADC and four configurable 13-bit DACs to provide four configurable input/output channels and a suite of diagnostic functions. The AD74413R differentiates itself from the AD74412R by being HART-compatible. When configured with channel 0 as voltage output, channel 1 as current output, channel 2 as voltage input and channel 3 as current input, the following structure is created under the corresponding IIO device. . ├── in_current0_offset ├── in_current0_raw ├── in_current0_sampling_frequency ├── in_current0_sampling_frequency_available ├── in_current0_scale ├── in_voltage1_offset ├── in_voltage1_raw ├── in_voltage1_sampling_frequency ├── in_voltage1_sampling_frequency_available ├── in_voltage1_scale ├── in_voltage2_offset ├── in_voltage2_raw ├── in_voltage2_sampling_frequency ├── in_voltage2_sampling_frequency_available ├── in_voltage2_scale ├── in_current3_offset ├── in_current3_raw ├── in_current3_sampling_frequency ├── in_current3_sampling_frequency_available ├── in_current3_scale ├── out_voltage0_raw ├── out_voltage0_scale ├── out_current1_raw ├── out_current1_scale ├── name ├── buffer │   ├── data_available │   ├── enable │   ├── length │   └── watermark └── scan_elements    ├── in_current0_en    ├── in_current0_index    ├── in_current0_type    ├── in_voltage1_en    ├── in_voltage1_index    ├── in_voltage1_type    ├── in_voltage2_en    ├── in_voltage2_index    ├── in_voltage2_type    ├── in_current3_en    ├── in_current3_index    └── in_current3_type Signed-off-by: Cosmin Tanislav Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20211205114045.173612-4-cosmin.tanislav@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/addac/Kconfig | 12 + drivers/iio/addac/Makefile | 1 + drivers/iio/addac/ad74413r.c | 1475 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1488 insertions(+) create mode 100644 drivers/iio/addac/ad74413r.c (limited to 'drivers/iio') diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index 2e64d7755d5e..138492362f20 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -5,4 +5,16 @@ menu "Analog to digital and digital to analog converters" +config AD74413R + tristate "Analog Devices AD74412R/AD74413R driver" + depends on GPIOLIB && SPI + select REGMAP_SPI + select CRC8 + help + Say yes here to build support for Analog Devices AD74412R/AD74413R + quad-channel software configurable input/output solution. + + To compile this driver as a module, choose M here: the + module will be called ad74413r. + endmenu diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile index b888b9ee12da..cfd4bbe64ad3 100644 --- a/drivers/iio/addac/Makefile +++ b/drivers/iio/addac/Makefile @@ -4,3 +4,4 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD74413R) += ad74413r.o diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c new file mode 100644 index 000000000000..cbd9aa9b399a --- /dev/null +++ b/drivers/iio/addac/ad74413r.c @@ -0,0 +1,1475 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD74413R_CRC_POLYNOMIAL 0x7 +DECLARE_CRC8_TABLE(ad74413r_crc8_table); + +#define AD74413R_CHANNEL_MAX 4 + +#define AD74413R_FRAME_SIZE 4 + +struct ad74413r_chip_info { + const char *name; + bool hart_support; +}; + +struct ad74413r_channel_config { + u32 func; + bool gpo_comparator; + bool initialized; +}; + +struct ad74413r_channels { + struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ad74413r_state { + struct ad74413r_channel_config channel_configs[AD74413R_CHANNEL_MAX]; + unsigned int gpo_gpio_offsets[AD74413R_CHANNEL_MAX]; + unsigned int comp_gpio_offsets[AD74413R_CHANNEL_MAX]; + struct gpio_chip gpo_gpiochip; + struct gpio_chip comp_gpiochip; + struct completion adc_data_completion; + unsigned int num_gpo_gpios; + unsigned int num_comparator_gpios; + u32 sense_resistor_ohms; + + /* + * Synchronize consecutive operations when doing a one-shot + * conversion and when updating the ADC samples SPI message. + */ + struct mutex lock; + + const struct ad74413r_chip_info *chip_info; + struct spi_device *spi; + struct regulator *refin_reg; + struct regmap *regmap; + struct device *dev; + struct iio_trigger *trig; + + size_t adc_active_channels; + struct spi_message adc_samples_msg; + struct spi_transfer adc_samples_xfer[AD74413R_CHANNEL_MAX + 1]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + struct { + u8 rx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; + s64 timestamp; + } adc_samples_buf ____cacheline_aligned; + + u8 adc_samples_tx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; + u8 reg_tx_buf[AD74413R_FRAME_SIZE]; + u8 reg_rx_buf[AD74413R_FRAME_SIZE]; +}; + +#define AD74413R_REG_NOP 0x00 + +#define AD74413R_REG_CH_FUNC_SETUP_X(x) (0x01 + (x)) +#define AD74413R_CH_FUNC_SETUP_MASK GENMASK(3, 0) + +#define AD74413R_REG_ADC_CONFIG_X(x) (0x05 + (x)) +#define AD74413R_ADC_CONFIG_RANGE_MASK GENMASK(7, 5) +#define AD74413R_ADC_CONFIG_REJECTION_MASK GENMASK(4, 3) +#define AD74413R_ADC_RANGE_10V 0b000 +#define AD74413R_ADC_RANGE_2P5V_EXT_POW 0b001 +#define AD74413R_ADC_RANGE_2P5V_INT_POW 0b010 +#define AD74413R_ADC_RANGE_5V_BI_DIR 0b011 +#define AD74413R_ADC_REJECTION_50_60 0b00 +#define AD74413R_ADC_REJECTION_NONE 0b01 +#define AD74413R_ADC_REJECTION_50_60_HART 0b10 +#define AD74413R_ADC_REJECTION_HART 0b11 + +#define AD74413R_REG_DIN_CONFIG_X(x) (0x09 + (x)) +#define AD74413R_DIN_DEBOUNCE_MASK GENMASK(4, 0) +#define AD74413R_DIN_DEBOUNCE_LEN BIT(5) + +#define AD74413R_REG_DAC_CODE_X(x) (0x16 + (x)) +#define AD74413R_DAC_CODE_MAX GENMASK(12, 0) +#define AD74413R_DAC_VOLTAGE_MAX 11000 + +#define AD74413R_REG_GPO_PAR_DATA 0x0d +#define AD74413R_REG_GPO_CONFIG_X(x) (0x0e + (x)) +#define AD74413R_GPO_CONFIG_DATA_MASK BIT(3) +#define AD74413R_GPO_CONFIG_SELECT_MASK GENMASK(2, 0) +#define AD74413R_GPO_CONFIG_100K_PULL_DOWN 0b000 +#define AD74413R_GPO_CONFIG_LOGIC 0b001 +#define AD74413R_GPO_CONFIG_LOGIC_PARALLEL 0b010 +#define AD74413R_GPO_CONFIG_COMPARATOR 0b011 +#define AD74413R_GPO_CONFIG_HIGH_IMPEDANCE 0b100 + +#define AD74413R_REG_ADC_CONV_CTRL 0x23 +#define AD74413R_CONV_SEQ_MASK GENMASK(9, 8) +#define AD74413R_CONV_SEQ_ON 0b00 +#define AD74413R_CONV_SEQ_SINGLE 0b01 +#define AD74413R_CONV_SEQ_CONTINUOUS 0b10 +#define AD74413R_CONV_SEQ_OFF 0b11 +#define AD74413R_CH_EN_MASK(x) BIT(x) + +#define AD74413R_REG_DIN_COMP_OUT 0x25 +#define AD74413R_DIN_COMP_OUT_SHIFT_X(x) x + +#define AD74413R_REG_ADC_RESULT_X(x) (0x26 + (x)) +#define AD74413R_ADC_RESULT_MAX GENMASK(15, 0) + +#define AD74413R_REG_READ_SELECT 0x41 + +#define AD74413R_REG_CMD_KEY 0x44 +#define AD74413R_CMD_KEY_LDAC 0x953a +#define AD74413R_CMD_KEY_RESET1 0x15fa +#define AD74413R_CMD_KEY_RESET2 0xaf51 + +static const int ad74413r_adc_sampling_rates[] = { + 20, 4800, +}; + +static const int ad74413r_adc_sampling_rates_hart[] = { + 10, 20, 1200, 4800, +}; + +static int ad74413r_crc(u8 *buf) +{ + return crc8(ad74413r_crc8_table, buf, 3, 0); +} + +static void ad74413r_format_reg_write(u8 reg, u16 val, u8 *buf) +{ + buf[0] = reg; + put_unaligned_be16(val, &buf[1]); + buf[3] = ad74413r_crc(buf); +} + +static int ad74413r_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ad74413r_state *st = context; + + ad74413r_format_reg_write(reg, val, st->reg_tx_buf); + + return spi_write(st->spi, st->reg_tx_buf, AD74413R_FRAME_SIZE); +} + +static int ad74413r_crc_check(struct ad74413r_state *st, u8 *buf) +{ + u8 expected_crc = ad74413r_crc(buf); + + if (buf[3] != expected_crc) { + dev_err(st->dev, "Bad CRC %02x for %02x%02x%02x\n", + buf[3], buf[0], buf[1], buf[2]); + return -EINVAL; + } + + return 0; +} + +static int ad74413r_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ad74413r_state *st = context; + struct spi_transfer reg_read_xfer[] = { + { + .tx_buf = st->reg_tx_buf, + .len = AD74413R_FRAME_SIZE, + .cs_change = 1, + }, + { + .rx_buf = st->reg_rx_buf, + .len = AD74413R_FRAME_SIZE, + }, + }; + int ret; + + ad74413r_format_reg_write(AD74413R_REG_READ_SELECT, reg, + st->reg_tx_buf); + + ret = spi_sync_transfer(st->spi, reg_read_xfer, + ARRAY_SIZE(reg_read_xfer)); + if (ret) + return ret; + + ret = ad74413r_crc_check(st, st->reg_rx_buf); + if (ret) + return ret; + + *val = get_unaligned_be16(&st->reg_rx_buf[1]); + + return 0; +} + +static const struct regmap_config ad74413r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .reg_read = ad74413r_reg_read, + .reg_write = ad74413r_reg_write, +}; + +static int ad74413r_set_gpo_config(struct ad74413r_state *st, + unsigned int offset, u8 mode) +{ + return regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(offset), + AD74413R_GPO_CONFIG_SELECT_MASK, mode); +} + +static const unsigned int ad74413r_debounce_map[AD74413R_DIN_DEBOUNCE_LEN] = { + 0, 13, 18, 24, 32, 42, 56, 75, + 100, 130, 180, 240, 320, 420, 560, 750, + 1000, 1300, 1800, 2400, 3200, 4200, 5600, 7500, + 10000, 13000, 18000, 24000, 32000, 42000, 56000, 75000, +}; + +static int ad74413r_set_comp_debounce(struct ad74413r_state *st, + unsigned int offset, + unsigned int debounce) +{ + unsigned int val = AD74413R_DIN_DEBOUNCE_LEN - 1; + unsigned int i; + + for (i = 0; i < AD74413R_DIN_DEBOUNCE_LEN; i++) + if (debounce <= ad74413r_debounce_map[i]) { + val = i; + break; + } + + return regmap_update_bits(st->regmap, + AD74413R_REG_DIN_CONFIG_X(offset), + AD74413R_DIN_DEBOUNCE_MASK, + val); +} + +static void ad74413r_gpio_set(struct gpio_chip *chip, + unsigned int offset, int val) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->gpo_gpio_offsets[offset]; + int ret; + + ret = ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_LOGIC); + if (ret) + return; + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(real_offset), + AD74413R_GPO_CONFIG_DATA_MASK, + val ? AD74413R_GPO_CONFIG_DATA_MASK : 0); +} + +static void ad74413r_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned long real_mask = 0; + unsigned long real_bits = 0; + unsigned int offset = 0; + int ret; + + for_each_set_bit_from(offset, mask, AD74413R_CHANNEL_MAX) { + unsigned int real_offset = st->gpo_gpio_offsets[offset]; + + ret = ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_LOGIC_PARALLEL); + if (ret) + return; + + real_mask |= BIT(real_offset); + if (*bits & offset) + real_bits |= BIT(real_offset); + } + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_PAR_DATA, + real_mask, real_bits); +} + +static int ad74413r_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->comp_gpio_offsets[offset]; + unsigned int status; + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_DIN_COMP_OUT, &status); + if (ret) + return ret; + + status &= AD74413R_DIN_COMP_OUT_SHIFT_X(real_offset); + + return status ? 1 : 0; +} + +static int ad74413r_gpio_get_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int offset = 0; + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_DIN_COMP_OUT, &val); + if (ret) + return ret; + + for_each_set_bit_from(offset, mask, AD74413R_CHANNEL_MAX) { + unsigned int real_offset = st->comp_gpio_offsets[offset]; + + if (val & BIT(real_offset)) + *bits |= offset; + } + + return ret; +} + +static int ad74413r_gpio_get_gpo_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ad74413r_gpio_get_comp_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_IN; +} + +static int ad74413r_gpio_set_gpo_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->gpo_gpio_offsets[offset]; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_PULL_DOWN: + return ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_100K_PULL_DOWN); + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + return ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_HIGH_IMPEDANCE); + default: + return -ENOTSUPP; + } +} + +static int ad74413r_gpio_set_comp_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->comp_gpio_offsets[offset]; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_INPUT_DEBOUNCE: + return ad74413r_set_comp_debounce(st, real_offset, + pinconf_to_config_argument(config)); + default: + return -ENOTSUPP; + } +} + +static int ad74413r_reset(struct ad74413r_state *st) +{ + int ret; + + ret = regmap_write(st->regmap, AD74413R_REG_CMD_KEY, + AD74413R_CMD_KEY_RESET1); + if (ret) + return ret; + + return regmap_write(st->regmap, AD74413R_REG_CMD_KEY, + AD74413R_CMD_KEY_RESET2); +} + +static int ad74413r_set_channel_dac_code(struct ad74413r_state *st, + unsigned int channel, int dac_code) +{ + struct reg_sequence reg_seq[2] = { + { AD74413R_REG_DAC_CODE_X(channel), dac_code }, + { AD74413R_REG_CMD_KEY, AD74413R_CMD_KEY_LDAC }, + }; + + return regmap_multi_reg_write(st->regmap, reg_seq, 2); +} + +static int ad74413r_set_channel_function(struct ad74413r_state *st, + unsigned int channel, u8 func) +{ + return regmap_update_bits(st->regmap, + AD74413R_REG_CH_FUNC_SETUP_X(channel), + AD74413R_CH_FUNC_SETUP_MASK, func); +} + +static int ad74413r_set_adc_conv_seq(struct ad74413r_state *st, + unsigned int status) +{ + int ret; + + /* + * These bits do not clear when a conversion completes. + * To enable a subsequent conversion, repeat the write. + */ + ret = regmap_write_bits(st->regmap, AD74413R_REG_ADC_CONV_CTRL, + AD74413R_CONV_SEQ_MASK, + FIELD_PREP(AD74413R_CONV_SEQ_MASK, status)); + if (ret) + return ret; + + /* + * Wait 100us before starting conversions. + */ + usleep_range(100, 120); + + return 0; +} + +static int ad74413r_set_adc_channel_enable(struct ad74413r_state *st, + unsigned int channel, + bool status) +{ + return regmap_update_bits(st->regmap, AD74413R_REG_ADC_CONV_CTRL, + AD74413R_CH_EN_MASK(channel), + status ? AD74413R_CH_EN_MASK(channel) : 0); +} + +static int ad74413r_get_adc_range(struct ad74413r_state *st, + unsigned int channel, + unsigned int *val) +{ + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val = FIELD_GET(AD74413R_ADC_CONFIG_RANGE_MASK, *val); + + return 0; +} + +static int ad74413r_get_adc_rejection(struct ad74413r_state *st, + unsigned int channel, + unsigned int *val) +{ + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val = FIELD_GET(AD74413R_ADC_CONFIG_REJECTION_MASK, *val); + + return 0; +} + +static int ad74413r_set_adc_rejection(struct ad74413r_state *st, + unsigned int channel, + unsigned int val) +{ + return regmap_update_bits(st->regmap, + AD74413R_REG_ADC_CONFIG_X(channel), + AD74413R_ADC_CONFIG_REJECTION_MASK, + FIELD_PREP(AD74413R_ADC_CONFIG_REJECTION_MASK, + val)); +} + +static int ad74413r_rejection_to_rate(struct ad74413r_state *st, + unsigned int rej, int *val) +{ + switch (rej) { + case AD74413R_ADC_REJECTION_50_60: + *val = 20; + return 0; + case AD74413R_ADC_REJECTION_NONE: + *val = 4800; + return 0; + case AD74413R_ADC_REJECTION_50_60_HART: + *val = 10; + return 0; + case AD74413R_ADC_REJECTION_HART: + *val = 1200; + return 0; + default: + dev_err(st->dev, "ADC rejection invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_rate_to_rejection(struct ad74413r_state *st, + int rate, unsigned int *val) +{ + switch (rate) { + case 20: + *val = AD74413R_ADC_REJECTION_50_60; + return 0; + case 4800: + *val = AD74413R_ADC_REJECTION_NONE; + return 0; + case 10: + *val = AD74413R_ADC_REJECTION_50_60_HART; + return 0; + case 1200: + *val = AD74413R_ADC_REJECTION_HART; + return 0; + default: + dev_err(st->dev, "ADC rate invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_range(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + *val = 10000; + return 0; + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val = 2500; + return 0; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = 5000; + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_offset(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val = 0; + return 0; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = -2500; + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_offset_raw(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val = 0; + return 0; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val = -((int)AD74413R_ADC_RESULT_MAX); + return 0; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = -((int)AD74413R_ADC_RESULT_MAX / 2); + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_get_output_voltage_scale(struct ad74413r_state *st, + int *val, int *val2) +{ + *val = AD74413R_DAC_VOLTAGE_MAX; + *val2 = AD74413R_DAC_CODE_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_output_current_scale(struct ad74413r_state *st, + int *val, int *val2) +{ + *val = regulator_get_voltage(st->refin_reg); + *val2 = st->sense_resistor_ohms * AD74413R_DAC_CODE_MAX * 1000; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_input_voltage_scale(struct ad74413r_state *st, + unsigned int channel, + int *val, int *val2) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_range(st, range, val); + if (ret) + return ret; + + *val2 = AD74413R_ADC_RESULT_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_input_voltage_offset(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_offset_raw(st, range, val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad74413r_get_input_current_scale(struct ad74413r_state *st, + unsigned int channel, int *val, + int *val2) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_range(st, range, val); + if (ret) + return ret; + + *val2 = AD74413R_ADC_RESULT_MAX * st->sense_resistor_ohms; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413_get_input_current_offset(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int range; + int voltage_range; + int voltage_offset; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_range(st, range, &voltage_range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_offset(st, range, &voltage_offset); + if (ret) + return ret; + + *val = voltage_offset * AD74413R_ADC_RESULT_MAX / voltage_range; + + return IIO_VAL_INT; +} + +static int ad74413r_get_adc_rate(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int rejection; + int ret; + + ret = ad74413r_get_adc_rejection(st, channel, &rejection); + if (ret) + return ret; + + ret = ad74413r_rejection_to_rate(st, rejection, val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad74413r_set_adc_rate(struct ad74413r_state *st, + unsigned int channel, int val) +{ + unsigned int rejection; + int ret; + + ret = ad74413r_rate_to_rejection(st, val, &rejection); + if (ret) + return ret; + + return ad74413r_set_adc_rejection(st, channel, rejection); +} + +static irqreturn_t ad74413r_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad74413r_state *st = iio_priv(indio_dev); + u8 *rx_buf = st->adc_samples_buf.rx_buf; + unsigned int i; + int ret; + + ret = spi_sync(st->spi, &st->adc_samples_msg); + if (ret) + goto out; + + for (i = 0; i < st->adc_active_channels; i++) + ad74413r_crc_check(st, &rx_buf[i * AD74413R_FRAME_SIZE]); + + iio_push_to_buffers_with_timestamp(indio_dev, &st->adc_samples_buf, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ad74413r_adc_data_interrupt(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ad74413r_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->adc_data_completion); + + return IRQ_HANDLED; +} + +static int _ad74413r_get_single_adc_result(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int uval; + int ret; + + reinit_completion(&st->adc_data_completion); + + ret = ad74413r_set_adc_channel_enable(st, channel, true); + if (ret) + return ret; + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_SINGLE); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&st->adc_data_completion, + msecs_to_jiffies(1000)); + if (!ret) { + ret = -ETIMEDOUT; + return ret; + } + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_RESULT_X(channel), + &uval); + if (ret) + return ret; + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + return ret; + + ret = ad74413r_set_adc_channel_enable(st, channel, false); + if (ret) + return ret; + + *val = uval; + + return IIO_VAL_INT; +} + +static int ad74413r_get_single_adc_result(struct iio_dev *indio_dev, + unsigned int channel, int *val) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + ret = _ad74413r_get_single_adc_result(st, channel, val); + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static void ad74413r_adc_to_resistance_result(int adc_result, int *val) +{ + if (adc_result == AD74413R_ADC_RESULT_MAX) + adc_result = AD74413R_ADC_RESULT_MAX - 1; + + *val = DIV_ROUND_CLOSEST(adc_result * 2100, + AD74413R_ADC_RESULT_MAX - adc_result); +} + +static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct spi_transfer *xfer = st->adc_samples_xfer; + u8 *rx_buf = &st->adc_samples_buf.rx_buf[-1 * AD74413R_FRAME_SIZE]; + u8 *tx_buf = st->adc_samples_tx_buf; + unsigned int channel; + int ret; + + mutex_lock(&st->lock); + + spi_message_init(&st->adc_samples_msg); + st->adc_active_channels = 0; + + for_each_clear_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { + ret = ad74413r_set_adc_channel_enable(st, channel, false); + if (ret) + goto out; + } + + if (*active_scan_mask == 0) + goto out; + + /* + * The read select register is used to select which register's value + * will be sent by the slave on the next SPI frame. + * + * Create an SPI message that, on each step, writes to the read select + * register to select the ADC result of the next enabled channel, and + * reads the ADC result of the previous enabled channel. + * + * Example: + * W: [WCH1] [WCH2] [WCH2] [WCH3] [ ] + * R: [ ] [RCH1] [RCH2] [RCH3] [RCH4] + */ + + for_each_set_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { + ret = ad74413r_set_adc_channel_enable(st, channel, true); + if (ret) + goto out; + + st->adc_active_channels++; + + if (xfer == st->adc_samples_xfer) + xfer->rx_buf = NULL; + else + xfer->rx_buf = rx_buf; + + xfer->tx_buf = tx_buf; + xfer->len = AD74413R_FRAME_SIZE; + xfer->cs_change = 1; + + ad74413r_format_reg_write(AD74413R_REG_READ_SELECT, + AD74413R_REG_ADC_RESULT_X(channel), + tx_buf); + + spi_message_add_tail(xfer, &st->adc_samples_msg); + + xfer++; + tx_buf += AD74413R_FRAME_SIZE; + rx_buf += AD74413R_FRAME_SIZE; + } + + xfer->rx_buf = rx_buf; + xfer->tx_buf = NULL; + xfer->len = AD74413R_FRAME_SIZE; + xfer->cs_change = 0; + + spi_message_add_tail(xfer, &st->adc_samples_msg); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad74413r_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + return ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_CONTINUOUS); +} + +static int ad74413r_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + return ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); +} + +static int ad74413r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) + return ad74413r_get_output_voltage_scale(st, + val, val2); + else + return ad74413r_get_input_voltage_scale(st, + chan->channel, val, val2); + case IIO_CURRENT: + if (chan->output) + return ad74413r_get_output_current_scale(st, + val, val2); + else + return ad74413r_get_input_current_scale(st, + chan->channel, val, val2); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + return ad74413r_get_input_voltage_offset(st, + chan->channel, val); + case IIO_CURRENT: + return ad74413_get_input_current_offset(st, + chan->channel, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + if (chan->output) + return -EINVAL; + + return ad74413r_get_single_adc_result(indio_dev, chan->channel, + val); + case IIO_CHAN_INFO_PROCESSED: { + int ret; + + ret = ad74413r_get_single_adc_result(indio_dev, chan->channel, + val); + if (ret) + return ret; + + ad74413r_adc_to_resistance_result(*val, val); + + return ret; + } + case IIO_CHAN_INFO_SAMP_FREQ: + return ad74413r_get_adc_rate(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad74413r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!chan->output) + return -EINVAL; + + if (val < 0 || val > AD74413R_DAC_CODE_MAX) { + dev_err(st->dev, "Invalid DAC code\n"); + return -EINVAL; + } + + return ad74413r_set_channel_dac_code(st, chan->channel, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad74413r_set_adc_rate(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad74413r_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (st->chip_info->hart_support) { + *vals = ad74413r_adc_sampling_rates_hart; + *length = ARRAY_SIZE(ad74413r_adc_sampling_rates_hart); + } else { + *vals = ad74413r_adc_sampling_rates; + *length = ARRAY_SIZE(ad74413r_adc_sampling_rates); + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_buffer_setup_ops ad74413r_buffer_ops = { + .postenable = &ad74413r_buffer_postenable, + .predisable = &ad74413r_buffer_predisable, +}; + +static const struct iio_trigger_ops ad74413r_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static const struct iio_info ad74413r_info = { + .read_raw = &ad74413r_read_raw, + .write_raw = &ad74413r_write_raw, + .read_avail = &ad74413r_read_avail, + .update_scan_mode = &ad74413r_update_scan_mode, +}; + +#define AD74413R_DAC_CHANNEL(_type, extra_mask_separate) \ + { \ + .type = (_type), \ + .indexed = 1, \ + .output = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | (extra_mask_separate), \ + } + +#define AD74413R_ADC_CHANNEL(_type, extra_mask_separate) \ + { \ + .type = (_type), \ + .indexed = 1, \ + .output = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | (extra_mask_separate), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 32, \ + .shift = 8, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD74413R_ADC_VOLTAGE_CHANNEL \ + AD74413R_ADC_CHANNEL(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET)) + +#define AD74413R_ADC_CURRENT_CHANNEL \ + AD74413R_ADC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET)) + +static struct iio_chan_spec ad74413r_voltage_output_channels[] = { + AD74413R_DAC_CHANNEL(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_output_channels[] = { + AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_voltage_input_channels[] = { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_input_channels[] = { + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_resistance_input_channels[] = { + AD74413R_ADC_CHANNEL(IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)), +}; + +static struct iio_chan_spec ad74413r_digital_input_channels[] = { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +#define _AD74413R_CHANNELS(_channels) \ + { \ + .channels = _channels, \ + .num_channels = ARRAY_SIZE(_channels), \ + } + +#define AD74413R_CHANNELS(name) \ + _AD74413R_CHANNELS(ad74413r_ ## name ## _channels) + +static const struct ad74413r_channels ad74413r_channels_map[] = { + [CH_FUNC_HIGH_IMPEDANCE] = AD74413R_CHANNELS(voltage_input), + [CH_FUNC_VOLTAGE_OUTPUT] = AD74413R_CHANNELS(voltage_output), + [CH_FUNC_CURRENT_OUTPUT] = AD74413R_CHANNELS(current_output), + [CH_FUNC_VOLTAGE_INPUT] = AD74413R_CHANNELS(voltage_input), + [CH_FUNC_CURRENT_INPUT_EXT_POWER] = AD74413R_CHANNELS(current_input), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER] = AD74413R_CHANNELS(current_input), + [CH_FUNC_RESISTANCE_INPUT] = AD74413R_CHANNELS(resistance_input), + [CH_FUNC_DIGITAL_INPUT_LOGIC] = AD74413R_CHANNELS(digital_input), + [CH_FUNC_DIGITAL_INPUT_LOOP_POWER] = AD74413R_CHANNELS(digital_input), + [CH_FUNC_CURRENT_INPUT_EXT_POWER_HART] = AD74413R_CHANNELS(current_input), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART] = AD74413R_CHANNELS(current_input), +}; + +static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, + struct fwnode_handle *channel_node) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct ad74413r_channel_config *config; + u32 index; + int ret; + + ret = fwnode_property_read_u32(channel_node, "reg", &index); + if (ret) { + dev_err(st->dev, "Failed to read channel reg: %d\n", ret); + return ret; + } + + if (index > AD74413R_CHANNEL_MAX) { + dev_err(st->dev, "Channel index %u is too large\n", index); + return -EINVAL; + } + + config = &st->channel_configs[index]; + if (config->initialized) { + dev_err(st->dev, "Channel %u already initialized\n", index); + return -EINVAL; + } + + config->func = CH_FUNC_HIGH_IMPEDANCE; + fwnode_property_read_u32(channel_node, "adi,ch-func", &config->func); + + if (config->func < CH_FUNC_MIN || config->func > CH_FUNC_MAX) { + dev_err(st->dev, "Invalid channel function %u\n", config->func); + return -EINVAL; + } + + if (!st->chip_info->hart_support && + (config->func == CH_FUNC_CURRENT_INPUT_EXT_POWER_HART || + config->func == CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART)) { + dev_err(st->dev, "Unsupported HART function %u\n", config->func); + return -EINVAL; + } + + if (config->func == CH_FUNC_DIGITAL_INPUT_LOGIC || + config->func == CH_FUNC_DIGITAL_INPUT_LOOP_POWER) + st->num_comparator_gpios++; + + config->gpo_comparator = fwnode_property_read_bool(channel_node, + "adi,gpo-comparator"); + + if (!config->gpo_comparator) + st->num_gpo_gpios++; + + indio_dev->num_channels += ad74413r_channels_map[config->func].num_channels; + + config->initialized = true; + + return 0; +} + +static int ad74413r_parse_channel_configs(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct fwnode_handle *channel_node = NULL; + int ret; + + fwnode_for_each_available_child_node(dev_fwnode(st->dev), channel_node) { + ret = ad74413r_parse_channel_config(indio_dev, channel_node); + if (ret) + goto put_channel_node; + } + + return 0; + +put_channel_node: + fwnode_handle_put(channel_node); + + return ret; +} + +static int ad74413r_setup_channels(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct ad74413r_channel_config *config; + struct iio_chan_spec *channels, *chans; + unsigned int i, num_chans, chan_i; + int ret; + + channels = devm_kcalloc(st->dev, sizeof(*channels), + indio_dev->num_channels, GFP_KERNEL); + if (!channels) + return -ENOMEM; + + indio_dev->channels = channels; + + for (i = 0; i < AD74413R_CHANNEL_MAX; i++) { + config = &st->channel_configs[i]; + chans = ad74413r_channels_map[config->func].channels; + num_chans = ad74413r_channels_map[config->func].num_channels; + + memcpy(channels, chans, num_chans * sizeof(*chans)); + + for (chan_i = 0; chan_i < num_chans; chan_i++) { + struct iio_chan_spec *chan = &channels[chan_i]; + + chan->channel = i; + if (chan->output) + chan->scan_index = -1; + else + chan->scan_index = i; + } + + ret = ad74413r_set_channel_function(st, i, config->func); + if (ret) + return ret; + + channels += num_chans; + } + + return 0; +} + +static int ad74413r_setup_gpios(struct ad74413r_state *st) +{ + struct ad74413r_channel_config *config; + unsigned int comp_gpio_i = 0; + unsigned int gpo_gpio_i = 0; + unsigned int i; + u8 gpo_config; + int ret; + + for (i = 0; i < AD74413R_CHANNEL_MAX; i++) { + config = &st->channel_configs[i]; + + if (config->gpo_comparator) { + gpo_config = AD74413R_GPO_CONFIG_COMPARATOR; + } else { + gpo_config = AD74413R_GPO_CONFIG_LOGIC; + st->gpo_gpio_offsets[gpo_gpio_i++] = i; + } + + if (config->func == CH_FUNC_DIGITAL_INPUT_LOGIC || + config->func == CH_FUNC_DIGITAL_INPUT_LOOP_POWER) + st->comp_gpio_offsets[comp_gpio_i++] = i; + + ret = ad74413r_set_gpo_config(st, i, gpo_config); + if (ret) + return ret; + } + + return 0; +} + +static void ad74413r_regulator_disable(void *regulator) +{ + regulator_disable(regulator); +} + +static int ad74413r_probe(struct spi_device *spi) +{ + struct ad74413r_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->spi = spi; + st->dev = &spi->dev; + st->chip_info = device_get_match_data(&spi->dev); + mutex_init(&st->lock); + init_completion(&st->adc_data_completion); + + st->regmap = devm_regmap_init(st->dev, NULL, st, + &ad74413r_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + st->refin_reg = devm_regulator_get(st->dev, "refin"); + if (IS_ERR(st->refin_reg)) + return dev_err_probe(st->dev, PTR_ERR(st->refin_reg), + "Failed to get refin regulator\n"); + + ret = regulator_enable(st->refin_reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(st->dev, ad74413r_regulator_disable, + st->refin_reg); + if (ret) + return ret; + + st->sense_resistor_ohms = 100000000; + device_property_read_u32(st->dev, "shunt-resistor-micro-ohms", + &st->sense_resistor_ohms); + st->sense_resistor_ohms /= 1000000; + + st->trig = devm_iio_trigger_alloc(st->dev, "%s-dev%d", + st->chip_info->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad74413r_trigger_ops; + iio_trigger_set_drvdata(st->trig, st); + + ret = devm_iio_trigger_register(st->dev, st->trig); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad74413r_info; + indio_dev->trig = iio_trigger_get(st->trig); + + ret = ad74413r_reset(st); + if (ret) + return ret; + + ret = ad74413r_parse_channel_configs(indio_dev); + if (ret) + return ret; + + ret = ad74413r_setup_channels(indio_dev); + if (ret) + return ret; + + ret = ad74413r_setup_gpios(st); + if (ret) + return ret; + + if (st->num_gpo_gpios) { + st->gpo_gpiochip.owner = THIS_MODULE; + st->gpo_gpiochip.label = st->chip_info->name; + st->gpo_gpiochip.base = -1; + st->gpo_gpiochip.ngpio = st->num_gpo_gpios; + st->gpo_gpiochip.parent = st->dev; + st->gpo_gpiochip.can_sleep = true; + st->gpo_gpiochip.set = ad74413r_gpio_set; + st->gpo_gpiochip.set_multiple = ad74413r_gpio_set_multiple; + st->gpo_gpiochip.set_config = ad74413r_gpio_set_gpo_config; + st->gpo_gpiochip.get_direction = + ad74413r_gpio_get_gpo_direction; + + ret = devm_gpiochip_add_data(st->dev, &st->gpo_gpiochip, st); + if (ret) + return ret; + } + + if (st->num_comparator_gpios) { + st->comp_gpiochip.owner = THIS_MODULE; + st->comp_gpiochip.label = st->chip_info->name; + st->comp_gpiochip.base = -1; + st->comp_gpiochip.ngpio = st->num_comparator_gpios; + st->comp_gpiochip.parent = st->dev; + st->comp_gpiochip.can_sleep = true; + st->comp_gpiochip.get = ad74413r_gpio_get; + st->comp_gpiochip.get_multiple = ad74413r_gpio_get_multiple; + st->comp_gpiochip.set_config = ad74413r_gpio_set_comp_config; + st->comp_gpiochip.get_direction = + ad74413r_gpio_get_comp_direction; + + ret = devm_gpiochip_add_data(st->dev, &st->comp_gpiochip, st); + if (ret) + return ret; + } + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + return ret; + + ret = devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt, + 0, st->chip_info->name, indio_dev); + if (ret) + return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + + ret = devm_iio_triggered_buffer_setup(st->dev, indio_dev, + &iio_pollfunc_store_time, + &ad74413r_trigger_handler, + &ad74413r_buffer_ops); + if (ret) + return ret; + + return devm_iio_device_register(st->dev, indio_dev); +} + +static int ad74413r_unregister_driver(struct spi_driver *spi) +{ + spi_unregister_driver(spi); + + return 0; +} + +static int __init ad74413r_register_driver(struct spi_driver *spi) +{ + crc8_populate_msb(ad74413r_crc8_table, AD74413R_CRC_POLYNOMIAL); + + return spi_register_driver(spi); +} + +static const struct ad74413r_chip_info ad74412r_chip_info_data = { + .hart_support = false, + .name = "ad74412r", +}; + +static const struct ad74413r_chip_info ad74413r_chip_info_data = { + .hart_support = true, + .name = "ad74413r", +}; + +static const struct of_device_id ad74413r_dt_id[] = { + { + .compatible = "adi,ad74412r", + .data = &ad74412r_chip_info_data, + }, + { + .compatible = "adi,ad74413r", + .data = &ad74413r_chip_info_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad74413r_dt_id); + +static struct spi_driver ad74413r_driver = { + .driver = { + .name = "ad74413r", + .of_match_table = ad74413r_dt_id, + }, + .probe = ad74413r_probe, +}; + +module_driver(ad74413r_driver, + ad74413r_register_driver, + ad74413r_unregister_driver); + +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_DESCRIPTION("Analog Devices AD74413R ADDAC"); +MODULE_LICENSE("GPL v2"); -- cgit From 9020ef659885f2622cfb386cc229b6d618362895 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 17 Oct 2021 18:22:09 +0100 Subject: iio: trigger: Fix a scheduling whilst atomic issue seen on tsc2046 IIO triggers are software IRQ chips that split an incoming IRQ into separate IRQs routed to all devices using the trigger. When all consumers are done then a trigger callback reenable() is called. There are a few circumstances under which this can happen in atomic context. 1) A single user of the trigger that calls the iio_trigger_done() function from interrupt context. 2) A race between disconnecting the last device from a trigger and the trigger itself sucessfully being disabled. To avoid a resulting scheduling whilst atomic, close this second corner by using schedule_work() to ensure the reenable is not done in atomic context. Note that drivers must be careful to manage the interaction of set_state() and reenable() callbacks to ensure appropriate reference counting if they are relying on the same hardware controls. Deliberately taking this the slow path rather than via a fixes tree because the error has hard to hit and I would like it to soak for a while before hitting a release kernel. Signed-off-by: Jonathan Cameron Cc: Pengutronix Kernel Team Cc: Dmitry Torokhov Tested-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20211017172209.112387-1-jic23@kernel.org --- drivers/iio/industrialio-trigger.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index b23caa2f2aa1..d3bdc9800b4a 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -162,6 +162,39 @@ static struct iio_trigger *iio_trigger_acquire_by_name(const char *name) return trig; } +static void iio_reenable_work_fn(struct work_struct *work) +{ + struct iio_trigger *trig = container_of(work, struct iio_trigger, + reenable_work); + + /* + * This 'might' occur after the trigger state is set to disabled - + * in that case the driver should skip reenabling. + */ + trig->ops->reenable(trig); +} + +/* + * In general, reenable callbacks may need to sleep and this path is + * not performance sensitive, so just queue up a work item + * to reneable the trigger for us. + * + * Races that can cause this. + * 1) A handler occurs entirely in interrupt context so the counter + * the final decrement is still in this interrupt. + * 2) The trigger has been removed, but one last interrupt gets through. + * + * For (1) we must call reenable, but not in atomic context. + * For (2) it should be safe to call reenanble, if drivers never blindly + * reenable after state is off. + */ +static void iio_trigger_notify_done_atomic(struct iio_trigger *trig) +{ + if (atomic_dec_and_test(&trig->use_count) && trig->ops && + trig->ops->reenable) + schedule_work(&trig->reenable_work); +} + void iio_trigger_poll(struct iio_trigger *trig) { int i; @@ -173,7 +206,7 @@ void iio_trigger_poll(struct iio_trigger *trig) if (trig->subirqs[i].enabled) generic_handle_irq(trig->subirq_base + i); else - iio_trigger_notify_done(trig); + iio_trigger_notify_done_atomic(trig); } } } @@ -535,6 +568,7 @@ struct iio_trigger *viio_trigger_alloc(struct device *parent, trig->dev.type = &iio_trig_type; trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); + INIT_WORK(&trig->reenable_work, iio_reenable_work_fn); mutex_init(&trig->pool_lock); trig->subirq_base = irq_alloc_descs(-1, 0, -- cgit From 3ac27afefd5dd6a53e830542b899f092a58b6b51 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:29 +0000 Subject: iio:dac:ad5755: Switch to generic firmware properties and drop pdata Lars pointed out that platform data can also be supported via the generic properties interface, so there is no point in continuing to support it separately. Hence squish the linux/platform_data/ad5755.h header into the c file and drop accessing the platform data directly. Done by inspection only. Mostly completely mechanical with the exception of a few places where default value handling is cleaner done by first setting the value, then calling the firmware reading function but and not checking the return value, as opposed to reading firmware then setting the default if an error occurs. Part of general attempt to move all of IIO over to generic device properties, both to enable other firmware types and to remove drivers that can be the source of of_ specific behaviour in new drivers. Suggested-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/dac/ad5755.c | 152 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 116 insertions(+), 36 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index cabc38d54085..7a62e6e1d5f1 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -13,10 +13,10 @@ #include #include #include -#include +#include + #include #include -#include #define AD5755_NUM_CHANNELS 4 @@ -63,6 +63,101 @@ #define AD5755_SLEW_RATE_SHIFT 3 #define AD5755_SLEW_ENABLE BIT(12) +enum ad5755_mode { + AD5755_MODE_VOLTAGE_0V_5V = 0, + AD5755_MODE_VOLTAGE_0V_10V = 1, + AD5755_MODE_VOLTAGE_PLUSMINUS_5V = 2, + AD5755_MODE_VOLTAGE_PLUSMINUS_10V = 3, + AD5755_MODE_CURRENT_4mA_20mA = 4, + AD5755_MODE_CURRENT_0mA_20mA = 5, + AD5755_MODE_CURRENT_0mA_24mA = 6, +}; + +enum ad5755_dc_dc_phase { + AD5755_DC_DC_PHASE_ALL_SAME_EDGE = 0, + AD5755_DC_DC_PHASE_A_B_SAME_EDGE_C_D_OPP_EDGE = 1, + AD5755_DC_DC_PHASE_A_C_SAME_EDGE_B_D_OPP_EDGE = 2, + AD5755_DC_DC_PHASE_90_DEGREE = 3, +}; + +enum ad5755_dc_dc_freq { + AD5755_DC_DC_FREQ_250kHZ = 0, + AD5755_DC_DC_FREQ_410kHZ = 1, + AD5755_DC_DC_FREQ_650kHZ = 2, +}; + +enum ad5755_dc_dc_maxv { + AD5755_DC_DC_MAXV_23V = 0, + AD5755_DC_DC_MAXV_24V5 = 1, + AD5755_DC_DC_MAXV_27V = 2, + AD5755_DC_DC_MAXV_29V5 = 3, +}; + +enum ad5755_slew_rate { + AD5755_SLEW_RATE_64k = 0, + AD5755_SLEW_RATE_32k = 1, + AD5755_SLEW_RATE_16k = 2, + AD5755_SLEW_RATE_8k = 3, + AD5755_SLEW_RATE_4k = 4, + AD5755_SLEW_RATE_2k = 5, + AD5755_SLEW_RATE_1k = 6, + AD5755_SLEW_RATE_500 = 7, + AD5755_SLEW_RATE_250 = 8, + AD5755_SLEW_RATE_125 = 9, + AD5755_SLEW_RATE_64 = 10, + AD5755_SLEW_RATE_32 = 11, + AD5755_SLEW_RATE_16 = 12, + AD5755_SLEW_RATE_8 = 13, + AD5755_SLEW_RATE_4 = 14, + AD5755_SLEW_RATE_0_5 = 15, +}; + +enum ad5755_slew_step_size { + AD5755_SLEW_STEP_SIZE_1 = 0, + AD5755_SLEW_STEP_SIZE_2 = 1, + AD5755_SLEW_STEP_SIZE_4 = 2, + AD5755_SLEW_STEP_SIZE_8 = 3, + AD5755_SLEW_STEP_SIZE_16 = 4, + AD5755_SLEW_STEP_SIZE_32 = 5, + AD5755_SLEW_STEP_SIZE_64 = 6, + AD5755_SLEW_STEP_SIZE_128 = 7, + AD5755_SLEW_STEP_SIZE_256 = 8, +}; + +/** + * struct ad5755_platform_data - AD5755 DAC driver platform data + * @ext_dc_dc_compenstation_resistor: Whether an external DC-DC converter + * compensation register is used. + * @dc_dc_phase: DC-DC converter phase. + * @dc_dc_freq: DC-DC converter frequency. + * @dc_dc_maxv: DC-DC maximum allowed boost voltage. + * @dac: Per DAC instance parameters. + * @dac.mode: The mode to be used for the DAC output. + * @dac.ext_current_sense_resistor: Whether an external current sense resistor + * is used. + * @dac.enable_voltage_overrange: Whether to enable 20% voltage output overrange. + * @dac.slew.enable: Whether to enable digital slew. + * @dac.slew.rate: Slew rate of the digital slew. + * @dac.slew.step_size: Slew step size of the digital slew. + **/ +struct ad5755_platform_data { + bool ext_dc_dc_compenstation_resistor; + enum ad5755_dc_dc_phase dc_dc_phase; + enum ad5755_dc_dc_freq dc_dc_freq; + enum ad5755_dc_dc_maxv dc_dc_maxv; + + struct { + enum ad5755_mode mode; + bool ext_current_sense_resistor; + bool enable_voltage_overrange; + struct { + bool enable; + enum ad5755_slew_rate rate; + enum ad5755_slew_step_size step_size; + } slew; + } dac[4]; +}; + /** * struct ad5755_chip_info - chip specific information * @channel_template: channel specification @@ -111,7 +206,6 @@ enum ad5755_type { ID_AD5737, }; -#ifdef CONFIG_OF static const int ad5755_dcdc_freq_table[][2] = { { 250000, AD5755_DC_DC_FREQ_250kHZ }, { 410000, AD5755_DC_DC_FREQ_410kHZ }, @@ -154,7 +248,6 @@ static const int ad5755_slew_step_table[][2] = { { 2, AD5755_SLEW_STEP_SIZE_2 }, { 1, AD5755_SLEW_STEP_SIZE_1 }, }; -#endif static int ad5755_write_unlocked(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) @@ -604,30 +697,29 @@ static const struct ad5755_platform_data ad5755_default_pdata = { }, }; -#ifdef CONFIG_OF -static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) +static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev) { - struct device_node *np = dev->of_node; - struct device_node *pp; + struct fwnode_handle *pp; struct ad5755_platform_data *pdata; unsigned int tmp; unsigned int tmparray[3]; int devnr, i; + if (!dev_fwnode(dev)) + return NULL; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; pdata->ext_dc_dc_compenstation_resistor = - of_property_read_bool(np, "adi,ext-dc-dc-compenstation-resistor"); + device_property_read_bool(dev, "adi,ext-dc-dc-compenstation-resistor"); - if (!of_property_read_u32(np, "adi,dc-dc-phase", &tmp)) - pdata->dc_dc_phase = tmp; - else - pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE; + pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE; + device_property_read_u32(dev, "adi,dc-dc-phase", &pdata->dc_dc_phase); pdata->dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ; - if (!of_property_read_u32(np, "adi,dc-dc-freq-hz", &tmp)) { + if (!device_property_read_u32(dev, "adi,dc-dc-freq-hz", &tmp)) { for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_freq_table); i++) { if (tmp == ad5755_dcdc_freq_table[i][0]) { pdata->dc_dc_freq = ad5755_dcdc_freq_table[i][1]; @@ -641,7 +733,7 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) } pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V; - if (!of_property_read_u32(np, "adi,dc-dc-max-microvolt", &tmp)) { + if (!device_property_read_u32(dev, "adi,dc-dc-max-microvolt", &tmp)) { for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_maxv_table); i++) { if (tmp == ad5755_dcdc_maxv_table[i][0]) { pdata->dc_dc_maxv = ad5755_dcdc_maxv_table[i][1]; @@ -654,25 +746,23 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) } devnr = 0; - for_each_child_of_node(np, pp) { + device_for_each_child_node(dev, pp) { if (devnr >= AD5755_NUM_CHANNELS) { dev_err(dev, "There are too many channels defined in DT\n"); goto error_out; } - if (!of_property_read_u32(pp, "adi,mode", &tmp)) - pdata->dac[devnr].mode = tmp; - else - pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA; + pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA; + fwnode_property_read_u32(pp, "adi,mode", &pdata->dac[devnr].mode); pdata->dac[devnr].ext_current_sense_resistor = - of_property_read_bool(pp, "adi,ext-current-sense-resistor"); + fwnode_property_read_bool(pp, "adi,ext-current-sense-resistor"); pdata->dac[devnr].enable_voltage_overrange = - of_property_read_bool(pp, "adi,enable-voltage-overrange"); + fwnode_property_read_bool(pp, "adi,enable-voltage-overrange"); - if (!of_property_read_u32_array(pp, "adi,slew", tmparray, 3)) { + if (!fwnode_property_read_u32_array(pp, "adi,slew", tmparray, 3)) { pdata->dac[devnr].slew.enable = tmparray[0]; pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; @@ -715,18 +805,11 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) devm_kfree(dev, pdata); return NULL; } -#else -static -struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) -{ - return NULL; -} -#endif static int ad5755_probe(struct spi_device *spi) { enum ad5755_type type = spi_get_device_id(spi)->driver_data; - const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev); + const struct ad5755_platform_data *pdata; struct iio_dev *indio_dev; struct ad5755_state *st; int ret; @@ -751,13 +834,10 @@ static int ad5755_probe(struct spi_device *spi) mutex_init(&st->lock); - if (spi->dev.of_node) - pdata = ad5755_parse_dt(&spi->dev); - else - pdata = spi->dev.platform_data; + pdata = ad5755_parse_fw(&spi->dev); if (!pdata) { - dev_warn(&spi->dev, "no platform data? using default\n"); + dev_warn(&spi->dev, "no firmware provided parameters? using default\n"); pdata = &ad5755_default_pdata; } -- cgit From f191fe4f0d3e8ed033d888b4da9039f8ffe4039f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:30 +0000 Subject: iio:dac:ad5758: Drop unused of specific headers. These have never been used in this driver. What is used is in mod_devicetable.h so add that include (struct of_device_id) Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Daniel Gomez --- drivers/iio/dac/ad5758.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index 0572ef518101..98771e37a7b5 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -10,9 +10,8 @@ #include #include #include +#include #include -#include -#include #include #include -- cgit From 5669c086e699ff269a977b225a8c9643cf39e53f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:31 +0000 Subject: iio:dac:dpot-dac: Swap of.h for mod_devicetable.h This driver never used anything in the of specific header. It just wants the struct of_device_id from mod_devicetable.h. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Acked-by: Peter Rosin --- drivers/iio/dac/dpot-dac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index 5d1819448102..83ce9489259c 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include -- cgit From 09a74ea737352a80ed6e3fd427350d7d9c5a5502 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:32 +0000 Subject: iio:dac:lpc18xx_dac: Swap from of* to mod_devicetable.h This driver never used anything from the of specific headers. mod_devicetable.h provides the struct of_device_id definition. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/dac/lpc18xx_dac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index 5502e4f62f0d..60467c6f2c6e 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -16,9 +16,8 @@ #include #include #include +#include #include -#include -#include #include #include -- cgit From 92311717b3a37f2c888b30b940510ed6c058bfb0 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:33 +0000 Subject: iio:pot:mcp41010: Switch to generic firmware properties. In this case it was only of_device_get_match_data() + header update. This enables use of other firmware types with no other changes, such as ACPI via the PRP0001 route. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Chris Coffey --- drivers/iio/potentiometer/mcp41010.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c index 79ccac6d4be0..30a4594d4e11 100644 --- a/drivers/iio/potentiometer/mcp41010.c +++ b/drivers/iio/potentiometer/mcp41010.c @@ -21,9 +21,9 @@ #include #include #include +#include #include -#include -#include +#include #include #define MCP41010_MAX_WIPERS 2 @@ -146,7 +146,7 @@ static int mcp41010_probe(struct spi_device *spi) data = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); data->spi = spi; - data->cfg = of_device_get_match_data(&spi->dev); + data->cfg = device_get_match_data(&spi->dev); if (!data->cfg) data->cfg = &mcp41010_cfg[spi_get_device_id(spi)->driver_data]; -- cgit From fdb726c4f9ef8a9301137530344aa4303fa0e571 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:34 +0000 Subject: iio:light:cm3605: Switch to generic firmware properties. This enables use of other firmware types with minimal driver changes. Part of an ongoing effort to move all IIO drivers over to generic accessors in order to reduce the chance of of_* versions being copied into new drivers. Also updated the headers to reflect this change including using mod_devicetable.h for struct of_device_id definition rather than going via of.h Signed-off-by: Jonathan Cameron Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko --- drivers/iio/light/cm3605.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 3e7fb16ab1f6..50d34a98839c 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -18,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -156,7 +157,6 @@ static int cm3605_probe(struct platform_device *pdev) struct cm3605 *cm3605; struct iio_dev *indio_dev; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; enum iio_chan_type ch_type; u32 rset; int irq; @@ -171,7 +171,7 @@ static int cm3605_probe(struct platform_device *pdev) cm3605->dev = dev; cm3605->dir = IIO_EV_DIR_FALLING; - ret = of_property_read_u32(np, "capella,aset-resistance-ohms", &rset); + ret = device_property_read_u32(dev, "capella,aset-resistance-ohms", &rset); if (ret) { dev_info(dev, "no RSET specified, assuming 100K\n"); rset = 100000; -- cgit From c88eba5a186e6de87d7dcdf160d7d9fb989a191c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:35 +0000 Subject: iio:adc:max9611: Switch to generic firmware properties. Note the handling of the device tree node in this driver was somewhat unusual. I have cleaned that up whilst also moving over to generic properties. Part of a general attempt to move all IIO drivers over to generic firmware properties both as a general improvement and to avoid sources of cut and paste into future drivers. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/max9611.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 052ab23f10b2..01a4275e9c46 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -22,7 +22,8 @@ #include #include #include -#include +#include +#include #define DRIVER_NAME "max9611" @@ -513,11 +514,9 @@ static int max9611_probe(struct i2c_client *client, const struct i2c_device_id *id) { const char * const shunt_res_prop = "shunt-resistor-micro-ohms"; - const struct device_node *of_node = client->dev.of_node; - const struct of_device_id *of_id = - of_match_device(max9611_of_table, &client->dev); struct max9611_dev *max9611; struct iio_dev *indio_dev; + struct device *dev = &client->dev; unsigned int of_shunt; int ret; @@ -528,15 +527,14 @@ static int max9611_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); max9611 = iio_priv(indio_dev); - max9611->dev = &client->dev; + max9611->dev = dev; max9611->i2c_client = client; mutex_init(&max9611->lock); - ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt); + ret = device_property_read_u32(dev, shunt_res_prop, &of_shunt); if (ret) { - dev_err(&client->dev, - "Missing %s property for %pOF node\n", - shunt_res_prop, of_node); + dev_err(dev, "Missing %s property for %pfw node\n", + shunt_res_prop, dev_fwnode(dev)); return ret; } max9611->shunt_resistor_uohm = of_shunt; @@ -545,13 +543,13 @@ static int max9611_probe(struct i2c_client *client, if (ret) return ret; - indio_dev->name = of_id->data; + indio_dev->name = device_get_match_data(dev); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &indio_info; indio_dev->channels = max9611_channels; indio_dev->num_channels = ARRAY_SIZE(max9611_channels); - return devm_iio_device_register(&client->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static struct i2c_driver max9611_driver = { -- cgit From 4efc1c614d334883cce09c38aa3fe74d3fb0bbf0 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:36 +0000 Subject: iio:adc:mcp3911: Switch to generic firmware properties. This allows use of the driver with other types of firmware such as ACPI PRP0001 based probing. Also part of a general attempt to remove direct use of of_ specific accessors from IIO. Added an include for mod_devicetable.h whilst here to cover the struct of_device_id definition. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Kent Gustavsson Reviewed-by: Marcus Folkesson --- drivers/iio/adc/mcp3911.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index e573da5397bb..13535f148c4c 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -200,12 +202,13 @@ static const struct iio_info mcp3911_info = { .write_raw = mcp3911_write_raw, }; -static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node) +static int mcp3911_config(struct mcp3911 *adc) { + struct device *dev = &adc->spi->dev; u32 configreg; int ret; - of_property_read_u32(of_node, "device-addr", &adc->dev_addr); + device_property_read_u32(dev, "device-addr", &adc->dev_addr); if (adc->dev_addr > 3) { dev_err(&adc->spi->dev, "invalid device address (%i). Must be in range 0-3.\n", @@ -289,7 +292,7 @@ static int mcp3911_probe(struct spi_device *spi) } } - ret = mcp3911_config(adc, spi->dev.of_node); + ret = mcp3911_config(adc); if (ret) goto clk_disable; -- cgit From 3c3969a0c99b0da5223d869b3b21ce3d38931810 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:37 +0000 Subject: iio:adc:ti-adc12138: Switch to generic firmware properties and drop of_match_ptr This enables using the driver with other firmware types such as ACPI via PRP0001. Also part of a general attempt to move IIO drivers over to generic properties to avoid opportunities for cut and paste. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/ti-adc12138.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index 5b5d45210539..6eb62b564dae 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -430,8 +431,8 @@ static int adc12138_probe(struct spi_device *spi) return -EINVAL; } - ret = of_property_read_u32(spi->dev.of_node, "ti,acquisition-time", - &adc->acquisition_time); + ret = device_property_read_u32(&spi->dev, "ti,acquisition-time", + &adc->acquisition_time); if (ret) adc->acquisition_time = 10; @@ -517,8 +518,6 @@ static int adc12138_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_OF - static const struct of_device_id adc12138_dt_ids[] = { { .compatible = "ti,adc12130", }, { .compatible = "ti,adc12132", }, @@ -527,8 +526,6 @@ static const struct of_device_id adc12138_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, adc12138_dt_ids); -#endif - static const struct spi_device_id adc12138_id[] = { { "adc12130", adc12130 }, { "adc12132", adc12132 }, @@ -540,7 +537,7 @@ MODULE_DEVICE_TABLE(spi, adc12138_id); static struct spi_driver adc12138_driver = { .driver = { .name = "adc12138", - .of_match_table = of_match_ptr(adc12138_dt_ids), + .of_match_table = adc12138_dt_ids, }, .probe = adc12138_probe, .remove = adc12138_remove, -- cgit From f346c96505412d83f7c8aa09629e48c2d3315fd4 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:38 +0000 Subject: iio:adc:envelope-detector: Switch from of headers to mod_devicetable.h There is nothing directly using of specific interfaces in this driver, so lets not include the headers. Signed-off-by: Jonathan Cameron Acked-by: Peter Rosin Reviewed-by: Andy Shevchenko --- drivers/iio/adc/envelope-detector.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index d73eac36153f..e911c25d106d 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -31,14 +31,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include -#include #include #include #include -- cgit From ade2be6d9b07529f35cb98c49b00a1b3bf26973c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:39 +0000 Subject: iio:adc:ti-ads124s08: Drop dependency on OF. Nothing in this driver depends on OF firmware so drop the dependency and update the headers to remove the false impression such a dependency exists. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/Kconfig | 2 +- drivers/iio/adc/ti-ads124s08.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 8bf5b62a73f4..9b0b99bc826c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1166,7 +1166,7 @@ config TI_ADS8688 config TI_ADS124S08 tristate "Texas Instruments ADS124S08" - depends on SPI && OF + depends on SPI help If you say yes here you get support for Texas Instruments ADS124S08 and ADS124S06 ADC chips diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 17d0da5877a9..767b3b634809 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -8,8 +8,7 @@ #include #include #include -#include -#include +#include #include #include -- cgit From a81c33f56abea1a63495e9a72073dda8c28083b8 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:40 +0000 Subject: iio:adc/dac:Kconfig: Update to drop OF dependencies. We could probably drop a lot more of these, but for now this removes unnecessary restrictions on stand alone ADC devices. For these 3 drivers the false dependency seems to date all the way back to their initial introduction. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/Kconfig | 4 ++-- drivers/iio/dac/Kconfig | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 9b0b99bc826c..c7de4632f24a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1146,7 +1146,7 @@ config TI_ADS7950 config TI_ADS8344 tristate "Texas Instruments ADS8344" - depends on SPI && OF + depends on SPI help If you say yes here you get support for Texas Instruments ADS8344 ADC chips @@ -1156,7 +1156,7 @@ config TI_ADS8344 config TI_ADS8688 tristate "Texas Instruments ADS8688" - depends on SPI && OF + depends on SPI help If you say yes here you get support for Texas Instruments ADS8684 and and ADS8688 ADC chips diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 6206b90fc08f..b95619f18fa5 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -340,7 +340,6 @@ config MAX517 config MAX5821 tristate "Maxim MAX5821 DAC driver" depends on I2C - depends on OF help Say yes here to build support for Maxim MAX5821 10 bits DAC. -- cgit From 8b7651f2596238ca54225ebbcfbd3f14a4c41887 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Dec 2021 13:50:52 +0100 Subject: iio: iio_device_alloc(): Remove unnecessary self drvdata Drvdata is typically used by drivers to attach driver specific data to a device. It is used to retrieve driver specific information when only the device to which the data is attached is available. In the IIO core in the `iio_device_alloc()` function we call `iio_device_set_drvdata(indio_dev, indio_dev)`. This sets the drvdata of the IIO device to itself. This is rather unnecessary since if we have a pointer to the IIO device to call `iio_device_get_drvdata()` on it we don't need to call the function since we already have the pointer. If we only have a pointer to the `struct device` we can use `dev_to_iio_dev()` to get the IIO device from it. Furthermore the drvdata is supposed to be reserved for drivers, so it should not be used by the IIO core in the first place. The `set_drvdata()` has been around from the very beginning of the IIO framework and back then it was used in the IIO device sysfs attribute handling code. But that was subsequently replaced with a `dev_to_iio_dev()` in commit e53f5ac52ec1 ("iio: Use dev_to_iio_dev()") and other cleanups. The self `set_drvdata()` is now no longer needed and can be removed. Verified that there no longer any users by checking for potential users using the following two coccinelle scripts and reviewing that none of the matches are problematic code. @@ struct iio_dev *iio_dev; expression dev; identifier fn !~ "(remove|resume|suspend)"; @@ fn(...) { ... *iio_dev = dev_get_drvdata(dev) ... } @r1@ position p; struct iio_dev *indio_dev; identifier dev_fn =~ "^dev_"; identifier devm_fn =~ "^devm_"; @@ ( dev_fn | devm_fn ) (&indio_dev@p->dev, ...) @@ struct iio_dev *indio_dev; position p != r1.p; @@ *&indio_dev@p->dev Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 20d5178ca073..409c278a4c2c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1656,7 +1656,6 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) indio_dev->dev.type = &iio_device_type; indio_dev->dev.bus = &iio_bus_type; device_initialize(&indio_dev->dev); - iio_device_set_drvdata(indio_dev, (void *)indio_dev); mutex_init(&indio_dev->mlock); mutex_init(&iio_dev_opaque->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); -- cgit From 0a52c3f347fd0173a6aa718bffedca90816ddac6 Mon Sep 17 00:00:00 2001 From: Xiang wangx Date: Sun, 12 Dec 2021 22:41:18 +0800 Subject: iio: adc: ad7606: Fix syntax errors in comments Delete the redundant word 'the'. Signed-off-by: Xiang wangx Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7606.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 9350ef1f63b5..4f82d7c9acfd 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -62,7 +62,7 @@ struct ad7606_chip_info { * struct ad7606_state - driver instance specific data * @dev pointer to kernel device * @chip_info entry in the table of chips that describes this device - * @reg regulator info for the the power supply of the device + * @reg regulator info for the power supply of the device * @bops bus operations (SPI or parallel) * @range voltage range selection, selects which scale to apply * @oversampling oversampling selection -- cgit From 5d97d9e9a703be2a602ac24c1ba3dae22155a2c8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 15 Dec 2021 14:50:53 +0300 Subject: iio: addac: ad74413r: fix off by one in ad74413r_parse_channel_config() The > needs to be >= to prevent accessing one element beyond the end of the st->channel_configs[] array. Fixes: fea251b6a5db ("iio: addac: add AD74413R driver") Signed-off-by: Dan Carpenter Reviewed-by: Cosmin Tanislav Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index cbd9aa9b399a..289d254943e1 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -1150,7 +1150,7 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, return ret; } - if (index > AD74413R_CHANNEL_MAX) { + if (index >= AD74413R_CHANNEL_MAX) { dev_err(st->dev, "Channel index %u is too large\n", index); return -EINVAL; } -- cgit From 8a457852bc12c16968c025cce6a7005b41fafa87 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Wed, 15 Dec 2021 06:07:10 +0000 Subject: iio:adc:ti-ads8688:: remove redundant ret variable Return value from ads8688_prog_write() directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads8688.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index 79c803537dc4..2e24717d7f55 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -281,12 +281,10 @@ static int ads8688_write_reg_range(struct iio_dev *indio_dev, enum ads8688_range range) { unsigned int tmp; - int ret; tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel); - ret = ads8688_prog_write(indio_dev, tmp, range); - return ret; + return ads8688_prog_write(indio_dev, tmp, range); } static int ads8688_write_raw(struct iio_dev *indio_dev, -- cgit From 3511989cd22b06599b98e2566ecb571b846ffb86 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 15 Dec 2021 15:25:13 -0800 Subject: iio: stmpe-adc: Use correctly sized arguments for bit field The find.h APIs are designed to be used only on unsigned long arguments. This can technically result in a over-read, but it is harmless in this case. Regardless, fix it to avoid the warning seen under -Warray-bounds, which we'd like to enable globally: In file included from ./include/linux/bitmap.h:9, from ./include/linux/cpumask.h:12, from ./arch/x86/include/asm/cpumask.h:5, from ./arch/x86/include/asm/msr.h:11, from ./arch/x86/include/asm/processor.h:22, from ./arch/x86/include/asm/cpufeature.h:5, from ./arch/x86/include/asm/thread_info.h:53, from ./include/linux/thread_info.h:60, from ./arch/x86/include/asm/preempt.h:7, from ./include/linux/preempt.h:78, from ./include/linux/spinlock.h:55, from ./include/linux/swait.h:7, from ./include/linux/completion.h:12, from drivers/iio/adc/stmpe-adc.c:10: drivers/iio/adc/stmpe-adc.c: In function 'stmpe_adc_probe': ./include/linux/find.h:98:23: warning: array subscript 'long unsigned int[0]' is partly outside array bounds of 'u32[1]' {aka 'unsigned int[1]'} [-Warray-bounds] 98 | val = *addr | ~GENMASK(size - 1, offset); | ^~~~~ drivers/iio/adc/stmpe-adc.c:258:13: note: while referencing 'norequest_mask' 258 | u32 norequest_mask = 0; | ^~~~~~~~~~~~~~ Signed-off-by: Kees Cook Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stmpe-adc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c index fba659bfdb40..d2d405388499 100644 --- a/drivers/iio/adc/stmpe-adc.c +++ b/drivers/iio/adc/stmpe-adc.c @@ -256,6 +256,7 @@ static int stmpe_adc_probe(struct platform_device *pdev) struct stmpe_adc *info; struct device_node *np; u32 norequest_mask = 0; + unsigned long bits; int irq_temp, irq_adc; int num_chan = 0; int i = 0; @@ -309,8 +310,8 @@ static int stmpe_adc_probe(struct platform_device *pdev) of_property_read_u32(np, "st,norequest-mask", &norequest_mask); - for_each_clear_bit(i, (unsigned long *) &norequest_mask, - (STMPE_ADC_LAST_NR + 1)) { + bits = norequest_mask; + for_each_clear_bit(i, &bits, (STMPE_ADC_LAST_NR + 1)) { stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i); num_chan++; } -- cgit From 91b49aadbabf6860a8dae45df7aa982ca058b203 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Dec 2021 17:17:28 +0100 Subject: iio: as3935: Remove unnecessary cast `buf` is cast to a const char *, but `buf` is already a const char *, so the case is unnecessary. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index d62766b6b39e..51f4f92ae84a 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -133,7 +133,7 @@ static ssize_t as3935_sensor_sensitivity_store(struct device *dev, unsigned long val; int ret; - ret = kstrtoul((const char *) buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (ret) return -EINVAL; -- cgit From 52c65f5b095782abb1accbacfe6f6962a583fe05 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Dec 2021 17:17:29 +0100 Subject: iio: in2xx-adc: Remove unnecessary cast `buf` is cast to a const char *, but `buf` is already a const char *, so the case is unnecessary. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 352f27657238..08f243f5b92b 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -550,7 +550,7 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev, bool val; int ret; - ret = strtobool((const char *) buf, &val); + ret = strtobool(buf, &val); if (ret) return ret; -- cgit From 79ca243d83415c8f43e1c071b744fd8db8c1ccc1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Dec 2021 17:17:30 +0100 Subject: iio: vz89x: Remove unnecessary cast The case to u8 * is unnecessary here since the expression is already of type u8 *. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/vz89x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 23b22a5f5c1c..e7e1c74a351e 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -242,7 +242,7 @@ static int vz89x_get_resistance_reading(struct vz89x_data *data, struct iio_chan_spec const *chan, int *val) { - u8 *tmp = (u8 *) &data->buffer[chan->address]; + u8 *tmp = &data->buffer[chan->address]; switch (chan->scan_type.endianness) { case IIO_LE: -- cgit From 35c35b0c4161273e22d1bfb17e935d5dd7cefa8e Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 7 Dec 2021 17:54:42 +0200 Subject: iio: add filter subfolder Add filter subfolder for IIO devices that handle filter functionality. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/filter/Kconfig | 8 ++++++++ drivers/iio/filter/Makefile | 6 ++++++ 4 files changed, 16 insertions(+) create mode 100644 drivers/iio/filter/Kconfig create mode 100644 drivers/iio/filter/Makefile (limited to 'drivers/iio') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 4fb4321a72cb..b190846c3dc2 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -78,6 +78,7 @@ source "drivers/iio/chemical/Kconfig" source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" source "drivers/iio/dummy/Kconfig" +source "drivers/iio/filter/Kconfig" source "drivers/iio/frequency/Kconfig" source "drivers/iio/gyro/Kconfig" source "drivers/iio/health/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 8d48c70fee4d..3be08cdadd7e 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -25,6 +25,7 @@ obj-y += common/ obj-y += dac/ obj-y += dummy/ obj-y += gyro/ +obj-y += filter/ obj-y += frequency/ obj-y += health/ obj-y += humidity/ diff --git a/drivers/iio/filter/Kconfig b/drivers/iio/filter/Kconfig new file mode 100644 index 000000000000..e268bba43852 --- /dev/null +++ b/drivers/iio/filter/Kconfig @@ -0,0 +1,8 @@ +# +# Filter drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Filters" + +endmenu diff --git a/drivers/iio/filter/Makefile b/drivers/iio/filter/Makefile new file mode 100644 index 000000000000..cc0892c01142 --- /dev/null +++ b/drivers/iio/filter/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O Filter drivers +# + +# When adding new entries keep the list in alphabetical order -- cgit From f34fe888ad0546dacf678aa604435d442934984f Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 7 Dec 2021 17:54:43 +0200 Subject: iio:filter:admv8818: add support for ADMV8818 The ADMV8818-EP is a fully monolithic microwave integrated circuit (MMIC) that features a digitally selectable frequency of operation. The device features four independently controlled high- pass filters (HPFs) and four independently controlled low-pass filters (LPFs) that span the 2 GHz to 18 GHz frequency range. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/admv8818-ep.pdf Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/filter/Kconfig | 10 + drivers/iio/filter/Makefile | 1 + drivers/iio/filter/admv8818.c | 665 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 676 insertions(+) create mode 100644 drivers/iio/filter/admv8818.c (limited to 'drivers/iio') diff --git a/drivers/iio/filter/Kconfig b/drivers/iio/filter/Kconfig index e268bba43852..3ae35817ad82 100644 --- a/drivers/iio/filter/Kconfig +++ b/drivers/iio/filter/Kconfig @@ -5,4 +5,14 @@ menu "Filters" +config ADMV8818 + tristate "Analog Devices ADMV8818 High-Pass and Low-Pass Filter" + depends on SPI && COMMON_CLK && 64BIT + help + Say yes here to build support for Analog Devices ADMV8818 + 2 GHz to 18 GHz, Digitally Tunable, High-Pass and Low-Pass Filter. + + To compile this driver as a module, choose M here: the + modiule will be called admv8818. + endmenu diff --git a/drivers/iio/filter/Makefile b/drivers/iio/filter/Makefile index cc0892c01142..55e228c0dd20 100644 --- a/drivers/iio/filter/Makefile +++ b/drivers/iio/filter/Makefile @@ -4,3 +4,4 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_ADMV8818) += admv8818.o diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c new file mode 100644 index 000000000000..68de45fe21b4 --- /dev/null +++ b/drivers/iio/filter/admv8818.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADMV8818 driver + * + * Copyright 2021 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ADMV8818 Register Map */ +#define ADMV8818_REG_SPI_CONFIG_A 0x0 +#define ADMV8818_REG_SPI_CONFIG_B 0x1 +#define ADMV8818_REG_CHIPTYPE 0x3 +#define ADMV8818_REG_PRODUCT_ID_L 0x4 +#define ADMV8818_REG_PRODUCT_ID_H 0x5 +#define ADMV8818_REG_FAST_LATCH_POINTER 0x10 +#define ADMV8818_REG_FAST_LATCH_STOP 0x11 +#define ADMV8818_REG_FAST_LATCH_START 0x12 +#define ADMV8818_REG_FAST_LATCH_DIRECTION 0x13 +#define ADMV8818_REG_FAST_LATCH_STATE 0x14 +#define ADMV8818_REG_WR0_SW 0x20 +#define ADMV8818_REG_WR0_FILTER 0x21 +#define ADMV8818_REG_WR1_SW 0x22 +#define ADMV8818_REG_WR1_FILTER 0x23 +#define ADMV8818_REG_WR2_SW 0x24 +#define ADMV8818_REG_WR2_FILTER 0x25 +#define ADMV8818_REG_WR3_SW 0x26 +#define ADMV8818_REG_WR3_FILTER 0x27 +#define ADMV8818_REG_WR4_SW 0x28 +#define ADMV8818_REG_WR4_FILTER 0x29 +#define ADMV8818_REG_LUT0_SW 0x100 +#define ADMV8818_REG_LUT0_FILTER 0x101 +#define ADMV8818_REG_LUT127_SW 0x1FE +#define ADMV8818_REG_LUT127_FILTER 0x1FF + +/* ADMV8818_REG_SPI_CONFIG_A Map */ +#define ADMV8818_SOFTRESET_N_MSK BIT(7) +#define ADMV8818_LSB_FIRST_N_MSK BIT(6) +#define ADMV8818_ENDIAN_N_MSK BIT(5) +#define ADMV8818_SDOACTIVE_N_MSK BIT(4) +#define ADMV8818_SDOACTIVE_MSK BIT(3) +#define ADMV8818_ENDIAN_MSK BIT(2) +#define ADMV8818_LSBFIRST_MSK BIT(1) +#define ADMV8818_SOFTRESET_MSK BIT(0) + +/* ADMV8818_REG_SPI_CONFIG_B Map */ +#define ADMV8818_SINGLE_INSTRUCTION_MSK BIT(7) +#define ADMV8818_CSB_STALL_MSK BIT(6) +#define ADMV8818_MASTER_SLAVE_RB_MSK BIT(5) +#define ADMV8818_MASTER_SLAVE_TRANSFER_MSK BIT(0) + +/* ADMV8818_REG_WR0_SW Map */ +#define ADMV8818_SW_IN_SET_WR0_MSK BIT(7) +#define ADMV8818_SW_OUT_SET_WR0_MSK BIT(6) +#define ADMV8818_SW_IN_WR0_MSK GENMASK(5, 3) +#define ADMV8818_SW_OUT_WR0_MSK GENMASK(2, 0) + +/* ADMV8818_REG_WR0_FILTER Map */ +#define ADMV8818_HPF_WR0_MSK GENMASK(7, 4) +#define ADMV8818_LPF_WR0_MSK GENMASK(3, 0) + +enum { + ADMV8818_BW_FREQ, + ADMV8818_CENTER_FREQ +}; + +enum { + ADMV8818_AUTO_MODE, + ADMV8818_MANUAL_MODE, +}; + +struct admv8818_state { + struct spi_device *spi; + struct regmap *regmap; + struct clk *clkin; + struct notifier_block nb; + /* Protect against concurrent accesses to the device and data content*/ + struct mutex lock; + unsigned int filter_mode; + u64 cf_hz; +}; + +static const unsigned long long freq_range_hpf[4][2] = { + {1750000000ULL, 3550000000ULL}, + {3400000000ULL, 7250000000ULL}, + {6600000000, 12000000000}, + {12500000000, 19900000000} +}; + +static const unsigned long long freq_range_lpf[4][2] = { + {2050000000ULL, 3850000000ULL}, + {3350000000ULL, 7250000000ULL}, + {7000000000, 13000000000}, + {12550000000, 18500000000} +}; + +static const struct regmap_config admv8818_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = 0x80, + .max_register = 0x1FF, +}; + +static const char * const admv8818_modes[] = { + [0] = "auto", + [1] = "manual" +}; + +static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq) +{ + unsigned int hpf_step = 0, hpf_band = 0, i, j; + u64 freq_step; + int ret; + + if (freq < freq_range_hpf[0][0]) + goto hpf_write; + + if (freq > freq_range_hpf[3][1]) { + hpf_step = 15; + hpf_band = 4; + + goto hpf_write; + } + + for (i = 0; i < 4; i++) { + freq_step = div_u64((freq_range_hpf[i][1] - + freq_range_hpf[i][0]), 15); + + if (freq > freq_range_hpf[i][0] && + (freq < freq_range_hpf[i][1] + freq_step)) { + hpf_band = i + 1; + + for (j = 1; j <= 16; j++) { + if (freq < (freq_range_hpf[i][0] + (freq_step * j))) { + hpf_step = j - 1; + break; + } + } + break; + } + } + + /* Close HPF frequency gap between 12 and 12.5 GHz */ + if (freq >= 12000 * HZ_PER_MHZ && freq <= 12500 * HZ_PER_MHZ) { + hpf_band = 3; + hpf_step = 15; + } + +hpf_write: + ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW, + ADMV8818_SW_IN_SET_WR0_MSK | + ADMV8818_SW_IN_WR0_MSK, + FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) | + FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, hpf_band)); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER, + ADMV8818_HPF_WR0_MSK, + FIELD_PREP(ADMV8818_HPF_WR0_MSK, hpf_step)); +} + +static int admv8818_hpf_select(struct admv8818_state *st, u64 freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_hpf_select(st, freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv8818_lpf_select(struct admv8818_state *st, u64 freq) +{ + unsigned int lpf_step = 0, lpf_band = 0, i, j; + u64 freq_step; + int ret; + + if (freq > freq_range_lpf[3][1]) + goto lpf_write; + + if (freq < freq_range_lpf[0][0]) { + lpf_band = 1; + + goto lpf_write; + } + + for (i = 0; i < 4; i++) { + if (freq > freq_range_lpf[i][0] && freq < freq_range_lpf[i][1]) { + lpf_band = i + 1; + freq_step = div_u64((freq_range_lpf[i][1] - freq_range_lpf[i][0]), 15); + + for (j = 0; j <= 15; j++) { + if (freq < (freq_range_lpf[i][0] + (freq_step * j))) { + lpf_step = j; + break; + } + } + break; + } + } + +lpf_write: + ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW, + ADMV8818_SW_OUT_SET_WR0_MSK | + ADMV8818_SW_OUT_WR0_MSK, + FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) | + FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, lpf_band)); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER, + ADMV8818_LPF_WR0_MSK, + FIELD_PREP(ADMV8818_LPF_WR0_MSK, lpf_step)); +} + +static int admv8818_lpf_select(struct admv8818_state *st, u64 freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_lpf_select(st, freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int admv8818_rfin_band_select(struct admv8818_state *st) +{ + int ret; + + st->cf_hz = clk_get_rate(st->clkin); + + mutex_lock(&st->lock); + + ret = __admv8818_hpf_select(st, st->cf_hz); + if (ret) + goto exit; + + ret = __admv8818_lpf_select(st, st->cf_hz); +exit: + mutex_unlock(&st->lock); + return ret; +} + +static int __admv8818_read_hpf_freq(struct admv8818_state *st, u64 *hpf_freq) +{ + unsigned int data, hpf_band, hpf_state; + int ret; + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_SW, &data); + if (ret) + return ret; + + hpf_band = FIELD_GET(ADMV8818_SW_IN_WR0_MSK, data); + if (!hpf_band) { + *hpf_freq = 0; + return ret; + } + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_FILTER, &data); + if (ret) + return ret; + + hpf_state = FIELD_GET(ADMV8818_HPF_WR0_MSK, data); + + *hpf_freq = div_u64(freq_range_hpf[hpf_band - 1][1] - freq_range_hpf[hpf_band - 1][0], 15); + *hpf_freq = freq_range_hpf[hpf_band - 1][0] + (*hpf_freq * hpf_state); + + return ret; +} + +static int admv8818_read_hpf_freq(struct admv8818_state *st, u64 *hpf_freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_read_hpf_freq(st, hpf_freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) +{ + unsigned int data, lpf_band, lpf_state; + int ret; + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_SW, &data); + if (ret) + return ret; + + lpf_band = FIELD_GET(ADMV8818_SW_OUT_WR0_MSK, data); + if (!lpf_band) { + *lpf_freq = 0; + return ret; + } + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_FILTER, &data); + if (ret) + return ret; + + lpf_state = FIELD_GET(ADMV8818_LPF_WR0_MSK, data); + + *lpf_freq = div_u64(freq_range_lpf[lpf_band - 1][1] - freq_range_lpf[lpf_band - 1][0], 15); + *lpf_freq = freq_range_lpf[lpf_band - 1][0] + (*lpf_freq * lpf_state); + + return ret; +} + +static int admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_read_lpf_freq(st, lpf_freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int admv8818_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct admv8818_state *st = iio_priv(indio_dev); + + u64 freq = ((u64)val2 << 32 | (u32)val); + + switch (info) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return admv8818_lpf_select(st, freq); + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + return admv8818_hpf_select(st, freq); + default: + return -EINVAL; + } +} + +static int admv8818_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct admv8818_state *st = iio_priv(indio_dev); + int ret; + u64 freq; + + switch (info) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = admv8818_read_lpf_freq(st, &freq); + if (ret) + return ret; + + *val = (u32)freq; + *val2 = (u32)(freq >> 32); + + return IIO_VAL_INT_64; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + ret = admv8818_read_hpf_freq(st, &freq); + if (ret) + return ret; + + *val = (u32)freq; + *val2 = (u32)(freq >> 32); + + return IIO_VAL_INT_64; + default: + return -EINVAL; + } +} + +static int admv8818_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct admv8818_state *st = iio_priv(indio_dev); + + if (read_val) + return regmap_read(st->regmap, reg, read_val); + else + return regmap_write(st->regmap, reg, write_val); +} + +static int admv8818_get_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct admv8818_state *st = iio_priv(indio_dev); + + return st->filter_mode; +} + +static int admv8818_set_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct admv8818_state *st = iio_priv(indio_dev); + int ret = 0; + + if (!st->clkin) { + if (mode == ADMV8818_MANUAL_MODE) + return 0; + + return -EINVAL; + } + + switch (mode) { + case ADMV8818_AUTO_MODE: + if (!st->filter_mode) + return 0; + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = clk_notifier_register(st->clkin, &st->nb); + if (ret) { + clk_disable_unprepare(st->clkin); + + return ret; + } + + break; + case ADMV8818_MANUAL_MODE: + if (st->filter_mode) + return 0; + + clk_disable_unprepare(st->clkin); + + ret = clk_notifier_unregister(st->clkin, &st->nb); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + st->filter_mode = mode; + + return ret; +} + +static const struct iio_info admv8818_info = { + .write_raw = admv8818_write_raw, + .read_raw = admv8818_read_raw, + .debugfs_reg_access = &admv8818_reg_access, +}; + +static const struct iio_enum admv8818_mode_enum = { + .items = admv8818_modes, + .num_items = ARRAY_SIZE(admv8818_modes), + .get = admv8818_get_mode, + .set = admv8818_set_mode, +}; + +static const struct iio_chan_spec_ext_info admv8818_ext_info[] = { + IIO_ENUM("filter_mode", IIO_SHARED_BY_ALL, &admv8818_mode_enum), + IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_ALL, &admv8818_mode_enum), + { }, +}; + +#define ADMV8818_CHAN(_channel) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) \ +} + +#define ADMV8818_CHAN_BW_CF(_channel, _admv8818_ext_info) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .ext_info = _admv8818_ext_info, \ +} + +static const struct iio_chan_spec admv8818_channels[] = { + ADMV8818_CHAN(0), + ADMV8818_CHAN_BW_CF(0, admv8818_ext_info), +}; + +static int admv8818_freq_change(struct notifier_block *nb, unsigned long action, void *data) +{ + struct admv8818_state *st = container_of(nb, struct admv8818_state, nb); + + if (action == POST_RATE_CHANGE) + return notifier_from_errno(admv8818_rfin_band_select(st)); + + return NOTIFY_OK; +} + +static void admv8818_clk_notifier_unreg(void *data) +{ + struct admv8818_state *st = data; + + if (st->filter_mode == 0) + clk_notifier_unregister(st->clkin, &st->nb); +} + +static void admv8818_clk_disable(void *data) +{ + struct admv8818_state *st = data; + + if (st->filter_mode == 0) + clk_disable_unprepare(st->clkin); +} + +static int admv8818_init(struct admv8818_state *st) +{ + int ret; + struct spi_device *spi = st->spi; + unsigned int chip_id; + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_A, + ADMV8818_SOFTRESET_N_MSK | + ADMV8818_SOFTRESET_MSK, + FIELD_PREP(ADMV8818_SOFTRESET_N_MSK, 1) | + FIELD_PREP(ADMV8818_SOFTRESET_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADMV8818 Soft Reset failed.\n"); + return ret; + } + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_A, + ADMV8818_SDOACTIVE_N_MSK | + ADMV8818_SDOACTIVE_MSK, + FIELD_PREP(ADMV8818_SDOACTIVE_N_MSK, 1) | + FIELD_PREP(ADMV8818_SDOACTIVE_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADMV8818 SDO Enable failed.\n"); + return ret; + } + + ret = regmap_read(st->regmap, ADMV8818_REG_CHIPTYPE, &chip_id); + if (ret) { + dev_err(&spi->dev, "ADMV8818 Chip ID read failed.\n"); + return ret; + } + + if (chip_id != 0x1) { + dev_err(&spi->dev, "ADMV8818 Invalid Chip ID.\n"); + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_B, + ADMV8818_SINGLE_INSTRUCTION_MSK, + FIELD_PREP(ADMV8818_SINGLE_INSTRUCTION_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADMV8818 Single Instruction failed.\n"); + return ret; + } + + if (st->clkin) + return admv8818_rfin_band_select(st); + else + return 0; +} + +static int admv8818_clk_setup(struct admv8818_state *st) +{ + struct spi_device *spi = st->spi; + int ret; + + st->clkin = devm_clk_get_optional(&spi->dev, "rf_in"); + if (IS_ERR(st->clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + "failed to get the input clock\n"); + else if (!st->clkin) + return 0; + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, admv8818_clk_disable, st); + if (ret) + return ret; + + st->nb.notifier_call = admv8818_freq_change; + ret = clk_notifier_register(st->clkin, &st->nb); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(&spi->dev, admv8818_clk_notifier_unreg, st); +} + +static int admv8818_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct regmap *regmap; + struct admv8818_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_spi(spi, &admv8818_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + st = iio_priv(indio_dev); + st->regmap = regmap; + + indio_dev->info = &admv8818_info; + indio_dev->name = "admv8818"; + indio_dev->channels = admv8818_channels; + indio_dev->num_channels = ARRAY_SIZE(admv8818_channels); + + st->spi = spi; + + ret = admv8818_clk_setup(st); + if (ret) + return ret; + + mutex_init(&st->lock); + + ret = admv8818_init(st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id admv8818_id[] = { + { "admv8818", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, admv8818_id); + +static const struct of_device_id admv8818_of_match[] = { + { .compatible = "adi,admv8818" }, + {} +}; +MODULE_DEVICE_TABLE(of, admv8818_of_match); + +static struct spi_driver admv8818_driver = { + .driver = { + .name = "admv8818", + .of_match_table = admv8818_of_match, + }, + .probe = admv8818_probe, + .id_table = admv8818_id, +}; +module_spi_driver(admv8818_driver); + +MODULE_AUTHOR("Antoniu Miclaus Date: Mon, 13 Dec 2021 11:08:25 +0000 Subject: drivers:iio:dac: Add AD3552R driver support The AD3552R-16 is a low drift ultrafast, 16-bit accuracy, current output digital-to-analog converter (DAC) designed to generate multiple output voltage span ranges. The AD3552R-16 operates with a fixed 2.5V reference. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad3552r.pdf Signed-off-by: Mihail Chindris Link: https://lore.kernel.org/r/20211213110825.244347-3-mihail.chindris@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad3552r.c | 1138 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1149 insertions(+) create mode 100644 drivers/iio/dac/ad3552r.c (limited to 'drivers/iio') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b95619f18fa5..bfcf7568de32 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -6,6 +6,16 @@ menu "Digital to analog converters" +config AD3552R + tristate "Analog Devices AD3552R DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD3552R + Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad3552r. + config AD5064 tristate "Analog Devices AD5064 and similar multi-channel DAC driver" depends on (SPI_MASTER && I2C!=m) || I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 3c17246ee89b..01a50131572f 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD3552R) += ad3552r.o obj-$(CONFIG_AD5360) += ad5360.o obj-$(CONFIG_AD5380) += ad5380.o obj-$(CONFIG_AD5421) += ad5421.o diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c new file mode 100644 index 000000000000..97f13c0b9631 --- /dev/null +++ b/drivers/iio/dac/ad3552r.c @@ -0,0 +1,1138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD3552R + * Digital to Analog converter driver + * + * Copyright 2021 Analog Devices Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register addresses */ +/* Primary address space */ +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A 0x00 +#define AD3552R_MASK_SOFTWARE_RESET (BIT(7) | BIT(0)) +#define AD3552R_MASK_ADDR_ASCENSION BIT(5) +#define AD3552R_MASK_SDO_ACTIVE BIT(4) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B 0x01 +#define AD3552R_MASK_SINGLE_INST BIT(7) +#define AD3552R_MASK_SHORT_INSTRUCTION BIT(3) +#define AD3552R_REG_ADDR_DEVICE_CONFIG 0x02 +#define AD3552R_MASK_DEVICE_STATUS(n) BIT(4 + (n)) +#define AD3552R_MASK_CUSTOM_MODES GENMASK(3, 2) +#define AD3552R_MASK_OPERATING_MODES GENMASK(1, 0) +#define AD3552R_REG_ADDR_CHIP_TYPE 0x03 +#define AD3552R_MASK_CLASS GENMASK(7, 0) +#define AD3552R_REG_ADDR_PRODUCT_ID_L 0x04 +#define AD3552R_REG_ADDR_PRODUCT_ID_H 0x05 +#define AD3552R_REG_ADDR_CHIP_GRADE 0x06 +#define AD3552R_MASK_GRADE GENMASK(7, 4) +#define AD3552R_MASK_DEVICE_REVISION GENMASK(3, 0) +#define AD3552R_REG_ADDR_SCRATCH_PAD 0x0A +#define AD3552R_REG_ADDR_SPI_REVISION 0x0B +#define AD3552R_REG_ADDR_VENDOR_L 0x0C +#define AD3552R_REG_ADDR_VENDOR_H 0x0D +#define AD3552R_REG_ADDR_STREAM_MODE 0x0E +#define AD3552R_MASK_LENGTH GENMASK(7, 0) +#define AD3552R_REG_ADDR_TRANSFER_REGISTER 0x0F +#define AD3552R_MASK_MULTI_IO_MODE GENMASK(7, 6) +#define AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE BIT(2) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C 0x10 +#define AD3552R_MASK_CRC_ENABLE (GENMASK(7, 6) |\ + GENMASK(1, 0)) +#define AD3552R_MASK_STRICT_REGISTER_ACCESS BIT(5) +#define AD3552R_REG_ADDR_INTERFACE_STATUS_A 0x11 +#define AD3552R_MASK_INTERFACE_NOT_READY BIT(7) +#define AD3552R_MASK_CLOCK_COUNTING_ERROR BIT(5) +#define AD3552R_MASK_INVALID_OR_NO_CRC BIT(3) +#define AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER BIT(2) +#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS BIT(1) +#define AD3552R_MASK_REGISTER_ADDRESS_INVALID BIT(0) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D 0x14 +#define AD3552R_MASK_ALERT_ENABLE_PULLUP BIT(6) +#define AD3552R_MASK_MEM_CRC_EN BIT(4) +#define AD3552R_MASK_SDO_DRIVE_STRENGTH GENMASK(3, 2) +#define AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN BIT(1) +#define AD3552R_MASK_SPI_CONFIG_DDR BIT(0) +#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG 0x15 +#define AD3552R_MASK_IDUMP_FAST_MODE BIT(6) +#define AD3552R_MASK_SAMPLE_HOLD_DIFFERENTIAL_USER_EN BIT(5) +#define AD3552R_MASK_SAMPLE_HOLD_USER_TRIM GENMASK(4, 3) +#define AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE BIT(2) +#define AD3552R_MASK_REFERENCE_VOLTAGE_SEL GENMASK(1, 0) +#define AD3552R_REG_ADDR_ERR_ALARM_MASK 0x16 +#define AD3552R_MASK_REF_RANGE_ALARM BIT(6) +#define AD3552R_MASK_CLOCK_COUNT_ERR_ALARM BIT(5) +#define AD3552R_MASK_MEM_CRC_ERR_ALARM BIT(4) +#define AD3552R_MASK_SPI_CRC_ERR_ALARM BIT(3) +#define AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM BIT(2) +#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM BIT(1) +#define AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0) +#define AD3552R_REG_ADDR_ERR_STATUS 0x17 +#define AD3552R_MASK_REF_RANGE_ERR_STATUS BIT(6) +#define AD3552R_MASK_DUAL_SPI_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5) +#define AD3552R_MASK_MEM_CRC_ERR_STATUS BIT(4) +#define AD3552R_MASK_RESET_STATUS BIT(0) +#define AD3552R_REG_ADDR_POWERDOWN_CONFIG 0x18 +#define AD3552R_MASK_CH_DAC_POWERDOWN(ch) BIT(4 + (ch)) +#define AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch) BIT(ch) +#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE 0x19 +#define AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) ((ch) ? GENMASK(7, 4) :\ + GENMASK(3, 0)) +#define AD3552R_REG_ADDR_CH_OFFSET(ch) (0x1B + (ch) * 2) +#define AD3552R_MASK_CH_OFFSET_BITS_0_7 GENMASK(7, 0) +#define AD3552R_REG_ADDR_CH_GAIN(ch) (0x1C + (ch) * 2) +#define AD3552R_MASK_CH_RANGE_OVERRIDE BIT(7) +#define AD3552R_MASK_CH_GAIN_SCALING_N GENMASK(6, 5) +#define AD3552R_MASK_CH_GAIN_SCALING_P GENMASK(4, 3) +#define AD3552R_MASK_CH_OFFSET_POLARITY BIT(2) +#define AD3552R_MASK_CH_OFFSET_BIT_8 BIT(0) +/* + * Secondary region + * For multibyte registers specify the highest address because the access is + * done in descending order + */ +#define AD3552R_SECONDARY_REGION_START 0x28 +#define AD3552R_REG_ADDR_HW_LDAC_16B 0x28 +#define AD3552R_REG_ADDR_CH_DAC_16B(ch) (0x2C - (1 - ch) * 2) +#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B 0x2E +#define AD3552R_REG_ADDR_CH_SELECT_16B 0x2F +#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B 0x31 +#define AD3552R_REG_ADDR_SW_LDAC_16B 0x32 +#define AD3552R_REG_ADDR_CH_INPUT_16B(ch) (0x36 - (1 - ch) * 2) +/* 3 bytes registers */ +#define AD3552R_REG_START_24B 0x37 +#define AD3552R_REG_ADDR_HW_LDAC_24B 0x37 +#define AD3552R_REG_ADDR_CH_DAC_24B(ch) (0x3D - (1 - ch) * 3) +#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B 0x40 +#define AD3552R_REG_ADDR_CH_SELECT_24B 0x41 +#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B 0x44 +#define AD3552R_REG_ADDR_SW_LDAC_24B 0x45 +#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - ch) * 3) + +/* Useful defines */ +#define AD3552R_NUM_CH 2 +#define AD3552R_MASK_CH(ch) BIT(ch) +#define AD3552R_MASK_ALL_CH GENMASK(1, 0) +#define AD3552R_MAX_REG_SIZE 3 +#define AD3552R_READ_BIT BIT(7) +#define AD3552R_ADDR_MASK GENMASK(6, 0) +#define AD3552R_MASK_DAC_12B 0xFFF0 +#define AD3552R_DEFAULT_CONFIG_B_VALUE 0x8 +#define AD3552R_SCRATCH_PAD_TEST_VAL1 0x34 +#define AD3552R_SCRATCH_PAD_TEST_VAL2 0xB2 +#define AD3552R_GAIN_SCALE 1000 +#define AD3552R_LDAC_PULSE_US 100 + +enum ad3552r_ch_vref_select { + /* Internal source with Vref I/O floating */ + AD3552R_INTERNAL_VREF_PIN_FLOATING, + /* Internal source with Vref I/O at 2.5V */ + AD3552R_INTERNAL_VREF_PIN_2P5V, + /* External source with Vref I/O as input */ + AD3552R_EXTERNAL_VREF_PIN_INPUT +}; + +enum ad3542r_id { + AD3542R_ID = 0x4008, + AD3552R_ID = 0x4009, +}; + +enum ad3552r_ch_output_range { + /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ + AD3552R_CH_OUTPUT_RANGE_0__2P5V, + /* Range from 0 V to 5 V. Requires Rfb1x connection */ + AD3552R_CH_OUTPUT_RANGE_0__5V, + /* Range from 0 V to 10 V. Requires Rfb2x connection */ + AD3552R_CH_OUTPUT_RANGE_0__10V, + /* Range from -5 V to 5 V. Requires Rfb2x connection */ + AD3552R_CH_OUTPUT_RANGE_NEG_5__5V, + /* Range from -10 V to 10 V. Requires Rfb4x connection */ + AD3552R_CH_OUTPUT_RANGE_NEG_10__10V, +}; + +static const s32 ad3552r_ch_ranges[][2] = { + [AD3552R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500}, + [AD3552R_CH_OUTPUT_RANGE_0__5V] = {0, 5000}, + [AD3552R_CH_OUTPUT_RANGE_0__10V] = {0, 10000}, + [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000}, + [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = {-10000, 10000} +}; + +enum ad3542r_ch_output_range { + /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__2P5V, + /* Range from 0 V to 3 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__3V, + /* Range from 0 V to 5 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__5V, + /* Range from 0 V to 10 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_0__10V, + /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V, + /* Range from -5 V to 5 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_NEG_5__5V, +}; + +static const s32 ad3542r_ch_ranges[][2] = { + [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500}, + [AD3542R_CH_OUTPUT_RANGE_0__3V] = {0, 3000}, + [AD3542R_CH_OUTPUT_RANGE_0__5V] = {0, 5000}, + [AD3542R_CH_OUTPUT_RANGE_0__10V] = {0, 10000}, + [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = {-2500, 7500}, + [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000} +}; + +enum ad3552r_ch_gain_scaling { + /* Gain scaling of 1 */ + AD3552R_CH_GAIN_SCALING_1, + /* Gain scaling of 0.5 */ + AD3552R_CH_GAIN_SCALING_0_5, + /* Gain scaling of 0.25 */ + AD3552R_CH_GAIN_SCALING_0_25, + /* Gain scaling of 0.125 */ + AD3552R_CH_GAIN_SCALING_0_125, +}; + +/* Gain * AD3552R_GAIN_SCALE */ +static const s32 gains_scaling_table[] = { + [AD3552R_CH_GAIN_SCALING_1] = 1000, + [AD3552R_CH_GAIN_SCALING_0_5] = 500, + [AD3552R_CH_GAIN_SCALING_0_25] = 250, + [AD3552R_CH_GAIN_SCALING_0_125] = 125 +}; + +enum ad3552r_dev_attributes { + /* - Direct register values */ + /* From 0-3 */ + AD3552R_SDO_DRIVE_STRENGTH, + /* + * 0 -> Internal Vref, vref_io pin floating (default) + * 1 -> Internal Vref, vref_io driven by internal vref + * 2 or 3 -> External Vref + */ + AD3552R_VREF_SELECT, + /* Read registers in ascending order if set. Else descending */ + AD3552R_ADDR_ASCENSION, +}; + +enum ad3552r_ch_attributes { + /* DAC powerdown */ + AD3552R_CH_DAC_POWERDOWN, + /* DAC amplifier powerdown */ + AD3552R_CH_AMPLIFIER_POWERDOWN, + /* Select the output range. Select from enum ad3552r_ch_output_range */ + AD3552R_CH_OUTPUT_RANGE_SEL, + /* + * Over-rider the range selector in order to manually set the output + * voltage range + */ + AD3552R_CH_RANGE_OVERRIDE, + /* Manually set the offset voltage */ + AD3552R_CH_GAIN_OFFSET, + /* Sets the polarity of the offset. */ + AD3552R_CH_GAIN_OFFSET_POLARITY, + /* PDAC gain scaling */ + AD3552R_CH_GAIN_SCALING_P, + /* NDAC gain scaling */ + AD3552R_CH_GAIN_SCALING_N, + /* Rfb value */ + AD3552R_CH_RFB, + /* Channel select. When set allow Input -> DAC and Mask -> DAC */ + AD3552R_CH_SELECT, +}; + +struct ad3552r_ch_data { + s32 scale_int; + s32 scale_dec; + s32 offset_int; + s32 offset_dec; + s16 gain_offset; + u16 rfb; + u8 n; + u8 p; + u8 range; + bool range_override; +}; + +struct ad3552r_desc { + /* Used to look the spi bus for atomic operations where needed */ + struct mutex lock; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_ldac; + struct spi_device *spi; + struct ad3552r_ch_data ch_data[AD3552R_NUM_CH]; + struct iio_chan_spec channels[AD3552R_NUM_CH + 1]; + unsigned long enabled_ch; + unsigned int num_ch; + enum ad3542r_id chip_id; +}; + +static const u16 addr_mask_map[][2] = { + [AD3552R_ADDR_ASCENSION] = { + AD3552R_REG_ADDR_INTERFACE_CONFIG_A, + AD3552R_MASK_ADDR_ASCENSION + }, + [AD3552R_SDO_DRIVE_STRENGTH] = { + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SDO_DRIVE_STRENGTH + }, + [AD3552R_VREF_SELECT] = { + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, + AD3552R_MASK_REFERENCE_VOLTAGE_SEL + }, +}; + +/* 0 -> reg addr, 1->ch0 mask, 2->ch1 mask */ +static const u16 addr_mask_map_ch[][3] = { + [AD3552R_CH_DAC_POWERDOWN] = { + AD3552R_REG_ADDR_POWERDOWN_CONFIG, + AD3552R_MASK_CH_DAC_POWERDOWN(0), + AD3552R_MASK_CH_DAC_POWERDOWN(1) + }, + [AD3552R_CH_AMPLIFIER_POWERDOWN] = { + AD3552R_REG_ADDR_POWERDOWN_CONFIG, + AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), + AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1) + }, + [AD3552R_CH_OUTPUT_RANGE_SEL] = { + AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, + AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), + AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1) + }, + [AD3552R_CH_SELECT] = { + AD3552R_REG_ADDR_CH_SELECT_16B, + AD3552R_MASK_CH(0), + AD3552R_MASK_CH(1) + } +}; + +static u8 _ad3552r_reg_len(u8 addr) +{ + switch (addr) { + case AD3552R_REG_ADDR_HW_LDAC_16B: + case AD3552R_REG_ADDR_CH_SELECT_16B: + case AD3552R_REG_ADDR_SW_LDAC_16B: + case AD3552R_REG_ADDR_HW_LDAC_24B: + case AD3552R_REG_ADDR_CH_SELECT_24B: + case AD3552R_REG_ADDR_SW_LDAC_24B: + return 1; + default: + break; + } + + if (addr > AD3552R_REG_ADDR_HW_LDAC_24B) + return 3; + if (addr > AD3552R_REG_ADDR_HW_LDAC_16B) + return 2; + + return 1; +} + +/* SPI transfer to device */ +static int ad3552r_transfer(struct ad3552r_desc *dac, u8 addr, u32 len, + u8 *data, bool is_read) +{ + /* Maximum transfer: Addr (1B) + 2 * (Data Reg (3B)) + SW LDAC(1B) */ + u8 buf[8]; + + buf[0] = addr & AD3552R_ADDR_MASK; + buf[0] |= is_read ? AD3552R_READ_BIT : 0; + if (is_read) + return spi_write_then_read(dac->spi, buf, 1, data, len); + + memcpy(buf + 1, data, len); + return spi_write_then_read(dac->spi, buf, len + 1, NULL, 0); +} + +static int ad3552r_write_reg(struct ad3552r_desc *dac, u8 addr, u16 val) +{ + u8 reg_len; + u8 buf[AD3552R_MAX_REG_SIZE] = { 0 }; + + reg_len = _ad3552r_reg_len(addr); + if (reg_len == 2) + /* Only DAC register are 2 bytes wide */ + val &= AD3552R_MASK_DAC_12B; + if (reg_len == 1) + buf[0] = val & 0xFF; + else + /* reg_len can be 2 or 3, but 3rd bytes needs to be set to 0 */ + put_unaligned_be16(val, buf); + + return ad3552r_transfer(dac, addr, reg_len, buf, false); +} + +static int ad3552r_read_reg(struct ad3552r_desc *dac, u8 addr, u16 *val) +{ + int err; + u8 reg_len, buf[AD3552R_MAX_REG_SIZE] = { 0 }; + + reg_len = _ad3552r_reg_len(addr); + err = ad3552r_transfer(dac, addr, reg_len, buf, true); + if (err) + return err; + + if (reg_len == 1) + *val = buf[0]; + else + /* reg_len can be 2 or 3, but only first 2 bytes are relevant */ + *val = get_unaligned_be16(buf); + + return 0; +} + +static u16 ad3552r_field_prep(u16 val, u16 mask) +{ + return (val << __ffs(mask)) & mask; +} + +/* Update field of a register, shift val if needed */ +static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask, + u16 val) +{ + int ret; + u16 reg; + + ret = ad3552r_read_reg(dac, addr, ®); + if (ret < 0) + return ret; + + reg &= ~mask; + reg |= ad3552r_field_prep(val, mask); + + return ad3552r_write_reg(dac, addr, reg); +} + +static int ad3552r_set_ch_value(struct ad3552r_desc *dac, + enum ad3552r_ch_attributes attr, + u8 ch, + u16 val) +{ + /* Update register related to attributes in chip */ + return ad3552r_update_reg_field(dac, addr_mask_map_ch[attr][0], + addr_mask_map_ch[attr][ch + 1], val); +} + +#define AD3552R_CH_DAC(_idx) ((struct iio_chan_spec) { \ + .type = IIO_VOLTAGE, \ + .output = true, \ + .indexed = true, \ + .channel = _idx, \ + .scan_index = _idx, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_ENABLE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +}) + +static int ad3552r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad3552r_desc *dac = iio_priv(indio_dev); + u16 tmp_val; + int err; + u8 ch = chan->channel; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&dac->lock); + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_CH_DAC_24B(ch), + &tmp_val); + mutex_unlock(&dac->lock); + if (err < 0) + return err; + *val = tmp_val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_ENABLE: + mutex_lock(&dac->lock); + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG, + &tmp_val); + mutex_unlock(&dac->lock); + if (err < 0) + return err; + *val = !((tmp_val & AD3552R_MASK_CH_DAC_POWERDOWN(ch)) >> + __ffs(AD3552R_MASK_CH_DAC_POWERDOWN(ch))); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = dac->ch_data[ch].scale_int; + *val2 = dac->ch_data[ch].scale_dec; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = dac->ch_data[ch].offset_int; + *val2 = dac->ch_data[ch].offset_dec; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ad3552r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad3552r_desc *dac = iio_priv(indio_dev); + int err; + + mutex_lock(&dac->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = ad3552r_write_reg(dac, + AD3552R_REG_ADDR_CH_DAC_24B(chan->channel), + val); + break; + case IIO_CHAN_INFO_ENABLE: + err = ad3552r_set_ch_value(dac, AD3552R_CH_DAC_POWERDOWN, + chan->channel, !val); + break; + default: + err = -EINVAL; + break; + } + mutex_unlock(&dac->lock); + + return err; +} + +static const struct iio_info ad3552r_iio_info = { + .read_raw = ad3552r_read_raw, + .write_raw = ad3552r_write_raw +}; + +static int32_t ad3552r_trigger_hw_ldac(struct gpio_desc *ldac) +{ + gpiod_set_value_cansleep(ldac, 0); + usleep_range(AD3552R_LDAC_PULSE_US, AD3552R_LDAC_PULSE_US + 10); + gpiod_set_value_cansleep(ldac, 1); + + return 0; +} + +static int ad3552r_write_all_channels(struct ad3552r_desc *dac, u8 *data) +{ + int err, len; + u8 addr, buff[AD3552R_NUM_CH * AD3552R_MAX_REG_SIZE + 1]; + + addr = AD3552R_REG_ADDR_CH_INPUT_24B(1); + /* CH1 */ + memcpy(buff, data + 2, 2); + buff[2] = 0; + /* CH0 */ + memcpy(buff + 3, data, 2); + buff[5] = 0; + len = 6; + if (!dac->gpio_ldac) { + /* Software LDAC */ + buff[6] = AD3552R_MASK_ALL_CH; + ++len; + } + err = ad3552r_transfer(dac, addr, len, buff, false); + if (err) + return err; + + if (dac->gpio_ldac) + return ad3552r_trigger_hw_ldac(dac->gpio_ldac); + + return 0; +} + +static int ad3552r_write_codes(struct ad3552r_desc *dac, u32 mask, u8 *data) +{ + int err; + u8 addr, buff[AD3552R_MAX_REG_SIZE]; + + if (mask == AD3552R_MASK_ALL_CH) { + if (memcmp(data, data + 2, 2) != 0) + return ad3552r_write_all_channels(dac, data); + + addr = AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B; + } else { + addr = AD3552R_REG_ADDR_CH_INPUT_24B(__ffs(mask)); + } + + memcpy(buff, data, 2); + buff[2] = 0; + err = ad3552r_transfer(dac, addr, 3, data, false); + if (err) + return err; + + if (dac->gpio_ldac) + return ad3552r_trigger_hw_ldac(dac->gpio_ldac); + + return ad3552r_write_reg(dac, AD3552R_REG_ADDR_SW_LDAC_24B, mask); +} + +static irqreturn_t ad3552r_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct iio_buffer *buf = indio_dev->buffer; + struct ad3552r_desc *dac = iio_priv(indio_dev); + /* Maximum size of a scan */ + u8 buff[AD3552R_NUM_CH * AD3552R_MAX_REG_SIZE]; + int err; + + memset(buff, 0, sizeof(buff)); + err = iio_pop_from_buffer(buf, buff); + if (err) + goto end; + + mutex_lock(&dac->lock); + ad3552r_write_codes(dac, *indio_dev->active_scan_mask, buff); + mutex_unlock(&dac->lock); +end: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad3552r_check_scratch_pad(struct ad3552r_desc *dac) +{ + const u16 val1 = AD3552R_SCRATCH_PAD_TEST_VAL1; + const u16 val2 = AD3552R_SCRATCH_PAD_TEST_VAL2; + u16 val; + int err; + + err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val1); + if (err < 0) + return err; + + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val); + if (err < 0) + return err; + + if (val1 != val) + return -ENODEV; + + err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val2); + if (err < 0) + return err; + + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val); + if (err < 0) + return err; + + if (val2 != val) + return -ENODEV; + + return 0; +} + +struct reg_addr_pool { + struct ad3552r_desc *dac; + u8 addr; +}; + +static int ad3552r_read_reg_wrapper(struct reg_addr_pool *addr) +{ + int err; + u16 val; + + err = ad3552r_read_reg(addr->dac, addr->addr, &val); + if (err) + return err; + + return val; +} + +static int ad3552r_reset(struct ad3552r_desc *dac) +{ + struct reg_addr_pool addr; + int ret; + u16 val; + + dac->gpio_reset = devm_gpiod_get_optional(&dac->spi->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(dac->gpio_reset)) + return dev_err_probe(&dac->spi->dev, PTR_ERR(dac->gpio_reset), + "Error while getting gpio reset"); + + if (dac->gpio_reset) { + /* Perform hardware reset */ + usleep_range(10, 20); + gpiod_set_value_cansleep(dac->gpio_reset, 1); + } else { + /* Perform software reset if no GPIO provided */ + ret = ad3552r_update_reg_field(dac, + AD3552R_REG_ADDR_INTERFACE_CONFIG_A, + AD3552R_MASK_SOFTWARE_RESET, + AD3552R_MASK_SOFTWARE_RESET); + if (ret < 0) + return ret; + + } + + addr.dac = dac; + addr.addr = AD3552R_REG_ADDR_INTERFACE_CONFIG_B; + ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val, + val == AD3552R_DEFAULT_CONFIG_B_VALUE || + val < 0, + 5000, 50000); + if (val < 0) + ret = val; + if (ret) { + dev_err(&dac->spi->dev, "Error while resetting"); + return ret; + } + + ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val, + !(val & AD3552R_MASK_INTERFACE_NOT_READY) || + val < 0, + 5000, 50000); + if (val < 0) + ret = val; + if (ret) { + dev_err(&dac->spi->dev, "Error while resetting"); + return ret; + } + + return ad3552r_update_reg_field(dac, + addr_mask_map[AD3552R_ADDR_ASCENSION][0], + addr_mask_map[AD3552R_ADDR_ASCENSION][1], + val); +} + +static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min, + s32 *v_max) +{ + s64 vref, tmp, common, offset, gn, gp; + /* + * From datasheet formula (In Volts): + * Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03] + * Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03] + * Calculus are converted to milivolts + */ + vref = 2500; + /* 2.5 * 1.03 * 1000 (To mV) */ + common = 2575 * dac->ch_data[i].rfb; + offset = dac->ch_data[i].gain_offset; + + gn = gains_scaling_table[dac->ch_data[i].n]; + tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common; + tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); + *v_max = vref + tmp; + + gp = gains_scaling_table[dac->ch_data[i].p]; + tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common; + tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); + *v_min = vref - tmp; +} + +static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch) +{ + s32 idx, v_max, v_min, span, rem; + s64 tmp; + + if (dac->ch_data[ch].range_override) { + ad3552r_get_custom_range(dac, ch, &v_min, &v_max); + } else { + /* Normal range */ + idx = dac->ch_data[ch].range; + if (dac->chip_id == AD3542R_ID) { + v_min = ad3542r_ch_ranges[idx][0]; + v_max = ad3542r_ch_ranges[idx][1]; + } else { + v_min = ad3552r_ch_ranges[idx][0]; + v_max = ad3552r_ch_ranges[idx][1]; + } + } + + /* + * From datasheet formula: + * Vout = Span * (D / 65536) + Vmin + * Converted to scale and offset: + * Scale = Span / 65536 + * Offset = 65536 * Vmin / Span + * + * Reminders are in micros in order to be printed as + * IIO_VAL_INT_PLUS_MICRO + */ + span = v_max - v_min; + dac->ch_data[ch].scale_int = div_s64_rem(span, 65536, &rem); + /* Do operations in microvolts */ + dac->ch_data[ch].scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000, + 65536); + + dac->ch_data[ch].offset_int = div_s64_rem(v_min * 65536, span, &rem); + tmp = (s64)rem * 1000000; + dac->ch_data[ch].offset_dec = div_s64(tmp, span); +} + +static int ad3552r_find_range(u16 id, s32 *vals) +{ + int i, len; + const s32 (*ranges)[2]; + + if (id == AD3542R_ID) { + len = ARRAY_SIZE(ad3542r_ch_ranges); + ranges = ad3542r_ch_ranges; + } else { + len = ARRAY_SIZE(ad3552r_ch_ranges); + ranges = ad3552r_ch_ranges; + } + + for (i = 0; i < len; i++) + if (vals[0] == ranges[i][0] * 1000 && + vals[1] == ranges[i][1] * 1000) + return i; + + return -EINVAL; +} + +static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, + struct fwnode_handle *child, + u32 ch) +{ + struct device *dev = &dac->spi->dev; + struct fwnode_handle *gain_child; + u32 val; + int err; + u8 addr; + u16 reg = 0, offset; + + gain_child = fwnode_get_named_child_node(child, + "custom-output-range-config"); + if (IS_ERR(gain_child)) { + dev_err(dev, + "mandatory custom-output-range-config property missing\n"); + return PTR_ERR(gain_child); + } + + dac->ch_data[ch].range_override = 1; + reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE); + + err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val); + if (err) { + dev_err(dev, "mandatory adi,gain-scaling-p property missing\n"); + goto put_child; + } + reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P); + dac->ch_data[ch].p = val; + + err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val); + if (err) { + dev_err(dev, "mandatory adi,gain-scaling-n property missing\n"); + goto put_child; + } + reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N); + dac->ch_data[ch].n = val; + + err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val); + if (err) { + dev_err(dev, "mandatory adi,rfb-ohms property missing\n"); + goto put_child; + } + dac->ch_data[ch].rfb = val; + + err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val); + if (err) { + dev_err(dev, "mandatory adi,gain-offset property missing\n"); + goto put_child; + } + dac->ch_data[ch].gain_offset = val; + + offset = abs((s32)val); + reg |= ad3552r_field_prep((offset >> 8), AD3552R_MASK_CH_OFFSET_BIT_8); + + reg |= ad3552r_field_prep((s32)val < 0, AD3552R_MASK_CH_OFFSET_POLARITY); + addr = AD3552R_REG_ADDR_CH_GAIN(ch); + err = ad3552r_write_reg(dac, addr, + offset & AD3552R_MASK_CH_OFFSET_BITS_0_7); + if (err) { + dev_err(dev, "Error writing register\n"); + goto put_child; + } + + err = ad3552r_write_reg(dac, addr, reg); + if (err) { + dev_err(dev, "Error writing register\n"); + goto put_child; + } + +put_child: + fwnode_handle_put(gain_child); + + return err; +} + +static void ad3552r_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int ad3552r_configure_device(struct ad3552r_desc *dac) +{ + struct device *dev = &dac->spi->dev; + struct fwnode_handle *child; + struct regulator *vref; + int err, cnt = 0, voltage, delta = 100000; + u32 vals[2], val, ch; + + dac->gpio_ldac = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH); + if (IS_ERR(dac->gpio_ldac)) + return dev_err_probe(dev, PTR_ERR(dac->gpio_ldac), + "Error getting gpio ldac"); + + vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(vref)) { + if (PTR_ERR(vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(vref), + "Error getting vref"); + + if (device_property_read_bool(dev, "adi,vref-out-en")) + val = AD3552R_INTERNAL_VREF_PIN_2P5V; + else + val = AD3552R_INTERNAL_VREF_PIN_FLOATING; + } else { + err = regulator_enable(vref); + if (err) { + dev_err(dev, "Failed to enable external vref supply\n"); + return err; + } + + err = devm_add_action_or_reset(dev, ad3552r_reg_disable, vref); + if (err) { + regulator_disable(vref); + return err; + } + + voltage = regulator_get_voltage(vref); + if (voltage > 2500000 + delta || voltage < 2500000 - delta) { + dev_warn(dev, "vref-supply must be 2.5V"); + return -EINVAL; + } + val = AD3552R_EXTERNAL_VREF_PIN_INPUT; + } + + err = ad3552r_update_reg_field(dac, + addr_mask_map[AD3552R_VREF_SELECT][0], + addr_mask_map[AD3552R_VREF_SELECT][1], + val); + if (err) + return err; + + err = device_property_read_u32(dev, "adi,sdo-drive-strength", &val); + if (!err) { + if (val > 3) { + dev_err(dev, "adi,sdo-drive-strength must be less than 4\n"); + return -EINVAL; + } + + err = ad3552r_update_reg_field(dac, + addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][0], + addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][1], + val); + if (err) + return err; + } + + dac->num_ch = device_get_child_node_count(dev); + if (!dac->num_ch) { + dev_err(dev, "No channels defined\n"); + return -ENODEV; + } + + device_for_each_child_node(dev, child) { + err = fwnode_property_read_u32(child, "reg", &ch); + if (err) { + dev_err(dev, "mandatory reg property missing\n"); + goto put_child; + } + if (ch >= AD3552R_NUM_CH) { + dev_err(dev, "reg must be less than %d\n", + AD3552R_NUM_CH); + err = -EINVAL; + goto put_child; + } + + if (fwnode_property_present(child, "adi,output-range-microvolt")) { + err = fwnode_property_read_u32_array(child, + "adi,output-range-microvolt", + vals, + 2); + if (err) { + dev_err(dev, + "adi,output-range-microvolt property could not be parsed\n"); + goto put_child; + } + + err = ad3552r_find_range(dac->chip_id, vals); + if (err < 0) { + dev_err(dev, + "Invalid adi,output-range-microvolt value\n"); + goto put_child; + } + val = err; + err = ad3552r_set_ch_value(dac, + AD3552R_CH_OUTPUT_RANGE_SEL, + ch, val); + if (err) + goto put_child; + + dac->ch_data[ch].range = val; + } else if (dac->chip_id == AD3542R_ID) { + dev_err(dev, + "adi,output-range-microvolt is required for ad3542r\n"); + err = -EINVAL; + goto put_child; + } else { + err = ad3552r_configure_custom_gain(dac, child, ch); + if (err) + goto put_child; + } + + ad3552r_calc_gain_and_offset(dac, ch); + dac->enabled_ch |= BIT(ch); + + err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1); + if (err < 0) + goto put_child; + + dac->channels[cnt] = AD3552R_CH_DAC(ch); + ++cnt; + + } + + /* Disable unused channels */ + for_each_clear_bit(ch, &dac->enabled_ch, AD3552R_NUM_CH) { + err = ad3552r_set_ch_value(dac, AD3552R_CH_AMPLIFIER_POWERDOWN, + ch, 1); + if (err) + return err; + } + + dac->num_ch = cnt; + + return 0; +put_child: + fwnode_handle_put(child); + + return err; +} + +static int ad3552r_init(struct ad3552r_desc *dac) +{ + int err; + u16 val, id; + + err = ad3552r_reset(dac); + if (err) { + dev_err(&dac->spi->dev, "Reset failed\n"); + return err; + } + + err = ad3552r_check_scratch_pad(dac); + if (err) { + dev_err(&dac->spi->dev, "Scratch pad test failed\n"); + return err; + } + + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_L, &val); + if (err) { + dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_L\n"); + return err; + } + + id = val; + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_H, &val); + if (err) { + dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_H\n"); + return err; + } + + id |= val << 8; + if (id != dac->chip_id) { + dev_err(&dac->spi->dev, "Product id not matching\n"); + return -ENODEV; + } + + return ad3552r_configure_device(dac); +} + +static int ad3552r_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct ad3552r_desc *dac; + struct iio_dev *indio_dev; + int err; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*dac)); + if (!indio_dev) + return -ENOMEM; + + dac = iio_priv(indio_dev); + dac->spi = spi; + dac->chip_id = id->driver_data; + + mutex_init(&dac->lock); + + err = ad3552r_init(dac); + if (err) + return err; + + /* Config triggered buffer device */ + if (dac->chip_id == AD3552R_ID) + indio_dev->name = "ad3552r"; + else + indio_dev->name = "ad3542r"; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ad3552r_iio_info; + indio_dev->num_channels = dac->num_ch; + indio_dev->channels = dac->channels; + indio_dev->modes = INDIO_DIRECT_MODE; + + err = devm_iio_triggered_buffer_setup_ext(&indio_dev->dev, indio_dev, NULL, + &ad3552r_trigger_handler, + IIO_BUFFER_DIRECTION_OUT, + NULL, + NULL); + if (err) + return err; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad3552r_id[] = { + { "ad3542r", AD3542R_ID }, + { "ad3552r", AD3552R_ID }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad3552r_id); + +static const struct of_device_id ad3552r_of_match[] = { + { .compatible = "adi,ad3542r"}, + { .compatible = "adi,ad3552r"}, + { } +}; +MODULE_DEVICE_TABLE(of, ad3552r_of_match); + +static struct spi_driver ad3552r_driver = { + .driver = { + .name = "ad3552r", + .of_match_table = ad3552r_of_match, + }, + .probe = ad3552r_probe, + .id_table = ad3552r_id +}; +module_spi_driver(ad3552r_driver); + +MODULE_AUTHOR("Mihail Chindris "); +MODULE_DESCRIPTION("Analog Device AD3552R DAC"); +MODULE_LICENSE("GPL v2"); -- cgit From 1155ed05756a4e0f8fbc1760d6ca79354fe034c1 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:34 +0000 Subject: iio:accel:bma180: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Stephan Gerhold Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-2-jic23@kernel.org --- drivers/iio/accel/bma180.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 09496f358ad9..d8a454c266d5 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -938,7 +938,7 @@ static int bma180_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; if (client->dev.of_node) - chip = (enum chip_ids)of_device_get_match_data(dev); + chip = (uintptr_t)of_device_get_match_data(dev); else chip = id->driver_data; data->part_info = &bma180_part_info[chip]; -- cgit From 702bab85d6cdb8aba464f3c8758399edc7368ff2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:35 +0000 Subject: iio:adc:ina2xx-adc: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-3-jic23@kernel.org --- drivers/iio/adc/ina2xx-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 08f243f5b92b..4f9992a51e64 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -971,7 +971,7 @@ static int ina2xx_probe(struct i2c_client *client, } if (client->dev.of_node) - type = (enum ina2xx_ids)of_device_get_match_data(&client->dev); + type = (uintptr_t)of_device_get_match_data(&client->dev); else type = id->driver_data; chip->config = &ina2xx_config[type]; -- cgit From 835122a333dc3a0866398b41047e5b293286a9ce Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:36 +0000 Subject: iio:adc:rcar: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-4-jic23@kernel.org --- drivers/iio/adc/rcar-gyroadc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index a48895046408..727ea6c68049 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -511,8 +511,7 @@ static int rcar_gyroadc_probe(struct platform_device *pdev) if (ret) return ret; - priv->model = (enum rcar_gyroadc_model) - of_device_get_match_data(&pdev->dev); + priv->model = (uintptr_t)of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, indio_dev); -- cgit From 7926f8a8c7060896a45ffad9d0ac1154b9d67190 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:37 +0000 Subject: iio:adc:ti-ads1015: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-5-jic23@kernel.org --- drivers/iio/adc/ti-ads1015.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index b92d4cd1b823..068efbce1710 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -950,7 +950,7 @@ static int ads1015_probe(struct i2c_client *client, indio_dev->name = ADS1015_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - chip = (enum chip_ids)device_get_match_data(&client->dev); + chip = (uintptr_t)device_get_match_data(&client->dev); if (chip == ADSXXXX) chip = id->driver_data; switch (chip) { -- cgit From dce71a5fe3b07c3ade6df2687704853fb6fcfd74 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:38 +0000 Subject: iio:amplifiers:hmc425a: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-6-jic23@kernel.org --- drivers/iio/amplifiers/hmc425a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index 9efa692151f0..16c0a77f6a1c 100644 --- a/drivers/iio/amplifiers/hmc425a.c +++ b/drivers/iio/amplifiers/hmc425a.c @@ -192,7 +192,7 @@ static int hmc425a_probe(struct platform_device *pdev) return -ENOMEM; st = iio_priv(indio_dev); - st->type = (enum hmc425a_type)of_device_get_match_data(&pdev->dev); + st->type = (uintptr_t)of_device_get_match_data(&pdev->dev); st->chip_info = &hmc425a_chip_info_tbl[st->type]; indio_dev->num_channels = st->chip_info->num_channels; -- cgit From e064222dcc165ba2feee7f9ecc90121a7dfbc334 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:39 +0000 Subject: iio:dac:mcp4725: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-7-jic23@kernel.org --- drivers/iio/dac/mcp4725.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 98b2c2f10bf3..842bad57cb88 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -386,7 +386,7 @@ static int mcp4725_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; if (dev_fwnode(&client->dev)) - data->id = (enum chip_id)device_get_match_data(&client->dev); + data->id = (uintptr_t)device_get_match_data(&client->dev); else data->id = id->driver_data; pdata = dev_get_platdata(&client->dev); -- cgit From 072cc9816c902103bbc41112fe914e884b3f9882 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:40 +0000 Subject: iio:imu:inv_icm42600: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Jean-Baptiste Maneyrol Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-8-jic23@kernel.org --- drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c | 2 +- drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 85b1934cec60..33d9afb1ba91 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -58,7 +58,7 @@ static int inv_icm42600_probe(struct i2c_client *client) match = device_get_match_data(&client->dev); if (!match) return -EINVAL; - chip = (enum inv_icm42600_chip)match; + chip = (uintptr_t)match; regmap = devm_regmap_init_i2c(client, &inv_icm42600_regmap_config); if (IS_ERR(regmap)) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 323789697a08..e6305e5fa975 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -57,7 +57,7 @@ static int inv_icm42600_probe(struct spi_device *spi) match = device_get_match_data(&spi->dev); if (!match) return -EINVAL; - chip = (enum inv_icm42600_chip)match; + chip = (uintptr_t)match; regmap = devm_regmap_init_spi(spi, &inv_icm42600_regmap_config); if (IS_ERR(regmap)) -- cgit From 6713847817e0ab66b853294137f58c4d3211ad24 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:41 +0000 Subject: iio:imu:inv_mpu6050: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Baptiste Mansuy Cc: Jean-Baptiste Maneyrol Cc: Linus Walleij Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-9-jic23@kernel.org --- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 3ef17e3f50e2..fe03707ec2d3 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -110,7 +110,7 @@ static int inv_mpu_probe(struct i2c_client *client, match = device_get_match_data(&client->dev); if (match) { - chip_type = (enum inv_devices)match; + chip_type = (uintptr_t)match; name = client->name; } else if (id) { chip_type = (enum inv_devices) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index b056f3fe2561..6800356b25fb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -45,7 +45,7 @@ static int inv_mpu_probe(struct spi_device *spi) chip_type = (enum inv_devices)spi_id->driver_data; name = spi_id->name; } else if ((match = device_get_match_data(&spi->dev))) { - chip_type = (enum inv_devices)match; + chip_type = (uintptr_t)match; name = dev_name(&spi->dev); } else { return -ENODEV; -- cgit From ea011add51bc8980c067a1c2df9ec84219062b53 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:42 +0000 Subject: iio:magn:ak8975: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Jonathan Albrieux Cc: Linus Walleij Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-10-jic23@kernel.org --- drivers/iio/magnetometer/ak8975.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 6e82dc54a417..55879a20ae52 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -929,7 +929,7 @@ static int ak8975_probe(struct i2c_client *client, /* id will be NULL when enumerated via ACPI */ match = device_get_match_data(&client->dev); if (match) { - chipset = (enum asahi_compass_chipset)(match); + chipset = (uintptr_t)match; name = dev_name(&client->dev); } else if (id) { chipset = (enum asahi_compass_chipset)(id->driver_data); -- cgit From e8ffca613cd8dfc27adbfc6cee08b659abed3d88 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:43 +0000 Subject: iio:dummy: Drop set but unused variable len. Not sure what the thinking was here, as lost to history, but the variable is clearly not used so get rid of it. Warning seen with clang W=1 tests (may be present with other compilers and build options). Signed-off-by: Jonathan Cameron Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-11-jic23@kernel.org --- drivers/iio/dummy/iio_simple_dummy_buffer.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index 59aa60d4ca37..d81c2b2dad82 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -45,7 +45,6 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - int len = 0; u16 *data; data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); @@ -79,7 +78,6 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) indio_dev->masklength, j); /* random access read from the 'device' */ data[i] = fakedata[j]; - len += 2; } } -- cgit From f3d29c85e6eb5d83d29e2c2bbdf9c824df4cc442 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:44 +0000 Subject: iio:accel:bmc150: Mark structure __maybe_unused as only needed with for pm ops. If CONFIG_PM not set then clang warns this structure is unused. Signed-off-by: Jonathan Cameron Cc: Stephan Gerhold Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-12-jic23@kernel.org --- drivers/iio/accel/bmc150-accel-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index b0678c351e82..e6081dd0a880 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -170,7 +170,7 @@ static const struct { {1000, 0, 0x0E}, {2000, 0, 0x0F} }; -static const struct { +static __maybe_unused const struct { int bw_bits; int msec; } bmc150_accel_sample_upd_time[] = { {0x08, 64}, -- cgit From 8ebbfb9882f8f0e52195d08f02a030e617381b3b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:45 +0000 Subject: iio:accel:kxcjk-1013: Mark struct __maybe_unused to avoid warning. This structure is only used in PM ops, so may not be used depending on build configuration. Signed-off-by: Jonathan Cameron Cc: Stephan Gerhold Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-13-jic23@kernel.org --- drivers/iio/accel/kxcjk-1013.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 88cf0c276893..460b1a89d575 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -315,7 +315,7 @@ static const char *const kxtf9_samp_freq_avail = "25 50 100 200 400 800"; /* Refer to section 4 of the specification */ -static const struct { +static __maybe_unused const struct { int odr_bits; int usec; } odr_start_up_times[KX_MAX_CHIPS][12] = { -- cgit From d5c70627a79455154f5f636096abe6fe57510605 Mon Sep 17 00:00:00 2001 From: Anand Ashok Dumbre Date: Fri, 3 Dec 2021 21:23:56 +0000 Subject: iio: adc: Add Xilinx AMS driver The AMS includes an ADC as well as on-chip sensors that can be used to sample external voltages and monitor on-die operating conditions, such as temperature and supply voltage levels. The AMS has two SYSMON blocks. PL-SYSMON block is capable of monitoring off chip voltage and temperature. PL-SYSMON block has DRP, JTAG and I2C interface to enable monitoring from an external master. Out of these interfaces currently only DRP is supported. Other block PS-SYSMON is memory mapped to PS. The AMS can use internal channels to monitor voltage and temperature as well as one primary and up to 16 auxiliary channels for measuring external voltages. The voltage and temperature monitoring channels also have event capability which allows to generate an interrupt when their value falls below or raises above a set threshold. Co-developed-by: Manish Narani Signed-off-by: Manish Narani Signed-off-by: Anand Ashok Dumbre Link: https://lore.kernel.org/r/20211203212358.31444-4-anand.ashok.dumbre@xilinx.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 15 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/xilinx-ams.c | 1451 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1467 insertions(+) create mode 100644 drivers/iio/adc/xilinx-ams.c (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index c7de4632f24a..3570c4e41708 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1288,4 +1288,19 @@ config XILINX_XADC The driver can also be build as a module. If so, the module will be called xilinx-xadc. +config XILINX_AMS + tristate "Xilinx AMS driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to have support for the Xilinx AMS for Ultrascale/Ultrascale+ + System Monitor. With this you can measure and monitor the Voltages and + Temperature values on the SOC. + + The driver supports Voltage and Temperature monitoring on Xilinx Ultrascale + devices. + + The driver can also be built as a module. If so, the module will be called + xilinx-ams. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d3f53549720c..4a8f1833993b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -115,4 +115,5 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o +obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c new file mode 100644 index 000000000000..8343c5f74121 --- /dev/null +++ b/drivers/iio/adc/xilinx-ams.c @@ -0,0 +1,1451 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AMS driver + * + * Copyright (C) 2021 Xilinx, Inc. + * + * Manish Narani + * Rajnikant Bhojani + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* AMS registers definitions */ +#define AMS_ISR_0 0x010 +#define AMS_ISR_1 0x014 +#define AMS_IER_0 0x020 +#define AMS_IER_1 0x024 +#define AMS_IDR_0 0x028 +#define AMS_IDR_1 0x02C +#define AMS_PS_CSTS 0x040 +#define AMS_PL_CSTS 0x044 + +#define AMS_VCC_PSPLL0 0x060 +#define AMS_VCC_PSPLL3 0x06C +#define AMS_VCCINT 0x078 +#define AMS_VCCBRAM 0x07C +#define AMS_VCCAUX 0x080 +#define AMS_PSDDRPLL 0x084 +#define AMS_PSINTFPDDR 0x09C + +#define AMS_VCC_PSPLL0_CH 48 +#define AMS_VCC_PSPLL3_CH 51 +#define AMS_VCCINT_CH 54 +#define AMS_VCCBRAM_CH 55 +#define AMS_VCCAUX_CH 56 +#define AMS_PSDDRPLL_CH 57 +#define AMS_PSINTFPDDR_CH 63 + +#define AMS_REG_CONFIG0 0x100 +#define AMS_REG_CONFIG1 0x104 +#define AMS_REG_CONFIG3 0x10C +#define AMS_REG_CONFIG4 0x110 +#define AMS_REG_SEQ_CH0 0x120 +#define AMS_REG_SEQ_CH1 0x124 +#define AMS_REG_SEQ_CH2 0x118 + +#define AMS_VUSER0_MASK BIT(0) +#define AMS_VUSER1_MASK BIT(1) +#define AMS_VUSER2_MASK BIT(2) +#define AMS_VUSER3_MASK BIT(3) + +#define AMS_TEMP 0x000 +#define AMS_SUPPLY1 0x004 +#define AMS_SUPPLY2 0x008 +#define AMS_VP_VN 0x00C +#define AMS_VREFP 0x010 +#define AMS_VREFN 0x014 +#define AMS_SUPPLY3 0x018 +#define AMS_SUPPLY4 0x034 +#define AMS_SUPPLY5 0x038 +#define AMS_SUPPLY6 0x03C +#define AMS_SUPPLY7 0x200 +#define AMS_SUPPLY8 0x204 +#define AMS_SUPPLY9 0x208 +#define AMS_SUPPLY10 0x20C +#define AMS_VCCAMS 0x210 +#define AMS_TEMP_REMOTE 0x214 + +#define AMS_REG_VAUX(x) (0x40 + 4 * (x)) + +#define AMS_PS_RESET_VALUE 0xFFFF +#define AMS_PL_RESET_VALUE 0xFFFF + +#define AMS_CONF0_CHANNEL_NUM_MASK GENMASK(6, 0) + +#define AMS_CONF1_SEQ_MASK GENMASK(15, 12) +#define AMS_CONF1_SEQ_DEFAULT FIELD_PREP(AMS_CONF1_SEQ_MASK, 0) +#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 1) +#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 2) + +#define AMS_REG_SEQ0_MASK GENMASK(15, 0) +#define AMS_REG_SEQ2_MASK GENMASK(21, 16) +#define AMS_REG_SEQ1_MASK GENMASK_ULL(37, 22) + +#define AMS_PS_SEQ_MASK GENMASK(21, 0) +#define AMS_PL_SEQ_MASK GENMASK_ULL(59, 22) + +#define AMS_ALARM_TEMP 0x140 +#define AMS_ALARM_SUPPLY1 0x144 +#define AMS_ALARM_SUPPLY2 0x148 +#define AMS_ALARM_SUPPLY3 0x160 +#define AMS_ALARM_SUPPLY4 0x164 +#define AMS_ALARM_SUPPLY5 0x168 +#define AMS_ALARM_SUPPLY6 0x16C +#define AMS_ALARM_SUPPLY7 0x180 +#define AMS_ALARM_SUPPLY8 0x184 +#define AMS_ALARM_SUPPLY9 0x188 +#define AMS_ALARM_SUPPLY10 0x18C +#define AMS_ALARM_VCCAMS 0x190 +#define AMS_ALARM_TEMP_REMOTE 0x194 +#define AMS_ALARM_THRESHOLD_OFF_10 0x10 +#define AMS_ALARM_THRESHOLD_OFF_20 0x20 + +#define AMS_ALARM_THR_DIRECT_MASK BIT(1) +#define AMS_ALARM_THR_MIN 0x0000 +#define AMS_ALARM_THR_MAX (BIT(16) - 1) + +#define AMS_ALARM_MASK GENMASK_ULL(63, 0) +#define AMS_NO_OF_ALARMS 32 +#define AMS_PL_ALARM_START 16 +#define AMS_PL_ALARM_MASK GENMASK(31, 16) +#define AMS_ISR0_ALARM_MASK GENMASK(31, 0) +#define AMS_ISR1_ALARM_MASK (GENMASK(31, 29) | GENMASK(4, 0)) +#define AMS_ISR1_EOC_MASK BIT(3) +#define AMS_ISR1_INTR_MASK GENMASK_ULL(63, 32) +#define AMS_ISR0_ALARM_2_TO_0_MASK GENMASK(2, 0) +#define AMS_ISR0_ALARM_6_TO_3_MASK GENMASK(6, 3) +#define AMS_ISR0_ALARM_12_TO_7_MASK GENMASK(13, 8) +#define AMS_CONF1_ALARM_2_TO_0_MASK GENMASK(3, 1) +#define AMS_CONF1_ALARM_6_TO_3_MASK GENMASK(11, 8) +#define AMS_CONF1_ALARM_12_TO_7_MASK GENMASK(5, 0) +#define AMS_REGCFG1_ALARM_MASK \ + (AMS_CONF1_ALARM_2_TO_0_MASK | AMS_CONF1_ALARM_6_TO_3_MASK | BIT(0)) +#define AMS_REGCFG3_ALARM_MASK AMS_CONF1_ALARM_12_TO_7_MASK + +#define AMS_PS_CSTS_PS_READY (BIT(27) | BIT(16)) +#define AMS_PL_CSTS_ACCESS_MASK BIT(1) + +#define AMS_PL_MAX_FIXED_CHANNEL 10 +#define AMS_PL_MAX_EXT_CHANNEL 20 + +#define AMS_INIT_POLL_TIME_US 200 +#define AMS_INIT_TIMEOUT_US 10000 +#define AMS_UNMASK_TIMEOUT_MS 500 + +/* + * Following scale and offset value is derived from + * UG580 (v1.7) December 20, 2016 + */ +#define AMS_SUPPLY_SCALE_1VOLT_mV 1000 +#define AMS_SUPPLY_SCALE_3VOLT_mV 3000 +#define AMS_SUPPLY_SCALE_6VOLT_mV 6000 +#define AMS_SUPPLY_SCALE_DIV_BIT 16 + +#define AMS_TEMP_SCALE 509314 +#define AMS_TEMP_SCALE_DIV_BIT 16 +#define AMS_TEMP_OFFSET -((280230LL << 16) / 509314) + +enum ams_alarm_bit { + AMS_ALARM_BIT_TEMP = 0, + AMS_ALARM_BIT_SUPPLY1 = 1, + AMS_ALARM_BIT_SUPPLY2 = 2, + AMS_ALARM_BIT_SUPPLY3 = 3, + AMS_ALARM_BIT_SUPPLY4 = 4, + AMS_ALARM_BIT_SUPPLY5 = 5, + AMS_ALARM_BIT_SUPPLY6 = 6, + AMS_ALARM_BIT_RESERVED = 7, + AMS_ALARM_BIT_SUPPLY7 = 8, + AMS_ALARM_BIT_SUPPLY8 = 9, + AMS_ALARM_BIT_SUPPLY9 = 10, + AMS_ALARM_BIT_SUPPLY10 = 11, + AMS_ALARM_BIT_VCCAMS = 12, + AMS_ALARM_BIT_TEMP_REMOTE = 13, +}; + +enum ams_seq { + AMS_SEQ_VCC_PSPLL = 0, + AMS_SEQ_VCC_PSBATT = 1, + AMS_SEQ_VCCINT = 2, + AMS_SEQ_VCCBRAM = 3, + AMS_SEQ_VCCAUX = 4, + AMS_SEQ_PSDDRPLL = 5, + AMS_SEQ_INTDDR = 6, +}; + +enum ams_ps_pl_seq { + AMS_SEQ_CALIB = 0, + AMS_SEQ_RSVD_1 = 1, + AMS_SEQ_RSVD_2 = 2, + AMS_SEQ_TEST = 3, + AMS_SEQ_RSVD_4 = 4, + AMS_SEQ_SUPPLY4 = 5, + AMS_SEQ_SUPPLY5 = 6, + AMS_SEQ_SUPPLY6 = 7, + AMS_SEQ_TEMP = 8, + AMS_SEQ_SUPPLY2 = 9, + AMS_SEQ_SUPPLY1 = 10, + AMS_SEQ_VP_VN = 11, + AMS_SEQ_VREFP = 12, + AMS_SEQ_VREFN = 13, + AMS_SEQ_SUPPLY3 = 14, + AMS_SEQ_CURRENT_MON = 15, + AMS_SEQ_SUPPLY7 = 16, + AMS_SEQ_SUPPLY8 = 17, + AMS_SEQ_SUPPLY9 = 18, + AMS_SEQ_SUPPLY10 = 19, + AMS_SEQ_VCCAMS = 20, + AMS_SEQ_TEMP_REMOTE = 21, + AMS_SEQ_MAX = 22 +}; + +#define AMS_PS_SEQ_MAX AMS_SEQ_MAX +#define AMS_SEQ(x) (AMS_SEQ_MAX + (x)) +#define PS_SEQ(x) (x) +#define PL_SEQ(x) (AMS_PS_SEQ_MAX + (x)) +#define AMS_CTRL_SEQ_BASE (AMS_PS_SEQ_MAX * 3) + +#define AMS_CHAN_TEMP(_scan_index, _addr) { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .event_spec = ams_temp_events, \ + .scan_index = _scan_index, \ + .num_event_specs = ARRAY_SIZE(ams_temp_events), \ +} + +#define AMS_CHAN_VOLTAGE(_scan_index, _addr, _alarm) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .event_spec = (_alarm) ? ams_voltage_events : NULL, \ + .scan_index = _scan_index, \ + .num_event_specs = (_alarm) ? ARRAY_SIZE(ams_voltage_events) : 0, \ +} + +#define AMS_PS_CHAN_TEMP(_scan_index, _addr) \ + AMS_CHAN_TEMP(PS_SEQ(_scan_index), _addr) +#define AMS_PS_CHAN_VOLTAGE(_scan_index, _addr) \ + AMS_CHAN_VOLTAGE(PS_SEQ(_scan_index), _addr, true) + +#define AMS_PL_CHAN_TEMP(_scan_index, _addr) \ + AMS_CHAN_TEMP(PL_SEQ(_scan_index), _addr) +#define AMS_PL_CHAN_VOLTAGE(_scan_index, _addr, _alarm) \ + AMS_CHAN_VOLTAGE(PL_SEQ(_scan_index), _addr, _alarm) +#define AMS_PL_AUX_CHAN_VOLTAGE(_auxno) \ + AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(_auxno)), AMS_REG_VAUX(_auxno), false) +#define AMS_CTRL_CHAN_VOLTAGE(_scan_index, _addr) \ + AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(AMS_SEQ(_scan_index))), _addr, false) + +/** + * struct ams - This structure contains necessary state for xilinx-ams to operate + * @base: physical base address of device + * @ps_base: physical base address of PS device + * @pl_base: physical base address of PL device + * @clk: clocks associated with the device + * @dev: pointer to device struct + * @lock: to handle multiple user interaction + * @intr_lock: to protect interrupt mask values + * @alarm_mask: alarm configuration + * @current_masked_alarm: currently masked due to alarm + * @intr_mask: interrupt configuration + * @ams_unmask_work: re-enables event once the event condition disappears + * + */ +struct ams { + void __iomem *base; + void __iomem *ps_base; + void __iomem *pl_base; + struct clk *clk; + struct device *dev; + struct mutex lock; + spinlock_t intr_lock; + unsigned int alarm_mask; + unsigned int current_masked_alarm; + u64 intr_mask; + struct delayed_work ams_unmask_work; +}; + +static inline void ams_ps_update_reg(struct ams *ams, unsigned int offset, + u32 mask, u32 data) +{ + u32 val, regval; + + val = readl(ams->ps_base + offset); + regval = (val & ~mask) | (data & mask); + writel(regval, ams->ps_base + offset); +} + +static inline void ams_pl_update_reg(struct ams *ams, unsigned int offset, + u32 mask, u32 data) +{ + u32 val, regval; + + val = readl(ams->pl_base + offset); + regval = (val & ~mask) | (data & mask); + writel(regval, ams->pl_base + offset); +} + +static void ams_update_intrmask(struct ams *ams, u64 mask, u64 val) +{ + u32 regval; + + ams->intr_mask = (ams->intr_mask & ~mask) | (val & mask); + + regval = ~(ams->intr_mask | ams->current_masked_alarm); + writel(regval, ams->base + AMS_IER_0); + + regval = ~(FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask)); + writel(regval, ams->base + AMS_IER_1); + + regval = ams->intr_mask | ams->current_masked_alarm; + writel(regval, ams->base + AMS_IDR_0); + + regval = FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask); + writel(regval, ams->base + AMS_IDR_1); +} + +static void ams_disable_all_alarms(struct ams *ams) +{ + /* disable PS module alarm */ + if (ams->ps_base) { + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, + AMS_REGCFG1_ALARM_MASK); + ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, + AMS_REGCFG3_ALARM_MASK); + } + + /* disable PL module alarm */ + if (ams->pl_base) { + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, + AMS_REGCFG1_ALARM_MASK); + ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, + AMS_REGCFG3_ALARM_MASK); + } +} + +static void ams_update_ps_alarm(struct ams *ams, unsigned long alarm_mask) +{ + u32 cfg; + u32 val; + + val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val)); + + val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, alarm_mask); + cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val)); + + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg); + + val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val)); + ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg); +} + +static void ams_update_pl_alarm(struct ams *ams, unsigned long alarm_mask) +{ + unsigned long pl_alarm_mask; + u32 cfg; + u32 val; + + pl_alarm_mask = FIELD_GET(AMS_PL_ALARM_MASK, alarm_mask); + + val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, pl_alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val)); + + val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, pl_alarm_mask); + cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val)); + + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg); + + val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, pl_alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val)); + ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg); +} + +static void ams_update_alarm(struct ams *ams, unsigned long alarm_mask) +{ + unsigned long flags; + + if (ams->ps_base) + ams_update_ps_alarm(ams, alarm_mask); + + if (ams->pl_base) + ams_update_pl_alarm(ams, alarm_mask); + + spin_lock_irqsave(&ams->intr_lock, flags); + ams_update_intrmask(ams, AMS_ISR0_ALARM_MASK, ~alarm_mask); + spin_unlock_irqrestore(&ams->intr_lock, flags); +} + +static void ams_enable_channel_sequence(struct iio_dev *indio_dev) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned long long scan_mask; + int i; + u32 regval; + + /* + * Enable channel sequence. First 22 bits of scan_mask represent + * PS channels, and next remaining bits represent PL channels. + */ + + /* Run calibration of PS & PL as part of the sequence */ + scan_mask = BIT(0) | BIT(AMS_PS_SEQ_MAX); + for (i = 0; i < indio_dev->num_channels; i++) + scan_mask |= BIT_ULL(indio_dev->channels[i].scan_index); + + if (ams->ps_base) { + /* put sysmon in a soft reset to change the sequence */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + + /* configure basic channels */ + regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask); + writel(regval, ams->ps_base + AMS_REG_SEQ_CH0); + + regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask); + writel(regval, ams->ps_base + AMS_REG_SEQ_CH2); + + /* set continuous sequence mode */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_CONTINUOUS); + } + + if (ams->pl_base) { + /* put sysmon in a soft reset to change the sequence */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + + /* configure basic channels */ + scan_mask = FIELD_GET(AMS_PL_SEQ_MASK, scan_mask); + + regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH0); + + regval = FIELD_GET(AMS_REG_SEQ1_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH1); + + regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH2); + + /* set continuous sequence mode */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_CONTINUOUS); + } +} + +static int ams_init_device(struct ams *ams) +{ + u32 expect = AMS_PS_CSTS_PS_READY; + u32 reg, value; + int ret; + + /* reset AMS */ + if (ams->ps_base) { + writel(AMS_PS_RESET_VALUE, ams->ps_base + AMS_VP_VN); + + ret = readl_poll_timeout(ams->base + AMS_PS_CSTS, reg, (reg & expect), + AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US); + if (ret) + return ret; + + /* put sysmon in a default state */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + } + + if (ams->pl_base) { + value = readl(ams->base + AMS_PL_CSTS); + if (value == 0) + return 0; + + writel(AMS_PL_RESET_VALUE, ams->pl_base + AMS_VP_VN); + + /* put sysmon in a default state */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + } + + ams_disable_all_alarms(ams); + + /* Disable interrupt */ + ams_update_intrmask(ams, AMS_ALARM_MASK, AMS_ALARM_MASK); + + /* Clear any pending interrupt */ + writel(AMS_ISR0_ALARM_MASK, ams->base + AMS_ISR_0); + writel(AMS_ISR1_ALARM_MASK, ams->base + AMS_ISR_1); + + return 0; +} + +static int ams_enable_single_channel(struct ams *ams, unsigned int offset) +{ + u8 channel_num; + + switch (offset) { + case AMS_VCC_PSPLL0: + channel_num = AMS_VCC_PSPLL0_CH; + break; + case AMS_VCC_PSPLL3: + channel_num = AMS_VCC_PSPLL3_CH; + break; + case AMS_VCCINT: + channel_num = AMS_VCCINT_CH; + break; + case AMS_VCCBRAM: + channel_num = AMS_VCCBRAM_CH; + break; + case AMS_VCCAUX: + channel_num = AMS_VCCAUX_CH; + break; + case AMS_PSDDRPLL: + channel_num = AMS_PSDDRPLL_CH; + break; + case AMS_PSINTFPDDR: + channel_num = AMS_PSINTFPDDR_CH; + break; + default: + return -EINVAL; + } + + /* set single channel, sequencer off mode */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_SINGLE_CHANNEL); + + /* write the channel number */ + ams_ps_update_reg(ams, AMS_REG_CONFIG0, AMS_CONF0_CHANNEL_NUM_MASK, + channel_num); + + return 0; +} + +static int ams_read_vcc_reg(struct ams *ams, unsigned int offset, u32 *data) +{ + u32 expect = AMS_ISR1_EOC_MASK; + u32 reg; + int ret; + + ret = ams_enable_single_channel(ams, offset); + if (ret) + return ret; + + ret = readl_poll_timeout(ams->base + AMS_ISR_1, reg, (reg & expect), + AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US); + if (ret) + return ret; + + *data = readl(ams->base + offset); + + return 0; +} + +static int ams_get_ps_scale(int address) +{ + int val; + + switch (address) { + case AMS_SUPPLY1: + case AMS_SUPPLY2: + case AMS_SUPPLY3: + case AMS_SUPPLY4: + case AMS_SUPPLY9: + case AMS_SUPPLY10: + case AMS_VCCAMS: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY5: + case AMS_SUPPLY6: + case AMS_SUPPLY7: + case AMS_SUPPLY8: + val = AMS_SUPPLY_SCALE_6VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_get_pl_scale(struct ams *ams, int address) +{ + int val, regval; + + switch (address) { + case AMS_SUPPLY1: + case AMS_SUPPLY2: + case AMS_SUPPLY3: + case AMS_SUPPLY4: + case AMS_SUPPLY5: + case AMS_SUPPLY6: + case AMS_VCCAMS: + case AMS_VREFP: + case AMS_VREFN: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY7: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER0_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY8: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER1_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY9: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER2_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY10: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER3_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_VP_VN: + case AMS_REG_VAUX(0) ... AMS_REG_VAUX(15): + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_get_ctrl_scale(int address) +{ + int val; + + switch (address) { + case AMS_VCC_PSPLL0: + case AMS_VCC_PSPLL3: + case AMS_VCCINT: + case AMS_VCCBRAM: + case AMS_VCCAUX: + case AMS_PSDDRPLL: + case AMS_PSINTFPDDR: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ams *ams = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ams->lock); + if (chan->scan_index >= AMS_CTRL_SEQ_BASE) { + ret = ams_read_vcc_reg(ams, chan->address, val); + if (ret) + goto unlock_mutex; + ams_enable_channel_sequence(indio_dev); + } else if (chan->scan_index >= AMS_PS_SEQ_MAX) + *val = readl(ams->pl_base + chan->address); + else + *val = readl(ams->ps_base + chan->address); + + ret = IIO_VAL_INT; +unlock_mutex: + mutex_unlock(&ams->lock); + return ret; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->scan_index < AMS_PS_SEQ_MAX) + *val = ams_get_ps_scale(chan->address); + else if (chan->scan_index >= AMS_PS_SEQ_MAX && + chan->scan_index < AMS_CTRL_SEQ_BASE) + *val = ams_get_pl_scale(ams, chan->address); + else + *val = ams_get_ctrl_scale(chan->address); + + *val2 = AMS_SUPPLY_SCALE_DIV_BIT; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = AMS_TEMP_SCALE; + *val2 = AMS_TEMP_SCALE_DIV_BIT; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset */ + *val = AMS_TEMP_OFFSET; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ams_get_alarm_offset(int scan_index, enum iio_event_direction dir) +{ + int offset; + + if (scan_index >= AMS_PS_SEQ_MAX) + scan_index -= AMS_PS_SEQ_MAX; + + if (dir == IIO_EV_DIR_FALLING) { + if (scan_index < AMS_SEQ_SUPPLY7) + offset = AMS_ALARM_THRESHOLD_OFF_10; + else + offset = AMS_ALARM_THRESHOLD_OFF_20; + } else { + offset = 0; + } + + switch (scan_index) { + case AMS_SEQ_TEMP: + return AMS_ALARM_TEMP + offset; + case AMS_SEQ_SUPPLY1: + return AMS_ALARM_SUPPLY1 + offset; + case AMS_SEQ_SUPPLY2: + return AMS_ALARM_SUPPLY2 + offset; + case AMS_SEQ_SUPPLY3: + return AMS_ALARM_SUPPLY3 + offset; + case AMS_SEQ_SUPPLY4: + return AMS_ALARM_SUPPLY4 + offset; + case AMS_SEQ_SUPPLY5: + return AMS_ALARM_SUPPLY5 + offset; + case AMS_SEQ_SUPPLY6: + return AMS_ALARM_SUPPLY6 + offset; + case AMS_SEQ_SUPPLY7: + return AMS_ALARM_SUPPLY7 + offset; + case AMS_SEQ_SUPPLY8: + return AMS_ALARM_SUPPLY8 + offset; + case AMS_SEQ_SUPPLY9: + return AMS_ALARM_SUPPLY9 + offset; + case AMS_SEQ_SUPPLY10: + return AMS_ALARM_SUPPLY10 + offset; + case AMS_SEQ_VCCAMS: + return AMS_ALARM_VCCAMS + offset; + case AMS_SEQ_TEMP_REMOTE: + return AMS_ALARM_TEMP_REMOTE + offset; + default: + return 0; + } +} + +static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, + u32 event) +{ + int scan_index = 0, i; + + if (event >= AMS_PL_ALARM_START) { + event -= AMS_PL_ALARM_START; + scan_index = AMS_PS_SEQ_MAX; + } + + switch (event) { + case AMS_ALARM_BIT_TEMP: + scan_index += AMS_SEQ_TEMP; + break; + case AMS_ALARM_BIT_SUPPLY1: + scan_index += AMS_SEQ_SUPPLY1; + break; + case AMS_ALARM_BIT_SUPPLY2: + scan_index += AMS_SEQ_SUPPLY2; + break; + case AMS_ALARM_BIT_SUPPLY3: + scan_index += AMS_SEQ_SUPPLY3; + break; + case AMS_ALARM_BIT_SUPPLY4: + scan_index += AMS_SEQ_SUPPLY4; + break; + case AMS_ALARM_BIT_SUPPLY5: + scan_index += AMS_SEQ_SUPPLY5; + break; + case AMS_ALARM_BIT_SUPPLY6: + scan_index += AMS_SEQ_SUPPLY6; + break; + case AMS_ALARM_BIT_SUPPLY7: + scan_index += AMS_SEQ_SUPPLY7; + break; + case AMS_ALARM_BIT_SUPPLY8: + scan_index += AMS_SEQ_SUPPLY8; + break; + case AMS_ALARM_BIT_SUPPLY9: + scan_index += AMS_SEQ_SUPPLY9; + break; + case AMS_ALARM_BIT_SUPPLY10: + scan_index += AMS_SEQ_SUPPLY10; + break; + case AMS_ALARM_BIT_VCCAMS: + scan_index += AMS_SEQ_VCCAMS; + break; + case AMS_ALARM_BIT_TEMP_REMOTE: + scan_index += AMS_SEQ_TEMP_REMOTE; + break; + default: + break; + } + + for (i = 0; i < dev->num_channels; i++) + if (dev->channels[i].scan_index == scan_index) + break; + + return &dev->channels[i]; +} + +static int ams_get_alarm_mask(int scan_index) +{ + int bit = 0; + + if (scan_index >= AMS_PS_SEQ_MAX) { + bit = AMS_PL_ALARM_START; + scan_index -= AMS_PS_SEQ_MAX; + } + + switch (scan_index) { + case AMS_SEQ_TEMP: + return BIT(AMS_ALARM_BIT_TEMP + bit); + case AMS_SEQ_SUPPLY1: + return BIT(AMS_ALARM_BIT_SUPPLY1 + bit); + case AMS_SEQ_SUPPLY2: + return BIT(AMS_ALARM_BIT_SUPPLY2 + bit); + case AMS_SEQ_SUPPLY3: + return BIT(AMS_ALARM_BIT_SUPPLY3 + bit); + case AMS_SEQ_SUPPLY4: + return BIT(AMS_ALARM_BIT_SUPPLY4 + bit); + case AMS_SEQ_SUPPLY5: + return BIT(AMS_ALARM_BIT_SUPPLY5 + bit); + case AMS_SEQ_SUPPLY6: + return BIT(AMS_ALARM_BIT_SUPPLY6 + bit); + case AMS_SEQ_SUPPLY7: + return BIT(AMS_ALARM_BIT_SUPPLY7 + bit); + case AMS_SEQ_SUPPLY8: + return BIT(AMS_ALARM_BIT_SUPPLY8 + bit); + case AMS_SEQ_SUPPLY9: + return BIT(AMS_ALARM_BIT_SUPPLY9 + bit); + case AMS_SEQ_SUPPLY10: + return BIT(AMS_ALARM_BIT_SUPPLY10 + bit); + case AMS_SEQ_VCCAMS: + return BIT(AMS_ALARM_BIT_VCCAMS + bit); + case AMS_SEQ_TEMP_REMOTE: + return BIT(AMS_ALARM_BIT_TEMP_REMOTE + bit); + default: + return 0; + } +} + +static int ams_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ams *ams = iio_priv(indio_dev); + + return !!(ams->alarm_mask & ams_get_alarm_mask(chan->scan_index)); +} + +static int ams_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int alarm; + + alarm = ams_get_alarm_mask(chan->scan_index); + + mutex_lock(&ams->lock); + + if (state) + ams->alarm_mask |= alarm; + else + ams->alarm_mask &= ~alarm; + + ams_update_alarm(ams, ams->alarm_mask); + + mutex_unlock(&ams->lock); + + return 0; +} + +static int ams_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int offset = ams_get_alarm_offset(chan->scan_index, dir); + + mutex_lock(&ams->lock); + + if (chan->scan_index >= AMS_PS_SEQ_MAX) + *val = readl(ams->pl_base + offset); + else + *val = readl(ams->ps_base + offset); + + mutex_unlock(&ams->lock); + + return IIO_VAL_INT; +} + +static int ams_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int offset; + + mutex_lock(&ams->lock); + + /* Set temperature channel threshold to direct threshold */ + if (chan->type == IIO_TEMP) { + offset = ams_get_alarm_offset(chan->scan_index, IIO_EV_DIR_FALLING); + + if (chan->scan_index >= AMS_PS_SEQ_MAX) + ams_pl_update_reg(ams, offset, + AMS_ALARM_THR_DIRECT_MASK, + AMS_ALARM_THR_DIRECT_MASK); + else + ams_ps_update_reg(ams, offset, + AMS_ALARM_THR_DIRECT_MASK, + AMS_ALARM_THR_DIRECT_MASK); + } + + offset = ams_get_alarm_offset(chan->scan_index, dir); + if (chan->scan_index >= AMS_PS_SEQ_MAX) + writel(val, ams->pl_base + offset); + else + writel(val, ams->ps_base + offset); + + mutex_unlock(&ams->lock); + + return 0; +} + +static void ams_handle_event(struct iio_dev *indio_dev, u32 event) +{ + const struct iio_chan_spec *chan; + + chan = ams_event_to_channel(indio_dev, event); + + if (chan->type == IIO_TEMP) { + /* + * The temperature channel only supports over-temperature + * events. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } else { + /* + * For other channels we don't know whether it is a upper or + * lower threshold event. Userspace will have to check the + * channel value if it wants to know. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + } +} + +static void ams_handle_events(struct iio_dev *indio_dev, unsigned long events) +{ + unsigned int bit; + + for_each_set_bit(bit, &events, AMS_NO_OF_ALARMS) + ams_handle_event(indio_dev, bit); +} + +/** + * ams_unmask_worker - ams alarm interrupt unmask worker + * @work: work to be done + * + * The ZynqMP threshold interrupts are level sensitive. Since we can't make the + * threshold condition go way from within the interrupt handler, this means as + * soon as a threshold condition is present we would enter the interrupt handler + * again and again. To work around this we mask all active threshold interrupts + * in the interrupt handler and start a timer. In this timer we poll the + * interrupt status and only if the interrupt is inactive we unmask it again. + */ +static void ams_unmask_worker(struct work_struct *work) +{ + struct ams *ams = container_of(work, struct ams, ams_unmask_work.work); + unsigned int status, unmask; + + spin_lock_irq(&ams->intr_lock); + + status = readl(ams->base + AMS_ISR_0); + + /* Clear those bits which are not active anymore */ + unmask = (ams->current_masked_alarm ^ status) & ams->current_masked_alarm; + + /* Clear status of disabled alarm */ + unmask |= ams->intr_mask; + + ams->current_masked_alarm &= status; + + /* Also clear those which are masked out anyway */ + ams->current_masked_alarm &= ~ams->intr_mask; + + /* Clear the interrupts before we unmask them */ + writel(unmask, ams->base + AMS_ISR_0); + + ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK); + + spin_unlock_irq(&ams->intr_lock); + + /* If still pending some alarm re-trigger the timer */ + if (ams->current_masked_alarm) + schedule_delayed_work(&ams->ams_unmask_work, + msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS)); +} + +static irqreturn_t ams_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ams *ams = iio_priv(indio_dev); + u32 isr0; + + spin_lock(&ams->intr_lock); + + isr0 = readl(ams->base + AMS_ISR_0); + + /* Only process alarms that are not masked */ + isr0 &= ~((ams->intr_mask & AMS_ISR0_ALARM_MASK) | ams->current_masked_alarm); + if (!isr0) { + spin_unlock(&ams->intr_lock); + return IRQ_NONE; + } + + /* Clear interrupt */ + writel(isr0, ams->base + AMS_ISR_0); + + /* Mask the alarm interrupts until cleared */ + ams->current_masked_alarm |= isr0; + ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK); + + ams_handle_events(indio_dev, isr0); + + schedule_delayed_work(&ams->ams_unmask_work, + msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS)); + + spin_unlock(&ams->intr_lock); + + return IRQ_HANDLED; +} + +static const struct iio_event_spec ams_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const struct iio_event_spec ams_voltage_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec ams_ps_channels[] = { + AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP), + AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP_REMOTE, AMS_TEMP_REMOTE), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS), +}; + +static const struct iio_chan_spec ams_pl_channels[] = { + AMS_PL_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFP, AMS_VREFP, false), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFN, AMS_VREFN, false), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VP_VN, AMS_VP_VN, false), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10, true), + AMS_PL_AUX_CHAN_VOLTAGE(0), + AMS_PL_AUX_CHAN_VOLTAGE(1), + AMS_PL_AUX_CHAN_VOLTAGE(2), + AMS_PL_AUX_CHAN_VOLTAGE(3), + AMS_PL_AUX_CHAN_VOLTAGE(4), + AMS_PL_AUX_CHAN_VOLTAGE(5), + AMS_PL_AUX_CHAN_VOLTAGE(6), + AMS_PL_AUX_CHAN_VOLTAGE(7), + AMS_PL_AUX_CHAN_VOLTAGE(8), + AMS_PL_AUX_CHAN_VOLTAGE(9), + AMS_PL_AUX_CHAN_VOLTAGE(10), + AMS_PL_AUX_CHAN_VOLTAGE(11), + AMS_PL_AUX_CHAN_VOLTAGE(12), + AMS_PL_AUX_CHAN_VOLTAGE(13), + AMS_PL_AUX_CHAN_VOLTAGE(14), + AMS_PL_AUX_CHAN_VOLTAGE(15), +}; + +static const struct iio_chan_spec ams_ctrl_channels[] = { + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSPLL, AMS_VCC_PSPLL0), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSBATT, AMS_VCC_PSPLL3), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCINT, AMS_VCCINT), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCBRAM, AMS_VCCBRAM), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCAUX, AMS_VCCAUX), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_PSDDRPLL, AMS_PSDDRPLL), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_INTDDR, AMS_PSINTFPDDR), +}; + +static int ams_get_ext_chan(struct fwnode_handle *chan_node, + struct iio_chan_spec *channels, int num_channels) +{ + struct iio_chan_spec *chan; + struct fwnode_handle *child; + unsigned int reg, ext_chan; + int ret; + + fwnode_for_each_child_node(chan_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg > AMS_PL_MAX_EXT_CHANNEL + 30) + continue; + + chan = &channels[num_channels]; + ext_chan = reg + AMS_PL_MAX_FIXED_CHANNEL - 30; + memcpy(chan, &ams_pl_channels[ext_chan], sizeof(*channels)); + + if (fwnode_property_read_bool(child, "xlnx,bipolar")) + chan->scan_type.sign = 's'; + + num_channels++; + } + + return num_channels; +} + +static void ams_iounmap_ps(void *data) +{ + struct ams *ams = data; + + iounmap(ams->ps_base); +} + +static void ams_iounmap_pl(void *data) +{ + struct ams *ams = data; + + iounmap(ams->pl_base); +} + +static int ams_init_module(struct iio_dev *indio_dev, + struct fwnode_handle *fwnode, + struct iio_chan_spec *channels) +{ + struct device *dev = indio_dev->dev.parent; + struct ams *ams = iio_priv(indio_dev); + int num_channels = 0; + int ret; + + if (fwnode_property_match_string(fwnode, "compatible", + "xlnx,zynqmp-ams-ps") == 0) { + ams->ps_base = fwnode_iomap(fwnode, 0); + if (!ams->ps_base) + return -ENXIO; + ret = devm_add_action_or_reset(dev, ams_iounmap_ps, ams); + if (ret < 0) + return ret; + + /* add PS channels to iio device channels */ + memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels)); + } else if (fwnode_property_match_string(fwnode, "compatible", + "xlnx,zynqmp-ams-pl") == 0) { + ams->pl_base = fwnode_iomap(fwnode, 0); + if (!ams->pl_base) + return -ENXIO; + + ret = devm_add_action_or_reset(dev, ams_iounmap_pl, ams); + if (ret < 0) + return ret; + + /* Copy only first 10 fix channels */ + memcpy(channels, ams_pl_channels, AMS_PL_MAX_FIXED_CHANNEL * sizeof(*channels)); + num_channels += AMS_PL_MAX_FIXED_CHANNEL; + num_channels = ams_get_ext_chan(fwnode, channels, + num_channels); + } else if (fwnode_property_match_string(fwnode, "compatible", + "xlnx,zynqmp-ams") == 0) { + /* add AMS channels to iio device channels */ + memcpy(channels, ams_ctrl_channels, sizeof(ams_ctrl_channels)); + num_channels += ARRAY_SIZE(ams_ctrl_channels); + } else { + return -EINVAL; + } + + return num_channels; +} + +static int ams_parse_firmware(struct iio_dev *indio_dev) +{ + struct ams *ams = iio_priv(indio_dev); + struct iio_chan_spec *ams_channels, *dev_channels; + struct device *dev = indio_dev->dev.parent; + struct fwnode_handle *child = NULL; + struct fwnode_handle *fwnode = dev_fwnode(dev); + size_t ams_size, dev_size; + int ret, ch_cnt = 0, i, rising_off, falling_off; + unsigned int num_channels = 0; + + ams_size = ARRAY_SIZE(ams_ps_channels) + ARRAY_SIZE(ams_pl_channels) + + ARRAY_SIZE(ams_ctrl_channels); + + /* Initialize buffer for channel specification */ + ams_channels = devm_kcalloc(dev, ams_size, sizeof(*ams_channels), GFP_KERNEL); + if (!ams_channels) + return -ENOMEM; + + if (fwnode_device_is_available(fwnode)) { + ret = ams_init_module(indio_dev, fwnode, ams_channels); + if (ret < 0) + return ret; + + num_channels += ret; + } + + fwnode_for_each_child_node(fwnode, child) { + if (fwnode_device_is_available(child)) { + ret = ams_init_module(indio_dev, child, ams_channels + num_channels); + if (ret < 0) { + fwnode_handle_put(child); + return ret; + } + + num_channels += ret; + } + } + + for (i = 0; i < num_channels; i++) { + ams_channels[i].channel = ch_cnt++; + + if (ams_channels[i].scan_index < AMS_CTRL_SEQ_BASE) { + /* set threshold to max and min for each channel */ + falling_off = + ams_get_alarm_offset(ams_channels[i].scan_index, + IIO_EV_DIR_FALLING); + rising_off = + ams_get_alarm_offset(ams_channels[i].scan_index, + IIO_EV_DIR_RISING); + if (ams_channels[i].scan_index >= AMS_PS_SEQ_MAX) { + writel(AMS_ALARM_THR_MIN, + ams->pl_base + falling_off); + writel(AMS_ALARM_THR_MAX, + ams->pl_base + rising_off); + } else { + writel(AMS_ALARM_THR_MIN, + ams->ps_base + falling_off); + writel(AMS_ALARM_THR_MAX, + ams->ps_base + rising_off); + } + } + } + + dev_size = array_size(sizeof(*dev_channels), num_channels); + if (dev_size == SIZE_MAX) + return -ENOMEM; + + dev_channels = devm_krealloc(dev, ams_channels, dev_size, GFP_KERNEL); + if (!dev_channels) + ret = -ENOMEM; + + indio_dev->channels = dev_channels; + indio_dev->num_channels = num_channels; + + return 0; +} + +static const struct iio_info iio_ams_info = { + .read_raw = &ams_read_raw, + .read_event_config = &ams_read_event_config, + .write_event_config = &ams_write_event_config, + .read_event_value = &ams_read_event_value, + .write_event_value = &ams_write_event_value, +}; + +static const struct of_device_id ams_of_match_table[] = { + { .compatible = "xlnx,zynqmp-ams" }, + { } +}; +MODULE_DEVICE_TABLE(of, ams_of_match_table); + +static void ams_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static void ams_cancel_delayed_work(void *data) +{ + cancel_delayed_work(data); +} + +static int ams_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct ams *ams; + int ret; + int irq; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ams)); + if (!indio_dev) + return -ENOMEM; + + ams = iio_priv(indio_dev); + mutex_init(&ams->lock); + spin_lock_init(&ams->intr_lock); + + indio_dev->name = "xilinx-ams"; + + indio_dev->info = &iio_ams_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ams->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ams->base)) + return PTR_ERR(ams->base); + + ams->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ams->clk)) + return PTR_ERR(ams->clk); + + ret = clk_prepare_enable(ams->clk); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, ams_clk_disable_unprepare, ams->clk); + if (ret < 0) + return ret; + + INIT_DELAYED_WORK(&ams->ams_unmask_work, ams_unmask_worker); + ret = devm_add_action_or_reset(&pdev->dev, ams_cancel_delayed_work, + &ams->ams_unmask_work); + if (ret < 0) + return ret; + + ret = ams_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failure in parsing DT\n"); + + ret = ams_init_device(ams); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to initialize AMS\n"); + + ams_enable_channel_sequence(indio_dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return ret; + + ret = devm_request_irq(&pdev->dev, irq, &ams_irq, 0, "ams-irq", + indio_dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to register interrupt\n"); + + platform_set_drvdata(pdev, indio_dev); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static int __maybe_unused ams_suspend(struct device *dev) +{ + struct ams *ams = iio_priv(dev_get_drvdata(dev)); + + clk_disable_unprepare(ams->clk); + + return 0; +} + +static int __maybe_unused ams_resume(struct device *dev) +{ + struct ams *ams = iio_priv(dev_get_drvdata(dev)); + + return clk_prepare_enable(ams->clk); +} + +static SIMPLE_DEV_PM_OPS(ams_pm_ops, ams_suspend, ams_resume); + +static struct platform_driver ams_driver = { + .probe = ams_probe, + .driver = { + .name = "xilinx-ams", + .pm = &ams_pm_ops, + .of_match_table = ams_of_match_table, + }, +}; +module_platform_driver(ams_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Xilinx, Inc."); -- cgit From f4a73a97accf5635815de148cf077fa6d076812d Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 20 Dec 2021 16:47:26 +0000 Subject: iio:addac:ad74413r: Fix uninitialized ret in a path that won't be hit. I don't believe it's possible to hit this, because we drop out of __iio_update_buffers() earlier in the event of an empty list. However, that is not visible to the compiler so lets return an error if we do hit the loop with an empty bitmask. Fixes: 5d97d9e9a703 ("iio: addac: ad74413r: fix off by one in ad74413r_parse_channel_config()") Signed-off-by: Jonathan Cameron Cc: Cosmin Tanislav Link: https://lore.kernel.org/r/20211220164726.3136307-1-jic23@kernel.org --- drivers/iio/addac/ad74413r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 289d254943e1..5271073bb74e 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -843,7 +843,7 @@ static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, u8 *rx_buf = &st->adc_samples_buf.rx_buf[-1 * AD74413R_FRAME_SIZE]; u8 *tx_buf = st->adc_samples_tx_buf; unsigned int channel; - int ret; + int ret = -EINVAL; mutex_lock(&st->lock); -- cgit From c9791a94384af07592d29504004d2255dbaf8663 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:27:28 +0000 Subject: iio: adc: ti-adc081c: Partial revert of removal of ACPI IDs Unfortuanately a non standards compliant ACPI ID is known to be in the wild on some AAEON boards. Partly revert the removal of these IDs so that ADC081C will again work + add a comment to that affect for future reference. Whilst here use generic firmware properties rather than the ACPI specific handling previously found in this driver. Reported-by: Kunyang Fan Fixes: c458b7ca3fd0 ("iio:adc:ti-adc081c: Drop ACPI ids that seem very unlikely to be official.") Signed-off-by: Jonathan Cameron Cc: Andy Shevchenko Tested-by: Kunyang Fan #UP-extremei11 Link: https://lore.kernel.org/r/20211205172728.2826512-1-jic23@kernel.org Cc: --- drivers/iio/adc/ti-adc081c.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 16fc608db36a..bd48b073e720 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -156,13 +157,16 @@ static int adc081c_probe(struct i2c_client *client, { struct iio_dev *iio; struct adc081c *adc; - struct adcxx1c_model *model; + const struct adcxx1c_model *model; int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EOPNOTSUPP; - model = &adcxx1c_models[id->driver_data]; + if (dev_fwnode(&client->dev)) + model = device_get_match_data(&client->dev); + else + model = &adcxx1c_models[id->driver_data]; iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); if (!iio) @@ -210,10 +214,17 @@ static const struct i2c_device_id adc081c_id[] = { }; MODULE_DEVICE_TABLE(i2c, adc081c_id); +static const struct acpi_device_id adc081c_acpi_match[] = { + /* Used on some AAEON boards */ + { "ADC081C", (kernel_ulong_t)&adcxx1c_models[ADC081C] }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc081c_acpi_match); + static const struct of_device_id adc081c_of_match[] = { - { .compatible = "ti,adc081c" }, - { .compatible = "ti,adc101c" }, - { .compatible = "ti,adc121c" }, + { .compatible = "ti,adc081c", .data = &adcxx1c_models[ADC081C] }, + { .compatible = "ti,adc101c", .data = &adcxx1c_models[ADC101C] }, + { .compatible = "ti,adc121c", .data = &adcxx1c_models[ADC121C] }, { } }; MODULE_DEVICE_TABLE(of, adc081c_of_match); @@ -222,6 +233,7 @@ static struct i2c_driver adc081c_driver = { .driver = { .name = "adc081c", .of_match_table = adc081c_of_match, + .acpi_match_table = adc081c_acpi_match, }, .probe = adc081c_probe, .id_table = adc081c_id, -- cgit