All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2017-01-07 10:16 ` Allen Liu
  0 siblings, 0 replies; 19+ messages in thread
From: Allen Liu @ 2017-01-07 10:16 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, xuejiancheng, kevin.lixu, liurenzhong

Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
The ADC controller is primarily in charge of detecting voltage.

Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
---
 .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
 drivers/iio/adc/Kconfig                            |  10 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
 4 files changed, 369 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
 create mode 100644 drivers/iio/adc/hibvt_lsadc.c

diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
new file mode 100644
index 0000000..fce1ff4
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
@@ -0,0 +1,23 @@
+Hisilicon BVT Low Speed (LS) A/D Converter bindings
+
+Required properties:
+- compatible: should be "hisilicon,<name>-lsadc"
+   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
+
+- reg: physical base address of the controller and length of memory mapped 
+	   region.
+- interrupts: The interrupt number for the ADC device. 
+
+Optional properties:
+- resets: Must contain an entry for each entry in reset-names if need support
+		  this option. See ../../reset/reset.txt for details.
+- reset-names: Must include the name "lsadc-crg".
+
+Example:
+	adc: adc@120e0000 {
+			compatible = "hisilicon,hi3516cv300-lsadc";
+			reg = <0x120e0000 0x1000>;
+			interrupts = <19>;
+			resets = <&crg 0x7c 3>;
+			reset-names = "lsadc-crg";
+	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 99c0514..0443f51 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -225,6 +225,16 @@ config HI8435
 	  This driver can also be built as a module. If so, the module will be
 	  called hi8435.
 
+config HIBVT_LSADC
+	tristate "HIBVT LSADC driver"
+	depends on ARCH_HISI || COMPILE_TEST
+	help
+	  Say yes here to build support for the LSADC found in SoCs from
+	  hisilicon BVT chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hibvt_lsadc.
+
 config INA2XX_ADC
 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
 	depends on I2C && !SENSORS_INA2XX
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..6554d92 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
 obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
 obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
diff --git a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c
new file mode 100644
index 0000000..aaf2024
--- /dev/null
+++ b/drivers/iio/adc/hibvt_lsadc.c
@@ -0,0 +1,335 @@
+/*
+ * Hisilicon BVT Low Speed (LS) A/D Converter
+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+
+/* hisilicon bvt adc registers definitions */
+#define HIBVT_LSADC_CONFIG		0x00
+#define HIBVT_CONFIG_DEGLITCH	BIT(17)
+#define HIBVT_CONFIG_RESET		BIT(15)
+#define HIBVT_CONFIG_POWERDOWN	BIT(14)
+#define HIBVT_CONFIG_MODE		BIT(13)
+#define HIBVT_CONFIG_CHNC		BIT(10)
+#define HIBVT_CONFIG_CHNB		BIT(9)
+#define HIBVT_CONFIG_CHNA		BIT(8)
+
+#define HIBVT_LSADC_TIMESCAN	0x08
+#define HIBVT_LSADC_INTEN		0x10
+#define HIBVT_LSADC_INTSTATUS	0x14
+#define HIBVT_LSADC_INTCLR		0x18
+#define HIBVT_LSADC_START		0x1C
+#define HIBVT_LSADC_STOP		0x20
+#define HIBVT_LSADC_ACTBIT		0x24
+#define HIBVT_LSADC_CHNDATA		0x2C
+
+#define HIBVT_LSADC_CON_EN		(1u << 0)
+#define HIBVT_LSADC_CON_DEN		(0u << 0)
+
+#define HIBVT_LSADC_NUM_BITS_V1	10
+#define HIBVT_LSADC_CHN_MASK_v1	0x7
+
+/* fix clk:3000000, default tscan set 10ms */
+#define HIBVT_LSADC_TSCAN_MS	(10*3000)
+
+#define HIBVT_LSADC_TIMEOUT		msecs_to_jiffies(100)
+
+/* default voltage scale for every channel <mv> */
+static int g_hibvt_lsadc_voltage[] = {
+	3300, 3300, 3300
+};
+
+struct hibvt_lsadc {
+	void __iomem		*regs;
+	struct completion	completion;
+	struct reset_control	*reset;
+	const struct hibvt_lsadc_data	*data;
+	unsigned int		cur_chn;
+	unsigned int		value;
+};
+
+struct hibvt_lsadc_data {
+	int				num_bits;
+	const struct iio_chan_spec	*channels;
+	int				num_channels;
+
+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
+	void (*start_conv)(struct hibvt_lsadc *info);
+	void (*stop_conv)(struct hibvt_lsadc *info);
+};
+
+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
+				    struct iio_chan_spec const *chan,
+				    int *val, int *val2, long mask)
+{
+	struct hibvt_lsadc *info = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+
+		reinit_completion(&info->completion);
+
+		/* Select the channel to be used */
+		info->cur_chn = chan->channel;
+
+		if (info->data->start_conv)
+			info->data->start_conv(info);
+
+		if (!wait_for_completion_timeout(&info->completion,
+							HIBVT_LSADC_TIMEOUT)) {
+			if (info->data->stop_conv)
+				info->data->stop_conv(info);
+			mutex_unlock(&indio_dev->mlock);
+			return -ETIMEDOUT;
+		}
+
+		*val = info->value;
+		mutex_unlock(&indio_dev->mlock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = g_hibvt_lsadc_voltage[chan->channel];
+		*val2 = info->data->num_bits;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
+{
+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
+	int mask;
+
+	mask = readl(info->regs + HIBVT_LSADC_INTSTATUS);
+	if ((mask & HIBVT_LSADC_CHN_MASK_v1) == 0)
+		return IRQ_NONE;
+
+	/* Clear irq */
+	mask &= HIBVT_LSADC_CHN_MASK_v1;
+	if (info->data->clear_irq)
+		info->data->clear_irq(info, mask);
+
+	/* Read value */
+	info->value = readl(info->regs +
+		HIBVT_LSADC_CHNDATA + (info->cur_chn << 2));
+	info->value &= GENMASK(info->data->num_bits - 1, 0);
+
+	/* stop adc */
+	if (info->data->stop_conv)
+		info->data->stop_conv(info);
+
+	complete(&info->completion);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_info hibvt_lsadc_iio_info = {
+	.read_raw = hibvt_lsadc_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+#define HIBVT_LSADC_CHANNEL(_index, _id) {      \
+	.type = IIO_VOLTAGE,                \
+	.indexed = 1,						\
+	.channel = _index,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+			BIT(IIO_CHAN_INFO_SCALE),   \
+	.datasheet_name = _id,              \
+}
+
+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
+	HIBVT_LSADC_CHANNEL(0, "adc0"),
+	HIBVT_LSADC_CHANNEL(1, "adc1"),
+	HIBVT_LSADC_CHANNEL(2, "adc2"),
+};
+
+static void hibvt_lsadc_v1_clear_irq(struct hibvt_lsadc *info, int mask)
+{
+	writel(mask, info->regs + HIBVT_LSADC_INTCLR);
+}
+
+static void hibvt_lsadc_v1_start_conv(struct hibvt_lsadc *info)
+{
+	unsigned int con;
+
+	/* set number bit */
+	con = GENMASK(info->data->num_bits - 1, 0);
+	writel(con, (info->regs + HIBVT_LSADC_ACTBIT));
+
+	/* config */
+	con = readl(info->regs + HIBVT_LSADC_CONFIG);
+	con &= ~HIBVT_CONFIG_RESET;
+	con |= (HIBVT_CONFIG_POWERDOWN | HIBVT_CONFIG_DEGLITCH |
+		HIBVT_CONFIG_MODE);
+	con &= ~(HIBVT_CONFIG_CHNA | HIBVT_CONFIG_CHNB | HIBVT_CONFIG_CHNC);
+	con |= (HIBVT_CONFIG_CHNA << info->cur_chn);
+	writel(con, (info->regs + HIBVT_LSADC_CONFIG));
+
+	/* set timescan */
+	writel(HIBVT_LSADC_TSCAN_MS, (info->regs + HIBVT_LSADC_TIMESCAN));
+
+	/* clear interrupt */
+	writel(HIBVT_LSADC_CHN_MASK_v1, info->regs + HIBVT_LSADC_INTCLR);
+
+	/* enable interrupt */
+	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_INTEN));
+
+	/* start scan */
+	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_START));
+}
+
+static void hibvt_lsadc_v1_stop_conv(struct hibvt_lsadc *info)
+{
+	/* reset the timescan */
+	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_TIMESCAN));
+
+	/* disable interrupt */
+	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_INTEN));
+
+	/* stop scan */
+	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_STOP));
+}
+
+static const struct hibvt_lsadc_data lsadc_data_v1 = {
+	.num_bits = HIBVT_LSADC_NUM_BITS_V1,
+	.channels = hibvt_lsadc_iio_channels,
+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
+
+	.clear_irq = hibvt_lsadc_v1_clear_irq,
+	.start_conv = hibvt_lsadc_v1_start_conv,
+	.stop_conv = hibvt_lsadc_v1_stop_conv,
+};
+
+static const struct of_device_id hibvt_lsadc_match[] = {
+	{
+		.compatible = "hisilicon,hi3516cv300-lsadc",
+		.data = &lsadc_data_v1,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
+
+/* Reset LSADC Controller */
+static void hibvt_lsadc_reset_controller(struct reset_control *reset)
+{
+	reset_control_assert(reset);
+	usleep_range(10, 20);
+	reset_control_deassert(reset);
+}
+
+static int hibvt_lsadc_probe(struct platform_device *pdev)
+{
+	struct hibvt_lsadc *info = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev = NULL;
+	struct resource	*mem;
+	const struct of_device_id *match;
+	int ret;
+	int irq;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+	if (!indio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+	info = iio_priv(indio_dev);
+
+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
+	info->data = match->data;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(info->regs))
+		return PTR_ERR(info->regs);
+
+	/*
+	 * The reset should be an optional property, as it should work
+	 * with old devicetrees as well
+	 */
+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
+	if (IS_ERR(info->reset)) {
+		ret = PTR_ERR(info->reset);
+		if (ret != -ENOENT)
+			return ret;
+
+		dev_dbg(&pdev->dev, "no reset control found\n");
+		info->reset = NULL;
+	}
+
+	init_completion(&info->completion);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
+			       0, dev_name(&pdev->dev), info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
+		return ret;
+	}
+
+	if (info->reset)
+		hibvt_lsadc_reset_controller(info->reset);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	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 = &hibvt_lsadc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	indio_dev->channels = info->data->channels;
+	indio_dev->num_channels = info->data->num_channels;
+
+	ret = devm_iio_device_register(&pdev->dev, indio_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed register iio device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver hibvt_lsadc_driver = {
+	.probe		= hibvt_lsadc_probe,
+	.driver		= {
+		.name	= "hibvt-lsadc",
+		.of_match_table = hibvt_lsadc_match,
+	},
+};
+
+module_platform_driver(hibvt_lsadc_driver);
+
+MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>");
+MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.4

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

* [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2017-01-07 10:16 ` Allen Liu
  0 siblings, 0 replies; 19+ messages in thread
From: Allen Liu @ 2017-01-07 10:16 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, xuejiancheng, kevin.lixu, liurenzhong

Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
The ADC controller is primarily in charge of detecting voltage.

Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
---
 .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
 drivers/iio/adc/Kconfig                            |  10 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
 4 files changed, 369 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
 create mode 100644 drivers/iio/adc/hibvt_lsadc.c

diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
new file mode 100644
index 0000000..fce1ff4
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
@@ -0,0 +1,23 @@
+Hisilicon BVT Low Speed (LS) A/D Converter bindings
+
+Required properties:
+- compatible: should be "hisilicon,<name>-lsadc"
+   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
+
+- reg: physical base address of the controller and length of memory mapped 
+	   region.
+- interrupts: The interrupt number for the ADC device. 
+
+Optional properties:
+- resets: Must contain an entry for each entry in reset-names if need support
+		  this option. See ../../reset/reset.txt for details.
+- reset-names: Must include the name "lsadc-crg".
+
+Example:
+	adc: adc@120e0000 {
+			compatible = "hisilicon,hi3516cv300-lsadc";
+			reg = <0x120e0000 0x1000>;
+			interrupts = <19>;
+			resets = <&crg 0x7c 3>;
+			reset-names = "lsadc-crg";
+	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 99c0514..0443f51 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -225,6 +225,16 @@ config HI8435
 	  This driver can also be built as a module. If so, the module will be
 	  called hi8435.
 
+config HIBVT_LSADC
+	tristate "HIBVT LSADC driver"
+	depends on ARCH_HISI || COMPILE_TEST
+	help
+	  Say yes here to build support for the LSADC found in SoCs from
+	  hisilicon BVT chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hibvt_lsadc.
+
 config INA2XX_ADC
 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
 	depends on I2C && !SENSORS_INA2XX
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..6554d92 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
 obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
 obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
diff --git a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c
new file mode 100644
index 0000000..aaf2024
--- /dev/null
+++ b/drivers/iio/adc/hibvt_lsadc.c
@@ -0,0 +1,335 @@
+/*
+ * Hisilicon BVT Low Speed (LS) A/D Converter
+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+
+/* hisilicon bvt adc registers definitions */
+#define HIBVT_LSADC_CONFIG		0x00
+#define HIBVT_CONFIG_DEGLITCH	BIT(17)
+#define HIBVT_CONFIG_RESET		BIT(15)
+#define HIBVT_CONFIG_POWERDOWN	BIT(14)
+#define HIBVT_CONFIG_MODE		BIT(13)
+#define HIBVT_CONFIG_CHNC		BIT(10)
+#define HIBVT_CONFIG_CHNB		BIT(9)
+#define HIBVT_CONFIG_CHNA		BIT(8)
+
+#define HIBVT_LSADC_TIMESCAN	0x08
+#define HIBVT_LSADC_INTEN		0x10
+#define HIBVT_LSADC_INTSTATUS	0x14
+#define HIBVT_LSADC_INTCLR		0x18
+#define HIBVT_LSADC_START		0x1C
+#define HIBVT_LSADC_STOP		0x20
+#define HIBVT_LSADC_ACTBIT		0x24
+#define HIBVT_LSADC_CHNDATA		0x2C
+
+#define HIBVT_LSADC_CON_EN		(1u << 0)
+#define HIBVT_LSADC_CON_DEN		(0u << 0)
+
+#define HIBVT_LSADC_NUM_BITS_V1	10
+#define HIBVT_LSADC_CHN_MASK_v1	0x7
+
+/* fix clk:3000000, default tscan set 10ms */
+#define HIBVT_LSADC_TSCAN_MS	(10*3000)
+
+#define HIBVT_LSADC_TIMEOUT		msecs_to_jiffies(100)
+
+/* default voltage scale for every channel <mv> */
+static int g_hibvt_lsadc_voltage[] = {
+	3300, 3300, 3300
+};
+
+struct hibvt_lsadc {
+	void __iomem		*regs;
+	struct completion	completion;
+	struct reset_control	*reset;
+	const struct hibvt_lsadc_data	*data;
+	unsigned int		cur_chn;
+	unsigned int		value;
+};
+
+struct hibvt_lsadc_data {
+	int				num_bits;
+	const struct iio_chan_spec	*channels;
+	int				num_channels;
+
+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
+	void (*start_conv)(struct hibvt_lsadc *info);
+	void (*stop_conv)(struct hibvt_lsadc *info);
+};
+
+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
+				    struct iio_chan_spec const *chan,
+				    int *val, int *val2, long mask)
+{
+	struct hibvt_lsadc *info = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+
+		reinit_completion(&info->completion);
+
+		/* Select the channel to be used */
+		info->cur_chn = chan->channel;
+
+		if (info->data->start_conv)
+			info->data->start_conv(info);
+
+		if (!wait_for_completion_timeout(&info->completion,
+							HIBVT_LSADC_TIMEOUT)) {
+			if (info->data->stop_conv)
+				info->data->stop_conv(info);
+			mutex_unlock(&indio_dev->mlock);
+			return -ETIMEDOUT;
+		}
+
+		*val = info->value;
+		mutex_unlock(&indio_dev->mlock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = g_hibvt_lsadc_voltage[chan->channel];
+		*val2 = info->data->num_bits;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
+{
+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
+	int mask;
+
+	mask = readl(info->regs + HIBVT_LSADC_INTSTATUS);
+	if ((mask & HIBVT_LSADC_CHN_MASK_v1) == 0)
+		return IRQ_NONE;
+
+	/* Clear irq */
+	mask &= HIBVT_LSADC_CHN_MASK_v1;
+	if (info->data->clear_irq)
+		info->data->clear_irq(info, mask);
+
+	/* Read value */
+	info->value = readl(info->regs +
+		HIBVT_LSADC_CHNDATA + (info->cur_chn << 2));
+	info->value &= GENMASK(info->data->num_bits - 1, 0);
+
+	/* stop adc */
+	if (info->data->stop_conv)
+		info->data->stop_conv(info);
+
+	complete(&info->completion);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_info hibvt_lsadc_iio_info = {
+	.read_raw = hibvt_lsadc_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+#define HIBVT_LSADC_CHANNEL(_index, _id) {      \
+	.type = IIO_VOLTAGE,                \
+	.indexed = 1,						\
+	.channel = _index,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+			BIT(IIO_CHAN_INFO_SCALE),   \
+	.datasheet_name = _id,              \
+}
+
+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
+	HIBVT_LSADC_CHANNEL(0, "adc0"),
+	HIBVT_LSADC_CHANNEL(1, "adc1"),
+	HIBVT_LSADC_CHANNEL(2, "adc2"),
+};
+
+static void hibvt_lsadc_v1_clear_irq(struct hibvt_lsadc *info, int mask)
+{
+	writel(mask, info->regs + HIBVT_LSADC_INTCLR);
+}
+
+static void hibvt_lsadc_v1_start_conv(struct hibvt_lsadc *info)
+{
+	unsigned int con;
+
+	/* set number bit */
+	con = GENMASK(info->data->num_bits - 1, 0);
+	writel(con, (info->regs + HIBVT_LSADC_ACTBIT));
+
+	/* config */
+	con = readl(info->regs + HIBVT_LSADC_CONFIG);
+	con &= ~HIBVT_CONFIG_RESET;
+	con |= (HIBVT_CONFIG_POWERDOWN | HIBVT_CONFIG_DEGLITCH |
+		HIBVT_CONFIG_MODE);
+	con &= ~(HIBVT_CONFIG_CHNA | HIBVT_CONFIG_CHNB | HIBVT_CONFIG_CHNC);
+	con |= (HIBVT_CONFIG_CHNA << info->cur_chn);
+	writel(con, (info->regs + HIBVT_LSADC_CONFIG));
+
+	/* set timescan */
+	writel(HIBVT_LSADC_TSCAN_MS, (info->regs + HIBVT_LSADC_TIMESCAN));
+
+	/* clear interrupt */
+	writel(HIBVT_LSADC_CHN_MASK_v1, info->regs + HIBVT_LSADC_INTCLR);
+
+	/* enable interrupt */
+	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_INTEN));
+
+	/* start scan */
+	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_START));
+}
+
+static void hibvt_lsadc_v1_stop_conv(struct hibvt_lsadc *info)
+{
+	/* reset the timescan */
+	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_TIMESCAN));
+
+	/* disable interrupt */
+	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_INTEN));
+
+	/* stop scan */
+	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_STOP));
+}
+
+static const struct hibvt_lsadc_data lsadc_data_v1 = {
+	.num_bits = HIBVT_LSADC_NUM_BITS_V1,
+	.channels = hibvt_lsadc_iio_channels,
+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
+
+	.clear_irq = hibvt_lsadc_v1_clear_irq,
+	.start_conv = hibvt_lsadc_v1_start_conv,
+	.stop_conv = hibvt_lsadc_v1_stop_conv,
+};
+
+static const struct of_device_id hibvt_lsadc_match[] = {
+	{
+		.compatible = "hisilicon,hi3516cv300-lsadc",
+		.data = &lsadc_data_v1,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
+
+/* Reset LSADC Controller */
+static void hibvt_lsadc_reset_controller(struct reset_control *reset)
+{
+	reset_control_assert(reset);
+	usleep_range(10, 20);
+	reset_control_deassert(reset);
+}
+
+static int hibvt_lsadc_probe(struct platform_device *pdev)
+{
+	struct hibvt_lsadc *info = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev = NULL;
+	struct resource	*mem;
+	const struct of_device_id *match;
+	int ret;
+	int irq;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+	if (!indio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+	info = iio_priv(indio_dev);
+
+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
+	info->data = match->data;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(info->regs))
+		return PTR_ERR(info->regs);
+
+	/*
+	 * The reset should be an optional property, as it should work
+	 * with old devicetrees as well
+	 */
+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
+	if (IS_ERR(info->reset)) {
+		ret = PTR_ERR(info->reset);
+		if (ret != -ENOENT)
+			return ret;
+
+		dev_dbg(&pdev->dev, "no reset control found\n");
+		info->reset = NULL;
+	}
+
+	init_completion(&info->completion);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
+			       0, dev_name(&pdev->dev), info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
+		return ret;
+	}
+
+	if (info->reset)
+		hibvt_lsadc_reset_controller(info->reset);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	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 = &hibvt_lsadc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	indio_dev->channels = info->data->channels;
+	indio_dev->num_channels = info->data->num_channels;
+
+	ret = devm_iio_device_register(&pdev->dev, indio_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed register iio device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver hibvt_lsadc_driver = {
+	.probe		= hibvt_lsadc_probe,
+	.driver		= {
+		.name	= "hibvt-lsadc",
+		.of_match_table = hibvt_lsadc_match,
+	},
+};
+
+module_platform_driver(hibvt_lsadc_driver);
+
+MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>");
+MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.4

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

* Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
  2017-01-07 10:16 ` Allen Liu
  (?)
@ 2017-01-07 17:51 ` Jonathan Cameron
  2017-01-10  5:35   ` Rob Herring
  2017-02-06 12:19     ` liurenzhong
  -1 siblings, 2 replies; 19+ messages in thread
From: Jonathan Cameron @ 2017-01-07 17:51 UTC (permalink / raw)
  To: Allen Liu, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, xuejiancheng, kevin.lixu

On 07/01/17 05:16, Allen Liu wrote:
> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
> The ADC controller is primarily in charge of detecting voltage.
> 
> Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
> Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
Hi Allen,

One quick submission process note first.  It is very important to clearly identify new
versions of a patch and what changes have occurred since the previous posting.

So the email title should have been [PATCH V2] adc...

Also, below the --- please add a brief change log.

The driver is coming together nicely.  A few minor points inline.

Jonathan
> ---
>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
>  drivers/iio/adc/Kconfig                            |  10 +
>  drivers/iio/adc/Makefile                           |   1 +
>  drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
>  4 files changed, 369 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> new file mode 100644
> index 0000000..fce1ff4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> @@ -0,0 +1,23 @@
> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
> +
> +Required properties:
> +- compatible: should be "hisilicon,<name>-lsadc"
> +   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
> +
> +- reg: physical base address of the controller and length of memory mapped 
> +	   region.
> +- interrupts: The interrupt number for the ADC device.
Ideally refer to the standard interrupt binding document.
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt

> +
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +		  this option. See ../../reset/reset.txt for details.
Don't use a relative path in a binding document. It's far too likely to
be broken by a reorganization of the docs and cannot be grepped for.
> +- reset-names: Must include the name "lsadc-crg".
> +
> +Example:
> +	adc: adc@120e0000 {
> +			compatible = "hisilicon,hi3516cv300-lsadc";
> +			reg = <0x120e0000 0x1000>;
> +			interrupts = <19>;
> +			resets = <&crg 0x7c 3>;
> +			reset-names = "lsadc-crg";
> +	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 99c0514..0443f51 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -225,6 +225,16 @@ config HI8435
>  	  This driver can also be built as a module. If so, the module will be
>  	  called hi8435.
>  
> +config HIBVT_LSADC
> +	tristate "HIBVT LSADC driver"
> +	depends on ARCH_HISI || COMPILE_TEST
> +	help
> +	  Say yes here to build support for the LSADC found in SoCs from
> +	  hisilicon BVT chip.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called hibvt_lsadc.
> +
>  config INA2XX_ADC
>  	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
>  	depends on I2C && !SENSORS_INA2XX
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..6554d92 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>  obj-$(CONFIG_HI8435) += hi8435.o
> +obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
>  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
>  obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
> diff --git a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c
> new file mode 100644
> index 0000000..aaf2024
> --- /dev/null
> +++ b/drivers/iio/adc/hibvt_lsadc.c
> @@ -0,0 +1,335 @@
> +/*
> + * Hisilicon BVT Low Speed (LS) A/D Converter
> + * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/iio/iio.h>
> +
> +/* hisilicon bvt adc registers definitions */
> +#define HIBVT_LSADC_CONFIG		0x00
> +#define HIBVT_CONFIG_DEGLITCH	BIT(17)
> +#define HIBVT_CONFIG_RESET		BIT(15)
> +#define HIBVT_CONFIG_POWERDOWN	BIT(14)
> +#define HIBVT_CONFIG_MODE		BIT(13)
> +#define HIBVT_CONFIG_CHNC		BIT(10)
> +#define HIBVT_CONFIG_CHNB		BIT(9)
> +#define HIBVT_CONFIG_CHNA		BIT(8)
> +
> +#define HIBVT_LSADC_TIMESCAN	0x08
> +#define HIBVT_LSADC_INTEN		0x10
> +#define HIBVT_LSADC_INTSTATUS	0x14
> +#define HIBVT_LSADC_INTCLR		0x18
> +#define HIBVT_LSADC_START		0x1C
> +#define HIBVT_LSADC_STOP		0x20
> +#define HIBVT_LSADC_ACTBIT		0x24
> +#define HIBVT_LSADC_CHNDATA		0x2C
> +
> +#define HIBVT_LSADC_CON_EN		(1u << 0)
> +#define HIBVT_LSADC_CON_DEN		(0u << 0)
> +
> +#define HIBVT_LSADC_NUM_BITS_V1	10
> +#define HIBVT_LSADC_CHN_MASK_v1	0x7
> +
> +/* fix clk:3000000, default tscan set 10ms */
> +#define HIBVT_LSADC_TSCAN_MS	(10*3000)
> +
> +#define HIBVT_LSADC_TIMEOUT		msecs_to_jiffies(100)
> +
> +/* default voltage scale for every channel <mv> */
> +static int g_hibvt_lsadc_voltage[] = {
> +	3300, 3300, 3300
Is default due to an external reference voltage or is there an internal
regulator?  If it is external it should really be described using the
regulator framework.

Const? 
> +};
> +
> +struct hibvt_lsadc {
> +	void __iomem		*regs;
> +	struct completion	completion;
> +	struct reset_control	*reset;
> +	const struct hibvt_lsadc_data	*data;
> +	unsigned int		cur_chn;
> +	unsigned int		value;
> +};
> +
> +struct hibvt_lsadc_data {
> +	int				num_bits;
> +	const struct iio_chan_spec	*channels;
> +	int				num_channels;
> +
> +	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
> +	void (*start_conv)(struct hibvt_lsadc *info);
> +	void (*stop_conv)(struct hibvt_lsadc *info);
> +};
> +
> +static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec const *chan,
> +				    int *val, int *val2, long mask)
> +{
> +	struct hibvt_lsadc *info = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&indio_dev->mlock);
> +
> +		reinit_completion(&info->completion);
> +
> +		/* Select the channel to be used */
> +		info->cur_chn = chan->channel;
> +
> +		if (info->data->start_conv)
> +			info->data->start_conv(info);
> +
> +		if (!wait_for_completion_timeout(&info->completion,
> +							HIBVT_LSADC_TIMEOUT)) {
> +			if (info->data->stop_conv)
> +				info->data->stop_conv(info);
> +			mutex_unlock(&indio_dev->mlock);
> +			return -ETIMEDOUT;
> +		}
> +
> +		*val = info->value;
> +		mutex_unlock(&indio_dev->mlock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = g_hibvt_lsadc_voltage[chan->channel];
> +		*val2 = info->data->num_bits;
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
> +{
> +	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
> +	int mask;
> +
> +	mask = readl(info->regs + HIBVT_LSADC_INTSTATUS);
> +	if ((mask & HIBVT_LSADC_CHN_MASK_v1) == 0)
> +		return IRQ_NONE;
> +
> +	/* Clear irq */
> +	mask &= HIBVT_LSADC_CHN_MASK_v1;
> +	if (info->data->clear_irq)
> +		info->data->clear_irq(info, mask);
> +
> +	/* Read value */
> +	info->value = readl(info->regs +
> +		HIBVT_LSADC_CHNDATA + (info->cur_chn << 2));
> +	info->value &= GENMASK(info->data->num_bits - 1, 0);
> +
> +	/* stop adc */
> +	if (info->data->stop_conv)
> +		info->data->stop_conv(info);
> +
> +	complete(&info->completion);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct iio_info hibvt_lsadc_iio_info = {
> +	.read_raw = hibvt_lsadc_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +#define HIBVT_LSADC_CHANNEL(_index, _id) {      \
> +	.type = IIO_VOLTAGE,                \
> +	.indexed = 1,						\
> +	.channel = _index,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
> +			BIT(IIO_CHAN_INFO_SCALE),   \
> +	.datasheet_name = _id,              \
> +}
> +
> +static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
> +	HIBVT_LSADC_CHANNEL(0, "adc0"),
> +	HIBVT_LSADC_CHANNEL(1, "adc1"),
> +	HIBVT_LSADC_CHANNEL(2, "adc2"),
> +};
> +
> +static void hibvt_lsadc_v1_clear_irq(struct hibvt_lsadc *info, int mask)
> +{
> +	writel(mask, info->regs + HIBVT_LSADC_INTCLR);
> +}
> +
> +static void hibvt_lsadc_v1_start_conv(struct hibvt_lsadc *info)
> +{
> +	unsigned int con;
> +
> +	/* set number bit */
set number of bits?
> +	con = GENMASK(info->data->num_bits - 1, 0);
> +	writel(con, (info->regs + HIBVT_LSADC_ACTBIT));
> +
> +	/* config */
> +	con = readl(info->regs + HIBVT_LSADC_CONFIG);
> +	con &= ~HIBVT_CONFIG_RESET;
> +	con |= (HIBVT_CONFIG_POWERDOWN | HIBVT_CONFIG_DEGLITCH |
> +		HIBVT_CONFIG_MODE);
> +	con &= ~(HIBVT_CONFIG_CHNA | HIBVT_CONFIG_CHNB | HIBVT_CONFIG_CHNC);
> +	con |= (HIBVT_CONFIG_CHNA << info->cur_chn);
> +	writel(con, (info->regs + HIBVT_LSADC_CONFIG));
> +
> +	/* set timescan */
> +	writel(HIBVT_LSADC_TSCAN_MS, (info->regs + HIBVT_LSADC_TIMESCAN));
> +
> +	/* clear interrupt */
> +	writel(HIBVT_LSADC_CHN_MASK_v1, info->regs + HIBVT_LSADC_INTCLR);
> +
> +	/* enable interrupt */
> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_INTEN));
> +
> +	/* start scan */
> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_START));
> +}
> +
> +static void hibvt_lsadc_v1_stop_conv(struct hibvt_lsadc *info)
> +{
> +	/* reset the timescan */
This isn't a particularly common pice of terminology, perhaps a short
description here of what timescan is and why we should reset it would
make the code easier to follow.

> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_TIMESCAN));
> +
> +	/* disable interrupt */
> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_INTEN));
> +
> +	/* stop scan */
> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_STOP));
> +}
> +
> +static const struct hibvt_lsadc_data lsadc_data_v1 = {
> +	.num_bits = HIBVT_LSADC_NUM_BITS_V1,
> +	.channels = hibvt_lsadc_iio_channels,
> +	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
> +
> +	.clear_irq = hibvt_lsadc_v1_clear_irq,
> +	.start_conv = hibvt_lsadc_v1_start_conv,
> +	.stop_conv = hibvt_lsadc_v1_stop_conv,
> +};
> +
> +static const struct of_device_id hibvt_lsadc_match[] = {
> +	{
> +		.compatible = "hisilicon,hi3516cv300-lsadc",
> +		.data = &lsadc_data_v1,
The usual convention is to only introduce 'variant' type data as a
precursor patch to a series including the support of new parts.

It is acceptable to post a version with this in if you are shortly to submit
the follow up that adds other device support.  If you are doing this,
please put a note in the patch description to that effect.  Note that if
the additional support doesn't turn up, the driver may we get 'simplified'
by someone else.

I'd also generally expect to see this match table further down - directly
above where it is used.  Makes for ever so slightly easier reviewing!
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
> +
> +/* Reset LSADC Controller */
> +static void hibvt_lsadc_reset_controller(struct reset_control *reset)
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
> +static int hibvt_lsadc_probe(struct platform_device *pdev)
> +{
> +	struct hibvt_lsadc *info = NULL;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev = NULL;
> +	struct resource	*mem;
> +	const struct of_device_id *match;
> +	int ret;
> +	int irq;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> +	if (!indio_dev) {
> +		dev_err(&pdev->dev, "failed allocating iio device\n");
> +		return -ENOMEM;
> +	}
> +	info = iio_priv(indio_dev);
> +
> +	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
> +	info->data = match->data;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	info->regs = devm_ioremap_resource(&pdev->dev, mem);
> +	if (IS_ERR(info->regs))
> +		return PTR_ERR(info->regs);
> +
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
> +	init_completion(&info->completion);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq resource?\n");
> +		return irq;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
> +			       0, dev_name(&pdev->dev), info);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
> +		return ret;
> +	}
> +
> +	if (info->reset)
> +		hibvt_lsadc_reset_controller(info->reset);
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	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 = &hibvt_lsadc_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	indio_dev->channels = info->data->channels;
> +	indio_dev->num_channels = info->data->num_channels;
> +
> +	ret = devm_iio_device_register(&pdev->dev, indio_dev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed register iio device\n");
> +		return ret;
Drop this return ret and just return ret instead of the return 0 below.
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver hibvt_lsadc_driver = {
> +	.probe		= hibvt_lsadc_probe,
> +	.driver		= {
> +		.name	= "hibvt-lsadc",
> +		.of_match_table = hibvt_lsadc_match,
> +	},
> +};
> +
> +module_platform_driver(hibvt_lsadc_driver);
> +
> +MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>");
> +MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
  2017-01-07 17:51 ` Jonathan Cameron
@ 2017-01-10  5:35   ` Rob Herring
  2017-02-06 12:19     ` liurenzhong
  1 sibling, 0 replies; 19+ messages in thread
From: Rob Herring @ 2017-01-10  5:35 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Allen Liu, knaack.h, lars, pmeerw, mark.rutland, akinobu.mita,
	ludovic.desroches, krzk, vilhelm.gray, ksenija.stanojevic,
	zhiyong.tao, daniel.baluta, leonard.crestez, ray.jui,
	raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, xuejiancheng, kevin.lixu

On Sat, Jan 07, 2017 at 12:51:31PM -0500, Jonathan Cameron wrote:
> On 07/01/17 05:16, Allen Liu wrote:
> > Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
> > The ADC controller is primarily in charge of detecting voltage.
> > 
> > Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
> > Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
> Hi Allen,
> 
> One quick submission process note first.  It is very important to clearly identify new
> versions of a patch and what changes have occurred since the previous posting.
> 
> So the email title should have been [PATCH V2] adc...
> 
> Also, below the --- please add a brief change log.
> 
> The driver is coming together nicely.  A few minor points inline.
> 
> Jonathan
> > ---
> >  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
> >  drivers/iio/adc/Kconfig                            |  10 +
> >  drivers/iio/adc/Makefile                           |   1 +
> >  drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
> >  4 files changed, 369 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> >  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> > new file mode 100644
> > index 0000000..fce1ff4
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> > @@ -0,0 +1,23 @@
> > +Hisilicon BVT Low Speed (LS) A/D Converter bindings
> > +
> > +Required properties:
> > +- compatible: should be "hisilicon,<name>-lsadc"
> > +   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
> > +
> > +- reg: physical base address of the controller and length of memory mapped 
> > +	   region.
> > +- interrupts: The interrupt number for the ADC device.
> Ideally refer to the standard interrupt binding document.
> Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> 
> > +
> > +Optional properties:
> > +- resets: Must contain an entry for each entry in reset-names if need support
> > +		  this option. See ../../reset/reset.txt for details.
> Don't use a relative path in a binding document. It's far too likely to
> be broken by a reorganization of the docs and cannot be grepped for.

However, in the filtered DT tree, the base path is already different. I 
haven't looked but I'm sure we have a mixture of different forms.

My preference would be just "reset/reset.txt" or ".../reset/reset.txt". 
We generally try to avoid bindings referring to other kernel docs, so 
this should be sufficient.

Rob

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

* 答复: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
  2017-01-07 17:51 ` Jonathan Cameron
@ 2017-02-06 12:19     ` liurenzhong
  2017-02-06 12:19     ` liurenzhong
  1 sibling, 0 replies; 19+ messages in thread
From: liurenzhong @ 2017-02-06 12:19 UTC (permalink / raw)
  To: Jonathan Cameron, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, Xuejiancheng, Lixu (kevin)

Hi Jonathan,

Thanks for your suggestion,  I'm sorry to reply after a long time . This is some problems ask for your advice:

1, It's nice to use just "reset/reset.txt" or ".../reset/reset.txt" from Rob's suggestion, but in the filtered DT tree, "../reset/reset.txt" is used more times, which one we should get, "../" or ".../"? 

2, The ADC on hisilicon BVT socs can work in single scanning mode or continuous scanning mode. The single mode get conversion value one time after start the configure, while the continuous scanning mode will get conversion value each scan time after start the configure while stopping the adc configure. For more expansibility,  the ADC driver use the continuous scanning mode and  stop the adc configure after get one time conversion value.  Is it necessary to change our driver to single scanning mode or just add a short description?

3, This drvier is only support ADC IF found on hi3516cv300 and hi3519v101 now , and future SoCs from Hisilicon BVT would get some changes on channel number or getting data ways and so on,  so the driver use "V1" on match table and add other version while any change happens.  Which match table can we provide and  is't OK like this?
> +static const struct of_device_id hibvt_lsadc_match[] = {
> +	{
> +		.compatible = "hisilicon,hi3516cv300-lsadc",
> +		.data = &lsadc_data_v1,
> +	},
> +	{
> +		.compatible = "hisilicon,hi3519v101-lsadc",
> +		.data = &lsadc_data_v1,
> +	},
> +	{},
> +};

Awaiting for your reply Thank you very much!

Best regards
/Allen

-----邮件原件-----
发件人: Jonathan Cameron [mailto:jic23@kernel.org] 
发送时间: 2017年1月8日 1:52
收件人: liurenzhong <liurenzhong@hisilicon.com>; knaack.h@gmx.de; lars@metafoo.de; pmeerw@pmeerw.net; robh+dt@kernel.org; mark.rutland@arm.com
抄送: akinobu.mita@gmail.com; ludovic.desroches@atmel.com; krzk@kernel.org; vilhelm.gray@gmail.com; ksenija.stanojevic@gmail.com; zhiyong.tao@mediatek.com; daniel.baluta@intel.com; leonard.crestez@intel.com; ray.jui@broadcom.com; raveendra.padasalagi@broadcom.com; mranostay@gmail.com; amsfield22@gmail.com; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Xuejiancheng <xuejiancheng@hisilicon.com>; Lixu (kevin) <kevin.lixu@hisilicon.com>
主题: Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs

On 07/01/17 05:16, Allen Liu wrote:
> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
> The ADC controller is primarily in charge of detecting voltage.
> 
> Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
> Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
Hi Allen,

One quick submission process note first.  It is very important to clearly identify new versions of a patch and what changes have occurred since the previous posting.

So the email title should have been [PATCH V2] adc...

Also, below the --- please add a brief change log.

The driver is coming together nicely.  A few minor points inline.

Jonathan
> ---
>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
>  drivers/iio/adc/Kconfig                            |  10 +
>  drivers/iio/adc/Makefile                           |   1 +
>  drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
>  4 files changed, 369 insertions(+)
>  create mode 100644 
> Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt 
> b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> new file mode 100644
> index 0000000..fce1ff4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> @@ -0,0 +1,23 @@
> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
> +
> +Required properties:
> +- compatible: should be "hisilicon,<name>-lsadc"
> +   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
> +
> +- reg: physical base address of the controller and length of memory mapped 
> +	   region.
> +- interrupts: The interrupt number for the ADC device.
Ideally refer to the standard interrupt binding document.
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt

> +
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +		  this option. See ../../reset/reset.txt for details.
Don't use a relative path in a binding document. It's far too likely to be broken by a reorganization of the docs and cannot be grepped for.
> +- reset-names: Must include the name "lsadc-crg".
> +
> +Example:
> +	adc: adc@120e0000 {
> +			compatible = "hisilicon,hi3516cv300-lsadc";
> +			reg = <0x120e0000 0x1000>;
> +			interrupts = <19>;
> +			resets = <&crg 0x7c 3>;
> +			reset-names = "lsadc-crg";
> +	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 
> 99c0514..0443f51 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -225,6 +225,16 @@ config HI8435
>  	  This driver can also be built as a module. If so, the module will be
>  	  called hi8435.
>  
> +config HIBVT_LSADC
> +	tristate "HIBVT LSADC driver"
> +	depends on ARCH_HISI || COMPILE_TEST
> +	help
> +	  Say yes here to build support for the LSADC found in SoCs from
> +	  hisilicon BVT chip.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called hibvt_lsadc.
> +
>  config INA2XX_ADC
>  	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
>  	depends on I2C && !SENSORS_INA2XX
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 
> 7a40c04..6554d92 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>  obj-$(CONFIG_HI8435) += hi8435.o
> +obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
>  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
>  obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git 
> a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c new 
> file mode 100644 index 0000000..aaf2024
> --- /dev/null
> +++ b/drivers/iio/adc/hibvt_lsadc.c
> @@ -0,0 +1,335 @@
> +/*
> + * Hisilicon BVT Low Speed (LS) A/D Converter
> + * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or 
> +modify
> + * it under the terms of the GNU General Public License as published 
> +by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h>
> +
> +/* hisilicon bvt adc registers definitions */
> +#define HIBVT_LSADC_CONFIG		0x00
> +#define HIBVT_CONFIG_DEGLITCH	BIT(17)
> +#define HIBVT_CONFIG_RESET		BIT(15)
> +#define HIBVT_CONFIG_POWERDOWN	BIT(14)
> +#define HIBVT_CONFIG_MODE		BIT(13)
> +#define HIBVT_CONFIG_CHNC		BIT(10)
> +#define HIBVT_CONFIG_CHNB		BIT(9)
> +#define HIBVT_CONFIG_CHNA		BIT(8)
> +
> +#define HIBVT_LSADC_TIMESCAN	0x08
> +#define HIBVT_LSADC_INTEN		0x10
> +#define HIBVT_LSADC_INTSTATUS	0x14
> +#define HIBVT_LSADC_INTCLR		0x18
> +#define HIBVT_LSADC_START		0x1C
> +#define HIBVT_LSADC_STOP		0x20
> +#define HIBVT_LSADC_ACTBIT		0x24
> +#define HIBVT_LSADC_CHNDATA		0x2C
> +
> +#define HIBVT_LSADC_CON_EN		(1u << 0)
> +#define HIBVT_LSADC_CON_DEN		(0u << 0)
> +
> +#define HIBVT_LSADC_NUM_BITS_V1	10
> +#define HIBVT_LSADC_CHN_MASK_v1	0x7
> +
> +/* fix clk:3000000, default tscan set 10ms */
> +#define HIBVT_LSADC_TSCAN_MS	(10*3000)
> +
> +#define HIBVT_LSADC_TIMEOUT		msecs_to_jiffies(100)
> +
> +/* default voltage scale for every channel <mv> */ static int 
> +g_hibvt_lsadc_voltage[] = {
> +	3300, 3300, 3300
Is default due to an external reference voltage or is there an internal regulator?  If it is external it should really be described using the regulator framework.

Const? 
> +};
> +
> +struct hibvt_lsadc {
> +	void __iomem		*regs;
> +	struct completion	completion;
> +	struct reset_control	*reset;
> +	const struct hibvt_lsadc_data	*data;
> +	unsigned int		cur_chn;
> +	unsigned int		value;
> +};
> +
> +struct hibvt_lsadc_data {
> +	int				num_bits;
> +	const struct iio_chan_spec	*channels;
> +	int				num_channels;
> +
> +	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
> +	void (*start_conv)(struct hibvt_lsadc *info);
> +	void (*stop_conv)(struct hibvt_lsadc *info); };
> +
> +static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec const *chan,
> +				    int *val, int *val2, long mask) {
> +	struct hibvt_lsadc *info = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&indio_dev->mlock);
> +
> +		reinit_completion(&info->completion);
> +
> +		/* Select the channel to be used */
> +		info->cur_chn = chan->channel;
> +
> +		if (info->data->start_conv)
> +			info->data->start_conv(info);
> +
> +		if (!wait_for_completion_timeout(&info->completion,
> +							HIBVT_LSADC_TIMEOUT)) {
> +			if (info->data->stop_conv)
> +				info->data->stop_conv(info);
> +			mutex_unlock(&indio_dev->mlock);
> +			return -ETIMEDOUT;
> +		}
> +
> +		*val = info->value;
> +		mutex_unlock(&indio_dev->mlock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = g_hibvt_lsadc_voltage[chan->channel];
> +		*val2 = info->data->num_bits;
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id) {
> +	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
> +	int mask;
> +
> +	mask = readl(info->regs + HIBVT_LSADC_INTSTATUS);
> +	if ((mask & HIBVT_LSADC_CHN_MASK_v1) == 0)
> +		return IRQ_NONE;
> +
> +	/* Clear irq */
> +	mask &= HIBVT_LSADC_CHN_MASK_v1;
> +	if (info->data->clear_irq)
> +		info->data->clear_irq(info, mask);
> +
> +	/* Read value */
> +	info->value = readl(info->regs +
> +		HIBVT_LSADC_CHNDATA + (info->cur_chn << 2));
> +	info->value &= GENMASK(info->data->num_bits - 1, 0);
> +
> +	/* stop adc */
> +	if (info->data->stop_conv)
> +		info->data->stop_conv(info);
> +
> +	complete(&info->completion);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct iio_info hibvt_lsadc_iio_info = {
> +	.read_raw = hibvt_lsadc_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +#define HIBVT_LSADC_CHANNEL(_index, _id) {      \
> +	.type = IIO_VOLTAGE,                \
> +	.indexed = 1,						\
> +	.channel = _index,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
> +			BIT(IIO_CHAN_INFO_SCALE),   \
> +	.datasheet_name = _id,              \
> +}
> +
> +static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
> +	HIBVT_LSADC_CHANNEL(0, "adc0"),
> +	HIBVT_LSADC_CHANNEL(1, "adc1"),
> +	HIBVT_LSADC_CHANNEL(2, "adc2"),
> +};
> +
> +static void hibvt_lsadc_v1_clear_irq(struct hibvt_lsadc *info, int 
> +mask) {
> +	writel(mask, info->regs + HIBVT_LSADC_INTCLR); }
> +
> +static void hibvt_lsadc_v1_start_conv(struct hibvt_lsadc *info) {
> +	unsigned int con;
> +
> +	/* set number bit */
set number of bits?
> +	con = GENMASK(info->data->num_bits - 1, 0);
> +	writel(con, (info->regs + HIBVT_LSADC_ACTBIT));
> +
> +	/* config */
> +	con = readl(info->regs + HIBVT_LSADC_CONFIG);
> +	con &= ~HIBVT_CONFIG_RESET;
> +	con |= (HIBVT_CONFIG_POWERDOWN | HIBVT_CONFIG_DEGLITCH |
> +		HIBVT_CONFIG_MODE);
> +	con &= ~(HIBVT_CONFIG_CHNA | HIBVT_CONFIG_CHNB | HIBVT_CONFIG_CHNC);
> +	con |= (HIBVT_CONFIG_CHNA << info->cur_chn);
> +	writel(con, (info->regs + HIBVT_LSADC_CONFIG));
> +
> +	/* set timescan */
> +	writel(HIBVT_LSADC_TSCAN_MS, (info->regs + HIBVT_LSADC_TIMESCAN));
> +
> +	/* clear interrupt */
> +	writel(HIBVT_LSADC_CHN_MASK_v1, info->regs + HIBVT_LSADC_INTCLR);
> +
> +	/* enable interrupt */
> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_INTEN));
> +
> +	/* start scan */
> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_START)); }
> +
> +static void hibvt_lsadc_v1_stop_conv(struct hibvt_lsadc *info) {
> +	/* reset the timescan */
This isn't a particularly common pice of terminology, perhaps a short description here of what timescan is and why we should reset it would make the code easier to follow.

> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_TIMESCAN));
> +
> +	/* disable interrupt */
> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_INTEN));
> +
> +	/* stop scan */
> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_STOP)); }
> +
> +static const struct hibvt_lsadc_data lsadc_data_v1 = {
> +	.num_bits = HIBVT_LSADC_NUM_BITS_V1,
> +	.channels = hibvt_lsadc_iio_channels,
> +	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
> +
> +	.clear_irq = hibvt_lsadc_v1_clear_irq,
> +	.start_conv = hibvt_lsadc_v1_start_conv,
> +	.stop_conv = hibvt_lsadc_v1_stop_conv, };
> +
> +static const struct of_device_id hibvt_lsadc_match[] = {
> +	{
> +		.compatible = "hisilicon,hi3516cv300-lsadc",
> +		.data = &lsadc_data_v1,
The usual convention is to only introduce 'variant' type data as a precursor patch to a series including the support of new parts.

It is acceptable to post a version with this in if you are shortly to submit the follow up that adds other device support.  If you are doing this, please put a note in the patch description to that effect.  Note that if the additional support doesn't turn up, the driver may we get 'simplified'
by someone else.

I'd also generally expect to see this match table further down - directly above where it is used.  Makes for ever so slightly easier reviewing!
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
> +
> +/* Reset LSADC Controller */
> +static void hibvt_lsadc_reset_controller(struct reset_control *reset) 
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
> +static int hibvt_lsadc_probe(struct platform_device *pdev) {
> +	struct hibvt_lsadc *info = NULL;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev = NULL;
> +	struct resource	*mem;
> +	const struct of_device_id *match;
> +	int ret;
> +	int irq;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> +	if (!indio_dev) {
> +		dev_err(&pdev->dev, "failed allocating iio device\n");
> +		return -ENOMEM;
> +	}
> +	info = iio_priv(indio_dev);
> +
> +	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
> +	info->data = match->data;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	info->regs = devm_ioremap_resource(&pdev->dev, mem);
> +	if (IS_ERR(info->regs))
> +		return PTR_ERR(info->regs);
> +
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
> +	init_completion(&info->completion);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq resource?\n");
> +		return irq;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
> +			       0, dev_name(&pdev->dev), info);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
> +		return ret;
> +	}
> +
> +	if (info->reset)
> +		hibvt_lsadc_reset_controller(info->reset);
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	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 = &hibvt_lsadc_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	indio_dev->channels = info->data->channels;
> +	indio_dev->num_channels = info->data->num_channels;
> +
> +	ret = devm_iio_device_register(&pdev->dev, indio_dev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed register iio device\n");
> +		return ret;
Drop this return ret and just return ret instead of the return 0 below.
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver hibvt_lsadc_driver = {
> +	.probe		= hibvt_lsadc_probe,
> +	.driver		= {
> +		.name	= "hibvt-lsadc",
> +		.of_match_table = hibvt_lsadc_match,
> +	},
> +};
> +
> +module_platform_driver(hibvt_lsadc_driver);
> +
> +MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>"); 
> +MODULE_DESCRIPTION("hisilicon BVT LSADC driver"); MODULE_LICENSE("GPL 
> +v2");
> 


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

* 答复: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2017-02-06 12:19     ` liurenzhong
  0 siblings, 0 replies; 19+ messages in thread
From: liurenzhong @ 2017-02-06 12:19 UTC (permalink / raw)
  To: Jonathan Cameron, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, Xuejiancheng, Lixu (kevin)

SGkgSm9uYXRoYW4sDQoNClRoYW5rcyBmb3IgeW91ciBzdWdnZXN0aW9uLCAgSSdtIHNvcnJ5IHRv
IHJlcGx5IGFmdGVyIGEgbG9uZyB0aW1lIC4gVGhpcyBpcyBzb21lIHByb2JsZW1zIGFzayBmb3Ig
eW91ciBhZHZpY2U6DQoNCjEsIEl0J3MgbmljZSB0byB1c2UganVzdCAicmVzZXQvcmVzZXQudHh0
IiBvciAiLi4uL3Jlc2V0L3Jlc2V0LnR4dCIgZnJvbSBSb2IncyBzdWdnZXN0aW9uLCBidXQgaW4g
dGhlIGZpbHRlcmVkIERUIHRyZWUsICIuLi9yZXNldC9yZXNldC50eHQiIGlzIHVzZWQgbW9yZSB0
aW1lcywgd2hpY2ggb25lIHdlIHNob3VsZCBnZXQsICIuLi8iIG9yICIuLi4vIj8gDQoNCjIsIFRo
ZSBBREMgb24gaGlzaWxpY29uIEJWVCBzb2NzIGNhbiB3b3JrIGluIHNpbmdsZSBzY2FubmluZyBt
b2RlIG9yIGNvbnRpbnVvdXMgc2Nhbm5pbmcgbW9kZS4gVGhlIHNpbmdsZSBtb2RlIGdldCBjb252
ZXJzaW9uIHZhbHVlIG9uZSB0aW1lIGFmdGVyIHN0YXJ0IHRoZSBjb25maWd1cmUsIHdoaWxlIHRo
ZSBjb250aW51b3VzIHNjYW5uaW5nIG1vZGUgd2lsbCBnZXQgY29udmVyc2lvbiB2YWx1ZSBlYWNo
IHNjYW4gdGltZSBhZnRlciBzdGFydCB0aGUgY29uZmlndXJlIHdoaWxlIHN0b3BwaW5nIHRoZSBh
ZGMgY29uZmlndXJlLiBGb3IgbW9yZSBleHBhbnNpYmlsaXR5LCAgdGhlIEFEQyBkcml2ZXIgdXNl
IHRoZSBjb250aW51b3VzIHNjYW5uaW5nIG1vZGUgYW5kICBzdG9wIHRoZSBhZGMgY29uZmlndXJl
IGFmdGVyIGdldCBvbmUgdGltZSBjb252ZXJzaW9uIHZhbHVlLiAgSXMgaXQgbmVjZXNzYXJ5IHRv
IGNoYW5nZSBvdXIgZHJpdmVyIHRvIHNpbmdsZSBzY2FubmluZyBtb2RlIG9yIGp1c3QgYWRkIGEg
c2hvcnQgZGVzY3JpcHRpb24/DQoNCjMsIFRoaXMgZHJ2aWVyIGlzIG9ubHkgc3VwcG9ydCBBREMg
SUYgZm91bmQgb24gaGkzNTE2Y3YzMDAgYW5kIGhpMzUxOXYxMDEgbm93ICwgYW5kIGZ1dHVyZSBT
b0NzIGZyb20gSGlzaWxpY29uIEJWVCB3b3VsZCBnZXQgc29tZSBjaGFuZ2VzIG9uIGNoYW5uZWwg
bnVtYmVyIG9yIGdldHRpbmcgZGF0YSB3YXlzIGFuZCBzbyBvbiwgIHNvIHRoZSBkcml2ZXIgdXNl
ICJWMSIgb24gbWF0Y2ggdGFibGUgYW5kIGFkZCBvdGhlciB2ZXJzaW9uIHdoaWxlIGFueSBjaGFu
Z2UgaGFwcGVucy4gIFdoaWNoIG1hdGNoIHRhYmxlIGNhbiB3ZSBwcm92aWRlIGFuZCAgaXMndCBP
SyBsaWtlIHRoaXM/DQo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2RldmljZV9pZCBoaWJ2dF9s
c2FkY19tYXRjaFtdID0gew0KPiArCXsNCj4gKwkJLmNvbXBhdGlibGUgPSAiaGlzaWxpY29uLGhp
MzUxNmN2MzAwLWxzYWRjIiwNCj4gKwkJLmRhdGEgPSAmbHNhZGNfZGF0YV92MSwNCj4gKwl9LA0K
PiArCXsNCj4gKwkJLmNvbXBhdGlibGUgPSAiaGlzaWxpY29uLGhpMzUxOXYxMDEtbHNhZGMiLA0K
PiArCQkuZGF0YSA9ICZsc2FkY19kYXRhX3YxLA0KPiArCX0sDQo+ICsJe30sDQo+ICt9Ow0KDQpB
d2FpdGluZyBmb3IgeW91ciByZXBseSBUaGFuayB5b3UgdmVyeSBtdWNoIQ0KDQpCZXN0IHJlZ2Fy
ZHMNCi9BbGxlbg0KDQotLS0tLdPKvP7Urbz+LS0tLS0NCreivP7IyzogSm9uYXRoYW4gQ2FtZXJv
biBbbWFpbHRvOmppYzIzQGtlcm5lbC5vcmddIA0Kt6LLzcqxvOQ6IDIwMTfE6jHUwjjI1SAxOjUy
DQrK1bz+yMs6IGxpdXJlbnpob25nIDxsaXVyZW56aG9uZ0BoaXNpbGljb24uY29tPjsga25hYWNr
LmhAZ214LmRlOyBsYXJzQG1ldGFmb28uZGU7IHBtZWVyd0BwbWVlcncubmV0OyByb2JoK2R0QGtl
cm5lbC5vcmc7IG1hcmsucnV0bGFuZEBhcm0uY29tDQqzrcvNOiBha2lub2J1Lm1pdGFAZ21haWwu
Y29tOyBsdWRvdmljLmRlc3JvY2hlc0BhdG1lbC5jb207IGtyemtAa2VybmVsLm9yZzsgdmlsaGVs
bS5ncmF5QGdtYWlsLmNvbTsga3NlbmlqYS5zdGFub2pldmljQGdtYWlsLmNvbTsgemhpeW9uZy50
YW9AbWVkaWF0ZWsuY29tOyBkYW5pZWwuYmFsdXRhQGludGVsLmNvbTsgbGVvbmFyZC5jcmVzdGV6
QGludGVsLmNvbTsgcmF5Lmp1aUBicm9hZGNvbS5jb207IHJhdmVlbmRyYS5wYWRhc2FsYWdpQGJy
b2FkY29tLmNvbTsgbXJhbm9zdGF5QGdtYWlsLmNvbTsgYW1zZmllbGQyMkBnbWFpbC5jb207IGxp
bnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmc7IGRldmljZXRyZWVAdmdlci5rZXJuZWwub3JnOyBsaW51
eC1rZXJuZWxAdmdlci5rZXJuZWwub3JnOyBYdWVqaWFuY2hlbmcgPHh1ZWppYW5jaGVuZ0BoaXNp
bGljb24uY29tPjsgTGl4dSAoa2V2aW4pIDxrZXZpbi5saXh1QGhpc2lsaWNvbi5jb20+DQrW98zi
OiBSZTogW1BBVENIXSBhZGM6IGFkZCBhZGMgZHJpdmVyIGZvciBIaXNpbGljb24gQlZUIFNPQ3MN
Cg0KT24gMDcvMDEvMTcgMDU6MTYsIEFsbGVuIExpdSB3cm90ZToNCj4gQWRkIEFEQyBkcml2ZXIg
Zm9yIHRoZSBBREMgY29udHJvbGxlciBmb3VuZCBvbiBIaVNpbGljb24gQlZUIFNPQ3MsIGxpa2Ug
SGkzNTE2Q1YzMDAsIGV0Yy4NCj4gVGhlIEFEQyBjb250cm9sbGVyIGlzIHByaW1hcmlseSBpbiBj
aGFyZ2Ugb2YgZGV0ZWN0aW5nIHZvbHRhZ2UuDQo+IA0KPiBSZXZpZXdlZC1ieTogS2V2aW4gTGkg
PGtldmluLmxpeHVAaGlzaWxpY29uLmNvbT4NCj4gU2lnbmVkLW9mZi1ieTogQWxsZW4gTGl1IDxs
aXVyZW56aG9uZ0BoaXNpbGljb24uY29tPg0KSGkgQWxsZW4sDQoNCk9uZSBxdWljayBzdWJtaXNz
aW9uIHByb2Nlc3Mgbm90ZSBmaXJzdC4gIEl0IGlzIHZlcnkgaW1wb3J0YW50IHRvIGNsZWFybHkg
aWRlbnRpZnkgbmV3IHZlcnNpb25zIG9mIGEgcGF0Y2ggYW5kIHdoYXQgY2hhbmdlcyBoYXZlIG9j
Y3VycmVkIHNpbmNlIHRoZSBwcmV2aW91cyBwb3N0aW5nLg0KDQpTbyB0aGUgZW1haWwgdGl0bGUg
c2hvdWxkIGhhdmUgYmVlbiBbUEFUQ0ggVjJdIGFkYy4uLg0KDQpBbHNvLCBiZWxvdyB0aGUgLS0t
IHBsZWFzZSBhZGQgYSBicmllZiBjaGFuZ2UgbG9nLg0KDQpUaGUgZHJpdmVyIGlzIGNvbWluZyB0
b2dldGhlciBuaWNlbHkuICBBIGZldyBtaW5vciBwb2ludHMgaW5saW5lLg0KDQpKb25hdGhhbg0K
PiAtLS0NCj4gIC4uLi9kZXZpY2V0cmVlL2JpbmRpbmdzL2lpby9hZGMvaGlidnQtbHNhZGMudHh0
ICAgIHwgIDIzICsrDQo+ICBkcml2ZXJzL2lpby9hZGMvS2NvbmZpZyAgICAgICAgICAgICAgICAg
ICAgICAgICAgICB8ICAxMCArDQo+ICBkcml2ZXJzL2lpby9hZGMvTWFrZWZpbGUgICAgICAgICAg
ICAgICAgICAgICAgICAgICB8ICAgMSArDQo+ICBkcml2ZXJzL2lpby9hZGMvaGlidnRfbHNhZGMu
YyAgICAgICAgICAgICAgICAgICAgICB8IDMzNSArKysrKysrKysrKysrKysrKysrKysNCj4gIDQg
ZmlsZXMgY2hhbmdlZCwgMzY5IGluc2VydGlvbnMoKykNCj4gIGNyZWF0ZSBtb2RlIDEwMDY0NCAN
Cj4gRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRpbmdzL2lpby9hZGMvaGlidnQtbHNhZGMu
dHh0DQo+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9paW8vYWRjL2hpYnZ0X2xzYWRjLmMN
Cj4gDQo+IGRpZmYgLS1naXQgYS9Eb2N1bWVudGF0aW9uL2RldmljZXRyZWUvYmluZGluZ3MvaWlv
L2FkYy9oaWJ2dC1sc2FkYy50eHQgDQo+IGIvRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRp
bmdzL2lpby9hZGMvaGlidnQtbHNhZGMudHh0DQo+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+IGlu
ZGV4IDAwMDAwMDAuLmZjZTFmZjQNCj4gLS0tIC9kZXYvbnVsbA0KPiArKysgYi9Eb2N1bWVudGF0
aW9uL2RldmljZXRyZWUvYmluZGluZ3MvaWlvL2FkYy9oaWJ2dC1sc2FkYy50eHQNCj4gQEAgLTAs
MCArMSwyMyBAQA0KPiArSGlzaWxpY29uIEJWVCBMb3cgU3BlZWQgKExTKSBBL0QgQ29udmVydGVy
IGJpbmRpbmdzDQo+ICsNCj4gK1JlcXVpcmVkIHByb3BlcnRpZXM6DQo+ICstIGNvbXBhdGlibGU6
IHNob3VsZCBiZSAiaGlzaWxpY29uLDxuYW1lPi1sc2FkYyINCj4gKyAgIC0gImhpc2lsaWNvbixo
aTM1MTZjdjMwMC1sc2FkYyI6IGZvciBoaTM1MTZjdjMwMA0KPiArDQo+ICstIHJlZzogcGh5c2lj
YWwgYmFzZSBhZGRyZXNzIG9mIHRoZSBjb250cm9sbGVyIGFuZCBsZW5ndGggb2YgbWVtb3J5IG1h
cHBlZCANCj4gKwkgICByZWdpb24uDQo+ICstIGludGVycnVwdHM6IFRoZSBpbnRlcnJ1cHQgbnVt
YmVyIGZvciB0aGUgQURDIGRldmljZS4NCklkZWFsbHkgcmVmZXIgdG8gdGhlIHN0YW5kYXJkIGlu
dGVycnVwdCBiaW5kaW5nIGRvY3VtZW50Lg0KRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRp
bmdzL2ludGVycnVwdC1jb250cm9sbGVyL2ludGVycnVwdHMudHh0DQoNCj4gKw0KPiArT3B0aW9u
YWwgcHJvcGVydGllczoNCj4gKy0gcmVzZXRzOiBNdXN0IGNvbnRhaW4gYW4gZW50cnkgZm9yIGVh
Y2ggZW50cnkgaW4gcmVzZXQtbmFtZXMgaWYgbmVlZCBzdXBwb3J0DQo+ICsJCSAgdGhpcyBvcHRp
b24uIFNlZSAuLi8uLi9yZXNldC9yZXNldC50eHQgZm9yIGRldGFpbHMuDQpEb24ndCB1c2UgYSBy
ZWxhdGl2ZSBwYXRoIGluIGEgYmluZGluZyBkb2N1bWVudC4gSXQncyBmYXIgdG9vIGxpa2VseSB0
byBiZSBicm9rZW4gYnkgYSByZW9yZ2FuaXphdGlvbiBvZiB0aGUgZG9jcyBhbmQgY2Fubm90IGJl
IGdyZXBwZWQgZm9yLg0KPiArLSByZXNldC1uYW1lczogTXVzdCBpbmNsdWRlIHRoZSBuYW1lICJs
c2FkYy1jcmciLg0KPiArDQo+ICtFeGFtcGxlOg0KPiArCWFkYzogYWRjQDEyMGUwMDAwIHsNCj4g
KwkJCWNvbXBhdGlibGUgPSAiaGlzaWxpY29uLGhpMzUxNmN2MzAwLWxzYWRjIjsNCj4gKwkJCXJl
ZyA9IDwweDEyMGUwMDAwIDB4MTAwMD47DQo+ICsJCQlpbnRlcnJ1cHRzID0gPDE5PjsNCj4gKwkJ
CXJlc2V0cyA9IDwmY3JnIDB4N2MgMz47DQo+ICsJCQlyZXNldC1uYW1lcyA9ICJsc2FkYy1jcmci
Ow0KPiArCX07DQo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2lpby9hZGMvS2NvbmZpZyBiL2RyaXZl
cnMvaWlvL2FkYy9LY29uZmlnIGluZGV4IA0KPiA5OWMwNTE0Li4wNDQzZjUxIDEwMDY0NA0KPiAt
LS0gYS9kcml2ZXJzL2lpby9hZGMvS2NvbmZpZw0KPiArKysgYi9kcml2ZXJzL2lpby9hZGMvS2Nv
bmZpZw0KPiBAQCAtMjI1LDYgKzIyNSwxNiBAQCBjb25maWcgSEk4NDM1DQo+ICAJICBUaGlzIGRy
aXZlciBjYW4gYWxzbyBiZSBidWlsdCBhcyBhIG1vZHVsZS4gSWYgc28sIHRoZSBtb2R1bGUgd2ls
bCBiZQ0KPiAgCSAgY2FsbGVkIGhpODQzNS4NCj4gIA0KPiArY29uZmlnIEhJQlZUX0xTQURDDQo+
ICsJdHJpc3RhdGUgIkhJQlZUIExTQURDIGRyaXZlciINCj4gKwlkZXBlbmRzIG9uIEFSQ0hfSElT
SSB8fCBDT01QSUxFX1RFU1QNCj4gKwloZWxwDQo+ICsJICBTYXkgeWVzIGhlcmUgdG8gYnVpbGQg
c3VwcG9ydCBmb3IgdGhlIExTQURDIGZvdW5kIGluIFNvQ3MgZnJvbQ0KPiArCSAgaGlzaWxpY29u
IEJWVCBjaGlwLg0KPiArDQo+ICsJICBUbyBjb21waWxlIHRoaXMgZHJpdmVyIGFzIGEgbW9kdWxl
LCBjaG9vc2UgTSBoZXJlOiB0aGUNCj4gKwkgIG1vZHVsZSB3aWxsIGJlIGNhbGxlZCBoaWJ2dF9s
c2FkYy4NCj4gKw0KPiAgY29uZmlnIElOQTJYWF9BREMNCj4gIAl0cmlzdGF0ZSAiVGV4YXMgSW5z
dHJ1bWVudHMgSU5BMnh4IFBvd2VyIE1vbml0b3JzIElJTyBkcml2ZXIiDQo+ICAJZGVwZW5kcyBv
biBJMkMgJiYgIVNFTlNPUlNfSU5BMlhYDQo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2lpby9hZGMv
TWFrZWZpbGUgYi9kcml2ZXJzL2lpby9hZGMvTWFrZWZpbGUgaW5kZXggDQo+IDdhNDBjMDQuLjY1
NTRkOTIgMTAwNjQ0DQo+IC0tLSBhL2RyaXZlcnMvaWlvL2FkYy9NYWtlZmlsZQ0KPiArKysgYi9k
cml2ZXJzL2lpby9hZGMvTWFrZWZpbGUNCj4gQEAgLTIzLDYgKzIzLDcgQEAgb2JqLSQoQ09ORklH
X0RBOTE1MF9HUEFEQykgKz0gZGE5MTUwLWdwYWRjLm8NCj4gIG9iai0kKENPTkZJR19FWFlOT1Nf
QURDKSArPSBleHlub3NfYWRjLm8NCj4gIG9iai0kKENPTkZJR19GU0xfTVgyNV9BREMpICs9IGZz
bC1pbXgyNS1nY3Eubw0KPiAgb2JqLSQoQ09ORklHX0hJODQzNSkgKz0gaGk4NDM1Lm8NCj4gK29i
ai0kKENPTkZJR19ISUJWVF9MU0FEQykgKz0gaGlidnRfbHNhZGMubw0KPiAgb2JqLSQoQ09ORklH
X0lNWDdEX0FEQykgKz0gaW14N2RfYWRjLm8NCj4gIG9iai0kKENPTkZJR19JTkEyWFhfQURDKSAr
PSBpbmEyeHgtYWRjLm8NCj4gIG9iai0kKENPTkZJR19MUDg3ODhfQURDKSArPSBscDg3ODhfYWRj
Lm8gZGlmZiAtLWdpdCANCj4gYS9kcml2ZXJzL2lpby9hZGMvaGlidnRfbHNhZGMuYyBiL2RyaXZl
cnMvaWlvL2FkYy9oaWJ2dF9sc2FkYy5jIG5ldyANCj4gZmlsZSBtb2RlIDEwMDY0NCBpbmRleCAw
MDAwMDAwLi5hYWYyMDI0DQo+IC0tLSAvZGV2L251bGwNCj4gKysrIGIvZHJpdmVycy9paW8vYWRj
L2hpYnZ0X2xzYWRjLmMNCj4gQEAgLTAsMCArMSwzMzUgQEANCj4gKy8qDQo+ICsgKiBIaXNpbGlj
b24gQlZUIExvdyBTcGVlZCAoTFMpIEEvRCBDb252ZXJ0ZXINCj4gKyAqIENvcHlyaWdodCAoQykg
MjAxNiBIaVNpbGljb24gVGVjaG5vbG9naWVzIENvLiwgTHRkLg0KPiArICoNCj4gKyAqIFRoaXMg
cHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3Ig
DQo+ICttb2RpZnkNCj4gKyAqIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwg
UHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIA0KPiArYnkNCj4gKyAqIHRoZSBGcmVlIFNvZnR3
YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yDQo+ICsg
KiAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLg0KPiArICoNCj4gKyAqIFRoaXMg
cHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVs
LA0KPiArICogYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxp
ZWQgd2FycmFudHkgb2YNCj4gKyAqIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBB
UlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUNCj4gKyAqIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNl
bnNlIGZvciBtb3JlIGRldGFpbHMuDQo+ICsgKi8NCj4gKw0KPiArI2luY2x1ZGUgPGxpbnV4L21v
ZHVsZS5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPg0KPiArI2luY2x1
ZGUgPGxpbnV4L2ludGVycnVwdC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2lvLmg+DQo+ICsjaW5j
bHVkZSA8bGludXgvb2YuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9vZl9kZXZpY2UuaD4NCj4gKyNp
bmNsdWRlIDxsaW51eC9jbGsuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9jb21wbGV0aW9uLmg+DQo+
ICsjaW5jbHVkZSA8bGludXgvZGVsYXkuaD4NCj4gKyNpbmNsdWRlIDxsaW51eC9yZXNldC5oPg0K
PiArI2luY2x1ZGUgPGxpbnV4L3JlZ3VsYXRvci9jb25zdW1lci5oPiAjaW5jbHVkZSA8bGludXgv
aWlvL2lpby5oPg0KPiArDQo+ICsvKiBoaXNpbGljb24gYnZ0IGFkYyByZWdpc3RlcnMgZGVmaW5p
dGlvbnMgKi8NCj4gKyNkZWZpbmUgSElCVlRfTFNBRENfQ09ORklHCQkweDAwDQo+ICsjZGVmaW5l
IEhJQlZUX0NPTkZJR19ERUdMSVRDSAlCSVQoMTcpDQo+ICsjZGVmaW5lIEhJQlZUX0NPTkZJR19S
RVNFVAkJQklUKDE1KQ0KPiArI2RlZmluZSBISUJWVF9DT05GSUdfUE9XRVJET1dOCUJJVCgxNCkN
Cj4gKyNkZWZpbmUgSElCVlRfQ09ORklHX01PREUJCUJJVCgxMykNCj4gKyNkZWZpbmUgSElCVlRf
Q09ORklHX0NITkMJCUJJVCgxMCkNCj4gKyNkZWZpbmUgSElCVlRfQ09ORklHX0NITkIJCUJJVCg5
KQ0KPiArI2RlZmluZSBISUJWVF9DT05GSUdfQ0hOQQkJQklUKDgpDQo+ICsNCj4gKyNkZWZpbmUg
SElCVlRfTFNBRENfVElNRVNDQU4JMHgwOA0KPiArI2RlZmluZSBISUJWVF9MU0FEQ19JTlRFTgkJ
MHgxMA0KPiArI2RlZmluZSBISUJWVF9MU0FEQ19JTlRTVEFUVVMJMHgxNA0KPiArI2RlZmluZSBI
SUJWVF9MU0FEQ19JTlRDTFIJCTB4MTgNCj4gKyNkZWZpbmUgSElCVlRfTFNBRENfU1RBUlQJCTB4
MUMNCj4gKyNkZWZpbmUgSElCVlRfTFNBRENfU1RPUAkJMHgyMA0KPiArI2RlZmluZSBISUJWVF9M
U0FEQ19BQ1RCSVQJCTB4MjQNCj4gKyNkZWZpbmUgSElCVlRfTFNBRENfQ0hOREFUQQkJMHgyQw0K
PiArDQo+ICsjZGVmaW5lIEhJQlZUX0xTQURDX0NPTl9FTgkJKDF1IDw8IDApDQo+ICsjZGVmaW5l
IEhJQlZUX0xTQURDX0NPTl9ERU4JCSgwdSA8PCAwKQ0KPiArDQo+ICsjZGVmaW5lIEhJQlZUX0xT
QURDX05VTV9CSVRTX1YxCTEwDQo+ICsjZGVmaW5lIEhJQlZUX0xTQURDX0NITl9NQVNLX3YxCTB4
Nw0KPiArDQo+ICsvKiBmaXggY2xrOjMwMDAwMDAsIGRlZmF1bHQgdHNjYW4gc2V0IDEwbXMgKi8N
Cj4gKyNkZWZpbmUgSElCVlRfTFNBRENfVFNDQU5fTVMJKDEwKjMwMDApDQo+ICsNCj4gKyNkZWZp
bmUgSElCVlRfTFNBRENfVElNRU9VVAkJbXNlY3NfdG9famlmZmllcygxMDApDQo+ICsNCj4gKy8q
IGRlZmF1bHQgdm9sdGFnZSBzY2FsZSBmb3IgZXZlcnkgY2hhbm5lbCA8bXY+ICovIHN0YXRpYyBp
bnQgDQo+ICtnX2hpYnZ0X2xzYWRjX3ZvbHRhZ2VbXSA9IHsNCj4gKwkzMzAwLCAzMzAwLCAzMzAw
DQpJcyBkZWZhdWx0IGR1ZSB0byBhbiBleHRlcm5hbCByZWZlcmVuY2Ugdm9sdGFnZSBvciBpcyB0
aGVyZSBhbiBpbnRlcm5hbCByZWd1bGF0b3I/ICBJZiBpdCBpcyBleHRlcm5hbCBpdCBzaG91bGQg
cmVhbGx5IGJlIGRlc2NyaWJlZCB1c2luZyB0aGUgcmVndWxhdG9yIGZyYW1ld29yay4NCg0KQ29u
c3Q/IA0KPiArfTsNCj4gKw0KPiArc3RydWN0IGhpYnZ0X2xzYWRjIHsNCj4gKwl2b2lkIF9faW9t
ZW0JCSpyZWdzOw0KPiArCXN0cnVjdCBjb21wbGV0aW9uCWNvbXBsZXRpb247DQo+ICsJc3RydWN0
IHJlc2V0X2NvbnRyb2wJKnJlc2V0Ow0KPiArCWNvbnN0IHN0cnVjdCBoaWJ2dF9sc2FkY19kYXRh
CSpkYXRhOw0KPiArCXVuc2lnbmVkIGludAkJY3VyX2NobjsNCj4gKwl1bnNpZ25lZCBpbnQJCXZh
bHVlOw0KPiArfTsNCj4gKw0KPiArc3RydWN0IGhpYnZ0X2xzYWRjX2RhdGEgew0KPiArCWludAkJ
CQludW1fYml0czsNCj4gKwljb25zdCBzdHJ1Y3QgaWlvX2NoYW5fc3BlYwkqY2hhbm5lbHM7DQo+
ICsJaW50CQkJCW51bV9jaGFubmVsczsNCj4gKw0KPiArCXZvaWQgKCpjbGVhcl9pcnEpKHN0cnVj
dCBoaWJ2dF9sc2FkYyAqaW5mbywgaW50IG1hc2spOw0KPiArCXZvaWQgKCpzdGFydF9jb252KShz
dHJ1Y3QgaGlidnRfbHNhZGMgKmluZm8pOw0KPiArCXZvaWQgKCpzdG9wX2NvbnYpKHN0cnVjdCBo
aWJ2dF9sc2FkYyAqaW5mbyk7IH07DQo+ICsNCj4gK3N0YXRpYyBpbnQgaGlidnRfbHNhZGNfcmVh
ZF9yYXcoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiwNCj4gKwkJCQkgICAgc3RydWN0IGlpb19j
aGFuX3NwZWMgY29uc3QgKmNoYW4sDQo+ICsJCQkJICAgIGludCAqdmFsLCBpbnQgKnZhbDIsIGxv
bmcgbWFzaykgew0KPiArCXN0cnVjdCBoaWJ2dF9sc2FkYyAqaW5mbyA9IGlpb19wcml2KGluZGlv
X2Rldik7DQo+ICsNCj4gKwlzd2l0Y2ggKG1hc2spIHsNCj4gKwljYXNlIElJT19DSEFOX0lORk9f
UkFXOg0KPiArCQltdXRleF9sb2NrKCZpbmRpb19kZXYtPm1sb2NrKTsNCj4gKw0KPiArCQlyZWlu
aXRfY29tcGxldGlvbigmaW5mby0+Y29tcGxldGlvbik7DQo+ICsNCj4gKwkJLyogU2VsZWN0IHRo
ZSBjaGFubmVsIHRvIGJlIHVzZWQgKi8NCj4gKwkJaW5mby0+Y3VyX2NobiA9IGNoYW4tPmNoYW5u
ZWw7DQo+ICsNCj4gKwkJaWYgKGluZm8tPmRhdGEtPnN0YXJ0X2NvbnYpDQo+ICsJCQlpbmZvLT5k
YXRhLT5zdGFydF9jb252KGluZm8pOw0KPiArDQo+ICsJCWlmICghd2FpdF9mb3JfY29tcGxldGlv
bl90aW1lb3V0KCZpbmZvLT5jb21wbGV0aW9uLA0KPiArCQkJCQkJCUhJQlZUX0xTQURDX1RJTUVP
VVQpKSB7DQo+ICsJCQlpZiAoaW5mby0+ZGF0YS0+c3RvcF9jb252KQ0KPiArCQkJCWluZm8tPmRh
dGEtPnN0b3BfY29udihpbmZvKTsNCj4gKwkJCW11dGV4X3VubG9jaygmaW5kaW9fZGV2LT5tbG9j
ayk7DQo+ICsJCQlyZXR1cm4gLUVUSU1FRE9VVDsNCj4gKwkJfQ0KPiArDQo+ICsJCSp2YWwgPSBp
bmZvLT52YWx1ZTsNCj4gKwkJbXV0ZXhfdW5sb2NrKCZpbmRpb19kZXYtPm1sb2NrKTsNCj4gKwkJ
cmV0dXJuIElJT19WQUxfSU5UOw0KPiArCWNhc2UgSUlPX0NIQU5fSU5GT19TQ0FMRToNCj4gKwkJ
KnZhbCA9IGdfaGlidnRfbHNhZGNfdm9sdGFnZVtjaGFuLT5jaGFubmVsXTsNCj4gKwkJKnZhbDIg
PSBpbmZvLT5kYXRhLT5udW1fYml0czsNCj4gKwkJcmV0dXJuIElJT19WQUxfRlJBQ1RJT05BTF9M
T0cyOw0KPiArCWRlZmF1bHQ6DQo+ICsJCXJldHVybiAtRUlOVkFMOw0KPiArCX0NCj4gK30NCj4g
Kw0KPiArc3RhdGljIGlycXJldHVybl90IGhpYnZ0X2xzYWRjX2lzcihpbnQgaXJxLCB2b2lkICpk
ZXZfaWQpIHsNCj4gKwlzdHJ1Y3QgaGlidnRfbHNhZGMgKmluZm8gPSAoc3RydWN0IGhpYnZ0X2xz
YWRjICopZGV2X2lkOw0KPiArCWludCBtYXNrOw0KPiArDQo+ICsJbWFzayA9IHJlYWRsKGluZm8t
PnJlZ3MgKyBISUJWVF9MU0FEQ19JTlRTVEFUVVMpOw0KPiArCWlmICgobWFzayAmIEhJQlZUX0xT
QURDX0NITl9NQVNLX3YxKSA9PSAwKQ0KPiArCQlyZXR1cm4gSVJRX05PTkU7DQo+ICsNCj4gKwkv
KiBDbGVhciBpcnEgKi8NCj4gKwltYXNrICY9IEhJQlZUX0xTQURDX0NITl9NQVNLX3YxOw0KPiAr
CWlmIChpbmZvLT5kYXRhLT5jbGVhcl9pcnEpDQo+ICsJCWluZm8tPmRhdGEtPmNsZWFyX2lycShp
bmZvLCBtYXNrKTsNCj4gKw0KPiArCS8qIFJlYWQgdmFsdWUgKi8NCj4gKwlpbmZvLT52YWx1ZSA9
IHJlYWRsKGluZm8tPnJlZ3MgKw0KPiArCQlISUJWVF9MU0FEQ19DSE5EQVRBICsgKGluZm8tPmN1
cl9jaG4gPDwgMikpOw0KPiArCWluZm8tPnZhbHVlICY9IEdFTk1BU0soaW5mby0+ZGF0YS0+bnVt
X2JpdHMgLSAxLCAwKTsNCj4gKw0KPiArCS8qIHN0b3AgYWRjICovDQo+ICsJaWYgKGluZm8tPmRh
dGEtPnN0b3BfY29udikNCj4gKwkJaW5mby0+ZGF0YS0+c3RvcF9jb252KGluZm8pOw0KPiArDQo+
ICsJY29tcGxldGUoJmluZm8tPmNvbXBsZXRpb24pOw0KPiArDQo+ICsJcmV0dXJuIElSUV9IQU5E
TEVEOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IGlpb19pbmZvIGhpYnZ0X2xz
YWRjX2lpb19pbmZvID0gew0KPiArCS5yZWFkX3JhdyA9IGhpYnZ0X2xzYWRjX3JlYWRfcmF3LA0K
PiArCS5kcml2ZXJfbW9kdWxlID0gVEhJU19NT0RVTEUsDQo+ICt9Ow0KPiArDQo+ICsjZGVmaW5l
IEhJQlZUX0xTQURDX0NIQU5ORUwoX2luZGV4LCBfaWQpIHsgICAgICBcDQo+ICsJLnR5cGUgPSBJ
SU9fVk9MVEFHRSwgICAgICAgICAgICAgICAgXA0KPiArCS5pbmRleGVkID0gMSwJCQkJCQlcDQo+
ICsJLmNoYW5uZWwgPSBfaW5kZXgsCQkJCQlcDQo+ICsJLmluZm9fbWFza19zZXBhcmF0ZSA9IEJJ
VChJSU9fQ0hBTl9JTkZPX1JBVykgfCAgXA0KPiArCQkJQklUKElJT19DSEFOX0lORk9fU0NBTEUp
LCAgIFwNCj4gKwkuZGF0YXNoZWV0X25hbWUgPSBfaWQsICAgICAgICAgICAgICBcDQo+ICt9DQo+
ICsNCj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgaWlvX2NoYW5fc3BlYyBoaWJ2dF9sc2FkY19paW9f
Y2hhbm5lbHNbXSA9IHsNCj4gKwlISUJWVF9MU0FEQ19DSEFOTkVMKDAsICJhZGMwIiksDQo+ICsJ
SElCVlRfTFNBRENfQ0hBTk5FTCgxLCAiYWRjMSIpLA0KPiArCUhJQlZUX0xTQURDX0NIQU5ORUwo
MiwgImFkYzIiKSwNCj4gK307DQo+ICsNCj4gK3N0YXRpYyB2b2lkIGhpYnZ0X2xzYWRjX3YxX2Ns
ZWFyX2lycShzdHJ1Y3QgaGlidnRfbHNhZGMgKmluZm8sIGludCANCj4gK21hc2spIHsNCj4gKwl3
cml0ZWwobWFzaywgaW5mby0+cmVncyArIEhJQlZUX0xTQURDX0lOVENMUik7IH0NCj4gKw0KPiAr
c3RhdGljIHZvaWQgaGlidnRfbHNhZGNfdjFfc3RhcnRfY29udihzdHJ1Y3QgaGlidnRfbHNhZGMg
KmluZm8pIHsNCj4gKwl1bnNpZ25lZCBpbnQgY29uOw0KPiArDQo+ICsJLyogc2V0IG51bWJlciBi
aXQgKi8NCnNldCBudW1iZXIgb2YgYml0cz8NCj4gKwljb24gPSBHRU5NQVNLKGluZm8tPmRhdGEt
Pm51bV9iaXRzIC0gMSwgMCk7DQo+ICsJd3JpdGVsKGNvbiwgKGluZm8tPnJlZ3MgKyBISUJWVF9M
U0FEQ19BQ1RCSVQpKTsNCj4gKw0KPiArCS8qIGNvbmZpZyAqLw0KPiArCWNvbiA9IHJlYWRsKGlu
Zm8tPnJlZ3MgKyBISUJWVF9MU0FEQ19DT05GSUcpOw0KPiArCWNvbiAmPSB+SElCVlRfQ09ORklH
X1JFU0VUOw0KPiArCWNvbiB8PSAoSElCVlRfQ09ORklHX1BPV0VSRE9XTiB8IEhJQlZUX0NPTkZJ
R19ERUdMSVRDSCB8DQo+ICsJCUhJQlZUX0NPTkZJR19NT0RFKTsNCj4gKwljb24gJj0gfihISUJW
VF9DT05GSUdfQ0hOQSB8IEhJQlZUX0NPTkZJR19DSE5CIHwgSElCVlRfQ09ORklHX0NITkMpOw0K
PiArCWNvbiB8PSAoSElCVlRfQ09ORklHX0NITkEgPDwgaW5mby0+Y3VyX2Nobik7DQo+ICsJd3Jp
dGVsKGNvbiwgKGluZm8tPnJlZ3MgKyBISUJWVF9MU0FEQ19DT05GSUcpKTsNCj4gKw0KPiArCS8q
IHNldCB0aW1lc2NhbiAqLw0KPiArCXdyaXRlbChISUJWVF9MU0FEQ19UU0NBTl9NUywgKGluZm8t
PnJlZ3MgKyBISUJWVF9MU0FEQ19USU1FU0NBTikpOw0KPiArDQo+ICsJLyogY2xlYXIgaW50ZXJy
dXB0ICovDQo+ICsJd3JpdGVsKEhJQlZUX0xTQURDX0NITl9NQVNLX3YxLCBpbmZvLT5yZWdzICsg
SElCVlRfTFNBRENfSU5UQ0xSKTsNCj4gKw0KPiArCS8qIGVuYWJsZSBpbnRlcnJ1cHQgKi8NCj4g
Kwl3cml0ZWwoSElCVlRfTFNBRENfQ09OX0VOLCAoaW5mby0+cmVncyArIEhJQlZUX0xTQURDX0lO
VEVOKSk7DQo+ICsNCj4gKwkvKiBzdGFydCBzY2FuICovDQo+ICsJd3JpdGVsKEhJQlZUX0xTQURD
X0NPTl9FTiwgKGluZm8tPnJlZ3MgKyBISUJWVF9MU0FEQ19TVEFSVCkpOyB9DQo+ICsNCj4gK3N0
YXRpYyB2b2lkIGhpYnZ0X2xzYWRjX3YxX3N0b3BfY29udihzdHJ1Y3QgaGlidnRfbHNhZGMgKmlu
Zm8pIHsNCj4gKwkvKiByZXNldCB0aGUgdGltZXNjYW4gKi8NClRoaXMgaXNuJ3QgYSBwYXJ0aWN1
bGFybHkgY29tbW9uIHBpY2Ugb2YgdGVybWlub2xvZ3ksIHBlcmhhcHMgYSBzaG9ydCBkZXNjcmlw
dGlvbiBoZXJlIG9mIHdoYXQgdGltZXNjYW4gaXMgYW5kIHdoeSB3ZSBzaG91bGQgcmVzZXQgaXQg
d291bGQgbWFrZSB0aGUgY29kZSBlYXNpZXIgdG8gZm9sbG93Lg0KDQo+ICsJd3JpdGVsKEhJQlZU
X0xTQURDX0NPTl9ERU4sIChpbmZvLT5yZWdzICsgSElCVlRfTFNBRENfVElNRVNDQU4pKTsNCj4g
Kw0KPiArCS8qIGRpc2FibGUgaW50ZXJydXB0ICovDQo+ICsJd3JpdGVsKEhJQlZUX0xTQURDX0NP
Tl9ERU4sIChpbmZvLT5yZWdzICsgSElCVlRfTFNBRENfSU5URU4pKTsNCj4gKw0KPiArCS8qIHN0
b3Agc2NhbiAqLw0KPiArCXdyaXRlbChISUJWVF9MU0FEQ19DT05fRU4sIChpbmZvLT5yZWdzICsg
SElCVlRfTFNBRENfU1RPUCkpOyB9DQo+ICsNCj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgaGlidnRf
bHNhZGNfZGF0YSBsc2FkY19kYXRhX3YxID0gew0KPiArCS5udW1fYml0cyA9IEhJQlZUX0xTQURD
X05VTV9CSVRTX1YxLA0KPiArCS5jaGFubmVscyA9IGhpYnZ0X2xzYWRjX2lpb19jaGFubmVscywN
Cj4gKwkubnVtX2NoYW5uZWxzID0gQVJSQVlfU0laRShoaWJ2dF9sc2FkY19paW9fY2hhbm5lbHMp
LA0KPiArDQo+ICsJLmNsZWFyX2lycSA9IGhpYnZ0X2xzYWRjX3YxX2NsZWFyX2lycSwNCj4gKwku
c3RhcnRfY29udiA9IGhpYnZ0X2xzYWRjX3YxX3N0YXJ0X2NvbnYsDQo+ICsJLnN0b3BfY29udiA9
IGhpYnZ0X2xzYWRjX3YxX3N0b3BfY29udiwgfTsNCj4gKw0KPiArc3RhdGljIGNvbnN0IHN0cnVj
dCBvZl9kZXZpY2VfaWQgaGlidnRfbHNhZGNfbWF0Y2hbXSA9IHsNCj4gKwl7DQo+ICsJCS5jb21w
YXRpYmxlID0gImhpc2lsaWNvbixoaTM1MTZjdjMwMC1sc2FkYyIsDQo+ICsJCS5kYXRhID0gJmxz
YWRjX2RhdGFfdjEsDQpUaGUgdXN1YWwgY29udmVudGlvbiBpcyB0byBvbmx5IGludHJvZHVjZSAn
dmFyaWFudCcgdHlwZSBkYXRhIGFzIGEgcHJlY3Vyc29yIHBhdGNoIHRvIGEgc2VyaWVzIGluY2x1
ZGluZyB0aGUgc3VwcG9ydCBvZiBuZXcgcGFydHMuDQoNCkl0IGlzIGFjY2VwdGFibGUgdG8gcG9z
dCBhIHZlcnNpb24gd2l0aCB0aGlzIGluIGlmIHlvdSBhcmUgc2hvcnRseSB0byBzdWJtaXQgdGhl
IGZvbGxvdyB1cCB0aGF0IGFkZHMgb3RoZXIgZGV2aWNlIHN1cHBvcnQuICBJZiB5b3UgYXJlIGRv
aW5nIHRoaXMsIHBsZWFzZSBwdXQgYSBub3RlIGluIHRoZSBwYXRjaCBkZXNjcmlwdGlvbiB0byB0
aGF0IGVmZmVjdC4gIE5vdGUgdGhhdCBpZiB0aGUgYWRkaXRpb25hbCBzdXBwb3J0IGRvZXNuJ3Qg
dHVybiB1cCwgdGhlIGRyaXZlciBtYXkgd2UgZ2V0ICdzaW1wbGlmaWVkJw0KYnkgc29tZW9uZSBl
bHNlLg0KDQpJJ2QgYWxzbyBnZW5lcmFsbHkgZXhwZWN0IHRvIHNlZSB0aGlzIG1hdGNoIHRhYmxl
IGZ1cnRoZXIgZG93biAtIGRpcmVjdGx5IGFib3ZlIHdoZXJlIGl0IGlzIHVzZWQuICBNYWtlcyBm
b3IgZXZlciBzbyBzbGlnaHRseSBlYXNpZXIgcmV2aWV3aW5nIQ0KPiArCX0sDQo+ICsJe30sDQo+
ICt9Ow0KPiArTU9EVUxFX0RFVklDRV9UQUJMRShvZiwgaGlidnRfbHNhZGNfbWF0Y2gpOw0KPiAr
DQo+ICsvKiBSZXNldCBMU0FEQyBDb250cm9sbGVyICovDQo+ICtzdGF0aWMgdm9pZCBoaWJ2dF9s
c2FkY19yZXNldF9jb250cm9sbGVyKHN0cnVjdCByZXNldF9jb250cm9sICpyZXNldCkgDQo+ICt7
DQo+ICsJcmVzZXRfY29udHJvbF9hc3NlcnQocmVzZXQpOw0KPiArCXVzbGVlcF9yYW5nZSgxMCwg
MjApOw0KPiArCXJlc2V0X2NvbnRyb2xfZGVhc3NlcnQocmVzZXQpOw0KPiArfQ0KPiArDQo+ICtz
dGF0aWMgaW50IGhpYnZ0X2xzYWRjX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYp
IHsNCj4gKwlzdHJ1Y3QgaGlidnRfbHNhZGMgKmluZm8gPSBOVUxMOw0KPiArCXN0cnVjdCBkZXZp
Y2Vfbm9kZSAqbnAgPSBwZGV2LT5kZXYub2Zfbm9kZTsNCj4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5k
aW9fZGV2ID0gTlVMTDsNCj4gKwlzdHJ1Y3QgcmVzb3VyY2UJKm1lbTsNCj4gKwljb25zdCBzdHJ1
Y3Qgb2ZfZGV2aWNlX2lkICptYXRjaDsNCj4gKwlpbnQgcmV0Ow0KPiArCWludCBpcnE7DQo+ICsN
Cj4gKwlpZiAoIW5wKQ0KPiArCQlyZXR1cm4gLUVOT0RFVjsNCj4gKw0KPiArCWluZGlvX2RldiA9
IGRldm1faWlvX2RldmljZV9hbGxvYygmcGRldi0+ZGV2LCBzaXplb2YoKmluZm8pKTsNCj4gKwlp
ZiAoIWluZGlvX2Rldikgew0KPiArCQlkZXZfZXJyKCZwZGV2LT5kZXYsICJmYWlsZWQgYWxsb2Nh
dGluZyBpaW8gZGV2aWNlXG4iKTsNCj4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ICsJfQ0KPiArCWlu
Zm8gPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiArDQo+ICsJbWF0Y2ggPSBvZl9tYXRjaF9kZXZp
Y2UoaGlidnRfbHNhZGNfbWF0Y2gsICZwZGV2LT5kZXYpOw0KPiArCWluZm8tPmRhdGEgPSBtYXRj
aC0+ZGF0YTsNCj4gKw0KPiArCW1lbSA9IHBsYXRmb3JtX2dldF9yZXNvdXJjZShwZGV2LCBJT1JF
U09VUkNFX01FTSwgMCk7DQo+ICsJaW5mby0+cmVncyA9IGRldm1faW9yZW1hcF9yZXNvdXJjZSgm
cGRldi0+ZGV2LCBtZW0pOw0KPiArCWlmIChJU19FUlIoaW5mby0+cmVncykpDQo+ICsJCXJldHVy
biBQVFJfRVJSKGluZm8tPnJlZ3MpOw0KPiArDQo+ICsJLyoNCj4gKwkgKiBUaGUgcmVzZXQgc2hv
dWxkIGJlIGFuIG9wdGlvbmFsIHByb3BlcnR5LCBhcyBpdCBzaG91bGQgd29yaw0KPiArCSAqIHdp
dGggb2xkIGRldmljZXRyZWVzIGFzIHdlbGwNCj4gKwkgKi8NCj4gKwlpbmZvLT5yZXNldCA9IGRl
dm1fcmVzZXRfY29udHJvbF9nZXQoJnBkZXYtPmRldiwgImxzYWRjLWNyZyIpOw0KPiArCWlmIChJ
U19FUlIoaW5mby0+cmVzZXQpKSB7DQo+ICsJCXJldCA9IFBUUl9FUlIoaW5mby0+cmVzZXQpOw0K
PiArCQlpZiAocmV0ICE9IC1FTk9FTlQpDQo+ICsJCQlyZXR1cm4gcmV0Ow0KPiArDQo+ICsJCWRl
dl9kYmcoJnBkZXYtPmRldiwgIm5vIHJlc2V0IGNvbnRyb2wgZm91bmRcbiIpOw0KPiArCQlpbmZv
LT5yZXNldCA9IE5VTEw7DQo+ICsJfQ0KPiArDQo+ICsJaW5pdF9jb21wbGV0aW9uKCZpbmZvLT5j
b21wbGV0aW9uKTsNCj4gKw0KPiArCWlycSA9IHBsYXRmb3JtX2dldF9pcnEocGRldiwgMCk7DQo+
ICsJaWYgKGlycSA8IDApIHsNCj4gKwkJZGV2X2VycigmcGRldi0+ZGV2LCAibm8gaXJxIHJlc291
cmNlP1xuIik7DQo+ICsJCXJldHVybiBpcnE7DQo+ICsJfQ0KPiArDQo+ICsJcmV0ID0gZGV2bV9y
ZXF1ZXN0X2lycSgmcGRldi0+ZGV2LCBpcnEsIGhpYnZ0X2xzYWRjX2lzciwNCj4gKwkJCSAgICAg
ICAwLCBkZXZfbmFtZSgmcGRldi0+ZGV2KSwgaW5mbyk7DQo+ICsJaWYgKHJldCA8IDApIHsNCj4g
KwkJZGV2X2VycigmcGRldi0+ZGV2LCAiZmFpbGVkIHJlcXVlc3RpbmcgaXJxICVkXG4iLCBpcnEp
Ow0KPiArCQlyZXR1cm4gcmV0Ow0KPiArCX0NCj4gKw0KPiArCWlmIChpbmZvLT5yZXNldCkNCj4g
KwkJaGlidnRfbHNhZGNfcmVzZXRfY29udHJvbGxlcihpbmZvLT5yZXNldCk7DQo+ICsNCj4gKwlw
bGF0Zm9ybV9zZXRfZHJ2ZGF0YShwZGV2LCBpbmRpb19kZXYpOw0KPiArDQo+ICsJaW5kaW9fZGV2
LT5uYW1lID0gZGV2X25hbWUoJnBkZXYtPmRldik7DQo+ICsJaW5kaW9fZGV2LT5kZXYucGFyZW50
ID0gJnBkZXYtPmRldjsNCj4gKwlpbmRpb19kZXYtPmRldi5vZl9ub2RlID0gcGRldi0+ZGV2Lm9m
X25vZGU7DQo+ICsJaW5kaW9fZGV2LT5pbmZvID0gJmhpYnZ0X2xzYWRjX2lpb19pbmZvOw0KPiAr
CWluZGlvX2Rldi0+bW9kZXMgPSBJTkRJT19ESVJFQ1RfTU9ERTsNCj4gKw0KPiArCWluZGlvX2Rl
di0+Y2hhbm5lbHMgPSBpbmZvLT5kYXRhLT5jaGFubmVsczsNCj4gKwlpbmRpb19kZXYtPm51bV9j
aGFubmVscyA9IGluZm8tPmRhdGEtPm51bV9jaGFubmVsczsNCj4gKw0KPiArCXJldCA9IGRldm1f
aWlvX2RldmljZV9yZWdpc3RlcigmcGRldi0+ZGV2LCBpbmRpb19kZXYpOw0KPiArCWlmIChyZXQg
PCAwKSB7DQo+ICsJCWRldl9lcnIoJnBkZXYtPmRldiwgImZhaWxlZCByZWdpc3RlciBpaW8gZGV2
aWNlXG4iKTsNCj4gKwkJcmV0dXJuIHJldDsNCkRyb3AgdGhpcyByZXR1cm4gcmV0IGFuZCBqdXN0
IHJldHVybiByZXQgaW5zdGVhZCBvZiB0aGUgcmV0dXJuIDAgYmVsb3cuDQo+ICsJfQ0KPiArDQo+
ICsJcmV0dXJuIDA7DQo+ICt9DQo+ICsNCj4gK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVy
IGhpYnZ0X2xzYWRjX2RyaXZlciA9IHsNCj4gKwkucHJvYmUJCT0gaGlidnRfbHNhZGNfcHJvYmUs
DQo+ICsJLmRyaXZlcgkJPSB7DQo+ICsJCS5uYW1lCT0gImhpYnZ0LWxzYWRjIiwNCj4gKwkJLm9m
X21hdGNoX3RhYmxlID0gaGlidnRfbHNhZGNfbWF0Y2gsDQo+ICsJfSwNCj4gK307DQo+ICsNCj4g
K21vZHVsZV9wbGF0Zm9ybV9kcml2ZXIoaGlidnRfbHNhZGNfZHJpdmVyKTsNCj4gKw0KPiArTU9E
VUxFX0FVVEhPUigiQWxsZW4gTGl1IDxsaXVyZW56aG9uZ0BoaXNpbGljb24uY29tPiIpOyANCj4g
K01PRFVMRV9ERVNDUklQVElPTigiaGlzaWxpY29uIEJWVCBMU0FEQyBkcml2ZXIiKTsgTU9EVUxF
X0xJQ0VOU0UoIkdQTCANCj4gK3YyIik7DQo+IA0KDQo=

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

* Re: 答复: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
  2017-02-06 12:19     ` liurenzhong
  (?)
@ 2017-02-06 18:50       ` Jonathan Cameron
  -1 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2017-02-06 18:50 UTC (permalink / raw)
  To: liurenzhong, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, Xuejiancheng, Lixu (kevin)

On 06/02/17 12:19, liurenzhong wrote:
> Hi Jonathan,
Hi Allen,
> 
> Thanks for your suggestion,  I'm sorry to reply after a long time .
> This is some problems ask for your advice:
Not to worry on time, we are all busy people. I've had patches that have
taken me literally years to reply to reviews on ;) (there's one I posted
just yesterday after 3 years.)

One small procedural point. It's always helpful to reply inline within
the patch.  To be honest I couldn't remember what we were referring to in
quite a bit of this, so the context is helpful! (easy enough to follow
here, but it's good practice that helps a lot when things get more complex).
> 
> 1, It's nice to use just "reset/reset.txt" or ".../reset/reset.txt"
> from Rob's suggestion, but in the filtered DT tree,
> "../reset/reset.txt" is used more times, which one we should get,
> "../" or ".../"?
Go with Rob's view on this. Device tree bindings are his (and Mark's)
domain.  If he changes his mind then that's fine as well.

> 
> 2, The ADC on hisilicon BVT socs can work in single scanning mode or
> continuous scanning mode. The single mode get conversion value one
> time after start the configure, while the continuous scanning mode
> will get conversion value each scan time after start the configure
> while stopping the adc configure. For more expansibility, the ADC
> driver use the continuous scanning mode and stop the adc configure
> after get one time conversion value. Is it necessary to change our
> driver to single scanning mode or just add a short description?

I think a short description would be fine. Perhaps as short as:
'A single cycle of continuous mode capture is used for polled operation.
 This stops continuous mode after the cycle is complete.'
or something like that.

It sounds like this continuous sampling functionality will lend itself
nicely to the addition of a streaming (called buffered in iio) mode
in the driver as a possible future addition - but that certainly isn't
necessary to have a useful driver.

> 3, This drvier is only support ADC IF found on hi3516cv300 and
> hi3519v101 now , and future SoCs from Hisilicon BVT would get some
> changes on channel number or getting data ways and so on, so the
> driver use "V1" on match table and add other version while any change
> happens. Which match table can we provide and is't OK like this?
>> +static const struct of_device_id hibvt_lsadc_match[] = {
>> +	{
>> +		.compatible = "hisilicon,hi3516cv300-lsadc",
>> +		.data = &lsadc_data_v1,
>> +	},
>> +	{
>> +		.compatible = "hisilicon,hi3519v101-lsadc",
>> +		.data = &lsadc_data_v1,
>> +	},
>> +	{},
>> +};
That's absolutely fine though I would like a prefix on the lsadc part as
it is generic enough that it's just possible it'll clash with a name in
some header in the future.  The hibvt prefix you have used elsewhere
would be good for this.
.data = &hibvt_lsadc_data_v1,

Also remember that you can always change internal naming in future if a
different naming scheme makes more sense at that time.

You could (for now) drop the data_v1 structure entirely and leave
allowing for other variants as an exercise to be done when they show up.

e.g.:

1) Patch with no variant support is applied.
... some time passes...
2) Patch to add variant support
3) Patch to add new variants.
2, 3 in a series together as nice simple steps with the use of patch 2
becoming clear in patch 3.

This is a common thing to have with drivers as the hardware evolves.

Thanks and looking forward to v2,

Jonathan

> 
> Awaiting for your reply Thank you very much!
> 
> Best regards
> /Allen
> 
> -----邮件原件-----
> 发件人: Jonathan Cameron [mailto:jic23@kernel.org] 
> 发送时间: 2017年1月8日 1:52
> 收件人: liurenzhong <liurenzhong@hisilicon.com>; knaack.h@gmx.de; lars@metafoo.de; pmeerw@pmeerw.net; robh+dt@kernel.org; mark.rutland@arm.com
> 抄送: akinobu.mita@gmail.com; ludovic.desroches@atmel.com; krzk@kernel.org; vilhelm.gray@gmail.com; ksenija.stanojevic@gmail.com; zhiyong.tao@mediatek.com; daniel.baluta@intel.com; leonard.crestez@intel.com; ray.jui@broadcom.com; raveendra.padasalagi@broadcom.com; mranostay@gmail.com; amsfield22@gmail.com; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Xuejiancheng <xuejiancheng@hisilicon.com>; Lixu (kevin) <kevin.lixu@hisilicon.com>
> 主题: Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
> 
> On 07/01/17 05:16, Allen Liu wrote:
>> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
>> The ADC controller is primarily in charge of detecting voltage.
>>
>> Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
>> Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
> Hi Allen,
> 
> One quick submission process note first.  It is very important to clearly identify new versions of a patch and what changes have occurred since the previous posting.
> 
> So the email title should have been [PATCH V2] adc...
> 
> Also, below the --- please add a brief change log.
> 
> The driver is coming together nicely.  A few minor points inline.
> 
> Jonathan
>> ---
>>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
>>  drivers/iio/adc/Kconfig                            |  10 +
>>  drivers/iio/adc/Makefile                           |   1 +
>>  drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
>>  4 files changed, 369 insertions(+)
>>  create mode 100644 
>> Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt 
>> b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>> new file mode 100644
>> index 0000000..fce1ff4
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>> @@ -0,0 +1,23 @@
>> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
>> +
>> +Required properties:
>> +- compatible: should be "hisilicon,<name>-lsadc"
>> +   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
>> +
>> +- reg: physical base address of the controller and length of memory mapped 
>> +	   region.
>> +- interrupts: The interrupt number for the ADC device.
> Ideally refer to the standard interrupt binding document.
> Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> 
>> +
>> +Optional properties:
>> +- resets: Must contain an entry for each entry in reset-names if need support
>> +		  this option. See ../../reset/reset.txt for details.
> Don't use a relative path in a binding document. It's far too likely to be broken by a reorganization of the docs and cannot be grepped for.
>> +- reset-names: Must include the name "lsadc-crg".
>> +
>> +Example:
>> +	adc: adc@120e0000 {
>> +			compatible = "hisilicon,hi3516cv300-lsadc";
>> +			reg = <0x120e0000 0x1000>;
>> +			interrupts = <19>;
>> +			resets = <&crg 0x7c 3>;
>> +			reset-names = "lsadc-crg";
>> +	};
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 
>> 99c0514..0443f51 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -225,6 +225,16 @@ config HI8435
>>  	  This driver can also be built as a module. If so, the module will be
>>  	  called hi8435.
>>  
>> +config HIBVT_LSADC
>> +	tristate "HIBVT LSADC driver"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +	help
>> +	  Say yes here to build support for the LSADC found in SoCs from
>> +	  hisilicon BVT chip.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called hibvt_lsadc.
>> +
>>  config INA2XX_ADC
>>  	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
>>  	depends on I2C && !SENSORS_INA2XX
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 
>> 7a40c04..6554d92 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>>  obj-$(CONFIG_HI8435) += hi8435.o
>> +obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
>>  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
>>  obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
>>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git 
>> a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c new 
>> file mode 100644 index 0000000..aaf2024
>> --- /dev/null
>> +++ b/drivers/iio/adc/hibvt_lsadc.c
>> @@ -0,0 +1,335 @@
>> +/*
>> + * Hisilicon BVT Low Speed (LS) A/D Converter
>> + * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or 
>> +modify
>> + * it under the terms of the GNU General Public License as published 
>> +by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/completion.h>
>> +#include <linux/delay.h>
>> +#include <linux/reset.h>
>> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h>
>> +
>> +/* hisilicon bvt adc registers definitions */
>> +#define HIBVT_LSADC_CONFIG		0x00
>> +#define HIBVT_CONFIG_DEGLITCH	BIT(17)
>> +#define HIBVT_CONFIG_RESET		BIT(15)
>> +#define HIBVT_CONFIG_POWERDOWN	BIT(14)
>> +#define HIBVT_CONFIG_MODE		BIT(13)
>> +#define HIBVT_CONFIG_CHNC		BIT(10)
>> +#define HIBVT_CONFIG_CHNB		BIT(9)
>> +#define HIBVT_CONFIG_CHNA		BIT(8)
>> +
>> +#define HIBVT_LSADC_TIMESCAN	0x08
>> +#define HIBVT_LSADC_INTEN		0x10
>> +#define HIBVT_LSADC_INTSTATUS	0x14
>> +#define HIBVT_LSADC_INTCLR		0x18
>> +#define HIBVT_LSADC_START		0x1C
>> +#define HIBVT_LSADC_STOP		0x20
>> +#define HIBVT_LSADC_ACTBIT		0x24
>> +#define HIBVT_LSADC_CHNDATA		0x2C
>> +
>> +#define HIBVT_LSADC_CON_EN		(1u << 0)
>> +#define HIBVT_LSADC_CON_DEN		(0u << 0)
>> +
>> +#define HIBVT_LSADC_NUM_BITS_V1	10
>> +#define HIBVT_LSADC_CHN_MASK_v1	0x7
>> +
>> +/* fix clk:3000000, default tscan set 10ms */
>> +#define HIBVT_LSADC_TSCAN_MS	(10*3000)
>> +
>> +#define HIBVT_LSADC_TIMEOUT		msecs_to_jiffies(100)
>> +
>> +/* default voltage scale for every channel <mv> */ static int 
>> +g_hibvt_lsadc_voltage[] = {
>> +	3300, 3300, 3300
> Is default due to an external reference voltage or is there an internal regulator?  If it is external it should really be described using the regulator framework.
> 
> Const? 
>> +};
>> +
>> +struct hibvt_lsadc {
>> +	void __iomem		*regs;
>> +	struct completion	completion;
>> +	struct reset_control	*reset;
>> +	const struct hibvt_lsadc_data	*data;
>> +	unsigned int		cur_chn;
>> +	unsigned int		value;
>> +};
>> +
>> +struct hibvt_lsadc_data {
>> +	int				num_bits;
>> +	const struct iio_chan_spec	*channels;
>> +	int				num_channels;
>> +
>> +	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
>> +	void (*start_conv)(struct hibvt_lsadc *info);
>> +	void (*stop_conv)(struct hibvt_lsadc *info); };
>> +
>> +static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
>> +				    struct iio_chan_spec const *chan,
>> +				    int *val, int *val2, long mask) {
>> +	struct hibvt_lsadc *info = iio_priv(indio_dev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		mutex_lock(&indio_dev->mlock);
>> +
>> +		reinit_completion(&info->completion);
>> +
>> +		/* Select the channel to be used */
>> +		info->cur_chn = chan->channel;
>> +
>> +		if (info->data->start_conv)
>> +			info->data->start_conv(info);
>> +
>> +		if (!wait_for_completion_timeout(&info->completion,
>> +							HIBVT_LSADC_TIMEOUT)) {
>> +			if (info->data->stop_conv)
>> +				info->data->stop_conv(info);
>> +			mutex_unlock(&indio_dev->mlock);
>> +			return -ETIMEDOUT;
>> +		}
>> +
>> +		*val = info->value;
>> +		mutex_unlock(&indio_dev->mlock);
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = g_hibvt_lsadc_voltage[chan->channel];
>> +		*val2 = info->data->num_bits;
>> +		return IIO_VAL_FRACTIONAL_LOG2;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id) {
>> +	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
>> +	int mask;
>> +
>> +	mask = readl(info->regs + HIBVT_LSADC_INTSTATUS);
>> +	if ((mask & HIBVT_LSADC_CHN_MASK_v1) == 0)
>> +		return IRQ_NONE;
>> +
>> +	/* Clear irq */
>> +	mask &= HIBVT_LSADC_CHN_MASK_v1;
>> +	if (info->data->clear_irq)
>> +		info->data->clear_irq(info, mask);
>> +
>> +	/* Read value */
>> +	info->value = readl(info->regs +
>> +		HIBVT_LSADC_CHNDATA + (info->cur_chn << 2));
>> +	info->value &= GENMASK(info->data->num_bits - 1, 0);
>> +
>> +	/* stop adc */
>> +	if (info->data->stop_conv)
>> +		info->data->stop_conv(info);
>> +
>> +	complete(&info->completion);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static const struct iio_info hibvt_lsadc_iio_info = {
>> +	.read_raw = hibvt_lsadc_read_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +#define HIBVT_LSADC_CHANNEL(_index, _id) {      \
>> +	.type = IIO_VOLTAGE,                \
>> +	.indexed = 1,						\
>> +	.channel = _index,					\
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>> +			BIT(IIO_CHAN_INFO_SCALE),   \
>> +	.datasheet_name = _id,              \
>> +}
>> +
>> +static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
>> +	HIBVT_LSADC_CHANNEL(0, "adc0"),
>> +	HIBVT_LSADC_CHANNEL(1, "adc1"),
>> +	HIBVT_LSADC_CHANNEL(2, "adc2"),
>> +};
>> +
>> +static void hibvt_lsadc_v1_clear_irq(struct hibvt_lsadc *info, int 
>> +mask) {
>> +	writel(mask, info->regs + HIBVT_LSADC_INTCLR); }
>> +
>> +static void hibvt_lsadc_v1_start_conv(struct hibvt_lsadc *info) {
>> +	unsigned int con;
>> +
>> +	/* set number bit */
> set number of bits?
>> +	con = GENMASK(info->data->num_bits - 1, 0);
>> +	writel(con, (info->regs + HIBVT_LSADC_ACTBIT));
>> +
>> +	/* config */
>> +	con = readl(info->regs + HIBVT_LSADC_CONFIG);
>> +	con &= ~HIBVT_CONFIG_RESET;
>> +	con |= (HIBVT_CONFIG_POWERDOWN | HIBVT_CONFIG_DEGLITCH |
>> +		HIBVT_CONFIG_MODE);
>> +	con &= ~(HIBVT_CONFIG_CHNA | HIBVT_CONFIG_CHNB | HIBVT_CONFIG_CHNC);
>> +	con |= (HIBVT_CONFIG_CHNA << info->cur_chn);
>> +	writel(con, (info->regs + HIBVT_LSADC_CONFIG));
>> +
>> +	/* set timescan */
>> +	writel(HIBVT_LSADC_TSCAN_MS, (info->regs + HIBVT_LSADC_TIMESCAN));
>> +
>> +	/* clear interrupt */
>> +	writel(HIBVT_LSADC_CHN_MASK_v1, info->regs + HIBVT_LSADC_INTCLR);
>> +
>> +	/* enable interrupt */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_INTEN));
>> +
>> +	/* start scan */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_START)); }
>> +
>> +static void hibvt_lsadc_v1_stop_conv(struct hibvt_lsadc *info) {
>> +	/* reset the timescan */
> This isn't a particularly common pice of terminology, perhaps a short
> description here of what timescan is and why we should reset it would
> make the code easier to follow.
> 
>> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_TIMESCAN));
>> +
>> +	/* disable interrupt */
>> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_INTEN));
>> +
>> +	/* stop scan */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_STOP)); }
>> +
>> +static const struct hibvt_lsadc_data lsadc_data_v1 = {
>> +	.num_bits = HIBVT_LSADC_NUM_BITS_V1,
>> +	.channels = hibvt_lsadc_iio_channels,
>> +	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
>> +
>> +	.clear_irq = hibvt_lsadc_v1_clear_irq,
>> +	.start_conv = hibvt_lsadc_v1_start_conv,
>> +	.stop_conv = hibvt_lsadc_v1_stop_conv, };
>> +
>> +static const struct of_device_id hibvt_lsadc_match[] = {
>> +	{
>> +		.compatible = "hisilicon,hi3516cv300-lsadc",
>> +		.data = &lsadc_data_v1,
> The usual convention is to only introduce 'variant' type data as a precursor patch to a series including the support of new parts.
> 
> It is acceptable to post a version with this in if you are shortly to submit the follow up that adds other device support.  If you are doing this, please put a note in the patch description to that effect.  Note that if the additional support doesn't turn up, the driver may we get 'simplified'
> by someone else.
> 
> I'd also generally expect to see this match table further down - directly above where it is used.  Makes for ever so slightly easier reviewing!
>> +	},
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
>> +
>> +/* Reset LSADC Controller */
>> +static void hibvt_lsadc_reset_controller(struct reset_control *reset) 
>> +{
>> +	reset_control_assert(reset);
>> +	usleep_range(10, 20);
>> +	reset_control_deassert(reset);
>> +}
>> +
>> +static int hibvt_lsadc_probe(struct platform_device *pdev) {
>> +	struct hibvt_lsadc *info = NULL;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct iio_dev *indio_dev = NULL;
>> +	struct resource	*mem;
>> +	const struct of_device_id *match;
>> +	int ret;
>> +	int irq;
>> +
>> +	if (!np)
>> +		return -ENODEV;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>> +	if (!indio_dev) {
>> +		dev_err(&pdev->dev, "failed allocating iio device\n");
>> +		return -ENOMEM;
>> +	}
>> +	info = iio_priv(indio_dev);
>> +
>> +	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
>> +	info->data = match->data;
>> +
>> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>> +	if (IS_ERR(info->regs))
>> +		return PTR_ERR(info->regs);
>> +
>> +	/*
>> +	 * The reset should be an optional property, as it should work
>> +	 * with old devicetrees as well
>> +	 */
>> +	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
>> +	if (IS_ERR(info->reset)) {
>> +		ret = PTR_ERR(info->reset);
>> +		if (ret != -ENOENT)
>> +			return ret;
>> +
>> +		dev_dbg(&pdev->dev, "no reset control found\n");
>> +		info->reset = NULL;
>> +	}
>> +
>> +	init_completion(&info->completion);
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(&pdev->dev, "no irq resource?\n");
>> +		return irq;
>> +	}
>> +
>> +	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
>> +			       0, dev_name(&pdev->dev), info);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
>> +		return ret;
>> +	}
>> +
>> +	if (info->reset)
>> +		hibvt_lsadc_reset_controller(info->reset);
>> +
>> +	platform_set_drvdata(pdev, indio_dev);
>> +
>> +	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 = &hibvt_lsadc_iio_info;
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +	indio_dev->channels = info->data->channels;
>> +	indio_dev->num_channels = info->data->num_channels;
>> +
>> +	ret = devm_iio_device_register(&pdev->dev, indio_dev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed register iio device\n");
>> +		return ret;
> Drop this return ret and just return ret instead of the return 0 below.
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver hibvt_lsadc_driver = {
>> +	.probe		= hibvt_lsadc_probe,
>> +	.driver		= {
>> +		.name	= "hibvt-lsadc",
>> +		.of_match_table = hibvt_lsadc_match,
>> +	},
>> +};
>> +
>> +module_platform_driver(hibvt_lsadc_driver);
>> +
>> +MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>"); 
>> +MODULE_DESCRIPTION("hisilicon BVT LSADC driver"); MODULE_LICENSE("GPL 
>> +v2");
>>
> 

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

* Re: 答复: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2017-02-06 18:50       ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2017-02-06 18:50 UTC (permalink / raw)
  To: liurenzhong, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel

On 06/02/17 12:19, liurenzhong wrote:
> Hi Jonathan,
Hi Allen,
> 
> Thanks for your suggestion,  I'm sorry to reply after a long time .
> This is some problems ask for your advice:
Not to worry on time, we are all busy people. I've had patches that have
taken me literally years to reply to reviews on ;) (there's one I posted
just yesterday after 3 years.)

One small procedural point. It's always helpful to reply inline within
the patch.  To be honest I couldn't remember what we were referring to in
quite a bit of this, so the context is helpful! (easy enough to follow
here, but it's good practice that helps a lot when things get more complex).
> 
> 1, It's nice to use just "reset/reset.txt" or ".../reset/reset.txt"
> from Rob's suggestion, but in the filtered DT tree,
> "../reset/reset.txt" is used more times, which one we should get,
> "../" or ".../"?
Go with Rob's view on this. Device tree bindings are his (and Mark's)
domain.  If he changes his mind then that's fine as well.

> 
> 2, The ADC on hisilicon BVT socs can work in single scanning mode or
> continuous scanning mode. The single mode get conversion value one
> time after start the configure, while the continuous scanning mode
> will get conversion value each scan time after start the configure
> while stopping the adc configure. For more expansibility, the ADC
> driver use the continuous scanning mode and stop the adc configure
> after get one time conversion value. Is it necessary to change our
> driver to single scanning mode or just add a short description?

I think a short description would be fine. Perhaps as short as:
'A single cycle of continuous mode capture is used for polled operation.
 This stops continuous mode after the cycle is complete.'
or something like that.

It sounds like this continuous sampling functionality will lend itself
nicely to the addition of a streaming (called buffered in iio) mode
in the driver as a possible future addition - but that certainly isn't
necessary to have a useful driver.

> 3, This drvier is only support ADC IF found on hi3516cv300 and
> hi3519v101 now , and future SoCs from Hisilicon BVT would get some
> changes on channel number or getting data ways and so on, so the
> driver use "V1" on match table and add other version while any change
> happens. Which match table can we provide and is't OK like this?
>> +static const struct of_device_id hibvt_lsadc_match[] = {
>> +	{
>> +		.compatible = "hisilicon,hi3516cv300-lsadc",
>> +		.data = &lsadc_data_v1,
>> +	},
>> +	{
>> +		.compatible = "hisilicon,hi3519v101-lsadc",
>> +		.data = &lsadc_data_v1,
>> +	},
>> +	{},
>> +};
That's absolutely fine though I would like a prefix on the lsadc part as
it is generic enough that it's just possible it'll clash with a name in
some header in the future.  The hibvt prefix you have used elsewhere
would be good for this.
.data = &hibvt_lsadc_data_v1,

Also remember that you can always change internal naming in future if a
different naming scheme makes more sense at that time.

You could (for now) drop the data_v1 structure entirely and leave
allowing for other variants as an exercise to be done when they show up.

e.g.:

1) Patch with no variant support is applied.
... some time passes...
2) Patch to add variant support
3) Patch to add new variants.
2, 3 in a series together as nice simple steps with the use of patch 2
becoming clear in patch 3.

This is a common thing to have with drivers as the hardware evolves.

Thanks and looking forward to v2,

Jonathan

> 
> Awaiting for your reply Thank you very much!
> 
> Best regards
> /Allen
> 
> -----邮件原件-----
> 发件人: Jonathan Cameron [mailto:jic23@kernel.org] 
> 发送时间: 2017年1月8日 1:52
> 收件人: liurenzhong <liurenzhong@hisilicon.com>; knaack.h@gmx.de; lars@metafoo.de; pmeerw@pmeerw.net; robh+dt@kernel.org; mark.rutland@arm.com
> 抄送: akinobu.mita@gmail.com; ludovic.desroches@atmel.com; krzk@kernel.org; vilhelm.gray@gmail.com; ksenija.stanojevic@gmail.com; zhiyong.tao@mediatek.com; daniel.baluta@intel.com; leonard.crestez@intel.com; ray.jui@broadcom.com; raveendra.padasalagi@broadcom.com; mranostay@gmail.com; amsfield22@gmail.com; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Xuejiancheng <xuejiancheng@hisilicon.com>; Lixu (kevin) <kevin.lixu@hisilicon.com>
> 主题: Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
> 
> On 07/01/17 05:16, Allen Liu wrote:
>> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
>> The ADC controller is primarily in charge of detecting voltage.
>>
>> Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
>> Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
> Hi Allen,
> 
> One quick submission process note first.  It is very important to clearly identify new versions of a patch and what changes have occurred since the previous posting.
> 
> So the email title should have been [PATCH V2] adc...
> 
> Also, below the --- please add a brief change log.
> 
> The driver is coming together nicely.  A few minor points inline.
> 
> Jonathan
>> ---
>>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
>>  drivers/iio/adc/Kconfig                            |  10 +
>>  drivers/iio/adc/Makefile                           |   1 +
>>  drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
>>  4 files changed, 369 insertions(+)
>>  create mode 100644 
>> Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt 
>> b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>> new file mode 100644
>> index 0000000..fce1ff4
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>> @@ -0,0 +1,23 @@
>> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
>> +
>> +Required properties:
>> +- compatible: should be "hisilicon,<name>-lsadc"
>> +   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
>> +
>> +- reg: physical base address of the controller and length of memory mapped 
>> +	   region.
>> +- interrupts: The interrupt number for the ADC device.
> Ideally refer to the standard interrupt binding document.
> Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> 
>> +
>> +Optional properties:
>> +- resets: Must contain an entry for each entry in reset-names if need support
>> +		  this option. See ../../reset/reset.txt for details.
> Don't use a relative path in a binding document. It's far too likely to be broken by a reorganization of the docs and cannot be grepped for.
>> +- reset-names: Must include the name "lsadc-crg".
>> +
>> +Example:
>> +	adc: adc@120e0000 {
>> +			compatible = "hisilicon,hi3516cv300-lsadc";
>> +			reg = <0x120e0000 0x1000>;
>> +			interrupts = <19>;
>> +			resets = <&crg 0x7c 3>;
>> +			reset-names = "lsadc-crg";
>> +	};
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 
>> 99c0514..0443f51 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -225,6 +225,16 @@ config HI8435
>>  	  This driver can also be built as a module. If so, the module will be
>>  	  called hi8435.
>>  
>> +config HIBVT_LSADC
>> +	tristate "HIBVT LSADC driver"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +	help
>> +	  Say yes here to build support for the LSADC found in SoCs from
>> +	  hisilicon BVT chip.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called hibvt_lsadc.
>> +
>>  config INA2XX_ADC
>>  	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
>>  	depends on I2C && !SENSORS_INA2XX
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 
>> 7a40c04..6554d92 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>>  obj-$(CONFIG_HI8435) += hi8435.o
>> +obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
>>  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
>>  obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
>>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git 
>> a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c new 
>> file mode 100644 index 0000000..aaf2024
>> --- /dev/null
>> +++ b/drivers/iio/adc/hibvt_lsadc.c
>> @@ -0,0 +1,335 @@
>> +/*
>> + * Hisilicon BVT Low Speed (LS) A/D Converter
>> + * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or 
>> +modify
>> + * it under the terms of the GNU General Public License as published 
>> +by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/completion.h>
>> +#include <linux/delay.h>
>> +#include <linux/reset.h>
>> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h>
>> +
>> +/* hisilicon bvt adc registers definitions */
>> +#define HIBVT_LSADC_CONFIG		0x00
>> +#define HIBVT_CONFIG_DEGLITCH	BIT(17)
>> +#define HIBVT_CONFIG_RESET		BIT(15)
>> +#define HIBVT_CONFIG_POWERDOWN	BIT(14)
>> +#define HIBVT_CONFIG_MODE		BIT(13)
>> +#define HIBVT_CONFIG_CHNC		BIT(10)
>> +#define HIBVT_CONFIG_CHNB		BIT(9)
>> +#define HIBVT_CONFIG_CHNA		BIT(8)
>> +
>> +#define HIBVT_LSADC_TIMESCAN	0x08
>> +#define HIBVT_LSADC_INTEN		0x10
>> +#define HIBVT_LSADC_INTSTATUS	0x14
>> +#define HIBVT_LSADC_INTCLR		0x18
>> +#define HIBVT_LSADC_START		0x1C
>> +#define HIBVT_LSADC_STOP		0x20
>> +#define HIBVT_LSADC_ACTBIT		0x24
>> +#define HIBVT_LSADC_CHNDATA		0x2C
>> +
>> +#define HIBVT_LSADC_CON_EN		(1u << 0)
>> +#define HIBVT_LSADC_CON_DEN		(0u << 0)
>> +
>> +#define HIBVT_LSADC_NUM_BITS_V1	10
>> +#define HIBVT_LSADC_CHN_MASK_v1	0x7
>> +
>> +/* fix clk:3000000, default tscan set 10ms */
>> +#define HIBVT_LSADC_TSCAN_MS	(10*3000)
>> +
>> +#define HIBVT_LSADC_TIMEOUT		msecs_to_jiffies(100)
>> +
>> +/* default voltage scale for every channel <mv> */ static int 
>> +g_hibvt_lsadc_voltage[] = {
>> +	3300, 3300, 3300
> Is default due to an external reference voltage or is there an internal regulator?  If it is external it should really be described using the regulator framework.
> 
> Const? 
>> +};
>> +
>> +struct hibvt_lsadc {
>> +	void __iomem		*regs;
>> +	struct completion	completion;
>> +	struct reset_control	*reset;
>> +	const struct hibvt_lsadc_data	*data;
>> +	unsigned int		cur_chn;
>> +	unsigned int		value;
>> +};
>> +
>> +struct hibvt_lsadc_data {
>> +	int				num_bits;
>> +	const struct iio_chan_spec	*channels;
>> +	int				num_channels;
>> +
>> +	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
>> +	void (*start_conv)(struct hibvt_lsadc *info);
>> +	void (*stop_conv)(struct hibvt_lsadc *info); };
>> +
>> +static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
>> +				    struct iio_chan_spec const *chan,
>> +				    int *val, int *val2, long mask) {
>> +	struct hibvt_lsadc *info = iio_priv(indio_dev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		mutex_lock(&indio_dev->mlock);
>> +
>> +		reinit_completion(&info->completion);
>> +
>> +		/* Select the channel to be used */
>> +		info->cur_chn = chan->channel;
>> +
>> +		if (info->data->start_conv)
>> +			info->data->start_conv(info);
>> +
>> +		if (!wait_for_completion_timeout(&info->completion,
>> +							HIBVT_LSADC_TIMEOUT)) {
>> +			if (info->data->stop_conv)
>> +				info->data->stop_conv(info);
>> +			mutex_unlock(&indio_dev->mlock);
>> +			return -ETIMEDOUT;
>> +		}
>> +
>> +		*val = info->value;
>> +		mutex_unlock(&indio_dev->mlock);
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = g_hibvt_lsadc_voltage[chan->channel];
>> +		*val2 = info->data->num_bits;
>> +		return IIO_VAL_FRACTIONAL_LOG2;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id) {
>> +	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
>> +	int mask;
>> +
>> +	mask = readl(info->regs + HIBVT_LSADC_INTSTATUS);
>> +	if ((mask & HIBVT_LSADC_CHN_MASK_v1) == 0)
>> +		return IRQ_NONE;
>> +
>> +	/* Clear irq */
>> +	mask &= HIBVT_LSADC_CHN_MASK_v1;
>> +	if (info->data->clear_irq)
>> +		info->data->clear_irq(info, mask);
>> +
>> +	/* Read value */
>> +	info->value = readl(info->regs +
>> +		HIBVT_LSADC_CHNDATA + (info->cur_chn << 2));
>> +	info->value &= GENMASK(info->data->num_bits - 1, 0);
>> +
>> +	/* stop adc */
>> +	if (info->data->stop_conv)
>> +		info->data->stop_conv(info);
>> +
>> +	complete(&info->completion);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static const struct iio_info hibvt_lsadc_iio_info = {
>> +	.read_raw = hibvt_lsadc_read_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +#define HIBVT_LSADC_CHANNEL(_index, _id) {      \
>> +	.type = IIO_VOLTAGE,                \
>> +	.indexed = 1,						\
>> +	.channel = _index,					\
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>> +			BIT(IIO_CHAN_INFO_SCALE),   \
>> +	.datasheet_name = _id,              \
>> +}
>> +
>> +static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
>> +	HIBVT_LSADC_CHANNEL(0, "adc0"),
>> +	HIBVT_LSADC_CHANNEL(1, "adc1"),
>> +	HIBVT_LSADC_CHANNEL(2, "adc2"),
>> +};
>> +
>> +static void hibvt_lsadc_v1_clear_irq(struct hibvt_lsadc *info, int 
>> +mask) {
>> +	writel(mask, info->regs + HIBVT_LSADC_INTCLR); }
>> +
>> +static void hibvt_lsadc_v1_start_conv(struct hibvt_lsadc *info) {
>> +	unsigned int con;
>> +
>> +	/* set number bit */
> set number of bits?
>> +	con = GENMASK(info->data->num_bits - 1, 0);
>> +	writel(con, (info->regs + HIBVT_LSADC_ACTBIT));
>> +
>> +	/* config */
>> +	con = readl(info->regs + HIBVT_LSADC_CONFIG);
>> +	con &= ~HIBVT_CONFIG_RESET;
>> +	con |= (HIBVT_CONFIG_POWERDOWN | HIBVT_CONFIG_DEGLITCH |
>> +		HIBVT_CONFIG_MODE);
>> +	con &= ~(HIBVT_CONFIG_CHNA | HIBVT_CONFIG_CHNB | HIBVT_CONFIG_CHNC);
>> +	con |= (HIBVT_CONFIG_CHNA << info->cur_chn);
>> +	writel(con, (info->regs + HIBVT_LSADC_CONFIG));
>> +
>> +	/* set timescan */
>> +	writel(HIBVT_LSADC_TSCAN_MS, (info->regs + HIBVT_LSADC_TIMESCAN));
>> +
>> +	/* clear interrupt */
>> +	writel(HIBVT_LSADC_CHN_MASK_v1, info->regs + HIBVT_LSADC_INTCLR);
>> +
>> +	/* enable interrupt */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_INTEN));
>> +
>> +	/* start scan */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_START)); }
>> +
>> +static void hibvt_lsadc_v1_stop_conv(struct hibvt_lsadc *info) {
>> +	/* reset the timescan */
> This isn't a particularly common pice of terminology, perhaps a short
> description here of what timescan is and why we should reset it would
> make the code easier to follow.
> 
>> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_TIMESCAN));
>> +
>> +	/* disable interrupt */
>> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_INTEN));
>> +
>> +	/* stop scan */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_STOP)); }
>> +
>> +static const struct hibvt_lsadc_data lsadc_data_v1 = {
>> +	.num_bits = HIBVT_LSADC_NUM_BITS_V1,
>> +	.channels = hibvt_lsadc_iio_channels,
>> +	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
>> +
>> +	.clear_irq = hibvt_lsadc_v1_clear_irq,
>> +	.start_conv = hibvt_lsadc_v1_start_conv,
>> +	.stop_conv = hibvt_lsadc_v1_stop_conv, };
>> +
>> +static const struct of_device_id hibvt_lsadc_match[] = {
>> +	{
>> +		.compatible = "hisilicon,hi3516cv300-lsadc",
>> +		.data = &lsadc_data_v1,
> The usual convention is to only introduce 'variant' type data as a precursor patch to a series including the support of new parts.
> 
> It is acceptable to post a version with this in if you are shortly to submit the follow up that adds other device support.  If you are doing this, please put a note in the patch description to that effect.  Note that if the additional support doesn't turn up, the driver may we get 'simplified'
> by someone else.
> 
> I'd also generally expect to see this match table further down - directly above where it is used.  Makes for ever so slightly easier reviewing!
>> +	},
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
>> +
>> +/* Reset LSADC Controller */
>> +static void hibvt_lsadc_reset_controller(struct reset_control *reset) 
>> +{
>> +	reset_control_assert(reset);
>> +	usleep_range(10, 20);
>> +	reset_control_deassert(reset);
>> +}
>> +
>> +static int hibvt_lsadc_probe(struct platform_device *pdev) {
>> +	struct hibvt_lsadc *info = NULL;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct iio_dev *indio_dev = NULL;
>> +	struct resource	*mem;
>> +	const struct of_device_id *match;
>> +	int ret;
>> +	int irq;
>> +
>> +	if (!np)
>> +		return -ENODEV;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>> +	if (!indio_dev) {
>> +		dev_err(&pdev->dev, "failed allocating iio device\n");
>> +		return -ENOMEM;
>> +	}
>> +	info = iio_priv(indio_dev);
>> +
>> +	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
>> +	info->data = match->data;
>> +
>> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>> +	if (IS_ERR(info->regs))
>> +		return PTR_ERR(info->regs);
>> +
>> +	/*
>> +	 * The reset should be an optional property, as it should work
>> +	 * with old devicetrees as well
>> +	 */
>> +	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
>> +	if (IS_ERR(info->reset)) {
>> +		ret = PTR_ERR(info->reset);
>> +		if (ret != -ENOENT)
>> +			return ret;
>> +
>> +		dev_dbg(&pdev->dev, "no reset control found\n");
>> +		info->reset = NULL;
>> +	}
>> +
>> +	init_completion(&info->completion);
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(&pdev->dev, "no irq resource?\n");
>> +		return irq;
>> +	}
>> +
>> +	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
>> +			       0, dev_name(&pdev->dev), info);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
>> +		return ret;
>> +	}
>> +
>> +	if (info->reset)
>> +		hibvt_lsadc_reset_controller(info->reset);
>> +
>> +	platform_set_drvdata(pdev, indio_dev);
>> +
>> +	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 = &hibvt_lsadc_iio_info;
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +	indio_dev->channels = info->data->channels;
>> +	indio_dev->num_channels = info->data->num_channels;
>> +
>> +	ret = devm_iio_device_register(&pdev->dev, indio_dev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed register iio device\n");
>> +		return ret;
> Drop this return ret and just return ret instead of the return 0 below.
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver hibvt_lsadc_driver = {
>> +	.probe		= hibvt_lsadc_probe,
>> +	.driver		= {
>> +		.name	= "hibvt-lsadc",
>> +		.of_match_table = hibvt_lsadc_match,
>> +	},
>> +};
>> +
>> +module_platform_driver(hibvt_lsadc_driver);
>> +
>> +MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>"); 
>> +MODULE_DESCRIPTION("hisilicon BVT LSADC driver"); MODULE_LICENSE("GPL 
>> +v2");
>>
> 

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

* Re: 答复: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2017-02-06 18:50       ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2017-02-06 18:50 UTC (permalink / raw)
  To: liurenzhong, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, Xuejiancheng, Lixu (kevin)

On 06/02/17 12:19, liurenzhong wrote:
> Hi Jonathan,
Hi Allen,
> 
> Thanks for your suggestion,  I'm sorry to reply after a long time .
> This is some problems ask for your advice:
Not to worry on time, we are all busy people. I've had patches that have
taken me literally years to reply to reviews on ;) (there's one I posted
just yesterday after 3 years.)

One small procedural point. It's always helpful to reply inline within
the patch.  To be honest I couldn't remember what we were referring to in
quite a bit of this, so the context is helpful! (easy enough to follow
here, but it's good practice that helps a lot when things get more complex).
> 
> 1, It's nice to use just "reset/reset.txt" or ".../reset/reset.txt"
> from Rob's suggestion, but in the filtered DT tree,
> "../reset/reset.txt" is used more times, which one we should get,
> "../" or ".../"?
Go with Rob's view on this. Device tree bindings are his (and Mark's)
domain.  If he changes his mind then that's fine as well.

> 
> 2, The ADC on hisilicon BVT socs can work in single scanning mode or
> continuous scanning mode. The single mode get conversion value one
> time after start the configure, while the continuous scanning mode
> will get conversion value each scan time after start the configure
> while stopping the adc configure. For more expansibility, the ADC
> driver use the continuous scanning mode and stop the adc configure
> after get one time conversion value. Is it necessary to change our
> driver to single scanning mode or just add a short description?

I think a short description would be fine. Perhaps as short as:
'A single cycle of continuous mode capture is used for polled operation.
 This stops continuous mode after the cycle is complete.'
or something like that.

It sounds like this continuous sampling functionality will lend itself
nicely to the addition of a streaming (called buffered in iio) mode
in the driver as a possible future addition - but that certainly isn't
necessary to have a useful driver.

> 3, This drvier is only support ADC IF found on hi3516cv300 and
> hi3519v101 now , and future SoCs from Hisilicon BVT would get some
> changes on channel number or getting data ways and so on, so the
> driver use "V1" on match table and add other version while any change
> happens. Which match table can we provide and is't OK like this?
>> +static const struct of_device_id hibvt_lsadc_match[] = {
>> +	{
>> +		.compatible = "hisilicon,hi3516cv300-lsadc",
>> +		.data = &lsadc_data_v1,
>> +	},
>> +	{
>> +		.compatible = "hisilicon,hi3519v101-lsadc",
>> +		.data = &lsadc_data_v1,
>> +	},
>> +	{},
>> +};
That's absolutely fine though I would like a prefix on the lsadc part as
it is generic enough that it's just possible it'll clash with a name in
some header in the future.  The hibvt prefix you have used elsewhere
would be good for this.
.data = &hibvt_lsadc_data_v1,

Also remember that you can always change internal naming in future if a
different naming scheme makes more sense at that time.

You could (for now) drop the data_v1 structure entirely and leave
allowing for other variants as an exercise to be done when they show up.

e.g.:

1) Patch with no variant support is applied.
... some time passes...
2) Patch to add variant support
3) Patch to add new variants.
2, 3 in a series together as nice simple steps with the use of patch 2
becoming clear in patch 3.

This is a common thing to have with drivers as the hardware evolves.

Thanks and looking forward to v2,

Jonathan

> 
> Awaiting for your reply Thank you very much!
> 
> Best regards
> /Allen
> 
> -----邮件原件-----
> 发件人: Jonathan Cameron [mailto:jic23@kernel.org] 
> 发送时间: 2017年1月8日 1:52
> 收件人: liurenzhong <liurenzhong@hisilicon.com>; knaack.h@gmx.de; lars@metafoo.de; pmeerw@pmeerw.net; robh+dt@kernel.org; mark.rutland@arm.com
> 抄送: akinobu.mita@gmail.com; ludovic.desroches@atmel.com; krzk@kernel.org; vilhelm.gray@gmail.com; ksenija.stanojevic@gmail.com; zhiyong.tao@mediatek.com; daniel.baluta@intel.com; leonard.crestez@intel.com; ray.jui@broadcom.com; raveendra.padasalagi@broadcom.com; mranostay@gmail.com; amsfield22@gmail.com; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Xuejiancheng <xuejiancheng@hisilicon.com>; Lixu (kevin) <kevin.lixu@hisilicon.com>
> 主题: Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
> 
> On 07/01/17 05:16, Allen Liu wrote:
>> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
>> The ADC controller is primarily in charge of detecting voltage.
>>
>> Reviewed-by: Kevin Li <kevin.lixu@hisilicon.com>
>> Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
> Hi Allen,
> 
> One quick submission process note first.  It is very important to clearly identify new versions of a patch and what changes have occurred since the previous posting.
> 
> So the email title should have been [PATCH V2] adc...
> 
> Also, below the --- please add a brief change log.
> 
> The driver is coming together nicely.  A few minor points inline.
> 
> Jonathan
>> ---
>>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  23 ++
>>  drivers/iio/adc/Kconfig                            |  10 +
>>  drivers/iio/adc/Makefile                           |   1 +
>>  drivers/iio/adc/hibvt_lsadc.c                      | 335 +++++++++++++++++++++
>>  4 files changed, 369 insertions(+)
>>  create mode 100644 
>> Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt 
>> b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>> new file mode 100644
>> index 0000000..fce1ff4
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>> @@ -0,0 +1,23 @@
>> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
>> +
>> +Required properties:
>> +- compatible: should be "hisilicon,<name>-lsadc"
>> +   - "hisilicon,hi3516cv300-lsadc": for hi3516cv300
>> +
>> +- reg: physical base address of the controller and length of memory mapped 
>> +	   region.
>> +- interrupts: The interrupt number for the ADC device.
> Ideally refer to the standard interrupt binding document.
> Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> 
>> +
>> +Optional properties:
>> +- resets: Must contain an entry for each entry in reset-names if need support
>> +		  this option. See ../../reset/reset.txt for details.
> Don't use a relative path in a binding document. It's far too likely to be broken by a reorganization of the docs and cannot be grepped for.
>> +- reset-names: Must include the name "lsadc-crg".
>> +
>> +Example:
>> +	adc: adc@120e0000 {
>> +			compatible = "hisilicon,hi3516cv300-lsadc";
>> +			reg = <0x120e0000 0x1000>;
>> +			interrupts = <19>;
>> +			resets = <&crg 0x7c 3>;
>> +			reset-names = "lsadc-crg";
>> +	};
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 
>> 99c0514..0443f51 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -225,6 +225,16 @@ config HI8435
>>  	  This driver can also be built as a module. If so, the module will be
>>  	  called hi8435.
>>  
>> +config HIBVT_LSADC
>> +	tristate "HIBVT LSADC driver"
>> +	depends on ARCH_HISI || COMPILE_TEST
>> +	help
>> +	  Say yes here to build support for the LSADC found in SoCs from
>> +	  hisilicon BVT chip.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called hibvt_lsadc.
>> +
>>  config INA2XX_ADC
>>  	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
>>  	depends on I2C && !SENSORS_INA2XX
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 
>> 7a40c04..6554d92 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>>  obj-$(CONFIG_HI8435) += hi8435.o
>> +obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
>>  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
>>  obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
>>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git 
>> a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c new 
>> file mode 100644 index 0000000..aaf2024
>> --- /dev/null
>> +++ b/drivers/iio/adc/hibvt_lsadc.c
>> @@ -0,0 +1,335 @@
>> +/*
>> + * Hisilicon BVT Low Speed (LS) A/D Converter
>> + * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or 
>> +modify
>> + * it under the terms of the GNU General Public License as published 
>> +by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/completion.h>
>> +#include <linux/delay.h>
>> +#include <linux/reset.h>
>> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h>
>> +
>> +/* hisilicon bvt adc registers definitions */
>> +#define HIBVT_LSADC_CONFIG		0x00
>> +#define HIBVT_CONFIG_DEGLITCH	BIT(17)
>> +#define HIBVT_CONFIG_RESET		BIT(15)
>> +#define HIBVT_CONFIG_POWERDOWN	BIT(14)
>> +#define HIBVT_CONFIG_MODE		BIT(13)
>> +#define HIBVT_CONFIG_CHNC		BIT(10)
>> +#define HIBVT_CONFIG_CHNB		BIT(9)
>> +#define HIBVT_CONFIG_CHNA		BIT(8)
>> +
>> +#define HIBVT_LSADC_TIMESCAN	0x08
>> +#define HIBVT_LSADC_INTEN		0x10
>> +#define HIBVT_LSADC_INTSTATUS	0x14
>> +#define HIBVT_LSADC_INTCLR		0x18
>> +#define HIBVT_LSADC_START		0x1C
>> +#define HIBVT_LSADC_STOP		0x20
>> +#define HIBVT_LSADC_ACTBIT		0x24
>> +#define HIBVT_LSADC_CHNDATA		0x2C
>> +
>> +#define HIBVT_LSADC_CON_EN		(1u << 0)
>> +#define HIBVT_LSADC_CON_DEN		(0u << 0)
>> +
>> +#define HIBVT_LSADC_NUM_BITS_V1	10
>> +#define HIBVT_LSADC_CHN_MASK_v1	0x7
>> +
>> +/* fix clk:3000000, default tscan set 10ms */
>> +#define HIBVT_LSADC_TSCAN_MS	(10*3000)
>> +
>> +#define HIBVT_LSADC_TIMEOUT		msecs_to_jiffies(100)
>> +
>> +/* default voltage scale for every channel <mv> */ static int 
>> +g_hibvt_lsadc_voltage[] = {
>> +	3300, 3300, 3300
> Is default due to an external reference voltage or is there an internal regulator?  If it is external it should really be described using the regulator framework.
> 
> Const? 
>> +};
>> +
>> +struct hibvt_lsadc {
>> +	void __iomem		*regs;
>> +	struct completion	completion;
>> +	struct reset_control	*reset;
>> +	const struct hibvt_lsadc_data	*data;
>> +	unsigned int		cur_chn;
>> +	unsigned int		value;
>> +};
>> +
>> +struct hibvt_lsadc_data {
>> +	int				num_bits;
>> +	const struct iio_chan_spec	*channels;
>> +	int				num_channels;
>> +
>> +	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
>> +	void (*start_conv)(struct hibvt_lsadc *info);
>> +	void (*stop_conv)(struct hibvt_lsadc *info); };
>> +
>> +static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
>> +				    struct iio_chan_spec const *chan,
>> +				    int *val, int *val2, long mask) {
>> +	struct hibvt_lsadc *info = iio_priv(indio_dev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		mutex_lock(&indio_dev->mlock);
>> +
>> +		reinit_completion(&info->completion);
>> +
>> +		/* Select the channel to be used */
>> +		info->cur_chn = chan->channel;
>> +
>> +		if (info->data->start_conv)
>> +			info->data->start_conv(info);
>> +
>> +		if (!wait_for_completion_timeout(&info->completion,
>> +							HIBVT_LSADC_TIMEOUT)) {
>> +			if (info->data->stop_conv)
>> +				info->data->stop_conv(info);
>> +			mutex_unlock(&indio_dev->mlock);
>> +			return -ETIMEDOUT;
>> +		}
>> +
>> +		*val = info->value;
>> +		mutex_unlock(&indio_dev->mlock);
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = g_hibvt_lsadc_voltage[chan->channel];
>> +		*val2 = info->data->num_bits;
>> +		return IIO_VAL_FRACTIONAL_LOG2;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id) {
>> +	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
>> +	int mask;
>> +
>> +	mask = readl(info->regs + HIBVT_LSADC_INTSTATUS);
>> +	if ((mask & HIBVT_LSADC_CHN_MASK_v1) == 0)
>> +		return IRQ_NONE;
>> +
>> +	/* Clear irq */
>> +	mask &= HIBVT_LSADC_CHN_MASK_v1;
>> +	if (info->data->clear_irq)
>> +		info->data->clear_irq(info, mask);
>> +
>> +	/* Read value */
>> +	info->value = readl(info->regs +
>> +		HIBVT_LSADC_CHNDATA + (info->cur_chn << 2));
>> +	info->value &= GENMASK(info->data->num_bits - 1, 0);
>> +
>> +	/* stop adc */
>> +	if (info->data->stop_conv)
>> +		info->data->stop_conv(info);
>> +
>> +	complete(&info->completion);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static const struct iio_info hibvt_lsadc_iio_info = {
>> +	.read_raw = hibvt_lsadc_read_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +#define HIBVT_LSADC_CHANNEL(_index, _id) {      \
>> +	.type = IIO_VOLTAGE,                \
>> +	.indexed = 1,						\
>> +	.channel = _index,					\
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>> +			BIT(IIO_CHAN_INFO_SCALE),   \
>> +	.datasheet_name = _id,              \
>> +}
>> +
>> +static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
>> +	HIBVT_LSADC_CHANNEL(0, "adc0"),
>> +	HIBVT_LSADC_CHANNEL(1, "adc1"),
>> +	HIBVT_LSADC_CHANNEL(2, "adc2"),
>> +};
>> +
>> +static void hibvt_lsadc_v1_clear_irq(struct hibvt_lsadc *info, int 
>> +mask) {
>> +	writel(mask, info->regs + HIBVT_LSADC_INTCLR); }
>> +
>> +static void hibvt_lsadc_v1_start_conv(struct hibvt_lsadc *info) {
>> +	unsigned int con;
>> +
>> +	/* set number bit */
> set number of bits?
>> +	con = GENMASK(info->data->num_bits - 1, 0);
>> +	writel(con, (info->regs + HIBVT_LSADC_ACTBIT));
>> +
>> +	/* config */
>> +	con = readl(info->regs + HIBVT_LSADC_CONFIG);
>> +	con &= ~HIBVT_CONFIG_RESET;
>> +	con |= (HIBVT_CONFIG_POWERDOWN | HIBVT_CONFIG_DEGLITCH |
>> +		HIBVT_CONFIG_MODE);
>> +	con &= ~(HIBVT_CONFIG_CHNA | HIBVT_CONFIG_CHNB | HIBVT_CONFIG_CHNC);
>> +	con |= (HIBVT_CONFIG_CHNA << info->cur_chn);
>> +	writel(con, (info->regs + HIBVT_LSADC_CONFIG));
>> +
>> +	/* set timescan */
>> +	writel(HIBVT_LSADC_TSCAN_MS, (info->regs + HIBVT_LSADC_TIMESCAN));
>> +
>> +	/* clear interrupt */
>> +	writel(HIBVT_LSADC_CHN_MASK_v1, info->regs + HIBVT_LSADC_INTCLR);
>> +
>> +	/* enable interrupt */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_INTEN));
>> +
>> +	/* start scan */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_START)); }
>> +
>> +static void hibvt_lsadc_v1_stop_conv(struct hibvt_lsadc *info) {
>> +	/* reset the timescan */
> This isn't a particularly common pice of terminology, perhaps a short
> description here of what timescan is and why we should reset it would
> make the code easier to follow.
> 
>> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_TIMESCAN));
>> +
>> +	/* disable interrupt */
>> +	writel(HIBVT_LSADC_CON_DEN, (info->regs + HIBVT_LSADC_INTEN));
>> +
>> +	/* stop scan */
>> +	writel(HIBVT_LSADC_CON_EN, (info->regs + HIBVT_LSADC_STOP)); }
>> +
>> +static const struct hibvt_lsadc_data lsadc_data_v1 = {
>> +	.num_bits = HIBVT_LSADC_NUM_BITS_V1,
>> +	.channels = hibvt_lsadc_iio_channels,
>> +	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
>> +
>> +	.clear_irq = hibvt_lsadc_v1_clear_irq,
>> +	.start_conv = hibvt_lsadc_v1_start_conv,
>> +	.stop_conv = hibvt_lsadc_v1_stop_conv, };
>> +
>> +static const struct of_device_id hibvt_lsadc_match[] = {
>> +	{
>> +		.compatible = "hisilicon,hi3516cv300-lsadc",
>> +		.data = &lsadc_data_v1,
> The usual convention is to only introduce 'variant' type data as a precursor patch to a series including the support of new parts.
> 
> It is acceptable to post a version with this in if you are shortly to submit the follow up that adds other device support.  If you are doing this, please put a note in the patch description to that effect.  Note that if the additional support doesn't turn up, the driver may we get 'simplified'
> by someone else.
> 
> I'd also generally expect to see this match table further down - directly above where it is used.  Makes for ever so slightly easier reviewing!
>> +	},
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
>> +
>> +/* Reset LSADC Controller */
>> +static void hibvt_lsadc_reset_controller(struct reset_control *reset) 
>> +{
>> +	reset_control_assert(reset);
>> +	usleep_range(10, 20);
>> +	reset_control_deassert(reset);
>> +}
>> +
>> +static int hibvt_lsadc_probe(struct platform_device *pdev) {
>> +	struct hibvt_lsadc *info = NULL;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct iio_dev *indio_dev = NULL;
>> +	struct resource	*mem;
>> +	const struct of_device_id *match;
>> +	int ret;
>> +	int irq;
>> +
>> +	if (!np)
>> +		return -ENODEV;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>> +	if (!indio_dev) {
>> +		dev_err(&pdev->dev, "failed allocating iio device\n");
>> +		return -ENOMEM;
>> +	}
>> +	info = iio_priv(indio_dev);
>> +
>> +	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
>> +	info->data = match->data;
>> +
>> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>> +	if (IS_ERR(info->regs))
>> +		return PTR_ERR(info->regs);
>> +
>> +	/*
>> +	 * The reset should be an optional property, as it should work
>> +	 * with old devicetrees as well
>> +	 */
>> +	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
>> +	if (IS_ERR(info->reset)) {
>> +		ret = PTR_ERR(info->reset);
>> +		if (ret != -ENOENT)
>> +			return ret;
>> +
>> +		dev_dbg(&pdev->dev, "no reset control found\n");
>> +		info->reset = NULL;
>> +	}
>> +
>> +	init_completion(&info->completion);
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(&pdev->dev, "no irq resource?\n");
>> +		return irq;
>> +	}
>> +
>> +	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
>> +			       0, dev_name(&pdev->dev), info);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
>> +		return ret;
>> +	}
>> +
>> +	if (info->reset)
>> +		hibvt_lsadc_reset_controller(info->reset);
>> +
>> +	platform_set_drvdata(pdev, indio_dev);
>> +
>> +	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 = &hibvt_lsadc_iio_info;
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +	indio_dev->channels = info->data->channels;
>> +	indio_dev->num_channels = info->data->num_channels;
>> +
>> +	ret = devm_iio_device_register(&pdev->dev, indio_dev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed register iio device\n");
>> +		return ret;
> Drop this return ret and just return ret instead of the return 0 below.
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver hibvt_lsadc_driver = {
>> +	.probe		= hibvt_lsadc_probe,
>> +	.driver		= {
>> +		.name	= "hibvt-lsadc",
>> +		.of_match_table = hibvt_lsadc_match,
>> +	},
>> +};
>> +
>> +module_platform_driver(hibvt_lsadc_driver);
>> +
>> +MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>"); 
>> +MODULE_DESCRIPTION("hisilicon BVT LSADC driver"); MODULE_LICENSE("GPL 
>> +v2");
>>
> 


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

* Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
  2016-12-24  1:54 ` Allen Liu
                   ` (2 preceding siblings ...)
  (?)
@ 2017-01-03 15:30 ` Rob Herring
  -1 siblings, 0 replies; 19+ messages in thread
From: Rob Herring @ 2017-01-03 15:30 UTC (permalink / raw)
  To: Allen Liu
  Cc: jic23, knaack.h, lars, pmeerw, mark.rutland, akinobu.mita,
	ludovic.desroches, krzk, vilhelm.gray, ksenija.stanojevic,
	zhiyong.tao, daniel.baluta, leonard.crestez, ray.jui,
	raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, xuejiancheng, kevin.lixu

On Sat, Dec 24, 2016 at 09:54:57AM +0800, Allen Liu wrote:
> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
> The ADC controller is primarily in charge of detecting voltage.
> 
> Reviewed-by: Jiancheng Xue <xuejiancheng@hisilicon.com>
> Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
> ---
>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
>  drivers/iio/adc/Kconfig                            |  10 +
>  drivers/iio/adc/Makefile                           |   1 +
>  drivers/iio/adc/hibvt_lsadc.c                      | 344 +++++++++++++++++++++
>  4 files changed, 381 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> new file mode 100644
> index 0000000..63de46e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> @@ -0,0 +1,26 @@
> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
> +
> +Required properties:
> +- compatible: should be "hisilicon,<name>-lsadc"
> +   - "hisilicon,hibvt-lsadc": for hi3516cv300

Use the SoC name in the compatible string.

> +
> +- reg: physical base address of the controller and length of memory mapped
> +       region.
> +- interrupts: The interrupt number to the cpu. The interrupt specifier format
> +              depends on the interrupt controller.
> +- #io-channel-cells: Should be 1, see ../iio-bindings.txt
> +
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +	  this option. See ../reset/reset.txt for details.
> +- reset-names: Must include the name "saradc-apb".
> +
> +Example:
> +	lsadc: hibvt-lsadc@120e0000 {

Just "adc@..."

> +			compatible = "hisilicon,hibvt-lsadc";
> +			reg = <0x120e0000 0x1000>;
> +			interrupts = <19>;
> +			resets = <&crg 0x7c 3>;
> +			reset-names = "lsadc-crg";
> +			status = "disabled";
> +	};

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

* RE:  [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-26 11:05     ` liurenzhong
  0 siblings, 0 replies; 19+ messages in thread
From: liurenzhong @ 2016-12-26 11:05 UTC (permalink / raw)
  To: Jonathan Cameron, jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel

Hi Jonathan,

Thanks very much for your reply , it get me great courage to continue this upstreaming . 

we will make a careful analysis of your suggestion and update a new patch after a few days.

Best regards
/Allen

/---------------------/
From: Jonathan Cameron [mailto:jic23@jic23.retrosnub.co.uk] 
Sent: 24 December 2016 19:46
To: liurenzhong; jic23@kernel.org; knaack.h@gmx.de; lars@metafoo.de; pmeerw@pmeerw.net; robh+dt@kernel.org; mark.rutland@arm.com
Cc: akinobu.mita@gmail.com; ludovic.desroches@atmel.com; krzk@kernel.org; vilhelm.gray@gmail.com; ksenija.stanojevic@gmail.com; zhiyong.tao@mediatek.com; daniel.baluta@intel.com; leonard.crestez@intel.com; ray.jui@broadcom.com; raveendra.padasalagi@broadcom.com; mranostay@gmail.com; amsfield22@gmail.com; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Xuejiancheng; Lixu (kevin)
Subject: Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs

On 24 December 2016 01:54:57 GMT+00:00, Allen Liu <liurenzhong@hisilicon.com> wrote:
>Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like 
>Hi3516CV300, etc.
>The ADC controller is primarily in charge of detecting voltage.
>
>Reviewed-by: Jiancheng Xue <xuejiancheng@hisilicon.com>
>Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
Reading on phone so may not be that thorough!

Looks pretty good. The device abstraction makes it slightly more complicated than it needs to be.  If you aren't going to follow up quickly with other device support please drop the  abstraction. It can be easily readded when needed. 

Various little things inline.

Thanks

Jonathan
>---
> .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
> drivers/iio/adc/Kconfig                            |  10 +
> drivers/iio/adc/Makefile                           |   1 +
>drivers/iio/adc/hibvt_lsadc.c                      | 344
>+++++++++++++++++++++
> 4 files changed, 381 insertions(+)
>create mode 100644
>Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> create mode 100644 drivers/iio/adc/hibvt_lsadc.c
>
>diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>new file mode 100644
>index 0000000..63de46e
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>@@ -0,0 +1,26 @@
>+Hisilicon BVT Low Speed (LS) A/D Converter bindings
>+
>+Required properties:
>+- compatible: should be "hisilicon,<name>-lsadc"
>+   - "hisilicon,hibvt-lsadc": for hi3516cv300
>+
>+- reg: physical base address of the controller and length of memory
>mapped
>+       region.
>+- interrupts: The interrupt number to the cpu. The interrupt specifier
>format
>+              depends on the interrupt controller.
A cross reference to the interrupt bindings doc always good to add.
>+- #io-channel-cells: Should be 1, see ../iio-bindings.txt
>+
>+Optional properties:
>+- resets: Must contain an entry for each entry in reset-names if need
>support
>+	  this option. See ../reset/reset.txt for details.
>+- reset-names: Must include the name "saradc-apb".
>+
>+Example:
>+	lsadc: hibvt-lsadc@120e0000 {
>+			compatible = "hisilicon,hibvt-lsadc";
>+			reg = <0x120e0000 0x1000>;
>+			interrupts = <19>;
>+			resets = <&crg 0x7c 3>;
>+			reset-names = "lsadc-crg";
Doesn't contain saradc-apb which docs say it must...
>+			status = "disabled";
Not documented...
>+	};
>diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 
>99c0514..0443f51 100644
>--- a/drivers/iio/adc/Kconfig
>+++ b/drivers/iio/adc/Kconfig
>@@ -225,6 +225,16 @@ config HI8435
>	  This driver can also be built as a module. If so, the module will be
> 	  called hi8435.
> 
>+config HIBVT_LSADC
>+	tristate "HIBVT LSADC driver"
>+	depends on ARCH_HISI || COMPILE_TEST
>+	help
>+	  Say yes here to build support for the LSADC found in SoCs from
>+	  hisilicon BVT chip.
>+
>+	  To compile this driver as a module, choose M here: the
>+	  module will be called hibvt_lsadc.
>+
> config INA2XX_ADC
> 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
> 	depends on I2C && !SENSORS_INA2XX
>diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 
>7a40c04..6554d92 100644
>--- a/drivers/iio/adc/Makefile
>+++ b/drivers/iio/adc/Makefile
>@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> obj-$(CONFIG_HI8435) += hi8435.o
>+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
> obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
> obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git 
>a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c new 
>file mode 100644 index 0000000..a20afe8
>--- /dev/null
>+++ b/drivers/iio/adc/hibvt_lsadc.c
>@@ -0,0 +1,344 @@
>+/*
>+ * Hisilicon BVT Low Speed (LS) A/D Converter
>+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
>+ *
>+ * This program is free software; you can redistribute it and/or
>modify
>+ * it under the terms of the GNU General Public License as published
>by
>+ * the Free Software Foundation; either version 2 of the License, or
>+ * (at your option) any later version.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/platform_device.h>
>+#include <linux/interrupt.h>
>+#include <linux/io.h>
>+#include <linux/of.h>
>+#include <linux/of_device.h>
>+#include <linux/clk.h>
>+#include <linux/completion.h>
>+#include <linux/delay.h>
>+#include <linux/reset.h>
>+#include <linux/regulator/consumer.h>
>+#include <linux/iio/iio.h>
>+
>+/* hisilicon bvt adc registers definitions */
>+#define LSADC_CONFIG		0x00
>+#define CONFIG_DEGLITCH		BIT(17)
Please add a driver specific prefix to all defines to keep them in their own namespace.
>+#define CONFIG_RESET		BIT(15)
>+#define CONFIG_POWERDOWN	BIT(14)
>+#define CONFIG_MODE			BIT(13)
>+#define CONFIG_CHC_VALID	BIT(10)
>+#define CONFIG_CHB_VALID	BIT(9)
>+#define CONFIG_CHA_VALID	BIT(8)
>+
>+#define LSADC_TIMESCAN		0x08
Lsadc is perhaps to generic a prefix. Clash chances are a bit high
>+#define LSADC_INTEN			0x10
>+#define LSADC_INTSTATUS		0x14
>+#define LSADC_INTCLR		0x18
>+#define LSADC_START			0x1C
>+#define LSADC_STOP			0x20
>+#define LSADC_ACTBIT		0x24
>+#define LSADC_CHNDATA		0x2C
>+
>+#define ADC_CON_EN			(1u << 0)
>+#define ADC_CON_DEN			(0u << 0)
>+
>+#define ADC_NUM_BITS		10
>+
>+/* fix clk:3000000, default tscan set 10ms */
>+#define DEF_ADC_TSCAN_MS	(10*3000)
>+
>+#define LSADC_CHN_MASK		0x7
>+
>+#define LSADC_TIMEOUT		msecs_to_jiffies(100)
>+
>+/* default voltage scale for every channel <mv> */ static int 
>+g_voltage[] = {
>+	3300, 3300, 3300
>+};
Prefix these as well.
>+
>+struct hibvt_lsadc {
>+	void __iomem		*regs;
>+	struct completion	completion;
>+	struct reset_control	*reset;
>+	const struct hibvt_lsadc_data	*data;
>+	unsigned int		cur_chn;
>+	unsigned int		value;
>+};
>+
>+struct hibvt_lsadc_data {
>+	int				num_bits;
>+	const struct iio_chan_spec	*channels;
>+	int				num_channels;
>+
>+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
>+	void (*start_conv)(struct hibvt_lsadc *info);
>+	void (*stop_conv)(struct hibvt_lsadc *info); };
>+
>+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
>+				    struct iio_chan_spec const *chan,
>+				    int *val, int *val2, long mask) {
>+	struct hibvt_lsadc *info = iio_priv(indio_dev);
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_RAW:
>+		mutex_lock(&indio_dev->mlock);
>+
>+		reinit_completion(&info->completion);
>+
>+		/* Select the channel to be used */
>+		info->cur_chn = chan->channel;
>+
>+		if (info->data->start_conv)
>+			info->data->start_conv(info);
>+
>+		if (!wait_for_completion_timeout(&info->completion,
>+							LSADC_TIMEOUT)) {
>+			if (info->data->stop_conv)
>+				info->data->stop_conv(info);
>+			mutex_unlock(&indio_dev->mlock);
>+			return -ETIMEDOUT;
>+		}
>+
>+		*val = info->value;
>+		mutex_unlock(&indio_dev->mlock);
>+		return IIO_VAL_INT;
>+	case IIO_CHAN_INFO_SCALE:
>+		*val = g_voltage[chan->channel];
>+		*val2 = info->data->num_bits;
>+		return IIO_VAL_FRACTIONAL_LOG2;
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id) {
>+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
>+	int mask;
>+
>+	mask = readl(info->regs + LSADC_INTSTATUS);
>+	mask &= LSADC_CHN_MASK;
>+
>+	/* Clear irq */
>+	if (info->data->clear_irq)
>+		info->data->clear_irq(info, mask);
>+
>+	/* Read value */
>+	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn <<
>2));
>+	info->value &= GENMASK(info->data->num_bits - 1, 0);
>+
>+	/* stop adc */
>+	if (info->data->stop_conv)
>+		info->data->stop_conv(info);
>+
>+	complete(&info->completion);
>+
>+	return IRQ_HANDLED;
>+}
>+
>+static const struct iio_info hibvt_lsadc_iio_info = {
>+	.read_raw = hibvt_lsadc_read_raw,
>+	.driver_module = THIS_MODULE,
>+};
>+
>+#define ADC_CHANNEL(_index, _id) {      \
Prefix this define. 
>+	.type = IIO_VOLTAGE,                \
>+	.indexed = 1,						\
>+	.channel = _index,					\
>+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>+			BIT(IIO_CHAN_INFO_SCALE),   \
>+	.datasheet_name = _id,              \
>+}
>+
>+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
>+	ADC_CHANNEL(0, "adc0"),
>+	ADC_CHANNEL(1, "adc1"),
>+	ADC_CHANNEL(2, "adc2"),
>+};
>+
>+static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask) 
>+{
>+	writel(mask, info->regs + LSADC_INTCLR); }
>+
>+static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info) {
>+	unsigned int con;
>+
>+	/* set number bit */
>+	con = GENMASK(info->data->num_bits - 1, 0);
>+	writel(con, (info->regs + LSADC_ACTBIT));
>+
>+    /* config */
>+	con = readl(info->regs + LSADC_CONFIG);
>+	con &= ~CONFIG_RESET;
>+	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
>+	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
>+	con |= (CONFIG_CHA_VALID << info->cur_chn);
>+	writel(con, (info->regs + LSADC_CONFIG));
>+
>+	/* set timescan */
>+	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
>+
>+	/* clear interrupt */
>+	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
>+
>+	/* enable interrupt */
>+	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
>+
>+	/* start scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_START)); }
>+
>+static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info) {
>+	/* reset the timescan */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
>+
>+	/* disable interrupt */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
>+
>+	/* stop scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_STOP)); }
>+
>+static const struct hibvt_lsadc_data lsadc_data = {
>+	.num_bits = ADC_NUM_BITS,
>+	.channels = hibvt_lsadc_iio_channels,
>+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
>+
>+	.clear_irq	= hibvt_lsadc_clear_irq,
>+	.start_conv	= hibvt_lsadc_start_conv,
>+	.stop_conv = hibvt_lsadc_stop_conv,
>+};
Usual convention is to only introduce a device type specific structure when more than one device is supported.  If you are going to follow up  shortly with more device support then leave it but add a note to the patch description. If not please drop this abstraction.
>+
>+static const struct of_device_id hibvt_lsadc_match[] = {
>+	{
>+		.compatible = "hisilicon,hibvt-lsadc",
>+		.data = &lsadc_data,
>+	},
>+	{},
>+};
>+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
>+
>+/**
>+ * Reset LSADC Controller.
Single line comment syntax please.
>+ */
>+static void hibvt_lsadc_reset_controller(struct reset_control *reset) 
>+{
>+	reset_control_assert(reset);
>+	usleep_range(10, 20);
>+	reset_control_deassert(reset);
>+}
>+
>+static int hibvt_lsadc_probe(struct platform_device *pdev) {
>+	struct hibvt_lsadc *info = NULL;
>+	struct device_node *np = pdev->dev.of_node;
>+	struct iio_dev *indio_dev = NULL;
>+	struct resource	*mem;
>+	const struct of_device_id *match;
>+	int ret;
>+	int irq;
>+
>+	if (!np)
>+		return -ENODEV;
>+
>+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>+	if (!indio_dev) {
>+		dev_err(&pdev->dev, "failed allocating iio device\n");
>+		return -ENOMEM;
>+	}
>+	info = iio_priv(indio_dev);
>+
>+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
>+	info->data = match->data;
>+
>+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>+	if (IS_ERR(info->regs))
>+		return PTR_ERR(info->regs);
>+
>+	/*
>+	 * The reset should be an optional property, as it should work
>+	 * with old devicetrees as well
>+	 */
>+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
>+	if (IS_ERR(info->reset)) {
>+		ret = PTR_ERR(info->reset);
>+		if (ret != -ENOENT)
>+			return ret;
>+
>+		dev_dbg(&pdev->dev, "no reset control found\n");
>+		info->reset = NULL;
>+	}
>+
>+	init_completion(&info->completion);
>+
>+	irq = platform_get_irq(pdev, 0);
>+	if (irq < 0) {
>+		dev_err(&pdev->dev, "no irq resource?\n");
>+		return irq;
>+	}
>+
>+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
>+			       0, dev_name(&pdev->dev), info);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
>+		return ret;
>+	}
>+
>+	if (info->reset)
>+		hibvt_lsadc_reset_controller(info->reset);
>+
>+	platform_set_drvdata(pdev, indio_dev);
>+
>+	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 = &hibvt_lsadc_iio_info;
>+	indio_dev->modes = INDIO_DIRECT_MODE;
>+
>+	indio_dev->channels = info->data->channels;
>+	indio_dev->num_channels = info->data->num_channels;
>+
>+	ret = iio_device_register(indio_dev);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed register iio device\n");
>+		return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+static int hibvt_lsadc_remove(struct platform_device *pdev) {
>+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>+
>+	iio_device_unregister(indio_dev);
As nothing else here can use devm version of register and drop remove entirely.
>+
>+	return 0;
>+}
>+
>+static struct platform_driver hibvt_lsadc_driver = {
>+	.probe		= hibvt_lsadc_probe,
>+	.remove		= hibvt_lsadc_remove,
>+	.driver		= {
>+		.name	= "hibvt-lsadc",
>+		.of_match_table = hibvt_lsadc_match,
>+	},
>+};
>+
>+module_platform_driver(hibvt_lsadc_driver);
>+
>+MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>"); 
>+MODULE_DESCRIPTION("hisilicon BVT LSADC driver"); MODULE_LICENSE("GPL 
>+v2");

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* RE:  [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-26 11:05     ` liurenzhong
  0 siblings, 0 replies; 19+ messages in thread
From: liurenzhong @ 2016-12-26 11:05 UTC (permalink / raw)
  To: Jonathan Cameron, jic23-DgEjT+Ai2ygdnm+yROfE0A,
	knaack.h-Mmb7MZpHnFY, lars-Qo5EllUWu/uELgA04lAiVw,
	pmeerw-jW+XmwGofnusTnJN9+BGXg, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8
  Cc: akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w,
	ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w,
	krzk-DgEjT+Ai2ygdnm+yROfE0A, vilhelm.gray-Re5JQEeQqe8AvxtiuMwx3w,
	ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w,
	zhiyong.tao-NuS5LvNUpcJWk0Htik3J/w,
	daniel.baluta-ral2JQCrhuEAvxtiuMwx3w,
	leonard.crestez-ral2JQCrhuEAvxtiuMwx3w,
	ray.jui-dY08KVG/lbpWk0Htik3J/w,
	raveendra.padasalagi-dY08KVG/lbpWk0Htik3J/w,
	mranostay-Re5JQEeQqe8AvxtiuMwx3w,
	amsfield22-Re5JQEeQqe8AvxtiuMwx3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, linux-kernel

Hi Jonathan,

Thanks very much for your reply , it get me great courage to continue this upstreaming . 

we will make a careful analysis of your suggestion and update a new patch after a few days.

Best regards
/Allen

/---------------------/
From: Jonathan Cameron [mailto:jic23@jic23.retrosnub.co.uk] 
Sent: 24 December 2016 19:46
To: liurenzhong; jic23@kernel.org; knaack.h@gmx.de; lars@metafoo.de; pmeerw@pmeerw.net; robh+dt@kernel.org; mark.rutland@arm.com
Cc: akinobu.mita@gmail.com; ludovic.desroches@atmel.com; krzk@kernel.org; vilhelm.gray@gmail.com; ksenija.stanojevic@gmail.com; zhiyong.tao@mediatek.com; daniel.baluta@intel.com; leonard.crestez@intel.com; ray.jui@broadcom.com; raveendra.padasalagi@broadcom.com; mranostay@gmail.com; amsfield22@gmail.com; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Xuejiancheng; Lixu (kevin)
Subject: Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs

On 24 December 2016 01:54:57 GMT+00:00, Allen Liu <liurenzhong@hisilicon.com> wrote:
>Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like 
>Hi3516CV300, etc.
>The ADC controller is primarily in charge of detecting voltage.
>
>Reviewed-by: Jiancheng Xue <xuejiancheng@hisilicon.com>
>Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
Reading on phone so may not be that thorough!

Looks pretty good. The device abstraction makes it slightly more complicated than it needs to be.  If you aren't going to follow up quickly with other device support please drop the  abstraction. It can be easily readded when needed. 

Various little things inline.

Thanks

Jonathan
>---
> .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
> drivers/iio/adc/Kconfig                            |  10 +
> drivers/iio/adc/Makefile                           |   1 +
>drivers/iio/adc/hibvt_lsadc.c                      | 344
>+++++++++++++++++++++
> 4 files changed, 381 insertions(+)
>create mode 100644
>Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> create mode 100644 drivers/iio/adc/hibvt_lsadc.c
>
>diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>new file mode 100644
>index 0000000..63de46e
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>@@ -0,0 +1,26 @@
>+Hisilicon BVT Low Speed (LS) A/D Converter bindings
>+
>+Required properties:
>+- compatible: should be "hisilicon,<name>-lsadc"
>+   - "hisilicon,hibvt-lsadc": for hi3516cv300
>+
>+- reg: physical base address of the controller and length of memory
>mapped
>+       region.
>+- interrupts: The interrupt number to the cpu. The interrupt specifier
>format
>+              depends on the interrupt controller.
A cross reference to the interrupt bindings doc always good to add.
>+- #io-channel-cells: Should be 1, see ../iio-bindings.txt
>+
>+Optional properties:
>+- resets: Must contain an entry for each entry in reset-names if need
>support
>+	  this option. See ../reset/reset.txt for details.
>+- reset-names: Must include the name "saradc-apb".
>+
>+Example:
>+	lsadc: hibvt-lsadc@120e0000 {
>+			compatible = "hisilicon,hibvt-lsadc";
>+			reg = <0x120e0000 0x1000>;
>+			interrupts = <19>;
>+			resets = <&crg 0x7c 3>;
>+			reset-names = "lsadc-crg";
Doesn't contain saradc-apb which docs say it must...
>+			status = "disabled";
Not documented...
>+	};
>diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 
>99c0514..0443f51 100644
>--- a/drivers/iio/adc/Kconfig
>+++ b/drivers/iio/adc/Kconfig
>@@ -225,6 +225,16 @@ config HI8435
>	  This driver can also be built as a module. If so, the module will be
> 	  called hi8435.
> 
>+config HIBVT_LSADC
>+	tristate "HIBVT LSADC driver"
>+	depends on ARCH_HISI || COMPILE_TEST
>+	help
>+	  Say yes here to build support for the LSADC found in SoCs from
>+	  hisilicon BVT chip.
>+
>+	  To compile this driver as a module, choose M here: the
>+	  module will be called hibvt_lsadc.
>+
> config INA2XX_ADC
> 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
> 	depends on I2C && !SENSORS_INA2XX
>diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 
>7a40c04..6554d92 100644
>--- a/drivers/iio/adc/Makefile
>+++ b/drivers/iio/adc/Makefile
>@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> obj-$(CONFIG_HI8435) += hi8435.o
>+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
> obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
> obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git 
>a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c new 
>file mode 100644 index 0000000..a20afe8
>--- /dev/null
>+++ b/drivers/iio/adc/hibvt_lsadc.c
>@@ -0,0 +1,344 @@
>+/*
>+ * Hisilicon BVT Low Speed (LS) A/D Converter
>+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
>+ *
>+ * This program is free software; you can redistribute it and/or
>modify
>+ * it under the terms of the GNU General Public License as published
>by
>+ * the Free Software Foundation; either version 2 of the License, or
>+ * (at your option) any later version.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/platform_device.h>
>+#include <linux/interrupt.h>
>+#include <linux/io.h>
>+#include <linux/of.h>
>+#include <linux/of_device.h>
>+#include <linux/clk.h>
>+#include <linux/completion.h>
>+#include <linux/delay.h>
>+#include <linux/reset.h>
>+#include <linux/regulator/consumer.h>
>+#include <linux/iio/iio.h>
>+
>+/* hisilicon bvt adc registers definitions */
>+#define LSADC_CONFIG		0x00
>+#define CONFIG_DEGLITCH		BIT(17)
Please add a driver specific prefix to all defines to keep them in their own namespace.
>+#define CONFIG_RESET		BIT(15)
>+#define CONFIG_POWERDOWN	BIT(14)
>+#define CONFIG_MODE			BIT(13)
>+#define CONFIG_CHC_VALID	BIT(10)
>+#define CONFIG_CHB_VALID	BIT(9)
>+#define CONFIG_CHA_VALID	BIT(8)
>+
>+#define LSADC_TIMESCAN		0x08
Lsadc is perhaps to generic a prefix. Clash chances are a bit high
>+#define LSADC_INTEN			0x10
>+#define LSADC_INTSTATUS		0x14
>+#define LSADC_INTCLR		0x18
>+#define LSADC_START			0x1C
>+#define LSADC_STOP			0x20
>+#define LSADC_ACTBIT		0x24
>+#define LSADC_CHNDATA		0x2C
>+
>+#define ADC_CON_EN			(1u << 0)
>+#define ADC_CON_DEN			(0u << 0)
>+
>+#define ADC_NUM_BITS		10
>+
>+/* fix clk:3000000, default tscan set 10ms */
>+#define DEF_ADC_TSCAN_MS	(10*3000)
>+
>+#define LSADC_CHN_MASK		0x7
>+
>+#define LSADC_TIMEOUT		msecs_to_jiffies(100)
>+
>+/* default voltage scale for every channel <mv> */ static int 
>+g_voltage[] = {
>+	3300, 3300, 3300
>+};
Prefix these as well.
>+
>+struct hibvt_lsadc {
>+	void __iomem		*regs;
>+	struct completion	completion;
>+	struct reset_control	*reset;
>+	const struct hibvt_lsadc_data	*data;
>+	unsigned int		cur_chn;
>+	unsigned int		value;
>+};
>+
>+struct hibvt_lsadc_data {
>+	int				num_bits;
>+	const struct iio_chan_spec	*channels;
>+	int				num_channels;
>+
>+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
>+	void (*start_conv)(struct hibvt_lsadc *info);
>+	void (*stop_conv)(struct hibvt_lsadc *info); };
>+
>+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
>+				    struct iio_chan_spec const *chan,
>+				    int *val, int *val2, long mask) {
>+	struct hibvt_lsadc *info = iio_priv(indio_dev);
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_RAW:
>+		mutex_lock(&indio_dev->mlock);
>+
>+		reinit_completion(&info->completion);
>+
>+		/* Select the channel to be used */
>+		info->cur_chn = chan->channel;
>+
>+		if (info->data->start_conv)
>+			info->data->start_conv(info);
>+
>+		if (!wait_for_completion_timeout(&info->completion,
>+							LSADC_TIMEOUT)) {
>+			if (info->data->stop_conv)
>+				info->data->stop_conv(info);
>+			mutex_unlock(&indio_dev->mlock);
>+			return -ETIMEDOUT;
>+		}
>+
>+		*val = info->value;
>+		mutex_unlock(&indio_dev->mlock);
>+		return IIO_VAL_INT;
>+	case IIO_CHAN_INFO_SCALE:
>+		*val = g_voltage[chan->channel];
>+		*val2 = info->data->num_bits;
>+		return IIO_VAL_FRACTIONAL_LOG2;
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id) {
>+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
>+	int mask;
>+
>+	mask = readl(info->regs + LSADC_INTSTATUS);
>+	mask &= LSADC_CHN_MASK;
>+
>+	/* Clear irq */
>+	if (info->data->clear_irq)
>+		info->data->clear_irq(info, mask);
>+
>+	/* Read value */
>+	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn <<
>2));
>+	info->value &= GENMASK(info->data->num_bits - 1, 0);
>+
>+	/* stop adc */
>+	if (info->data->stop_conv)
>+		info->data->stop_conv(info);
>+
>+	complete(&info->completion);
>+
>+	return IRQ_HANDLED;
>+}
>+
>+static const struct iio_info hibvt_lsadc_iio_info = {
>+	.read_raw = hibvt_lsadc_read_raw,
>+	.driver_module = THIS_MODULE,
>+};
>+
>+#define ADC_CHANNEL(_index, _id) {      \
Prefix this define. 
>+	.type = IIO_VOLTAGE,                \
>+	.indexed = 1,						\
>+	.channel = _index,					\
>+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>+			BIT(IIO_CHAN_INFO_SCALE),   \
>+	.datasheet_name = _id,              \
>+}
>+
>+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
>+	ADC_CHANNEL(0, "adc0"),
>+	ADC_CHANNEL(1, "adc1"),
>+	ADC_CHANNEL(2, "adc2"),
>+};
>+
>+static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask) 
>+{
>+	writel(mask, info->regs + LSADC_INTCLR); }
>+
>+static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info) {
>+	unsigned int con;
>+
>+	/* set number bit */
>+	con = GENMASK(info->data->num_bits - 1, 0);
>+	writel(con, (info->regs + LSADC_ACTBIT));
>+
>+    /* config */
>+	con = readl(info->regs + LSADC_CONFIG);
>+	con &= ~CONFIG_RESET;
>+	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
>+	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
>+	con |= (CONFIG_CHA_VALID << info->cur_chn);
>+	writel(con, (info->regs + LSADC_CONFIG));
>+
>+	/* set timescan */
>+	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
>+
>+	/* clear interrupt */
>+	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
>+
>+	/* enable interrupt */
>+	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
>+
>+	/* start scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_START)); }
>+
>+static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info) {
>+	/* reset the timescan */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
>+
>+	/* disable interrupt */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
>+
>+	/* stop scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_STOP)); }
>+
>+static const struct hibvt_lsadc_data lsadc_data = {
>+	.num_bits = ADC_NUM_BITS,
>+	.channels = hibvt_lsadc_iio_channels,
>+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
>+
>+	.clear_irq	= hibvt_lsadc_clear_irq,
>+	.start_conv	= hibvt_lsadc_start_conv,
>+	.stop_conv = hibvt_lsadc_stop_conv,
>+};
Usual convention is to only introduce a device type specific structure when more than one device is supported.  If you are going to follow up  shortly with more device support then leave it but add a note to the patch description. If not please drop this abstraction.
>+
>+static const struct of_device_id hibvt_lsadc_match[] = {
>+	{
>+		.compatible = "hisilicon,hibvt-lsadc",
>+		.data = &lsadc_data,
>+	},
>+	{},
>+};
>+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
>+
>+/**
>+ * Reset LSADC Controller.
Single line comment syntax please.
>+ */
>+static void hibvt_lsadc_reset_controller(struct reset_control *reset) 
>+{
>+	reset_control_assert(reset);
>+	usleep_range(10, 20);
>+	reset_control_deassert(reset);
>+}
>+
>+static int hibvt_lsadc_probe(struct platform_device *pdev) {
>+	struct hibvt_lsadc *info = NULL;
>+	struct device_node *np = pdev->dev.of_node;
>+	struct iio_dev *indio_dev = NULL;
>+	struct resource	*mem;
>+	const struct of_device_id *match;
>+	int ret;
>+	int irq;
>+
>+	if (!np)
>+		return -ENODEV;
>+
>+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>+	if (!indio_dev) {
>+		dev_err(&pdev->dev, "failed allocating iio device\n");
>+		return -ENOMEM;
>+	}
>+	info = iio_priv(indio_dev);
>+
>+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
>+	info->data = match->data;
>+
>+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>+	if (IS_ERR(info->regs))
>+		return PTR_ERR(info->regs);
>+
>+	/*
>+	 * The reset should be an optional property, as it should work
>+	 * with old devicetrees as well
>+	 */
>+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
>+	if (IS_ERR(info->reset)) {
>+		ret = PTR_ERR(info->reset);
>+		if (ret != -ENOENT)
>+			return ret;
>+
>+		dev_dbg(&pdev->dev, "no reset control found\n");
>+		info->reset = NULL;
>+	}
>+
>+	init_completion(&info->completion);
>+
>+	irq = platform_get_irq(pdev, 0);
>+	if (irq < 0) {
>+		dev_err(&pdev->dev, "no irq resource?\n");
>+		return irq;
>+	}
>+
>+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
>+			       0, dev_name(&pdev->dev), info);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
>+		return ret;
>+	}
>+
>+	if (info->reset)
>+		hibvt_lsadc_reset_controller(info->reset);
>+
>+	platform_set_drvdata(pdev, indio_dev);
>+
>+	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 = &hibvt_lsadc_iio_info;
>+	indio_dev->modes = INDIO_DIRECT_MODE;
>+
>+	indio_dev->channels = info->data->channels;
>+	indio_dev->num_channels = info->data->num_channels;
>+
>+	ret = iio_device_register(indio_dev);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed register iio device\n");
>+		return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+static int hibvt_lsadc_remove(struct platform_device *pdev) {
>+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>+
>+	iio_device_unregister(indio_dev);
As nothing else here can use devm version of register and drop remove entirely.
>+
>+	return 0;
>+}
>+
>+static struct platform_driver hibvt_lsadc_driver = {
>+	.probe		= hibvt_lsadc_probe,
>+	.remove		= hibvt_lsadc_remove,
>+	.driver		= {
>+		.name	= "hibvt-lsadc",
>+		.of_match_table = hibvt_lsadc_match,
>+	},
>+};
>+
>+module_platform_driver(hibvt_lsadc_driver);
>+
>+MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>"); 
>+MODULE_DESCRIPTION("hisilicon BVT LSADC driver"); MODULE_LICENSE("GPL 
>+v2");

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* RE:  [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-26 11:05     ` liurenzhong
  0 siblings, 0 replies; 19+ messages in thread
From: liurenzhong @ 2016-12-26 11:05 UTC (permalink / raw)
  To: Jonathan Cameron, jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel

SGkgSm9uYXRoYW4sDQoNClRoYW5rcyB2ZXJ5IG11Y2ggZm9yIHlvdXIgcmVwbHkgLCBpdCBnZXQg
bWUgZ3JlYXQgY291cmFnZSB0byBjb250aW51ZSB0aGlzIHVwc3RyZWFtaW5nIC4gDQoNCndlIHdp
bGwgbWFrZSBhIGNhcmVmdWwgYW5hbHlzaXMgb2YgeW91ciBzdWdnZXN0aW9uIGFuZCB1cGRhdGUg
YSBuZXcgcGF0Y2ggYWZ0ZXIgYSBmZXcgZGF5cy4NCg0KQmVzdCByZWdhcmRzDQovQWxsZW4NCg0K
Ly0tLS0tLS0tLS0tLS0tLS0tLS0tLS8NCkZyb206IEpvbmF0aGFuIENhbWVyb24gW21haWx0bzpq
aWMyM0BqaWMyMy5yZXRyb3NudWIuY28udWtdIA0KU2VudDogMjQgRGVjZW1iZXIgMjAxNiAxOTo0
Ng0KVG86IGxpdXJlbnpob25nOyBqaWMyM0BrZXJuZWwub3JnOyBrbmFhY2suaEBnbXguZGU7IGxh
cnNAbWV0YWZvby5kZTsgcG1lZXJ3QHBtZWVydy5uZXQ7IHJvYmgrZHRAa2VybmVsLm9yZzsgbWFy
ay5ydXRsYW5kQGFybS5jb20NCkNjOiBha2lub2J1Lm1pdGFAZ21haWwuY29tOyBsdWRvdmljLmRl
c3JvY2hlc0BhdG1lbC5jb207IGtyemtAa2VybmVsLm9yZzsgdmlsaGVsbS5ncmF5QGdtYWlsLmNv
bTsga3NlbmlqYS5zdGFub2pldmljQGdtYWlsLmNvbTsgemhpeW9uZy50YW9AbWVkaWF0ZWsuY29t
OyBkYW5pZWwuYmFsdXRhQGludGVsLmNvbTsgbGVvbmFyZC5jcmVzdGV6QGludGVsLmNvbTsgcmF5
Lmp1aUBicm9hZGNvbS5jb207IHJhdmVlbmRyYS5wYWRhc2FsYWdpQGJyb2FkY29tLmNvbTsgbXJh
bm9zdGF5QGdtYWlsLmNvbTsgYW1zZmllbGQyMkBnbWFpbC5jb207IGxpbnV4LWlpb0B2Z2VyLmtl
cm5lbC5vcmc7IGRldmljZXRyZWVAdmdlci5rZXJuZWwub3JnOyBsaW51eC1rZXJuZWxAdmdlci5r
ZXJuZWwub3JnOyBYdWVqaWFuY2hlbmc7IExpeHUgKGtldmluKQ0KU3ViamVjdDogUmU6IFtQQVRD
SF0gYWRjOiBhZGQgYWRjIGRyaXZlciBmb3IgSGlzaWxpY29uIEJWVCBTT0NzDQoNCk9uIDI0IERl
Y2VtYmVyIDIwMTYgMDE6NTQ6NTcgR01UKzAwOjAwLCBBbGxlbiBMaXUgPGxpdXJlbnpob25nQGhp
c2lsaWNvbi5jb20+IHdyb3RlOg0KPkFkZCBBREMgZHJpdmVyIGZvciB0aGUgQURDIGNvbnRyb2xs
ZXIgZm91bmQgb24gSGlTaWxpY29uIEJWVCBTT0NzLCBsaWtlIA0KPkhpMzUxNkNWMzAwLCBldGMu
DQo+VGhlIEFEQyBjb250cm9sbGVyIGlzIHByaW1hcmlseSBpbiBjaGFyZ2Ugb2YgZGV0ZWN0aW5n
IHZvbHRhZ2UuDQo+DQo+UmV2aWV3ZWQtYnk6IEppYW5jaGVuZyBYdWUgPHh1ZWppYW5jaGVuZ0Bo
aXNpbGljb24uY29tPg0KPlNpZ25lZC1vZmYtYnk6IEFsbGVuIExpdSA8bGl1cmVuemhvbmdAaGlz
aWxpY29uLmNvbT4NClJlYWRpbmcgb24gcGhvbmUgc28gbWF5IG5vdCBiZSB0aGF0IHRob3JvdWdo
IQ0KDQpMb29rcyBwcmV0dHkgZ29vZC4gVGhlIGRldmljZSBhYnN0cmFjdGlvbiBtYWtlcyBpdCBz
bGlnaHRseSBtb3JlIGNvbXBsaWNhdGVkIHRoYW4gaXQgbmVlZHMgdG8gYmUuICBJZiB5b3UgYXJl
bid0IGdvaW5nIHRvIGZvbGxvdyB1cCBxdWlja2x5IHdpdGggb3RoZXIgZGV2aWNlIHN1cHBvcnQg
cGxlYXNlIGRyb3AgdGhlICBhYnN0cmFjdGlvbi4gSXQgY2FuIGJlIGVhc2lseSByZWFkZGVkIHdo
ZW4gbmVlZGVkLiANCg0KVmFyaW91cyBsaXR0bGUgdGhpbmdzIGlubGluZS4NCg0KVGhhbmtzDQoN
CkpvbmF0aGFuDQo+LS0tDQo+IC4uLi9kZXZpY2V0cmVlL2JpbmRpbmdzL2lpby9hZGMvaGlidnQt
bHNhZGMudHh0ICAgIHwgIDI2ICsrDQo+IGRyaXZlcnMvaWlvL2FkYy9LY29uZmlnICAgICAgICAg
ICAgICAgICAgICAgICAgICAgIHwgIDEwICsNCj4gZHJpdmVycy9paW8vYWRjL01ha2VmaWxlICAg
ICAgICAgICAgICAgICAgICAgICAgICAgfCAgIDEgKw0KPmRyaXZlcnMvaWlvL2FkYy9oaWJ2dF9s
c2FkYy5jICAgICAgICAgICAgICAgICAgICAgIHwgMzQ0DQo+KysrKysrKysrKysrKysrKysrKysr
DQo+IDQgZmlsZXMgY2hhbmdlZCwgMzgxIGluc2VydGlvbnMoKykNCj5jcmVhdGUgbW9kZSAxMDA2
NDQNCj5Eb2N1bWVudGF0aW9uL2RldmljZXRyZWUvYmluZGluZ3MvaWlvL2FkYy9oaWJ2dC1sc2Fk
Yy50eHQNCj4gY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvaWlvL2FkYy9oaWJ2dF9sc2FkYy5j
DQo+DQo+ZGlmZiAtLWdpdCBhL0RvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy9paW8v
YWRjL2hpYnZ0LWxzYWRjLnR4dA0KPmIvRG9jdW1lbnRhdGlvbi9kZXZpY2V0cmVlL2JpbmRpbmdz
L2lpby9hZGMvaGlidnQtbHNhZGMudHh0DQo+bmV3IGZpbGUgbW9kZSAxMDA2NDQNCj5pbmRleCAw
MDAwMDAwLi42M2RlNDZlDQo+LS0tIC9kZXYvbnVsbA0KPisrKyBiL0RvY3VtZW50YXRpb24vZGV2
aWNldHJlZS9iaW5kaW5ncy9paW8vYWRjL2hpYnZ0LWxzYWRjLnR4dA0KPkBAIC0wLDAgKzEsMjYg
QEANCj4rSGlzaWxpY29uIEJWVCBMb3cgU3BlZWQgKExTKSBBL0QgQ29udmVydGVyIGJpbmRpbmdz
DQo+Kw0KPitSZXF1aXJlZCBwcm9wZXJ0aWVzOg0KPistIGNvbXBhdGlibGU6IHNob3VsZCBiZSAi
aGlzaWxpY29uLDxuYW1lPi1sc2FkYyINCj4rICAgLSAiaGlzaWxpY29uLGhpYnZ0LWxzYWRjIjog
Zm9yIGhpMzUxNmN2MzAwDQo+Kw0KPistIHJlZzogcGh5c2ljYWwgYmFzZSBhZGRyZXNzIG9mIHRo
ZSBjb250cm9sbGVyIGFuZCBsZW5ndGggb2YgbWVtb3J5DQo+bWFwcGVkDQo+KyAgICAgICByZWdp
b24uDQo+Ky0gaW50ZXJydXB0czogVGhlIGludGVycnVwdCBudW1iZXIgdG8gdGhlIGNwdS4gVGhl
IGludGVycnVwdCBzcGVjaWZpZXINCj5mb3JtYXQNCj4rICAgICAgICAgICAgICBkZXBlbmRzIG9u
IHRoZSBpbnRlcnJ1cHQgY29udHJvbGxlci4NCkEgY3Jvc3MgcmVmZXJlbmNlIHRvIHRoZSBpbnRl
cnJ1cHQgYmluZGluZ3MgZG9jIGFsd2F5cyBnb29kIHRvIGFkZC4NCj4rLSAjaW8tY2hhbm5lbC1j
ZWxsczogU2hvdWxkIGJlIDEsIHNlZSAuLi9paW8tYmluZGluZ3MudHh0DQo+Kw0KPitPcHRpb25h
bCBwcm9wZXJ0aWVzOg0KPistIHJlc2V0czogTXVzdCBjb250YWluIGFuIGVudHJ5IGZvciBlYWNo
IGVudHJ5IGluIHJlc2V0LW5hbWVzIGlmIG5lZWQNCj5zdXBwb3J0DQo+KwkgIHRoaXMgb3B0aW9u
LiBTZWUgLi4vcmVzZXQvcmVzZXQudHh0IGZvciBkZXRhaWxzLg0KPistIHJlc2V0LW5hbWVzOiBN
dXN0IGluY2x1ZGUgdGhlIG5hbWUgInNhcmFkYy1hcGIiLg0KPisNCj4rRXhhbXBsZToNCj4rCWxz
YWRjOiBoaWJ2dC1sc2FkY0AxMjBlMDAwMCB7DQo+KwkJCWNvbXBhdGlibGUgPSAiaGlzaWxpY29u
LGhpYnZ0LWxzYWRjIjsNCj4rCQkJcmVnID0gPDB4MTIwZTAwMDAgMHgxMDAwPjsNCj4rCQkJaW50
ZXJydXB0cyA9IDwxOT47DQo+KwkJCXJlc2V0cyA9IDwmY3JnIDB4N2MgMz47DQo+KwkJCXJlc2V0
LW5hbWVzID0gImxzYWRjLWNyZyI7DQpEb2Vzbid0IGNvbnRhaW4gc2FyYWRjLWFwYiB3aGljaCBk
b2NzIHNheSBpdCBtdXN0Li4uDQo+KwkJCXN0YXR1cyA9ICJkaXNhYmxlZCI7DQpOb3QgZG9jdW1l
bnRlZC4uLg0KPisJfTsNCj5kaWZmIC0tZ2l0IGEvZHJpdmVycy9paW8vYWRjL0tjb25maWcgYi9k
cml2ZXJzL2lpby9hZGMvS2NvbmZpZyBpbmRleCANCj45OWMwNTE0Li4wNDQzZjUxIDEwMDY0NA0K
Pi0tLSBhL2RyaXZlcnMvaWlvL2FkYy9LY29uZmlnDQo+KysrIGIvZHJpdmVycy9paW8vYWRjL0tj
b25maWcNCj5AQCAtMjI1LDYgKzIyNSwxNiBAQCBjb25maWcgSEk4NDM1DQo+CSAgVGhpcyBkcml2
ZXIgY2FuIGFsc28gYmUgYnVpbHQgYXMgYSBtb2R1bGUuIElmIHNvLCB0aGUgbW9kdWxlIHdpbGwg
YmUNCj4gCSAgY2FsbGVkIGhpODQzNS4NCj4gDQo+K2NvbmZpZyBISUJWVF9MU0FEQw0KPisJdHJp
c3RhdGUgIkhJQlZUIExTQURDIGRyaXZlciINCj4rCWRlcGVuZHMgb24gQVJDSF9ISVNJIHx8IENP
TVBJTEVfVEVTVA0KPisJaGVscA0KPisJICBTYXkgeWVzIGhlcmUgdG8gYnVpbGQgc3VwcG9ydCBm
b3IgdGhlIExTQURDIGZvdW5kIGluIFNvQ3MgZnJvbQ0KPisJICBoaXNpbGljb24gQlZUIGNoaXAu
DQo+Kw0KPisJICBUbyBjb21waWxlIHRoaXMgZHJpdmVyIGFzIGEgbW9kdWxlLCBjaG9vc2UgTSBo
ZXJlOiB0aGUNCj4rCSAgbW9kdWxlIHdpbGwgYmUgY2FsbGVkIGhpYnZ0X2xzYWRjLg0KPisNCj4g
Y29uZmlnIElOQTJYWF9BREMNCj4gCXRyaXN0YXRlICJUZXhhcyBJbnN0cnVtZW50cyBJTkEyeHgg
UG93ZXIgTW9uaXRvcnMgSUlPIGRyaXZlciINCj4gCWRlcGVuZHMgb24gSTJDICYmICFTRU5TT1JT
X0lOQTJYWA0KPmRpZmYgLS1naXQgYS9kcml2ZXJzL2lpby9hZGMvTWFrZWZpbGUgYi9kcml2ZXJz
L2lpby9hZGMvTWFrZWZpbGUgaW5kZXggDQo+N2E0MGMwNC4uNjU1NGQ5MiAxMDA2NDQNCj4tLS0g
YS9kcml2ZXJzL2lpby9hZGMvTWFrZWZpbGUNCj4rKysgYi9kcml2ZXJzL2lpby9hZGMvTWFrZWZp
bGUNCj5AQCAtMjMsNiArMjMsNyBAQCBvYmotJChDT05GSUdfREE5MTUwX0dQQURDKSArPSBkYTkx
NTAtZ3BhZGMubw0KPiBvYmotJChDT05GSUdfRVhZTk9TX0FEQykgKz0gZXh5bm9zX2FkYy5vDQo+
IG9iai0kKENPTkZJR19GU0xfTVgyNV9BREMpICs9IGZzbC1pbXgyNS1nY3Eubw0KPiBvYmotJChD
T05GSUdfSEk4NDM1KSArPSBoaTg0MzUubw0KPitvYmotJChDT05GSUdfSElCVlRfTFNBREMpICs9
IGhpYnZ0X2xzYWRjLm8NCj4gb2JqLSQoQ09ORklHX0lNWDdEX0FEQykgKz0gaW14N2RfYWRjLm8N
Cj4gb2JqLSQoQ09ORklHX0lOQTJYWF9BREMpICs9IGluYTJ4eC1hZGMubw0KPiBvYmotJChDT05G
SUdfTFA4Nzg4X0FEQykgKz0gbHA4Nzg4X2FkYy5vIGRpZmYgLS1naXQgDQo+YS9kcml2ZXJzL2lp
by9hZGMvaGlidnRfbHNhZGMuYyBiL2RyaXZlcnMvaWlvL2FkYy9oaWJ2dF9sc2FkYy5jIG5ldyAN
Cj5maWxlIG1vZGUgMTAwNjQ0IGluZGV4IDAwMDAwMDAuLmEyMGFmZTgNCj4tLS0gL2Rldi9udWxs
DQo+KysrIGIvZHJpdmVycy9paW8vYWRjL2hpYnZ0X2xzYWRjLmMNCj5AQCAtMCwwICsxLDM0NCBA
QA0KPisvKg0KPisgKiBIaXNpbGljb24gQlZUIExvdyBTcGVlZCAoTFMpIEEvRCBDb252ZXJ0ZXIN
Cj4rICogQ29weXJpZ2h0IChDKSAyMDE2IEhpU2lsaWNvbiBUZWNobm9sb2dpZXMgQ28uLCBMdGQu
DQo+KyAqDQo+KyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlz
dHJpYnV0ZSBpdCBhbmQvb3INCj5tb2RpZnkNCj4rICogaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRo
ZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQNCj5ieQ0KPisgKiB0aGUg
RnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyBlaXRoZXIgdmVyc2lvbiAyIG9mIHRoZSBMaWNlbnNl
LCBvcg0KPisgKiAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLg0KPisgKg0KPisg
KiBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJl
IHVzZWZ1bCwNCj4rICogYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhl
IGltcGxpZWQgd2FycmFudHkgb2YNCj4rICogTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9S
IEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQ0KPisgKiBHTlUgR2VuZXJhbCBQdWJsaWMg
TGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLg0KPisgKi8NCj4rDQo+KyNpbmNsdWRlIDxsaW51eC9t
b2R1bGUuaD4NCj4rI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPg0KPisjaW5jbHVk
ZSA8bGludXgvaW50ZXJydXB0Lmg+DQo+KyNpbmNsdWRlIDxsaW51eC9pby5oPg0KPisjaW5jbHVk
ZSA8bGludXgvb2YuaD4NCj4rI2luY2x1ZGUgPGxpbnV4L29mX2RldmljZS5oPg0KPisjaW5jbHVk
ZSA8bGludXgvY2xrLmg+DQo+KyNpbmNsdWRlIDxsaW51eC9jb21wbGV0aW9uLmg+DQo+KyNpbmNs
dWRlIDxsaW51eC9kZWxheS5oPg0KPisjaW5jbHVkZSA8bGludXgvcmVzZXQuaD4NCj4rI2luY2x1
ZGUgPGxpbnV4L3JlZ3VsYXRvci9jb25zdW1lci5oPg0KPisjaW5jbHVkZSA8bGludXgvaWlvL2lp
by5oPg0KPisNCj4rLyogaGlzaWxpY29uIGJ2dCBhZGMgcmVnaXN0ZXJzIGRlZmluaXRpb25zICov
DQo+KyNkZWZpbmUgTFNBRENfQ09ORklHCQkweDAwDQo+KyNkZWZpbmUgQ09ORklHX0RFR0xJVENI
CQlCSVQoMTcpDQpQbGVhc2UgYWRkIGEgZHJpdmVyIHNwZWNpZmljIHByZWZpeCB0byBhbGwgZGVm
aW5lcyB0byBrZWVwIHRoZW0gaW4gdGhlaXIgb3duIG5hbWVzcGFjZS4NCj4rI2RlZmluZSBDT05G
SUdfUkVTRVQJCUJJVCgxNSkNCj4rI2RlZmluZSBDT05GSUdfUE9XRVJET1dOCUJJVCgxNCkNCj4r
I2RlZmluZSBDT05GSUdfTU9ERQkJCUJJVCgxMykNCj4rI2RlZmluZSBDT05GSUdfQ0hDX1ZBTElE
CUJJVCgxMCkNCj4rI2RlZmluZSBDT05GSUdfQ0hCX1ZBTElECUJJVCg5KQ0KPisjZGVmaW5lIENP
TkZJR19DSEFfVkFMSUQJQklUKDgpDQo+Kw0KPisjZGVmaW5lIExTQURDX1RJTUVTQ0FOCQkweDA4
DQpMc2FkYyBpcyBwZXJoYXBzIHRvIGdlbmVyaWMgYSBwcmVmaXguIENsYXNoIGNoYW5jZXMgYXJl
IGEgYml0IGhpZ2gNCj4rI2RlZmluZSBMU0FEQ19JTlRFTgkJCTB4MTANCj4rI2RlZmluZSBMU0FE
Q19JTlRTVEFUVVMJCTB4MTQNCj4rI2RlZmluZSBMU0FEQ19JTlRDTFIJCTB4MTgNCj4rI2RlZmlu
ZSBMU0FEQ19TVEFSVAkJCTB4MUMNCj4rI2RlZmluZSBMU0FEQ19TVE9QCQkJMHgyMA0KPisjZGVm
aW5lIExTQURDX0FDVEJJVAkJMHgyNA0KPisjZGVmaW5lIExTQURDX0NITkRBVEEJCTB4MkMNCj4r
DQo+KyNkZWZpbmUgQURDX0NPTl9FTgkJCSgxdSA8PCAwKQ0KPisjZGVmaW5lIEFEQ19DT05fREVO
CQkJKDB1IDw8IDApDQo+Kw0KPisjZGVmaW5lIEFEQ19OVU1fQklUUwkJMTANCj4rDQo+Ky8qIGZp
eCBjbGs6MzAwMDAwMCwgZGVmYXVsdCB0c2NhbiBzZXQgMTBtcyAqLw0KPisjZGVmaW5lIERFRl9B
RENfVFNDQU5fTVMJKDEwKjMwMDApDQo+Kw0KPisjZGVmaW5lIExTQURDX0NITl9NQVNLCQkweDcN
Cj4rDQo+KyNkZWZpbmUgTFNBRENfVElNRU9VVAkJbXNlY3NfdG9famlmZmllcygxMDApDQo+Kw0K
PisvKiBkZWZhdWx0IHZvbHRhZ2Ugc2NhbGUgZm9yIGV2ZXJ5IGNoYW5uZWwgPG12PiAqLyBzdGF0
aWMgaW50IA0KPitnX3ZvbHRhZ2VbXSA9IHsNCj4rCTMzMDAsIDMzMDAsIDMzMDANCj4rfTsNClBy
ZWZpeCB0aGVzZSBhcyB3ZWxsLg0KPisNCj4rc3RydWN0IGhpYnZ0X2xzYWRjIHsNCj4rCXZvaWQg
X19pb21lbQkJKnJlZ3M7DQo+KwlzdHJ1Y3QgY29tcGxldGlvbgljb21wbGV0aW9uOw0KPisJc3Ry
dWN0IHJlc2V0X2NvbnRyb2wJKnJlc2V0Ow0KPisJY29uc3Qgc3RydWN0IGhpYnZ0X2xzYWRjX2Rh
dGEJKmRhdGE7DQo+Kwl1bnNpZ25lZCBpbnQJCWN1cl9jaG47DQo+Kwl1bnNpZ25lZCBpbnQJCXZh
bHVlOw0KPit9Ow0KPisNCj4rc3RydWN0IGhpYnZ0X2xzYWRjX2RhdGEgew0KPisJaW50CQkJCW51
bV9iaXRzOw0KPisJY29uc3Qgc3RydWN0IGlpb19jaGFuX3NwZWMJKmNoYW5uZWxzOw0KPisJaW50
CQkJCW51bV9jaGFubmVsczsNCj4rDQo+Kwl2b2lkICgqY2xlYXJfaXJxKShzdHJ1Y3QgaGlidnRf
bHNhZGMgKmluZm8sIGludCBtYXNrKTsNCj4rCXZvaWQgKCpzdGFydF9jb252KShzdHJ1Y3QgaGli
dnRfbHNhZGMgKmluZm8pOw0KPisJdm9pZCAoKnN0b3BfY29udikoc3RydWN0IGhpYnZ0X2xzYWRj
ICppbmZvKTsgfTsNCj4rDQo+K3N0YXRpYyBpbnQgaGlidnRfbHNhZGNfcmVhZF9yYXcoc3RydWN0
IGlpb19kZXYgKmluZGlvX2RldiwNCj4rCQkJCSAgICBzdHJ1Y3QgaWlvX2NoYW5fc3BlYyBjb25z
dCAqY2hhbiwNCj4rCQkJCSAgICBpbnQgKnZhbCwgaW50ICp2YWwyLCBsb25nIG1hc2spIHsNCj4r
CXN0cnVjdCBoaWJ2dF9sc2FkYyAqaW5mbyA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+Kw0KPisJ
c3dpdGNoIChtYXNrKSB7DQo+KwljYXNlIElJT19DSEFOX0lORk9fUkFXOg0KPisJCW11dGV4X2xv
Y2soJmluZGlvX2Rldi0+bWxvY2spOw0KPisNCj4rCQlyZWluaXRfY29tcGxldGlvbigmaW5mby0+
Y29tcGxldGlvbik7DQo+Kw0KPisJCS8qIFNlbGVjdCB0aGUgY2hhbm5lbCB0byBiZSB1c2VkICov
DQo+KwkJaW5mby0+Y3VyX2NobiA9IGNoYW4tPmNoYW5uZWw7DQo+Kw0KPisJCWlmIChpbmZvLT5k
YXRhLT5zdGFydF9jb252KQ0KPisJCQlpbmZvLT5kYXRhLT5zdGFydF9jb252KGluZm8pOw0KPisN
Cj4rCQlpZiAoIXdhaXRfZm9yX2NvbXBsZXRpb25fdGltZW91dCgmaW5mby0+Y29tcGxldGlvbiwN
Cj4rCQkJCQkJCUxTQURDX1RJTUVPVVQpKSB7DQo+KwkJCWlmIChpbmZvLT5kYXRhLT5zdG9wX2Nv
bnYpDQo+KwkJCQlpbmZvLT5kYXRhLT5zdG9wX2NvbnYoaW5mbyk7DQo+KwkJCW11dGV4X3VubG9j
aygmaW5kaW9fZGV2LT5tbG9jayk7DQo+KwkJCXJldHVybiAtRVRJTUVET1VUOw0KPisJCX0NCj4r
DQo+KwkJKnZhbCA9IGluZm8tPnZhbHVlOw0KPisJCW11dGV4X3VubG9jaygmaW5kaW9fZGV2LT5t
bG9jayk7DQo+KwkJcmV0dXJuIElJT19WQUxfSU5UOw0KPisJY2FzZSBJSU9fQ0hBTl9JTkZPX1ND
QUxFOg0KPisJCSp2YWwgPSBnX3ZvbHRhZ2VbY2hhbi0+Y2hhbm5lbF07DQo+KwkJKnZhbDIgPSBp
bmZvLT5kYXRhLT5udW1fYml0czsNCj4rCQlyZXR1cm4gSUlPX1ZBTF9GUkFDVElPTkFMX0xPRzI7
DQo+KwlkZWZhdWx0Og0KPisJCXJldHVybiAtRUlOVkFMOw0KPisJfQ0KPit9DQo+Kw0KPitzdGF0
aWMgaXJxcmV0dXJuX3QgaGlidnRfbHNhZGNfaXNyKGludCBpcnEsIHZvaWQgKmRldl9pZCkgew0K
PisJc3RydWN0IGhpYnZ0X2xzYWRjICppbmZvID0gKHN0cnVjdCBoaWJ2dF9sc2FkYyAqKWRldl9p
ZDsNCj4rCWludCBtYXNrOw0KPisNCj4rCW1hc2sgPSByZWFkbChpbmZvLT5yZWdzICsgTFNBRENf
SU5UU1RBVFVTKTsNCj4rCW1hc2sgJj0gTFNBRENfQ0hOX01BU0s7DQo+Kw0KPisJLyogQ2xlYXIg
aXJxICovDQo+KwlpZiAoaW5mby0+ZGF0YS0+Y2xlYXJfaXJxKQ0KPisJCWluZm8tPmRhdGEtPmNs
ZWFyX2lycShpbmZvLCBtYXNrKTsNCj4rDQo+KwkvKiBSZWFkIHZhbHVlICovDQo+KwlpbmZvLT52
YWx1ZSA9IHJlYWRsKGluZm8tPnJlZ3MgKyBMU0FEQ19DSE5EQVRBICsgKGluZm8tPmN1cl9jaG4g
PDwNCj4yKSk7DQo+KwlpbmZvLT52YWx1ZSAmPSBHRU5NQVNLKGluZm8tPmRhdGEtPm51bV9iaXRz
IC0gMSwgMCk7DQo+Kw0KPisJLyogc3RvcCBhZGMgKi8NCj4rCWlmIChpbmZvLT5kYXRhLT5zdG9w
X2NvbnYpDQo+KwkJaW5mby0+ZGF0YS0+c3RvcF9jb252KGluZm8pOw0KPisNCj4rCWNvbXBsZXRl
KCZpbmZvLT5jb21wbGV0aW9uKTsNCj4rDQo+KwlyZXR1cm4gSVJRX0hBTkRMRUQ7DQo+K30NCj4r
DQo+K3N0YXRpYyBjb25zdCBzdHJ1Y3QgaWlvX2luZm8gaGlidnRfbHNhZGNfaWlvX2luZm8gPSB7
DQo+KwkucmVhZF9yYXcgPSBoaWJ2dF9sc2FkY19yZWFkX3JhdywNCj4rCS5kcml2ZXJfbW9kdWxl
ID0gVEhJU19NT0RVTEUsDQo+K307DQo+Kw0KPisjZGVmaW5lIEFEQ19DSEFOTkVMKF9pbmRleCwg
X2lkKSB7ICAgICAgXA0KUHJlZml4IHRoaXMgZGVmaW5lLiANCj4rCS50eXBlID0gSUlPX1ZPTFRB
R0UsICAgICAgICAgICAgICAgIFwNCj4rCS5pbmRleGVkID0gMSwJCQkJCQlcDQo+KwkuY2hhbm5l
bCA9IF9pbmRleCwJCQkJCVwNCj4rCS5pbmZvX21hc2tfc2VwYXJhdGUgPSBCSVQoSUlPX0NIQU5f
SU5GT19SQVcpIHwgIFwNCj4rCQkJQklUKElJT19DSEFOX0lORk9fU0NBTEUpLCAgIFwNCj4rCS5k
YXRhc2hlZXRfbmFtZSA9IF9pZCwgICAgICAgICAgICAgIFwNCj4rfQ0KPisNCj4rc3RhdGljIGNv
bnN0IHN0cnVjdCBpaW9fY2hhbl9zcGVjIGhpYnZ0X2xzYWRjX2lpb19jaGFubmVsc1tdID0gew0K
PisJQURDX0NIQU5ORUwoMCwgImFkYzAiKSwNCj4rCUFEQ19DSEFOTkVMKDEsICJhZGMxIiksDQo+
KwlBRENfQ0hBTk5FTCgyLCAiYWRjMiIpLA0KPit9Ow0KPisNCj4rc3RhdGljIHZvaWQgaGlidnRf
bHNhZGNfY2xlYXJfaXJxKHN0cnVjdCBoaWJ2dF9sc2FkYyAqaW5mbywgaW50IG1hc2spIA0KPit7
DQo+Kwl3cml0ZWwobWFzaywgaW5mby0+cmVncyArIExTQURDX0lOVENMUik7IH0NCj4rDQo+K3N0
YXRpYyB2b2lkIGhpYnZ0X2xzYWRjX3N0YXJ0X2NvbnYoc3RydWN0IGhpYnZ0X2xzYWRjICppbmZv
KSB7DQo+Kwl1bnNpZ25lZCBpbnQgY29uOw0KPisNCj4rCS8qIHNldCBudW1iZXIgYml0ICovDQo+
Kwljb24gPSBHRU5NQVNLKGluZm8tPmRhdGEtPm51bV9iaXRzIC0gMSwgMCk7DQo+Kwl3cml0ZWwo
Y29uLCAoaW5mby0+cmVncyArIExTQURDX0FDVEJJVCkpOw0KPisNCj4rICAgIC8qIGNvbmZpZyAq
Lw0KPisJY29uID0gcmVhZGwoaW5mby0+cmVncyArIExTQURDX0NPTkZJRyk7DQo+Kwljb24gJj0g
fkNPTkZJR19SRVNFVDsNCj4rCWNvbiB8PSAoQ09ORklHX1BPV0VSRE9XTiB8IENPTkZJR19ERUdM
SVRDSCB8IENPTkZJR19NT0RFKTsNCj4rCWNvbiAmPSB+KENPTkZJR19DSEFfVkFMSUQgfCBDT05G
SUdfQ0hCX1ZBTElEIHwgQ09ORklHX0NIQ19WQUxJRCk7DQo+Kwljb24gfD0gKENPTkZJR19DSEFf
VkFMSUQgPDwgaW5mby0+Y3VyX2Nobik7DQo+Kwl3cml0ZWwoY29uLCAoaW5mby0+cmVncyArIExT
QURDX0NPTkZJRykpOw0KPisNCj4rCS8qIHNldCB0aW1lc2NhbiAqLw0KPisJd3JpdGVsKERFRl9B
RENfVFNDQU5fTVMsIChpbmZvLT5yZWdzICsgTFNBRENfVElNRVNDQU4pKTsNCj4rDQo+KwkvKiBj
bGVhciBpbnRlcnJ1cHQgKi8NCj4rCXdyaXRlbChMU0FEQ19DSE5fTUFTSywgaW5mby0+cmVncyAr
IExTQURDX0lOVENMUik7DQo+Kw0KPisJLyogZW5hYmxlIGludGVycnVwdCAqLw0KPisJd3JpdGVs
KEFEQ19DT05fRU4sIChpbmZvLT5yZWdzICsgTFNBRENfSU5URU4pKTsNCj4rDQo+KwkvKiBzdGFy
dCBzY2FuICovDQo+Kwl3cml0ZWwoQURDX0NPTl9FTiwgKGluZm8tPnJlZ3MgKyBMU0FEQ19TVEFS
VCkpOyB9DQo+Kw0KPitzdGF0aWMgdm9pZCBoaWJ2dF9sc2FkY19zdG9wX2NvbnYoc3RydWN0IGhp
YnZ0X2xzYWRjICppbmZvKSB7DQo+KwkvKiByZXNldCB0aGUgdGltZXNjYW4gKi8NCj4rCXdyaXRl
bChBRENfQ09OX0RFTiwgKGluZm8tPnJlZ3MgKyBMU0FEQ19USU1FU0NBTikpOw0KPisNCj4rCS8q
IGRpc2FibGUgaW50ZXJydXB0ICovDQo+Kwl3cml0ZWwoQURDX0NPTl9ERU4sIChpbmZvLT5yZWdz
ICsgTFNBRENfSU5URU4pKTsNCj4rDQo+KwkvKiBzdG9wIHNjYW4gKi8NCj4rCXdyaXRlbChBRENf
Q09OX0VOLCAoaW5mby0+cmVncyArIExTQURDX1NUT1ApKTsgfQ0KPisNCj4rc3RhdGljIGNvbnN0
IHN0cnVjdCBoaWJ2dF9sc2FkY19kYXRhIGxzYWRjX2RhdGEgPSB7DQo+KwkubnVtX2JpdHMgPSBB
RENfTlVNX0JJVFMsDQo+KwkuY2hhbm5lbHMgPSBoaWJ2dF9sc2FkY19paW9fY2hhbm5lbHMsDQo+
KwkubnVtX2NoYW5uZWxzID0gQVJSQVlfU0laRShoaWJ2dF9sc2FkY19paW9fY2hhbm5lbHMpLA0K
PisNCj4rCS5jbGVhcl9pcnEJPSBoaWJ2dF9sc2FkY19jbGVhcl9pcnEsDQo+Kwkuc3RhcnRfY29u
dgk9IGhpYnZ0X2xzYWRjX3N0YXJ0X2NvbnYsDQo+Kwkuc3RvcF9jb252ID0gaGlidnRfbHNhZGNf
c3RvcF9jb252LA0KPit9Ow0KVXN1YWwgY29udmVudGlvbiBpcyB0byBvbmx5IGludHJvZHVjZSBh
IGRldmljZSB0eXBlIHNwZWNpZmljIHN0cnVjdHVyZSB3aGVuIG1vcmUgdGhhbiBvbmUgZGV2aWNl
IGlzIHN1cHBvcnRlZC4gIElmIHlvdSBhcmUgZ29pbmcgdG8gZm9sbG93IHVwICBzaG9ydGx5IHdp
dGggbW9yZSBkZXZpY2Ugc3VwcG9ydCB0aGVuIGxlYXZlIGl0IGJ1dCBhZGQgYSBub3RlIHRvIHRo
ZSBwYXRjaCBkZXNjcmlwdGlvbi4gSWYgbm90IHBsZWFzZSBkcm9wIHRoaXMgYWJzdHJhY3Rpb24u
DQo+Kw0KPitzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2RldmljZV9pZCBoaWJ2dF9sc2FkY19tYXRj
aFtdID0gew0KPisJew0KPisJCS5jb21wYXRpYmxlID0gImhpc2lsaWNvbixoaWJ2dC1sc2FkYyIs
DQo+KwkJLmRhdGEgPSAmbHNhZGNfZGF0YSwNCj4rCX0sDQo+Kwl7fSwNCj4rfTsNCj4rTU9EVUxF
X0RFVklDRV9UQUJMRShvZiwgaGlidnRfbHNhZGNfbWF0Y2gpOw0KPisNCj4rLyoqDQo+KyAqIFJl
c2V0IExTQURDIENvbnRyb2xsZXIuDQpTaW5nbGUgbGluZSBjb21tZW50IHN5bnRheCBwbGVhc2Uu
DQo+KyAqLw0KPitzdGF0aWMgdm9pZCBoaWJ2dF9sc2FkY19yZXNldF9jb250cm9sbGVyKHN0cnVj
dCByZXNldF9jb250cm9sICpyZXNldCkgDQo+K3sNCj4rCXJlc2V0X2NvbnRyb2xfYXNzZXJ0KHJl
c2V0KTsNCj4rCXVzbGVlcF9yYW5nZSgxMCwgMjApOw0KPisJcmVzZXRfY29udHJvbF9kZWFzc2Vy
dChyZXNldCk7DQo+K30NCj4rDQo+K3N0YXRpYyBpbnQgaGlidnRfbHNhZGNfcHJvYmUoc3RydWN0
IHBsYXRmb3JtX2RldmljZSAqcGRldikgew0KPisJc3RydWN0IGhpYnZ0X2xzYWRjICppbmZvID0g
TlVMTDsNCj4rCXN0cnVjdCBkZXZpY2Vfbm9kZSAqbnAgPSBwZGV2LT5kZXYub2Zfbm9kZTsNCj4r
CXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBOVUxMOw0KPisJc3RydWN0IHJlc291cmNlCSpt
ZW07DQo+Kwljb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkICptYXRjaDsNCj4rCWludCByZXQ7DQo+
KwlpbnQgaXJxOw0KPisNCj4rCWlmICghbnApDQo+KwkJcmV0dXJuIC1FTk9ERVY7DQo+Kw0KPisJ
aW5kaW9fZGV2ID0gZGV2bV9paW9fZGV2aWNlX2FsbG9jKCZwZGV2LT5kZXYsIHNpemVvZigqaW5m
bykpOw0KPisJaWYgKCFpbmRpb19kZXYpIHsNCj4rCQlkZXZfZXJyKCZwZGV2LT5kZXYsICJmYWls
ZWQgYWxsb2NhdGluZyBpaW8gZGV2aWNlXG4iKTsNCj4rCQlyZXR1cm4gLUVOT01FTTsNCj4rCX0N
Cj4rCWluZm8gPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPisNCj4rCW1hdGNoID0gb2ZfbWF0Y2hf
ZGV2aWNlKGhpYnZ0X2xzYWRjX21hdGNoLCAmcGRldi0+ZGV2KTsNCj4rCWluZm8tPmRhdGEgPSBt
YXRjaC0+ZGF0YTsNCj4rDQo+KwltZW0gPSBwbGF0Zm9ybV9nZXRfcmVzb3VyY2UocGRldiwgSU9S
RVNPVVJDRV9NRU0sIDApOw0KPisJaW5mby0+cmVncyA9IGRldm1faW9yZW1hcF9yZXNvdXJjZSgm
cGRldi0+ZGV2LCBtZW0pOw0KPisJaWYgKElTX0VSUihpbmZvLT5yZWdzKSkNCj4rCQlyZXR1cm4g
UFRSX0VSUihpbmZvLT5yZWdzKTsNCj4rDQo+KwkvKg0KPisJICogVGhlIHJlc2V0IHNob3VsZCBi
ZSBhbiBvcHRpb25hbCBwcm9wZXJ0eSwgYXMgaXQgc2hvdWxkIHdvcmsNCj4rCSAqIHdpdGggb2xk
IGRldmljZXRyZWVzIGFzIHdlbGwNCj4rCSAqLw0KPisJaW5mby0+cmVzZXQgPSBkZXZtX3Jlc2V0
X2NvbnRyb2xfZ2V0KCZwZGV2LT5kZXYsICJsc2FkYy1jcmciKTsNCj4rCWlmIChJU19FUlIoaW5m
by0+cmVzZXQpKSB7DQo+KwkJcmV0ID0gUFRSX0VSUihpbmZvLT5yZXNldCk7DQo+KwkJaWYgKHJl
dCAhPSAtRU5PRU5UKQ0KPisJCQlyZXR1cm4gcmV0Ow0KPisNCj4rCQlkZXZfZGJnKCZwZGV2LT5k
ZXYsICJubyByZXNldCBjb250cm9sIGZvdW5kXG4iKTsNCj4rCQlpbmZvLT5yZXNldCA9IE5VTEw7
DQo+Kwl9DQo+Kw0KPisJaW5pdF9jb21wbGV0aW9uKCZpbmZvLT5jb21wbGV0aW9uKTsNCj4rDQo+
KwlpcnEgPSBwbGF0Zm9ybV9nZXRfaXJxKHBkZXYsIDApOw0KPisJaWYgKGlycSA8IDApIHsNCj4r
CQlkZXZfZXJyKCZwZGV2LT5kZXYsICJubyBpcnEgcmVzb3VyY2U/XG4iKTsNCj4rCQlyZXR1cm4g
aXJxOw0KPisJfQ0KPisNCj4rCXJldCA9IGRldm1fcmVxdWVzdF9pcnEoJnBkZXYtPmRldiwgaXJx
LCBoaWJ2dF9sc2FkY19pc3IsDQo+KwkJCSAgICAgICAwLCBkZXZfbmFtZSgmcGRldi0+ZGV2KSwg
aW5mbyk7DQo+KwlpZiAocmV0IDwgMCkgew0KPisJCWRldl9lcnIoJnBkZXYtPmRldiwgImZhaWxl
ZCByZXF1ZXN0aW5nIGlycSAlZFxuIiwgaXJxKTsNCj4rCQlyZXR1cm4gcmV0Ow0KPisJfQ0KPisN
Cj4rCWlmIChpbmZvLT5yZXNldCkNCj4rCQloaWJ2dF9sc2FkY19yZXNldF9jb250cm9sbGVyKGlu
Zm8tPnJlc2V0KTsNCj4rDQo+KwlwbGF0Zm9ybV9zZXRfZHJ2ZGF0YShwZGV2LCBpbmRpb19kZXYp
Ow0KPisNCj4rCWluZGlvX2Rldi0+bmFtZSA9IGRldl9uYW1lKCZwZGV2LT5kZXYpOw0KPisJaW5k
aW9fZGV2LT5kZXYucGFyZW50ID0gJnBkZXYtPmRldjsNCj4rCWluZGlvX2Rldi0+ZGV2Lm9mX25v
ZGUgPSBwZGV2LT5kZXYub2Zfbm9kZTsNCj4rCWluZGlvX2Rldi0+aW5mbyA9ICZoaWJ2dF9sc2Fk
Y19paW9faW5mbzsNCj4rCWluZGlvX2Rldi0+bW9kZXMgPSBJTkRJT19ESVJFQ1RfTU9ERTsNCj4r
DQo+KwlpbmRpb19kZXYtPmNoYW5uZWxzID0gaW5mby0+ZGF0YS0+Y2hhbm5lbHM7DQo+KwlpbmRp
b19kZXYtPm51bV9jaGFubmVscyA9IGluZm8tPmRhdGEtPm51bV9jaGFubmVsczsNCj4rDQo+Kwly
ZXQgPSBpaW9fZGV2aWNlX3JlZ2lzdGVyKGluZGlvX2Rldik7DQo+KwlpZiAocmV0IDwgMCkgew0K
PisJCWRldl9lcnIoJnBkZXYtPmRldiwgImZhaWxlZCByZWdpc3RlciBpaW8gZGV2aWNlXG4iKTsN
Cj4rCQlyZXR1cm4gcmV0Ow0KPisJfQ0KPisNCj4rCXJldHVybiAwOw0KPit9DQo+Kw0KPitzdGF0
aWMgaW50IGhpYnZ0X2xzYWRjX3JlbW92ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KSB7
DQo+KwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0gcGxhdGZvcm1fZ2V0X2RydmRhdGEocGRl
dik7DQo+Kw0KPisJaWlvX2RldmljZV91bnJlZ2lzdGVyKGluZGlvX2Rldik7DQpBcyBub3RoaW5n
IGVsc2UgaGVyZSBjYW4gdXNlIGRldm0gdmVyc2lvbiBvZiByZWdpc3RlciBhbmQgZHJvcCByZW1v
dmUgZW50aXJlbHkuDQo+Kw0KPisJcmV0dXJuIDA7DQo+K30NCj4rDQo+K3N0YXRpYyBzdHJ1Y3Qg
cGxhdGZvcm1fZHJpdmVyIGhpYnZ0X2xzYWRjX2RyaXZlciA9IHsNCj4rCS5wcm9iZQkJPSBoaWJ2
dF9sc2FkY19wcm9iZSwNCj4rCS5yZW1vdmUJCT0gaGlidnRfbHNhZGNfcmVtb3ZlLA0KPisJLmRy
aXZlcgkJPSB7DQo+KwkJLm5hbWUJPSAiaGlidnQtbHNhZGMiLA0KPisJCS5vZl9tYXRjaF90YWJs
ZSA9IGhpYnZ0X2xzYWRjX21hdGNoLA0KPisJfSwNCj4rfTsNCj4rDQo+K21vZHVsZV9wbGF0Zm9y
bV9kcml2ZXIoaGlidnRfbHNhZGNfZHJpdmVyKTsNCj4rDQo+K01PRFVMRV9BVVRIT1IoIkFsbGVu
IExpdSA8bGl1cmVuemhvbmdAaGlzaWxpY29uLmNvbT4iKTsgDQo+K01PRFVMRV9ERVNDUklQVElP
TigiaGlzaWxpY29uIEJWVCBMU0FEQyBkcml2ZXIiKTsgTU9EVUxFX0xJQ0VOU0UoIkdQTCANCj4r
djIiKTsNCg0KLS0NClNlbnQgZnJvbSBteSBBbmRyb2lkIGRldmljZSB3aXRoIEstOSBNYWlsLiBQ
bGVhc2UgZXhjdXNlIG15IGJyZXZpdHkuDQo=

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

* Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-24 11:46   ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2016-12-24 11:46 UTC (permalink / raw)
  To: Allen Liu, jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, xuejiancheng, kevin.lixu



On 24 December 2016 01:54:57 GMT+00:00, Allen Liu <liurenzhong@hisilicon.com> wrote:
>Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like
>Hi3516CV300, etc.
>The ADC controller is primarily in charge of detecting voltage.
>
>Reviewed-by: Jiancheng Xue <xuejiancheng@hisilicon.com>
>Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
Reading on phone so may not be that thorough!

Looks pretty good. The device abstraction makes it slightly more complicated than it needs
to be.  If you aren't going to follow up quickly with other device support please drop the
 abstraction. It can be easily readded when needed. 

Various little things inline.

Thanks

Jonathan
>---
> .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
> drivers/iio/adc/Kconfig                            |  10 +
> drivers/iio/adc/Makefile                           |   1 +
>drivers/iio/adc/hibvt_lsadc.c                      | 344
>+++++++++++++++++++++
> 4 files changed, 381 insertions(+)
>create mode 100644
>Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> create mode 100644 drivers/iio/adc/hibvt_lsadc.c
>
>diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>new file mode 100644
>index 0000000..63de46e
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>@@ -0,0 +1,26 @@
>+Hisilicon BVT Low Speed (LS) A/D Converter bindings
>+
>+Required properties:
>+- compatible: should be "hisilicon,<name>-lsadc"
>+   - "hisilicon,hibvt-lsadc": for hi3516cv300
>+
>+- reg: physical base address of the controller and length of memory
>mapped
>+       region.
>+- interrupts: The interrupt number to the cpu. The interrupt specifier
>format
>+              depends on the interrupt controller.
A cross reference to the interrupt bindings doc always good to add.
>+- #io-channel-cells: Should be 1, see ../iio-bindings.txt
>+
>+Optional properties:
>+- resets: Must contain an entry for each entry in reset-names if need
>support
>+	  this option. See ../reset/reset.txt for details.
>+- reset-names: Must include the name "saradc-apb".
>+
>+Example:
>+	lsadc: hibvt-lsadc@120e0000 {
>+			compatible = "hisilicon,hibvt-lsadc";
>+			reg = <0x120e0000 0x1000>;
>+			interrupts = <19>;
>+			resets = <&crg 0x7c 3>;
>+			reset-names = "lsadc-crg";
Doesn't contain saradc-apb which docs say it must...
>+			status = "disabled";
Not documented...
>+	};
>diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>index 99c0514..0443f51 100644
>--- a/drivers/iio/adc/Kconfig
>+++ b/drivers/iio/adc/Kconfig
>@@ -225,6 +225,16 @@ config HI8435
>	  This driver can also be built as a module. If so, the module will be
> 	  called hi8435.
> 
>+config HIBVT_LSADC
>+	tristate "HIBVT LSADC driver"
>+	depends on ARCH_HISI || COMPILE_TEST
>+	help
>+	  Say yes here to build support for the LSADC found in SoCs from
>+	  hisilicon BVT chip.
>+
>+	  To compile this driver as a module, choose M here: the
>+	  module will be called hibvt_lsadc.
>+
> config INA2XX_ADC
> 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
> 	depends on I2C && !SENSORS_INA2XX
>diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>index 7a40c04..6554d92 100644
>--- a/drivers/iio/adc/Makefile
>+++ b/drivers/iio/adc/Makefile
>@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> obj-$(CONFIG_HI8435) += hi8435.o
>+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
> obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
> obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>diff --git a/drivers/iio/adc/hibvt_lsadc.c
>b/drivers/iio/adc/hibvt_lsadc.c
>new file mode 100644
>index 0000000..a20afe8
>--- /dev/null
>+++ b/drivers/iio/adc/hibvt_lsadc.c
>@@ -0,0 +1,344 @@
>+/*
>+ * Hisilicon BVT Low Speed (LS) A/D Converter
>+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
>+ *
>+ * This program is free software; you can redistribute it and/or
>modify
>+ * it under the terms of the GNU General Public License as published
>by
>+ * the Free Software Foundation; either version 2 of the License, or
>+ * (at your option) any later version.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/platform_device.h>
>+#include <linux/interrupt.h>
>+#include <linux/io.h>
>+#include <linux/of.h>
>+#include <linux/of_device.h>
>+#include <linux/clk.h>
>+#include <linux/completion.h>
>+#include <linux/delay.h>
>+#include <linux/reset.h>
>+#include <linux/regulator/consumer.h>
>+#include <linux/iio/iio.h>
>+
>+/* hisilicon bvt adc registers definitions */
>+#define LSADC_CONFIG		0x00
>+#define CONFIG_DEGLITCH		BIT(17)
Please add a driver specific prefix to all defines to keep them in their own namespace.
>+#define CONFIG_RESET		BIT(15)
>+#define CONFIG_POWERDOWN	BIT(14)
>+#define CONFIG_MODE			BIT(13)
>+#define CONFIG_CHC_VALID	BIT(10)
>+#define CONFIG_CHB_VALID	BIT(9)
>+#define CONFIG_CHA_VALID	BIT(8)
>+
>+#define LSADC_TIMESCAN		0x08
Lsadc is perhaps to generic a prefix. Clash chances are a bit high
>+#define LSADC_INTEN			0x10
>+#define LSADC_INTSTATUS		0x14
>+#define LSADC_INTCLR		0x18
>+#define LSADC_START			0x1C
>+#define LSADC_STOP			0x20
>+#define LSADC_ACTBIT		0x24
>+#define LSADC_CHNDATA		0x2C
>+
>+#define ADC_CON_EN			(1u << 0)
>+#define ADC_CON_DEN			(0u << 0)
>+
>+#define ADC_NUM_BITS		10
>+
>+/* fix clk:3000000, default tscan set 10ms */
>+#define DEF_ADC_TSCAN_MS	(10*3000)
>+
>+#define LSADC_CHN_MASK		0x7
>+
>+#define LSADC_TIMEOUT		msecs_to_jiffies(100)
>+
>+/* default voltage scale for every channel <mv> */
>+static int g_voltage[] = {
>+	3300, 3300, 3300
>+};
Prefix these as well.
>+
>+struct hibvt_lsadc {
>+	void __iomem		*regs;
>+	struct completion	completion;
>+	struct reset_control	*reset;
>+	const struct hibvt_lsadc_data	*data;
>+	unsigned int		cur_chn;
>+	unsigned int		value;
>+};
>+
>+struct hibvt_lsadc_data {
>+	int				num_bits;
>+	const struct iio_chan_spec	*channels;
>+	int				num_channels;
>+
>+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
>+	void (*start_conv)(struct hibvt_lsadc *info);
>+	void (*stop_conv)(struct hibvt_lsadc *info);
>+};
>+
>+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
>+				    struct iio_chan_spec const *chan,
>+				    int *val, int *val2, long mask)
>+{
>+	struct hibvt_lsadc *info = iio_priv(indio_dev);
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_RAW:
>+		mutex_lock(&indio_dev->mlock);
>+
>+		reinit_completion(&info->completion);
>+
>+		/* Select the channel to be used */
>+		info->cur_chn = chan->channel;
>+
>+		if (info->data->start_conv)
>+			info->data->start_conv(info);
>+
>+		if (!wait_for_completion_timeout(&info->completion,
>+							LSADC_TIMEOUT)) {
>+			if (info->data->stop_conv)
>+				info->data->stop_conv(info);
>+			mutex_unlock(&indio_dev->mlock);
>+			return -ETIMEDOUT;
>+		}
>+
>+		*val = info->value;
>+		mutex_unlock(&indio_dev->mlock);
>+		return IIO_VAL_INT;
>+	case IIO_CHAN_INFO_SCALE:
>+		*val = g_voltage[chan->channel];
>+		*val2 = info->data->num_bits;
>+		return IIO_VAL_FRACTIONAL_LOG2;
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
>+{
>+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
>+	int mask;
>+
>+	mask = readl(info->regs + LSADC_INTSTATUS);
>+	mask &= LSADC_CHN_MASK;
>+
>+	/* Clear irq */
>+	if (info->data->clear_irq)
>+		info->data->clear_irq(info, mask);
>+
>+	/* Read value */
>+	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn <<
>2));
>+	info->value &= GENMASK(info->data->num_bits - 1, 0);
>+
>+	/* stop adc */
>+	if (info->data->stop_conv)
>+		info->data->stop_conv(info);
>+
>+	complete(&info->completion);
>+
>+	return IRQ_HANDLED;
>+}
>+
>+static const struct iio_info hibvt_lsadc_iio_info = {
>+	.read_raw = hibvt_lsadc_read_raw,
>+	.driver_module = THIS_MODULE,
>+};
>+
>+#define ADC_CHANNEL(_index, _id) {      \
Prefix this define. 
>+	.type = IIO_VOLTAGE,                \
>+	.indexed = 1,						\
>+	.channel = _index,					\
>+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>+			BIT(IIO_CHAN_INFO_SCALE),   \
>+	.datasheet_name = _id,              \
>+}
>+
>+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
>+	ADC_CHANNEL(0, "adc0"),
>+	ADC_CHANNEL(1, "adc1"),
>+	ADC_CHANNEL(2, "adc2"),
>+};
>+
>+static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask)
>+{
>+	writel(mask, info->regs + LSADC_INTCLR);
>+}
>+
>+static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info)
>+{
>+	unsigned int con;
>+
>+	/* set number bit */
>+	con = GENMASK(info->data->num_bits - 1, 0);
>+	writel(con, (info->regs + LSADC_ACTBIT));
>+
>+    /* config */
>+	con = readl(info->regs + LSADC_CONFIG);
>+	con &= ~CONFIG_RESET;
>+	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
>+	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
>+	con |= (CONFIG_CHA_VALID << info->cur_chn);
>+	writel(con, (info->regs + LSADC_CONFIG));
>+
>+	/* set timescan */
>+	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
>+
>+	/* clear interrupt */
>+	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
>+
>+	/* enable interrupt */
>+	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
>+
>+	/* start scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_START));
>+}
>+
>+static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info)
>+{
>+	/* reset the timescan */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
>+
>+	/* disable interrupt */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
>+
>+	/* stop scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_STOP));
>+}
>+
>+static const struct hibvt_lsadc_data lsadc_data = {
>+	.num_bits = ADC_NUM_BITS,
>+	.channels = hibvt_lsadc_iio_channels,
>+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
>+
>+	.clear_irq	= hibvt_lsadc_clear_irq,
>+	.start_conv	= hibvt_lsadc_start_conv,
>+	.stop_conv = hibvt_lsadc_stop_conv,
>+};
Usual convention is to only introduce a device type specific structure when more than one device is supported.  If you are going to follow up
 shortly with more device support then leave it but add a note to the patch description. If not please drop this abstraction.
>+
>+static const struct of_device_id hibvt_lsadc_match[] = {
>+	{
>+		.compatible = "hisilicon,hibvt-lsadc",
>+		.data = &lsadc_data,
>+	},
>+	{},
>+};
>+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
>+
>+/**
>+ * Reset LSADC Controller.
Single line comment syntax please.
>+ */
>+static void hibvt_lsadc_reset_controller(struct reset_control *reset)
>+{
>+	reset_control_assert(reset);
>+	usleep_range(10, 20);
>+	reset_control_deassert(reset);
>+}
>+
>+static int hibvt_lsadc_probe(struct platform_device *pdev)
>+{
>+	struct hibvt_lsadc *info = NULL;
>+	struct device_node *np = pdev->dev.of_node;
>+	struct iio_dev *indio_dev = NULL;
>+	struct resource	*mem;
>+	const struct of_device_id *match;
>+	int ret;
>+	int irq;
>+
>+	if (!np)
>+		return -ENODEV;
>+
>+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>+	if (!indio_dev) {
>+		dev_err(&pdev->dev, "failed allocating iio device\n");
>+		return -ENOMEM;
>+	}
>+	info = iio_priv(indio_dev);
>+
>+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
>+	info->data = match->data;
>+
>+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>+	if (IS_ERR(info->regs))
>+		return PTR_ERR(info->regs);
>+
>+	/*
>+	 * The reset should be an optional property, as it should work
>+	 * with old devicetrees as well
>+	 */
>+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
>+	if (IS_ERR(info->reset)) {
>+		ret = PTR_ERR(info->reset);
>+		if (ret != -ENOENT)
>+			return ret;
>+
>+		dev_dbg(&pdev->dev, "no reset control found\n");
>+		info->reset = NULL;
>+	}
>+
>+	init_completion(&info->completion);
>+
>+	irq = platform_get_irq(pdev, 0);
>+	if (irq < 0) {
>+		dev_err(&pdev->dev, "no irq resource?\n");
>+		return irq;
>+	}
>+
>+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
>+			       0, dev_name(&pdev->dev), info);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
>+		return ret;
>+	}
>+
>+	if (info->reset)
>+		hibvt_lsadc_reset_controller(info->reset);
>+
>+	platform_set_drvdata(pdev, indio_dev);
>+
>+	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 = &hibvt_lsadc_iio_info;
>+	indio_dev->modes = INDIO_DIRECT_MODE;
>+
>+	indio_dev->channels = info->data->channels;
>+	indio_dev->num_channels = info->data->num_channels;
>+
>+	ret = iio_device_register(indio_dev);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed register iio device\n");
>+		return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+static int hibvt_lsadc_remove(struct platform_device *pdev)
>+{
>+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>+
>+	iio_device_unregister(indio_dev);
As nothing else here can use devm version of register and drop remove entirely.
>+
>+	return 0;
>+}
>+
>+static struct platform_driver hibvt_lsadc_driver = {
>+	.probe		= hibvt_lsadc_probe,
>+	.remove		= hibvt_lsadc_remove,
>+	.driver		= {
>+		.name	= "hibvt-lsadc",
>+		.of_match_table = hibvt_lsadc_match,
>+	},
>+};
>+
>+module_platform_driver(hibvt_lsadc_driver);
>+
>+MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>");
>+MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
>+MODULE_LICENSE("GPL v2");

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-24 11:46   ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2016-12-24 11:46 UTC (permalink / raw)
  To: Allen Liu, jic23-DgEjT+Ai2ygdnm+yROfE0A, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
  Cc: akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w,
	ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w,
	krzk-DgEjT+Ai2ygdnm+yROfE0A, vilhelm.gray-Re5JQEeQqe8AvxtiuMwx3w,
	ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w,
	zhiyong.tao-NuS5LvNUpcJWk0Htik3J/w,
	daniel.baluta-ral2JQCrhuEAvxtiuMwx3w,
	leonard.crestez-ral2JQCrhuEAvxtiuMwx3w,
	ray.jui-dY08KVG/lbpWk0Htik3J/w,
	raveendra.padasalagi-dY08KVG/lbpWk0Htik3J/w,
	mranostay-Re5JQEeQqe8AvxtiuMwx3w,
	amsfield22-Re5JQEeQqe8AvxtiuMwx3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	xuejiancheng-C8/M+/jPZTeaMJb+Lgu22Q,
	kevin.lixu-C8/M+/jPZTeaMJb+Lgu22Q



On 24 December 2016 01:54:57 GMT+00:00, Allen Liu <liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org> wrote:
>Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like
>Hi3516CV300, etc.
>The ADC controller is primarily in charge of detecting voltage.
>
>Reviewed-by: Jiancheng Xue <xuejiancheng-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
>Signed-off-by: Allen Liu <liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
Reading on phone so may not be that thorough!

Looks pretty good. The device abstraction makes it slightly more complicated than it needs
to be.  If you aren't going to follow up quickly with other device support please drop the
 abstraction. It can be easily readded when needed. 

Various little things inline.

Thanks

Jonathan
>---
> .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
> drivers/iio/adc/Kconfig                            |  10 +
> drivers/iio/adc/Makefile                           |   1 +
>drivers/iio/adc/hibvt_lsadc.c                      | 344
>+++++++++++++++++++++
> 4 files changed, 381 insertions(+)
>create mode 100644
>Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> create mode 100644 drivers/iio/adc/hibvt_lsadc.c
>
>diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>new file mode 100644
>index 0000000..63de46e
>--- /dev/null
>+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>@@ -0,0 +1,26 @@
>+Hisilicon BVT Low Speed (LS) A/D Converter bindings
>+
>+Required properties:
>+- compatible: should be "hisilicon,<name>-lsadc"
>+   - "hisilicon,hibvt-lsadc": for hi3516cv300
>+
>+- reg: physical base address of the controller and length of memory
>mapped
>+       region.
>+- interrupts: The interrupt number to the cpu. The interrupt specifier
>format
>+              depends on the interrupt controller.
A cross reference to the interrupt bindings doc always good to add.
>+- #io-channel-cells: Should be 1, see ../iio-bindings.txt
>+
>+Optional properties:
>+- resets: Must contain an entry for each entry in reset-names if need
>support
>+	  this option. See ../reset/reset.txt for details.
>+- reset-names: Must include the name "saradc-apb".
>+
>+Example:
>+	lsadc: hibvt-lsadc@120e0000 {
>+			compatible = "hisilicon,hibvt-lsadc";
>+			reg = <0x120e0000 0x1000>;
>+			interrupts = <19>;
>+			resets = <&crg 0x7c 3>;
>+			reset-names = "lsadc-crg";
Doesn't contain saradc-apb which docs say it must...
>+			status = "disabled";
Not documented...
>+	};
>diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>index 99c0514..0443f51 100644
>--- a/drivers/iio/adc/Kconfig
>+++ b/drivers/iio/adc/Kconfig
>@@ -225,6 +225,16 @@ config HI8435
>	  This driver can also be built as a module. If so, the module will be
> 	  called hi8435.
> 
>+config HIBVT_LSADC
>+	tristate "HIBVT LSADC driver"
>+	depends on ARCH_HISI || COMPILE_TEST
>+	help
>+	  Say yes here to build support for the LSADC found in SoCs from
>+	  hisilicon BVT chip.
>+
>+	  To compile this driver as a module, choose M here: the
>+	  module will be called hibvt_lsadc.
>+
> config INA2XX_ADC
> 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
> 	depends on I2C && !SENSORS_INA2XX
>diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>index 7a40c04..6554d92 100644
>--- a/drivers/iio/adc/Makefile
>+++ b/drivers/iio/adc/Makefile
>@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
> obj-$(CONFIG_HI8435) += hi8435.o
>+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
> obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
> obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>diff --git a/drivers/iio/adc/hibvt_lsadc.c
>b/drivers/iio/adc/hibvt_lsadc.c
>new file mode 100644
>index 0000000..a20afe8
>--- /dev/null
>+++ b/drivers/iio/adc/hibvt_lsadc.c
>@@ -0,0 +1,344 @@
>+/*
>+ * Hisilicon BVT Low Speed (LS) A/D Converter
>+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
>+ *
>+ * This program is free software; you can redistribute it and/or
>modify
>+ * it under the terms of the GNU General Public License as published
>by
>+ * the Free Software Foundation; either version 2 of the License, or
>+ * (at your option) any later version.
>+ *
>+ * This program is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+ * GNU General Public License for more details.
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/platform_device.h>
>+#include <linux/interrupt.h>
>+#include <linux/io.h>
>+#include <linux/of.h>
>+#include <linux/of_device.h>
>+#include <linux/clk.h>
>+#include <linux/completion.h>
>+#include <linux/delay.h>
>+#include <linux/reset.h>
>+#include <linux/regulator/consumer.h>
>+#include <linux/iio/iio.h>
>+
>+/* hisilicon bvt adc registers definitions */
>+#define LSADC_CONFIG		0x00
>+#define CONFIG_DEGLITCH		BIT(17)
Please add a driver specific prefix to all defines to keep them in their own namespace.
>+#define CONFIG_RESET		BIT(15)
>+#define CONFIG_POWERDOWN	BIT(14)
>+#define CONFIG_MODE			BIT(13)
>+#define CONFIG_CHC_VALID	BIT(10)
>+#define CONFIG_CHB_VALID	BIT(9)
>+#define CONFIG_CHA_VALID	BIT(8)
>+
>+#define LSADC_TIMESCAN		0x08
Lsadc is perhaps to generic a prefix. Clash chances are a bit high
>+#define LSADC_INTEN			0x10
>+#define LSADC_INTSTATUS		0x14
>+#define LSADC_INTCLR		0x18
>+#define LSADC_START			0x1C
>+#define LSADC_STOP			0x20
>+#define LSADC_ACTBIT		0x24
>+#define LSADC_CHNDATA		0x2C
>+
>+#define ADC_CON_EN			(1u << 0)
>+#define ADC_CON_DEN			(0u << 0)
>+
>+#define ADC_NUM_BITS		10
>+
>+/* fix clk:3000000, default tscan set 10ms */
>+#define DEF_ADC_TSCAN_MS	(10*3000)
>+
>+#define LSADC_CHN_MASK		0x7
>+
>+#define LSADC_TIMEOUT		msecs_to_jiffies(100)
>+
>+/* default voltage scale for every channel <mv> */
>+static int g_voltage[] = {
>+	3300, 3300, 3300
>+};
Prefix these as well.
>+
>+struct hibvt_lsadc {
>+	void __iomem		*regs;
>+	struct completion	completion;
>+	struct reset_control	*reset;
>+	const struct hibvt_lsadc_data	*data;
>+	unsigned int		cur_chn;
>+	unsigned int		value;
>+};
>+
>+struct hibvt_lsadc_data {
>+	int				num_bits;
>+	const struct iio_chan_spec	*channels;
>+	int				num_channels;
>+
>+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
>+	void (*start_conv)(struct hibvt_lsadc *info);
>+	void (*stop_conv)(struct hibvt_lsadc *info);
>+};
>+
>+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
>+				    struct iio_chan_spec const *chan,
>+				    int *val, int *val2, long mask)
>+{
>+	struct hibvt_lsadc *info = iio_priv(indio_dev);
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_RAW:
>+		mutex_lock(&indio_dev->mlock);
>+
>+		reinit_completion(&info->completion);
>+
>+		/* Select the channel to be used */
>+		info->cur_chn = chan->channel;
>+
>+		if (info->data->start_conv)
>+			info->data->start_conv(info);
>+
>+		if (!wait_for_completion_timeout(&info->completion,
>+							LSADC_TIMEOUT)) {
>+			if (info->data->stop_conv)
>+				info->data->stop_conv(info);
>+			mutex_unlock(&indio_dev->mlock);
>+			return -ETIMEDOUT;
>+		}
>+
>+		*val = info->value;
>+		mutex_unlock(&indio_dev->mlock);
>+		return IIO_VAL_INT;
>+	case IIO_CHAN_INFO_SCALE:
>+		*val = g_voltage[chan->channel];
>+		*val2 = info->data->num_bits;
>+		return IIO_VAL_FRACTIONAL_LOG2;
>+	default:
>+		return -EINVAL;
>+	}
>+}
>+
>+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
>+{
>+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
>+	int mask;
>+
>+	mask = readl(info->regs + LSADC_INTSTATUS);
>+	mask &= LSADC_CHN_MASK;
>+
>+	/* Clear irq */
>+	if (info->data->clear_irq)
>+		info->data->clear_irq(info, mask);
>+
>+	/* Read value */
>+	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn <<
>2));
>+	info->value &= GENMASK(info->data->num_bits - 1, 0);
>+
>+	/* stop adc */
>+	if (info->data->stop_conv)
>+		info->data->stop_conv(info);
>+
>+	complete(&info->completion);
>+
>+	return IRQ_HANDLED;
>+}
>+
>+static const struct iio_info hibvt_lsadc_iio_info = {
>+	.read_raw = hibvt_lsadc_read_raw,
>+	.driver_module = THIS_MODULE,
>+};
>+
>+#define ADC_CHANNEL(_index, _id) {      \
Prefix this define. 
>+	.type = IIO_VOLTAGE,                \
>+	.indexed = 1,						\
>+	.channel = _index,					\
>+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>+			BIT(IIO_CHAN_INFO_SCALE),   \
>+	.datasheet_name = _id,              \
>+}
>+
>+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
>+	ADC_CHANNEL(0, "adc0"),
>+	ADC_CHANNEL(1, "adc1"),
>+	ADC_CHANNEL(2, "adc2"),
>+};
>+
>+static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask)
>+{
>+	writel(mask, info->regs + LSADC_INTCLR);
>+}
>+
>+static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info)
>+{
>+	unsigned int con;
>+
>+	/* set number bit */
>+	con = GENMASK(info->data->num_bits - 1, 0);
>+	writel(con, (info->regs + LSADC_ACTBIT));
>+
>+    /* config */
>+	con = readl(info->regs + LSADC_CONFIG);
>+	con &= ~CONFIG_RESET;
>+	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
>+	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
>+	con |= (CONFIG_CHA_VALID << info->cur_chn);
>+	writel(con, (info->regs + LSADC_CONFIG));
>+
>+	/* set timescan */
>+	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
>+
>+	/* clear interrupt */
>+	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
>+
>+	/* enable interrupt */
>+	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
>+
>+	/* start scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_START));
>+}
>+
>+static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info)
>+{
>+	/* reset the timescan */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
>+
>+	/* disable interrupt */
>+	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
>+
>+	/* stop scan */
>+	writel(ADC_CON_EN, (info->regs + LSADC_STOP));
>+}
>+
>+static const struct hibvt_lsadc_data lsadc_data = {
>+	.num_bits = ADC_NUM_BITS,
>+	.channels = hibvt_lsadc_iio_channels,
>+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
>+
>+	.clear_irq	= hibvt_lsadc_clear_irq,
>+	.start_conv	= hibvt_lsadc_start_conv,
>+	.stop_conv = hibvt_lsadc_stop_conv,
>+};
Usual convention is to only introduce a device type specific structure when more than one device is supported.  If you are going to follow up
 shortly with more device support then leave it but add a note to the patch description. If not please drop this abstraction.
>+
>+static const struct of_device_id hibvt_lsadc_match[] = {
>+	{
>+		.compatible = "hisilicon,hibvt-lsadc",
>+		.data = &lsadc_data,
>+	},
>+	{},
>+};
>+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
>+
>+/**
>+ * Reset LSADC Controller.
Single line comment syntax please.
>+ */
>+static void hibvt_lsadc_reset_controller(struct reset_control *reset)
>+{
>+	reset_control_assert(reset);
>+	usleep_range(10, 20);
>+	reset_control_deassert(reset);
>+}
>+
>+static int hibvt_lsadc_probe(struct platform_device *pdev)
>+{
>+	struct hibvt_lsadc *info = NULL;
>+	struct device_node *np = pdev->dev.of_node;
>+	struct iio_dev *indio_dev = NULL;
>+	struct resource	*mem;
>+	const struct of_device_id *match;
>+	int ret;
>+	int irq;
>+
>+	if (!np)
>+		return -ENODEV;
>+
>+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
>+	if (!indio_dev) {
>+		dev_err(&pdev->dev, "failed allocating iio device\n");
>+		return -ENOMEM;
>+	}
>+	info = iio_priv(indio_dev);
>+
>+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
>+	info->data = match->data;
>+
>+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>+	if (IS_ERR(info->regs))
>+		return PTR_ERR(info->regs);
>+
>+	/*
>+	 * The reset should be an optional property, as it should work
>+	 * with old devicetrees as well
>+	 */
>+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
>+	if (IS_ERR(info->reset)) {
>+		ret = PTR_ERR(info->reset);
>+		if (ret != -ENOENT)
>+			return ret;
>+
>+		dev_dbg(&pdev->dev, "no reset control found\n");
>+		info->reset = NULL;
>+	}
>+
>+	init_completion(&info->completion);
>+
>+	irq = platform_get_irq(pdev, 0);
>+	if (irq < 0) {
>+		dev_err(&pdev->dev, "no irq resource?\n");
>+		return irq;
>+	}
>+
>+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
>+			       0, dev_name(&pdev->dev), info);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
>+		return ret;
>+	}
>+
>+	if (info->reset)
>+		hibvt_lsadc_reset_controller(info->reset);
>+
>+	platform_set_drvdata(pdev, indio_dev);
>+
>+	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 = &hibvt_lsadc_iio_info;
>+	indio_dev->modes = INDIO_DIRECT_MODE;
>+
>+	indio_dev->channels = info->data->channels;
>+	indio_dev->num_channels = info->data->num_channels;
>+
>+	ret = iio_device_register(indio_dev);
>+	if (ret < 0) {
>+		dev_err(&pdev->dev, "failed register iio device\n");
>+		return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+static int hibvt_lsadc_remove(struct platform_device *pdev)
>+{
>+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>+
>+	iio_device_unregister(indio_dev);
As nothing else here can use devm version of register and drop remove entirely.
>+
>+	return 0;
>+}
>+
>+static struct platform_driver hibvt_lsadc_driver = {
>+	.probe		= hibvt_lsadc_probe,
>+	.remove		= hibvt_lsadc_remove,
>+	.driver		= {
>+		.name	= "hibvt-lsadc",
>+		.of_match_table = hibvt_lsadc_match,
>+	},
>+};
>+
>+module_platform_driver(hibvt_lsadc_driver);
>+
>+MODULE_AUTHOR("Allen Liu <liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>");
>+MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
>+MODULE_LICENSE("GPL v2");

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-24  2:31   ` Jiancheng Xue
  0 siblings, 0 replies; 19+ messages in thread
From: Jiancheng Xue @ 2016-12-24  2:31 UTC (permalink / raw)
  To: Allen Liu, jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: yanhaifeng, hermit.wangheming, akinobu.mita, ludovic.desroches,
	krzk, vilhelm.gray, ksenija.stanojevic, zhiyong.tao,
	daniel.baluta, leonard.crestez, ray.jui, raveendra.padasalagi,
	mranostay, amsfield22, linux-iio, devicetree, linux-kernel,
	kevin.lixu



On 2016/12/24 9:54, Allen Liu wrote:
> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
> The ADC controller is primarily in charge of detecting voltage.
> 
> Reviewed-by: Jiancheng Xue <xuejiancheng@hisilicon.com>
Hi

Sorry. I haven't reviewed this patch. Please remove this line. Thank you!

Regards,
Jiancheng

> Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
> ---
>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
>  drivers/iio/adc/Kconfig                            |  10 +
>  drivers/iio/adc/Makefile                           |   1 +
>  drivers/iio/adc/hibvt_lsadc.c                      | 344 +++++++++++++++++++++
>  4 files changed, 381 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> new file mode 100644
> index 0000000..63de46e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> @@ -0,0 +1,26 @@
> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
> +
> +Required properties:
> +- compatible: should be "hisilicon,<name>-lsadc"
> +   - "hisilicon,hibvt-lsadc": for hi3516cv300
> +
> +- reg: physical base address of the controller and length of memory mapped
> +       region.
> +- interrupts: The interrupt number to the cpu. The interrupt specifier format
> +              depends on the interrupt controller.
> +- #io-channel-cells: Should be 1, see ../iio-bindings.txt
> +
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +	  this option. See ../reset/reset.txt for details.
> +- reset-names: Must include the name "saradc-apb".
> +
> +Example:
> +	lsadc: hibvt-lsadc@120e0000 {
> +			compatible = "hisilicon,hibvt-lsadc";
> +			reg = <0x120e0000 0x1000>;
> +			interrupts = <19>;
> +			resets = <&crg 0x7c 3>;
> +			reset-names = "lsadc-crg";
> +			status = "disabled";
> +	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 99c0514..0443f51 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -225,6 +225,16 @@ config HI8435
>  	  This driver can also be built as a module. If so, the module will be
>  	  called hi8435.
>  
> +config HIBVT_LSADC
> +	tristate "HIBVT LSADC driver"
> +	depends on ARCH_HISI || COMPILE_TEST
> +	help
> +	  Say yes here to build support for the LSADC found in SoCs from
> +	  hisilicon BVT chip.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called hibvt_lsadc.
> +
>  config INA2XX_ADC
>  	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
>  	depends on I2C && !SENSORS_INA2XX
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..6554d92 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>  obj-$(CONFIG_HI8435) += hi8435.o
> +obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
>  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
>  obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
> diff --git a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c
> new file mode 100644
> index 0000000..a20afe8
> --- /dev/null
> +++ b/drivers/iio/adc/hibvt_lsadc.c
> @@ -0,0 +1,344 @@
> +/*
> + * Hisilicon BVT Low Speed (LS) A/D Converter
> + * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/iio/iio.h>
> +
> +/* hisilicon bvt adc registers definitions */
> +#define LSADC_CONFIG		0x00
> +#define CONFIG_DEGLITCH		BIT(17)
> +#define CONFIG_RESET		BIT(15)
> +#define CONFIG_POWERDOWN	BIT(14)
> +#define CONFIG_MODE			BIT(13)
> +#define CONFIG_CHC_VALID	BIT(10)
> +#define CONFIG_CHB_VALID	BIT(9)
> +#define CONFIG_CHA_VALID	BIT(8)
> +
> +#define LSADC_TIMESCAN		0x08
> +#define LSADC_INTEN			0x10
> +#define LSADC_INTSTATUS		0x14
> +#define LSADC_INTCLR		0x18
> +#define LSADC_START			0x1C
> +#define LSADC_STOP			0x20
> +#define LSADC_ACTBIT		0x24
> +#define LSADC_CHNDATA		0x2C
> +
> +#define ADC_CON_EN			(1u << 0)
> +#define ADC_CON_DEN			(0u << 0)
> +
> +#define ADC_NUM_BITS		10
> +
> +/* fix clk:3000000, default tscan set 10ms */
> +#define DEF_ADC_TSCAN_MS	(10*3000)
> +
> +#define LSADC_CHN_MASK		0x7
> +
> +#define LSADC_TIMEOUT		msecs_to_jiffies(100)
> +
> +/* default voltage scale for every channel <mv> */
> +static int g_voltage[] = {
> +	3300, 3300, 3300
> +};
> +
> +struct hibvt_lsadc {
> +	void __iomem		*regs;
> +	struct completion	completion;
> +	struct reset_control	*reset;
> +	const struct hibvt_lsadc_data	*data;
> +	unsigned int		cur_chn;
> +	unsigned int		value;
> +};
> +
> +struct hibvt_lsadc_data {
> +	int				num_bits;
> +	const struct iio_chan_spec	*channels;
> +	int				num_channels;
> +
> +	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
> +	void (*start_conv)(struct hibvt_lsadc *info);
> +	void (*stop_conv)(struct hibvt_lsadc *info);
> +};
> +
> +static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec const *chan,
> +				    int *val, int *val2, long mask)
> +{
> +	struct hibvt_lsadc *info = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&indio_dev->mlock);
> +
> +		reinit_completion(&info->completion);
> +
> +		/* Select the channel to be used */
> +		info->cur_chn = chan->channel;
> +
> +		if (info->data->start_conv)
> +			info->data->start_conv(info);
> +
> +		if (!wait_for_completion_timeout(&info->completion,
> +							LSADC_TIMEOUT)) {
> +			if (info->data->stop_conv)
> +				info->data->stop_conv(info);
> +			mutex_unlock(&indio_dev->mlock);
> +			return -ETIMEDOUT;
> +		}
> +
> +		*val = info->value;
> +		mutex_unlock(&indio_dev->mlock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = g_voltage[chan->channel];
> +		*val2 = info->data->num_bits;
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
> +{
> +	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
> +	int mask;
> +
> +	mask = readl(info->regs + LSADC_INTSTATUS);
> +	mask &= LSADC_CHN_MASK;
> +
> +	/* Clear irq */
> +	if (info->data->clear_irq)
> +		info->data->clear_irq(info, mask);
> +
> +	/* Read value */
> +	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn << 2));
> +	info->value &= GENMASK(info->data->num_bits - 1, 0);
> +
> +	/* stop adc */
> +	if (info->data->stop_conv)
> +		info->data->stop_conv(info);
> +
> +	complete(&info->completion);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct iio_info hibvt_lsadc_iio_info = {
> +	.read_raw = hibvt_lsadc_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +#define ADC_CHANNEL(_index, _id) {      \
> +	.type = IIO_VOLTAGE,                \
> +	.indexed = 1,						\
> +	.channel = _index,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
> +			BIT(IIO_CHAN_INFO_SCALE),   \
> +	.datasheet_name = _id,              \
> +}
> +
> +static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
> +	ADC_CHANNEL(0, "adc0"),
> +	ADC_CHANNEL(1, "adc1"),
> +	ADC_CHANNEL(2, "adc2"),
> +};
> +
> +static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask)
> +{
> +	writel(mask, info->regs + LSADC_INTCLR);
> +}
> +
> +static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info)
> +{
> +	unsigned int con;
> +
> +	/* set number bit */
> +	con = GENMASK(info->data->num_bits - 1, 0);
> +	writel(con, (info->regs + LSADC_ACTBIT));
> +
> +    /* config */
> +	con = readl(info->regs + LSADC_CONFIG);
> +	con &= ~CONFIG_RESET;
> +	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
> +	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
> +	con |= (CONFIG_CHA_VALID << info->cur_chn);
> +	writel(con, (info->regs + LSADC_CONFIG));
> +
> +	/* set timescan */
> +	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
> +
> +	/* clear interrupt */
> +	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
> +
> +	/* enable interrupt */
> +	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
> +
> +	/* start scan */
> +	writel(ADC_CON_EN, (info->regs + LSADC_START));
> +}
> +
> +static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info)
> +{
> +	/* reset the timescan */
> +	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
> +
> +	/* disable interrupt */
> +	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
> +
> +	/* stop scan */
> +	writel(ADC_CON_EN, (info->regs + LSADC_STOP));
> +}
> +
> +static const struct hibvt_lsadc_data lsadc_data = {
> +	.num_bits = ADC_NUM_BITS,
> +	.channels = hibvt_lsadc_iio_channels,
> +	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
> +
> +	.clear_irq	= hibvt_lsadc_clear_irq,
> +	.start_conv	= hibvt_lsadc_start_conv,
> +	.stop_conv = hibvt_lsadc_stop_conv,
> +};
> +
> +static const struct of_device_id hibvt_lsadc_match[] = {
> +	{
> +		.compatible = "hisilicon,hibvt-lsadc",
> +		.data = &lsadc_data,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
> +
> +/**
> + * Reset LSADC Controller.
> + */
> +static void hibvt_lsadc_reset_controller(struct reset_control *reset)
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
> +static int hibvt_lsadc_probe(struct platform_device *pdev)
> +{
> +	struct hibvt_lsadc *info = NULL;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev = NULL;
> +	struct resource	*mem;
> +	const struct of_device_id *match;
> +	int ret;
> +	int irq;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> +	if (!indio_dev) {
> +		dev_err(&pdev->dev, "failed allocating iio device\n");
> +		return -ENOMEM;
> +	}
> +	info = iio_priv(indio_dev);
> +
> +	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
> +	info->data = match->data;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	info->regs = devm_ioremap_resource(&pdev->dev, mem);
> +	if (IS_ERR(info->regs))
> +		return PTR_ERR(info->regs);
> +
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
> +	init_completion(&info->completion);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq resource?\n");
> +		return irq;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
> +			       0, dev_name(&pdev->dev), info);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
> +		return ret;
> +	}
> +
> +	if (info->reset)
> +		hibvt_lsadc_reset_controller(info->reset);
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	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 = &hibvt_lsadc_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	indio_dev->channels = info->data->channels;
> +	indio_dev->num_channels = info->data->num_channels;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed register iio device\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hibvt_lsadc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(indio_dev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver hibvt_lsadc_driver = {
> +	.probe		= hibvt_lsadc_probe,
> +	.remove		= hibvt_lsadc_remove,
> +	.driver		= {
> +		.name	= "hibvt-lsadc",
> +		.of_match_table = hibvt_lsadc_match,
> +	},
> +};
> +
> +module_platform_driver(hibvt_lsadc_driver);
> +
> +MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>");
> +MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-24  2:31   ` Jiancheng Xue
  0 siblings, 0 replies; 19+ messages in thread
From: Jiancheng Xue @ 2016-12-24  2:31 UTC (permalink / raw)
  To: Allen Liu, jic23-DgEjT+Ai2ygdnm+yROfE0A, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
  Cc: yanhaifeng-C8/M+/jPZTeaMJb+Lgu22Q,
	hermit.wangheming-C8/M+/jPZTeaMJb+Lgu22Q,
	akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w,
	ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w,
	krzk-DgEjT+Ai2ygdnm+yROfE0A, vilhelm.gray-Re5JQEeQqe8AvxtiuMwx3w,
	ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w,
	zhiyong.tao-NuS5LvNUpcJWk0Htik3J/w,
	daniel.baluta-ral2JQCrhuEAvxtiuMwx3w,
	leonard.crestez-ral2JQCrhuEAvxtiuMwx3w,
	ray.jui-dY08KVG/lbpWk0Htik3J/w,
	raveendra.padasalagi-dY08KVG/lbpWk0Htik3J/w,
	mranostay-Re5JQEeQqe8AvxtiuMwx3w,
	amsfield22-Re5JQEeQqe8AvxtiuMwx3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kevin.lixu-C8/M+/jPZTeaMJb+Lgu22Q



On 2016/12/24 9:54, Allen Liu wrote:
> Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
> The ADC controller is primarily in charge of detecting voltage.
> 
> Reviewed-by: Jiancheng Xue <xuejiancheng-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
Hi

Sorry. I haven't reviewed this patch. Please remove this line. Thank you!

Regards,
Jiancheng

> Signed-off-by: Allen Liu <liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
> ---
>  .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
>  drivers/iio/adc/Kconfig                            |  10 +
>  drivers/iio/adc/Makefile                           |   1 +
>  drivers/iio/adc/hibvt_lsadc.c                      | 344 +++++++++++++++++++++
>  4 files changed, 381 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
>  create mode 100644 drivers/iio/adc/hibvt_lsadc.c
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> new file mode 100644
> index 0000000..63de46e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
> @@ -0,0 +1,26 @@
> +Hisilicon BVT Low Speed (LS) A/D Converter bindings
> +
> +Required properties:
> +- compatible: should be "hisilicon,<name>-lsadc"
> +   - "hisilicon,hibvt-lsadc": for hi3516cv300
> +
> +- reg: physical base address of the controller and length of memory mapped
> +       region.
> +- interrupts: The interrupt number to the cpu. The interrupt specifier format
> +              depends on the interrupt controller.
> +- #io-channel-cells: Should be 1, see ../iio-bindings.txt
> +
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +	  this option. See ../reset/reset.txt for details.
> +- reset-names: Must include the name "saradc-apb".
> +
> +Example:
> +	lsadc: hibvt-lsadc@120e0000 {
> +			compatible = "hisilicon,hibvt-lsadc";
> +			reg = <0x120e0000 0x1000>;
> +			interrupts = <19>;
> +			resets = <&crg 0x7c 3>;
> +			reset-names = "lsadc-crg";
> +			status = "disabled";
> +	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 99c0514..0443f51 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -225,6 +225,16 @@ config HI8435
>  	  This driver can also be built as a module. If so, the module will be
>  	  called hi8435.
>  
> +config HIBVT_LSADC
> +	tristate "HIBVT LSADC driver"
> +	depends on ARCH_HISI || COMPILE_TEST
> +	help
> +	  Say yes here to build support for the LSADC found in SoCs from
> +	  hisilicon BVT chip.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called hibvt_lsadc.
> +
>  config INA2XX_ADC
>  	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
>  	depends on I2C && !SENSORS_INA2XX
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..6554d92 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>  obj-$(CONFIG_HI8435) += hi8435.o
> +obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
>  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
>  obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
> diff --git a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c
> new file mode 100644
> index 0000000..a20afe8
> --- /dev/null
> +++ b/drivers/iio/adc/hibvt_lsadc.c
> @@ -0,0 +1,344 @@
> +/*
> + * Hisilicon BVT Low Speed (LS) A/D Converter
> + * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/iio/iio.h>
> +
> +/* hisilicon bvt adc registers definitions */
> +#define LSADC_CONFIG		0x00
> +#define CONFIG_DEGLITCH		BIT(17)
> +#define CONFIG_RESET		BIT(15)
> +#define CONFIG_POWERDOWN	BIT(14)
> +#define CONFIG_MODE			BIT(13)
> +#define CONFIG_CHC_VALID	BIT(10)
> +#define CONFIG_CHB_VALID	BIT(9)
> +#define CONFIG_CHA_VALID	BIT(8)
> +
> +#define LSADC_TIMESCAN		0x08
> +#define LSADC_INTEN			0x10
> +#define LSADC_INTSTATUS		0x14
> +#define LSADC_INTCLR		0x18
> +#define LSADC_START			0x1C
> +#define LSADC_STOP			0x20
> +#define LSADC_ACTBIT		0x24
> +#define LSADC_CHNDATA		0x2C
> +
> +#define ADC_CON_EN			(1u << 0)
> +#define ADC_CON_DEN			(0u << 0)
> +
> +#define ADC_NUM_BITS		10
> +
> +/* fix clk:3000000, default tscan set 10ms */
> +#define DEF_ADC_TSCAN_MS	(10*3000)
> +
> +#define LSADC_CHN_MASK		0x7
> +
> +#define LSADC_TIMEOUT		msecs_to_jiffies(100)
> +
> +/* default voltage scale for every channel <mv> */
> +static int g_voltage[] = {
> +	3300, 3300, 3300
> +};
> +
> +struct hibvt_lsadc {
> +	void __iomem		*regs;
> +	struct completion	completion;
> +	struct reset_control	*reset;
> +	const struct hibvt_lsadc_data	*data;
> +	unsigned int		cur_chn;
> +	unsigned int		value;
> +};
> +
> +struct hibvt_lsadc_data {
> +	int				num_bits;
> +	const struct iio_chan_spec	*channels;
> +	int				num_channels;
> +
> +	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
> +	void (*start_conv)(struct hibvt_lsadc *info);
> +	void (*stop_conv)(struct hibvt_lsadc *info);
> +};
> +
> +static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec const *chan,
> +				    int *val, int *val2, long mask)
> +{
> +	struct hibvt_lsadc *info = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&indio_dev->mlock);
> +
> +		reinit_completion(&info->completion);
> +
> +		/* Select the channel to be used */
> +		info->cur_chn = chan->channel;
> +
> +		if (info->data->start_conv)
> +			info->data->start_conv(info);
> +
> +		if (!wait_for_completion_timeout(&info->completion,
> +							LSADC_TIMEOUT)) {
> +			if (info->data->stop_conv)
> +				info->data->stop_conv(info);
> +			mutex_unlock(&indio_dev->mlock);
> +			return -ETIMEDOUT;
> +		}
> +
> +		*val = info->value;
> +		mutex_unlock(&indio_dev->mlock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = g_voltage[chan->channel];
> +		*val2 = info->data->num_bits;
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
> +{
> +	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
> +	int mask;
> +
> +	mask = readl(info->regs + LSADC_INTSTATUS);
> +	mask &= LSADC_CHN_MASK;
> +
> +	/* Clear irq */
> +	if (info->data->clear_irq)
> +		info->data->clear_irq(info, mask);
> +
> +	/* Read value */
> +	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn << 2));
> +	info->value &= GENMASK(info->data->num_bits - 1, 0);
> +
> +	/* stop adc */
> +	if (info->data->stop_conv)
> +		info->data->stop_conv(info);
> +
> +	complete(&info->completion);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct iio_info hibvt_lsadc_iio_info = {
> +	.read_raw = hibvt_lsadc_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +#define ADC_CHANNEL(_index, _id) {      \
> +	.type = IIO_VOLTAGE,                \
> +	.indexed = 1,						\
> +	.channel = _index,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
> +			BIT(IIO_CHAN_INFO_SCALE),   \
> +	.datasheet_name = _id,              \
> +}
> +
> +static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
> +	ADC_CHANNEL(0, "adc0"),
> +	ADC_CHANNEL(1, "adc1"),
> +	ADC_CHANNEL(2, "adc2"),
> +};
> +
> +static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask)
> +{
> +	writel(mask, info->regs + LSADC_INTCLR);
> +}
> +
> +static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info)
> +{
> +	unsigned int con;
> +
> +	/* set number bit */
> +	con = GENMASK(info->data->num_bits - 1, 0);
> +	writel(con, (info->regs + LSADC_ACTBIT));
> +
> +    /* config */
> +	con = readl(info->regs + LSADC_CONFIG);
> +	con &= ~CONFIG_RESET;
> +	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
> +	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
> +	con |= (CONFIG_CHA_VALID << info->cur_chn);
> +	writel(con, (info->regs + LSADC_CONFIG));
> +
> +	/* set timescan */
> +	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
> +
> +	/* clear interrupt */
> +	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
> +
> +	/* enable interrupt */
> +	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
> +
> +	/* start scan */
> +	writel(ADC_CON_EN, (info->regs + LSADC_START));
> +}
> +
> +static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info)
> +{
> +	/* reset the timescan */
> +	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
> +
> +	/* disable interrupt */
> +	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
> +
> +	/* stop scan */
> +	writel(ADC_CON_EN, (info->regs + LSADC_STOP));
> +}
> +
> +static const struct hibvt_lsadc_data lsadc_data = {
> +	.num_bits = ADC_NUM_BITS,
> +	.channels = hibvt_lsadc_iio_channels,
> +	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
> +
> +	.clear_irq	= hibvt_lsadc_clear_irq,
> +	.start_conv	= hibvt_lsadc_start_conv,
> +	.stop_conv = hibvt_lsadc_stop_conv,
> +};
> +
> +static const struct of_device_id hibvt_lsadc_match[] = {
> +	{
> +		.compatible = "hisilicon,hibvt-lsadc",
> +		.data = &lsadc_data,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
> +
> +/**
> + * Reset LSADC Controller.
> + */
> +static void hibvt_lsadc_reset_controller(struct reset_control *reset)
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
> +static int hibvt_lsadc_probe(struct platform_device *pdev)
> +{
> +	struct hibvt_lsadc *info = NULL;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev = NULL;
> +	struct resource	*mem;
> +	const struct of_device_id *match;
> +	int ret;
> +	int irq;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> +	if (!indio_dev) {
> +		dev_err(&pdev->dev, "failed allocating iio device\n");
> +		return -ENOMEM;
> +	}
> +	info = iio_priv(indio_dev);
> +
> +	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
> +	info->data = match->data;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	info->regs = devm_ioremap_resource(&pdev->dev, mem);
> +	if (IS_ERR(info->regs))
> +		return PTR_ERR(info->regs);
> +
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
> +	init_completion(&info->completion);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq resource?\n");
> +		return irq;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
> +			       0, dev_name(&pdev->dev), info);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
> +		return ret;
> +	}
> +
> +	if (info->reset)
> +		hibvt_lsadc_reset_controller(info->reset);
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	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 = &hibvt_lsadc_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	indio_dev->channels = info->data->channels;
> +	indio_dev->num_channels = info->data->num_channels;
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed register iio device\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int hibvt_lsadc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(indio_dev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver hibvt_lsadc_driver = {
> +	.probe		= hibvt_lsadc_probe,
> +	.remove		= hibvt_lsadc_remove,
> +	.driver		= {
> +		.name	= "hibvt-lsadc",
> +		.of_match_table = hibvt_lsadc_match,
> +	},
> +};
> +
> +module_platform_driver(hibvt_lsadc_driver);
> +
> +MODULE_AUTHOR("Allen Liu <liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>");
> +MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
> +MODULE_LICENSE("GPL v2");
> 

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

* [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-24  1:54 ` Allen Liu
  0 siblings, 0 replies; 19+ messages in thread
From: Allen Liu @ 2016-12-24  1:54 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: akinobu.mita, ludovic.desroches, krzk, vilhelm.gray,
	ksenija.stanojevic, zhiyong.tao, daniel.baluta, leonard.crestez,
	ray.jui, raveendra.padasalagi, mranostay, amsfield22, linux-iio,
	devicetree, linux-kernel, xuejiancheng, kevin.lixu, liurenzhong

Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
The ADC controller is primarily in charge of detecting voltage.

Reviewed-by: Jiancheng Xue <xuejiancheng@hisilicon.com>
Signed-off-by: Allen Liu <liurenzhong@hisilicon.com>
---
 .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
 drivers/iio/adc/Kconfig                            |  10 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/hibvt_lsadc.c                      | 344 +++++++++++++++++++++
 4 files changed, 381 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
 create mode 100644 drivers/iio/adc/hibvt_lsadc.c

diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
new file mode 100644
index 0000000..63de46e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
@@ -0,0 +1,26 @@
+Hisilicon BVT Low Speed (LS) A/D Converter bindings
+
+Required properties:
+- compatible: should be "hisilicon,<name>-lsadc"
+   - "hisilicon,hibvt-lsadc": for hi3516cv300
+
+- reg: physical base address of the controller and length of memory mapped
+       region.
+- interrupts: The interrupt number to the cpu. The interrupt specifier format
+              depends on the interrupt controller.
+- #io-channel-cells: Should be 1, see ../iio-bindings.txt
+
+Optional properties:
+- resets: Must contain an entry for each entry in reset-names if need support
+	  this option. See ../reset/reset.txt for details.
+- reset-names: Must include the name "saradc-apb".
+
+Example:
+	lsadc: hibvt-lsadc@120e0000 {
+			compatible = "hisilicon,hibvt-lsadc";
+			reg = <0x120e0000 0x1000>;
+			interrupts = <19>;
+			resets = <&crg 0x7c 3>;
+			reset-names = "lsadc-crg";
+			status = "disabled";
+	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 99c0514..0443f51 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -225,6 +225,16 @@ config HI8435
 	  This driver can also be built as a module. If so, the module will be
 	  called hi8435.
 
+config HIBVT_LSADC
+	tristate "HIBVT LSADC driver"
+	depends on ARCH_HISI || COMPILE_TEST
+	help
+	  Say yes here to build support for the LSADC found in SoCs from
+	  hisilicon BVT chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hibvt_lsadc.
+
 config INA2XX_ADC
 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
 	depends on I2C && !SENSORS_INA2XX
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..6554d92 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
 obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
 obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
diff --git a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c
new file mode 100644
index 0000000..a20afe8
--- /dev/null
+++ b/drivers/iio/adc/hibvt_lsadc.c
@@ -0,0 +1,344 @@
+/*
+ * Hisilicon BVT Low Speed (LS) A/D Converter
+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+
+/* hisilicon bvt adc registers definitions */
+#define LSADC_CONFIG		0x00
+#define CONFIG_DEGLITCH		BIT(17)
+#define CONFIG_RESET		BIT(15)
+#define CONFIG_POWERDOWN	BIT(14)
+#define CONFIG_MODE			BIT(13)
+#define CONFIG_CHC_VALID	BIT(10)
+#define CONFIG_CHB_VALID	BIT(9)
+#define CONFIG_CHA_VALID	BIT(8)
+
+#define LSADC_TIMESCAN		0x08
+#define LSADC_INTEN			0x10
+#define LSADC_INTSTATUS		0x14
+#define LSADC_INTCLR		0x18
+#define LSADC_START			0x1C
+#define LSADC_STOP			0x20
+#define LSADC_ACTBIT		0x24
+#define LSADC_CHNDATA		0x2C
+
+#define ADC_CON_EN			(1u << 0)
+#define ADC_CON_DEN			(0u << 0)
+
+#define ADC_NUM_BITS		10
+
+/* fix clk:3000000, default tscan set 10ms */
+#define DEF_ADC_TSCAN_MS	(10*3000)
+
+#define LSADC_CHN_MASK		0x7
+
+#define LSADC_TIMEOUT		msecs_to_jiffies(100)
+
+/* default voltage scale for every channel <mv> */
+static int g_voltage[] = {
+	3300, 3300, 3300
+};
+
+struct hibvt_lsadc {
+	void __iomem		*regs;
+	struct completion	completion;
+	struct reset_control	*reset;
+	const struct hibvt_lsadc_data	*data;
+	unsigned int		cur_chn;
+	unsigned int		value;
+};
+
+struct hibvt_lsadc_data {
+	int				num_bits;
+	const struct iio_chan_spec	*channels;
+	int				num_channels;
+
+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
+	void (*start_conv)(struct hibvt_lsadc *info);
+	void (*stop_conv)(struct hibvt_lsadc *info);
+};
+
+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
+				    struct iio_chan_spec const *chan,
+				    int *val, int *val2, long mask)
+{
+	struct hibvt_lsadc *info = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+
+		reinit_completion(&info->completion);
+
+		/* Select the channel to be used */
+		info->cur_chn = chan->channel;
+
+		if (info->data->start_conv)
+			info->data->start_conv(info);
+
+		if (!wait_for_completion_timeout(&info->completion,
+							LSADC_TIMEOUT)) {
+			if (info->data->stop_conv)
+				info->data->stop_conv(info);
+			mutex_unlock(&indio_dev->mlock);
+			return -ETIMEDOUT;
+		}
+
+		*val = info->value;
+		mutex_unlock(&indio_dev->mlock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = g_voltage[chan->channel];
+		*val2 = info->data->num_bits;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
+{
+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
+	int mask;
+
+	mask = readl(info->regs + LSADC_INTSTATUS);
+	mask &= LSADC_CHN_MASK;
+
+	/* Clear irq */
+	if (info->data->clear_irq)
+		info->data->clear_irq(info, mask);
+
+	/* Read value */
+	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn << 2));
+	info->value &= GENMASK(info->data->num_bits - 1, 0);
+
+	/* stop adc */
+	if (info->data->stop_conv)
+		info->data->stop_conv(info);
+
+	complete(&info->completion);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_info hibvt_lsadc_iio_info = {
+	.read_raw = hibvt_lsadc_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+#define ADC_CHANNEL(_index, _id) {      \
+	.type = IIO_VOLTAGE,                \
+	.indexed = 1,						\
+	.channel = _index,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+			BIT(IIO_CHAN_INFO_SCALE),   \
+	.datasheet_name = _id,              \
+}
+
+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
+	ADC_CHANNEL(0, "adc0"),
+	ADC_CHANNEL(1, "adc1"),
+	ADC_CHANNEL(2, "adc2"),
+};
+
+static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask)
+{
+	writel(mask, info->regs + LSADC_INTCLR);
+}
+
+static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info)
+{
+	unsigned int con;
+
+	/* set number bit */
+	con = GENMASK(info->data->num_bits - 1, 0);
+	writel(con, (info->regs + LSADC_ACTBIT));
+
+    /* config */
+	con = readl(info->regs + LSADC_CONFIG);
+	con &= ~CONFIG_RESET;
+	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
+	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
+	con |= (CONFIG_CHA_VALID << info->cur_chn);
+	writel(con, (info->regs + LSADC_CONFIG));
+
+	/* set timescan */
+	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
+
+	/* clear interrupt */
+	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
+
+	/* enable interrupt */
+	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
+
+	/* start scan */
+	writel(ADC_CON_EN, (info->regs + LSADC_START));
+}
+
+static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info)
+{
+	/* reset the timescan */
+	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
+
+	/* disable interrupt */
+	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
+
+	/* stop scan */
+	writel(ADC_CON_EN, (info->regs + LSADC_STOP));
+}
+
+static const struct hibvt_lsadc_data lsadc_data = {
+	.num_bits = ADC_NUM_BITS,
+	.channels = hibvt_lsadc_iio_channels,
+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
+
+	.clear_irq	= hibvt_lsadc_clear_irq,
+	.start_conv	= hibvt_lsadc_start_conv,
+	.stop_conv = hibvt_lsadc_stop_conv,
+};
+
+static const struct of_device_id hibvt_lsadc_match[] = {
+	{
+		.compatible = "hisilicon,hibvt-lsadc",
+		.data = &lsadc_data,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
+
+/**
+ * Reset LSADC Controller.
+ */
+static void hibvt_lsadc_reset_controller(struct reset_control *reset)
+{
+	reset_control_assert(reset);
+	usleep_range(10, 20);
+	reset_control_deassert(reset);
+}
+
+static int hibvt_lsadc_probe(struct platform_device *pdev)
+{
+	struct hibvt_lsadc *info = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev = NULL;
+	struct resource	*mem;
+	const struct of_device_id *match;
+	int ret;
+	int irq;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+	if (!indio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+	info = iio_priv(indio_dev);
+
+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
+	info->data = match->data;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(info->regs))
+		return PTR_ERR(info->regs);
+
+	/*
+	 * The reset should be an optional property, as it should work
+	 * with old devicetrees as well
+	 */
+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
+	if (IS_ERR(info->reset)) {
+		ret = PTR_ERR(info->reset);
+		if (ret != -ENOENT)
+			return ret;
+
+		dev_dbg(&pdev->dev, "no reset control found\n");
+		info->reset = NULL;
+	}
+
+	init_completion(&info->completion);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
+			       0, dev_name(&pdev->dev), info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
+		return ret;
+	}
+
+	if (info->reset)
+		hibvt_lsadc_reset_controller(info->reset);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	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 = &hibvt_lsadc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	indio_dev->channels = info->data->channels;
+	indio_dev->num_channels = info->data->num_channels;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed register iio device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hibvt_lsadc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	iio_device_unregister(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver hibvt_lsadc_driver = {
+	.probe		= hibvt_lsadc_probe,
+	.remove		= hibvt_lsadc_remove,
+	.driver		= {
+		.name	= "hibvt-lsadc",
+		.of_match_table = hibvt_lsadc_match,
+	},
+};
+
+module_platform_driver(hibvt_lsadc_driver);
+
+MODULE_AUTHOR("Allen Liu <liurenzhong@hisilicon.com>");
+MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.4

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

* [PATCH] adc: add adc driver for Hisilicon BVT SOCs
@ 2016-12-24  1:54 ` Allen Liu
  0 siblings, 0 replies; 19+ messages in thread
From: Allen Liu @ 2016-12-24  1:54 UTC (permalink / raw)
  To: jic23-DgEjT+Ai2ygdnm+yROfE0A, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
  Cc: akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w,
	ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w,
	krzk-DgEjT+Ai2ygdnm+yROfE0A, vilhelm.gray-Re5JQEeQqe8AvxtiuMwx3w,
	ksenija.stanojevic-Re5JQEeQqe8AvxtiuMwx3w,
	zhiyong.tao-NuS5LvNUpcJWk0Htik3J/w,
	daniel.baluta-ral2JQCrhuEAvxtiuMwx3w,
	leonard.crestez-ral2JQCrhuEAvxtiuMwx3w,
	ray.jui-dY08KVG/lbpWk0Htik3J/w,
	raveendra.padasalagi-dY08KVG/lbpWk0Htik3J/w,
	mranostay-Re5JQEeQqe8AvxtiuMwx3w,
	amsfield22-Re5JQEeQqe8AvxtiuMwx3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	xuejiancheng-C8/M+/jPZTeaMJb+Lgu22Q,
	kevin.lixu-C8/M+/jPZTeaMJb+Lgu22Q,
	liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q

Add ADC driver for the ADC controller found on HiSilicon BVT SOCs, like Hi3516CV300, etc.
The ADC controller is primarily in charge of detecting voltage.

Reviewed-by: Jiancheng Xue <xuejiancheng-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
Signed-off-by: Allen Liu <liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
---
 .../devicetree/bindings/iio/adc/hibvt-lsadc.txt    |  26 ++
 drivers/iio/adc/Kconfig                            |  10 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/hibvt_lsadc.c                      | 344 +++++++++++++++++++++
 4 files changed, 381 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
 create mode 100644 drivers/iio/adc/hibvt_lsadc.c

diff --git a/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
new file mode 100644
index 0000000..63de46e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/hibvt-lsadc.txt
@@ -0,0 +1,26 @@
+Hisilicon BVT Low Speed (LS) A/D Converter bindings
+
+Required properties:
+- compatible: should be "hisilicon,<name>-lsadc"
+   - "hisilicon,hibvt-lsadc": for hi3516cv300
+
+- reg: physical base address of the controller and length of memory mapped
+       region.
+- interrupts: The interrupt number to the cpu. The interrupt specifier format
+              depends on the interrupt controller.
+- #io-channel-cells: Should be 1, see ../iio-bindings.txt
+
+Optional properties:
+- resets: Must contain an entry for each entry in reset-names if need support
+	  this option. See ../reset/reset.txt for details.
+- reset-names: Must include the name "saradc-apb".
+
+Example:
+	lsadc: hibvt-lsadc@120e0000 {
+			compatible = "hisilicon,hibvt-lsadc";
+			reg = <0x120e0000 0x1000>;
+			interrupts = <19>;
+			resets = <&crg 0x7c 3>;
+			reset-names = "lsadc-crg";
+			status = "disabled";
+	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 99c0514..0443f51 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -225,6 +225,16 @@ config HI8435
 	  This driver can also be built as a module. If so, the module will be
 	  called hi8435.
 
+config HIBVT_LSADC
+	tristate "HIBVT LSADC driver"
+	depends on ARCH_HISI || COMPILE_TEST
+	help
+	  Say yes here to build support for the LSADC found in SoCs from
+	  hisilicon BVT chip.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hibvt_lsadc.
+
 config INA2XX_ADC
 	tristate "Texas Instruments INA2xx Power Monitors IIO driver"
 	depends on I2C && !SENSORS_INA2XX
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..6554d92 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
 obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_HI8435) += hi8435.o
+obj-$(CONFIG_HIBVT_LSADC) += hibvt_lsadc.o
 obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
 obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
diff --git a/drivers/iio/adc/hibvt_lsadc.c b/drivers/iio/adc/hibvt_lsadc.c
new file mode 100644
index 0000000..a20afe8
--- /dev/null
+++ b/drivers/iio/adc/hibvt_lsadc.c
@@ -0,0 +1,344 @@
+/*
+ * Hisilicon BVT Low Speed (LS) A/D Converter
+ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+
+/* hisilicon bvt adc registers definitions */
+#define LSADC_CONFIG		0x00
+#define CONFIG_DEGLITCH		BIT(17)
+#define CONFIG_RESET		BIT(15)
+#define CONFIG_POWERDOWN	BIT(14)
+#define CONFIG_MODE			BIT(13)
+#define CONFIG_CHC_VALID	BIT(10)
+#define CONFIG_CHB_VALID	BIT(9)
+#define CONFIG_CHA_VALID	BIT(8)
+
+#define LSADC_TIMESCAN		0x08
+#define LSADC_INTEN			0x10
+#define LSADC_INTSTATUS		0x14
+#define LSADC_INTCLR		0x18
+#define LSADC_START			0x1C
+#define LSADC_STOP			0x20
+#define LSADC_ACTBIT		0x24
+#define LSADC_CHNDATA		0x2C
+
+#define ADC_CON_EN			(1u << 0)
+#define ADC_CON_DEN			(0u << 0)
+
+#define ADC_NUM_BITS		10
+
+/* fix clk:3000000, default tscan set 10ms */
+#define DEF_ADC_TSCAN_MS	(10*3000)
+
+#define LSADC_CHN_MASK		0x7
+
+#define LSADC_TIMEOUT		msecs_to_jiffies(100)
+
+/* default voltage scale for every channel <mv> */
+static int g_voltage[] = {
+	3300, 3300, 3300
+};
+
+struct hibvt_lsadc {
+	void __iomem		*regs;
+	struct completion	completion;
+	struct reset_control	*reset;
+	const struct hibvt_lsadc_data	*data;
+	unsigned int		cur_chn;
+	unsigned int		value;
+};
+
+struct hibvt_lsadc_data {
+	int				num_bits;
+	const struct iio_chan_spec	*channels;
+	int				num_channels;
+
+	void (*clear_irq)(struct hibvt_lsadc *info, int mask);
+	void (*start_conv)(struct hibvt_lsadc *info);
+	void (*stop_conv)(struct hibvt_lsadc *info);
+};
+
+static int hibvt_lsadc_read_raw(struct iio_dev *indio_dev,
+				    struct iio_chan_spec const *chan,
+				    int *val, int *val2, long mask)
+{
+	struct hibvt_lsadc *info = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+
+		reinit_completion(&info->completion);
+
+		/* Select the channel to be used */
+		info->cur_chn = chan->channel;
+
+		if (info->data->start_conv)
+			info->data->start_conv(info);
+
+		if (!wait_for_completion_timeout(&info->completion,
+							LSADC_TIMEOUT)) {
+			if (info->data->stop_conv)
+				info->data->stop_conv(info);
+			mutex_unlock(&indio_dev->mlock);
+			return -ETIMEDOUT;
+		}
+
+		*val = info->value;
+		mutex_unlock(&indio_dev->mlock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = g_voltage[chan->channel];
+		*val2 = info->data->num_bits;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t hibvt_lsadc_isr(int irq, void *dev_id)
+{
+	struct hibvt_lsadc *info = (struct hibvt_lsadc *)dev_id;
+	int mask;
+
+	mask = readl(info->regs + LSADC_INTSTATUS);
+	mask &= LSADC_CHN_MASK;
+
+	/* Clear irq */
+	if (info->data->clear_irq)
+		info->data->clear_irq(info, mask);
+
+	/* Read value */
+	info->value = readl(info->regs + LSADC_CHNDATA + (info->cur_chn << 2));
+	info->value &= GENMASK(info->data->num_bits - 1, 0);
+
+	/* stop adc */
+	if (info->data->stop_conv)
+		info->data->stop_conv(info);
+
+	complete(&info->completion);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_info hibvt_lsadc_iio_info = {
+	.read_raw = hibvt_lsadc_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+#define ADC_CHANNEL(_index, _id) {      \
+	.type = IIO_VOLTAGE,                \
+	.indexed = 1,						\
+	.channel = _index,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+			BIT(IIO_CHAN_INFO_SCALE),   \
+	.datasheet_name = _id,              \
+}
+
+static const struct iio_chan_spec hibvt_lsadc_iio_channels[] = {
+	ADC_CHANNEL(0, "adc0"),
+	ADC_CHANNEL(1, "adc1"),
+	ADC_CHANNEL(2, "adc2"),
+};
+
+static void hibvt_lsadc_clear_irq(struct hibvt_lsadc *info, int mask)
+{
+	writel(mask, info->regs + LSADC_INTCLR);
+}
+
+static void hibvt_lsadc_start_conv(struct hibvt_lsadc *info)
+{
+	unsigned int con;
+
+	/* set number bit */
+	con = GENMASK(info->data->num_bits - 1, 0);
+	writel(con, (info->regs + LSADC_ACTBIT));
+
+    /* config */
+	con = readl(info->regs + LSADC_CONFIG);
+	con &= ~CONFIG_RESET;
+	con |= (CONFIG_POWERDOWN | CONFIG_DEGLITCH | CONFIG_MODE);
+	con &= ~(CONFIG_CHA_VALID | CONFIG_CHB_VALID | CONFIG_CHC_VALID);
+	con |= (CONFIG_CHA_VALID << info->cur_chn);
+	writel(con, (info->regs + LSADC_CONFIG));
+
+	/* set timescan */
+	writel(DEF_ADC_TSCAN_MS, (info->regs + LSADC_TIMESCAN));
+
+	/* clear interrupt */
+	writel(LSADC_CHN_MASK, info->regs + LSADC_INTCLR);
+
+	/* enable interrupt */
+	writel(ADC_CON_EN, (info->regs + LSADC_INTEN));
+
+	/* start scan */
+	writel(ADC_CON_EN, (info->regs + LSADC_START));
+}
+
+static void hibvt_lsadc_stop_conv(struct hibvt_lsadc *info)
+{
+	/* reset the timescan */
+	writel(ADC_CON_DEN, (info->regs + LSADC_TIMESCAN));
+
+	/* disable interrupt */
+	writel(ADC_CON_DEN, (info->regs + LSADC_INTEN));
+
+	/* stop scan */
+	writel(ADC_CON_EN, (info->regs + LSADC_STOP));
+}
+
+static const struct hibvt_lsadc_data lsadc_data = {
+	.num_bits = ADC_NUM_BITS,
+	.channels = hibvt_lsadc_iio_channels,
+	.num_channels = ARRAY_SIZE(hibvt_lsadc_iio_channels),
+
+	.clear_irq	= hibvt_lsadc_clear_irq,
+	.start_conv	= hibvt_lsadc_start_conv,
+	.stop_conv = hibvt_lsadc_stop_conv,
+};
+
+static const struct of_device_id hibvt_lsadc_match[] = {
+	{
+		.compatible = "hisilicon,hibvt-lsadc",
+		.data = &lsadc_data,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hibvt_lsadc_match);
+
+/**
+ * Reset LSADC Controller.
+ */
+static void hibvt_lsadc_reset_controller(struct reset_control *reset)
+{
+	reset_control_assert(reset);
+	usleep_range(10, 20);
+	reset_control_deassert(reset);
+}
+
+static int hibvt_lsadc_probe(struct platform_device *pdev)
+{
+	struct hibvt_lsadc *info = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev = NULL;
+	struct resource	*mem;
+	const struct of_device_id *match;
+	int ret;
+	int irq;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+	if (!indio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+	info = iio_priv(indio_dev);
+
+	match = of_match_device(hibvt_lsadc_match, &pdev->dev);
+	info->data = match->data;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(info->regs))
+		return PTR_ERR(info->regs);
+
+	/*
+	 * The reset should be an optional property, as it should work
+	 * with old devicetrees as well
+	 */
+	info->reset = devm_reset_control_get(&pdev->dev, "lsadc-crg");
+	if (IS_ERR(info->reset)) {
+		ret = PTR_ERR(info->reset);
+		if (ret != -ENOENT)
+			return ret;
+
+		dev_dbg(&pdev->dev, "no reset control found\n");
+		info->reset = NULL;
+	}
+
+	init_completion(&info->completion);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, hibvt_lsadc_isr,
+			       0, dev_name(&pdev->dev), info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
+		return ret;
+	}
+
+	if (info->reset)
+		hibvt_lsadc_reset_controller(info->reset);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	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 = &hibvt_lsadc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	indio_dev->channels = info->data->channels;
+	indio_dev->num_channels = info->data->num_channels;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed register iio device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int hibvt_lsadc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+	iio_device_unregister(indio_dev);
+
+	return 0;
+}
+
+static struct platform_driver hibvt_lsadc_driver = {
+	.probe		= hibvt_lsadc_probe,
+	.remove		= hibvt_lsadc_remove,
+	.driver		= {
+		.name	= "hibvt-lsadc",
+		.of_match_table = hibvt_lsadc_match,
+	},
+};
+
+module_platform_driver(hibvt_lsadc_driver);
+
+MODULE_AUTHOR("Allen Liu <liurenzhong-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>");
+MODULE_DESCRIPTION("hisilicon BVT LSADC driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

end of thread, other threads:[~2017-02-06 18:54 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-07 10:16 [PATCH] adc: add adc driver for Hisilicon BVT SOCs Allen Liu
2017-01-07 10:16 ` Allen Liu
2017-01-07 17:51 ` Jonathan Cameron
2017-01-10  5:35   ` Rob Herring
2017-02-06 12:19   ` 答复: " liurenzhong
2017-02-06 12:19     ` liurenzhong
2017-02-06 18:50     ` Jonathan Cameron
2017-02-06 18:50       ` Jonathan Cameron
2017-02-06 18:50       ` Jonathan Cameron
  -- strict thread matches above, loose matches on Subject: below --
2016-12-24  1:54 Allen Liu
2016-12-24  1:54 ` Allen Liu
2016-12-24  2:31 ` Jiancheng Xue
2016-12-24  2:31   ` Jiancheng Xue
2016-12-24 11:46 ` Jonathan Cameron
2016-12-24 11:46   ` Jonathan Cameron
2016-12-26 11:05   ` liurenzhong
2016-12-26 11:05     ` liurenzhong
2016-12-26 11:05     ` liurenzhong
2017-01-03 15:30 ` Rob Herring

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.