summaryrefslogtreecommitdiff
path: root/drivers/iio
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-08-17 19:48:42 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-08-17 19:48:42 +0200
commit9d9d90a9af5472e0698cc5fb7b2acc37e5837c95 (patch)
treee8647d20d1317556150fb08be046029ed427017a /drivers/iio
parent9caf92ab573fd83c7455b65d4eefcefc1a9d2188 (diff)
parentd484c21bacfa8bd2fa9fc26393ec59108f508c4c (diff)
Merge tag 'iio-for-5.15b' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes: 2nd set of new IIO device support and cleanups for the 5.15 cycle. A small pull request to pick up a few new drivers and some cleanup and fix patches. New device support * ad5110 non-volatile digital potentiometer - New driver * renesas rzl/gl2 12-bit / 8 channel ADC block - New driver and bindings Minor or late breaking fixes and cleanups * ltc2983 - Fix a false assumption of initial interrupt during probe(). * hp03 - Use devm_* to simplify probe and allow the remove function to be dropped. * rockchip_saradc - Use a regulator notifier to reduce overheads of querying the scale. * tag 'iio-for-5.15b' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: iio: adc: Add driver for Renesas RZ/G2L A/D converter dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter iio: pressure: hp03: update device probe to register with devm functions iio: adc: rockchip_saradc: add voltage notifier so get referenced voltage once at probe iio: ltc2983: fix device probe iio: potentiometer: Add driver support for AD5110 dt-bindings: iio: potentiometer: Add AD5110 in trivial-devices
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/adc/Kconfig10
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/rockchip_saradc.c47
-rw-r--r--drivers/iio/adc/rzg2l_adc.c600
-rw-r--r--drivers/iio/potentiometer/Kconfig10
-rw-r--r--drivers/iio/potentiometer/Makefile1
-rw-r--r--drivers/iio/potentiometer/ad5110.c344
-rw-r--r--drivers/iio/pressure/hp03.c36
-rw-r--r--drivers/iio/temperature/ltc2983.c30
9 files changed, 1027 insertions, 52 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index db0c8fb60515..af168e1c9fdb 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -887,6 +887,16 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
+config RZG2L_ADC
+ tristate "Renesas RZ/G2L ADC driver"
+ depends on ARCH_R9A07G044 || COMPILE_TEST
+ help
+ Say yes here to build support for the ADC found in Renesas
+ RZ/G2L family.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rzg2l_adc.
+
config SC27XX_ADC
tristate "Spreadtrum SC27xx series PMICs ADC"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index f70d877c555a..d68550f493e3 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
+obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
obj-$(CONFIG_STX104) += stx104.o
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index f3eb8d2e50dc..a237fe469a30 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -49,10 +49,12 @@ struct rockchip_saradc {
struct clk *clk;
struct completion completion;
struct regulator *vref;
+ int uv_vref;
struct reset_control *reset;
const struct rockchip_saradc_data *data;
u16 last_val;
const struct iio_chan_spec *last_chan;
+ struct notifier_block nb;
};
static void rockchip_saradc_power_down(struct rockchip_saradc *info)
@@ -105,13 +107,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- ret = regulator_get_voltage(info->vref);
- if (ret < 0) {
- dev_err(&indio_dev->dev, "failed to get voltage\n");
- return ret;
- }
-
- *val = ret / 1000;
+ *val = info->uv_vref / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
@@ -298,6 +294,26 @@ out:
return IRQ_HANDLED;
}
+static int rockchip_saradc_volt_notify(struct notifier_block *nb,
+ unsigned long event,
+ void *data)
+{
+ struct rockchip_saradc *info =
+ container_of(nb, struct rockchip_saradc, nb);
+
+ if (event & REGULATOR_EVENT_VOLTAGE_CHANGE)
+ info->uv_vref = (unsigned long)data;
+
+ return NOTIFY_OK;
+}
+
+static void rockchip_saradc_regulator_unreg_notifier(void *data)
+{
+ struct rockchip_saradc *info = data;
+
+ regulator_unregister_notifier(info->vref, &info->nb);
+}
+
static int rockchip_saradc_probe(struct platform_device *pdev)
{
struct rockchip_saradc *info = NULL;
@@ -410,6 +426,12 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
return ret;
}
+ ret = regulator_get_voltage(info->vref);
+ if (ret < 0)
+ return ret;
+
+ info->uv_vref = ret;
+
ret = clk_prepare_enable(info->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable pclk\n");
@@ -450,6 +472,17 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (ret)
return ret;
+ info->nb.notifier_call = rockchip_saradc_volt_notify;
+ ret = regulator_register_notifier(info->vref, &info->nb);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rockchip_saradc_regulator_unreg_notifier,
+ info);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(&pdev->dev, indio_dev);
}
diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
new file mode 100644
index 000000000000..9996d5eef289
--- /dev/null
+++ b/drivers/iio/adc/rzg2l_adc.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G2L A/D Converter driver
+ *
+ * Copyright (c) 2021 Renesas Electronics Europe GmbH
+ *
+ * Author: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#define DRIVER_NAME "rzg2l-adc"
+
+#define RZG2L_ADM(n) ((n) * 0x4)
+#define RZG2L_ADM0_ADCE BIT(0)
+#define RZG2L_ADM0_ADBSY BIT(1)
+#define RZG2L_ADM0_PWDWNB BIT(2)
+#define RZG2L_ADM0_SRESB BIT(15)
+#define RZG2L_ADM1_TRG BIT(0)
+#define RZG2L_ADM1_MS BIT(2)
+#define RZG2L_ADM1_BS BIT(4)
+#define RZG2L_ADM1_EGA_MASK GENMASK(13, 12)
+#define RZG2L_ADM2_CHSEL_MASK GENMASK(7, 0)
+#define RZG2L_ADM3_ADIL_MASK GENMASK(31, 24)
+#define RZG2L_ADM3_ADCMP_MASK GENMASK(23, 16)
+#define RZG2L_ADM3_ADCMP_E FIELD_PREP(RZG2L_ADM3_ADCMP_MASK, 0xe)
+#define RZG2L_ADM3_ADSMP_MASK GENMASK(15, 0)
+
+#define RZG2L_ADINT 0x20
+#define RZG2L_ADINT_INTEN_MASK GENMASK(7, 0)
+#define RZG2L_ADINT_CSEEN BIT(16)
+#define RZG2L_ADINT_INTS BIT(31)
+
+#define RZG2L_ADSTS 0x24
+#define RZG2L_ADSTS_CSEST BIT(16)
+#define RZG2L_ADSTS_INTST_MASK GENMASK(7, 0)
+
+#define RZG2L_ADIVC 0x28
+#define RZG2L_ADIVC_DIVADC_MASK GENMASK(8, 0)
+#define RZG2L_ADIVC_DIVADC_4 FIELD_PREP(RZG2L_ADIVC_DIVADC_MASK, 0x4)
+
+#define RZG2L_ADFIL 0x2c
+
+#define RZG2L_ADCR(n) (0x30 + ((n) * 0x4))
+#define RZG2L_ADCR_AD_MASK GENMASK(11, 0)
+
+#define RZG2L_ADSMP_DEFUALT_SAMPLING 0x578
+
+#define RZG2L_ADC_MAX_CHANNELS 8
+#define RZG2L_ADC_CHN_MASK 0x7
+#define RZG2L_ADC_TIMEOUT usecs_to_jiffies(1 * 4)
+
+struct rzg2l_adc_data {
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+};
+
+struct rzg2l_adc {
+ void __iomem *base;
+ struct clk *pclk;
+ struct clk *adclk;
+ struct reset_control *presetn;
+ struct reset_control *adrstn;
+ struct completion completion;
+ const struct rzg2l_adc_data *data;
+ struct mutex lock;
+ u16 last_val[RZG2L_ADC_MAX_CHANNELS];
+};
+
+static const char * const rzg2l_adc_channel_name[] = {
+ "adc0",
+ "adc1",
+ "adc2",
+ "adc3",
+ "adc4",
+ "adc5",
+ "adc6",
+ "adc7",
+};
+
+static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg)
+{
+ return readl(adc->base + reg);
+}
+
+static void rzg2l_adc_writel(struct rzg2l_adc *adc, unsigned int reg, u32 val)
+{
+ writel(val, adc->base + reg);
+}
+
+static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on)
+{
+ u32 reg;
+
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(0));
+ if (on)
+ reg |= RZG2L_ADM0_PWDWNB;
+ else
+ reg &= ~RZG2L_ADM0_PWDWNB;
+ rzg2l_adc_writel(adc, RZG2L_ADM(0), reg);
+ udelay(2);
+}
+
+static void rzg2l_adc_start_stop(struct rzg2l_adc *adc, bool start)
+{
+ int timeout = 5;
+ u32 reg;
+
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(0));
+ if (start)
+ reg |= RZG2L_ADM0_ADCE;
+ else
+ reg &= ~RZG2L_ADM0_ADCE;
+ rzg2l_adc_writel(adc, RZG2L_ADM(0), reg);
+
+ if (start)
+ return;
+
+ do {
+ usleep_range(100, 200);
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(0));
+ timeout--;
+ if (!timeout) {
+ pr_err("%s stopping ADC timed out\n", __func__);
+ break;
+ }
+ } while (((reg & RZG2L_ADM0_ADBSY) || (reg & RZG2L_ADM0_ADCE)));
+}
+
+static void rzg2l_set_trigger(struct rzg2l_adc *adc)
+{
+ u32 reg;
+
+ /*
+ * Setup ADM1 for SW trigger
+ * EGA[13:12] - Set 00 to indicate hardware trigger is invalid
+ * BS[4] - Enable 1-buffer mode
+ * MS[1] - Enable Select mode
+ * TRG[0] - Enable software trigger mode
+ */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(1));
+ reg &= ~RZG2L_ADM1_EGA_MASK;
+ reg &= ~RZG2L_ADM1_BS;
+ reg &= ~RZG2L_ADM1_TRG;
+ reg |= RZG2L_ADM1_MS;
+ rzg2l_adc_writel(adc, RZG2L_ADM(1), reg);
+}
+
+static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch)
+{
+ u32 reg;
+
+ if (rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_ADBSY)
+ return -EBUSY;
+
+ rzg2l_set_trigger(adc);
+
+ /* Select analog input channel subjected to conversion. */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(2));
+ reg &= ~RZG2L_ADM2_CHSEL_MASK;
+ reg |= BIT(ch);
+ rzg2l_adc_writel(adc, RZG2L_ADM(2), reg);
+
+ /*
+ * Setup ADINT
+ * INTS[31] - Select pulse signal
+ * CSEEN[16] - Enable channel select error interrupt
+ * INTEN[7:0] - Select channel interrupt
+ */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADINT);
+ reg &= ~RZG2L_ADINT_INTS;
+ reg &= ~RZG2L_ADINT_INTEN_MASK;
+ reg |= (RZG2L_ADINT_CSEEN | BIT(ch));
+ rzg2l_adc_writel(adc, RZG2L_ADINT, reg);
+
+ return 0;
+}
+
+static int rzg2l_adc_set_power(struct iio_dev *indio_dev, bool on)
+{
+ struct device *dev = indio_dev->dev.parent;
+
+ if (on)
+ return pm_runtime_resume_and_get(dev);
+
+ return pm_runtime_put_sync(dev);
+}
+
+static int rzg2l_adc_conversion(struct iio_dev *indio_dev, struct rzg2l_adc *adc, u8 ch)
+{
+ int ret;
+
+ ret = rzg2l_adc_set_power(indio_dev, true);
+ if (ret)
+ return ret;
+
+ ret = rzg2l_adc_conversion_setup(adc, ch);
+ if (ret) {
+ rzg2l_adc_set_power(indio_dev, false);
+ return ret;
+ }
+
+ reinit_completion(&adc->completion);
+
+ rzg2l_adc_start_stop(adc, true);
+
+ if (!wait_for_completion_timeout(&adc->completion, RZG2L_ADC_TIMEOUT)) {
+ rzg2l_adc_writel(adc, RZG2L_ADINT,
+ rzg2l_adc_readl(adc, RZG2L_ADINT) & ~RZG2L_ADINT_INTEN_MASK);
+ rzg2l_adc_start_stop(adc, false);
+ rzg2l_adc_set_power(indio_dev, false);
+ return -ETIMEDOUT;
+ }
+
+ return rzg2l_adc_set_power(indio_dev, false);
+}
+
+static int rzg2l_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct rzg2l_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u8 ch;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ mutex_lock(&adc->lock);
+ ch = chan->channel & RZG2L_ADC_CHN_MASK;
+ ret = rzg2l_adc_conversion(indio_dev, adc, ch);
+ if (ret) {
+ mutex_unlock(&adc->lock);
+ return ret;
+ }
+ *val = adc->last_val[ch];
+ mutex_unlock(&adc->lock);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rzg2l_adc_read_label(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ char *label)
+{
+ if (chan->channel >= RZG2L_ADC_MAX_CHANNELS)
+ return -EINVAL;
+
+ return sysfs_emit(label, "%s\n", rzg2l_adc_channel_name[chan->channel]);
+}
+
+static const struct iio_info rzg2l_adc_iio_info = {
+ .read_raw = rzg2l_adc_read_raw,
+ .read_label = rzg2l_adc_read_label,
+};
+
+static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
+{
+ struct rzg2l_adc *adc = dev_id;
+ unsigned long intst;
+ u32 reg;
+ int ch;
+
+ reg = rzg2l_adc_readl(adc, RZG2L_ADSTS);
+
+ /* A/D conversion channel select error interrupt */
+ if (reg & RZG2L_ADSTS_CSEST) {
+ rzg2l_adc_writel(adc, RZG2L_ADSTS, reg);
+ return IRQ_HANDLED;
+ }
+
+ intst = reg & RZG2L_ADSTS_INTST_MASK;
+ if (!intst)
+ return IRQ_NONE;
+
+ for_each_set_bit(ch, &intst, RZG2L_ADC_MAX_CHANNELS)
+ adc->last_val[ch] = rzg2l_adc_readl(adc, RZG2L_ADCR(ch)) & RZG2L_ADCR_AD_MASK;
+
+ /* clear the channel interrupt */
+ rzg2l_adc_writel(adc, RZG2L_ADSTS, reg);
+
+ complete(&adc->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc)
+{
+ struct iio_chan_spec *chan_array;
+ struct fwnode_handle *fwnode;
+ struct rzg2l_adc_data *data;
+ unsigned int channel;
+ int num_channels;
+ int ret;
+ u8 i;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ num_channels = device_get_child_node_count(&pdev->dev);
+ if (!num_channels) {
+ dev_err(&pdev->dev, "no channel children\n");
+ return -ENODEV;
+ }
+
+ if (num_channels > RZG2L_ADC_MAX_CHANNELS) {
+ dev_err(&pdev->dev, "num of channel children out of range\n");
+ return -EINVAL;
+ }
+
+ chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array),
+ GFP_KERNEL);
+ if (!chan_array)
+ return -ENOMEM;
+
+ i = 0;
+ device_for_each_child_node(&pdev->dev, fwnode) {
+ ret = fwnode_property_read_u32(fwnode, "reg", &channel);
+ if (ret)
+ return ret;
+
+ if (channel >= RZG2L_ADC_MAX_CHANNELS)
+ return -EINVAL;
+
+ chan_array[i].type = IIO_VOLTAGE;
+ chan_array[i].indexed = 1;
+ chan_array[i].channel = channel;
+ chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan_array[i].datasheet_name = rzg2l_adc_channel_name[channel];
+ i++;
+ }
+
+ data->num_channels = num_channels;
+ data->channels = chan_array;
+ adc->data = data;
+
+ return 0;
+}
+
+static int rzg2l_adc_hw_init(struct rzg2l_adc *adc)
+{
+ int timeout = 5;
+ u32 reg;
+ int ret;
+
+ ret = clk_prepare_enable(adc->pclk);
+ if (ret)
+ return ret;
+
+ /* SW reset */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(0));
+ reg |= RZG2L_ADM0_SRESB;
+ rzg2l_adc_writel(adc, RZG2L_ADM(0), reg);
+
+ while (!(rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_SRESB)) {
+ if (!timeout) {
+ ret = -EBUSY;
+ goto exit_hw_init;
+ }
+ timeout--;
+ usleep_range(100, 200);
+ }
+
+ /* Only division by 4 can be set */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADIVC);
+ reg &= ~RZG2L_ADIVC_DIVADC_MASK;
+ reg |= RZG2L_ADIVC_DIVADC_4;
+ rzg2l_adc_writel(adc, RZG2L_ADIVC, reg);
+
+ /*
+ * Setup AMD3
+ * ADIL[31:24] - Should be always set to 0
+ * ADCMP[23:16] - Should be always set to 0xe
+ * ADSMP[15:0] - Set default (0x578) sampling period
+ */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(3));
+ reg &= ~RZG2L_ADM3_ADIL_MASK;
+ reg &= ~RZG2L_ADM3_ADCMP_MASK;
+ reg &= ~RZG2L_ADM3_ADSMP_MASK;
+ reg |= (RZG2L_ADM3_ADCMP_E | RZG2L_ADSMP_DEFUALT_SAMPLING);
+ rzg2l_adc_writel(adc, RZG2L_ADM(3), reg);
+
+exit_hw_init:
+ clk_disable_unprepare(adc->pclk);
+
+ return 0;
+}
+
+static void rzg2l_adc_pm_runtime_disable(void *data)
+{
+ struct device *dev = data;
+
+ pm_runtime_disable(dev->parent);
+}
+
+static void rzg2l_adc_pm_runtime_set_suspended(void *data)
+{
+ struct device *dev = data;
+
+ pm_runtime_set_suspended(dev->parent);
+}
+
+static void rzg2l_adc_reset_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
+static int rzg2l_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct rzg2l_adc *adc;
+ int ret;
+ int irq;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+
+ ret = rzg2l_adc_parse_properties(pdev, adc);
+ if (ret)
+ return ret;
+
+ mutex_init(&adc->lock);
+
+ adc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(adc->base))
+ return PTR_ERR(adc->base);
+
+ adc->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(adc->pclk)) {
+ dev_err(dev, "Failed to get pclk");
+ return PTR_ERR(adc->pclk);
+ }
+
+ adc->adclk = devm_clk_get(dev, "adclk");
+ if (IS_ERR(adc->adclk)) {
+ dev_err(dev, "Failed to get adclk");
+ return PTR_ERR(adc->adclk);
+ }
+
+ adc->adrstn = devm_reset_control_get_exclusive(dev, "adrst-n");
+ if (IS_ERR(adc->adrstn)) {
+ dev_err(dev, "failed to get adrstn\n");
+ return PTR_ERR(adc->adrstn);
+ }
+
+ adc->presetn = devm_reset_control_get_exclusive(dev, "presetn");
+ if (IS_ERR(adc->presetn)) {
+ dev_err(dev, "failed to get presetn\n");
+ return PTR_ERR(adc->presetn);
+ }
+
+ ret = reset_control_deassert(adc->adrstn);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to deassert adrstn pin, %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rzg2l_adc_reset_assert, adc->adrstn);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register adrstn assert devm action, %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = reset_control_deassert(adc->presetn);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to deassert presetn pin, %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rzg2l_adc_reset_assert, adc->presetn);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register presetn assert devm action, %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = rzg2l_adc_hw_init(adc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize ADC HW, %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "no irq resource\n");
+ return irq;
+ }
+
+ ret = devm_request_irq(dev, irq, rzg2l_adc_isr,
+ 0, dev_name(dev), adc);
+ if (ret < 0)
+ return ret;
+
+ init_completion(&adc->completion);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = DRIVER_NAME;
+ indio_dev->info = &rzg2l_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = adc->data->channels;
+ indio_dev->num_channels = adc->data->num_channels;
+
+ pm_runtime_set_suspended(dev);
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rzg2l_adc_pm_runtime_set_suspended, &indio_dev->dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(dev);
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rzg2l_adc_pm_runtime_disable, &indio_dev->dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id rzg2l_adc_match[] = {
+ { .compatible = "renesas,rzg2l-adc",},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzg2l_adc_match);
+
+static int __maybe_unused rzg2l_adc_pm_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rzg2l_adc *adc = iio_priv(indio_dev);
+
+ rzg2l_adc_pwr(adc, false);
+ clk_disable_unprepare(adc->adclk);
+ clk_disable_unprepare(adc->pclk);
+
+ return 0;
+}
+
+static int __maybe_unused rzg2l_adc_pm_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rzg2l_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = clk_prepare_enable(adc->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(adc->adclk);
+ if (ret)
+ return ret;
+
+ rzg2l_adc_pwr(adc, true);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rzg2l_adc_pm_ops = {
+ SET_RUNTIME_PM_OPS(rzg2l_adc_pm_runtime_suspend,
+ rzg2l_adc_pm_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver rzg2l_adc_driver = {
+ .probe = rzg2l_adc_probe,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = rzg2l_adc_match,
+ .pm = &rzg2l_adc_pm_ops,
+ },
+};
+
+module_platform_driver(rzg2l_adc_driver);
+
+MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
+MODULE_DESCRIPTION("Renesas RZ/G2L ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig
index 4cac0173db8b..832df8da2bc6 100644
--- a/drivers/iio/potentiometer/Kconfig
+++ b/drivers/iio/potentiometer/Kconfig
@@ -6,6 +6,16 @@
menu "Digital potentiometers"
+config AD5110
+ tristate "Analog Devices AD5110 and similar Digital Potentiometer driver"
+ depends on I2C
+ help
+ Say yes here to build support for the Analog Devices AD5110, AD5112
+ and AD5114 digital potentiometer chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5110.
+
config AD5272
tristate "Analog Devices AD5272 and similar Digital Potentiometer driver"
depends on I2C
diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile
index 091adf3cdd0b..5ebb8e3bbd76 100644
--- a/drivers/iio/potentiometer/Makefile
+++ b/drivers/iio/potentiometer/Makefile
@@ -4,6 +4,7 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AD5110) += ad5110.o
obj-$(CONFIG_AD5272) += ad5272.o
obj-$(CONFIG_DS1803) += ds1803.o
obj-$(CONFIG_MAX5432) += max5432.o
diff --git a/drivers/iio/potentiometer/ad5110.c b/drivers/iio/potentiometer/ad5110.c
new file mode 100644
index 000000000000..d4eeedae56e5
--- /dev/null
+++ b/drivers/iio/potentiometer/ad5110.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices AD5110 digital potentiometer driver
+ *
+ * Copyright (C) 2021 Mugilraj Dhavachelvan <dmugil2000@gmail.com>
+ *
+ * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/AD5110_5112_5114.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* AD5110 commands */
+#define AD5110_EEPROM_WR 1
+#define AD5110_RDAC_WR 2
+#define AD5110_SHUTDOWN 3
+#define AD5110_RESET 4
+#define AD5110_RDAC_RD 5
+#define AD5110_EEPROM_RD 6
+
+/* AD5110_EEPROM_RD data */
+#define AD5110_WIPER_POS 0
+#define AD5110_RESISTOR_TOL 1
+
+#define AD5110_WIPER_RESISTANCE 70
+
+struct ad5110_cfg {
+ int max_pos;
+ int kohms;
+ int shift;
+};
+
+enum ad5110_type {
+ AD5110_10,
+ AD5110_80,
+ AD5112_05,
+ AD5112_10,
+ AD5112_80,
+ AD5114_10,
+ AD5114_80,
+};
+
+static const struct ad5110_cfg ad5110_cfg[] = {
+ [AD5110_10] = { .max_pos = 128, .kohms = 10 },
+ [AD5110_80] = { .max_pos = 128, .kohms = 80 },
+ [AD5112_05] = { .max_pos = 64, .kohms = 5, .shift = 1 },
+ [AD5112_10] = { .max_pos = 64, .kohms = 10, .shift = 1 },
+ [AD5112_80] = { .max_pos = 64, .kohms = 80, .shift = 1 },
+ [AD5114_10] = { .max_pos = 32, .kohms = 10, .shift = 2 },
+ [AD5114_80] = { .max_pos = 32, .kohms = 80, .shift = 2 },
+};
+
+struct ad5110_data {
+ struct i2c_client *client;
+ s16 tol; /* resistor tolerance */
+ bool enable;
+ struct mutex lock;
+ const struct ad5110_cfg *cfg;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 buf[2] ____cacheline_aligned;
+};
+
+static const struct iio_chan_spec ad5110_channels[] = {
+ {
+ .type = IIO_RESISTANCE,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_ENABLE),
+ },
+};
+
+static int ad5110_read(struct ad5110_data *data, u8 cmd, int *val)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ data->buf[0] = cmd;
+ data->buf[1] = *val;
+
+ ret = i2c_master_send_dmasafe(data->client, data->buf, sizeof(data->buf));
+ if (ret < 0) {
+ goto error;
+ } else if (ret != sizeof(data->buf)) {
+ ret = -EIO;
+ goto error;
+ }
+
+ ret = i2c_master_recv_dmasafe(data->client, data->buf, 1);
+ if (ret < 0) {
+ goto error;
+ } else if (ret != 1) {
+ ret = -EIO;
+ goto error;
+ }
+
+ *val = data->buf[0];
+ ret = 0;
+
+error:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int ad5110_write(struct ad5110_data *data, u8 cmd, u8 val)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ data->buf[0] = cmd;
+ data->buf[1] = val;
+
+ ret = i2c_master_send_dmasafe(data->client, data->buf, sizeof(data->buf));
+ if (ret < 0) {
+ goto error;
+ } else if (ret != sizeof(data->buf)) {
+ ret = -EIO;
+ goto error;
+ }
+
+ ret = 0;
+
+error:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int ad5110_resistor_tol(struct ad5110_data *data, u8 cmd, int val)
+{
+ int ret;
+
+ ret = ad5110_read(data, cmd, &val);
+ if (ret)
+ return ret;
+
+ data->tol = data->cfg->kohms * (val & GENMASK(6, 0)) * 10 / 8;
+ if (!(val & BIT(7)))
+ data->tol *= -1;
+
+ return 0;
+}
+
+static ssize_t store_eeprom_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5110_data *data = iio_priv(indio_dev);
+ int val = AD5110_WIPER_POS;
+ int ret;
+
+ ret = ad5110_read(data, AD5110_EEPROM_RD, &val);
+ if (ret)
+ return ret;
+
+ val = val >> data->cfg->shift;
+ return iio_format_value(buf, IIO_VAL_INT, 1, &val);
+}
+
+static ssize_t store_eeprom_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5110_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad5110_write(data, AD5110_EEPROM_WR, 0);
+ if (ret) {
+ dev_err(&data->client->dev, "RDAC to EEPROM write failed\n");
+ return ret;
+ }
+
+ /* The storing of EEPROM data takes approximately 18 ms. */
+ msleep(20);
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_RW(store_eeprom, 0);
+
+static struct attribute *ad5110_attributes[] = {
+ &iio_dev_attr_store_eeprom.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad5110_attribute_group = {
+ .attrs = ad5110_attributes,
+};
+
+static int ad5110_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad5110_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5110_read(data, AD5110_RDAC_RD, val);
+ if (ret)
+ return ret;
+
+ *val = *val >> data->cfg->shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = AD5110_WIPER_RESISTANCE * data->cfg->max_pos;
+ *val2 = 1000 * data->cfg->kohms + data->tol;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1000 * data->cfg->kohms + data->tol;
+ *val2 = data->cfg->max_pos;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_ENABLE:
+ *val = data->enable;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5110_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ad5110_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val > data->cfg->max_pos || val < 0)
+ return -EINVAL;
+
+ return ad5110_write(data, AD5110_RDAC_WR, val << data->cfg->shift);
+ case IIO_CHAN_INFO_ENABLE:
+ if (val < 0 || val > 1)
+ return -EINVAL;
+ if (data->enable == val)
+ return 0;
+ ret = ad5110_write(data, AD5110_SHUTDOWN, val ? 0 : 1);
+ if (ret)
+ return ret;
+ data->enable = val;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad5110_info = {
+ .read_raw = ad5110_read_raw,
+ .write_raw = ad5110_write_raw,
+ .attrs = &ad5110_attribute_group,
+};
+
+#define AD5110_COMPATIBLE(of_compatible, cfg) { \
+ .compatible = of_compatible, \
+ .data = &ad5110_cfg[cfg], \
+}
+
+static const struct of_device_id ad5110_of_match[] = {
+ AD5110_COMPATIBLE("adi,ad5110-10", AD5110_10),
+ AD5110_COMPATIBLE("adi,ad5110-80", AD5110_80),
+ AD5110_COMPATIBLE("adi,ad5112-05", AD5112_05),
+ AD5110_COMPATIBLE("adi,ad5112-10", AD5112_10),
+ AD5110_COMPATIBLE("adi,ad5112-80", AD5112_80),
+ AD5110_COMPATIBLE("adi,ad5114-10", AD5114_10),
+ AD5110_COMPATIBLE("adi,ad5114-80", AD5114_80),
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad5110_of_match);
+
+static const struct i2c_device_id ad5110_id[] = {
+ { "ad5110-10", AD5110_10 },
+ { "ad5110-80", AD5110_80 },
+ { "ad5112-05", AD5112_05 },
+ { "ad5112-10", AD5112_10 },
+ { "ad5112-80", AD5112_80 },
+ { "ad5114-10", AD5114_10 },
+ { "ad5114-80", AD5114_80 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad5110_id);
+
+static int ad5110_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct ad5110_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ mutex_init(&data->lock);
+ data->enable = 1;
+ data->cfg = device_get_match_data(dev);
+
+ /* refresh RDAC register with EEPROM */
+ ret = ad5110_write(data, AD5110_RESET, 0);
+ if (ret) {
+ dev_err(dev, "Refresh RDAC with EEPROM failed\n");
+ return ret;
+ }
+
+ ret = ad5110_resistor_tol(data, AD5110_EEPROM_RD, AD5110_RESISTOR_TOL);
+ if (ret) {
+ dev_err(dev, "Read resistor tolerance failed\n");
+ return ret;
+ }
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad5110_info;
+ indio_dev->channels = ad5110_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad5110_channels);
+ indio_dev->name = client->name;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct i2c_driver ad5110_driver = {
+ .driver = {
+ .name = "ad5110",
+ .of_match_table = ad5110_of_match,
+ },
+ .probe_new = ad5110_probe,
+ .id_table = ad5110_id,
+};
+module_i2c_driver(ad5110_driver);
+
+MODULE_AUTHOR("Mugilraj Dhavachelvan <dmugil2000@gmail.com>");
+MODULE_DESCRIPTION("AD5110 digital potentiometer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/hp03.c b/drivers/iio/pressure/hp03.c
index e40b1d7dc129..9538118c9648 100644
--- a/drivers/iio/pressure/hp03.c
+++ b/drivers/iio/pressure/hp03.c
@@ -242,47 +242,26 @@ static int hp03_probe(struct i2c_client *client,
* which has it's dedicated I2C address and contains
* the calibration constants for the sensor.
*/
- priv->eeprom_client = i2c_new_dummy_device(client->adapter, HP03_EEPROM_ADDR);
+ priv->eeprom_client = devm_i2c_new_dummy_device(dev, client->adapter,
+ HP03_EEPROM_ADDR);
if (IS_ERR(priv->eeprom_client)) {
dev_err(dev, "New EEPROM I2C device failed\n");
return PTR_ERR(priv->eeprom_client);
}
- priv->eeprom_regmap = regmap_init_i2c(priv->eeprom_client,
- &hp03_regmap_config);
+ priv->eeprom_regmap = devm_regmap_init_i2c(priv->eeprom_client,
+ &hp03_regmap_config);
if (IS_ERR(priv->eeprom_regmap)) {
dev_err(dev, "Failed to allocate EEPROM regmap\n");
- ret = PTR_ERR(priv->eeprom_regmap);
- goto err_cleanup_eeprom_client;
+ return PTR_ERR(priv->eeprom_regmap);
}
- ret = iio_device_register(indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
if (ret) {
dev_err(dev, "Failed to register IIO device\n");
- goto err_cleanup_eeprom_regmap;
+ return ret;
}
- i2c_set_clientdata(client, indio_dev);
-
- return 0;
-
-err_cleanup_eeprom_regmap:
- regmap_exit(priv->eeprom_regmap);
-
-err_cleanup_eeprom_client:
- i2c_unregister_device(priv->eeprom_client);
- return ret;
-}
-
-static int hp03_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct hp03_priv *priv = iio_priv(indio_dev);
-
- iio_device_unregister(indio_dev);
- regmap_exit(priv->eeprom_regmap);
- i2c_unregister_device(priv->eeprom_client);
-
return 0;
}
@@ -304,7 +283,6 @@ static struct i2c_driver hp03_driver = {
.of_match_table = hp03_of_match,
},
.probe = hp03_probe,
- .remove = hp03_remove,
.id_table = hp03_id,
};
module_i2c_driver(hp03_driver);
diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c
index 3b5ba26d7d86..3b4a0e60e605 100644
--- a/drivers/iio/temperature/ltc2983.c
+++ b/drivers/iio/temperature/ltc2983.c
@@ -89,6 +89,8 @@
#define LTC2983_STATUS_START_MASK BIT(7)
#define LTC2983_STATUS_START(x) FIELD_PREP(LTC2983_STATUS_START_MASK, x)
+#define LTC2983_STATUS_UP_MASK GENMASK(7, 6)
+#define LTC2983_STATUS_UP(reg) FIELD_GET(LTC2983_STATUS_UP_MASK, reg)
#define LTC2983_STATUS_CHAN_SEL_MASK GENMASK(4, 0)
#define LTC2983_STATUS_CHAN_SEL(x) \
@@ -1362,17 +1364,16 @@ put_child:
static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio)
{
- u32 iio_chan_t = 0, iio_chan_v = 0, chan, iio_idx = 0;
+ u32 iio_chan_t = 0, iio_chan_v = 0, chan, iio_idx = 0, status;
int ret;
- unsigned long time;
-
- /* make sure the device is up */
- time = wait_for_completion_timeout(&st->completion,
- msecs_to_jiffies(250));
- if (!time) {
+ /* make sure the device is up: start bit (7) is 0 and done bit (6) is 1 */
+ ret = regmap_read_poll_timeout(st->regmap, LTC2983_STATUS_REG, status,
+ LTC2983_STATUS_UP(status) == 1, 25000,
+ 25000 * 10);
+ if (ret) {
dev_err(&st->spi->dev, "Device startup timed out\n");
- return -ETIMEDOUT;
+ return ret;
}
st->iio_chan = devm_kzalloc(&st->spi->dev,
@@ -1492,10 +1493,11 @@ static int ltc2983_probe(struct spi_device *spi)
ret = ltc2983_parse_dt(st);
if (ret)
return ret;
- /*
- * let's request the irq now so it is used to sync the device
- * startup in ltc2983_setup()
- */
+
+ ret = ltc2983_setup(st, true);
+ if (ret)
+ return ret;
+
ret = devm_request_irq(&spi->dev, spi->irq, ltc2983_irq_handler,
IRQF_TRIGGER_RISING, name, st);
if (ret) {
@@ -1503,10 +1505,6 @@ static int ltc2983_probe(struct spi_device *spi)
return ret;
}
- ret = ltc2983_setup(st, true);
- if (ret)
- return ret;
-
indio_dev->name = name;
indio_dev->num_channels = st->iio_channels;
indio_dev->channels = st->iio_chan;