iio: adc: add driver for Rockchip saradc
Subsystems (3)
Heiko Stübner
maintainerPATCH v4 1/2 iio: adc: add driver for Rockchip saradc
The ADC is a 3-channel signal-ended 10-bit Successive Approximation Register (SAR) A/D Converter. It uses the supply and ground as its reference and converts the analog input signal into 10-bit binary digital codes.
---
changes since v3:
- address comments from Jonathan Cameron
- explicitly check for negative values in error checks
- change Kconfig depends to ARCH_ROCKCHIP || (ARM && COMPILE_TEST) to prevent issues due to undefined writel_relaxed functions The previous check for OF was unecessary, as the only used function is of_property_read_u32, for which a stub is defined for the !OF case.
changes since v2:
- address more comments from Peter Meerwald
mainly the missing info_mask_shared_by_type element
changes since v1:
- address comments from Peter Meerwald
drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/rockchip_saradc.c | 315 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 drivers/iio/adc/rockchip_saradc.c
drivers/iio/adc/Kconfig +10 -0
@@ -187,6 +187,16 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config ROCKCHIP_SARADC + tristate "Rockchip SARADC driver" + depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST) + help + Say yes here to build support for the SARADC found in SoCs from + Rockchip. + + To compile this driver as a module, choose M here: the + module will be called rockchip_saradc. + config TI_ADC081C tristate "Texas Instruments ADC081C021/027" depends on I2C
drivers/iio/adc/Makefile +1 -0
@@ -20,6 +20,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_NAU7802) += nau7802.o +obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
drivers/iio/adc/rockchip_saradc.c new file +315 -0
315 lines not on this page · raw message
Signature · 1 line
1.9.0
Heiko Stübner
maintainerPATCH v4 2/2 dt-bindings: document Rockchip saradc
This add the necessary binding documentation for the saradc found in all recent processors from Rockchip.
---
changes since v1:
- fix typos found by Hartmut Knaack
.../bindings/iio/adc/rockchip-saradc.txt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt new file +28 -0
@@ -0,0 +1,28 @@ +Rockchip Successive Approximation Register (SAR) A/D Converter bindings + +Required properties: +- compatible: Should be "rockchip,saradc" +- 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. +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Shall be "saradc" for the converter-clock, and "apb_pclk" for + the peripheral clock. +- vref-supply: The regulator supply ADC reference voltage. +- #io-channel-cells: Should be 1, see ../iio-bindings.txt + +Optional properties : +- clock-frequency : converter frequency in Hz. If omitted, 1MHz is used. + +Example: + saradc: saradc@2006c000 { + compatible = "rockchip,saradc"; + reg = <0x2006c000 0x100>; + interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>; + clock-names = "saradc", "apb_pclk"; + #io-channel-cells = <1>; + vref-supply = <&vcc18>; + clock-frequency = <1000000>; + };
Signature · 1 line
1.9.0
Heiko Stübner
maintainerPATCH v5 1/2 iio: adc: add driver for Rockchip saradc
In reply to Heiko StübnerThe ADC is a 3-channel signal-ended 10-bit Successive Approximation Register (SAR) A/D Converter. It uses the supply and ground as its reference and converts the analog input signal into 10-bit binary digital codes.
---
changes since v4:
- address comments from Hartmut Knaack
- explain the DLY_PU_SOC value
- determine regulator voltage in IIO_CHAN_INFO_SCALE
- return ENODEV directly in the !np case
changes since v3:
- address comments from Jonathan Cameron
- explicitly check for negative values in error checks
- change Kconfig depends to ARCH_ROCKCHIP || (ARM && COMPILE_TEST) to prevent issues due to undefined writel_relaxed functions The previous check for OF was unecessary, as the only used function is of_property_read_u32, for which a stub is defined for the !OF case.
changes since v2:
- address more comments from Peter Meerwald
mainly the missing info_mask_shared_by_type element
changes since v1:
- address comments from Peter Meerwald
drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/rockchip_saradc.c | 316 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 drivers/iio/adc/rockchip_saradc.c
drivers/iio/adc/Kconfig +10 -0
@@ -187,6 +187,16 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config ROCKCHIP_SARADC + tristate "Rockchip SARADC driver" + depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST) + help + Say yes here to build support for the SARADC found in SoCs from + Rockchip. + + To compile this driver as a module, choose M here: the + module will be called rockchip_saradc. + config TI_ADC081C tristate "Texas Instruments ADC081C021/027" depends on I2C
drivers/iio/adc/Makefile +1 -0
@@ -20,6 +20,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_NAU7802) += nau7802.o +obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
drivers/iio/adc/rockchip_saradc.c new file +316 -0
316 lines not on this page · raw message
Signature · 1 line
1.9.0
Quoted 5 lines · Heiko St?bner
The ADC is a 3-channel signal-ended 10-bit Successive Approximation Register (SAR) A/D Converter. It uses the supply and ground as its reference and converts the analog input signal into 10-bit binary digital codes.
Signed-off-by Heiko Stuebner <[email protected]>
Quoted 1 line
---
Looks good to me. The fact, that SARADC_DLY_PU_SOC_MASK is defined but never used should be fine under the given circumstances, that no datasheet with register definitions is publicly available.
Quoted 379 lines
changes since v4:
- address comments from Hartmut Knaack
- explain theDLY_PU_SOCvalue
- determine regulator voltage inIIO_CHAN_INFO_SCALE
- return ENODEV directly in the !np case
changes since v3:
- address comments from Jonathan Cameron
- explicitly check for negative values in error checks
- changeKconfigdepends toARCH_ROCKCHIP|| (ARM &&COMPILE_TEST) to prevent issues due to undefinedwritel_relaxedfunctions The previous check for OF was unecessary, as the only used function isof_property_read_u32, for which a stub is defined for the !OF case.
changes since v2:
- address more comments from Peter Meerwald
mainly the missinginfo_mask_shared_by_typeelement
changes since v1:
- address comments from Peter Meerwalddrivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/rockchip_saradc.c | 316 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 drivers/iio/adc/rockchip_saradc.cdrivers/iio/adc/Kconfig +10
-0@@ -187,6 +187,16 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config ROCKCHIP_SARADC + tristate "Rockchip SARADC driver" + depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST) + help + Say yes here to build support for the SARADC found in SoCs from + Rockchip. + + To compile this driver as a module, choose M here: the + module will be called rockchip_saradc. + config TI_ADC081C tristate "Texas Instruments ADC081C021/027" depends on I2Cdrivers/iio/adc/Makefile +1
-0@@ -20,6 +20,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_NAU7802) += nau7802.o +obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.odrivers/iio/adc/rockchip_saradc.c new file +316
-0@@ -0,0 +1,316 @@ +/* +* Rockchip Successive Approximation Register (SAR) A/D Converter +* Copyright (C) 2014 ROCKCHIP, Inc. +* +* 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/clk.h> +#include <linux/completion.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/iio.h> + +#define SARADC_DATA 0x00 +#define SARADC_DATA_MASK 0x3ff + +#define SARADC_STAS 0x04 +#define SARADC_STAS_BUSY BIT(0) + +#define SARADC_CTRL 0x08 +#define SARADC_CTRL_IRQ_STATUS BIT(6) +#define SARADC_CTRL_IRQ_ENABLE BIT(5) +#define SARADC_CTRL_POWER_CTRL BIT(3) +#define SARADC_CTRL_CHN_MASK 0x7 + +#define SARADC_DLY_PU_SOC 0x0c +#define SARADC_DLY_PU_SOC_MASK 0x3f + +#define SARADC_BITS 10 +#define SARADC_TIMEOUT msecs_to_jiffies(100) + +struct rockchip_saradc { +void __iomem *regs; +struct clk *pclk; +struct clk *clk; +struct completion completion; +struct regulator *vref; +u16 last_val; +}; + +static int rockchip_saradc_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +int *val,int *val2,long mask) +{ +struct rockchip_saradc *info =iio_priv (indio_dev); +int ret; + +switch (mask) { +case IIO_CHAN_INFO_RAW : +mutex_lock (&indio_dev->mlock); + +/* 8 clock periods as delay between power up and start cmd */ +writel_relaxed (8 , info->regs +SARADC_DLY_PU_SOC ); + +/* Select the channel to be used and trigger conversion */ +writel (SARADC_CTRL_POWER_CTRL + | (chan->channel &SARADC_CTRL_CHN_MASK ) + |SARADC_CTRL_IRQ_ENABLE , + info->regs +SARADC_CTRL ); + +if (!wait_for_completion_timeout (&info->completion, +SARADC_TIMEOUT )) { +writel_relaxed (0 , info->regs +SARADC_CTRL ); +mutex_unlock (&indio_dev->mlock); +return -ETIMEDOUT ; + } + + *val = info->last_val; +mutex_unlock (&indio_dev->mlock); +return IIO_VAL_INT ; +case IIO_CHAN_INFO_SCALE : + ret =regulator_get_voltage (info->vref); +if (ret <0 ) { +dev_err (&indio_dev->dev,"failed to get voltage\n" ); +return ret; + } + + *val = ret /1000 ; + *val2 =SARADC_BITS ; +return IIO_VAL_FRACTIONAL_LOG2 ; +default : +return -EINVAL ; + } +} + +static irqreturn_t rockchip_saradc_isr (int irq,void *dev_id) +{ +struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id; + +/* Read value */ + info->last_val =readl_relaxed (info->regs +SARADC_DATA ); + info->last_val &=SARADC_DATA_MASK ; + +/* Clear irq & power down adc */ +writel_relaxed (0 , info->regs +SARADC_CTRL ); + +complete (&info->completion); + +return IRQ_HANDLED ; +} + +static const struct iio_info rockchip_saradc_iio_info = { + .read_raw = rockchip_saradc_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), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ +} + +static const struct iio_chan_spec rockchip_saradc_iio_channels[] = { +ADC_CHANNEL (0 ,"adc0" ), +ADC_CHANNEL (1 ,"adc1" ), +ADC_CHANNEL (2 ,"adc2" ), +}; + +static int rockchip_saradc_probe (struct platform_device *pdev) +{ +struct rockchip_saradc *info =NULL ; +struct device_node *np = pdev->dev.of_node; +struct iio_dev *indio_dev =NULL ; +struct resource *mem; +int ret; +int irq; +u32 rate; + +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); + + mem =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); + info->regs =devm_request_and_ioremap (&pdev->dev, mem); +if (!info->regs) +return -ENOMEM ; + + 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, rockchip_saradc_isr, +0 ,dev_name (&pdev->dev), info); +if (ret <0 ) { +dev_err (&pdev->dev,"failed requesting irq %d\n" , irq); +return ret; + } + +init_completion (&info->completion); + + info->pclk =devm_clk_get (&pdev->dev,"apb_pclk" ); +if (IS_ERR (info->pclk)) { +dev_err (&pdev->dev,"failed to get pclk\n" ); +return PTR_ERR (info->pclk); + } + + info->clk =devm_clk_get (&pdev->dev,"saradc" ); +if (IS_ERR (info->clk)) { +dev_err (&pdev->dev,"failed to get adc clock\n" ); +return PTR_ERR (info->clk); + } + + info->vref =devm_regulator_get (&pdev->dev,"vref" ); +if (IS_ERR (info->vref)) { +dev_err (&pdev->dev,"failed to get regulator, %ld\n" , +PTR_ERR (info->vref)); +return PTR_ERR (info->vref); + } + +/* use a default of 1MHz for the converter clock */ + ret =of_property_read_u32 (np,"clock-frequency" , &rate); +if (ret <0 ) + rate =1000000 ; + + ret =clk_set_rate (info->clk, rate); +if (ret <0 ) { +dev_err (&pdev->dev,"failed to set adc clk rate, %d\n" , ret); +return ret; + } + + ret =regulator_enable (info->vref); +if (ret <0 ) { +dev_err (&pdev->dev,"failed to enable vref regulator\n" ); +return ret; + } + + ret =clk_prepare_enable (info->pclk); +if (ret <0 ) { +dev_err (&pdev->dev,"failed to enable pclk\n" ); + goto err_reg_voltage; + } + + ret =clk_prepare_enable (info->clk); +if (ret <0 ) { +dev_err (&pdev->dev,"failed to enable converter clock\n" ); + goto err_pclk; + } + +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 = &rockchip_saradc_iio_info; + indio_dev->modes =INDIO_DIRECT_MODE ; + + indio_dev->channels = rockchip_saradc_iio_channels; + indio_dev->num_channels =ARRAY_SIZE (rockchip_saradc_iio_channels); + + ret =iio_device_register (indio_dev); +if (ret) + goto err_clk; + +return 0 ; + +err_clk: +clk_disable_unprepare (info->clk); +err_pclk: +clk_disable_unprepare (info->pclk); +err_reg_voltage: +regulator_disable (info->vref); +return ret; +} + +static int rockchip_saradc_remove (struct platform_device *pdev) +{ +struct iio_dev *indio_dev =platform_get_drvdata (pdev); +struct rockchip_saradc *info =iio_priv (indio_dev); + +iio_device_unregister (indio_dev); +clk_disable_unprepare (info->clk); +clk_disable_unprepare (info->pclk); +regulator_disable (info->vref); + +return 0 ; +} + +#ifdef CONFIG_PM_SLEEP +static int rockchip_saradc_suspend (struct device *dev) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct rockchip_saradc *info =iio_priv (indio_dev); + +clk_disable_unprepare (info->clk); +clk_disable_unprepare (info->pclk); +regulator_disable (info->vref); + +return 0 ; +} + +static int rockchip_saradc_resume (struct device *dev) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct rockchip_saradc *info =iio_priv (indio_dev); +int ret; + + ret =regulator_enable (info->vref); +if (ret) +return ret; + + ret =clk_prepare_enable (info->pclk); +if (ret) +return ret; + + ret =clk_prepare_enable (info->clk); +if (ret) +return ret; + +return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops, + rockchip_saradc_suspend, rockchip_saradc_resume); + +static const struct of_device_id rockchip_saradc_match[] = { + { .compatible ="rockchip,saradc" }, + {}, +}; +MODULE_DEVICE_TABLE (of, rockchip_saradc_match); + +static struct platform_driver rockchip_saradc_driver = { + .probe = rockchip_saradc_probe, + .remove = rockchip_saradc_remove, + .driver = { + .name ="rockchip-saradc" , + .owner =THIS_MODULE , + .of_match_table = rockchip_saradc_match, + .pm = &rockchip_saradc_pm_ops, + }, +}; + +module_platform_driver (rockchip_saradc_driver);
On 07/14/2014 10:43 PM, Heiko St?bner wrote:
Looks really good overall.
[...]
Quoted 4 lines
drivers/iio/adc/rockchip_saradc.c new file +4
-0+static int rockchip_saradc_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +int *val,int *val2,long mask) +{
drivers/iio/adc/rockchip_saradc.c line 66
Quoted 10 lines
drivers/iio/adc/rockchip_saradc.c new file +10
-0+struct rockchip_saradc *info =iio_priv (indio_dev); +int ret; + +switch (mask) { +case IIO_CHAN_INFO_RAW : +mutex_lock (&indio_dev->mlock); + +/* 8 clock periods as delay between power up and start cmd */ +writel_relaxed (8 , info->regs +SARADC_DLY_PU_SOC ); +
It makes sense to call reinit_completion() here.
Quoted 16 lines
drivers/iio/adc/rockchip_saradc.c new file +16
-0+/* Select the channel to be used and trigger conversion */ +writel (SARADC_CTRL_POWER_CTRL + | (chan->channel &SARADC_CTRL_CHN_MASK ) + |SARADC_CTRL_IRQ_ENABLE , + info->regs +SARADC_CTRL ); + +if (!wait_for_completion_timeout (&info->completion, +SARADC_TIMEOUT )) { +writel_relaxed (0 , info->regs +SARADC_CTRL ); +mutex_unlock (&indio_dev->mlock); +return -ETIMEDOUT ; + } + + *val = info->last_val; +mutex_unlock (&indio_dev->mlock); +return IIO_VAL_INT ;
[...]
Quoted 12 lines
drivers/iio/adc/rockchip_saradc.c new file +12
-0+static int rockchip_saradc_probe (struct platform_device *pdev) +{ +struct rockchip_saradc *info =NULL ; +struct device_node *np = pdev->dev.of_node; +struct iio_dev *indio_dev =NULL ; +struct resource *mem; +int ret; +int irq; +u32 rate; + +if (!np) +return -ENODEV ;
drivers/iio/adc/rockchip_saradc.c line 155
Quoted 10 lines
drivers/iio/adc/rockchip_saradc.c new file +10
-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); + + mem =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); + info->regs =devm_request_and_ioremap (&pdev->dev, mem);
devm_request_and_ioremap() is deprecated an will be removed in the next release[1]. Use devm_ioremap_resource(), note that devm_ioremap_resource() returns a ERR_PTR instead of NULL on error.
Quoted 7 lines
drivers/iio/adc/rockchip_saradc.c new file +7
-0+if (!info->regs) +return -ENOMEM ; + + irq =platform_get_irq (pdev,0 ); +if (irq <0 ) { +dev_err (&pdev->dev,"no irq resource?\n" ); +return irq;
drivers/iio/adc/rockchip_saradc.c line 172
Quoted 10 lines
drivers/iio/adc/rockchip_saradc.c new file +10
-0+ } + + ret =devm_request_irq (&pdev->dev, irq, rockchip_saradc_isr, +0 ,dev_name (&pdev->dev), info); +if (ret <0 ) { +dev_err (&pdev->dev,"failed requesting irq %d\n" , irq); +return ret; + } + +init_completion (&info->completion);
Since the completion is used in the interrupt callback it must be initialized before the interrupt is requested.
Quoted 1 line
+
[...]
drivers/iio/adc/rockchip_saradc.c line 196
Quoted 4 lines
drivers/iio/adc/rockchip_saradc.c new file +4
-0+/* use a default of 1MHz for the converter clock */ + ret =of_property_read_u32 (np,"clock-frequency" , &rate); +if (ret <0 ) + rate =1000000 ;
Should this really be in the devicetree or shouldn't this be user configurable at runtime?
[...]