linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] Renesas RZ/G2L ADC driver support
@ 2021-06-29 22:03 Lad Prabhakar
  2021-06-29 22:03 ` [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter Lad Prabhakar
                   ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Lad Prabhakar @ 2021-06-29 22:03 UTC (permalink / raw)
  To: Geert Uytterhoeven, Rob Herring, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel
  Cc: linux-iio, devicetree, linux-kernel, linux-renesas-soc,
	Prabhakar, Biju Das, Lad Prabhakar

Hi All,

This patch series adds DT binding and driver support for ADC block
found on Renesas RZ/G2L family.

Patches are based on top of rzg2l-update-clock-defs-v4 branch found on [1]

[1] https://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git/

Cheers,
Prabhakar

Lad Prabhakar (2):
  dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L
    A/D converter
  iio: adc: Add driver for Renesas RZ/G2L A/D converter

 .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 +++++
 MAINTAINERS                                   |   8 +
 drivers/iio/adc/Kconfig                       |  10 +
 drivers/iio/adc/Makefile                      |   1 +
 drivers/iio/adc/rzg2l_adc.c                   | 489 ++++++++++++++++++
 5 files changed, 629 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
 create mode 100644 drivers/iio/adc/rzg2l_adc.c


base-commit: 06c1e6911a7a76b446e4b00fc8bad5d8465932f8
-- 
2.17.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-06-29 22:03 [PATCH 0/2] Renesas RZ/G2L ADC driver support Lad Prabhakar
@ 2021-06-29 22:03 ` Lad Prabhakar
  2021-07-01 14:02   ` Rob Herring
                     ` (2 more replies)
  2021-06-29 22:03 ` [PATCH 2/2] iio: adc: Add driver " Lad Prabhakar
  2021-07-03 17:21 ` [PATCH 0/2] Renesas RZ/G2L ADC driver support Jonathan Cameron
  2 siblings, 3 replies; 19+ messages in thread
From: Lad Prabhakar @ 2021-06-29 22:03 UTC (permalink / raw)
  To: Geert Uytterhoeven, Rob Herring, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel
  Cc: linux-iio, devicetree, linux-kernel, linux-renesas-soc,
	Prabhakar, Biju Das, Lad Prabhakar

Add binding documentation for Renesas RZ/G2L A/D converter block.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
 1 file changed, 121 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml

diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
new file mode 100644
index 000000000000..db935d6d59eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas RZ/G2L ADC
+
+maintainers:
+  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+
+description: |
+  A/D Converter block is a successive approximation analog-to-digital converter
+  with a 12-bit accuracy. Up to eight analog input channels can be selected.
+  Conversions can be performed in single or repeat mode. Result of the ADC is
+  stored in a 32-bit data register corresponding to each channel.
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
+          - const: renesas,rzg2l-adc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: converter clock
+      - description: peripheral clock
+
+  clock-names:
+    items:
+      - const: adclk
+      - const: pclk
+
+  power-domains:
+    maxItems: 1
+
+  resets:
+    maxItems: 2
+
+  reset-names:
+    items:
+      - const: presetn
+      - const: adrst-n
+
+  renesas-rzg2l,adc-trigger-mode:
+    $ref: /schemas/types.yaml#/definitions/uint8
+    description: Trigger mode for A/D converter
+    enum:
+      - 0 # Software trigger mode (Defaults)
+      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
+      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)
+    default: 0
+
+  gpios:
+    description:
+      ADC_TRG trigger input pin
+    maxItems: 1
+
+  renesas-rzg2l,adc-channels:
+    $ref: /schemas/types.yaml#/definitions/uint8-array
+    description: Input channels available on platform
+    uniqueItems: true
+    minItems: 1
+    maxItems: 8
+    items:
+      enum: [0, 1, 2, 3, 4, 5, 6, 7]
+
+  "#io-channel-cells":
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - power-domains
+  - resets
+  - reset-names
+  - renesas-rzg2l,adc-channels
+  - "#io-channel-cells"
+
+allOf:
+  - if:
+      properties:
+        renesas-rzg2l,adc-trigger-mode:
+          const: 1
+    then:
+      required:
+        - gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/r9a07g044-cpg.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    adc: adc@10059000 {
+      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
+      reg = <0x10059000 0x400>;
+      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
+      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
+               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
+      clock-names = "adclk", "pclk";
+      power-domains = <&cpg>;
+      resets = <&cpg R9A07G044_ADC_PRESETN>,
+               <&cpg R9A07G044_ADC_ADRST_N>;
+      reset-names = "presetn", "adrst-n";
+      #io-channel-cells = <1>;
+      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
+      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
+    };
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/2] iio: adc: Add driver for Renesas RZ/G2L A/D converter
  2021-06-29 22:03 [PATCH 0/2] Renesas RZ/G2L ADC driver support Lad Prabhakar
  2021-06-29 22:03 ` [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter Lad Prabhakar
@ 2021-06-29 22:03 ` Lad Prabhakar
  2021-07-01 10:28   ` Alexandru Ardelean
  2021-07-03 17:21 ` [PATCH 0/2] Renesas RZ/G2L ADC driver support Jonathan Cameron
  2 siblings, 1 reply; 19+ messages in thread
From: Lad Prabhakar @ 2021-06-29 22:03 UTC (permalink / raw)
  To: Geert Uytterhoeven, Rob Herring, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel
  Cc: linux-iio, devicetree, linux-kernel, linux-renesas-soc,
	Prabhakar, Biju Das, Lad Prabhakar

Add ADC driver support for Renesas RZ/G2L A/D converter in SW
trigger mode.

A/D Converter block is a successive approximation analog-to-digital
converter with a 12-bit accuracy and supports a maximum of 8 input
channels.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 MAINTAINERS                 |   8 +
 drivers/iio/adc/Kconfig     |  10 +
 drivers/iio/adc/Makefile    |   1 +
 drivers/iio/adc/rzg2l_adc.c | 489 ++++++++++++++++++++++++++++++++++++
 4 files changed, 508 insertions(+)
 create mode 100644 drivers/iio/adc/rzg2l_adc.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 81e1edeceae4..bee4c3847e01 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15622,6 +15622,14 @@ L:	linux-renesas-soc@vger.kernel.org
 S:	Maintained
 F:	drivers/phy/renesas/phy-rcar-gen3-usb*.c
 
+RENESAS RZ/G2L A/D DRIVER
+M:	Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+L:	linux-iio@vger.kernel.org
+L:	linux-renesas-soc@vger.kernel.org
+S:	Supported
+F:	Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
+F:	drivers/iio/adc/rzg2l_adc.c
+
 RESET CONTROLLER FRAMEWORK
 M:	Philipp Zabel <p.zabel@pengutronix.de>
 S:	Maintained
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index c7946c439612..9408cbf97acc 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 a226657d19c0..d92bcc9c5fbb 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/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
new file mode 100644
index 000000000000..1c58eb8ae1ec
--- /dev/null
+++ b/drivers/iio/adc/rzg2l_adc.c
@@ -0,0 +1,489 @@
+// 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/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/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define ADM(n)			((n) * 0x4)
+#define ADM0_ADCE		BIT(0)
+#define ADM0_ADBSY		BIT(1)
+#define ADM0_PWDWNB		BIT(2)
+#define ADM0_SRESB		BIT(15)
+#define ADM1_TRG		BIT(0)
+#define ADM1_MS			BIT(2)
+#define ADM1_BS			BIT(4)
+#define ADM1_EGA_CLEAR		~GENMASK(13, 12)
+#define ADM2_CHSEL_CLEAR	~GENMASK(7, 0)
+#define ADM3_ADSMP		0x578
+#define ADM3_ADCMP		(0xe << 16)
+#define ADM3_ADIL_CLEAR		~GENMASK(31, 24)
+
+#define ADINT			0x20
+#define ADINT_CH_CLEAR		~GENMASK(7, 0)
+#define ADINT_CSEEN		BIT(16)
+#define ADINT_INTS		BIT(31)
+#define ADSTS			0x24
+#define ADINT_INTST_MASK	GENMASK(7, 0)
+#define ADSTS_CSEST		BIT(16)
+#define ADIVC			0x28
+#define ADIVC_DIVADC_CLEAR	~GENMASK(8, 0)
+#define ADIVC_DIVADC_4		0x4
+#define ADFIL			0x2c
+#define ADCR(n)			(0x30 + ((n) * 0x4))
+#define ADCR_AD_MASK		GENMASK(11, 0)
+
+#define ADC_MAX_CHANNELS	8
+#define ADC_CHN_MASK		0x7
+#define ADC_TIMEOUT		usecs_to_jiffies(1 * 4)
+
+enum trigger_mode {
+	SW_TRIGGER = 0,
+	SYNC_TRIGGER,
+	ASYNC_TRIGGER,
+};
+
+struct rzg2l_adc_data {
+	const struct iio_chan_spec *channels;
+	u8 num_channels;
+	u8 trigger;
+};
+
+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;
+	bool adc_disabled; /* protected with mlock mutex from indio_dev */
+	u16 last_val[ADC_MAX_CHANNELS];
+};
+
+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 int rzg2l_adc_adclk(struct rzg2l_adc *adc, bool prepare)
+{
+	if (prepare)
+		return clk_prepare_enable(adc->adclk);
+
+	clk_disable_unprepare(adc->adclk);
+	return 0;
+}
+
+static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on)
+{
+	u32 reg;
+
+	reg = rzg2l_adc_readl(adc, ADM(0));
+	if (on)
+		reg |= ADM0_PWDWNB;
+	else
+		reg &= ~ADM0_PWDWNB;
+	rzg2l_adc_writel(adc, ADM(0), reg);
+	udelay(2);
+}
+
+static void rzg2l_adc_conversion(struct rzg2l_adc *adc, bool start)
+{
+	int timeout = 5;
+	u32 reg;
+
+	/* stop A/D conversion */
+	reg = rzg2l_adc_readl(adc, ADM(0));
+	if (start)
+		reg |= ADM0_ADCE;
+	else
+		reg &= ~ADM0_ADCE;
+	rzg2l_adc_writel(adc, ADM(0), reg);
+
+	if (start)
+		return;
+
+	do {
+		usleep_range(100, 200);
+		reg = rzg2l_adc_readl(adc, ADM(0));
+		timeout--;
+		if (!timeout) {
+			pr_err("%s stopping ADC timed out\n", __func__);
+			break;
+		}
+	} while (((reg & ADM0_ADBSY) || (reg & ADM0_ADCE)));
+}
+
+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);
+	u32 reg;
+	int ret;
+	u8 ch;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+
+		if (adc->adc_disabled) {
+			mutex_unlock(&indio_dev->mlock);
+			return -EBUSY;
+		}
+
+		if (rzg2l_adc_readl(adc, ADM(0)) & ADM0_ADBSY) {
+			mutex_unlock(&indio_dev->mlock);
+			return -EBUSY;
+		}
+
+		ch = chan->channel & ADC_CHN_MASK;
+		/* SW trigger */
+		reg = rzg2l_adc_readl(adc, ADM(1));
+		reg &= ADM1_EGA_CLEAR;
+		reg &= ~ADM1_BS;
+		reg |= ADM1_MS;
+		reg &= ~ADM1_TRG;
+		rzg2l_adc_writel(adc, ADM(1), reg);
+
+		/* select channel */
+		reg = rzg2l_adc_readl(adc, ADM(2));
+		reg &= ADM2_CHSEL_CLEAR;
+		reg |= BIT(ch);
+		rzg2l_adc_writel(adc, ADM(2), reg);
+
+		reg = rzg2l_adc_readl(adc, ADM(3));
+		reg &= ADM3_ADIL_CLEAR;
+		reg |= ADM3_ADCMP;
+		reg |= ADM3_ADSMP;
+		rzg2l_adc_writel(adc, ADM(3), reg);
+
+		reg = rzg2l_adc_readl(adc, ADIVC);
+		reg &= ADIVC_DIVADC_CLEAR;
+		reg |= ADIVC_DIVADC_4;
+		rzg2l_adc_writel(adc, ADIVC, reg);
+
+		reg = rzg2l_adc_readl(adc, ADINT);
+		reg &= ~ADINT_INTS;
+		reg &= ADINT_CH_CLEAR;
+		reg |= ADINT_CSEEN;
+		reg |= BIT(ch);
+		rzg2l_adc_writel(adc, ADINT, reg);
+
+		rzg2l_adc_pwr(adc, true);
+
+		ret = rzg2l_adc_adclk(adc, true);
+		if (ret) {
+			rzg2l_adc_pwr(adc, false);
+			mutex_unlock(&indio_dev->mlock);
+			return -EINVAL;
+		}
+
+		reinit_completion(&adc->completion);
+
+		rzg2l_adc_conversion(adc, true);
+
+		if (!wait_for_completion_timeout(&adc->completion, ADC_TIMEOUT)) {
+			reg &= ADINT_CH_CLEAR;
+			rzg2l_adc_writel(adc, ADINT, reg);
+			rzg2l_adc_conversion(adc, false);
+			rzg2l_adc_adclk(adc, false);
+			rzg2l_adc_pwr(adc, false);
+			mutex_unlock(&indio_dev->mlock);
+			return -ETIMEDOUT;
+		}
+
+		*val = adc->last_val[ch];
+		rzg2l_adc_conversion(adc, false);
+		rzg2l_adc_adclk(adc, false);
+		rzg2l_adc_pwr(adc, false);
+		mutex_unlock(&indio_dev->mlock);
+		return IIO_VAL_INT;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
+{
+	struct rzg2l_adc *adc = (struct rzg2l_adc *)dev_id;
+	u8 intst;
+	u32 reg;
+	u8 i;
+
+	reg = rzg2l_adc_readl(adc, ADSTS);
+	if (reg & ADSTS_CSEST) {
+		rzg2l_adc_writel(adc, ADSTS, reg);
+		return IRQ_HANDLED;
+	}
+
+	intst = reg & ADINT_INTST_MASK;
+	if (!intst)
+		return IRQ_HANDLED;
+
+	for (i = 0; i < ADC_MAX_CHANNELS; i++) {
+		if (intst & BIT(i))
+			adc->last_val[i] = rzg2l_adc_readl(adc, ADCR(i)) & ADCR_AD_MASK;
+	}
+
+	rzg2l_adc_writel(adc, ADSTS, reg);
+
+	complete(&adc->completion);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_info rzg2l_adc_iio_info = {
+	.read_raw = rzg2l_adc_read_raw,
+};
+
+static const char * const rzg2l_adc_channel_name[] = {
+	"adc0",
+	"adc1",
+	"adc2",
+	"adc3",
+	"adc4",
+	"adc5",
+	"adc6",
+	"adc7",
+};
+
+static int rzg2l_adc_parse_of(struct platform_device *pdev, struct rzg2l_adc *adc)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct iio_chan_spec *chan_array;
+	u8 channels[ADC_MAX_CHANNELS];
+	struct rzg2l_adc_data *data;
+	int num_channels;
+	int ret;
+	u8 i;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	num_channels = of_property_count_u8_elems(node, "renesas-rzg2l,adc-channels");
+	if (num_channels <= 0 || num_channels > ADC_MAX_CHANNELS)
+		return -EINVAL;
+
+	ret = of_property_read_u8_array(node, "renesas-rzg2l,adc-channels",
+					channels, num_channels);
+	if (ret)
+		return ret;
+
+	chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array),
+				  GFP_KERNEL);
+	if (!chan_array)
+		return -ENOMEM;
+
+	for (i = 0; i < num_channels; i++) {
+		chan_array[i].type = IIO_VOLTAGE;
+		chan_array[i].indexed = 1;
+		chan_array[i].channel = channels[i];
+		chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+		chan_array[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+		chan_array[i].datasheet_name = rzg2l_adc_channel_name[i];
+	}
+
+	ret = of_property_read_u8(node, "renesas-rzg2l,adc-trigger-mode",
+				  &data->trigger);
+	if (ret)
+		data->trigger = SW_TRIGGER;
+
+	/* we support SW_TRIGGER as of now */
+	if (data->trigger != SW_TRIGGER)
+		return -EINVAL;
+
+	data->num_channels = num_channels;
+	data->channels = chan_array;
+	adc->data = data;
+
+	return 0;
+}
+
+static int rzg2l_adc_sw_reset(struct rzg2l_adc *adc)
+{
+	int timeout = 5;
+	u32 val;
+
+	val = rzg2l_adc_readl(adc, ADM(0));
+	val |= ADM0_SRESB;
+	rzg2l_adc_writel(adc, ADM(0), val);
+
+	while (!(rzg2l_adc_readl(adc, ADM(0)) & ADM0_SRESB)) {
+		if (!timeout)
+			return -EINVAL;
+		timeout--;
+		usleep_range(100, 200);
+	}
+
+	return 0;
+}
+
+static int rzg2l_adc_probe(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct rzg2l_adc *adc;
+	int ret;
+	int irq;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+	if (!indio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	adc = iio_priv(indio_dev);
+	if (!adc)
+		return -ENOMEM;
+
+	ret = rzg2l_adc_parse_of(pdev, adc);
+	if (ret)
+		return -ENOMEM;
+
+	adc->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(adc->base)) {
+		dev_err(&pdev->dev, "missing mem resource");
+		return PTR_ERR(adc->base);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq resource\n");
+		return irq;
+	}
+
+	adc->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(adc->pclk)) {
+		dev_err(&pdev->dev, "Failed to get pclk");
+		return PTR_ERR(adc->pclk);
+	}
+
+	adc->adclk = devm_clk_get(&pdev->dev, "adclk");
+	if (IS_ERR(adc->adclk)) {
+		dev_err(&pdev->dev, "Failed to get adclk");
+		return PTR_ERR(adc->adclk);
+	}
+
+	adc->adrstn = devm_reset_control_get_exclusive(&pdev->dev, "adrst-n");
+	if (IS_ERR(adc->adrstn)) {
+		dev_err(&pdev->dev, "failed to get adrstn\n");
+		return PTR_ERR(adc->adrstn);
+	}
+
+	adc->presetn = devm_reset_control_get_exclusive(&pdev->dev, "presetn");
+	if (IS_ERR(adc->presetn)) {
+		dev_err(&pdev->dev, "failed to get presetn\n");
+		return PTR_ERR(adc->presetn);
+	}
+
+	ret = reset_control_deassert(adc->adrstn);
+	if (ret)
+		return ret;
+
+	ret = reset_control_deassert(adc->presetn);
+	if (ret)
+		goto assert_adrstn;
+
+	ret = clk_prepare_enable(adc->pclk);
+	if (ret)
+		goto assert_presetn;
+
+	ret = rzg2l_adc_sw_reset(adc);
+	if (ret)
+		goto unprepare_pclk;
+
+	init_completion(&adc->completion);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = devm_request_irq(&pdev->dev, irq, rzg2l_adc_isr,
+			       0, dev_name(&pdev->dev), adc);
+	if (ret < 0)
+		goto unprepare_pclk;
+
+	adc->adc_disabled = false;
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	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;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto unprepare_pclk;
+
+	return 0;
+
+unprepare_pclk:
+	clk_disable_unprepare(adc->pclk);
+assert_presetn:
+	reset_control_assert(adc->presetn);
+assert_adrstn:
+	reset_control_assert(adc->adrstn);
+	return ret;
+}
+
+static int rzg2l_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct rzg2l_adc *adc = iio_priv(indio_dev);
+
+	mutex_lock(&indio_dev->mlock);
+	adc->adc_disabled = true;
+	mutex_unlock(&indio_dev->mlock);
+
+	iio_device_unregister(indio_dev);
+
+	clk_disable_unprepare(adc->pclk);
+	reset_control_assert(adc->presetn);
+	reset_control_assert(adc->adrstn);
+
+	return 0;
+}
+
+static const struct of_device_id rzg2l_adc_match[] = {
+	{
+		.compatible = "renesas,rzg2l-adc",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, rzg2l_adc_match);
+
+static struct platform_driver rzg2l_adc_driver = {
+	.probe		= rzg2l_adc_probe,
+	.remove		= rzg2l_adc_remove,
+	.driver		= {
+		.name	= "rzg2l-adc",
+		.of_match_table = rzg2l_adc_match,
+	},
+};
+
+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");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] iio: adc: Add driver for Renesas RZ/G2L A/D converter
  2021-06-29 22:03 ` [PATCH 2/2] iio: adc: Add driver " Lad Prabhakar
@ 2021-07-01 10:28   ` Alexandru Ardelean
  2021-07-03 17:41     ` Jonathan Cameron
  2021-07-14 21:25     ` Lad, Prabhakar
  0 siblings, 2 replies; 19+ messages in thread
From: Alexandru Ardelean @ 2021-07-01 10:28 UTC (permalink / raw)
  To: Lad Prabhakar
  Cc: Geert Uytterhoeven, Rob Herring, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel, linux-iio, devicetree, LKML,
	linux-renesas-soc, Prabhakar, Biju Das

On Wed, Jun 30, 2021 at 1:07 AM Lad Prabhakar
<prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
>
> Add ADC driver support for Renesas RZ/G2L A/D converter in SW
> trigger mode.
>
> A/D Converter block is a successive approximation analog-to-digital
> converter with a 12-bit accuracy and supports a maximum of 8 input
> channels.

Hey,

Some comments inline.

>
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
>  MAINTAINERS                 |   8 +
>  drivers/iio/adc/Kconfig     |  10 +
>  drivers/iio/adc/Makefile    |   1 +
>  drivers/iio/adc/rzg2l_adc.c | 489 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 508 insertions(+)
>  create mode 100644 drivers/iio/adc/rzg2l_adc.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 81e1edeceae4..bee4c3847e01 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15622,6 +15622,14 @@ L:     linux-renesas-soc@vger.kernel.org
>  S:     Maintained
>  F:     drivers/phy/renesas/phy-rcar-gen3-usb*.c
>
> +RENESAS RZ/G2L A/D DRIVER
> +M:     Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> +L:     linux-iio@vger.kernel.org
> +L:     linux-renesas-soc@vger.kernel.org
> +S:     Supported
> +F:     Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> +F:     drivers/iio/adc/rzg2l_adc.c
> +
>  RESET CONTROLLER FRAMEWORK
>  M:     Philipp Zabel <p.zabel@pengutronix.de>
>  S:     Maintained
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index c7946c439612..9408cbf97acc 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 a226657d19c0..d92bcc9c5fbb 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/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
> new file mode 100644
> index 000000000000..1c58eb8ae1ec
> --- /dev/null
> +++ b/drivers/iio/adc/rzg2l_adc.c
> @@ -0,0 +1,489 @@
> +// 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/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/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#define ADM(n)                 ((n) * 0x4)
> +#define ADM0_ADCE              BIT(0)
> +#define ADM0_ADBSY             BIT(1)
> +#define ADM0_PWDWNB            BIT(2)
> +#define ADM0_SRESB             BIT(15)
> +#define ADM1_TRG               BIT(0)
> +#define ADM1_MS                        BIT(2)
> +#define ADM1_BS                        BIT(4)
> +#define ADM1_EGA_CLEAR         ~GENMASK(13, 12)
> +#define ADM2_CHSEL_CLEAR       ~GENMASK(7, 0)
> +#define ADM3_ADSMP             0x578
> +#define ADM3_ADCMP             (0xe << 16)
> +#define ADM3_ADIL_CLEAR                ~GENMASK(31, 24)
> +
> +#define ADINT                  0x20
> +#define ADINT_CH_CLEAR         ~GENMASK(7, 0)
> +#define ADINT_CSEEN            BIT(16)
> +#define ADINT_INTS             BIT(31)
> +#define ADSTS                  0x24
> +#define ADINT_INTST_MASK       GENMASK(7, 0)
> +#define ADSTS_CSEST            BIT(16)
> +#define ADIVC                  0x28
> +#define ADIVC_DIVADC_CLEAR     ~GENMASK(8, 0)
> +#define ADIVC_DIVADC_4         0x4
> +#define ADFIL                  0x2c
> +#define ADCR(n)                        (0x30 + ((n) * 0x4))
> +#define ADCR_AD_MASK           GENMASK(11, 0)
> +
> +#define ADC_MAX_CHANNELS       8
> +#define ADC_CHN_MASK           0x7
> +#define ADC_TIMEOUT            usecs_to_jiffies(1 * 4)
> +
> +enum trigger_mode {
> +       SW_TRIGGER = 0,
> +       SYNC_TRIGGER,
> +       ASYNC_TRIGGER,
> +};

this enum could also be removed [for now] given that only SW_TRIGGER
is supported;

> +
> +struct rzg2l_adc_data {
> +       const struct iio_chan_spec *channels;
> +       u8 num_channels;
> +       u8 trigger;
> +};
> +
> +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;
> +       bool adc_disabled; /* protected with mlock mutex from indio_dev */

this adc_disabled flag looks a bit weird;
it seems to guard against this driver being removed to prevent some reads.
technically, this should be protected by IIO core;
so the flag itself (or how it is being used) looks like it doesn't do much;

> +       u16 last_val[ADC_MAX_CHANNELS];
> +};
> +
> +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 int rzg2l_adc_adclk(struct rzg2l_adc *adc, bool prepare)
> +{
> +       if (prepare)
> +               return clk_prepare_enable(adc->adclk);
> +
> +       clk_disable_unprepare(adc->adclk);
> +       return 0;
> +}
> +
> +static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on)
> +{
> +       u32 reg;
> +
> +       reg = rzg2l_adc_readl(adc, ADM(0));
> +       if (on)
> +               reg |= ADM0_PWDWNB;
> +       else
> +               reg &= ~ADM0_PWDWNB;
> +       rzg2l_adc_writel(adc, ADM(0), reg);
> +       udelay(2);
> +}
> +
> +static void rzg2l_adc_conversion(struct rzg2l_adc *adc, bool start)
> +{
> +       int timeout = 5;
> +       u32 reg;
> +
> +       /* stop A/D conversion */
> +       reg = rzg2l_adc_readl(adc, ADM(0));
> +       if (start)
> +               reg |= ADM0_ADCE;
> +       else
> +               reg &= ~ADM0_ADCE;
> +       rzg2l_adc_writel(adc, ADM(0), reg);
> +
> +       if (start)
> +               return;
> +
> +       do {
> +               usleep_range(100, 200);
> +               reg = rzg2l_adc_readl(adc, ADM(0));
> +               timeout--;
> +               if (!timeout) {
> +                       pr_err("%s stopping ADC timed out\n", __func__);
> +                       break;
> +               }
> +       } while (((reg & ADM0_ADBSY) || (reg & ADM0_ADCE)));
> +}
> +
> +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);
> +       u32 reg;
> +       int ret;
> +       u8 ch;
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               mutex_lock(&indio_dev->mlock);

[1]
acquiring indio_dev->mlock directly is discouraged;
this lock is reserved for IIO core logic and will be moved into an
iio_dev_opaque struct eventually;
driver state locks should be defined in struct rzg2l_adc and managed
by the driver to protect it's own internal state;


> +
> +               if (adc->adc_disabled) {
> +                       mutex_unlock(&indio_dev->mlock);
> +                       return -EBUSY;
> +               }
> +
> +               if (rzg2l_adc_readl(adc, ADM(0)) & ADM0_ADBSY) {
> +                       mutex_unlock(&indio_dev->mlock);
> +                       return -EBUSY;
> +               }
> +
> +               ch = chan->channel & ADC_CHN_MASK;
> +               /* SW trigger */
> +               reg = rzg2l_adc_readl(adc, ADM(1));
> +               reg &= ADM1_EGA_CLEAR;
> +               reg &= ~ADM1_BS;
> +               reg |= ADM1_MS;
> +               reg &= ~ADM1_TRG;
> +               rzg2l_adc_writel(adc, ADM(1), reg);
> +
> +               /* select channel */
> +               reg = rzg2l_adc_readl(adc, ADM(2));
> +               reg &= ADM2_CHSEL_CLEAR;
> +               reg |= BIT(ch);
> +               rzg2l_adc_writel(adc, ADM(2), reg);
> +
> +               reg = rzg2l_adc_readl(adc, ADM(3));
> +               reg &= ADM3_ADIL_CLEAR;
> +               reg |= ADM3_ADCMP;
> +               reg |= ADM3_ADSMP;
> +               rzg2l_adc_writel(adc, ADM(3), reg);
> +
> +               reg = rzg2l_adc_readl(adc, ADIVC);
> +               reg &= ADIVC_DIVADC_CLEAR;
> +               reg |= ADIVC_DIVADC_4;
> +               rzg2l_adc_writel(adc, ADIVC, reg);
> +
> +               reg = rzg2l_adc_readl(adc, ADINT);
> +               reg &= ~ADINT_INTS;
> +               reg &= ADINT_CH_CLEAR;
> +               reg |= ADINT_CSEEN;
> +               reg |= BIT(ch);
> +               rzg2l_adc_writel(adc, ADINT, reg);
> +
> +               rzg2l_adc_pwr(adc, true);

should all this clock & power management be done in this read function?
it looks like an awful lot just to perform a single read
maybe some PM suspend/resume hooks would be a better idea for these;

> +
> +               ret = rzg2l_adc_adclk(adc, true);
> +               if (ret) {
> +                       rzg2l_adc_pwr(adc, false);
> +                       mutex_unlock(&indio_dev->mlock);
> +                       return -EINVAL;
> +               }
> +
> +               reinit_completion(&adc->completion);
> +
> +               rzg2l_adc_conversion(adc, true);
> +
> +               if (!wait_for_completion_timeout(&adc->completion, ADC_TIMEOUT)) {
> +                       reg &= ADINT_CH_CLEAR;
> +                       rzg2l_adc_writel(adc, ADINT, reg);
> +                       rzg2l_adc_conversion(adc, false);
> +                       rzg2l_adc_adclk(adc, false);
> +                       rzg2l_adc_pwr(adc, false);
> +                       mutex_unlock(&indio_dev->mlock);
> +                       return -ETIMEDOUT;
> +               }
> +
> +               *val = adc->last_val[ch];
> +               rzg2l_adc_conversion(adc, false);
> +               rzg2l_adc_adclk(adc, false);
> +               rzg2l_adc_pwr(adc, false);
> +               mutex_unlock(&indio_dev->mlock);
> +               return IIO_VAL_INT;
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
> +{
> +       struct rzg2l_adc *adc = (struct rzg2l_adc *)dev_id;
> +       u8 intst;
> +       u32 reg;
> +       u8 i;
> +
> +       reg = rzg2l_adc_readl(adc, ADSTS);
> +       if (reg & ADSTS_CSEST) {
> +               rzg2l_adc_writel(adc, ADSTS, reg);
> +               return IRQ_HANDLED;
> +       }
> +
> +       intst = reg & ADINT_INTST_MASK;
> +       if (!intst)
> +               return IRQ_HANDLED;
> +
> +       for (i = 0; i < ADC_MAX_CHANNELS; i++) {
> +               if (intst & BIT(i))
> +                       adc->last_val[i] = rzg2l_adc_readl(adc, ADCR(i)) & ADCR_AD_MASK;
> +       }
> +
> +       rzg2l_adc_writel(adc, ADSTS, reg);
> +
> +       complete(&adc->completion);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static const struct iio_info rzg2l_adc_iio_info = {
> +       .read_raw = rzg2l_adc_read_raw,
> +};
> +
> +static const char * const rzg2l_adc_channel_name[] = {
> +       "adc0",
> +       "adc1",
> +       "adc2",
> +       "adc3",
> +       "adc4",
> +       "adc5",
> +       "adc6",
> +       "adc7",
> +};
> +
> +static int rzg2l_adc_parse_of(struct platform_device *pdev, struct rzg2l_adc *adc)
> +{
> +       struct device_node *node = pdev->dev.of_node;
> +       struct iio_chan_spec *chan_array;
> +       u8 channels[ADC_MAX_CHANNELS];
> +       struct rzg2l_adc_data *data;
> +       int num_channels;
> +       int ret;
> +       u8 i;
> +
> +       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +
> +       num_channels = of_property_count_u8_elems(node, "renesas-rzg2l,adc-channels");
> +       if (num_channels <= 0 || num_channels > ADC_MAX_CHANNELS)
> +               return -EINVAL;
> +
> +       ret = of_property_read_u8_array(node, "renesas-rzg2l,adc-channels",
> +                                       channels, num_channels);
> +       if (ret)
> +               return ret;
> +
> +       chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array),
> +                                 GFP_KERNEL);
> +       if (!chan_array)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < num_channels; i++) {
> +               chan_array[i].type = IIO_VOLTAGE;
> +               chan_array[i].indexed = 1;
> +               chan_array[i].channel = channels[i];
> +               chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> +               chan_array[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
> +               chan_array[i].datasheet_name = rzg2l_adc_channel_name[i];
> +       }
> +
> +       ret = of_property_read_u8(node, "renesas-rzg2l,adc-trigger-mode",
> +                                 &data->trigger);
> +       if (ret)
> +               data->trigger = SW_TRIGGER;
> +
> +       /* we support SW_TRIGGER as of now */
> +       if (data->trigger != SW_TRIGGER)
> +               return -EINVAL;

it would be an idea to remove this data->trigger field and the DT read
for this property and add it when it's supported;
typically these triggers don't get configured via DT;

> +
> +       data->num_channels = num_channels;
> +       data->channels = chan_array;
> +       adc->data = data;
> +
> +       return 0;
> +}
> +
> +static int rzg2l_adc_sw_reset(struct rzg2l_adc *adc)
> +{
> +       int timeout = 5;
> +       u32 val;
> +
> +       val = rzg2l_adc_readl(adc, ADM(0));
> +       val |= ADM0_SRESB;
> +       rzg2l_adc_writel(adc, ADM(0), val);
> +
> +       while (!(rzg2l_adc_readl(adc, ADM(0)) & ADM0_SRESB)) {
> +               if (!timeout)
> +                       return -EINVAL;

maybe -EBUSY is a bit better error code;

> +               timeout--;
> +               usleep_range(100, 200);
> +       }
> +
> +       return 0;
> +}
> +
> +static int rzg2l_adc_probe(struct platform_device *pdev)
> +{
> +       struct iio_dev *indio_dev;
> +       struct rzg2l_adc *adc;
> +       int ret;
> +       int irq;
> +
> +       indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
> +       if (!indio_dev) {
> +               dev_err(&pdev->dev, "failed allocating iio device\n");

this message can be removed;
looks like log spam;
and if it happens, the system will be in a pretty bad state anyway

> +               return -ENOMEM;
> +       }
> +
> +       adc = iio_priv(indio_dev);
> +       if (!adc)
> +               return -ENOMEM;

this check is redundant;
if indio_dev is non-NULL then iio_priv() will be good as well;

> +
> +       ret = rzg2l_adc_parse_of(pdev, adc);
> +       if (ret)
> +               return -ENOMEM;
> +
> +       adc->base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(adc->base)) {
> +               dev_err(&pdev->dev, "missing mem resource");

this message can be removed;
looks like log-spam

> +               return PTR_ERR(adc->base);
> +       }
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(&pdev->dev, "no irq resource\n");
> +               return irq;
> +       }
> +
> +       adc->pclk = devm_clk_get(&pdev->dev, "pclk");
> +       if (IS_ERR(adc->pclk)) {
> +               dev_err(&pdev->dev, "Failed to get pclk");
> +               return PTR_ERR(adc->pclk);
> +       }
> +
> +       adc->adclk = devm_clk_get(&pdev->dev, "adclk");
> +       if (IS_ERR(adc->adclk)) {
> +               dev_err(&pdev->dev, "Failed to get adclk");
> +               return PTR_ERR(adc->adclk);
> +       }
> +
> +       adc->adrstn = devm_reset_control_get_exclusive(&pdev->dev, "adrst-n");
> +       if (IS_ERR(adc->adrstn)) {
> +               dev_err(&pdev->dev, "failed to get adrstn\n");
> +               return PTR_ERR(adc->adrstn);
> +       }
> +
> +       adc->presetn = devm_reset_control_get_exclusive(&pdev->dev, "presetn");
> +       if (IS_ERR(adc->presetn)) {
> +               dev_err(&pdev->dev, "failed to get presetn\n");
> +               return PTR_ERR(adc->presetn);
> +       }
> +
> +       ret = reset_control_deassert(adc->adrstn);
> +       if (ret)
> +               return ret;
> +
> +       ret = reset_control_deassert(adc->presetn);
> +       if (ret)
> +               goto assert_adrstn;
> +
> +       ret = clk_prepare_enable(adc->pclk);
> +       if (ret)
> +               goto assert_presetn;
> +
> +       ret = rzg2l_adc_sw_reset(adc);
> +       if (ret)
> +               goto unprepare_pclk;
> +
> +       init_completion(&adc->completion);
> +
> +       platform_set_drvdata(pdev, indio_dev);
> +
> +       ret = devm_request_irq(&pdev->dev, irq, rzg2l_adc_isr,
> +                              0, dev_name(&pdev->dev), adc);
> +       if (ret < 0)
> +               goto unprepare_pclk;
> +
> +       adc->adc_disabled = false;
> +       indio_dev->name = dev_name(&pdev->dev);

indio_dev->name  should be the part-name;
since this driver supports a single part, this can be:

  indio_dev->name = "rzg2l-adc";

> +       indio_dev->dev.parent = &pdev->dev;
> +       indio_dev->dev.of_node = pdev->dev.of_node;


The 2 assignments above can be removed in the mainline driver.
They should be done in devm_iio_device_alloc() and iio_device_register()


> +       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;
> +
> +       ret = iio_device_register(indio_dev);
> +       if (ret)
> +               goto unprepare_pclk;
> +
> +       return 0;
> +
> +unprepare_pclk:
> +       clk_disable_unprepare(adc->pclk);
> +assert_presetn:
> +       reset_control_assert(adc->presetn);
> +assert_adrstn:
> +       reset_control_assert(adc->adrstn);
> +       return ret;
> +}
> +
> +static int rzg2l_adc_remove(struct platform_device *pdev)
> +{
> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +       struct rzg2l_adc *adc = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       adc->adc_disabled = true;
> +       mutex_unlock(&indio_dev->mlock);
> +
> +       iio_device_unregister(indio_dev);
> +
> +       clk_disable_unprepare(adc->pclk);
> +       reset_control_assert(adc->presetn);
> +       reset_control_assert(adc->adrstn);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id rzg2l_adc_match[] = {
> +       {
> +               .compatible = "renesas,rzg2l-adc",
> +       },
> +       {},

comma can be removed;
since this is a null terminator

> +};
> +MODULE_DEVICE_TABLE(of, rzg2l_adc_match);
> +
> +static struct platform_driver rzg2l_adc_driver = {
> +       .probe          = rzg2l_adc_probe,
> +       .remove         = rzg2l_adc_remove,
> +       .driver         = {
> +               .name   = "rzg2l-adc",
> +               .of_match_table = rzg2l_adc_match,
> +       },
> +};
> +
> +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");
> --
> 2.17.1
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-06-29 22:03 ` [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter Lad Prabhakar
@ 2021-07-01 14:02   ` Rob Herring
  2021-07-01 20:21   ` Rob Herring
  2021-07-03 17:19   ` Jonathan Cameron
  2 siblings, 0 replies; 19+ messages in thread
From: Rob Herring @ 2021-07-01 14:02 UTC (permalink / raw)
  To: Lad Prabhakar
  Cc: Philipp Zabel, Geert Uytterhoeven, Jonathan Cameron,
	Lars-Peter Clausen, linux-iio, linux-kernel, Rob Herring,
	Biju Das, Prabhakar, linux-renesas-soc, devicetree

On Tue, 29 Jun 2021 23:03:27 +0100, Lad Prabhakar wrote:
> Add binding documentation for Renesas RZ/G2L A/D converter block.
> 
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
>  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
>  1 file changed, 121 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.example.dts:19:18: fatal error: dt-bindings/clock/r9a07g044-cpg.h: No such file or directory
   19 |         #include <dt-bindings/clock/r9a07g044-cpg.h>
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[1]: *** [scripts/Makefile.lib:380: Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.example.dt.yaml] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1416: dt_binding_check] Error 2
\ndoc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1498675

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-06-29 22:03 ` [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter Lad Prabhakar
  2021-07-01 14:02   ` Rob Herring
@ 2021-07-01 20:21   ` Rob Herring
  2021-07-13 16:00     ` Lad, Prabhakar
  2021-07-03 17:19   ` Jonathan Cameron
  2 siblings, 1 reply; 19+ messages in thread
From: Rob Herring @ 2021-07-01 20:21 UTC (permalink / raw)
  To: Lad Prabhakar
  Cc: Geert Uytterhoeven, Jonathan Cameron, Lars-Peter Clausen,
	Philipp Zabel, linux-iio, devicetree, linux-kernel,
	linux-renesas-soc, Prabhakar, Biju Das

On Tue, Jun 29, 2021 at 11:03:27PM +0100, Lad Prabhakar wrote:
> Add binding documentation for Renesas RZ/G2L A/D converter block.
> 
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> ---
>  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
>  1 file changed, 121 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> new file mode 100644
> index 000000000000..db935d6d59eb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> @@ -0,0 +1,121 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Renesas RZ/G2L ADC
> +
> +maintainers:
> +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> +
> +description: |
> +  A/D Converter block is a successive approximation analog-to-digital converter
> +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> +  Conversions can be performed in single or repeat mode. Result of the ADC is
> +  stored in a 32-bit data register corresponding to each channel.
> +
> +properties:
> +  compatible:
> +    oneOf:

You can drop oneOf here.

> +      - items:
> +          - enum:
> +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> +          - const: renesas,rzg2l-adc
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    items:
> +      - description: converter clock
> +      - description: peripheral clock
> +
> +  clock-names:
> +    items:
> +      - const: adclk
> +      - const: pclk
> +
> +  power-domains:
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 2
> +
> +  reset-names:
> +    items:
> +      - const: presetn
> +      - const: adrst-n
> +
> +  renesas-rzg2l,adc-trigger-mode:
> +    $ref: /schemas/types.yaml#/definitions/uint8
> +    description: Trigger mode for A/D converter
> +    enum:
> +      - 0 # Software trigger mode (Defaults)
> +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)
> +    default: 0
> +
> +  gpios:

A named gpio is preferred. trigger-gpios?

> +    description:
> +      ADC_TRG trigger input pin
> +    maxItems: 1
> +
> +  renesas-rzg2l,adc-channels:
> +    $ref: /schemas/types.yaml#/definitions/uint8-array
> +    description: Input channels available on platform
> +    uniqueItems: true
> +    minItems: 1
> +    maxItems: 8
> +    items:
> +      enum: [0, 1, 2, 3, 4, 5, 6, 7]
> +
> +  "#io-channel-cells":
> +    const: 1
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - power-domains
> +  - resets
> +  - reset-names
> +  - renesas-rzg2l,adc-channels
> +  - "#io-channel-cells"
> +
> +allOf:
> +  - if:
> +      properties:
> +        renesas-rzg2l,adc-trigger-mode:
> +          const: 1
> +    then:
> +      required:
> +        - gpios
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/r9a07g044-cpg.h>
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    adc: adc@10059000 {
> +      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
> +      reg = <0x10059000 0x400>;
> +      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
> +      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
> +               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
> +      clock-names = "adclk", "pclk";
> +      power-domains = <&cpg>;
> +      resets = <&cpg R9A07G044_ADC_PRESETN>,
> +               <&cpg R9A07G044_ADC_ADRST_N>;
> +      reset-names = "presetn", "adrst-n";
> +      #io-channel-cells = <1>;
> +      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
> +      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
> +    };
> -- 
> 2.17.1
> 
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-06-29 22:03 ` [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter Lad Prabhakar
  2021-07-01 14:02   ` Rob Herring
  2021-07-01 20:21   ` Rob Herring
@ 2021-07-03 17:19   ` Jonathan Cameron
  2021-07-14  9:11     ` Lad, Prabhakar
  2 siblings, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2021-07-03 17:19 UTC (permalink / raw)
  To: Lad Prabhakar
  Cc: Geert Uytterhoeven, Rob Herring, Lars-Peter Clausen,
	Philipp Zabel, linux-iio, devicetree, linux-kernel,
	linux-renesas-soc, Prabhakar, Biju Das

On Tue, 29 Jun 2021 23:03:27 +0100
Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:

> Add binding documentation for Renesas RZ/G2L A/D converter block.
> 
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
Hi,

See inline

Jonathan

> ---
>  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
>  1 file changed, 121 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> new file mode 100644
> index 000000000000..db935d6d59eb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> @@ -0,0 +1,121 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Renesas RZ/G2L ADC
> +
> +maintainers:
> +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> +
> +description: |
> +  A/D Converter block is a successive approximation analog-to-digital converter
> +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> +  Conversions can be performed in single or repeat mode. Result of the ADC is
> +  stored in a 32-bit data register corresponding to each channel.
> +
> +properties:
> +  compatible:
> +    oneOf:
> +      - items:
> +          - enum:
> +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> +          - const: renesas,rzg2l-adc
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    items:
> +      - description: converter clock
> +      - description: peripheral clock
> +
> +  clock-names:
> +    items:
> +      - const: adclk
> +      - const: pclk
> +
> +  power-domains:
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 2
> +
> +  reset-names:
> +    items:
> +      - const: presetn
> +      - const: adrst-n
> +
> +  renesas-rzg2l,adc-trigger-mode:
> +    $ref: /schemas/types.yaml#/definitions/uint8
> +    description: Trigger mode for A/D converter
> +    enum:
> +      - 0 # Software trigger mode (Defaults)
> +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)

Is this a function of the board in some fashion?  If not it sounds like
something that should be in control of userspace.  Normally we'd
do that by having the driver register some iio_triggers and depending
on which one is selected do the equivalent of what you have here.

> +    default: 0
> +
> +  gpios:
> +    description:
> +      ADC_TRG trigger input pin
> +    maxItems: 1
Why is this mode useful?  I'm assuming the gpio write would take a register
write and the software trigger mode also requires a register write.

Normally the reason for a pin like this is to support synchronising with
external hardware.   If that's the case, we should call that out here.
often the pin isn't even connected to a gpio in our control.
(i.e. it's a trigger signal from some other device.)

> +
> +  renesas-rzg2l,adc-channels:
> +    $ref: /schemas/types.yaml#/definitions/uint8-array
> +    description: Input channels available on platform
> +    uniqueItems: true
> +    minItems: 1
> +    maxItems: 8
> +    items:
> +      enum: [0, 1, 2, 3, 4, 5, 6, 7]

Is this a function of different devices (should have different compatibles)
or of what is wired up.  If it's what is wired up, then how do you know which
subset of channels are connected?  We have the generic adc channel binding
in iio/adc/adc.yaml for the case where we only want to expose those channels
that are wired up.  It uses a node per channel.

> +
> +  "#io-channel-cells":
> +    const: 1
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - clock-names
> +  - power-domains
> +  - resets
> +  - reset-names
> +  - renesas-rzg2l,adc-channels
> +  - "#io-channel-cells"
> +
> +allOf:
> +  - if:
> +      properties:
> +        renesas-rzg2l,adc-trigger-mode:
> +          const: 1
> +    then:
> +      required:
> +        - gpios
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/r9a07g044-cpg.h>
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    adc: adc@10059000 {
> +      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
> +      reg = <0x10059000 0x400>;
> +      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
> +      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
> +               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
> +      clock-names = "adclk", "pclk";
> +      power-domains = <&cpg>;
> +      resets = <&cpg R9A07G044_ADC_PRESETN>,
> +               <&cpg R9A07G044_ADC_ADRST_N>;
> +      reset-names = "presetn", "adrst-n";
> +      #io-channel-cells = <1>;
> +      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
> +      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
> +    };


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 0/2] Renesas RZ/G2L ADC driver support
  2021-06-29 22:03 [PATCH 0/2] Renesas RZ/G2L ADC driver support Lad Prabhakar
  2021-06-29 22:03 ` [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter Lad Prabhakar
  2021-06-29 22:03 ` [PATCH 2/2] iio: adc: Add driver " Lad Prabhakar
@ 2021-07-03 17:21 ` Jonathan Cameron
  2 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2021-07-03 17:21 UTC (permalink / raw)
  To: Lad Prabhakar
  Cc: Geert Uytterhoeven, Rob Herring, Lars-Peter Clausen,
	Philipp Zabel, linux-iio, devicetree, linux-kernel,
	linux-renesas-soc, Prabhakar, Biju Das

On Tue, 29 Jun 2021 23:03:26 +0100
Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:

> Hi All,
> 
> This patch series adds DT binding and driver support for ADC block
> found on Renesas RZ/G2L family.
> 
> Patches are based on top of rzg2l-update-clock-defs-v4 branch found on [1]
> 
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git/

Please aim to call out any dependencies that I need to be aware of.
That tree has lots of random things on it so I have no idea if there is anything
I need to be aware of.

> 
> Cheers,
> Prabhakar
> 
> Lad Prabhakar (2):
>   dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L
>     A/D converter
>   iio: adc: Add driver for Renesas RZ/G2L A/D converter
> 
>  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 +++++
>  MAINTAINERS                                   |   8 +
>  drivers/iio/adc/Kconfig                       |  10 +
>  drivers/iio/adc/Makefile                      |   1 +
>  drivers/iio/adc/rzg2l_adc.c                   | 489 ++++++++++++++++++
>  5 files changed, 629 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
>  create mode 100644 drivers/iio/adc/rzg2l_adc.c
> 
> 
> base-commit: 06c1e6911a7a76b446e4b00fc8bad5d8465932f8


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] iio: adc: Add driver for Renesas RZ/G2L A/D converter
  2021-07-01 10:28   ` Alexandru Ardelean
@ 2021-07-03 17:41     ` Jonathan Cameron
  2021-07-14 21:31       ` Lad, Prabhakar
  2021-07-14 21:25     ` Lad, Prabhakar
  1 sibling, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2021-07-03 17:41 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Lad Prabhakar, Geert Uytterhoeven, Rob Herring,
	Lars-Peter Clausen, Philipp Zabel, linux-iio, devicetree, LKML,
	linux-renesas-soc, Prabhakar, Biju Das

On Thu, 1 Jul 2021 13:28:31 +0300
Alexandru Ardelean <ardeleanalex@gmail.com> wrote:

> On Wed, Jun 30, 2021 at 1:07 AM Lad Prabhakar
> <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
> >
> > Add ADC driver support for Renesas RZ/G2L A/D converter in SW
> > trigger mode.
> >
> > A/D Converter block is a successive approximation analog-to-digital
> > converter with a 12-bit accuracy and supports a maximum of 8 input
> > channels.  
> 
> Hey,
> 
> Some comments inline.

I added a few more on top, but Alex did a good job so it wasn't much!

Jonathan

> 
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > ---
> >  MAINTAINERS                 |   8 +
> >  drivers/iio/adc/Kconfig     |  10 +
> >  drivers/iio/adc/Makefile    |   1 +
> >  drivers/iio/adc/rzg2l_adc.c | 489 ++++++++++++++++++++++++++++++++++++
> >  4 files changed, 508 insertions(+)
> >  create mode 100644 drivers/iio/adc/rzg2l_adc.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 81e1edeceae4..bee4c3847e01 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -15622,6 +15622,14 @@ L:     linux-renesas-soc@vger.kernel.org
> >  S:     Maintained
> >  F:     drivers/phy/renesas/phy-rcar-gen3-usb*.c
> >
> > +RENESAS RZ/G2L A/D DRIVER
> > +M:     Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > +L:     linux-iio@vger.kernel.org
> > +L:     linux-renesas-soc@vger.kernel.org
> > +S:     Supported
> > +F:     Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > +F:     drivers/iio/adc/rzg2l_adc.c
> > +
> >  RESET CONTROLLER FRAMEWORK
> >  M:     Philipp Zabel <p.zabel@pengutronix.de>
> >  S:     Maintained
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index c7946c439612..9408cbf97acc 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 a226657d19c0..d92bcc9c5fbb 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/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
> > new file mode 100644
> > index 000000000000..1c58eb8ae1ec
> > --- /dev/null
> > +++ b/drivers/iio/adc/rzg2l_adc.c
> > @@ -0,0 +1,489 @@
> > +// 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/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/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>

I'd in general prefer use of generic firmware properties rather than
the of variant but I guess you can be fairly sure these devices are always
going to be using devicetree, so it doesn't really matter.

> > +#include <linux/platform_device.h>
> > +#include <linux/reset.h>
> > +
> > +#define ADM(n)                 ((n) * 0x4)

I'd prefer some sort of driver specific prefix on these defines as some
are very generic sounding so it would be good to make it clear they are 
local to this driver.

> > +#define ADM0_ADCE              BIT(0)
> > +#define ADM0_ADBSY             BIT(1)
> > +#define ADM0_PWDWNB            BIT(2)
> > +#define ADM0_SRESB             BIT(15)
> > +#define ADM1_TRG               BIT(0)
> > +#define ADM1_MS                        BIT(2)
> > +#define ADM1_BS                        BIT(4)
> > +#define ADM1_EGA_CLEAR         ~GENMASK(13, 12)
> > +#define ADM2_CHSEL_CLEAR       ~GENMASK(7, 0)
> > +#define ADM3_ADSMP             0x578
> > +#define ADM3_ADCMP             (0xe << 16)
> > +#define ADM3_ADIL_CLEAR                ~GENMASK(31, 24)
> > +
> > +#define ADINT                  0x20
> > +#define ADINT_CH_CLEAR         ~GENMASK(7, 0)
> > +#define ADINT_CSEEN            BIT(16)
> > +#define ADINT_INTS             BIT(31)
> > +#define ADSTS                  0x24
> > +#define ADINT_INTST_MASK       GENMASK(7, 0)
> > +#define ADSTS_CSEST            BIT(16)
> > +#define ADIVC                  0x28
> > +#define ADIVC_DIVADC_CLEAR     ~GENMASK(8, 0)
> > +#define ADIVC_DIVADC_4         0x4
> > +#define ADFIL                  0x2c
> > +#define ADCR(n)                        (0x30 + ((n) * 0x4))
> > +#define ADCR_AD_MASK           GENMASK(11, 0)
> > +
> > +#define ADC_MAX_CHANNELS       8
> > +#define ADC_CHN_MASK           0x7
> > +#define ADC_TIMEOUT            usecs_to_jiffies(1 * 4)
> > +
> > +enum trigger_mode {
> > +       SW_TRIGGER = 0,
> > +       SYNC_TRIGGER,
> > +       ASYNC_TRIGGER,
> > +};  
> 
> this enum could also be removed [for now] given that only SW_TRIGGER
> is supported;
> 
> > +
> > +struct rzg2l_adc_data {
> > +       const struct iio_chan_spec *channels;
> > +       u8 num_channels;
> > +       u8 trigger;
> > +};
> > +
> > +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;
> > +       bool adc_disabled; /* protected with mlock mutex from indio_dev */  
> 
> this adc_disabled flag looks a bit weird;
> it seems to guard against this driver being removed to prevent some reads.
> technically, this should be protected by IIO core;
> so the flag itself (or how it is being used) looks like it doesn't do much;

Agreed. If we have paths we are missing, then please let us know.  There may
well be some as it's been a while since Lars did a bunch of work testing and
fixing issues around remove races.

> 
> > +       u16 last_val[ADC_MAX_CHANNELS];
> > +};
> > +
> > +static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg)
> > +{
> > +       return readl(adc->base + reg);

Hmm. Bit marginal as to whether these are worthwhile. If you really like them
I guess I don't really mind.

> > +}
> > +
> > +static void rzg2l_adc_writel(struct rzg2l_adc *adc, unsigned int reg, u32 val)
> > +{
> > +       writel(val, adc->base + reg);
> > +}
> > +
> > +static int rzg2l_adc_adclk(struct rzg2l_adc *adc, bool prepare)
> > +{
> > +       if (prepare)
> > +               return clk_prepare_enable(adc->adclk);

I'd drop this function and call clk_prepare_enable() / clk_disable_unprepare()
directly inline.   This just makes the code more confusing but implying that
the disable_unprepare can fail for example.

> > +
> > +       clk_disable_unprepare(adc->adclk);
> > +       return 0;
> > +}
> > +
> > +static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on)
> > +{
> > +       u32 reg;
> > +
> > +       reg = rzg2l_adc_readl(adc, ADM(0));
> > +       if (on)
> > +               reg |= ADM0_PWDWNB;
> > +       else
> > +               reg &= ~ADM0_PWDWNB;
> > +       rzg2l_adc_writel(adc, ADM(0), reg);
> > +       udelay(2);
> > +}
> > +
> > +static void rzg2l_adc_conversion(struct rzg2l_adc *adc, bool start)
> > +{
> > +       int timeout = 5;
> > +       u32 reg;
> > +
> > +       /* stop A/D conversion */
> > +       reg = rzg2l_adc_readl(adc, ADM(0));
> > +       if (start)
> > +               reg |= ADM0_ADCE;
> > +       else
> > +               reg &= ~ADM0_ADCE;
> > +       rzg2l_adc_writel(adc, ADM(0), reg);
> > +
> > +       if (start)
> > +               return;
> > +
> > +       do {
> > +               usleep_range(100, 200);
> > +               reg = rzg2l_adc_readl(adc, ADM(0));
> > +               timeout--;
> > +               if (!timeout) {
> > +                       pr_err("%s stopping ADC timed out\n", __func__);
> > +                       break;
> > +               }
> > +       } while (((reg & ADM0_ADBSY) || (reg & ADM0_ADCE)));
> > +}
> > +
> > +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);
> > +       u32 reg;
> > +       int ret;
> > +       u8 ch;
> > +
> > +       switch (mask) {
> > +       case IIO_CHAN_INFO_RAW:
> > +               mutex_lock(&indio_dev->mlock);  
> 
> [1]
> acquiring indio_dev->mlock directly is discouraged;
> this lock is reserved for IIO core logic and will be moved into an
> iio_dev_opaque struct eventually;
> driver state locks should be defined in struct rzg2l_adc and managed
> by the driver to protect it's own internal state;

I'd also be tempted to factor this read switch block out to a separate
function.  That way you can either take the new lock outside of the
function (and then the function can do direct returns in error paths)
or you can use goto err; and unlock there so that you don't have
any risk of forgetting an unlock if this code is refactored in future.

> 
> 
> > +
> > +               if (adc->adc_disabled) {
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -EBUSY;
> > +               }
> > +
> > +               if (rzg2l_adc_readl(adc, ADM(0)) & ADM0_ADBSY) {
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -EBUSY;
> > +               }
> > +
> > +               ch = chan->channel & ADC_CHN_MASK;
> > +               /* SW trigger */
> > +               reg = rzg2l_adc_readl(adc, ADM(1));
> > +               reg &= ADM1_EGA_CLEAR;
> > +               reg &= ~ADM1_BS;
> > +               reg |= ADM1_MS;
> > +               reg &= ~ADM1_TRG;
> > +               rzg2l_adc_writel(adc, ADM(1), reg);
> > +
> > +               /* select channel */
> > +               reg = rzg2l_adc_readl(adc, ADM(2));
> > +               reg &= ADM2_CHSEL_CLEAR;
> > +               reg |= BIT(ch);
> > +               rzg2l_adc_writel(adc, ADM(2), reg);
> > +
> > +               reg = rzg2l_adc_readl(adc, ADM(3));
> > +               reg &= ADM3_ADIL_CLEAR;
> > +               reg |= ADM3_ADCMP;
> > +               reg |= ADM3_ADSMP;
> > +               rzg2l_adc_writel(adc, ADM(3), reg);
> > +
> > +               reg = rzg2l_adc_readl(adc, ADIVC);
> > +               reg &= ADIVC_DIVADC_CLEAR;
> > +               reg |= ADIVC_DIVADC_4;
> > +               rzg2l_adc_writel(adc, ADIVC, reg);
> > +
> > +               reg = rzg2l_adc_readl(adc, ADINT);
> > +               reg &= ~ADINT_INTS;
> > +               reg &= ADINT_CH_CLEAR;
> > +               reg |= ADINT_CSEEN;
> > +               reg |= BIT(ch);
> > +               rzg2l_adc_writel(adc, ADINT, reg);
> > +
> > +               rzg2l_adc_pwr(adc, true);  
> 
> should all this clock & power management be done in this read function?
> it looks like an awful lot just to perform a single read
> maybe some PM suspend/resume hooks would be a better idea for these;
> 
> > +
> > +               ret = rzg2l_adc_adclk(adc, true);
> > +               if (ret) {
> > +                       rzg2l_adc_pwr(adc, false);
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -EINVAL;
> > +               }
> > +
> > +               reinit_completion(&adc->completion);
> > +
> > +               rzg2l_adc_conversion(adc, true);
> > +
> > +               if (!wait_for_completion_timeout(&adc->completion, ADC_TIMEOUT)) {
> > +                       reg &= ADINT_CH_CLEAR;
> > +                       rzg2l_adc_writel(adc, ADINT, reg);
> > +                       rzg2l_adc_conversion(adc, false);
> > +                       rzg2l_adc_adclk(adc, false);
> > +                       rzg2l_adc_pwr(adc, false);
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -ETIMEDOUT;
> > +               }
> > +
> > +               *val = adc->last_val[ch];
> > +               rzg2l_adc_conversion(adc, false);
> > +               rzg2l_adc_adclk(adc, false);
> > +               rzg2l_adc_pwr(adc, false);
> > +               mutex_unlock(&indio_dev->mlock);
> > +               return IIO_VAL_INT;
> > +
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +}
> > +
> > +static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
> > +{
> > +       struct rzg2l_adc *adc = (struct rzg2l_adc *)dev_id;

No need to cast a void * to anything explicitly.  The c spec allows this to
always be done via a simple assignment.

> > +       u8 intst;
> > +       u32 reg;
> > +       u8 i;
> > +
> > +       reg = rzg2l_adc_readl(adc, ADSTS);
> > +       if (reg & ADSTS_CSEST) {
> > +               rzg2l_adc_writel(adc, ADSTS, reg);
> > +               return IRQ_HANDLED;
> > +       }
> > +
> > +       intst = reg & ADINT_INTST_MASK;
> > +       if (!intst)
> > +               return IRQ_HANDLED;
> > +
> > +       for (i = 0; i < ADC_MAX_CHANNELS; i++) {
> > +               if (intst & BIT(i))
> > +                       adc->last_val[i] = rzg2l_adc_readl(adc, ADCR(i)) & ADCR_AD_MASK;
> > +       }
> > +
> > +       rzg2l_adc_writel(adc, ADSTS, reg);
> > +
> > +       complete(&adc->completion);
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static const struct iio_info rzg2l_adc_iio_info = {
> > +       .read_raw = rzg2l_adc_read_raw,
> > +};
> > +
> > +static const char * const rzg2l_adc_channel_name[] = {
> > +       "adc0",
> > +       "adc1",
> > +       "adc2",
> > +       "adc3",
> > +       "adc4",
> > +       "adc5",
> > +       "adc6",
> > +       "adc7",
> > +};
> > +
> > +static int rzg2l_adc_parse_of(struct platform_device *pdev, struct rzg2l_adc *adc)
> > +{
> > +       struct device_node *node = pdev->dev.of_node;
> > +       struct iio_chan_spec *chan_array;
> > +       u8 channels[ADC_MAX_CHANNELS];
> > +       struct rzg2l_adc_data *data;
> > +       int num_channels;
> > +       int ret;
> > +       u8 i;
> > +
> > +       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> > +       if (!data)
> > +               return -ENOMEM;
> > +
> > +       num_channels = of_property_count_u8_elems(node, "renesas-rzg2l,adc-channels");
> > +       if (num_channels <= 0 || num_channels > ADC_MAX_CHANNELS)
> > +               return -EINVAL;
> > +
> > +       ret = of_property_read_u8_array(node, "renesas-rzg2l,adc-channels",
> > +                                       channels, num_channels);
> > +       if (ret)
> > +               return ret;
> > +
> > +       chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array),
> > +                                 GFP_KERNEL);
> > +       if (!chan_array)
> > +               return -ENOMEM;
> > +
> > +       for (i = 0; i < num_channels; i++) {
> > +               chan_array[i].type = IIO_VOLTAGE;
> > +               chan_array[i].indexed = 1;
> > +               chan_array[i].channel = channels[i];
> > +               chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> > +               chan_array[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
> > +               chan_array[i].datasheet_name = rzg2l_adc_channel_name[i];
> > +       }
> > +
> > +       ret = of_property_read_u8(node, "renesas-rzg2l,adc-trigger-mode",
> > +                                 &data->trigger);
> > +       if (ret)
> > +               data->trigger = SW_TRIGGER;
> > +
> > +       /* we support SW_TRIGGER as of now */
> > +       if (data->trigger != SW_TRIGGER)
> > +               return -EINVAL;  
> 
> it would be an idea to remove this data->trigger field and the DT read
> for this property and add it when it's supported;
> typically these triggers don't get configured via DT;
> 
> > +
> > +       data->num_channels = num_channels;
> > +       data->channels = chan_array;
> > +       adc->data = data;
> > +
> > +       return 0;
> > +}
> > +
> > +static int rzg2l_adc_sw_reset(struct rzg2l_adc *adc)
> > +{
> > +       int timeout = 5;
> > +       u32 val;
> > +
> > +       val = rzg2l_adc_readl(adc, ADM(0));
> > +       val |= ADM0_SRESB;
> > +       rzg2l_adc_writel(adc, ADM(0), val);
> > +
> > +       while (!(rzg2l_adc_readl(adc, ADM(0)) & ADM0_SRESB)) {
> > +               if (!timeout)
> > +                       return -EINVAL;  
> 
> maybe -EBUSY is a bit better error code;
> 
> > +               timeout--;
> > +               usleep_range(100, 200);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int rzg2l_adc_probe(struct platform_device *pdev)
> > +{
> > +       struct iio_dev *indio_dev;
> > +       struct rzg2l_adc *adc;
> > +       int ret;
> > +       int irq;
> > +
> > +       indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
> > +       if (!indio_dev) {
> > +               dev_err(&pdev->dev, "failed allocating iio device\n");  
> 
> this message can be removed;
> looks like log spam;
> and if it happens, the system will be in a pretty bad state anyway
> 
> > +               return -ENOMEM;
> > +       }
> > +
> > +       adc = iio_priv(indio_dev);
> > +       if (!adc)
> > +               return -ENOMEM;  
> 
> this check is redundant;
> if indio_dev is non-NULL then iio_priv() will be good as well;
> 
> > +
> > +       ret = rzg2l_adc_parse_of(pdev, adc);
> > +       if (ret)
> > +               return -ENOMEM;
> > +
> > +       adc->base = devm_platform_ioremap_resource(pdev, 0);
> > +       if (IS_ERR(adc->base)) {
> > +               dev_err(&pdev->dev, "missing mem resource");  
> 
> this message can be removed;
> looks like log-spam
> 
> > +               return PTR_ERR(adc->base);
> > +       }
> > +
> > +       irq = platform_get_irq(pdev, 0);
> > +       if (irq < 0) {
> > +               dev_err(&pdev->dev, "no irq resource\n");
> > +               return irq;
> > +       }
> > +
> > +       adc->pclk = devm_clk_get(&pdev->dev, "pclk");
> > +       if (IS_ERR(adc->pclk)) {
> > +               dev_err(&pdev->dev, "Failed to get pclk");
> > +               return PTR_ERR(adc->pclk);
> > +       }
> > +
> > +       adc->adclk = devm_clk_get(&pdev->dev, "adclk");
> > +       if (IS_ERR(adc->adclk)) {
> > +               dev_err(&pdev->dev, "Failed to get adclk");
> > +               return PTR_ERR(adc->adclk);
> > +       }
> > +
> > +       adc->adrstn = devm_reset_control_get_exclusive(&pdev->dev, "adrst-n");
> > +       if (IS_ERR(adc->adrstn)) {
> > +               dev_err(&pdev->dev, "failed to get adrstn\n");
> > +               return PTR_ERR(adc->adrstn);
> > +       }
> > +
> > +       adc->presetn = devm_reset_control_get_exclusive(&pdev->dev, "presetn");
> > +       if (IS_ERR(adc->presetn)) {
> > +               dev_err(&pdev->dev, "failed to get presetn\n");
> > +               return PTR_ERR(adc->presetn);
> > +       }
> > +
> > +       ret = reset_control_deassert(adc->adrstn);
> > +       if (ret)
> > +               return ret;
> > +
> > +       ret = reset_control_deassert(adc->presetn);
> > +       if (ret)
> > +               goto assert_adrstn;
> > +
> > +       ret = clk_prepare_enable(adc->pclk);
> > +       if (ret)
> > +               goto assert_presetn;
> > +
> > +       ret = rzg2l_adc_sw_reset(adc);
> > +       if (ret)
> > +               goto unprepare_pclk;
> > +
> > +       init_completion(&adc->completion);
> > +
> > +       platform_set_drvdata(pdev, indio_dev);
> > +
> > +       ret = devm_request_irq(&pdev->dev, irq, rzg2l_adc_isr,
> > +                              0, dev_name(&pdev->dev), adc);
> > +       if (ret < 0)
> > +               goto unprepare_pclk;
> > +
> > +       adc->adc_disabled = false;
> > +       indio_dev->name = dev_name(&pdev->dev);  
> 
> indio_dev->name  should be the part-name;
> since this driver supports a single part, this can be:
> 
>   indio_dev->name = "rzg2l-adc";
> 
> > +       indio_dev->dev.parent = &pdev->dev;
> > +       indio_dev->dev.of_node = pdev->dev.of_node;  
> 
> 
> The 2 assignments above can be removed in the mainline driver.
> They should be done in devm_iio_device_alloc() and iio_device_register()
> 
> 
> > +       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;
> > +
> > +       ret = iio_device_register(indio_dev);
> > +       if (ret)
> > +               goto unprepare_pclk;
> > +
> > +       return 0;
> > +
> > +unprepare_pclk:
> > +       clk_disable_unprepare(adc->pclk);
> > +assert_presetn:
> > +       reset_control_assert(adc->presetn);
> > +assert_adrstn:
> > +       reset_control_assert(adc->adrstn);
> > +       return ret;
> > +}
> > +
> > +static int rzg2l_adc_remove(struct platform_device *pdev)
> > +{
> > +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > +       struct rzg2l_adc *adc = iio_priv(indio_dev);
> > +
> > +       mutex_lock(&indio_dev->mlock);
> > +       adc->adc_disabled = true;
> > +       mutex_unlock(&indio_dev->mlock);
> > +
> > +       iio_device_unregister(indio_dev);
> > +
> > +       clk_disable_unprepare(adc->pclk);
> > +       reset_control_assert(adc->presetn);
> > +       reset_control_assert(adc->adrstn);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct of_device_id rzg2l_adc_match[] = {
> > +       {
> > +               .compatible = "renesas,rzg2l-adc",
> > +       },
> > +       {},  
> 
> comma can be removed;
> since this is a null terminator
> 
> > +};
> > +MODULE_DEVICE_TABLE(of, rzg2l_adc_match);
> > +
> > +static struct platform_driver rzg2l_adc_driver = {
> > +       .probe          = rzg2l_adc_probe,
> > +       .remove         = rzg2l_adc_remove,
> > +       .driver         = {
> > +               .name   = "rzg2l-adc",
> > +               .of_match_table = rzg2l_adc_match,
> > +       },
> > +};
> > +
> > +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");
> > --
> > 2.17.1
> >  


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-01 20:21   ` Rob Herring
@ 2021-07-13 16:00     ` Lad, Prabhakar
  2021-07-13 16:32       ` Geert Uytterhoeven
  0 siblings, 1 reply; 19+ messages in thread
From: Lad, Prabhakar @ 2021-07-13 16:00 UTC (permalink / raw)
  To: Rob Herring
  Cc: Lad Prabhakar, Geert Uytterhoeven, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML,
	Linux-Renesas, Biju Das

Hi Rob,

Thank you for the review.

On Thu, Jul 1, 2021 at 9:21 PM Rob Herring <robh@kernel.org> wrote:
>
> On Tue, Jun 29, 2021 at 11:03:27PM +0100, Lad Prabhakar wrote:
> > Add binding documentation for Renesas RZ/G2L A/D converter block.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > ---
> >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> >  1 file changed, 121 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > new file mode 100644
> > index 000000000000..db935d6d59eb
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > @@ -0,0 +1,121 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Renesas RZ/G2L ADC
> > +
> > +maintainers:
> > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > +
> > +description: |
> > +  A/D Converter block is a successive approximation analog-to-digital converter
> > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > +  stored in a 32-bit data register corresponding to each channel.
> > +
> > +properties:
> > +  compatible:
> > +    oneOf:
>
> You can drop oneOf here.
>
Dropping oneOf from here dt_binding_check complains with below report,
Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml:
properties:compatible: [{'items': [{'enum':
['renesas,r9a07g044-adc']}, {'const': 'renesas,rzg2l-adc'}]}] is not
of type 'object', 'boolean'
from schema $id: http://json-schema.org/draft-07/schema#

> > +      - items:
> > +          - enum:
> > +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> > +          - const: renesas,rzg2l-adc
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    items:
> > +      - description: converter clock
> > +      - description: peripheral clock
> > +
> > +  clock-names:
> > +    items:
> > +      - const: adclk
> > +      - const: pclk
> > +
> > +  power-domains:
> > +    maxItems: 1
> > +
> > +  resets:
> > +    maxItems: 2
> > +
> > +  reset-names:
> > +    items:
> > +      - const: presetn
> > +      - const: adrst-n
> > +
> > +  renesas-rzg2l,adc-trigger-mode:
> > +    $ref: /schemas/types.yaml#/definitions/uint8
> > +    description: Trigger mode for A/D converter
> > +    enum:
> > +      - 0 # Software trigger mode (Defaults)
> > +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> > +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)
> > +    default: 0
> > +
> > +  gpios:
>
> A named gpio is preferred. trigger-gpios?
>
Agreed.

Cheers,
Prabhakar

> > +    description:
> > +      ADC_TRG trigger input pin
> > +    maxItems: 1
> > +
> > +  renesas-rzg2l,adc-channels:
> > +    $ref: /schemas/types.yaml#/definitions/uint8-array
> > +    description: Input channels available on platform
> > +    uniqueItems: true
> > +    minItems: 1
> > +    maxItems: 8
> > +    items:
> > +      enum: [0, 1, 2, 3, 4, 5, 6, 7]
> > +
> > +  "#io-channel-cells":
> > +    const: 1
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupts
> > +  - clocks
> > +  - clock-names
> > +  - power-domains
> > +  - resets
> > +  - reset-names
> > +  - renesas-rzg2l,adc-channels
> > +  - "#io-channel-cells"
> > +
> > +allOf:
> > +  - if:
> > +      properties:
> > +        renesas-rzg2l,adc-trigger-mode:
> > +          const: 1
> > +    then:
> > +      required:
> > +        - gpios
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/clock/r9a07g044-cpg.h>
> > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +
> > +    adc: adc@10059000 {
> > +      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
> > +      reg = <0x10059000 0x400>;
> > +      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
> > +      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
> > +               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
> > +      clock-names = "adclk", "pclk";
> > +      power-domains = <&cpg>;
> > +      resets = <&cpg R9A07G044_ADC_PRESETN>,
> > +               <&cpg R9A07G044_ADC_ADRST_N>;
> > +      reset-names = "presetn", "adrst-n";
> > +      #io-channel-cells = <1>;
> > +      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
> > +      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
> > +    };
> > --
> > 2.17.1
> >
> >

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-13 16:00     ` Lad, Prabhakar
@ 2021-07-13 16:32       ` Geert Uytterhoeven
  2021-07-13 16:39         ` Lad, Prabhakar
  0 siblings, 1 reply; 19+ messages in thread
From: Geert Uytterhoeven @ 2021-07-13 16:32 UTC (permalink / raw)
  To: Lad, Prabhakar
  Cc: Rob Herring, Lad Prabhakar, Geert Uytterhoeven, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML,
	Linux-Renesas, Biju Das

Hi Prabhakar,

On Tue, Jul 13, 2021 at 6:01 PM Lad, Prabhakar
<prabhakar.csengg@gmail.com> wrote:
> On Thu, Jul 1, 2021 at 9:21 PM Rob Herring <robh@kernel.org> wrote:
> > On Tue, Jun 29, 2021 at 11:03:27PM +0100, Lad Prabhakar wrote:
> > > Add binding documentation for Renesas RZ/G2L A/D converter block.
> > >
> > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > ---
> > >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> > >  1 file changed, 121 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > new file mode 100644
> > > index 000000000000..db935d6d59eb
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > @@ -0,0 +1,121 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Renesas RZ/G2L ADC
> > > +
> > > +maintainers:
> > > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > +
> > > +description: |
> > > +  A/D Converter block is a successive approximation analog-to-digital converter
> > > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > > +  stored in a 32-bit data register corresponding to each channel.
> > > +
> > > +properties:
> > > +  compatible:
> > > +    oneOf:
> >
> > You can drop oneOf here.
> >
> Dropping oneOf from here dt_binding_check complains with below report,
> Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml:
> properties:compatible: [{'items': [{'enum':
> ['renesas,r9a07g044-adc']}, {'const': 'renesas,rzg2l-adc'}]}] is not
> of type 'object', 'boolean'
> from schema $id: http://json-schema.org/draft-07/schema#

You forgot to drop the dash in front of the items, right?

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-13 16:32       ` Geert Uytterhoeven
@ 2021-07-13 16:39         ` Lad, Prabhakar
  0 siblings, 0 replies; 19+ messages in thread
From: Lad, Prabhakar @ 2021-07-13 16:39 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Rob Herring, Lad Prabhakar, Geert Uytterhoeven, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML,
	Linux-Renesas, Biju Das

Hi Geert,

On Tue, Jul 13, 2021 at 5:32 PM Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>
> Hi Prabhakar,
>
> On Tue, Jul 13, 2021 at 6:01 PM Lad, Prabhakar
> <prabhakar.csengg@gmail.com> wrote:
> > On Thu, Jul 1, 2021 at 9:21 PM Rob Herring <robh@kernel.org> wrote:
> > > On Tue, Jun 29, 2021 at 11:03:27PM +0100, Lad Prabhakar wrote:
> > > > Add binding documentation for Renesas RZ/G2L A/D converter block.
> > > >
> > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > > ---
> > > >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> > > >  1 file changed, 121 insertions(+)
> > > >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > new file mode 100644
> > > > index 000000000000..db935d6d59eb
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > @@ -0,0 +1,121 @@
> > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: Renesas RZ/G2L ADC
> > > > +
> > > > +maintainers:
> > > > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > +
> > > > +description: |
> > > > +  A/D Converter block is a successive approximation analog-to-digital converter
> > > > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > > > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > > > +  stored in a 32-bit data register corresponding to each channel.
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    oneOf:
> > >
> > > You can drop oneOf here.
> > >
> > Dropping oneOf from here dt_binding_check complains with below report,
> > Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml:
> > properties:compatible: [{'items': [{'enum':
> > ['renesas,r9a07g044-adc']}, {'const': 'renesas,rzg2l-adc'}]}] is not
> > of type 'object', 'boolean'
> > from schema $id: http://json-schema.org/draft-07/schema#
>
> You forgot to drop the dash in front of the items, right?
>
Argh! Thanks for the pointer.

Cheers,
Prabhakar

> Gr{oetje,eeting}s,
>
>                         Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-03 17:19   ` Jonathan Cameron
@ 2021-07-14  9:11     ` Lad, Prabhakar
  2021-07-14 12:39       ` Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Lad, Prabhakar @ 2021-07-14  9:11 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Lad Prabhakar, Geert Uytterhoeven, Rob Herring,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML,
	Linux-Renesas, Biju Das

Hi Jonathan,

Thank you for the review.

On Sat, Jul 3, 2021 at 6:17 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Tue, 29 Jun 2021 23:03:27 +0100
> Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
>
> > Add binding documentation for Renesas RZ/G2L A/D converter block.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> Hi,
>
> See inline
>
> Jonathan
>
> > ---
> >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> >  1 file changed, 121 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > new file mode 100644
> > index 000000000000..db935d6d59eb
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > @@ -0,0 +1,121 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Renesas RZ/G2L ADC
> > +
> > +maintainers:
> > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > +
> > +description: |
> > +  A/D Converter block is a successive approximation analog-to-digital converter
> > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > +  stored in a 32-bit data register corresponding to each channel.
> > +
> > +properties:
> > +  compatible:
> > +    oneOf:
> > +      - items:
> > +          - enum:
> > +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> > +          - const: renesas,rzg2l-adc
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    items:
> > +      - description: converter clock
> > +      - description: peripheral clock
> > +
> > +  clock-names:
> > +    items:
> > +      - const: adclk
> > +      - const: pclk
> > +
> > +  power-domains:
> > +    maxItems: 1
> > +
> > +  resets:
> > +    maxItems: 2
> > +
> > +  reset-names:
> > +    items:
> > +      - const: presetn
> > +      - const: adrst-n
> > +
> > +  renesas-rzg2l,adc-trigger-mode:
> > +    $ref: /schemas/types.yaml#/definitions/uint8
> > +    description: Trigger mode for A/D converter
> > +    enum:
> > +      - 0 # Software trigger mode (Defaults)
> > +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> > +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)
>
> Is this a function of the board in some fashion?  If not it sounds like
> something that should be in control of userspace.  Normally we'd
> do that by having the driver register some iio_triggers and depending
> on which one is selected do the equivalent of what you have here.
>
Agreed for Asynchronous and Synchronous triggers. WRT Software trigger
should this be registered as a  iio_triggers too or read_raw()
callback (with IIO_CHAN_INFO_RAW case)  should be treated as Software
trigger?

> > +    default: 0
> > +
> > +  gpios:
> > +    description:
> > +      ADC_TRG trigger input pin
> > +    maxItems: 1
> Why is this mode useful?  I'm assuming the gpio write would take a register
> write and the software trigger mode also requires a register write.
>
Yes gpio write would take a register write.

> Normally the reason for a pin like this is to support synchronising with
> external hardware.   If that's the case, we should call that out here.
> often the pin isn't even connected to a gpio in our control.
> (i.e. it's a trigger signal from some other device.)
>
So just setting the GPIO pin as input should do the trick.

> > +
> > +  renesas-rzg2l,adc-channels:
> > +    $ref: /schemas/types.yaml#/definitions/uint8-array
> > +    description: Input channels available on platform
> > +    uniqueItems: true
> > +    minItems: 1
> > +    maxItems: 8
> > +    items:
> > +      enum: [0, 1, 2, 3, 4, 5, 6, 7]
>
> Is this a function of different devices (should have different compatibles)
> or of what is wired up.  If it's what is wired up, then how do you know which
Its channels which are wired, for example if channels 0-5 are wired up
the board dts would include the property "renesas-rzg2l,adc-channels =
/bits/ 8 <0 1 2 3 4 5>;"

> subset of channels are connected?  We have the generic adc channel binding
> in iio/adc/adc.yaml for the case where we only want to expose those channels
> that are wired up.  It uses a node per channel.
>
Agreed will do that and drop the custom "renesas-rzg2l,adc-channels"

Cheers,
Prabhakar
> > +
> > +  "#io-channel-cells":
> > +    const: 1
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - interrupts
> > +  - clocks
> > +  - clock-names
> > +  - power-domains
> > +  - resets
> > +  - reset-names
> > +  - renesas-rzg2l,adc-channels
> > +  - "#io-channel-cells"
> > +
> > +allOf:
> > +  - if:
> > +      properties:
> > +        renesas-rzg2l,adc-trigger-mode:
> > +          const: 1
> > +    then:
> > +      required:
> > +        - gpios
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/clock/r9a07g044-cpg.h>
> > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +
> > +    adc: adc@10059000 {
> > +      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
> > +      reg = <0x10059000 0x400>;
> > +      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
> > +      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
> > +               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
> > +      clock-names = "adclk", "pclk";
> > +      power-domains = <&cpg>;
> > +      resets = <&cpg R9A07G044_ADC_PRESETN>,
> > +               <&cpg R9A07G044_ADC_ADRST_N>;
> > +      reset-names = "presetn", "adrst-n";
> > +      #io-channel-cells = <1>;
> > +      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
> > +      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
> > +    };
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-14  9:11     ` Lad, Prabhakar
@ 2021-07-14 12:39       ` Jonathan Cameron
  2021-07-14 18:24         ` Lad, Prabhakar
  0 siblings, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2021-07-14 12:39 UTC (permalink / raw)
  To: Lad, Prabhakar
  Cc: Jonathan Cameron, Lad Prabhakar, Geert Uytterhoeven, Rob Herring,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS"
	<devicetree@vger.kernel.org>,
	LKML <linux-kernel@vger.kernel.org>,
	Linux-Renesas <linux-renesas-soc@vger.kernel.org>,
	Biju Das <biju.das.jz@bp.renesas.com>

On Wed, 14 Jul 2021 10:11:49 +0100
"Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote:

> Hi Jonathan,
> 
> Thank you for the review.
> 
> On Sat, Jul 3, 2021 at 6:17 PM Jonathan Cameron <jic23@kernel.org> wrote:
> >
> > On Tue, 29 Jun 2021 23:03:27 +0100
> > Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
> >  
> > > Add binding documentation for Renesas RZ/G2L A/D converter block.
> > >
> > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>  
> > Hi,
> >
> > See inline
> >
> > Jonathan
> >  
> > > ---
> > >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> > >  1 file changed, 121 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > new file mode 100644
> > > index 000000000000..db935d6d59eb
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > @@ -0,0 +1,121 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Renesas RZ/G2L ADC
> > > +
> > > +maintainers:
> > > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > +
> > > +description: |
> > > +  A/D Converter block is a successive approximation analog-to-digital converter
> > > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > > +  stored in a 32-bit data register corresponding to each channel.
> > > +
> > > +properties:
> > > +  compatible:
> > > +    oneOf:
> > > +      - items:
> > > +          - enum:
> > > +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> > > +          - const: renesas,rzg2l-adc
> > > +
> > > +  reg:
> > > +    maxItems: 1
> > > +
> > > +  interrupts:
> > > +    maxItems: 1
> > > +
> > > +  clocks:
> > > +    items:
> > > +      - description: converter clock
> > > +      - description: peripheral clock
> > > +
> > > +  clock-names:
> > > +    items:
> > > +      - const: adclk
> > > +      - const: pclk
> > > +
> > > +  power-domains:
> > > +    maxItems: 1
> > > +
> > > +  resets:
> > > +    maxItems: 2
> > > +
> > > +  reset-names:
> > > +    items:
> > > +      - const: presetn
> > > +      - const: adrst-n
> > > +
> > > +  renesas-rzg2l,adc-trigger-mode:
> > > +    $ref: /schemas/types.yaml#/definitions/uint8
> > > +    description: Trigger mode for A/D converter
> > > +    enum:
> > > +      - 0 # Software trigger mode (Defaults)
> > > +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> > > +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)  
> >
> > Is this a function of the board in some fashion?  If not it sounds like
> > something that should be in control of userspace.  Normally we'd
> > do that by having the driver register some iio_triggers and depending
> > on which one is selected do the equivalent of what you have here.
> >  
> Agreed for Asynchronous and Synchronous triggers. WRT Software trigger
> should this be registered as a  iio_triggers too or read_raw()
> callback (with IIO_CHAN_INFO_RAW case)  should be treated as Software
> trigger?
> 

Normally we'd use an external trigger to provide the software trigger
(plus as you say sysfs reads will map to this functionality).

Something like the sysfs trigger or the hrtimer one would get used, though
also fine to use the dataready trigger from a different device (if you want
approximately synced dta.

> > > +    default: 0
> > > +
> > > +  gpios:
> > > +    description:
> > > +      ADC_TRG trigger input pin
> > > +    maxItems: 1  
> > Why is this mode useful?  I'm assuming the gpio write would take a register
> > write and the software trigger mode also requires a register write.
> >  
> Yes gpio write would take a register write.
> 
> > Normally the reason for a pin like this is to support synchronising with
> > external hardware.   If that's the case, we should call that out here.
> > often the pin isn't even connected to a gpio in our control.
> > (i.e. it's a trigger signal from some other device.)
> >  
> So just setting the GPIO pin as input should do the trick.

Probably the best plan if you actually care about people writing some
trigger up to it that is otherwise invisible to the system.

> 
> > > +
> > > +  renesas-rzg2l,adc-channels:
> > > +    $ref: /schemas/types.yaml#/definitions/uint8-array
> > > +    description: Input channels available on platform
> > > +    uniqueItems: true
> > > +    minItems: 1
> > > +    maxItems: 8
> > > +    items:
> > > +      enum: [0, 1, 2, 3, 4, 5, 6, 7]  
> >
> > Is this a function of different devices (should have different compatibles)
> > or of what is wired up.  If it's what is wired up, then how do you know which  
> Its channels which are wired, for example if channels 0-5 are wired up
> the board dts would include the property "renesas-rzg2l,adc-channels =
> /bits/ 8 <0 1 2 3 4 5>;"
> 
> > subset of channels are connected?  We have the generic adc channel binding
> > in iio/adc/adc.yaml for the case where we only want to expose those channels
> > that are wired up.  It uses a node per channel.
> >  
> Agreed will do that and drop the custom "renesas-rzg2l,adc-channels"

Great,

Jonathan

> 
> Cheers,
> Prabhakar
> > > +
> > > +  "#io-channel-cells":
> > > +    const: 1
> > > +
> > > +required:
> > > +  - compatible
> > > +  - reg
> > > +  - interrupts
> > > +  - clocks
> > > +  - clock-names
> > > +  - power-domains
> > > +  - resets
> > > +  - reset-names
> > > +  - renesas-rzg2l,adc-channels
> > > +  - "#io-channel-cells"
> > > +
> > > +allOf:
> > > +  - if:
> > > +      properties:
> > > +        renesas-rzg2l,adc-trigger-mode:
> > > +          const: 1
> > > +    then:
> > > +      required:
> > > +        - gpios
> > > +
> > > +additionalProperties: false
> > > +
> > > +examples:
> > > +  - |
> > > +    #include <dt-bindings/clock/r9a07g044-cpg.h>
> > > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > > +
> > > +    adc: adc@10059000 {
> > > +      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
> > > +      reg = <0x10059000 0x400>;
> > > +      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
> > > +      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
> > > +               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
> > > +      clock-names = "adclk", "pclk";
> > > +      power-domains = <&cpg>;
> > > +      resets = <&cpg R9A07G044_ADC_PRESETN>,
> > > +               <&cpg R9A07G044_ADC_ADRST_N>;
> > > +      reset-names = "presetn", "adrst-n";
> > > +      #io-channel-cells = <1>;
> > > +      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
> > > +      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
> > > +    };  
> >  


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-14 12:39       ` Jonathan Cameron
@ 2021-07-14 18:24         ` Lad, Prabhakar
  2021-07-15 13:02           ` Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Lad, Prabhakar @ 2021-07-14 18:24 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jonathan Cameron, Lad Prabhakar, Geert Uytterhoeven, Rob Herring,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Linux-Renesas, Biju Das, LKML

Hi Jonathan,

On Wed, Jul 14, 2021 at 1:39 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Wed, 14 Jul 2021 10:11:49 +0100
> "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote:
>
> > Hi Jonathan,
> >
> > Thank you for the review.
> >
> > On Sat, Jul 3, 2021 at 6:17 PM Jonathan Cameron <jic23@kernel.org> wrote:
> > >
> > > On Tue, 29 Jun 2021 23:03:27 +0100
> > > Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
> > >
> > > > Add binding documentation for Renesas RZ/G2L A/D converter block.
> > > >
> > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > Hi,
> > >
> > > See inline
> > >
> > > Jonathan
> > >
> > > > ---
> > > >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> > > >  1 file changed, 121 insertions(+)
> > > >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > new file mode 100644
> > > > index 000000000000..db935d6d59eb
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > @@ -0,0 +1,121 @@
> > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: Renesas RZ/G2L ADC
> > > > +
> > > > +maintainers:
> > > > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > +
> > > > +description: |
> > > > +  A/D Converter block is a successive approximation analog-to-digital converter
> > > > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > > > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > > > +  stored in a 32-bit data register corresponding to each channel.
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    oneOf:
> > > > +      - items:
> > > > +          - enum:
> > > > +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> > > > +          - const: renesas,rzg2l-adc
> > > > +
> > > > +  reg:
> > > > +    maxItems: 1
> > > > +
> > > > +  interrupts:
> > > > +    maxItems: 1
> > > > +
> > > > +  clocks:
> > > > +    items:
> > > > +      - description: converter clock
> > > > +      - description: peripheral clock
> > > > +
> > > > +  clock-names:
> > > > +    items:
> > > > +      - const: adclk
> > > > +      - const: pclk
> > > > +
> > > > +  power-domains:
> > > > +    maxItems: 1
> > > > +
> > > > +  resets:
> > > > +    maxItems: 2
> > > > +
> > > > +  reset-names:
> > > > +    items:
> > > > +      - const: presetn
> > > > +      - const: adrst-n
> > > > +
> > > > +  renesas-rzg2l,adc-trigger-mode:
> > > > +    $ref: /schemas/types.yaml#/definitions/uint8
> > > > +    description: Trigger mode for A/D converter
> > > > +    enum:
> > > > +      - 0 # Software trigger mode (Defaults)
> > > > +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> > > > +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)
> > >
> > > Is this a function of the board in some fashion?  If not it sounds like
> > > something that should be in control of userspace.  Normally we'd
> > > do that by having the driver register some iio_triggers and depending
> > > on which one is selected do the equivalent of what you have here.
> > >
> > Agreed for Asynchronous and Synchronous triggers. WRT Software trigger
> > should this be registered as a  iio_triggers too or read_raw()
> > callback (with IIO_CHAN_INFO_RAW case)  should be treated as Software
> > trigger?
> >
>
> Normally we'd use an external trigger to provide the software trigger
> (plus as you say sysfs reads will map to this functionality).
>
> Something like the sysfs trigger or the hrtimer one would get used, though
> also fine to use the dataready trigger from a different device (if you want
> approximately synced dta.
>
We can live with syfs reads for now for SW triggers. Coming back to HW
triggers I responded too quickly!. I am now trying to implement a gpio
based HW trigger i.e. to kick adc conversion start but I couldn't find
any drivers doing that. I looked at iio-trig-interrupt.c which
registers irq based triggers, so something similar needs to be
implemented in the adc driver? If that is the case the gpio has to be
passed via to DT and use gpio_to_irq to register the handler. Or is it
that I am missing something here ?

Cheers,
Prabhakar

> > > > +    default: 0
> > > > +
> > > > +  gpios:
> > > > +    description:
> > > > +      ADC_TRG trigger input pin
> > > > +    maxItems: 1
> > > Why is this mode useful?  I'm assuming the gpio write would take a register
> > > write and the software trigger mode also requires a register write.
> > >
> > Yes gpio write would take a register write.
> >
> > > Normally the reason for a pin like this is to support synchronising with
> > > external hardware.   If that's the case, we should call that out here.
> > > often the pin isn't even connected to a gpio in our control.
> > > (i.e. it's a trigger signal from some other device.)
> > >
> > So just setting the GPIO pin as input should do the trick.
>
> Probably the best plan if you actually care about people writing some
> trigger up to it that is otherwise invisible to the system.
>
> >
> > > > +
> > > > +  renesas-rzg2l,adc-channels:
> > > > +    $ref: /schemas/types.yaml#/definitions/uint8-array
> > > > +    description: Input channels available on platform
> > > > +    uniqueItems: true
> > > > +    minItems: 1
> > > > +    maxItems: 8
> > > > +    items:
> > > > +      enum: [0, 1, 2, 3, 4, 5, 6, 7]
> > >
> > > Is this a function of different devices (should have different compatibles)
> > > or of what is wired up.  If it's what is wired up, then how do you know which
> > Its channels which are wired, for example if channels 0-5 are wired up
> > the board dts would include the property "renesas-rzg2l,adc-channels =
> > /bits/ 8 <0 1 2 3 4 5>;"
> >
> > > subset of channels are connected?  We have the generic adc channel binding
> > > in iio/adc/adc.yaml for the case where we only want to expose those channels
> > > that are wired up.  It uses a node per channel.
> > >
> > Agreed will do that and drop the custom "renesas-rzg2l,adc-channels"
>
> Great,
>
> Jonathan
>
> >
> > Cheers,
> > Prabhakar
> > > > +
> > > > +  "#io-channel-cells":
> > > > +    const: 1
> > > > +
> > > > +required:
> > > > +  - compatible
> > > > +  - reg
> > > > +  - interrupts
> > > > +  - clocks
> > > > +  - clock-names
> > > > +  - power-domains
> > > > +  - resets
> > > > +  - reset-names
> > > > +  - renesas-rzg2l,adc-channels
> > > > +  - "#io-channel-cells"
> > > > +
> > > > +allOf:
> > > > +  - if:
> > > > +      properties:
> > > > +        renesas-rzg2l,adc-trigger-mode:
> > > > +          const: 1
> > > > +    then:
> > > > +      required:
> > > > +        - gpios
> > > > +
> > > > +additionalProperties: false
> > > > +
> > > > +examples:
> > > > +  - |
> > > > +    #include <dt-bindings/clock/r9a07g044-cpg.h>
> > > > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > > > +
> > > > +    adc: adc@10059000 {
> > > > +      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
> > > > +      reg = <0x10059000 0x400>;
> > > > +      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
> > > > +      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
> > > > +               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
> > > > +      clock-names = "adclk", "pclk";
> > > > +      power-domains = <&cpg>;
> > > > +      resets = <&cpg R9A07G044_ADC_PRESETN>,
> > > > +               <&cpg R9A07G044_ADC_ADRST_N>;
> > > > +      reset-names = "presetn", "adrst-n";
> > > > +      #io-channel-cells = <1>;
> > > > +      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
> > > > +      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
> > > > +    };
> > >
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] iio: adc: Add driver for Renesas RZ/G2L A/D converter
  2021-07-01 10:28   ` Alexandru Ardelean
  2021-07-03 17:41     ` Jonathan Cameron
@ 2021-07-14 21:25     ` Lad, Prabhakar
  1 sibling, 0 replies; 19+ messages in thread
From: Lad, Prabhakar @ 2021-07-14 21:25 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Lad Prabhakar, Geert Uytterhoeven, Rob Herring, Jonathan Cameron,
	Lars-Peter Clausen, Philipp Zabel, linux-iio, devicetree, LKML,
	Linux-Renesas, Biju Das

HI Alexandru,

Thank you for the review.

On Thu, Jul 1, 2021 at 11:28 AM Alexandru Ardelean
<ardeleanalex@gmail.com> wrote:
>
> On Wed, Jun 30, 2021 at 1:07 AM Lad Prabhakar
> <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
> >
> > Add ADC driver support for Renesas RZ/G2L A/D converter in SW
> > trigger mode.
> >
> > A/D Converter block is a successive approximation analog-to-digital
> > converter with a 12-bit accuracy and supports a maximum of 8 input
> > channels.
>
> Hey,
>
> Some comments inline.
>
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > ---
> >  MAINTAINERS                 |   8 +
> >  drivers/iio/adc/Kconfig     |  10 +
> >  drivers/iio/adc/Makefile    |   1 +
> >  drivers/iio/adc/rzg2l_adc.c | 489 ++++++++++++++++++++++++++++++++++++
> >  4 files changed, 508 insertions(+)
> >  create mode 100644 drivers/iio/adc/rzg2l_adc.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 81e1edeceae4..bee4c3847e01 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -15622,6 +15622,14 @@ L:     linux-renesas-soc@vger.kernel.org
> >  S:     Maintained
> >  F:     drivers/phy/renesas/phy-rcar-gen3-usb*.c
> >
> > +RENESAS RZ/G2L A/D DRIVER
> > +M:     Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > +L:     linux-iio@vger.kernel.org
> > +L:     linux-renesas-soc@vger.kernel.org
> > +S:     Supported
> > +F:     Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > +F:     drivers/iio/adc/rzg2l_adc.c
> > +
> >  RESET CONTROLLER FRAMEWORK
> >  M:     Philipp Zabel <p.zabel@pengutronix.de>
> >  S:     Maintained
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index c7946c439612..9408cbf97acc 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 a226657d19c0..d92bcc9c5fbb 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/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
> > new file mode 100644
> > index 000000000000..1c58eb8ae1ec
> > --- /dev/null
> > +++ b/drivers/iio/adc/rzg2l_adc.c
> > @@ -0,0 +1,489 @@
> > +// 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/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/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/reset.h>
> > +
> > +#define ADM(n)                 ((n) * 0x4)
> > +#define ADM0_ADCE              BIT(0)
> > +#define ADM0_ADBSY             BIT(1)
> > +#define ADM0_PWDWNB            BIT(2)
> > +#define ADM0_SRESB             BIT(15)
> > +#define ADM1_TRG               BIT(0)
> > +#define ADM1_MS                        BIT(2)
> > +#define ADM1_BS                        BIT(4)
> > +#define ADM1_EGA_CLEAR         ~GENMASK(13, 12)
> > +#define ADM2_CHSEL_CLEAR       ~GENMASK(7, 0)
> > +#define ADM3_ADSMP             0x578
> > +#define ADM3_ADCMP             (0xe << 16)
> > +#define ADM3_ADIL_CLEAR                ~GENMASK(31, 24)
> > +
> > +#define ADINT                  0x20
> > +#define ADINT_CH_CLEAR         ~GENMASK(7, 0)
> > +#define ADINT_CSEEN            BIT(16)
> > +#define ADINT_INTS             BIT(31)
> > +#define ADSTS                  0x24
> > +#define ADINT_INTST_MASK       GENMASK(7, 0)
> > +#define ADSTS_CSEST            BIT(16)
> > +#define ADIVC                  0x28
> > +#define ADIVC_DIVADC_CLEAR     ~GENMASK(8, 0)
> > +#define ADIVC_DIVADC_4         0x4
> > +#define ADFIL                  0x2c
> > +#define ADCR(n)                        (0x30 + ((n) * 0x4))
> > +#define ADCR_AD_MASK           GENMASK(11, 0)
> > +
> > +#define ADC_MAX_CHANNELS       8
> > +#define ADC_CHN_MASK           0x7
> > +#define ADC_TIMEOUT            usecs_to_jiffies(1 * 4)
> > +
> > +enum trigger_mode {
> > +       SW_TRIGGER = 0,
> > +       SYNC_TRIGGER,
> > +       ASYNC_TRIGGER,
> > +};
>
> this enum could also be removed [for now] given that only SW_TRIGGER
> is supported;
>
OK will do.

> > +
> > +struct rzg2l_adc_data {
> > +       const struct iio_chan_spec *channels;
> > +       u8 num_channels;
> > +       u8 trigger;
> > +};
> > +
> > +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;
> > +       bool adc_disabled; /* protected with mlock mutex from indio_dev */
>
> this adc_disabled flag looks a bit weird;
> it seems to guard against this driver being removed to prevent some reads.
> technically, this should be protected by IIO core;
> so the flag itself (or how it is being used) looks like it doesn't do much;
>
Agreed will drop that.

> > +       u16 last_val[ADC_MAX_CHANNELS];
> > +};
> > +
> > +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 int rzg2l_adc_adclk(struct rzg2l_adc *adc, bool prepare)
> > +{
> > +       if (prepare)
> > +               return clk_prepare_enable(adc->adclk);
> > +
> > +       clk_disable_unprepare(adc->adclk);
> > +       return 0;
> > +}
> > +
> > +static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on)
> > +{
> > +       u32 reg;
> > +
> > +       reg = rzg2l_adc_readl(adc, ADM(0));
> > +       if (on)
> > +               reg |= ADM0_PWDWNB;
> > +       else
> > +               reg &= ~ADM0_PWDWNB;
> > +       rzg2l_adc_writel(adc, ADM(0), reg);
> > +       udelay(2);
> > +}
> > +
> > +static void rzg2l_adc_conversion(struct rzg2l_adc *adc, bool start)
> > +{
> > +       int timeout = 5;
> > +       u32 reg;
> > +
> > +       /* stop A/D conversion */
> > +       reg = rzg2l_adc_readl(adc, ADM(0));
> > +       if (start)
> > +               reg |= ADM0_ADCE;
> > +       else
> > +               reg &= ~ADM0_ADCE;
> > +       rzg2l_adc_writel(adc, ADM(0), reg);
> > +
> > +       if (start)
> > +               return;
> > +
> > +       do {
> > +               usleep_range(100, 200);
> > +               reg = rzg2l_adc_readl(adc, ADM(0));
> > +               timeout--;
> > +               if (!timeout) {
> > +                       pr_err("%s stopping ADC timed out\n", __func__);
> > +                       break;
> > +               }
> > +       } while (((reg & ADM0_ADBSY) || (reg & ADM0_ADCE)));
> > +}
> > +
> > +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);
> > +       u32 reg;
> > +       int ret;
> > +       u8 ch;
> > +
> > +       switch (mask) {
> > +       case IIO_CHAN_INFO_RAW:
> > +               mutex_lock(&indio_dev->mlock);
>
> [1]
> acquiring indio_dev->mlock directly is discouraged;
> this lock is reserved for IIO core logic and will be moved into an
> iio_dev_opaque struct eventually;
> driver state locks should be defined in struct rzg2l_adc and managed
> by the driver to protect it's own internal state;
>
Agreed will add driver state lock in struct rzg2l_adc
>
> > +
> > +               if (adc->adc_disabled) {
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -EBUSY;
> > +               }
> > +
> > +               if (rzg2l_adc_readl(adc, ADM(0)) & ADM0_ADBSY) {
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -EBUSY;
> > +               }
> > +
> > +               ch = chan->channel & ADC_CHN_MASK;
> > +               /* SW trigger */
> > +               reg = rzg2l_adc_readl(adc, ADM(1));
> > +               reg &= ADM1_EGA_CLEAR;
> > +               reg &= ~ADM1_BS;
> > +               reg |= ADM1_MS;
> > +               reg &= ~ADM1_TRG;
> > +               rzg2l_adc_writel(adc, ADM(1), reg);
> > +
> > +               /* select channel */
> > +               reg = rzg2l_adc_readl(adc, ADM(2));
> > +               reg &= ADM2_CHSEL_CLEAR;
> > +               reg |= BIT(ch);
> > +               rzg2l_adc_writel(adc, ADM(2), reg);
> > +
> > +               reg = rzg2l_adc_readl(adc, ADM(3));
> > +               reg &= ADM3_ADIL_CLEAR;
> > +               reg |= ADM3_ADCMP;
> > +               reg |= ADM3_ADSMP;
> > +               rzg2l_adc_writel(adc, ADM(3), reg);
> > +
> > +               reg = rzg2l_adc_readl(adc, ADIVC);
> > +               reg &= ADIVC_DIVADC_CLEAR;
> > +               reg |= ADIVC_DIVADC_4;
> > +               rzg2l_adc_writel(adc, ADIVC, reg);
> > +
> > +               reg = rzg2l_adc_readl(adc, ADINT);
> > +               reg &= ~ADINT_INTS;
> > +               reg &= ADINT_CH_CLEAR;
> > +               reg |= ADINT_CSEEN;
> > +               reg |= BIT(ch);
> > +               rzg2l_adc_writel(adc, ADINT, reg);
> > +
> > +               rzg2l_adc_pwr(adc, true);
>
> should all this clock & power management be done in this read function?
> it looks like an awful lot just to perform a single read
> maybe some PM suspend/resume hooks would be a better idea for these;
>
Agreed that would make the code much cleaner.

> > +
> > +               ret = rzg2l_adc_adclk(adc, true);
> > +               if (ret) {
> > +                       rzg2l_adc_pwr(adc, false);
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -EINVAL;
> > +               }
> > +
> > +               reinit_completion(&adc->completion);
> > +
> > +               rzg2l_adc_conversion(adc, true);
> > +
> > +               if (!wait_for_completion_timeout(&adc->completion, ADC_TIMEOUT)) {
> > +                       reg &= ADINT_CH_CLEAR;
> > +                       rzg2l_adc_writel(adc, ADINT, reg);
> > +                       rzg2l_adc_conversion(adc, false);
> > +                       rzg2l_adc_adclk(adc, false);
> > +                       rzg2l_adc_pwr(adc, false);
> > +                       mutex_unlock(&indio_dev->mlock);
> > +                       return -ETIMEDOUT;
> > +               }
> > +
> > +               *val = adc->last_val[ch];
> > +               rzg2l_adc_conversion(adc, false);
> > +               rzg2l_adc_adclk(adc, false);
> > +               rzg2l_adc_pwr(adc, false);
> > +               mutex_unlock(&indio_dev->mlock);
> > +               return IIO_VAL_INT;
> > +
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +}
> > +
> > +static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
> > +{
> > +       struct rzg2l_adc *adc = (struct rzg2l_adc *)dev_id;
> > +       u8 intst;
> > +       u32 reg;
> > +       u8 i;
> > +
> > +       reg = rzg2l_adc_readl(adc, ADSTS);
> > +       if (reg & ADSTS_CSEST) {
> > +               rzg2l_adc_writel(adc, ADSTS, reg);
> > +               return IRQ_HANDLED;
> > +       }
> > +
> > +       intst = reg & ADINT_INTST_MASK;
> > +       if (!intst)
> > +               return IRQ_HANDLED;
> > +
> > +       for (i = 0; i < ADC_MAX_CHANNELS; i++) {
> > +               if (intst & BIT(i))
> > +                       adc->last_val[i] = rzg2l_adc_readl(adc, ADCR(i)) & ADCR_AD_MASK;
> > +       }
> > +
> > +       rzg2l_adc_writel(adc, ADSTS, reg);
> > +
> > +       complete(&adc->completion);
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static const struct iio_info rzg2l_adc_iio_info = {
> > +       .read_raw = rzg2l_adc_read_raw,
> > +};
> > +
> > +static const char * const rzg2l_adc_channel_name[] = {
> > +       "adc0",
> > +       "adc1",
> > +       "adc2",
> > +       "adc3",
> > +       "adc4",
> > +       "adc5",
> > +       "adc6",
> > +       "adc7",
> > +};
> > +
> > +static int rzg2l_adc_parse_of(struct platform_device *pdev, struct rzg2l_adc *adc)
> > +{
> > +       struct device_node *node = pdev->dev.of_node;
> > +       struct iio_chan_spec *chan_array;
> > +       u8 channels[ADC_MAX_CHANNELS];
> > +       struct rzg2l_adc_data *data;
> > +       int num_channels;
> > +       int ret;
> > +       u8 i;
> > +
> > +       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> > +       if (!data)
> > +               return -ENOMEM;
> > +
> > +       num_channels = of_property_count_u8_elems(node, "renesas-rzg2l,adc-channels");
> > +       if (num_channels <= 0 || num_channels > ADC_MAX_CHANNELS)
> > +               return -EINVAL;
> > +
> > +       ret = of_property_read_u8_array(node, "renesas-rzg2l,adc-channels",
> > +                                       channels, num_channels);
> > +       if (ret)
> > +               return ret;
> > +
> > +       chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array),
> > +                                 GFP_KERNEL);
> > +       if (!chan_array)
> > +               return -ENOMEM;
> > +
> > +       for (i = 0; i < num_channels; i++) {
> > +               chan_array[i].type = IIO_VOLTAGE;
> > +               chan_array[i].indexed = 1;
> > +               chan_array[i].channel = channels[i];
> > +               chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> > +               chan_array[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
> > +               chan_array[i].datasheet_name = rzg2l_adc_channel_name[i];
> > +       }
> > +
> > +       ret = of_property_read_u8(node, "renesas-rzg2l,adc-trigger-mode",
> > +                                 &data->trigger);
> > +       if (ret)
> > +               data->trigger = SW_TRIGGER;
> > +
> > +       /* we support SW_TRIGGER as of now */
> > +       if (data->trigger != SW_TRIGGER)
> > +               return -EINVAL;
>
> it would be an idea to remove this data->trigger field and the DT read
> for this property and add it when it's supported;
> typically these triggers don't get configured via DT;
>
Agreed will drop this.

> > +
> > +       data->num_channels = num_channels;
> > +       data->channels = chan_array;
> > +       adc->data = data;
> > +
> > +       return 0;
> > +}
> > +
> > +static int rzg2l_adc_sw_reset(struct rzg2l_adc *adc)
> > +{
> > +       int timeout = 5;
> > +       u32 val;
> > +
> > +       val = rzg2l_adc_readl(adc, ADM(0));
> > +       val |= ADM0_SRESB;
> > +       rzg2l_adc_writel(adc, ADM(0), val);
> > +
> > +       while (!(rzg2l_adc_readl(adc, ADM(0)) & ADM0_SRESB)) {
> > +               if (!timeout)
> > +                       return -EINVAL;
>
> maybe -EBUSY is a bit better error code;
>
> > +               timeout--;
> > +               usleep_range(100, 200);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int rzg2l_adc_probe(struct platform_device *pdev)
> > +{
> > +       struct iio_dev *indio_dev;
> > +       struct rzg2l_adc *adc;
> > +       int ret;
> > +       int irq;
> > +
> > +       indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
> > +       if (!indio_dev) {
> > +               dev_err(&pdev->dev, "failed allocating iio device\n");
>
> this message can be removed;
> looks like log spam;
> and if it happens, the system will be in a pretty bad state anyway
>
OK will drop this.

> > +               return -ENOMEM;
> > +       }
> > +
> > +       adc = iio_priv(indio_dev);
> > +       if (!adc)
> > +               return -ENOMEM;
>
> this check is redundant;
> if indio_dev is non-NULL then iio_priv() will be good as well;
>
> > +
> > +       ret = rzg2l_adc_parse_of(pdev, adc);
> > +       if (ret)
> > +               return -ENOMEM;
> > +
> > +       adc->base = devm_platform_ioremap_resource(pdev, 0);
> > +       if (IS_ERR(adc->base)) {
> > +               dev_err(&pdev->dev, "missing mem resource");
>
> this message can be removed;
> looks like log-spam
>
Will drop.

> > +               return PTR_ERR(adc->base);
> > +       }
> > +
> > +       irq = platform_get_irq(pdev, 0);
> > +       if (irq < 0) {
> > +               dev_err(&pdev->dev, "no irq resource\n");
> > +               return irq;
> > +       }
> > +
> > +       adc->pclk = devm_clk_get(&pdev->dev, "pclk");
> > +       if (IS_ERR(adc->pclk)) {
> > +               dev_err(&pdev->dev, "Failed to get pclk");
> > +               return PTR_ERR(adc->pclk);
> > +       }
> > +
> > +       adc->adclk = devm_clk_get(&pdev->dev, "adclk");
> > +       if (IS_ERR(adc->adclk)) {
> > +               dev_err(&pdev->dev, "Failed to get adclk");
> > +               return PTR_ERR(adc->adclk);
> > +       }
> > +
> > +       adc->adrstn = devm_reset_control_get_exclusive(&pdev->dev, "adrst-n");
> > +       if (IS_ERR(adc->adrstn)) {
> > +               dev_err(&pdev->dev, "failed to get adrstn\n");
> > +               return PTR_ERR(adc->adrstn);
> > +       }
> > +
> > +       adc->presetn = devm_reset_control_get_exclusive(&pdev->dev, "presetn");
> > +       if (IS_ERR(adc->presetn)) {
> > +               dev_err(&pdev->dev, "failed to get presetn\n");
> > +               return PTR_ERR(adc->presetn);
> > +       }
> > +
> > +       ret = reset_control_deassert(adc->adrstn);
> > +       if (ret)
> > +               return ret;
> > +
> > +       ret = reset_control_deassert(adc->presetn);
> > +       if (ret)
> > +               goto assert_adrstn;
> > +
> > +       ret = clk_prepare_enable(adc->pclk);
> > +       if (ret)
> > +               goto assert_presetn;
> > +
> > +       ret = rzg2l_adc_sw_reset(adc);
> > +       if (ret)
> > +               goto unprepare_pclk;
> > +
> > +       init_completion(&adc->completion);
> > +
> > +       platform_set_drvdata(pdev, indio_dev);
> > +
> > +       ret = devm_request_irq(&pdev->dev, irq, rzg2l_adc_isr,
> > +                              0, dev_name(&pdev->dev), adc);
> > +       if (ret < 0)
> > +               goto unprepare_pclk;
> > +
> > +       adc->adc_disabled = false;
> > +       indio_dev->name = dev_name(&pdev->dev);
>
> indio_dev->name  should be the part-name;
> since this driver supports a single part, this can be:
>
>   indio_dev->name = "rzg2l-adc";
>
OK

> > +       indio_dev->dev.parent = &pdev->dev;
> > +       indio_dev->dev.of_node = pdev->dev.of_node;
>
>
> The 2 assignments above can be removed in the mainline driver.
> They should be done in devm_iio_device_alloc() and iio_device_register()
>
Agreed will drop this.

>
> > +       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;
> > +
> > +       ret = iio_device_register(indio_dev);
> > +       if (ret)
> > +               goto unprepare_pclk;
> > +
> > +       return 0;
> > +
> > +unprepare_pclk:
> > +       clk_disable_unprepare(adc->pclk);
> > +assert_presetn:
> > +       reset_control_assert(adc->presetn);
> > +assert_adrstn:
> > +       reset_control_assert(adc->adrstn);
> > +       return ret;
> > +}
> > +
> > +static int rzg2l_adc_remove(struct platform_device *pdev)
> > +{
> > +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > +       struct rzg2l_adc *adc = iio_priv(indio_dev);
> > +
> > +       mutex_lock(&indio_dev->mlock);
> > +       adc->adc_disabled = true;
> > +       mutex_unlock(&indio_dev->mlock);
> > +
> > +       iio_device_unregister(indio_dev);
> > +
> > +       clk_disable_unprepare(adc->pclk);
> > +       reset_control_assert(adc->presetn);
> > +       reset_control_assert(adc->adrstn);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct of_device_id rzg2l_adc_match[] = {
> > +       {
> > +               .compatible = "renesas,rzg2l-adc",
> > +       },
> > +       {},
>
> comma can be removed;
> since this is a null terminator
>
OK.

Cheers,
Prabhakar

> > +};
> > +MODULE_DEVICE_TABLE(of, rzg2l_adc_match);
> > +
> > +static struct platform_driver rzg2l_adc_driver = {
> > +       .probe          = rzg2l_adc_probe,
> > +       .remove         = rzg2l_adc_remove,
> > +       .driver         = {
> > +               .name   = "rzg2l-adc",
> > +               .of_match_table = rzg2l_adc_match,
> > +       },
> > +};
> > +
> > +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");
> > --
> > 2.17.1
> >

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] iio: adc: Add driver for Renesas RZ/G2L A/D converter
  2021-07-03 17:41     ` Jonathan Cameron
@ 2021-07-14 21:31       ` Lad, Prabhakar
  0 siblings, 0 replies; 19+ messages in thread
From: Lad, Prabhakar @ 2021-07-14 21:31 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, Lad Prabhakar, Geert Uytterhoeven,
	Rob Herring, Lars-Peter Clausen, Philipp Zabel, linux-iio,
	devicetree, LKML, Linux-Renesas, Biju Das

Hi Jonathan,

Thank you for the review.

On Sat, Jul 3, 2021 at 6:39 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Thu, 1 Jul 2021 13:28:31 +0300
> Alexandru Ardelean <ardeleanalex@gmail.com> wrote:
>
> > On Wed, Jun 30, 2021 at 1:07 AM Lad Prabhakar
> > <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
> > >
> > > Add ADC driver support for Renesas RZ/G2L A/D converter in SW
> > > trigger mode.
> > >
> > > A/D Converter block is a successive approximation analog-to-digital
> > > converter with a 12-bit accuracy and supports a maximum of 8 input
> > > channels.
> >
> > Hey,
> >
> > Some comments inline.
>
> I added a few more on top, but Alex did a good job so it wasn't much!
>
> Jonathan
>
> >
> > >
> > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > ---
> > >  MAINTAINERS                 |   8 +
> > >  drivers/iio/adc/Kconfig     |  10 +
> > >  drivers/iio/adc/Makefile    |   1 +
> > >  drivers/iio/adc/rzg2l_adc.c | 489 ++++++++++++++++++++++++++++++++++++
> > >  4 files changed, 508 insertions(+)
> > >  create mode 100644 drivers/iio/adc/rzg2l_adc.c
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 81e1edeceae4..bee4c3847e01 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -15622,6 +15622,14 @@ L:     linux-renesas-soc@vger.kernel.org
> > >  S:     Maintained
> > >  F:     drivers/phy/renesas/phy-rcar-gen3-usb*.c
> > >
> > > +RENESAS RZ/G2L A/D DRIVER
> > > +M:     Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > +L:     linux-iio@vger.kernel.org
> > > +L:     linux-renesas-soc@vger.kernel.org
> > > +S:     Supported
> > > +F:     Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > +F:     drivers/iio/adc/rzg2l_adc.c
> > > +
> > >  RESET CONTROLLER FRAMEWORK
> > >  M:     Philipp Zabel <p.zabel@pengutronix.de>
> > >  S:     Maintained
> > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > > index c7946c439612..9408cbf97acc 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 a226657d19c0..d92bcc9c5fbb 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/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
> > > new file mode 100644
> > > index 000000000000..1c58eb8ae1ec
> > > --- /dev/null
> > > +++ b/drivers/iio/adc/rzg2l_adc.c
> > > @@ -0,0 +1,489 @@
> > > +// 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/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/module.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_device.h>
>
> I'd in general prefer use of generic firmware properties rather than
> the of variant but I guess you can be fairly sure these devices are always
> going to be using devicetree, so it doesn't really matter.
>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/reset.h>
> > > +
> > > +#define ADM(n)                 ((n) * 0x4)
>
> I'd prefer some sort of driver specific prefix on these defines as some
> are very generic sounding so it would be good to make it clear they are
> local to this driver.
>
OK will prefix it with RZG2L_ for all the reg/reg bits/reg masks

> > > +#define ADM0_ADCE              BIT(0)
> > > +#define ADM0_ADBSY             BIT(1)
> > > +#define ADM0_PWDWNB            BIT(2)
> > > +#define ADM0_SRESB             BIT(15)
> > > +#define ADM1_TRG               BIT(0)
> > > +#define ADM1_MS                        BIT(2)
> > > +#define ADM1_BS                        BIT(4)
> > > +#define ADM1_EGA_CLEAR         ~GENMASK(13, 12)
> > > +#define ADM2_CHSEL_CLEAR       ~GENMASK(7, 0)
> > > +#define ADM3_ADSMP             0x578
> > > +#define ADM3_ADCMP             (0xe << 16)
> > > +#define ADM3_ADIL_CLEAR                ~GENMASK(31, 24)
> > > +
> > > +#define ADINT                  0x20
> > > +#define ADINT_CH_CLEAR         ~GENMASK(7, 0)
> > > +#define ADINT_CSEEN            BIT(16)
> > > +#define ADINT_INTS             BIT(31)
> > > +#define ADSTS                  0x24
> > > +#define ADINT_INTST_MASK       GENMASK(7, 0)
> > > +#define ADSTS_CSEST            BIT(16)
> > > +#define ADIVC                  0x28
> > > +#define ADIVC_DIVADC_CLEAR     ~GENMASK(8, 0)
> > > +#define ADIVC_DIVADC_4         0x4
> > > +#define ADFIL                  0x2c
> > > +#define ADCR(n)                        (0x30 + ((n) * 0x4))
> > > +#define ADCR_AD_MASK           GENMASK(11, 0)
> > > +
> > > +#define ADC_MAX_CHANNELS       8
> > > +#define ADC_CHN_MASK           0x7
> > > +#define ADC_TIMEOUT            usecs_to_jiffies(1 * 4)
> > > +
> > > +enum trigger_mode {
> > > +       SW_TRIGGER = 0,
> > > +       SYNC_TRIGGER,
> > > +       ASYNC_TRIGGER,
> > > +};
> >
> > this enum could also be removed [for now] given that only SW_TRIGGER
> > is supported;
> >
> > > +
> > > +struct rzg2l_adc_data {
> > > +       const struct iio_chan_spec *channels;
> > > +       u8 num_channels;
> > > +       u8 trigger;
> > > +};
> > > +
> > > +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;
> > > +       bool adc_disabled; /* protected with mlock mutex from indio_dev */
> >
> > this adc_disabled flag looks a bit weird;
> > it seems to guard against this driver being removed to prevent some reads.
> > technically, this should be protected by IIO core;
> > so the flag itself (or how it is being used) looks like it doesn't do much;
>
> Agreed. If we have paths we are missing, then please let us know.  There may
> well be some as it's been a while since Lars did a bunch of work testing and
> fixing issues around remove races.
>
> >
> > > +       u16 last_val[ADC_MAX_CHANNELS];
> > > +};
> > > +
> > > +static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg)
> > > +{
> > > +       return readl(adc->base + reg);
>
> Hmm. Bit marginal as to whether these are worthwhile. If you really like them
> I guess I don't really mind.
>
will keep this :)

> > > +}
> > > +
> > > +static void rzg2l_adc_writel(struct rzg2l_adc *adc, unsigned int reg, u32 val)
> > > +{
> > > +       writel(val, adc->base + reg);
> > > +}
> > > +
> > > +static int rzg2l_adc_adclk(struct rzg2l_adc *adc, bool prepare)
> > > +{
> > > +       if (prepare)
> > > +               return clk_prepare_enable(adc->adclk);
>
> I'd drop this function and call clk_prepare_enable() / clk_disable_unprepare()
> directly inline.   This just makes the code more confusing but implying that
> the disable_unprepare can fail for example.
>
OK, I will call clk_prepare_enable() / clk_disable_unprepare() directly.

> > > +
> > > +       clk_disable_unprepare(adc->adclk);
> > > +       return 0;
> > > +}
> > > +
> > > +static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on)
> > > +{
> > > +       u32 reg;
> > > +
> > > +       reg = rzg2l_adc_readl(adc, ADM(0));
> > > +       if (on)
> > > +               reg |= ADM0_PWDWNB;
> > > +       else
> > > +               reg &= ~ADM0_PWDWNB;
> > > +       rzg2l_adc_writel(adc, ADM(0), reg);
> > > +       udelay(2);
> > > +}
> > > +
> > > +static void rzg2l_adc_conversion(struct rzg2l_adc *adc, bool start)
> > > +{
> > > +       int timeout = 5;
> > > +       u32 reg;
> > > +
> > > +       /* stop A/D conversion */
> > > +       reg = rzg2l_adc_readl(adc, ADM(0));
> > > +       if (start)
> > > +               reg |= ADM0_ADCE;
> > > +       else
> > > +               reg &= ~ADM0_ADCE;
> > > +       rzg2l_adc_writel(adc, ADM(0), reg);
> > > +
> > > +       if (start)
> > > +               return;
> > > +
> > > +       do {
> > > +               usleep_range(100, 200);
> > > +               reg = rzg2l_adc_readl(adc, ADM(0));
> > > +               timeout--;
> > > +               if (!timeout) {
> > > +                       pr_err("%s stopping ADC timed out\n", __func__);
> > > +                       break;
> > > +               }
> > > +       } while (((reg & ADM0_ADBSY) || (reg & ADM0_ADCE)));
> > > +}
> > > +
> > > +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);
> > > +       u32 reg;
> > > +       int ret;
> > > +       u8 ch;
> > > +
> > > +       switch (mask) {
> > > +       case IIO_CHAN_INFO_RAW:
> > > +               mutex_lock(&indio_dev->mlock);
> >
> > [1]
> > acquiring indio_dev->mlock directly is discouraged;
> > this lock is reserved for IIO core logic and will be moved into an
> > iio_dev_opaque struct eventually;
> > driver state locks should be defined in struct rzg2l_adc and managed
> > by the driver to protect it's own internal state;
>
> I'd also be tempted to factor this read switch block out to a separate
> function.  That way you can either take the new lock outside of the
> function (and then the function can do direct returns in error paths)
> or you can use goto err; and unlock there so that you don't have
> any risk of forgetting an unlock if this code is refactored in future.
>
Agreed will factor this.

> >
> >
> > > +
> > > +               if (adc->adc_disabled) {
> > > +                       mutex_unlock(&indio_dev->mlock);
> > > +                       return -EBUSY;
> > > +               }
> > > +
> > > +               if (rzg2l_adc_readl(adc, ADM(0)) & ADM0_ADBSY) {
> > > +                       mutex_unlock(&indio_dev->mlock);
> > > +                       return -EBUSY;
> > > +               }
> > > +
> > > +               ch = chan->channel & ADC_CHN_MASK;
> > > +               /* SW trigger */
> > > +               reg = rzg2l_adc_readl(adc, ADM(1));
> > > +               reg &= ADM1_EGA_CLEAR;
> > > +               reg &= ~ADM1_BS;
> > > +               reg |= ADM1_MS;
> > > +               reg &= ~ADM1_TRG;
> > > +               rzg2l_adc_writel(adc, ADM(1), reg);
> > > +
> > > +               /* select channel */
> > > +               reg = rzg2l_adc_readl(adc, ADM(2));
> > > +               reg &= ADM2_CHSEL_CLEAR;
> > > +               reg |= BIT(ch);
> > > +               rzg2l_adc_writel(adc, ADM(2), reg);
> > > +
> > > +               reg = rzg2l_adc_readl(adc, ADM(3));
> > > +               reg &= ADM3_ADIL_CLEAR;
> > > +               reg |= ADM3_ADCMP;
> > > +               reg |= ADM3_ADSMP;
> > > +               rzg2l_adc_writel(adc, ADM(3), reg);
> > > +
> > > +               reg = rzg2l_adc_readl(adc, ADIVC);
> > > +               reg &= ADIVC_DIVADC_CLEAR;
> > > +               reg |= ADIVC_DIVADC_4;
> > > +               rzg2l_adc_writel(adc, ADIVC, reg);
> > > +
> > > +               reg = rzg2l_adc_readl(adc, ADINT);
> > > +               reg &= ~ADINT_INTS;
> > > +               reg &= ADINT_CH_CLEAR;
> > > +               reg |= ADINT_CSEEN;
> > > +               reg |= BIT(ch);
> > > +               rzg2l_adc_writel(adc, ADINT, reg);
> > > +
> > > +               rzg2l_adc_pwr(adc, true);
> >
> > should all this clock & power management be done in this read function?
> > it looks like an awful lot just to perform a single read
> > maybe some PM suspend/resume hooks would be a better idea for these;
> >
> > > +
> > > +               ret = rzg2l_adc_adclk(adc, true);
> > > +               if (ret) {
> > > +                       rzg2l_adc_pwr(adc, false);
> > > +                       mutex_unlock(&indio_dev->mlock);
> > > +                       return -EINVAL;
> > > +               }
> > > +
> > > +               reinit_completion(&adc->completion);
> > > +
> > > +               rzg2l_adc_conversion(adc, true);
> > > +
> > > +               if (!wait_for_completion_timeout(&adc->completion, ADC_TIMEOUT)) {
> > > +                       reg &= ADINT_CH_CLEAR;
> > > +                       rzg2l_adc_writel(adc, ADINT, reg);
> > > +                       rzg2l_adc_conversion(adc, false);
> > > +                       rzg2l_adc_adclk(adc, false);
> > > +                       rzg2l_adc_pwr(adc, false);
> > > +                       mutex_unlock(&indio_dev->mlock);
> > > +                       return -ETIMEDOUT;
> > > +               }
> > > +
> > > +               *val = adc->last_val[ch];
> > > +               rzg2l_adc_conversion(adc, false);
> > > +               rzg2l_adc_adclk(adc, false);
> > > +               rzg2l_adc_pwr(adc, false);
> > > +               mutex_unlock(&indio_dev->mlock);
> > > +               return IIO_VAL_INT;
> > > +
> > > +       default:
> > > +               return -EINVAL;
> > > +       }
> > > +}
> > > +
> > > +static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
> > > +{
> > > +       struct rzg2l_adc *adc = (struct rzg2l_adc *)dev_id;
>
> No need to cast a void * to anything explicitly.  The c spec allows this to
> always be done via a simple assignment.
>
Agreed, I will drop the cast.

Cheers,
Prabhakar
> > > +       u8 intst;
> > > +       u32 reg;
> > > +       u8 i;
> > > +
> > > +       reg = rzg2l_adc_readl(adc, ADSTS);
> > > +       if (reg & ADSTS_CSEST) {
> > > +               rzg2l_adc_writel(adc, ADSTS, reg);
> > > +               return IRQ_HANDLED;
> > > +       }
> > > +
> > > +       intst = reg & ADINT_INTST_MASK;
> > > +       if (!intst)
> > > +               return IRQ_HANDLED;
> > > +
> > > +       for (i = 0; i < ADC_MAX_CHANNELS; i++) {
> > > +               if (intst & BIT(i))
> > > +                       adc->last_val[i] = rzg2l_adc_readl(adc, ADCR(i)) & ADCR_AD_MASK;
> > > +       }
> > > +
> > > +       rzg2l_adc_writel(adc, ADSTS, reg);
> > > +
> > > +       complete(&adc->completion);
> > > +
> > > +       return IRQ_HANDLED;
> > > +}
> > > +
> > > +static const struct iio_info rzg2l_adc_iio_info = {
> > > +       .read_raw = rzg2l_adc_read_raw,
> > > +};
> > > +
> > > +static const char * const rzg2l_adc_channel_name[] = {
> > > +       "adc0",
> > > +       "adc1",
> > > +       "adc2",
> > > +       "adc3",
> > > +       "adc4",
> > > +       "adc5",
> > > +       "adc6",
> > > +       "adc7",
> > > +};
> > > +
> > > +static int rzg2l_adc_parse_of(struct platform_device *pdev, struct rzg2l_adc *adc)
> > > +{
> > > +       struct device_node *node = pdev->dev.of_node;
> > > +       struct iio_chan_spec *chan_array;
> > > +       u8 channels[ADC_MAX_CHANNELS];
> > > +       struct rzg2l_adc_data *data;
> > > +       int num_channels;
> > > +       int ret;
> > > +       u8 i;
> > > +
> > > +       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> > > +       if (!data)
> > > +               return -ENOMEM;
> > > +
> > > +       num_channels = of_property_count_u8_elems(node, "renesas-rzg2l,adc-channels");
> > > +       if (num_channels <= 0 || num_channels > ADC_MAX_CHANNELS)
> > > +               return -EINVAL;
> > > +
> > > +       ret = of_property_read_u8_array(node, "renesas-rzg2l,adc-channels",
> > > +                                       channels, num_channels);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array),
> > > +                                 GFP_KERNEL);
> > > +       if (!chan_array)
> > > +               return -ENOMEM;
> > > +
> > > +       for (i = 0; i < num_channels; i++) {
> > > +               chan_array[i].type = IIO_VOLTAGE;
> > > +               chan_array[i].indexed = 1;
> > > +               chan_array[i].channel = channels[i];
> > > +               chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> > > +               chan_array[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
> > > +               chan_array[i].datasheet_name = rzg2l_adc_channel_name[i];
> > > +       }
> > > +
> > > +       ret = of_property_read_u8(node, "renesas-rzg2l,adc-trigger-mode",
> > > +                                 &data->trigger);
> > > +       if (ret)
> > > +               data->trigger = SW_TRIGGER;
> > > +
> > > +       /* we support SW_TRIGGER as of now */
> > > +       if (data->trigger != SW_TRIGGER)
> > > +               return -EINVAL;
> >
> > it would be an idea to remove this data->trigger field and the DT read
> > for this property and add it when it's supported;
> > typically these triggers don't get configured via DT;
> >
> > > +
> > > +       data->num_channels = num_channels;
> > > +       data->channels = chan_array;
> > > +       adc->data = data;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int rzg2l_adc_sw_reset(struct rzg2l_adc *adc)
> > > +{
> > > +       int timeout = 5;
> > > +       u32 val;
> > > +
> > > +       val = rzg2l_adc_readl(adc, ADM(0));
> > > +       val |= ADM0_SRESB;
> > > +       rzg2l_adc_writel(adc, ADM(0), val);
> > > +
> > > +       while (!(rzg2l_adc_readl(adc, ADM(0)) & ADM0_SRESB)) {
> > > +               if (!timeout)
> > > +                       return -EINVAL;
> >
> > maybe -EBUSY is a bit better error code;
> >
> > > +               timeout--;
> > > +               usleep_range(100, 200);
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static int rzg2l_adc_probe(struct platform_device *pdev)
> > > +{
> > > +       struct iio_dev *indio_dev;
> > > +       struct rzg2l_adc *adc;
> > > +       int ret;
> > > +       int irq;
> > > +
> > > +       indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
> > > +       if (!indio_dev) {
> > > +               dev_err(&pdev->dev, "failed allocating iio device\n");
> >
> > this message can be removed;
> > looks like log spam;
> > and if it happens, the system will be in a pretty bad state anyway
> >
> > > +               return -ENOMEM;
> > > +       }
> > > +
> > > +       adc = iio_priv(indio_dev);
> > > +       if (!adc)
> > > +               return -ENOMEM;
> >
> > this check is redundant;
> > if indio_dev is non-NULL then iio_priv() will be good as well;
> >
> > > +
> > > +       ret = rzg2l_adc_parse_of(pdev, adc);
> > > +       if (ret)
> > > +               return -ENOMEM;
> > > +
> > > +       adc->base = devm_platform_ioremap_resource(pdev, 0);
> > > +       if (IS_ERR(adc->base)) {
> > > +               dev_err(&pdev->dev, "missing mem resource");
> >
> > this message can be removed;
> > looks like log-spam
> >
> > > +               return PTR_ERR(adc->base);
> > > +       }
> > > +
> > > +       irq = platform_get_irq(pdev, 0);
> > > +       if (irq < 0) {
> > > +               dev_err(&pdev->dev, "no irq resource\n");
> > > +               return irq;
> > > +       }
> > > +
> > > +       adc->pclk = devm_clk_get(&pdev->dev, "pclk");
> > > +       if (IS_ERR(adc->pclk)) {
> > > +               dev_err(&pdev->dev, "Failed to get pclk");
> > > +               return PTR_ERR(adc->pclk);
> > > +       }
> > > +
> > > +       adc->adclk = devm_clk_get(&pdev->dev, "adclk");
> > > +       if (IS_ERR(adc->adclk)) {
> > > +               dev_err(&pdev->dev, "Failed to get adclk");
> > > +               return PTR_ERR(adc->adclk);
> > > +       }
> > > +
> > > +       adc->adrstn = devm_reset_control_get_exclusive(&pdev->dev, "adrst-n");
> > > +       if (IS_ERR(adc->adrstn)) {
> > > +               dev_err(&pdev->dev, "failed to get adrstn\n");
> > > +               return PTR_ERR(adc->adrstn);
> > > +       }
> > > +
> > > +       adc->presetn = devm_reset_control_get_exclusive(&pdev->dev, "presetn");
> > > +       if (IS_ERR(adc->presetn)) {
> > > +               dev_err(&pdev->dev, "failed to get presetn\n");
> > > +               return PTR_ERR(adc->presetn);
> > > +       }
> > > +
> > > +       ret = reset_control_deassert(adc->adrstn);
> > > +       if (ret)
> > > +               return ret;
> > > +
> > > +       ret = reset_control_deassert(adc->presetn);
> > > +       if (ret)
> > > +               goto assert_adrstn;
> > > +
> > > +       ret = clk_prepare_enable(adc->pclk);
> > > +       if (ret)
> > > +               goto assert_presetn;
> > > +
> > > +       ret = rzg2l_adc_sw_reset(adc);
> > > +       if (ret)
> > > +               goto unprepare_pclk;
> > > +
> > > +       init_completion(&adc->completion);
> > > +
> > > +       platform_set_drvdata(pdev, indio_dev);
> > > +
> > > +       ret = devm_request_irq(&pdev->dev, irq, rzg2l_adc_isr,
> > > +                              0, dev_name(&pdev->dev), adc);
> > > +       if (ret < 0)
> > > +               goto unprepare_pclk;
> > > +
> > > +       adc->adc_disabled = false;
> > > +       indio_dev->name = dev_name(&pdev->dev);
> >
> > indio_dev->name  should be the part-name;
> > since this driver supports a single part, this can be:
> >
> >   indio_dev->name = "rzg2l-adc";
> >
> > > +       indio_dev->dev.parent = &pdev->dev;
> > > +       indio_dev->dev.of_node = pdev->dev.of_node;
> >
> >
> > The 2 assignments above can be removed in the mainline driver.
> > They should be done in devm_iio_device_alloc() and iio_device_register()
> >
> >
> > > +       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;
> > > +
> > > +       ret = iio_device_register(indio_dev);
> > > +       if (ret)
> > > +               goto unprepare_pclk;
> > > +
> > > +       return 0;
> > > +
> > > +unprepare_pclk:
> > > +       clk_disable_unprepare(adc->pclk);
> > > +assert_presetn:
> > > +       reset_control_assert(adc->presetn);
> > > +assert_adrstn:
> > > +       reset_control_assert(adc->adrstn);
> > > +       return ret;
> > > +}
> > > +
> > > +static int rzg2l_adc_remove(struct platform_device *pdev)
> > > +{
> > > +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > > +       struct rzg2l_adc *adc = iio_priv(indio_dev);
> > > +
> > > +       mutex_lock(&indio_dev->mlock);
> > > +       adc->adc_disabled = true;
> > > +       mutex_unlock(&indio_dev->mlock);
> > > +
> > > +       iio_device_unregister(indio_dev);
> > > +
> > > +       clk_disable_unprepare(adc->pclk);
> > > +       reset_control_assert(adc->presetn);
> > > +       reset_control_assert(adc->adrstn);
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +static const struct of_device_id rzg2l_adc_match[] = {
> > > +       {
> > > +               .compatible = "renesas,rzg2l-adc",
> > > +       },
> > > +       {},
> >
> > comma can be removed;
> > since this is a null terminator
> >
> > > +};
> > > +MODULE_DEVICE_TABLE(of, rzg2l_adc_match);
> > > +
> > > +static struct platform_driver rzg2l_adc_driver = {
> > > +       .probe          = rzg2l_adc_probe,
> > > +       .remove         = rzg2l_adc_remove,
> > > +       .driver         = {
> > > +               .name   = "rzg2l-adc",
> > > +               .of_match_table = rzg2l_adc_match,
> > > +       },
> > > +};
> > > +
> > > +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");
> > > --
> > > 2.17.1
> > >
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-14 18:24         ` Lad, Prabhakar
@ 2021-07-15 13:02           ` Jonathan Cameron
  2021-07-16 15:45             ` Lad, Prabhakar
  0 siblings, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2021-07-15 13:02 UTC (permalink / raw)
  To: Lad, Prabhakar
  Cc: Jonathan Cameron, Lad Prabhakar, Geert Uytterhoeven, Rob Herring,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Linux-Renesas, Biju Das, LKML

On Wed, 14 Jul 2021 19:24:27 +0100
"Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote:

> Hi Jonathan,
> 
> On Wed, Jul 14, 2021 at 1:39 PM Jonathan Cameron
> <Jonathan.Cameron@huawei.com> wrote:
> >
> > On Wed, 14 Jul 2021 10:11:49 +0100
> > "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote:
> >  
> > > Hi Jonathan,
> > >
> > > Thank you for the review.
> > >
> > > On Sat, Jul 3, 2021 at 6:17 PM Jonathan Cameron <jic23@kernel.org> wrote:  
> > > >
> > > > On Tue, 29 Jun 2021 23:03:27 +0100
> > > > Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
> > > >  
> > > > > Add binding documentation for Renesas RZ/G2L A/D converter block.
> > > > >
> > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>  
> > > > Hi,
> > > >
> > > > See inline
> > > >
> > > > Jonathan
> > > >  
> > > > > ---
> > > > >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> > > > >  1 file changed, 121 insertions(+)
> > > > >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > >
> > > > > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > > new file mode 100644
> > > > > index 000000000000..db935d6d59eb
> > > > > --- /dev/null
> > > > > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > > @@ -0,0 +1,121 @@
> > > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > > +%YAML 1.2
> > > > > +---
> > > > > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > > +
> > > > > +title: Renesas RZ/G2L ADC
> > > > > +
> > > > > +maintainers:
> > > > > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > > +
> > > > > +description: |
> > > > > +  A/D Converter block is a successive approximation analog-to-digital converter
> > > > > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > > > > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > > > > +  stored in a 32-bit data register corresponding to each channel.
> > > > > +
> > > > > +properties:
> > > > > +  compatible:
> > > > > +    oneOf:
> > > > > +      - items:
> > > > > +          - enum:
> > > > > +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> > > > > +          - const: renesas,rzg2l-adc
> > > > > +
> > > > > +  reg:
> > > > > +    maxItems: 1
> > > > > +
> > > > > +  interrupts:
> > > > > +    maxItems: 1
> > > > > +
> > > > > +  clocks:
> > > > > +    items:
> > > > > +      - description: converter clock
> > > > > +      - description: peripheral clock
> > > > > +
> > > > > +  clock-names:
> > > > > +    items:
> > > > > +      - const: adclk
> > > > > +      - const: pclk
> > > > > +
> > > > > +  power-domains:
> > > > > +    maxItems: 1
> > > > > +
> > > > > +  resets:
> > > > > +    maxItems: 2
> > > > > +
> > > > > +  reset-names:
> > > > > +    items:
> > > > > +      - const: presetn
> > > > > +      - const: adrst-n
> > > > > +
> > > > > +  renesas-rzg2l,adc-trigger-mode:
> > > > > +    $ref: /schemas/types.yaml#/definitions/uint8
> > > > > +    description: Trigger mode for A/D converter
> > > > > +    enum:
> > > > > +      - 0 # Software trigger mode (Defaults)
> > > > > +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> > > > > +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)  
> > > >
> > > > Is this a function of the board in some fashion?  If not it sounds like
> > > > something that should be in control of userspace.  Normally we'd
> > > > do that by having the driver register some iio_triggers and depending
> > > > on which one is selected do the equivalent of what you have here.
> > > >  
> > > Agreed for Asynchronous and Synchronous triggers. WRT Software trigger
> > > should this be registered as a  iio_triggers too or read_raw()
> > > callback (with IIO_CHAN_INFO_RAW case)  should be treated as Software
> > > trigger?
> > >  
> >
> > Normally we'd use an external trigger to provide the software trigger
> > (plus as you say sysfs reads will map to this functionality).
> >
> > Something like the sysfs trigger or the hrtimer one would get used, though
> > also fine to use the dataready trigger from a different device (if you want
> > approximately synced dta.
> >  
> We can live with syfs reads for now for SW triggers. Coming back to HW
> triggers I responded too quickly!. I am now trying to implement a gpio
> based HW trigger i.e. to kick adc conversion start but I couldn't find
> any drivers doing that. I looked at iio-trig-interrupt.c which
> registers irq based triggers, so something similar needs to be
> implemented in the adc driver? If that is the case the gpio has to be
> passed via to DT and use gpio_to_irq to register the handler. Or is it
> that I am missing something here ?

Ok, I'm not really following the usecase for this. Is the thought that you'll
get lower latency / jitter triggering via a gpio rather than using a
bus write to the device (though on an integrated ADC I can't see why that would
be the case)?

If so, then what is actually setting the gpio?  Something is ultimately
acting as the real trigger.  A common model would be an hrtimer trigger
for example.   If you then want to wire the driver up to capture on demand
using the gpio (to reduce latency) that's fine, but the gpio itself is
never a trigger in the sense of an IIO trigger (rather than a trigger
to the ADC itself).  In that case, have the trigger handler set the
the gpio and wait for data capture to finish.  Quite a few drivers
do this as some devices can only start sampling on an external pin being
set.  E.g. adc/ad7606.c 

The iio-trig-interrupt is about using an external interrupt to trigger
a capture initialized by a register write or similar, it's not a direct
hardware capture signal.

Jonathan

> 
> Cheers,
> Prabhakar
> 
> > > > > +    default: 0
> > > > > +
> > > > > +  gpios:
> > > > > +    description:
> > > > > +      ADC_TRG trigger input pin
> > > > > +    maxItems: 1  
> > > > Why is this mode useful?  I'm assuming the gpio write would take a register
> > > > write and the software trigger mode also requires a register write.
> > > >  
> > > Yes gpio write would take a register write.
> > >  
> > > > Normally the reason for a pin like this is to support synchronising with
> > > > external hardware.   If that's the case, we should call that out here.
> > > > often the pin isn't even connected to a gpio in our control.
> > > > (i.e. it's a trigger signal from some other device.)
> > > >  
> > > So just setting the GPIO pin as input should do the trick.  
> >
> > Probably the best plan if you actually care about people writing some
> > trigger up to it that is otherwise invisible to the system.
> >  
> > >  
> > > > > +
> > > > > +  renesas-rzg2l,adc-channels:
> > > > > +    $ref: /schemas/types.yaml#/definitions/uint8-array
> > > > > +    description: Input channels available on platform
> > > > > +    uniqueItems: true
> > > > > +    minItems: 1
> > > > > +    maxItems: 8
> > > > > +    items:
> > > > > +      enum: [0, 1, 2, 3, 4, 5, 6, 7]  
> > > >
> > > > Is this a function of different devices (should have different compatibles)
> > > > or of what is wired up.  If it's what is wired up, then how do you know which  
> > > Its channels which are wired, for example if channels 0-5 are wired up
> > > the board dts would include the property "renesas-rzg2l,adc-channels =
> > > /bits/ 8 <0 1 2 3 4 5>;"
> > >  
> > > > subset of channels are connected?  We have the generic adc channel binding
> > > > in iio/adc/adc.yaml for the case where we only want to expose those channels
> > > > that are wired up.  It uses a node per channel.
> > > >  
> > > Agreed will do that and drop the custom "renesas-rzg2l,adc-channels"  
> >
> > Great,
> >
> > Jonathan
> >  
> > >
> > > Cheers,
> > > Prabhakar  
> > > > > +
> > > > > +  "#io-channel-cells":
> > > > > +    const: 1
> > > > > +
> > > > > +required:
> > > > > +  - compatible
> > > > > +  - reg
> > > > > +  - interrupts
> > > > > +  - clocks
> > > > > +  - clock-names
> > > > > +  - power-domains
> > > > > +  - resets
> > > > > +  - reset-names
> > > > > +  - renesas-rzg2l,adc-channels
> > > > > +  - "#io-channel-cells"
> > > > > +
> > > > > +allOf:
> > > > > +  - if:
> > > > > +      properties:
> > > > > +        renesas-rzg2l,adc-trigger-mode:
> > > > > +          const: 1
> > > > > +    then:
> > > > > +      required:
> > > > > +        - gpios
> > > > > +
> > > > > +additionalProperties: false
> > > > > +
> > > > > +examples:
> > > > > +  - |
> > > > > +    #include <dt-bindings/clock/r9a07g044-cpg.h>
> > > > > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > > > > +
> > > > > +    adc: adc@10059000 {
> > > > > +      compatible = "renesas,r9a07g044-adc", "renesas,rzg2l-adc";
> > > > > +      reg = <0x10059000 0x400>;
> > > > > +      interrupts = <GIC_SPI 347 IRQ_TYPE_EDGE_RISING>;
> > > > > +      clocks = <&cpg CPG_MOD R9A07G044_ADC_ADCLK>,
> > > > > +               <&cpg CPG_MOD R9A07G044_ADC_PCLK>;
> > > > > +      clock-names = "adclk", "pclk";
> > > > > +      power-domains = <&cpg>;
> > > > > +      resets = <&cpg R9A07G044_ADC_PRESETN>,
> > > > > +               <&cpg R9A07G044_ADC_ADRST_N>;
> > > > > +      reset-names = "presetn", "adrst-n";
> > > > > +      #io-channel-cells = <1>;
> > > > > +      renesas-rzg2l,adc-trigger-mode = /bits/ 8 <0>;
> > > > > +      renesas-rzg2l,adc-channels = /bits/ 8 <0 1 2 3 4 5 6>;
> > > > > +    };  
> > > >  
> >  


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter
  2021-07-15 13:02           ` Jonathan Cameron
@ 2021-07-16 15:45             ` Lad, Prabhakar
  0 siblings, 0 replies; 19+ messages in thread
From: Lad, Prabhakar @ 2021-07-16 15:45 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jonathan Cameron, Lad Prabhakar, Geert Uytterhoeven, Rob Herring,
	Lars-Peter Clausen, Philipp Zabel, linux-iio,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Linux-Renesas, Biju Das, LKML

Hi Jonathan,

On Thu, Jul 15, 2021 at 2:02 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Wed, 14 Jul 2021 19:24:27 +0100
> "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote:
>
> > Hi Jonathan,
> >
> > On Wed, Jul 14, 2021 at 1:39 PM Jonathan Cameron
> > <Jonathan.Cameron@huawei.com> wrote:
> > >
> > > On Wed, 14 Jul 2021 10:11:49 +0100
> > > "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote:
> > >
> > > > Hi Jonathan,
> > > >
> > > > Thank you for the review.
> > > >
> > > > On Sat, Jul 3, 2021 at 6:17 PM Jonathan Cameron <jic23@kernel.org> wrote:
> > > > >
> > > > > On Tue, 29 Jun 2021 23:03:27 +0100
> > > > > Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote:
> > > > >
> > > > > > Add binding documentation for Renesas RZ/G2L A/D converter block.
> > > > > >
> > > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > > > Hi,
> > > > >
> > > > > See inline
> > > > >
> > > > > Jonathan
> > > > >
> > > > > > ---
> > > > > >  .../bindings/iio/adc/renesas,rzg2l-adc.yaml   | 121 ++++++++++++++++++
> > > > > >  1 file changed, 121 insertions(+)
> > > > > >  create mode 100644 Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > > >
> > > > > > diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > > > new file mode 100644
> > > > > > index 000000000000..db935d6d59eb
> > > > > > --- /dev/null
> > > > > > +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
> > > > > > @@ -0,0 +1,121 @@
> > > > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > > > +%YAML 1.2
> > > > > > +---
> > > > > > +$id: http://devicetree.org/schemas/iio/adc/renesas,rzg2l-adc.yaml#
> > > > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > > > +
> > > > > > +title: Renesas RZ/G2L ADC
> > > > > > +
> > > > > > +maintainers:
> > > > > > +  - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > > > > > +
> > > > > > +description: |
> > > > > > +  A/D Converter block is a successive approximation analog-to-digital converter
> > > > > > +  with a 12-bit accuracy. Up to eight analog input channels can be selected.
> > > > > > +  Conversions can be performed in single or repeat mode. Result of the ADC is
> > > > > > +  stored in a 32-bit data register corresponding to each channel.
> > > > > > +
> > > > > > +properties:
> > > > > > +  compatible:
> > > > > > +    oneOf:
> > > > > > +      - items:
> > > > > > +          - enum:
> > > > > > +              - renesas,r9a07g044-adc   # RZ/G2{L,LC}
> > > > > > +          - const: renesas,rzg2l-adc
> > > > > > +
> > > > > > +  reg:
> > > > > > +    maxItems: 1
> > > > > > +
> > > > > > +  interrupts:
> > > > > > +    maxItems: 1
> > > > > > +
> > > > > > +  clocks:
> > > > > > +    items:
> > > > > > +      - description: converter clock
> > > > > > +      - description: peripheral clock
> > > > > > +
> > > > > > +  clock-names:
> > > > > > +    items:
> > > > > > +      - const: adclk
> > > > > > +      - const: pclk
> > > > > > +
> > > > > > +  power-domains:
> > > > > > +    maxItems: 1
> > > > > > +
> > > > > > +  resets:
> > > > > > +    maxItems: 2
> > > > > > +
> > > > > > +  reset-names:
> > > > > > +    items:
> > > > > > +      - const: presetn
> > > > > > +      - const: adrst-n
> > > > > > +
> > > > > > +  renesas-rzg2l,adc-trigger-mode:
> > > > > > +    $ref: /schemas/types.yaml#/definitions/uint8
> > > > > > +    description: Trigger mode for A/D converter
> > > > > > +    enum:
> > > > > > +      - 0 # Software trigger mode (Defaults)
> > > > > > +      - 1 # Asynchronous trigger using ADC_TRG trigger input pin
> > > > > > +      - 2 # Synchronous trigger (Trigger from MTU3a/GPT)
> > > > >
> > > > > Is this a function of the board in some fashion?  If not it sounds like
> > > > > something that should be in control of userspace.  Normally we'd
> > > > > do that by having the driver register some iio_triggers and depending
> > > > > on which one is selected do the equivalent of what you have here.
> > > > >
> > > > Agreed for Asynchronous and Synchronous triggers. WRT Software trigger
> > > > should this be registered as a  iio_triggers too or read_raw()
> > > > callback (with IIO_CHAN_INFO_RAW case)  should be treated as Software
> > > > trigger?
> > > >
> > >
> > > Normally we'd use an external trigger to provide the software trigger
> > > (plus as you say sysfs reads will map to this functionality).
> > >
> > > Something like the sysfs trigger or the hrtimer one would get used, though
> > > also fine to use the dataready trigger from a different device (if you want
> > > approximately synced dta.
> > >
> > We can live with syfs reads for now for SW triggers. Coming back to HW
> > triggers I responded too quickly!. I am now trying to implement a gpio
> > based HW trigger i.e. to kick adc conversion start but I couldn't find
> > any drivers doing that. I looked at iio-trig-interrupt.c which
> > registers irq based triggers, so something similar needs to be
> > implemented in the adc driver? If that is the case the gpio has to be
> > passed via to DT and use gpio_to_irq to register the handler. Or is it
> > that I am missing something here ?
>
> Ok, I'm not really following the usecase for this. Is the thought that you'll
> get lower latency / jitter triggering via a gpio rather than using a
> bus write to the device (though on an integrated ADC I can't see why that would
> be the case)?
>
Sorry for the confusion. ADC_TRIG I was referring to automatically
triggers  ADC conversion depending on the edges (whatever its is
configured to). The external triggers can be handled by iio_trigger as
you pointed out earlier!

> If so, then what is actually setting the gpio?  Something is ultimately
> acting as the real trigger.  A common model would be an hrtimer trigger
> for example.   If you then want to wire the driver up to capture on demand
> using the gpio (to reduce latency) that's fine, but the gpio itself is
> never a trigger in the sense of an IIO trigger (rather than a trigger
> to the ADC itself).  In that case, have the trigger handler set the
> the gpio and wait for data capture to finish.  Quite a few drivers
> do this as some devices can only start sampling on an external pin being
> set.  E.g. adc/ad7606.c
>
> The iio-trig-interrupt is about using an external interrupt to trigger
> a capture initialized by a register write or similar, it's not a direct
> hardware capture signal.
>
thanks for the explanation, I realized it now.

Cheers,
Prabhakar

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2021-07-16 15:45 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-29 22:03 [PATCH 0/2] Renesas RZ/G2L ADC driver support Lad Prabhakar
2021-06-29 22:03 ` [PATCH 1/2] dt-bindings: iio: adc: Add binding documentation for Renesas RZ/G2L A/D converter Lad Prabhakar
2021-07-01 14:02   ` Rob Herring
2021-07-01 20:21   ` Rob Herring
2021-07-13 16:00     ` Lad, Prabhakar
2021-07-13 16:32       ` Geert Uytterhoeven
2021-07-13 16:39         ` Lad, Prabhakar
2021-07-03 17:19   ` Jonathan Cameron
2021-07-14  9:11     ` Lad, Prabhakar
2021-07-14 12:39       ` Jonathan Cameron
2021-07-14 18:24         ` Lad, Prabhakar
2021-07-15 13:02           ` Jonathan Cameron
2021-07-16 15:45             ` Lad, Prabhakar
2021-06-29 22:03 ` [PATCH 2/2] iio: adc: Add driver " Lad Prabhakar
2021-07-01 10:28   ` Alexandru Ardelean
2021-07-03 17:41     ` Jonathan Cameron
2021-07-14 21:31       ` Lad, Prabhakar
2021-07-14 21:25     ` Lad, Prabhakar
2021-07-03 17:21 ` [PATCH 0/2] Renesas RZ/G2L ADC driver support Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).