* [PATCH 1/4] iio: adc: add NXP LPC18xx ADC driver
2016-02-29 19:05 [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Joachim Eastwood
@ 2016-02-29 19:05 ` Joachim Eastwood
2016-02-29 19:42 ` Peter Meerwald-Stadler
2016-02-29 19:05 ` [PATCH 2/4] dt: document NXP LPC1850 ADC driver bindings Joachim Eastwood
` (4 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Joachim Eastwood @ 2016-02-29 19:05 UTC (permalink / raw)
To: jic23; +Cc: Joachim Eastwood, knaack.h, lars, pmeerw, linux-iio, robh+dt
Add base support for the 10-bit SAR ADC peripheral found
on NXP LPC18xx/43xx SoCs.
This is a minimal driver that does not support burst mode,
interrupts, DMA or hardware triggers.
User manual with register description can be found on:
LPC18xx: www.nxp.com/documents/user_manual/UM10430.pdf
LPC43xx: www.nxp.com/documents/user_manual/UM10503.pdf
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/iio/adc/Kconfig | 10 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/lpc18xx_adc.c | 226 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 237 insertions(+)
create mode 100644 drivers/iio/adc/lpc18xx_adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 932de1f9d1e7..85994e5889ec 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -234,6 +234,16 @@ config LP8788_ADC
To compile this driver as a module, choose M here: the module will be
called lp8788_adc.
+config LPC18XX_ADC
+ tristate "NXP LPC18xx ADC driver"
+ depends on ARCH_LPC18XX || COMPILE_TEST
+ depends on OF && HAS_IOMEM
+ help
+ Say yes here to build support for NXP LPC18XX ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called lpc18xx_adc.
+
config MAX1027
tristate "Maxim max1027 ADC driver"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index b1aa456e6af3..1a4ac4590857 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_HI8435) += hi8435.o
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
+obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MCP320X) += mcp320x.o
diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c
new file mode 100644
index 000000000000..f78bd8ddbff3
--- /dev/null
+++ b/drivers/iio/adc/lpc18xx_adc.c
@@ -0,0 +1,226 @@
+/*
+ * IIO ADC driver for NXP LPC18xx ADC
+ *
+ * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * UNSUPPORTED hardware features:
+ * - Hardware triggers
+ * - Burst mode
+ * - Interrupts
+ * - DMA
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+/* LPC18XX ADC registers and bits */
+#define LPC18XX_ADC_CR 0x000
+#define LPC18XX_ADC_CR_CLKDIV_SHIFT 8
+#define LPC18XX_ADC_CR_PDN BIT(21)
+#define LPC18XX_ADC_CR_START_NOW (0x1 << 24)
+#define LPC18XX_ADC_GDR 0x004
+
+/* Data registers bits */
+#define LPC18XX_ADC_SAMPLE_SHIFT 6
+#define LPC18XX_ADC_SAMPLE_MASK 0x3ff
+#define LPC18XX_ADC_CONV_DONE BIT(31)
+
+/* Clock should be 4.5 MHz or less */
+#define LPC18XX_ADC_CLK_TARGET 4500000
+
+struct lpc18xx_adc {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *clk;
+ struct regulator *vref;
+ u32 cr_reg;
+};
+
+#define LPC18XX_ADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _idx, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = {
+ LPC18XX_ADC_CHAN(0),
+ LPC18XX_ADC_CHAN(1),
+ LPC18XX_ADC_CHAN(2),
+ LPC18XX_ADC_CHAN(3),
+ LPC18XX_ADC_CHAN(4),
+ LPC18XX_ADC_CHAN(5),
+ LPC18XX_ADC_CHAN(6),
+ LPC18XX_ADC_CHAN(7),
+};
+
+static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch)
+{
+ int ret;
+ u32 reg;
+
+ reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW;
+ writel(reg, adc->base + LPC18XX_ADC_CR);
+
+ ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg,
+ reg & LPC18XX_ADC_CONV_DONE, 3, 9);
+ if (ret) {
+ dev_warn(adc->dev, "adc read timed out\n");
+ return ret;
+ }
+
+ return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK;
+}
+
+static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lpc18xx_adc *adc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = lpc18xx_adc_read_chan(adc, chan->channel);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(adc->vref) / 1000;
+ *val2 = 10;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info lpc18xx_adc_info = {
+ .read_raw = lpc18xx_adc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int lpc18xx_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct lpc18xx_adc *adc;
+ struct resource *res;
+ unsigned int clkdiv;
+ unsigned long rate;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, indio_dev);
+ adc = iio_priv(indio_dev);
+ adc->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ adc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(adc->base))
+ return PTR_ERR(adc->base);
+
+ adc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(adc->clk)) {
+ dev_err(&pdev->dev, "error getting clock\n");
+ return PTR_ERR(adc->clk);
+ }
+
+ rate = clk_get_rate(adc->clk);
+ clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET);
+
+ adc->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(adc->vref)) {
+ dev_err(&pdev->dev, "error get regulator\n");
+ return PTR_ERR(adc->vref);
+ }
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &lpc18xx_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = lpc18xx_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels);
+
+ ret = regulator_enable(adc->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable regulator\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable clock.\n");
+ goto dis_reg;
+ }
+
+ adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) |
+ LPC18XX_ADC_CR_PDN;
+ writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ goto dis_clk;
+ }
+
+ return 0;
+
+dis_clk:
+ writel(0, adc->base + LPC18XX_ADC_CR);
+ clk_disable_unprepare(adc->clk);
+dis_reg:
+ regulator_disable(adc->vref);
+ return ret;
+}
+
+static int lpc18xx_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct lpc18xx_adc *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ writel(0, adc->base + LPC18XX_ADC_CR);
+ clk_disable_unprepare(adc->clk);
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static const struct of_device_id lpc18xx_adc_match[] = {
+ { .compatible = "nxp,lpc1850-adc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_adc_match);
+
+static struct platform_driver lpc18xx_adc_driver = {
+ .probe = lpc18xx_adc_probe,
+ .remove = lpc18xx_adc_remove,
+ .driver = {
+ .name = "lpc18xx-adc",
+ .of_match_table = lpc18xx_adc_match,
+ },
+};
+module_platform_driver(lpc18xx_adc_driver);
+
+MODULE_DESCRIPTION("LPC18xx ADC driver");
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_LICENSE("GPL v2");
--
1.8.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] iio: adc: add NXP LPC18xx ADC driver
2016-02-29 19:05 ` [PATCH 1/4] iio: adc: add NXP LPC18xx ADC driver Joachim Eastwood
@ 2016-02-29 19:42 ` Peter Meerwald-Stadler
2016-02-29 20:45 ` Joachim Eastwood
0 siblings, 1 reply; 10+ messages in thread
From: Peter Meerwald-Stadler @ 2016-02-29 19:42 UTC (permalink / raw)
To: Joachim Eastwood; +Cc: jic23, knaack.h, lars, linux-iio, robh+dt
> Add base support for the 10-bit SAR ADC peripheral found
> on NXP LPC18xx/43xx SoCs.
nitpicking below
> This is a minimal driver that does not support burst mode,
> interrupts, DMA or hardware triggers.
>
> User manual with register description can be found on:
> LPC18xx: www.nxp.com/documents/user_manual/UM10430.pdf
> LPC43xx: www.nxp.com/documents/user_manual/UM10503.pdf
>
> Signed-off-by: Joachim Eastwood <manabian@gmail.com>
> ---
> drivers/iio/adc/Kconfig | 10 ++
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/lpc18xx_adc.c | 226 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 237 insertions(+)
> create mode 100644 drivers/iio/adc/lpc18xx_adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 932de1f9d1e7..85994e5889ec 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -234,6 +234,16 @@ config LP8788_ADC
> To compile this driver as a module, choose M here: the module will be
> called lp8788_adc.
>
> +config LPC18XX_ADC
> + tristate "NXP LPC18xx ADC driver"
> + depends on ARCH_LPC18XX || COMPILE_TEST
> + depends on OF && HAS_IOMEM
> + help
> + Say yes here to build support for NXP LPC18XX ADC.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called lpc18xx_adc.
> +
> config MAX1027
> tristate "Maxim max1027 ADC driver"
> depends on SPI
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index b1aa456e6af3..1a4ac4590857 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_HI8435) += hi8435.o
> obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
> obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
> obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
> +obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
> obj-$(CONFIG_MAX1027) += max1027.o
> obj-$(CONFIG_MAX1363) += max1363.o
> obj-$(CONFIG_MCP320X) += mcp320x.o
> diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c
> new file mode 100644
> index 000000000000..f78bd8ddbff3
> --- /dev/null
> +++ b/drivers/iio/adc/lpc18xx_adc.c
> @@ -0,0 +1,226 @@
> +/*
> + * IIO ADC driver for NXP LPC18xx ADC
> + *
> + * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * UNSUPPORTED hardware features:
> + * - Hardware triggers
> + * - Burst mode
> + * - Interrupts
> + * - DMA
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/driver.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +
> +/* LPC18XX ADC registers and bits */
> +#define LPC18XX_ADC_CR 0x000
> +#define LPC18XX_ADC_CR_CLKDIV_SHIFT 8
> +#define LPC18XX_ADC_CR_PDN BIT(21)
> +#define LPC18XX_ADC_CR_START_NOW (0x1 << 24)
> +#define LPC18XX_ADC_GDR 0x004
> +
> +/* Data registers bits */
'Data register bits' maybe
> +#define LPC18XX_ADC_SAMPLE_SHIFT 6
> +#define LPC18XX_ADC_SAMPLE_MASK 0x3ff
> +#define LPC18XX_ADC_CONV_DONE BIT(31)
> +
> +/* Clock should be 4.5 MHz or less */
> +#define LPC18XX_ADC_CLK_TARGET 4500000
> +
> +struct lpc18xx_adc {
> + void __iomem *base;
> + struct device *dev;
> + struct clk *clk;
> + struct regulator *vref;
> + u32 cr_reg;
> +};
> +
> +#define LPC18XX_ADC_CHAN(_idx) { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .channel = _idx, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
> +}
> +
> +static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = {
> + LPC18XX_ADC_CHAN(0),
> + LPC18XX_ADC_CHAN(1),
> + LPC18XX_ADC_CHAN(2),
> + LPC18XX_ADC_CHAN(3),
> + LPC18XX_ADC_CHAN(4),
> + LPC18XX_ADC_CHAN(5),
> + LPC18XX_ADC_CHAN(6),
> + LPC18XX_ADC_CHAN(7),
> +};
> +
> +static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch)
> +{
> + int ret;
> + u32 reg;
> +
> + reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW;
> + writel(reg, adc->base + LPC18XX_ADC_CR);
> +
> + ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg,
> + reg & LPC18XX_ADC_CONV_DONE, 3, 9);
> + if (ret) {
> + dev_warn(adc->dev, "adc read timed out\n");
> + return ret;
> + }
> +
> + return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK;
> +}
> +
> +static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct lpc18xx_adc *adc = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + *val = lpc18xx_adc_read_chan(adc, chan->channel);
> + if (*val < 0)
> + return *val;
> +
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_SCALE:
> + *val = regulator_get_voltage(adc->vref) / 1000;
> + *val2 = 10;
> +
> + return IIO_VAL_FRACTIONAL_LOG2;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct iio_info lpc18xx_adc_info = {
> + .read_raw = lpc18xx_adc_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int lpc18xx_adc_probe(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev;
> + struct lpc18xx_adc *adc;
> + struct resource *res;
> + unsigned int clkdiv;
> + unsigned long rate;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, indio_dev);
> + adc = iio_priv(indio_dev);
> + adc->dev = &pdev->dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + adc->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(adc->base))
> + return PTR_ERR(adc->base);
> +
> + adc->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(adc->clk)) {
> + dev_err(&pdev->dev, "error getting clock\n");
> + return PTR_ERR(adc->clk);
> + }
> +
> + rate = clk_get_rate(adc->clk);
> + clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET);
> +
> + adc->vref = devm_regulator_get(&pdev->dev, "vref");
> + if (IS_ERR(adc->vref)) {
> + dev_err(&pdev->dev, "error get regulator\n");
getting
> + return PTR_ERR(adc->vref);
> + }
> +
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->info = &lpc18xx_adc_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = lpc18xx_adc_iio_channels;
> + indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels);
> +
> + ret = regulator_enable(adc->vref);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable regulator\n");
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(adc->clk);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to enable clock.\n");
maybe delete '.', it's the only message with a full stop
> + goto dis_reg;
> + }
> +
> + adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) |
> + LPC18XX_ADC_CR_PDN;
> + writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR);
> +
> + ret = iio_device_register(indio_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "unable to register device\n");
> + goto dis_clk;
> + }
> +
> + return 0;
> +
> +dis_clk:
> + writel(0, adc->base + LPC18XX_ADC_CR);
> + clk_disable_unprepare(adc->clk);
> +dis_reg:
> + regulator_disable(adc->vref);
> + return ret;
> +}
> +
> +static int lpc18xx_adc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct lpc18xx_adc *adc = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> +
> + writel(0, adc->base + LPC18XX_ADC_CR);
> + clk_disable_unprepare(adc->clk);
> + regulator_disable(adc->vref);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id lpc18xx_adc_match[] = {
> + { .compatible = "nxp,lpc1850-adc" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, lpc18xx_adc_match);
> +
> +static struct platform_driver lpc18xx_adc_driver = {
> + .probe = lpc18xx_adc_probe,
> + .remove = lpc18xx_adc_remove,
> + .driver = {
> + .name = "lpc18xx-adc",
> + .of_match_table = lpc18xx_adc_match,
> + },
> +};
> +module_platform_driver(lpc18xx_adc_driver);
> +
> +MODULE_DESCRIPTION("LPC18xx ADC driver");
> +MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
> +MODULE_LICENSE("GPL v2");
>
--
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] iio: adc: add NXP LPC18xx ADC driver
2016-02-29 19:42 ` Peter Meerwald-Stadler
@ 2016-02-29 20:45 ` Joachim Eastwood
0 siblings, 0 replies; 10+ messages in thread
From: Joachim Eastwood @ 2016-02-29 20:45 UTC (permalink / raw)
To: Peter Meerwald-Stadler
Cc: Jonathan Cameron, knaack.h, Lars-Peter Clausen, linux-iio,
Rob Herring
Hi Peter,
On 29 February 2016 at 20:42, Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:
>
>> Add base support for the 10-bit SAR ADC peripheral found
>> on NXP LPC18xx/43xx SoCs.
>
> nitpicking below
>
>> This is a minimal driver that does not support burst mode,
>> interrupts, DMA or hardware triggers.
>>
>> User manual with register description can be found on:
>> LPC18xx: www.nxp.com/documents/user_manual/UM10430.pdf
>> LPC43xx: www.nxp.com/documents/user_manual/UM10503.pdf
>>
>> Signed-off-by: Joachim Eastwood <manabian@gmail.com>
>> ---
>> +/* Data registers bits */
>
> 'Data register bits' maybe
>
>> + adc->vref = devm_regulator_get(&pdev->dev, "vref");
>> + if (IS_ERR(adc->vref)) {
>> + dev_err(&pdev->dev, "error get regulator\n");
>
> getting
>
>> + return PTR_ERR(adc->vref);
>> + }
>> +
>> + indio_dev->name = dev_name(&pdev->dev);
>> + indio_dev->dev.parent = &pdev->dev;
>> + indio_dev->info = &lpc18xx_adc_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> + indio_dev->channels = lpc18xx_adc_iio_channels;
>> + indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels);
>> +
>> + ret = regulator_enable(adc->vref);
>> + if (ret) {
>> + dev_err(&pdev->dev, "unable to enable regulator\n");
>> + return ret;
>> + }
>> +
>> + ret = clk_prepare_enable(adc->clk);
>> + if (ret) {
>> + dev_err(&pdev->dev, "unable to enable clock.\n");
>
> maybe delete '.', it's the only message with a full stop
I will fix them all in the next version. I also noticed that a couple
of your comments also apply to the dac driver.
Thanks for looking through!
regards,
Joachim Eastwood
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/4] dt: document NXP LPC1850 ADC driver bindings
2016-02-29 19:05 [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Joachim Eastwood
2016-02-29 19:05 ` [PATCH 1/4] iio: adc: add NXP LPC18xx ADC driver Joachim Eastwood
@ 2016-02-29 19:05 ` Joachim Eastwood
2016-02-29 19:05 ` [PATCH 3/4] iio: dac: add NXP LPC18xx DAC driver Joachim Eastwood
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Joachim Eastwood @ 2016-02-29 19:05 UTC (permalink / raw)
To: jic23; +Cc: Joachim Eastwood, knaack.h, lars, pmeerw, linux-iio, robh+dt
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
.../devicetree/bindings/iio/adc/lpc1850-adc.txt | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/lpc1850-adc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/lpc1850-adc.txt b/Documentation/devicetree/bindings/iio/adc/lpc1850-adc.txt
new file mode 100644
index 000000000000..0bcae5140bc5
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/lpc1850-adc.txt
@@ -0,0 +1,21 @@
+NXP LPC1850 ADC bindings
+
+Required properties:
+- compatible: Should be "nxp,lpc1850-adc"
+- reg: Offset and length of the register set for the ADC device
+- interrupts: The interrupt number for the ADC device
+- clocks: The root clock of the ADC controller
+- vref-supply: The regulator supply ADC reference voltage
+- resets: phandle to reset controller and line specifier
+
+Example:
+
+adc0: adc@400e3000 {
+ compatible = "nxp,lpc1850-adc";
+ reg = <0x400e3000 0x1000>;
+ interrupts = <17>;
+ clocks = <&ccu1 CLK_APB3_ADC0>;
+ vref-supply = <®_vdda>;
+ resets = <&rgu 40>;
+ status = "disabled";
+};
--
1.8.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/4] iio: dac: add NXP LPC18xx DAC driver
2016-02-29 19:05 [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Joachim Eastwood
2016-02-29 19:05 ` [PATCH 1/4] iio: adc: add NXP LPC18xx ADC driver Joachim Eastwood
2016-02-29 19:05 ` [PATCH 2/4] dt: document NXP LPC1850 ADC driver bindings Joachim Eastwood
@ 2016-02-29 19:05 ` Joachim Eastwood
2016-02-29 19:05 ` [PATCH 4/4] dt: document NXP LPC1850 DAC driver bindings Joachim Eastwood
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Joachim Eastwood @ 2016-02-29 19:05 UTC (permalink / raw)
To: jic23; +Cc: Joachim Eastwood, knaack.h, lars, pmeerw, linux-iio, robh+dt
Add base support for the 10-bit DAC peripheral found
on NXP LPC18xx/43xx SoCs.
This is a minimal driver that does not support DMA or
interrupts.
User manual with register description can be found on:
LPC18xx: www.nxp.com/documents/user_manual/UM10430.pdf
LPC43xx: www.nxp.com/documents/user_manual/UM10503.pdf
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
drivers/iio/dac/Kconfig | 10 +++
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/lpc18xx_dac.c | 204 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 215 insertions(+)
create mode 100644 drivers/iio/dac/lpc18xx_dac.c
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index a995139f907c..210db81ca144 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -154,6 +154,16 @@ config AD7303
To compile this driver as module choose M here: the module will be called
ad7303.
+config LPC18XX_DAC
+ tristate "NXP LPC18xx DAC driver"
+ depends on ARCH_LPC18XX || COMPILE_TEST
+ depends on OF && HAS_IOMEM
+ help
+ Say yes here to build support for NXP LPC18XX DAC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called lpc18xx_dac.
+
config M62332
tristate "Mitsubishi M62332 DAC driver"
depends on I2C
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 67b48429686d..420a15cdaa53 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
obj-$(CONFIG_AD7303) += ad7303.o
+obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c
new file mode 100644
index 000000000000..5f4668be603f
--- /dev/null
+++ b/drivers/iio/dac/lpc18xx_dac.c
@@ -0,0 +1,204 @@
+/*
+ * IIO DAC driver for NXP LPC18xx DAC
+ *
+ * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * UNSUPPORTED hardware features:
+ * - Interrupts
+ * - DMA
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+/* LPC18XX DAC registers and bits */
+#define LPC18XX_DAC_CR 0x000
+#define LPC18XX_DAC_CR_VALUE_SHIFT 6
+#define LPC18XX_DAC_CR_VALUE_MASK 0x3ff
+#define LPC18XX_DAC_CR_BIAS BIT(16)
+#define LPC18XX_DAC_CTRL 0x004
+#define LPC18XX_DAC_CTRL_DMA_ENA BIT(3)
+
+struct lpc18xx_dac {
+ struct clk *clk;
+ void __iomem *base;
+ struct regulator *vref;
+};
+
+static const struct iio_chan_spec lpc18xx_dac_iio_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int lpc18xx_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lpc18xx_dac *dac = iio_priv(indio_dev);
+ u32 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ reg = readl(dac->base + LPC18XX_DAC_CR);
+ *val = reg >> LPC18XX_DAC_CR_VALUE_SHIFT;
+ *val &= LPC18XX_DAC_CR_VALUE_MASK;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(dac->vref) / 1000;
+ *val2 = 10;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static int lpc18xx_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct lpc18xx_dac *dac = iio_priv(indio_dev);
+ u32 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < 0 || val > LPC18XX_DAC_CR_VALUE_MASK)
+ return -EINVAL;
+
+ reg = LPC18XX_DAC_CR_BIAS;
+ reg |= val << LPC18XX_DAC_CR_VALUE_SHIFT;
+ writel(reg, dac->base + LPC18XX_DAC_CR);
+ writel(LPC18XX_DAC_CTRL_DMA_ENA, dac->base + LPC18XX_DAC_CTRL);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info lpc18xx_dac_info = {
+ .read_raw = lpc18xx_dac_read_raw,
+ .write_raw = lpc18xx_dac_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int lpc18xx_dac_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct lpc18xx_dac *dac;
+ struct resource *res;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, indio_dev);
+ dac = iio_priv(indio_dev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dac->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dac->base))
+ return PTR_ERR(dac->base);
+
+ dac->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dac->clk)) {
+ dev_err(&pdev->dev, "error getting clock\n");
+ return PTR_ERR(dac->clk);
+ }
+
+ dac->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(dac->vref)) {
+ dev_err(&pdev->dev, "error get regulator\n");
+ return PTR_ERR(dac->vref);
+ }
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &lpc18xx_dac_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = lpc18xx_dac_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lpc18xx_dac_iio_channels);
+
+ ret = regulator_enable(dac->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable regulator\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dac->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable clock.\n");
+ goto dis_reg;
+ }
+
+ writel(0, dac->base + LPC18XX_DAC_CTRL);
+ writel(0, dac->base + LPC18XX_DAC_CR);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ goto dis_clk;
+ }
+
+ return 0;
+
+dis_clk:
+ clk_disable_unprepare(dac->clk);
+dis_reg:
+ regulator_disable(dac->vref);
+ return ret;
+}
+
+static int lpc18xx_dac_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct lpc18xx_dac *dac = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ writel(0, dac->base + LPC18XX_DAC_CTRL);
+ clk_disable_unprepare(dac->clk);
+ regulator_disable(dac->vref);
+
+ return 0;
+}
+
+static const struct of_device_id lpc18xx_dac_match[] = {
+ { .compatible = "nxp,lpc1850-dac" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_dac_match);
+
+static struct platform_driver lpc18xx_dac_driver = {
+ .probe = lpc18xx_dac_probe,
+ .remove = lpc18xx_dac_remove,
+ .driver = {
+ .name = "lpc18xx-dac",
+ .of_match_table = lpc18xx_dac_match,
+ },
+};
+module_platform_driver(lpc18xx_dac_driver);
+
+MODULE_DESCRIPTION("LPC18xx DAC driver");
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_LICENSE("GPL v2");
--
1.8.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/4] dt: document NXP LPC1850 DAC driver bindings
2016-02-29 19:05 [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Joachim Eastwood
` (2 preceding siblings ...)
2016-02-29 19:05 ` [PATCH 3/4] iio: dac: add NXP LPC18xx DAC driver Joachim Eastwood
@ 2016-02-29 19:05 ` Joachim Eastwood
2016-03-01 20:33 ` [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Rob Herring
2016-03-05 18:24 ` Jonathan Cameron
5 siblings, 0 replies; 10+ messages in thread
From: Joachim Eastwood @ 2016-02-29 19:05 UTC (permalink / raw)
To: jic23; +Cc: Joachim Eastwood, knaack.h, lars, pmeerw, linux-iio, robh+dt
Signed-off-by: Joachim Eastwood <manabian@gmail.com>
---
.../devicetree/bindings/iio/dac/lpc1850-dac.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/dac/lpc1850-dac.txt
diff --git a/Documentation/devicetree/bindings/iio/dac/lpc1850-dac.txt b/Documentation/devicetree/bindings/iio/dac/lpc1850-dac.txt
new file mode 100644
index 000000000000..7d6647d4af5e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/lpc1850-dac.txt
@@ -0,0 +1,20 @@
+NXP LPC1850 DAC bindings
+
+Required properties:
+- compatible: Should be "nxp,lpc1850-dac"
+- reg: Offset and length of the register set for the ADC device
+- interrupts: The interrupt number for the ADC device
+- clocks: The root clock of the ADC controller
+- vref-supply: The regulator supply ADC reference voltage
+- resets: phandle to reset controller and line specifier
+
+Example:
+dac: dac@400e1000 {
+ compatible = "nxp,lpc1850-dac";
+ reg = <0x400e1000 0x1000>;
+ interrupts = <0>;
+ clocks = <&ccu1 CLK_APB3_DAC>;
+ vref-supply = <®_vdda>;
+ resets = <&rgu 42>;
+ status = "disabled";
+};
--
1.8.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 0/4] Support for analog peripherals on LPC18xx familiy
2016-02-29 19:05 [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Joachim Eastwood
` (3 preceding siblings ...)
2016-02-29 19:05 ` [PATCH 4/4] dt: document NXP LPC1850 DAC driver bindings Joachim Eastwood
@ 2016-03-01 20:33 ` Rob Herring
2016-03-01 20:43 ` Joachim Eastwood
2016-03-05 18:24 ` Jonathan Cameron
5 siblings, 1 reply; 10+ messages in thread
From: Rob Herring @ 2016-03-01 20:33 UTC (permalink / raw)
To: Joachim Eastwood
Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-iio@vger.kernel.org
On Mon, Feb 29, 2016 at 1:05 PM, Joachim Eastwood <manabian@gmail.com> wrote:
> This patch set adds support for the analog (ADC and DAC) peripherals
> found on NXP LPC18xx/43xx SoCs. The ADC is a SAR converter with 10-bit
> resolution and 8 muxed inputs. The DAC is a simple single channel 10-
> bit DAC.
>
> Note that these drivers do not support all the features found on the
> peripherals. DMA and interrupts are not supported in either drivers.
> The ADC driver also lack 'burst-mode' and hardware triggers. So for
> now the ADC is limited to single shot samples.
>
> Both drivers tested on Embedded Artists' LPC4357 Developer's Kit.
>
> Based on iio/testing branch from 29.02.15.
>
>
> Joachim Eastwood (4):
> iio: adc: add NXP LPC18xx ADC driver
> dt: document NXP LPC1850 ADC driver bindings
> iio: dac: add NXP LPC18xx DAC driver
> dt: document NXP LPC1850 DAC driver bindings
You need to send bindings to the DT list.
Rob
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 0/4] Support for analog peripherals on LPC18xx familiy
2016-03-01 20:33 ` [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Rob Herring
@ 2016-03-01 20:43 ` Joachim Eastwood
0 siblings, 0 replies; 10+ messages in thread
From: Joachim Eastwood @ 2016-03-01 20:43 UTC (permalink / raw)
To: Rob Herring
Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-iio@vger.kernel.org
Hi Rob,
On 1 March 2016 at 21:33, Rob Herring <robh+dt@kernel.org> wrote:
> On Mon, Feb 29, 2016 at 1:05 PM, Joachim Eastwood <manabian@gmail.com> wrote:
>> This patch set adds support for the analog (ADC and DAC) peripherals
>> found on NXP LPC18xx/43xx SoCs. The ADC is a SAR converter with 10-bit
>> resolution and 8 muxed inputs. The DAC is a simple single channel 10-
>> bit DAC.
>>
>> Note that these drivers do not support all the features found on the
>> peripherals. DMA and interrupts are not supported in either drivers.
>> The ADC driver also lack 'burst-mode' and hardware triggers. So for
>> now the ADC is limited to single shot samples.
>>
>> Both drivers tested on Embedded Artists' LPC4357 Developer's Kit.
>>
>> Based on iio/testing branch from 29.02.15.
>>
>>
>> Joachim Eastwood (4):
>> iio: adc: add NXP LPC18xx ADC driver
>> dt: document NXP LPC1850 ADC driver bindings
>> iio: dac: add NXP LPC18xx DAC driver
>> dt: document NXP LPC1850 DAC driver bindings
>
> You need to send bindings to the DT list.
ah, my bad. I was pretty sure I added it when I did format-patch, but
it seems to have been lost along the way...
Either way there will be a v2 and I will make sure to cc the DT list then.
Thanks for reporting.
regards,
Joachim Eastwood
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 0/4] Support for analog peripherals on LPC18xx familiy
2016-02-29 19:05 [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Joachim Eastwood
` (4 preceding siblings ...)
2016-03-01 20:33 ` [PATCH 0/4] Support for analog peripherals on LPC18xx familiy Rob Herring
@ 2016-03-05 18:24 ` Jonathan Cameron
5 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2016-03-05 18:24 UTC (permalink / raw)
To: Joachim Eastwood; +Cc: knaack.h, lars, pmeerw, linux-iio, robh+dt
On 29/02/16 19:05, Joachim Eastwood wrote:
> This patch set adds support for the analog (ADC and DAC) peripherals
> found on NXP LPC18xx/43xx SoCs. The ADC is a SAR converter with 10-bit
> resolution and 8 muxed inputs. The DAC is a simple single channel 10-
> bit DAC.
>
> Note that these drivers do not support all the features found on the
> peripherals. DMA and interrupts are not supported in either drivers.
> The ADC driver also lack 'burst-mode' and hardware triggers. So for
> now the ADC is limited to single shot samples.
>
> Both drivers tested on Embedded Artists' LPC4357 Developer's Kit.
>
> Based on iio/testing branch from 29.02.15.
Series looks pretty good. Will do a review of V2 - right now I haven't
found anything to add to what Peter found.
Nice little ADC from a quick read of the data sheet. Not too complex
but still well featured.
>
>
> Joachim Eastwood (4):
> iio: adc: add NXP LPC18xx ADC driver
> dt: document NXP LPC1850 ADC driver bindings
> iio: dac: add NXP LPC18xx DAC driver
> dt: document NXP LPC1850 DAC driver bindings
>
> .../devicetree/bindings/iio/adc/lpc1850-adc.txt | 21 ++
> .../devicetree/bindings/iio/dac/lpc1850-dac.txt | 20 ++
> drivers/iio/adc/Kconfig | 10 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/lpc18xx_adc.c | 226 +++++++++++++++++++++
> drivers/iio/dac/Kconfig | 10 +
> drivers/iio/dac/Makefile | 1 +
> drivers/iio/dac/lpc18xx_dac.c | 204 +++++++++++++++++++
> 8 files changed, 493 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/lpc1850-adc.txt
> create mode 100644 Documentation/devicetree/bindings/iio/dac/lpc1850-dac.txt
> create mode 100644 drivers/iio/adc/lpc18xx_adc.c
> create mode 100644 drivers/iio/dac/lpc18xx_dac.c
>
^ permalink raw reply [flat|nested] 10+ messages in thread