All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH linux dev-4.13 v1 0/2] ADC: npcm: add NPCM7xx ADC iio driver
@ 2017-12-27  8:30 Tomer Maimon
  2017-12-27  8:30 ` [PATCH linux dev-4.13 v1 1/2] dt-binding: iio: document NPCM7xx ADC DT bindings Tomer Maimon
  2017-12-27  8:30 ` [PATCH linux dev-4.13 v1 2/2] ADC: npcm: add NPCM7xx ADC driver Tomer Maimon
  0 siblings, 2 replies; 3+ messages in thread
From: Tomer Maimon @ 2017-12-27  8:30 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Tomer Maimon

This patch set adds Analog-to-digital converter (ADC) support 
for the Nuvoton NPCM7xx Baseboard Management Controller (BMC).

The NPCM7xx ADC is a 10-bit converter for eight channel
inputs, the ADC module includes an eight-to-one multiplexer.

Tomer Maimon (2):
  dt-binding: iio: document NPCM7xx ADC DT bindings
  ADC: npcm: add NPCM7xx ADC  driver

 .../devicetree/bindings/iio/adc/npcm7xx-adc.txt    |  23 ++
 drivers/iio/adc/Kconfig                            |   9 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/npcm7xx-adc.c                      | 352 +++++++++++++++++++++
 4 files changed, 385 insertions(+)
 create mode 100755 Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt
 create mode 100644 drivers/iio/adc/npcm7xx-adc.c

-- 
2.14.1

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

* [PATCH linux dev-4.13 v1 1/2] dt-binding: iio: document NPCM7xx ADC DT bindings
  2017-12-27  8:30 [PATCH linux dev-4.13 v1 0/2] ADC: npcm: add NPCM7xx ADC iio driver Tomer Maimon
@ 2017-12-27  8:30 ` Tomer Maimon
  2017-12-27  8:30 ` [PATCH linux dev-4.13 v1 2/2] ADC: npcm: add NPCM7xx ADC driver Tomer Maimon
  1 sibling, 0 replies; 3+ messages in thread
From: Tomer Maimon @ 2017-12-27  8:30 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Tomer Maimon

Added device tree binding documentation for Nuvoton NPCM7xx ADC.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 .../devicetree/bindings/iio/adc/npcm7xx-adc.txt    | 23 ++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100755 Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt b/Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt
new file mode 100755
index 000000000000..c4eb433563b6
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/npcm7xx-adc.txt
@@ -0,0 +1,23 @@
+Nuvoton NPCM7XX Analog to Digital Converter (ADC)
+
+The NPCM7XX ADC is a 10-bit converter for eight channel inputs,
+The ADC module includes an eight-to-one multiplexer.
+
+Required properties:
+- compatible      : "nuvoton,npcm750-adc" for Poleg NPCM750.
+- reg             : Offset and length of the register set for the device.
+
+Required clocking property, have to be one of:
+- clocks          : phandle of timer reference clock.
+- clock-names	  : Must contain "clk_adc", matching entry in the clocks property.
+- vref			  : ADC Reference voltage, defualt 2048.
+
+Example:
+
+adc: adc@f000c000 {
+	compatible = "nuvoton,npcm750-adc";
+	reg = <0xf000c000 0x1000>;
+	clocks = <&clk NPCM7XX_CLK_ADC>;
+	clock-names = "clk_adc";
+	vref = <2048>;
+};
-- 
2.14.1

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

* [PATCH linux dev-4.13 v1 2/2] ADC: npcm: add NPCM7xx ADC  driver
  2017-12-27  8:30 [PATCH linux dev-4.13 v1 0/2] ADC: npcm: add NPCM7xx ADC iio driver Tomer Maimon
  2017-12-27  8:30 ` [PATCH linux dev-4.13 v1 1/2] dt-binding: iio: document NPCM7xx ADC DT bindings Tomer Maimon
@ 2017-12-27  8:30 ` Tomer Maimon
  1 sibling, 0 replies; 3+ messages in thread
From: Tomer Maimon @ 2017-12-27  8:30 UTC (permalink / raw)
  To: openbmc; +Cc: Joel Stanley, Tomer Maimon

Add Nuvoton BMC NPCM7xx Analog-to-digital
converter (ADC) driver

The NPCM7xx ADC is a 10-bit converter for eight channel
inputs, the ADC module includes an eight-to-one multiplexer.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 drivers/iio/adc/Kconfig       |   9 ++
 drivers/iio/adc/Makefile      |   1 +
 drivers/iio/adc/npcm7xx-adc.c | 352 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 362 insertions(+)
 create mode 100644 drivers/iio/adc/npcm7xx-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 614fa41559b1..77705fde21ca 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -857,4 +857,13 @@ config XILINX_XADC
 	  The driver can also be build as a module. If so, the module will be called
 	  xilinx-xadc.
 
+config NPCM7XX_ADC
+	tristate "NPCM7XX ADC driver"
+	depends on ARCH_NPCM7XX || COMPILE_TEST
+	depends on HAS_IOMEM
+	help
+	  Say yes here to build support for NPCM7XX ADC.
+	  This driver can also be built as a module. If so, the module will be
+	  called npcm750_adc.
+
 endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index b546736a5541..871074d7b0cd 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
 xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
 obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
+obj-$(CONFIG_NPCM7XX_ADC) += npcm7xx-adc.o
diff --git a/drivers/iio/adc/npcm7xx-adc.c b/drivers/iio/adc/npcm7xx-adc.c
new file mode 100644
index 000000000000..734e350b5b7d
--- /dev/null
+++ b/drivers/iio/adc/npcm7xx-adc.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2014-2017 Nuvoton Technology corporation.
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/clk.h>
+
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+static struct regmap *rst_regmap;
+
+#define  IPSRST1_OFFSET 0x020
+
+struct npcm7xx_adc {
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *adc_clk;
+	u32 vref;
+	u32 adc_clk_rate;
+	u32 ADCReading;
+	u8  ADCChannelNum;
+};
+
+/* ADC registers */
+#define NPCM7XX_ADCCON	 0x00
+#define NPCM7XX_ADCDATA	 0x04
+
+/* ADCCON Register Bits */
+#define NPCM7XX_ADCCON_ADC_INT_EN	BIT(21)
+#define NPCM7XX_ADCCON_REFSEL		BIT(19)
+#define NPCM7XX_ADCCON_ADC_INT		BIT(18)
+#define NPCM7XX_ADCCON_ADC_EN		BIT(17)
+#define NPCM7XX_ADCCON_ADC_RST		BIT(16)
+#define NPCM7XX_ADCCON_ADC_CONV		BIT(13)
+
+#define NPCM7XX_ADCCON_ADCMUX(x)		(((x) & 0x0F)<<24)
+#define NPCM7XX_ADCCON_ADC_DIV(x)		(((x) & 0xFF)<<24)
+#define NPCM7XX_ADCCON_ADC_DATA_MASK(x)		((x) & 0x3FF)
+#define NPCM7XX_ADCCON_MUXMASK			(0x0F<<24)
+
+/* ADC General Defintion */
+#define NPCM7XX_ADC_INPUT_CLK_DIV		0
+#define NPCM7XX_ADC_CONVERT_MAX_RETRY_CNT	1000
+
+#define NPCM7XX_ADC_MAX_CHNL_NUM	8
+
+#define NPCM7XX_ADC_CHNL0_ADCI0		0
+#define NPCM7XX_ADC_CHNL1_ADCI1		1
+#define NPCM7XX_ADC_CHNL2_ADCI2		2
+#define NPCM7XX_ADC_CHNL3_ADCI3		3
+#define NPCM7XX_ADC_CHNL4_ADCI4		4
+#define NPCM7XX_ADC_CHNL5_ADCI5		5
+#define NPCM7XX_ADC_CHNL6_ADCI6		6
+#define NPCM7XX_ADC_CHNL7_ADCI7		7
+
+#define ADC_MAX_CLOCK 12500000
+#define VREF_MVOLT 2048		//vref = 2.000v
+
+#define NPCM7XX_ADC_CHAN(_idx) {			\
+	.type = IIO_VOLTAGE,				\
+	.indexed = 1,					\
+	.channel = (_idx),				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+}
+
+static const struct iio_chan_spec npcm7xx_adc_iio_channels[] = {
+	NPCM7XX_ADC_CHAN(0),
+	NPCM7XX_ADC_CHAN(1),
+	NPCM7XX_ADC_CHAN(2),
+	NPCM7XX_ADC_CHAN(3),
+	NPCM7XX_ADC_CHAN(4),
+	NPCM7XX_ADC_CHAN(5),
+	NPCM7XX_ADC_CHAN(6),
+	NPCM7XX_ADC_CHAN(7),
+};
+
+//#define ADC_DEBUG
+
+#ifdef ADC_DEBUG
+static char *S_ADCChnlString[] = {
+	"ADCI0", "ADCI1", "ADCI2", "ADCI3", "ADCI4", "ADCI5", "ADCI6", "ADCI7"
+};
+#define PDEBUG(fmt, args...) pr_info("aess_adcdrv %s() " fmt, __func__, ##args)
+#else
+#define PDEBUG(fmt, args...)
+#endif
+#define PERROR(fmt, args...) pr_err("aess_adcdrv %s(): " fmt, __func__, ##args)
+
+static int adcsensor_read(struct npcm7xx_adc *info)
+{
+	u8  u8ChannelNum = info->ADCChannelNum;
+	u32 regtemp = 0;
+	int cnt = 0;
+
+	/* Select ADC channal */
+	regtemp = ioread32(info->regs + NPCM7XX_ADCCON);
+	regtemp &= ~NPCM7XX_ADCCON_MUXMASK;
+
+	iowrite32((u32) (regtemp | NPCM7XX_ADCCON_ADCMUX(u8ChannelNum) |
+			 NPCM7XX_ADCCON_ADC_EN | NPCM7XX_ADCCON_REFSEL),
+		  info->regs + NPCM7XX_ADCCON);
+
+	/* Activate convert the ADC input */
+	regtemp = ioread32(info->regs + NPCM7XX_ADCCON);
+	iowrite32((u32) (regtemp | NPCM7XX_ADCCON_ADC_CONV),
+		  info->regs + NPCM7XX_ADCCON);
+
+	/* Wait value */
+	while (((regtemp = ioread32(info->regs + NPCM7XX_ADCCON)) &
+	       NPCM7XX_ADCCON_ADC_CONV) != 0) {
+		if (cnt < NPCM7XX_ADC_CONVERT_MAX_RETRY_CNT)
+			cnt++;
+		else {
+			PERROR("ADC CONVERT FAIL - Timeout\n");
+			PERROR("NPCM7XX_ADCCON=0x%08X, ADC_MUX=%d u8ChannelNum="
+			       "%d!!\n", regtemp, (regtemp>>24)&0xF,
+			       u8ChannelNum);
+			if (((regtemp>>24) & 0xF) != u8ChannelNum)
+				PERROR("ADC_MUX != u8ChannelNum, I suspect that"
+				       " 2 threads are trying to access this "
+				       "read and it is not protected "
+				       "by mutex\n");
+
+			/* if convertion failed - reset ADC module */
+			regmap_write(rst_regmap, IPSRST1_OFFSET, 0x08000000);
+			msleep(100);
+			regmap_write(rst_regmap, IPSRST1_OFFSET, 0x0);
+			msleep(100);
+			PERROR("RESET ADC Complete\n");
+			return (-EAGAIN);
+		}
+	}
+
+/* When an ADC conversion operation finished, a delay must be added before
+ * the next conversion operation.
+ * The delay depend on the ADC clock:
+ * When ADC clock is 0.5 MHz: delay is 4 us.
+ * When ADC clock is 12.5 MHz: delay is 160 ns.
+ *
+ * In the current driver the ADC clock is 12.5MHz, so delay is not needed.
+ * (already the R/W register take more than 160ns)
+ * If the ADC clock will be lower than 12.5MHz please add delay according
+ * the details above
+ * udelay(conv_delay);
+ */
+
+	/* finish to convert */
+	info->ADCReading = NPCM7XX_ADCCON_ADC_DATA_MASK
+		(ioread32(info->regs + NPCM7XX_ADCDATA));
+
+	PDEBUG("[%d_%s] ADCReading=%ld [%ldmV]\n",
+	       u8ChannelNum, S_ADCChnlString[u8ChannelNum],
+	       (long int)info->ADCReading,
+	       (long int)(info->ADCReading * info->vref / 1024));
+
+	return 0;
+}
+
+static int npcm7xx_adc_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *chan, int *val, int *val2,
+			long mask)
+{
+	int err_check;
+	struct npcm7xx_adc *info = iio_priv(indio_dev);
+
+	info->ADCChannelNum = chan->channel;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+
+		switch (info->ADCChannelNum) {
+		case NPCM7XX_ADC_CHNL0_ADCI0:
+		case NPCM7XX_ADC_CHNL1_ADCI1:
+		case NPCM7XX_ADC_CHNL2_ADCI2:
+		case NPCM7XX_ADC_CHNL3_ADCI3:
+		case NPCM7XX_ADC_CHNL4_ADCI4:
+		case NPCM7XX_ADC_CHNL5_ADCI5:
+		case NPCM7XX_ADC_CHNL6_ADCI6:
+		case NPCM7XX_ADC_CHNL7_ADCI7:
+			mutex_lock(&indio_dev->mlock);
+			err_check = adcsensor_read(info);
+			PDEBUG("%d = aess_adcsensor_read()\n", err_check);
+			if (err_check) {
+				PERROR("err_check %d\n", err_check);
+				mutex_unlock(&indio_dev->mlock);
+				return err_check;
+			}
+			*val = info->ADCReading;
+			mutex_unlock(&indio_dev->mlock);
+			return IIO_VAL_INT;
+		default:
+			PERROR("aess_adcsensor_ioctl, Unsupport channel number"
+			       " [%d]!\n", info->ADCChannelNum);
+			err_check = -ENODEV;
+			break;
+		}
+		break;
+
+	default:
+		PERROR("aess_adcsensor_ioctl, command error!!!\n");
+		err_check = -EINVAL;
+	}
+
+	/* 0->ok, minus->fail */
+	return err_check;
+}
+
+static const struct iio_info npcm7xx_adc_iio_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &npcm7xx_adc_read_raw,
+};
+
+static const struct of_device_id npcm7xx_adc_match[] = {
+	{ .compatible = "nuvoton,npcm7xx-adc", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, npcm7xx_adc_match);
+
+
+static int npcm7xx_adc_probe(struct platform_device *pdev)
+{
+	struct npcm7xx_adc *info;
+	struct iio_dev *indio_dev;
+	struct resource *mem;
+	struct device *dev = &pdev->dev;
+	int ret;
+	u32 regtemp = 0;
+
+	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);
+	info->dev = &pdev->dev;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	info->regs = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(info->regs)) {
+		ret = PTR_ERR(info->regs);
+		dev_err(&pdev->dev, "Failed to remap adc memory, err = %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "vref", &info->vref);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed getting reference voltage, Assuming"
+				   " reference voltage 2V(2048)\n");
+		info->vref = VREF_MVOLT;
+		ret = 0;
+	}
+
+	info->adc_clk = devm_clk_get(&pdev->dev, "clk_adc");
+	if (IS_ERR(info->adc_clk)) {
+		dev_err(&pdev->dev, "ADC clock failed: can't read clk. "
+				    "Assuming ADC clock Rate 12.5MHz\n");
+		info->adc_clk_rate = ADC_MAX_CLOCK;
+	} else {
+		/* calculate ADC clock divider */
+		regtemp = ioread32(info->regs + NPCM7XX_ADCCON);
+		regtemp = regtemp >> 1;
+		regtemp &= 0xff;
+
+		info->adc_clk_rate = clk_get_rate(info->adc_clk) /
+			((regtemp+1)*2);
+	}
+
+	rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst");
+	if (IS_ERR(rst_regmap)) {
+		pr_err("%s: failed to find nuvoton,npcm750-rst\n", __func__);
+		return IS_ERR(rst_regmap);
+	}
+
+	pr_info("ADC clock Rate %d\n", info->adc_clk_rate);
+
+	/** Enable the ADC Module **/
+	iowrite32((u32) NPCM7XX_ADCCON_ADC_EN, info->regs + NPCM7XX_ADCCON);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &npcm7xx_adc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = npcm7xx_adc_iio_channels;
+	indio_dev->num_channels = ARRAY_SIZE(npcm7xx_adc_iio_channels);
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't register the device.\n");
+		clk_disable_unprepare(info->adc_clk);
+		return ret;
+	}
+
+	pr_info("NPCM7XX ADC driver probed\n");
+
+	return 0;
+}
+
+static int npcm7xx_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct npcm7xx_adc *info = iio_priv(indio_dev);
+	u32 regtemp = 0;
+
+	regtemp = ioread32(info->regs + NPCM7XX_ADCCON);
+
+	/* Disable the ADC Module */
+	iowrite32((u32) (regtemp & ~NPCM7XX_ADCCON_ADC_EN),
+		  info->regs + NPCM7XX_ADCCON);
+
+	iio_device_unregister(indio_dev);
+
+	pr_info("NPCM7XX ADC driver removed\n");
+
+	return 0;
+}
+
+static struct platform_driver npcm7xx_adc_driver = {
+	.probe		= npcm7xx_adc_probe,
+	.remove		= npcm7xx_adc_remove,
+	.driver		= {
+		.name	= "npcm7xx_adc",
+		.of_match_table = npcm7xx_adc_match,
+	},
+};
+
+module_platform_driver(npcm7xx_adc_driver);
+
+MODULE_DESCRIPTION("NPCM7XX ADC Sensor Driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.14.1

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

end of thread, other threads:[~2017-12-27  8:30 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-12-27  8:30 [PATCH linux dev-4.13 v1 0/2] ADC: npcm: add NPCM7xx ADC iio driver Tomer Maimon
2017-12-27  8:30 ` [PATCH linux dev-4.13 v1 1/2] dt-binding: iio: document NPCM7xx ADC DT bindings Tomer Maimon
2017-12-27  8:30 ` [PATCH linux dev-4.13 v1 2/2] ADC: npcm: add NPCM7xx ADC driver Tomer Maimon

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.