From 2fb5904d92d42c88432cb29f567654d28f9d6411 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Tue, 31 Jan 2017 19:03:06 +0100 Subject: iio: distance: add dt binding for devantech-srf04 This patch adds dt binding for devantech ultrasonic ranger srf04. The vendor "devantech" was already added to the vendor list with "[PATCH v4 1/3] iio: distance: srf08: add trivial DT binding" Signed-off-by: Andreas Klinger Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/proximity/devantech-srf04.txt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt new file mode 100644 index 000000000000..d4dc7a227e2e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt @@ -0,0 +1,28 @@ +* Devantech SRF04 ultrasonic range finder + Bit-banging driver using two GPIOs + +Required properties: + - compatible: Should be "devantech,srf04" + + - trig-gpios: Definition of the GPIO for the triggering (output) + This GPIO is set for about 10 us by the driver to tell the + device it should initiate the measurement cycle. + + - echo-gpios: Definition of the GPIO for the echo (input) + This GPIO is set by the device as soon as an ultrasonic + burst is sent out and reset when the first echo is + received. + Thus this GPIO is set while the ultrasonic waves are doing + one round trip. + It needs to be an GPIO which is able to deliver an + interrupt because the time between two interrupts is + measured in the driver. + See Documentation/devicetree/bindings/gpio/gpio.txt for + information on how to specify a consumer gpio. + +Example: +srf04@0 { + compatible = "devantech,srf04"; + trig-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + echo-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; +}; -- cgit v1.2.3 From 3e9b1222ef06d336d771ae7279a147ee965071bd Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 29 Jan 2017 11:49:26 +0100 Subject: Documentation: dt: iio: imu: st_lsm6dsx: add lsm6dsl sensor device binding Signed-off-by: Lorenzo Bianconi Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt index cf81afdf7803..048eca7a5272 100644 --- a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -3,6 +3,7 @@ Required properties: - compatible: must be one of: "st,lsm6ds3" + "st,lsm6dsl" "st,lsm6dsm" - reg: i2c address of the sensor / spi cs line -- cgit v1.2.3 From 1bf707675fb84ad02dae751a14110c17a2736a81 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 29 Jan 2017 11:49:28 +0100 Subject: Documentation: dt: iio: imu: st_lsm6dsx: add lsm6ds3h sensor device binding Signed-off-by: Lorenzo Bianconi Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt index 048eca7a5272..8305fb05ffda 100644 --- a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -3,6 +3,7 @@ Required properties: - compatible: must be one of: "st,lsm6ds3" + "st,lsm6ds3h" "st,lsm6dsl" "st,lsm6dsm" - reg: i2c address of the sensor / spi cs line -- cgit v1.2.3 From 5247dd46cf1c2e3300545ea102b866a46aaddbc3 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sat, 4 Feb 2017 02:22:50 -0800 Subject: devicetree: add documentation for MAX30102 oximeter Acked-by: Rob Herring Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/health/max30102.txt | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/health/max30102.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/health/max30102.txt b/Documentation/devicetree/bindings/iio/health/max30102.txt new file mode 100644 index 000000000000..c695e7cbeefb --- /dev/null +++ b/Documentation/devicetree/bindings/iio/health/max30102.txt @@ -0,0 +1,30 @@ +Maxim MAX30102 heart rate and pulse oximeter sensor + +* https://datasheets.maximintegrated.com/en/ds/MAX30102.pdf + +Required properties: + - compatible: must be "maxim,max30102" + - reg: the I2C address of the sensor + - interrupt-parent: should be the phandle for the interrupt controller + - interrupts: the sole interrupt generated by the device + + Refer to interrupt-controller/interrupts.txt for generic + interrupt client node bindings. + +Optional properties: + - maxim,red-led-current-microamp: configuration for RED LED current + - maxim,ir-led-current-microamp: configuration for IR LED current + + Note that each step is approximately 200 microamps, ranging from 0 uA to + 50800 uA. + +Example: + +max30100@57 { + compatible = "maxim,max30102"; + reg = <0x57>; + maxim,red-led-current-microamp = <7000>; + maxim,ir-led-current-microamp = <7000>; + interrupt-parent = <&gpio1>; + interrupts = <16 2>; +}; -- cgit v1.2.3 From a18ac95442c29caa6e1e6b7e3768de95aa19e79f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Wed, 8 Feb 2017 20:54:30 -0500 Subject: iio: Documentation: add ABI documentation for in_proximity_sampling_frequency_available Add entry for the in_proximity_sampling_frequency_available sysfs entry to the existing sampling_frequency_available ABI documentation. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 530809ccfacf..1ce46035ecab 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -55,6 +55,7 @@ Description: then it is to be found in the base device directory. What: /sys/bus/iio/devices/iio:deviceX/sampling_frequency_available +What: /sys/bus/iio/devices/iio:deviceX/in_proximity_sampling_frequency_available What: /sys/.../iio:deviceX/buffer/sampling_frequency_available What: /sys/bus/iio/devices/triggerX/sampling_frequency_available KernelVersion: 2.6.35 -- cgit v1.2.3 From 7527a6ed033dc23048e21e5e8ddee681d204dd1e Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 8 Feb 2017 09:01:35 -0500 Subject: Documentation: iio: Fix KernelVersion in counter sysfs ABI documentation The IIO counter driver support did not appear in the 4.9 version of the Linux kernel. This patch fixes the KernelVersion lines of the relevant IIO counter sysfs ABI documentation. Fixes: 1a8f324aa1f2 ("iio: Implement counter channel type and info constants") Signed-off-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 1ce46035ecab..7531d383be8c 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1594,7 +1594,7 @@ Description: can be processed to siemens per meter. What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Raw counter device counts from channel Y. For quadrature @@ -1602,7 +1602,7 @@ Description: the counts of a single quadrature signal phase from channel Y. What: /sys/bus/iio/devices/iio:deviceX/in_indexY_raw -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Raw counter device index value from channel Y. This attribute -- cgit v1.2.3 From b061f1a57c3d2e6ab0fc3988619eaab77917592b Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 8 Feb 2017 09:01:45 -0500 Subject: Documentation: iio: 104-quad-8: Fix KernelVersion in sysfs ABI documentation The ACCES 104-QUAD-8 IIO driver did not appear in the 4.9 version of the Linux kernel. This patch fixes the KernelVersion lines of the 104-QUAD-8 sysfs ABI documentation. Fixes: 28e5d3bb0325 ("iio: 104-quad-8: Add IIO support for the ACCES 104-QUAD-8") Signed-off-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-counter-104-quad-8 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 index ba676520b953..5f4924a94949 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 +++ b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 @@ -4,21 +4,21 @@ What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available What: /sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Discrete set of available values for the respective counter configuration are listed in this file. What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Read-only attribute that indicates whether the counter for channel Y is counting up or down. What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Count mode for channel Y. Four count modes are available: @@ -52,7 +52,7 @@ Description: continuously throughout. What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Read-only attribute that indicates whether excessive noise is @@ -60,14 +60,14 @@ Description: irrelevant in non-quadrature clock mode. What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: If the counter device supports preset registers, the preset count for channel Y is provided by this attribute. What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Configure channel Y counter for non-quadrature or quadrature @@ -88,7 +88,7 @@ Description: decoded for UP/DN clock. What: /sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Whether to set channel Y counter with channel Y preset value @@ -96,14 +96,14 @@ Description: Valid attribute values are boolean. What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Active level of channel Y index input; irrelevant in non-synchronous load mode. What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode -KernelVersion: 4.9 +KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org Description: Configure channel Y counter for non-synchronous or synchronous -- cgit v1.2.3 From 3af109131b7eb880ba0711344912d7a523a812b4 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 15 Feb 2017 20:31:45 +0100 Subject: iio: adc: meson-saradc: switch from polling to interrupt mode Switch from polling to interrupt mode. Successfully tested on a S905GXBB-based Odroid C2. Signed-off-by: Heiner Kallweit Acked-by: Martin Blumenstingl Tested-by: Martin Blumenstingl Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/amlogic,meson-saradc.txt | 2 + drivers/iio/adc/meson_saradc.c | 56 ++++++++++++++++++++-- 2 files changed, 53 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt index f9e3ff2c656e..047189192aec 100644 --- a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt @@ -7,6 +7,7 @@ Required properties: - "amlogic,meson-gxm-saradc" for GXM along with the generic "amlogic,meson-saradc" - reg: the physical base address and length of the registers +- interrupts: the interrupt indicating end of sampling - clocks: phandle and clock identifier (see clock-names) - clock-names: mandatory clocks: - "clkin" for the reference clock (typically XTAL) @@ -23,6 +24,7 @@ Example: compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc"; #io-channel-cells = <1>; reg = <0x0 0x8680 0x0 0x34>; + interrupts = ; clocks = <&xtal>, <&clkc CLKID_SAR_ADC>, <&clkc CLKID_SANA>, diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 89def6034f40..9f186de24cd7 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -163,6 +165,7 @@ #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) #define MESON_SAR_ADC_MAX_FIFO_SIZE 32 +#define MESON_SAR_ADC_TIMEOUT 100 /* ms */ #define MESON_SAR_ADC_CHAN(_chan) { \ .type = IIO_VOLTAGE, \ @@ -229,6 +232,7 @@ struct meson_sar_adc_priv { struct clk_gate clk_gate; struct clk *adc_div_clk; struct clk_divider clk_div; + struct completion done; }; static const struct regmap_config meson_sar_adc_regmap_config = { @@ -274,11 +278,11 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, int *val) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - int ret, regval, fifo_chan, fifo_val, sum = 0, count = 0; + int regval, fifo_chan, fifo_val, sum = 0, count = 0; - ret = meson_sar_adc_wait_busy_clear(indio_dev); - if (ret) - return ret; + if(!wait_for_completion_timeout(&priv->done, + msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT))) + return -ETIMEDOUT; while (meson_sar_adc_get_fifo_count(indio_dev) > 0 && count < MESON_SAR_ADC_MAX_FIFO_SIZE) { @@ -378,6 +382,12 @@ static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + reinit_completion(&priv->done); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE); @@ -391,6 +401,9 @@ static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, MESON_SAR_ADC_REG0_SAMPLING_STOP, MESON_SAR_ADC_REG0_SAMPLING_STOP); @@ -643,6 +656,7 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); int ret; + u32 regval; ret = meson_sar_adc_lock(indio_dev); if (ret) @@ -667,6 +681,9 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) goto err_sana_clk; } + regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_BANDGAP_EN, MESON_SAR_ADC_REG11_BANDGAP_EN); @@ -728,6 +745,25 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) return 0; } +static irqreturn_t meson_sar_adc_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + unsigned int cnt, threshold; + u32 regval; + + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + cnt = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); + threshold = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); + + if (cnt < threshold) + return IRQ_NONE; + + complete(&priv->done); + + return IRQ_HANDLED; +} + static const struct iio_info meson_sar_adc_iio_info = { .read_raw = meson_sar_adc_iio_info_read_raw, .driver_module = THIS_MODULE, @@ -770,7 +806,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) struct resource *res; void __iomem *base; const struct of_device_id *match; - int ret; + int irq, ret; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); if (!indio_dev) { @@ -779,6 +815,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) } priv = iio_priv(indio_dev); + init_completion(&priv->done); match = of_match_device(meson_sar_adc_of_match, &pdev->dev); priv->data = match->data; @@ -797,6 +834,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + return -EINVAL; + + ret = devm_request_irq(&pdev->dev, irq, meson_sar_adc_irq, IRQF_SHARED, + dev_name(&pdev->dev), indio_dev); + if (ret) + return ret; + priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, &meson_sar_adc_regmap_config); if (IS_ERR(priv->regmap)) -- cgit v1.2.3 From a70b93bcb36ffe0844b7b4ca33e5c5234f629006 Mon Sep 17 00:00:00 2001 From: Eva Rachel Retuya Date: Sat, 4 Mar 2017 16:31:23 +0800 Subject: dt-bindings: iio: accel: Document ADXL345 accelerometer binding Add the device tree binding documentation for the ADXL345 3-axis digital accelerometer. Signed-off-by: Eva Rachel Retuya Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/adxl345.txt | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/accel/adxl345.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/accel/adxl345.txt b/Documentation/devicetree/bindings/iio/accel/adxl345.txt new file mode 100644 index 000000000000..e7111b02c02c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adxl345.txt @@ -0,0 +1,38 @@ +Analog Devices ADXL345 3-Axis, +/-(2g/4g/8g/16g) Digital Accelerometer + +http://www.analog.com/en/products/mems/accelerometers/adxl345.html + +Required properties: + - compatible : should be "adi,adxl345" + - reg : the I2C address or SPI chip select number of the sensor + +Required properties for SPI bus usage: + - spi-max-frequency : set maximum clock frequency, must be 5000000 + - spi-cpol and spi-cpha : must be defined for adxl345 to enable SPI mode 3 + +Optional properties: + - interrupt-parent : phandle to the parent interrupt controller as documented + in Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + - interrupts: interrupt mapping for IRQ as documented in + Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +Example for a I2C device node: + + accelerometer@2a { + compatible = "adi,adxl345"; + reg = <0x53>; + interrupt-parent = <&gpio1>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + }; + +Example for a SPI device node: + + accelerometer@0 { + compatible = "adi,adxl345"; + reg = <0>; + spi-max-frequency = <5000000>; + spi-cpol; + spi-cpha; + interrupt-parent = <&gpio1>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + }; -- cgit v1.2.3 From 521dbebf70189bdc7ee1830ea393c8a74fd24f21 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Wed, 1 Mar 2017 15:45:05 +0100 Subject: iio: Generalize counting direction modes Move counting direction documentation for 104-quad-8 to sysfs-bus-iio to avoid duplicated for other incoming drivers Signed-off-by: Benjamin Gaignard Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 14 ++++++++++++++ Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 | 8 -------- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 7531d383be8c..8c24d0892f61 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1609,3 +1609,17 @@ Description: provides an absolute positional reference (e.g. a pulse once per revolution) which may be used to home positional systems as required. + +What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available +KernelVersion: 4.12 +Contact: linux-iio@vger.kernel.org +Description: + A list of possible counting directions which are: + - "up" : counter device is increasing. + - "down": counter device is decreasing. + +What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction +KernelVersion: 4.12 +Contact: linux-iio@vger.kernel.org +Description: + Raw counter device counters direction for channel Y. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 index 5f4924a94949..7fac2c268d9a 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 +++ b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 @@ -1,4 +1,3 @@ -What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available @@ -10,13 +9,6 @@ Description: Discrete set of available values for the respective counter configuration are listed in this file. -What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - Read-only attribute that indicates whether the counter for - channel Y is counting up or down. - What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode KernelVersion: 4.10 Contact: linux-iio@vger.kernel.org -- cgit v1.2.3 From 02b829f9e11fd9e6eeebc1e85e4d5ae641706c70 Mon Sep 17 00:00:00 2001 From: Maxime Roussin-Belanger Date: Fri, 17 Mar 2017 14:04:02 -0400 Subject: iio: dac: Add support for ltc2632 DACs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Linear Technology LTC2632 (SPI) family of· dual 12- 10-, and 8-bits output DACs. Signed-off-by: Maxime Roussin-Belanger Signed-off-by: Jean-Francois Dagenais Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/ltc2632.txt | 23 ++ drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ltc2632.c | 314 +++++++++++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/ltc2632.txt create mode 100644 drivers/iio/dac/ltc2632.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/dac/ltc2632.txt b/Documentation/devicetree/bindings/iio/dac/ltc2632.txt new file mode 100644 index 000000000000..eb911e5a8ab4 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/ltc2632.txt @@ -0,0 +1,23 @@ +Linear Technology LTC2632 DAC device driver + +Required properties: + - compatible: Has to contain one of the following: + lltc,ltc2632-l12 + lltc,ltc2632-l10 + lltc,ltc2632-l8 + lltc,ltc2632-h12 + lltc,ltc2632-h10 + lltc,ltc2632-h8 + +Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt +apply. In particular, "reg" and "spi-max-frequency" properties must be given. + +Example: + + spi_master { + dac: ltc2632@0 { + compatible = "lltc,ltc2632-l12"; + reg = <0>; /* CS0 */ + spi-max-frequency = <1000000>; + }; + }; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index d3084028905b..08f2f9037409 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -118,6 +118,16 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. +config LTC2632 + tristate "Linear Technology LTC2632-12/10/8 DAC spi driver" + depends on SPI + help + Say yes here to build support for Linear Technology + LTC2632-12, LTC2632-10, LTC2632-8 converters (DAC). + + To compile this driver as a module, choose M here: the + module will be called ltc2632. + config AD5686 tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index f01bf4a99867..6e4d99557309 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o +obj-$(CONFIG_LTC2632) += ltc2632.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX5821) += max5821.o diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c new file mode 100644 index 000000000000..ac5e05f6eb8b --- /dev/null +++ b/drivers/iio/dac/ltc2632.c @@ -0,0 +1,314 @@ +/* + * LTC2632 Digital to analog convertors spi driver + * + * Copyright 2017 Maxime Roussin-Bélanger + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include + +#define LTC2632_DAC_CHANNELS 2 + +#define LTC2632_ADDR_DAC0 0x0 +#define LTC2632_ADDR_DAC1 0x1 + +#define LTC2632_CMD_WRITE_INPUT_N 0x0 +#define LTC2632_CMD_UPDATE_DAC_N 0x1 +#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 +#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define LTC2632_CMD_POWERDOWN_DAC_N 0x4 +#define LTC2632_CMD_POWERDOWN_CHIP 0x5 +#define LTC2632_CMD_INTERNAL_REFER 0x6 +#define LTC2632_CMD_EXTERNAL_REFER 0x7 + +/** + * struct ltc2632_chip_info - chip specific information + * @channels: channel spec for the DAC + * @vref_mv: reference voltage + */ +struct ltc2632_chip_info { + const struct iio_chan_spec *channels; + const int vref_mv; +}; + +/** + * struct ltc2632_state - driver instance specific data + * @spi_dev: pointer to the spi_device struct + * @powerdown_cache_mask used to show current channel powerdown state + */ +struct ltc2632_state { + struct spi_device *spi_dev; + unsigned int powerdown_cache_mask; +}; + +enum ltc2632_supported_device_ids { + ID_LTC2632L12, + ID_LTC2632L10, + ID_LTC2632L8, + ID_LTC2632H12, + ID_LTC2632H10, + ID_LTC2632H8, +}; + +static int ltc2632_spi_write(struct spi_device *spi, + u8 cmd, u8 addr, u16 val, u8 shift) +{ + u32 data; + u8 msg[3]; + + /* + * The input shift register is 24 bits wide. + * The next four are the command bits, C3 to C0, + * followed by the 4-bit DAC address, A3 to A0, and then the + * 12-, 10-, 8-bit data-word. The data-word comprises the 12-, + * 10-, 8-bit input code followed by 4, 6, or 8 don't care bits. + */ + data = (cmd << 20) | (addr << 16) | (val << shift); + msg[0] = data >> 16; + msg[1] = data >> 8; + msg[2] = data; + + return spi_write(spi, msg, sizeof(msg)); +} + +static int ltc2632_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ltc2632_chip_info *chip_info; + + const struct ltc2632_state *st = iio_priv(indio_dev); + const struct spi_device_id *spi_dev_id = spi_get_device_id(st->spi_dev); + + chip_info = (struct ltc2632_chip_info *)spi_dev_id->driver_data; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + *val = chip_info->vref_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +static int ltc2632_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ltc2632_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + return ltc2632_spi_write(st->spi_dev, + LTC2632_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val, + chan->scan_type.shift); + default: + return -EINVAL; + } +} + +static ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ltc2632_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + !!(st->powerdown_cache_mask & (1 << chan->channel))); +} + +static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, + size_t len) +{ + bool pwr_down; + int ret; + struct ltc2632_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (pwr_down) + st->powerdown_cache_mask |= (1 << chan->channel); + else + st->powerdown_cache_mask &= ~(1 << chan->channel); + + ret = ltc2632_spi_write(st->spi_dev, + LTC2632_CMD_POWERDOWN_DAC_N, + chan->channel, 0, 0); + + return ret ? ret : len; +} + +static const struct iio_info ltc2632_info = { + .write_raw = ltc2632_write_raw, + .read_raw = ltc2632_read_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { + { + .name = "powerdown", + .read = ltc2632_read_dac_powerdown, + .write = ltc2632_write_dac_powerdown, + .shared = IIO_SEPARATE, + }, + { }, +}; + +#define LTC2632_CHANNEL(_chan, _bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (_chan), \ + .scan_type = { \ + .realbits = (_bits), \ + .shift = 16 - (_bits), \ + }, \ + .ext_info = ltc2632_ext_info, \ +} + +#define DECLARE_LTC2632_CHANNELS(_name, _bits) \ + const struct iio_chan_spec _name ## _channels[] = { \ + LTC2632_CHANNEL(0, _bits), \ + LTC2632_CHANNEL(1, _bits), \ + } + +static DECLARE_LTC2632_CHANNELS(ltc2632l12, 12); +static DECLARE_LTC2632_CHANNELS(ltc2632l10, 10); +static DECLARE_LTC2632_CHANNELS(ltc2632l8, 8); + +static DECLARE_LTC2632_CHANNELS(ltc2632h12, 12); +static DECLARE_LTC2632_CHANNELS(ltc2632h10, 10); +static DECLARE_LTC2632_CHANNELS(ltc2632h8, 8); + +static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { + [ID_LTC2632L12] = { + .channels = ltc2632l12_channels, + .vref_mv = 2500, + }, + [ID_LTC2632L10] = { + .channels = ltc2632l10_channels, + .vref_mv = 2500, + }, + [ID_LTC2632L8] = { + .channels = ltc2632l8_channels, + .vref_mv = 2500, + }, + [ID_LTC2632H12] = { + .channels = ltc2632h12_channels, + .vref_mv = 4096, + }, + [ID_LTC2632H10] = { + .channels = ltc2632h10_channels, + .vref_mv = 4096, + }, + [ID_LTC2632H8] = { + .channels = ltc2632h8_channels, + .vref_mv = 4096, + }, +}; + +static int ltc2632_probe(struct spi_device *spi) +{ + struct ltc2632_state *st; + struct iio_dev *indio_dev; + struct ltc2632_chip_info *chip_info; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + st->spi_dev = spi; + + chip_info = (struct ltc2632_chip_info *) + spi_get_device_id(spi)->driver_data; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name + : spi_get_device_id(spi)->name; + indio_dev->info = <c2632_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = LTC2632_DAC_CHANNELS; + + ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, 0, 0, 0); + if (ret) { + dev_err(&spi->dev, + "Set internal reference command failed, %d\n", ret); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ltc2632_id[] = { + { "ltc2632-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L12] }, + { "ltc2632-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L10] }, + { "ltc2632-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L8] }, + { "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] }, + { "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] }, + { "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] }, + {} +}; +MODULE_DEVICE_TABLE(spi, ltc2632_id); + +static struct spi_driver ltc2632_driver = { + .driver = { + .name = "ltc2632", + }, + .probe = ltc2632_probe, + .id_table = ltc2632_id, +}; +module_spi_driver(ltc2632_driver); + +static const struct of_device_id ltc2632_of_match[] = { + { + .compatible = "lltc,ltc2632-l12", + .data = <c2632_chip_info_tbl[ID_LTC2632L12] + }, { + .compatible = "lltc,ltc2632-l10", + .data = <c2632_chip_info_tbl[ID_LTC2632L10] + }, { + .compatible = "lltc,ltc2632-l8", + .data = <c2632_chip_info_tbl[ID_LTC2632L8] + }, { + .compatible = "lltc,ltc2632-h12", + .data = <c2632_chip_info_tbl[ID_LTC2632H12] + }, { + .compatible = "lltc,ltc2632-h10", + .data = <c2632_chip_info_tbl[ID_LTC2632H10] + }, { + .compatible = "lltc,ltc2632-h8", + .data = <c2632_chip_info_tbl[ID_LTC2632H8] + }, + {} +}; +MODULE_DEVICE_TABLE(of, ltc2632_of_match); + +MODULE_AUTHOR("Maxime Roussin-Belanger "); +MODULE_DESCRIPTION("LTC2632 DAC SPI driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 5e7f47e495ad3657e1a7264bf20fb363607161e6 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Sun, 19 Mar 2017 20:23:12 +0530 Subject: iio:light: Add support for STMicro VL6180 sensor This patch adds support for STMicro VL6180 - ALS, range and proximity sensor. Sensor is capable of measuring the light intensity as well as object distance using TOF (Time of Flight) technology. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Peter Meerwald-Stadler Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/light/vl6180.txt | 15 + drivers/iio/light/Kconfig | 10 + drivers/iio/light/Makefile | 1 + drivers/iio/light/vl6180.c | 543 +++++++++++++++++++++ 4 files changed, 569 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/vl6180.txt create mode 100644 drivers/iio/light/vl6180.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/light/vl6180.txt b/Documentation/devicetree/bindings/iio/light/vl6180.txt new file mode 100644 index 000000000000..2c52952715a0 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/vl6180.txt @@ -0,0 +1,15 @@ +STMicro VL6180 - ALS, range and proximity sensor + +Link to datasheet: http://www.st.com/resource/en/datasheet/vl6180x.pdf + +Required properties: + + -compatible: should be "st,vl6180" + -reg: the I2C address of the sensor + +Example: + +vl6180@29 { + compatible = "st,vl6180"; + reg = <0x29>; +}; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 2afcbacc0f7e..33e755d8d825 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -405,4 +405,14 @@ config VEML6070 To compile this driver as a module, choose M here: the module will be called veml6070. +config VL6180 + tristate "VL6180 ALS, range and proximity sensor" + depends on I2C + help + Say Y here if you want to build a driver for the STMicroelectronics + VL6180 combined ambient light, range and proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called vl6180. + endmenu diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index edfd69b6dc6d..681363c2b298 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o obj-$(CONFIG_VEML6070) += veml6070.o +obj-$(CONFIG_VL6180) += vl6180.o diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c new file mode 100644 index 000000000000..6e25b724d941 --- /dev/null +++ b/drivers/iio/light/vl6180.c @@ -0,0 +1,543 @@ +/* + * vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity + * sensor + * + * Copyright 2017 Peter Meerwald-Stadler + * Copyright 2017 Manivannan Sadhasivam + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * IIO driver for VL6180 (7-bit I2C slave address 0x29) + * + * Range: 0 to 100mm + * ALS: < 1 Lux up to 100 kLux + * IR: 850nm + * + * TODO: irq, threshold events, continuous mode, hardware buffer + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define VL6180_DRV_NAME "vl6180" + +/* Device identification register and value */ +#define VL6180_MODEL_ID 0x000 +#define VL6180_MODEL_ID_VAL 0xb4 + +/* Configuration registers */ +#define VL6180_INTR_CONFIG 0x014 +#define VL6180_INTR_CLEAR 0x015 +#define VL6180_OUT_OF_RESET 0x016 +#define VL6180_HOLD 0x017 +#define VL6180_RANGE_START 0x018 +#define VL6180_ALS_START 0x038 +#define VL6180_ALS_GAIN 0x03f +#define VL6180_ALS_IT 0x040 + +/* Status registers */ +#define VL6180_RANGE_STATUS 0x04d +#define VL6180_ALS_STATUS 0x04e +#define VL6180_INTR_STATUS 0x04f + +/* Result value registers */ +#define VL6180_ALS_VALUE 0x050 +#define VL6180_RANGE_VALUE 0x062 +#define VL6180_RANGE_RATE 0x066 + +/* bits of the RANGE_START and ALS_START register */ +#define VL6180_MODE_CONT BIT(1) /* continuous mode */ +#define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */ + +/* bits of the INTR_STATUS and INTR_CONFIG register */ +#define VL6180_ALS_READY BIT(5) +#define VL6180_RANGE_READY BIT(2) + +/* bits of the INTR_CLEAR register */ +#define VL6180_CLEAR_ERROR BIT(2) +#define VL6180_CLEAR_ALS BIT(1) +#define VL6180_CLEAR_RANGE BIT(0) + +/* bits of the HOLD register */ +#define VL6180_HOLD_ON BIT(0) + +/* default value for the ALS_IT register */ +#define VL6180_ALS_IT_100 0x63 /* 100 ms */ + +/* values for the ALS_GAIN register */ +#define VL6180_ALS_GAIN_1 0x46 +#define VL6180_ALS_GAIN_1_25 0x45 +#define VL6180_ALS_GAIN_1_67 0x44 +#define VL6180_ALS_GAIN_2_5 0x43 +#define VL6180_ALS_GAIN_5 0x42 +#define VL6180_ALS_GAIN_10 0x41 +#define VL6180_ALS_GAIN_20 0x40 +#define VL6180_ALS_GAIN_40 0x47 + +struct vl6180_data { + struct i2c_client *client; + struct mutex lock; +}; + +enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX }; + +/** + * struct vl6180_chan_regs - Registers for accessing channels + * @drdy_mask: Data ready bit in status register + * @start_reg: Conversion start register + * @value_reg: Result value register + * @word: Register word length + */ +struct vl6180_chan_regs { + u8 drdy_mask; + u16 start_reg, value_reg; + bool word; +}; + +static const struct vl6180_chan_regs vl6180_chan_regs_table[] = { + [VL6180_ALS] = { + .drdy_mask = VL6180_ALS_READY, + .start_reg = VL6180_ALS_START, + .value_reg = VL6180_ALS_VALUE, + .word = true, + }, + [VL6180_RANGE] = { + .drdy_mask = VL6180_RANGE_READY, + .start_reg = VL6180_RANGE_START, + .value_reg = VL6180_RANGE_VALUE, + .word = false, + }, + [VL6180_PROX] = { + .drdy_mask = VL6180_RANGE_READY, + .start_reg = VL6180_RANGE_START, + .value_reg = VL6180_RANGE_RATE, + .word = true, + }, +}; + +static int vl6180_read(struct i2c_client *client, u16 cmd, void *databuf, + u8 len) +{ + __be16 cmdbuf = cpu_to_be16(cmd); + struct i2c_msg msgs[2] = { + { .addr = client->addr, .len = sizeof(cmdbuf), .buf = (u8 *) &cmdbuf }, + { .addr = client->addr, .len = len, .buf = databuf, + .flags = I2C_M_RD } }; + int ret; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + dev_err(&client->dev, "failed reading register 0x%04x\n", cmd); + + return ret; +} + +static int vl6180_read_byte(struct i2c_client *client, u16 cmd) +{ + u8 data; + int ret; + + ret = vl6180_read(client, cmd, &data, sizeof(data)); + if (ret < 0) + return ret; + + return data; +} + +static int vl6180_read_word(struct i2c_client *client, u16 cmd) +{ + __be16 data; + int ret; + + ret = vl6180_read(client, cmd, &data, sizeof(data)); + if (ret < 0) + return ret; + + return be16_to_cpu(data); +} + +static int vl6180_write_byte(struct i2c_client *client, u16 cmd, u8 val) +{ + u8 buf[3]; + struct i2c_msg msgs[1] = { + { .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } }; + int ret; + + buf[0] = cmd >> 8; + buf[1] = cmd & 0xff; + buf[2] = val; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + dev_err(&client->dev, "failed writing register 0x%04x\n", cmd); + return ret; + } + + return 0; +} + +static int vl6180_write_word(struct i2c_client *client, u16 cmd, u16 val) +{ + __be16 buf[2]; + struct i2c_msg msgs[1] = { + { .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } }; + int ret; + + buf[0] = cpu_to_be16(cmd); + buf[1] = cpu_to_be16(val); + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + dev_err(&client->dev, "failed writing register 0x%04x\n", cmd); + return ret; + } + + return 0; +} + +static int vl6180_measure(struct vl6180_data *data, int addr) +{ + struct i2c_client *client = data->client; + int tries = 20, ret; + u16 value; + + mutex_lock(&data->lock); + /* Start single shot measurement */ + ret = vl6180_write_byte(client, + vl6180_chan_regs_table[addr].start_reg, VL6180_STARTSTOP); + if (ret < 0) + goto fail; + + while (tries--) { + ret = vl6180_read_byte(client, VL6180_INTR_STATUS); + if (ret < 0) + goto fail; + + if (ret & vl6180_chan_regs_table[addr].drdy_mask) + break; + msleep(20); + } + + if (tries < 0) { + ret = -EIO; + goto fail; + } + + /* Read result value from appropriate registers */ + ret = vl6180_chan_regs_table[addr].word ? + vl6180_read_word(client, vl6180_chan_regs_table[addr].value_reg) : + vl6180_read_byte(client, vl6180_chan_regs_table[addr].value_reg); + if (ret < 0) + goto fail; + value = ret; + + /* Clear the interrupt flag after data read */ + ret = vl6180_write_byte(client, VL6180_INTR_CLEAR, + VL6180_CLEAR_ERROR | VL6180_CLEAR_ALS | VL6180_CLEAR_RANGE); + if (ret < 0) + goto fail; + + ret = value; + +fail: + mutex_unlock(&data->lock); + + return ret; +} + +static const struct iio_chan_spec vl6180_channels[] = { + { + .type = IIO_LIGHT, + .address = VL6180_ALS, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + }, { + .type = IIO_DISTANCE, + .address = VL6180_RANGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, { + .type = IIO_PROXIMITY, + .address = VL6180_PROX, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + } +}; + +/* + * Columns 3 & 4 represent the same value in decimal and hex notations. + * Kept in order to avoid the datatype conversion while reading the + * hardware_gain. + */ +static const int vl6180_als_gain[8][4] = { + { 1, 0, 70, VL6180_ALS_GAIN_1 }, + { 1, 250000, 69, VL6180_ALS_GAIN_1_25 }, + { 1, 670000, 68, VL6180_ALS_GAIN_1_67 }, + { 2, 500000, 67, VL6180_ALS_GAIN_2_5 }, + { 5, 0, 66, VL6180_ALS_GAIN_5 }, + { 10, 0, 65, VL6180_ALS_GAIN_10 }, + { 20, 0, 64, VL6180_ALS_GAIN_20 }, + { 40, 0, 71, VL6180_ALS_GAIN_40 } +}; + +static int vl6180_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct vl6180_data *data = iio_priv(indio_dev); + int ret, i; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = vl6180_measure(data, chan->address); + if (ret < 0) + return ret; + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + ret = vl6180_read_word(data->client, VL6180_ALS_IT); + if (ret < 0) + return ret; + *val = 0; /* 1 count = 1ms (0 = 1ms) */ + *val2 = (ret + 1) * 1000; /* convert to seconds */ + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_LIGHT: + *val = 0; /* one ALS count is 0.32 Lux */ + *val2 = 320000; + break; + case IIO_DISTANCE: + *val = 0; /* sensor reports mm, scale to meter */ + *val2 = 1000; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = vl6180_read_byte(data->client, VL6180_ALS_GAIN); + if (ret < 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { + if (ret == vl6180_als_gain[i][2]) { + *val = vl6180_als_gain[i][0]; + *val2 = vl6180_als_gain[i][1]; + } + } + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR(als_gain_available, "1 1.25 1.67 2.5 5 10 20 40"); + +static struct attribute *vl6180_attributes[] = { + &iio_const_attr_als_gain_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group vl6180_attribute_group = { + .attrs = vl6180_attributes, +}; + +/* HOLD is needed before updating any config registers */ +static int vl6180_hold(struct vl6180_data *data, bool hold) +{ + return vl6180_write_byte(data->client, VL6180_HOLD, + hold ? VL6180_HOLD_ON : 0); +} + +static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { + if (val == vl6180_als_gain[i][0] && + val2 == vl6180_als_gain[i][1]) { + mutex_lock(&data->lock); + ret = vl6180_hold(data, true); + if (ret < 0) + goto fail; + ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN, + vl6180_als_gain[i][3]); +fail: + vl6180_hold(data, false); + mutex_unlock(&data->lock); + return ret; + } + } + + return -EINVAL; +} + +static int vl6180_set_it(struct vl6180_data *data, int val2) +{ + int ret; + + mutex_lock(&data->lock); + ret = vl6180_hold(data, true); + if (ret < 0) + goto fail; + ret = vl6180_write_word(data->client, VL6180_ALS_IT, + (val2 - 500) / 1000); /* write value in ms */ +fail: + vl6180_hold(data, false); + mutex_unlock(&data->lock); + + return ret; +} + +static int vl6180_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct vl6180_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val != 0 || val2 < 500 || val2 >= 512500) + return -EINVAL; + + return vl6180_set_it(data, val2); + case IIO_CHAN_INFO_HARDWAREGAIN: + if (chan->type != IIO_LIGHT) + return -EINVAL; + + return vl6180_set_als_gain(data, val, val2); + default: + return -EINVAL; + } +} + +static const struct iio_info vl6180_info = { + .read_raw = vl6180_read_raw, + .write_raw = vl6180_write_raw, + .attrs = &vl6180_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int vl6180_init(struct vl6180_data *data) +{ + struct i2c_client *client = data->client; + int ret; + + ret = vl6180_read_byte(client, VL6180_MODEL_ID); + if (ret < 0) + return ret; + + if (ret != VL6180_MODEL_ID_VAL) { + dev_err(&client->dev, "invalid model ID %02x\n", ret); + return -ENODEV; + } + + ret = vl6180_hold(data, true); + if (ret < 0) + return ret; + + ret = vl6180_read_byte(client, VL6180_OUT_OF_RESET); + if (ret < 0) + return ret; + + /* + * Detect false reset condition here. This bit is always set when the + * system comes out of reset. + */ + if (ret != 0x01) + dev_info(&client->dev, "device is not fresh out of reset\n"); + + /* Enable ALS and Range ready interrupts */ + ret = vl6180_write_byte(client, VL6180_INTR_CONFIG, + VL6180_ALS_READY | VL6180_RANGE_READY); + if (ret < 0) + return ret; + + /* ALS integration time: 100ms */ + ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100); + if (ret < 0) + return ret; + + /* ALS gain: 1 */ + ret = vl6180_write_byte(client, VL6180_ALS_GAIN, VL6180_ALS_GAIN_1); + if (ret < 0) + return ret; + + ret = vl6180_write_byte(client, VL6180_OUT_OF_RESET, 0x00); + if (ret < 0) + return ret; + + return vl6180_hold(data, false); +} + +static int vl6180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vl6180_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &vl6180_info; + indio_dev->channels = vl6180_channels; + indio_dev->num_channels = ARRAY_SIZE(vl6180_channels); + indio_dev->name = VL6180_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = vl6180_init(data); + if (ret < 0) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id vl6180_of_match[] = { + { .compatible = "st,vl6180", }, + { }, +}; +MODULE_DEVICE_TABLE(of, vl6180_of_match); + +static const struct i2c_device_id vl6180_id[] = { + { "vl6180", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vl6180_id); + +static struct i2c_driver vl6180_driver = { + .driver = { + .name = VL6180_DRV_NAME, + .of_match_table = of_match_ptr(vl6180_of_match), + }, + .probe = vl6180_probe, + .id_table = vl6180_id, +}; + +module_i2c_driver(vl6180_driver); + +MODULE_AUTHOR("Peter Meerwald-Stadler "); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_DESCRIPTION("STMicro VL6180 ALS, range and proximity sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 25ec249632d505192789e15dcafed5a9cdb1283e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 23 Mar 2017 20:38:42 -0700 Subject: iio: adc: cpcap: Add minimal support for CPCAP PMIC ADC On Motorola phones like droid 4 there is a custom CPCAP PMIC. This PMIC has ADCs that are used for battery charging and USB PHY VBUS and ID pin detection. Unfortunately the only documentation for this ADC seems to be the Motorola mapphone Linux kernel tree. I have tested that reading raw and scaled values works, but I have not used the timed sampling that the ADC seems to support. Let's add a minimal support for it so we can eventually provide IIO channels for the related battery charging and USB PHY drivers. Cc: devicetree@vger.kernel.org Cc: Marcel Partap Cc: Michael Scott Cc: Sebastian Reichel Signed-off-by: Tony Lindgren Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/cpcap-adc.txt | 18 + drivers/iio/adc/Kconfig | 11 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/cpcap-adc.c | 1007 ++++++++++++++++++++ 4 files changed, 1037 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt create mode 100644 drivers/iio/adc/cpcap-adc.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt b/Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt new file mode 100644 index 000000000000..487ea966858e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt @@ -0,0 +1,18 @@ +Motorola CPCAP PMIC ADC binding + +Required properties: +- compatible: Should be "motorola,cpcap-adc" or "motorola,mapphone-cpcap-adc" +- interrupt-parent: The interrupt controller +- interrupts: The interrupt number for the ADC device +- interrupt-names: Should be "adcdone" +- #io-channel-cells: Number of cells in an IIO specifier + +Example: + +cpcap_adc: adc { + compatible = "motorola,mapphone-cpcap-adc"; + interrupt-parent = <&cpcap>; + interrupts = <8 IRQ_TYPE_NONE>; + interrupt-names = "adcdone"; + #io-channel-cells = <1>; +}; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2268a6fb9865..b369a4397d90 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -195,6 +195,17 @@ config CC10001_ADC This driver can also be built as a module. If so, the module will be called cc10001_adc. +config CPCAP_ADC + tristate "Motorola CPCAP PMIC ADC driver" + depends on MFD_CPCAP + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Motorola CPCAP PMIC ADC. + + This driver can also be built as a module. If so, the module will be + called cpcap-adc. + config DA9150_GPADC tristate "Dialog DA9150 GPADC driver support" depends on MFD_DA9150 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 73dbe399f894..26ce71bdeb67 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o +obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c new file mode 100644 index 000000000000..62d37f8725b8 --- /dev/null +++ b/drivers/iio/adc/cpcap-adc.c @@ -0,0 +1,1007 @@ +/* + * Copyright (C) 2017 Tony Lindgren + * + * Rewritten for Linux IIO framework with some code based on + * earlier driver found in the Motorola Linux kernel: + * + * Copyright (C) 2009-2010 Motorola, Inc. + * + * 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. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Register CPCAP_REG_ADCC1 bits */ +#define CPCAP_BIT_ADEN_AUTO_CLR BIT(15) /* Currently unused */ +#define CPCAP_BIT_CAL_MODE BIT(14) /* Set with BIT_RAND0 */ +#define CPCAP_BIT_ADC_CLK_SEL1 BIT(13) /* Currently unused */ +#define CPCAP_BIT_ADC_CLK_SEL0 BIT(12) /* Currently unused */ +#define CPCAP_BIT_ATOX BIT(11) +#define CPCAP_BIT_ATO3 BIT(10) +#define CPCAP_BIT_ATO2 BIT(9) +#define CPCAP_BIT_ATO1 BIT(8) +#define CPCAP_BIT_ATO0 BIT(7) +#define CPCAP_BIT_ADA2 BIT(6) +#define CPCAP_BIT_ADA1 BIT(5) +#define CPCAP_BIT_ADA0 BIT(4) +#define CPCAP_BIT_AD_SEL1 BIT(3) /* Set for bank1 */ +#define CPCAP_BIT_RAND1 BIT(2) /* Set for channel 16 & 17 */ +#define CPCAP_BIT_RAND0 BIT(1) /* Set with CAL_MODE */ +#define CPCAP_BIT_ADEN BIT(0) /* Currently unused */ + +/* Register CPCAP_REG_ADCC2 bits */ +#define CPCAP_BIT_CAL_FACTOR_ENABLE BIT(15) /* Currently unused */ +#define CPCAP_BIT_BATDETB_EN BIT(14) /* Currently unused */ +#define CPCAP_BIT_ADTRIG_ONESHOT BIT(13) /* Set for !TIMING_IMM */ +#define CPCAP_BIT_ASC BIT(12) /* Set for TIMING_IMM */ +#define CPCAP_BIT_ATOX_PS_FACTOR BIT(11) +#define CPCAP_BIT_ADC_PS_FACTOR1 BIT(10) +#define CPCAP_BIT_ADC_PS_FACTOR0 BIT(9) +#define CPCAP_BIT_AD4_SELECT BIT(8) /* Currently unused */ +#define CPCAP_BIT_ADC_BUSY BIT(7) /* Currently unused */ +#define CPCAP_BIT_THERMBIAS_EN BIT(6) /* Currently unused */ +#define CPCAP_BIT_ADTRIG_DIS BIT(5) /* Disable interrupt */ +#define CPCAP_BIT_LIADC BIT(4) /* Currently unused */ +#define CPCAP_BIT_TS_REFEN BIT(3) /* Currently unused */ +#define CPCAP_BIT_TS_M2 BIT(2) /* Currently unused */ +#define CPCAP_BIT_TS_M1 BIT(1) /* Currently unused */ +#define CPCAP_BIT_TS_M0 BIT(0) /* Currently unused */ + +#define CPCAP_MAX_TEMP_LVL 27 +#define CPCAP_FOUR_POINT_TWO_ADC 801 +#define ST_ADC_CAL_CHRGI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_CHRGI_LOW_THRESHOLD 494 +#define ST_ADC_CAL_BATTI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_BATTI_LOW_THRESHOLD 494 +#define ST_ADC_CALIBRATE_DIFF_THRESHOLD 3 + +#define CPCAP_ADC_MAX_RETRIES 5 /* Calibration and quirk */ + +/** + * struct cpcap_adc_ato - timing settings for cpcap adc + * + * Unfortunately no cpcap documentation available, please document when + * using these. + */ +struct cpcap_adc_ato { + unsigned short ato_in; + unsigned short atox_in; + unsigned short adc_ps_factor_in; + unsigned short atox_ps_factor_in; + unsigned short ato_out; + unsigned short atox_out; + unsigned short adc_ps_factor_out; + unsigned short atox_ps_factor_out; +}; + +/** + * struct cpcap-adc - cpcap adc device driver data + * @reg: cpcap regmap + * @dev: struct device + * @vendor: cpcap vendor + * @irq: interrupt + * @lock: mutex + * @ato: request timings + * @wq_data_avail: work queue + * @done: work done + */ +struct cpcap_adc { + struct regmap *reg; + struct device *dev; + u16 vendor; + int irq; + struct mutex lock; /* ADC register access lock */ + const struct cpcap_adc_ato *ato; + wait_queue_head_t wq_data_avail; + bool done; +}; + +/** + * enum cpcap_adc_channel - cpcap adc channels + */ +enum cpcap_adc_channel { + /* Bank0 channels */ + CPCAP_ADC_AD0_BATTDETB, /* Battery detection */ + CPCAP_ADC_BATTP, /* Battery voltage */ + CPCAP_ADC_VBUS, /* USB VBUS voltage */ + CPCAP_ADC_AD3, /* Battery temperature when charging */ + CPCAP_ADC_BPLUS_AD4, /* Another battery or system voltage */ + CPCAP_ADC_CHG_ISENSE, /* Calibrated charge current */ + CPCAP_ADC_BATTI, /* Calibrated system current */ + CPCAP_ADC_USB_ID, /* USB OTG ID, unused on droid 4? */ + + /* Bank1 channels */ + CPCAP_ADC_AD8, /* Seems unused */ + CPCAP_ADC_AD9, /* Seems unused */ + CPCAP_ADC_LICELL, /* Maybe system voltage? Always 3V */ + CPCAP_ADC_HV_BATTP, /* Another battery detection? */ + CPCAP_ADC_TSX1_AD12, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSX2_AD13, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY1_AD14, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY2_AD15, /* Seems unused, for touchscreen? */ + + /* Remuxed channels using bank0 entries */ + CPCAP_ADC_BATTP_PI16, /* Alternative mux mode for BATTP */ + CPCAP_ADC_BATTI_PI17, /* Alternative mux mode for BATTI */ + + CPCAP_ADC_CHANNEL_NUM, +}; + +/** + * enum cpcap_adc_timing - cpcap adc timing options + * + * CPCAP_ADC_TIMING_IMM seems to be immediate with no timings. + * Please document when using. + */ +enum cpcap_adc_timing { + CPCAP_ADC_TIMING_IMM, + CPCAP_ADC_TIMING_IN, + CPCAP_ADC_TIMING_OUT, +}; + +/** + * struct cpcap_adc_phasing_tbl - cpcap phasing table + * @offset: offset in the phasing table + * @multiplier: multiplier in the phasing table + * @divider: divider in the phasing table + * @min: minimum value + * @max: maximum value + */ +struct cpcap_adc_phasing_tbl { + short offset; + unsigned short multiplier; + unsigned short divider; + short min; + short max; +}; + +/** + * struct cpcap_adc_conversion_tbl - cpcap conversion table + * @conv_type: conversion type + * @align_offset: align offset + * @conv_offset: conversion offset + * @cal_offset: calibration offset + * @multiplier: conversion multiplier + * @divider: conversion divider + */ +struct cpcap_adc_conversion_tbl { + enum iio_chan_info_enum conv_type; + int align_offset; + int conv_offset; + int cal_offset; + int multiplier; + int divider; +}; + +/** + * struct cpcap_adc_request - cpcap adc request + * @channel: request channel + * @phase_tbl: channel phasing table + * @conv_tbl: channel conversion table + * @bank_index: channel index within the bank + * @timing: timing settings + * @result: result + */ +struct cpcap_adc_request { + int channel; + const struct cpcap_adc_phasing_tbl *phase_tbl; + const struct cpcap_adc_conversion_tbl *conv_tbl; + int bank_index; + enum cpcap_adc_timing timing; + int result; +}; + +/* Phasing table for channels. Note that channels 16 & 17 use BATTP and BATTI */ +static const struct cpcap_adc_phasing_tbl bank_phasing[] = { + /* Bank0 */ + [CPCAP_ADC_AD0_BATTDETB] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_BATTI] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023}, + + /* Bank1 */ + [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023}, +}; + +/* + * Conversion table for channels. Updated during init based on calibration. + * Here too channels 16 & 17 use BATTP and BATTI. + */ +static struct cpcap_adc_conversion_tbl bank_conversion[] = { + /* Bank0 */ + [CPCAP_ADC_AD0_BATTDETB] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BATTP] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_VBUS] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 10000, 1023, + }, + [CPCAP_ADC_AD3] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BPLUS_AD4] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_CHG_ISENSE] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_BATTI] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_USB_ID] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + + /* Bank1 */ + [CPCAP_ADC_AD8] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_AD9] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_LICELL] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 3400, 1023, + }, + [CPCAP_ADC_HV_BATTP] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX1_AD12] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX2_AD13] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY1_AD14] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY2_AD15] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, +}; + +/* + * Temperature lookup table of register values to milliCelcius. + * REVISIT: Check the duplicate 0x3ff entry in a freezer + */ +static const int temp_map[CPCAP_MAX_TEMP_LVL][2] = { + { 0x03ff, -40000 }, + { 0x03ff, -35000 }, + { 0x03ef, -30000 }, + { 0x03b2, -25000 }, + { 0x036c, -20000 }, + { 0x0320, -15000 }, + { 0x02d0, -10000 }, + { 0x027f, -5000 }, + { 0x022f, 0 }, + { 0x01e4, 5000 }, + { 0x019f, 10000 }, + { 0x0161, 15000 }, + { 0x012b, 20000 }, + { 0x00fc, 25000 }, + { 0x00d4, 30000 }, + { 0x00b2, 35000 }, + { 0x0095, 40000 }, + { 0x007d, 45000 }, + { 0x0069, 50000 }, + { 0x0059, 55000 }, + { 0x004b, 60000 }, + { 0x003f, 65000 }, + { 0x0036, 70000 }, + { 0x002e, 75000 }, + { 0x0027, 80000 }, + { 0x0022, 85000 }, + { 0x001d, 90000 }, +}; + +#define CPCAP_CHAN(_type, _index, _address, _datasheet_name) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 10, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .datasheet_name = (_datasheet_name), \ +} + +/* + * The datasheet names are from Motorola mapphone Linux kernel except + * for the last two which might be uncalibrated charge voltage and + * current. + */ +static const struct iio_chan_spec cpcap_adc_channels[] = { + /* Bank0 */ + CPCAP_CHAN(IIO_TEMP, 0, CPCAP_REG_ADCD0, "battdetb"), + CPCAP_CHAN(IIO_VOLTAGE, 1, CPCAP_REG_ADCD1, "battp"), + CPCAP_CHAN(IIO_VOLTAGE, 2, CPCAP_REG_ADCD2, "vbus"), + CPCAP_CHAN(IIO_TEMP, 3, CPCAP_REG_ADCD3, "ad3"), + CPCAP_CHAN(IIO_VOLTAGE, 4, CPCAP_REG_ADCD4, "ad4"), + CPCAP_CHAN(IIO_CURRENT, 5, CPCAP_REG_ADCD5, "chg_isense"), + CPCAP_CHAN(IIO_CURRENT, 6, CPCAP_REG_ADCD6, "batti"), + CPCAP_CHAN(IIO_VOLTAGE, 7, CPCAP_REG_ADCD7, "usb_id"), + + /* Bank1 */ + CPCAP_CHAN(IIO_CURRENT, 8, CPCAP_REG_ADCD0, "ad8"), + CPCAP_CHAN(IIO_VOLTAGE, 9, CPCAP_REG_ADCD1, "ad9"), + CPCAP_CHAN(IIO_VOLTAGE, 10, CPCAP_REG_ADCD2, "licell"), + CPCAP_CHAN(IIO_VOLTAGE, 11, CPCAP_REG_ADCD3, "hv_battp"), + CPCAP_CHAN(IIO_VOLTAGE, 12, CPCAP_REG_ADCD4, "tsx1_ad12"), + CPCAP_CHAN(IIO_VOLTAGE, 13, CPCAP_REG_ADCD5, "tsx2_ad13"), + CPCAP_CHAN(IIO_VOLTAGE, 14, CPCAP_REG_ADCD6, "tsy1_ad14"), + CPCAP_CHAN(IIO_VOLTAGE, 15, CPCAP_REG_ADCD7, "tsy2_ad15"), + + /* There are two registers with multiplexed functionality */ + CPCAP_CHAN(IIO_VOLTAGE, 16, CPCAP_REG_ADCD0, "chg_vsense"), + CPCAP_CHAN(IIO_CURRENT, 17, CPCAP_REG_ADCD1, "batti2"), +}; + +static irqreturn_t cpcap_adc_irq_thread(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct cpcap_adc *ddata = iio_priv(indio_dev); + int error; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return IRQ_NONE; + + ddata->done = true; + wake_up_interruptible(&ddata->wq_data_avail); + + return IRQ_HANDLED; +} + +/* ADC calibration functions */ +static void cpcap_adc_setup_calibrate(struct cpcap_adc *ddata, + enum cpcap_adc_channel chan) +{ + unsigned int value = 0; + unsigned long timeout = jiffies + msecs_to_jiffies(3000); + int error; + + if ((chan != CPCAP_ADC_CHG_ISENSE) && + (chan != CPCAP_ADC_BATTI)) + return; + + value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0; + value |= ((chan << 4) & + (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0)); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0, + 0); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + if (error) + return; + + do { + schedule_timeout_uninterruptible(1); + error = regmap_read(ddata->reg, CPCAP_REG_ADCC2, &value); + if (error) + return; + } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout)); + + if (value & CPCAP_BIT_ASC) + dev_err(ddata->dev, + "Timeout waiting for calibration to complete\n"); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE, 0); + if (error) + return; +} + +static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata, + int channel, + u16 calibration_register, + int lower_threshold, + int upper_threshold) +{ + unsigned int calibration_data[2]; + unsigned short cal_data_diff; + int i, error; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + calibration_data[0] = 0; + calibration_data[1] = 0; + cal_data_diff = 0; + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[0]); + if (error) + return error; + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[1]); + if (error) + return error; + + if (calibration_data[0] > calibration_data[1]) + cal_data_diff = + calibration_data[0] - calibration_data[1]; + else + cal_data_diff = + calibration_data[1] - calibration_data[0]; + + if (((calibration_data[1] >= lower_threshold) && + (calibration_data[1] <= upper_threshold) && + (cal_data_diff <= ST_ADC_CALIBRATE_DIFF_THRESHOLD)) || + (ddata->vendor == CPCAP_VENDOR_TI)) { + bank_conversion[channel].cal_offset = + ((short)calibration_data[1] * -1) + 512; + dev_dbg(ddata->dev, "ch%i calibration complete: %i\n", + channel, bank_conversion[channel].cal_offset); + break; + } + usleep_range(5000, 10000); + } + + return 0; +} + +static int cpcap_adc_calibrate(struct cpcap_adc *ddata) +{ + int error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_CHG_ISENSE, + CPCAP_REG_ADCAL1, + ST_ADC_CAL_CHRGI_LOW_THRESHOLD, + ST_ADC_CAL_CHRGI_HIGH_THRESHOLD); + if (error) + return error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_BATTI, + CPCAP_REG_ADCAL2, + ST_ADC_CAL_BATTI_LOW_THRESHOLD, + ST_ADC_CAL_BATTI_HIGH_THRESHOLD); + if (error) + return error; + + return 0; +} + +/* ADC setup, read and scale functions */ +static void cpcap_adc_setup_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + const struct cpcap_adc_ato *ato = ddata->ato; + unsigned short value1 = 0; + unsigned short value2 = 0; + int error; + + if (!ato) + return; + + switch (req->channel) { + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + value1 |= CPCAP_BIT_AD_SEL1; + break; + case CPCAP_ADC_BATTP_PI16 ... CPCAP_ADC_BATTI_PI17: + value1 |= CPCAP_BIT_RAND1; + default: + break; + } + + switch (req->timing) { + case CPCAP_ADC_TIMING_IN: + value1 |= ato->ato_in; + value1 |= ato->atox_in; + value2 |= ato->adc_ps_factor_in; + value2 |= ato->atox_ps_factor_in; + break; + case CPCAP_ADC_TIMING_OUT: + value1 |= ato->ato_out; + value1 |= ato->atox_out; + value2 |= ato->adc_ps_factor_out; + value2 |= ato->atox_ps_factor_out; + break; + + case CPCAP_ADC_TIMING_IMM: + default: + break; + } + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value1); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0, + value2); + if (error) + return; + + if (req->timing == CPCAP_ADC_TIMING_IMM) { + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + if (error) + return; + } else { + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_ONESHOT, + CPCAP_BIT_ADTRIG_ONESHOT); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, 0); + if (error) + return; + } +} + +/* + * Occasionally the ADC does not seem to start and there will be no + * interrupt. Let's re-init interrupt to prevent the ADC from hanging + * for the next request. It is unclear why this happens, but the next + * request will usually work after doing this. + */ +static void cpcap_adc_quirk_reset_lost_irq(struct cpcap_adc *ddata) +{ + int error; + + dev_info(ddata->dev, "lost ADC irq, attempting to reinit\n"); + disable_irq(ddata->irq); + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + dev_warn(ddata->dev, "%s reset failed: %i\n", + __func__, error); + enable_irq(ddata->irq); +} + +static int cpcap_adc_start_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int i, error; + + req->timing = CPCAP_ADC_TIMING_IMM; + ddata->done = false; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + cpcap_adc_setup_bank(ddata, req); + error = wait_event_interruptible_timeout(ddata->wq_data_avail, + ddata->done, + msecs_to_jiffies(50)); + if (error > 0) + return 0; + + if (error == 0) { + cpcap_adc_quirk_reset_lost_irq(ddata); + error = -ETIMEDOUT; + continue; + } + + if (error < 0) + return error; + } + + return error; +} + +static void cpcap_adc_phase(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + const struct cpcap_adc_phasing_tbl *phase_tbl = req->phase_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP: + case CPCAP_ADC_BATTP_PI16: + index = req->bank_index; + req->result -= phase_tbl[index].offset; + req->result -= CPCAP_FOUR_POINT_TWO_ADC; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += CPCAP_FOUR_POINT_TWO_ADC; + break; + case CPCAP_ADC_BATTI_PI17: + index = req->bank_index; + /* fallthrough */ + default: + req->result += conv_tbl[index].cal_offset; + req->result += conv_tbl[index].align_offset; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += phase_tbl[index].offset; + break; + } + + if (req->result < phase_tbl[index].min) + req->result = phase_tbl[index].min; + else if (req->result > phase_tbl[index].max) + req->result = phase_tbl[index].max; +} + +/* Looks up temperatures in a table and calculates averages if needed */ +static int cpcap_adc_table_to_millicelcius(unsigned short value) +{ + int i, result = 0, alpha; + + if (value <= temp_map[CPCAP_MAX_TEMP_LVL - 1][0]) + return temp_map[CPCAP_MAX_TEMP_LVL - 1][1]; + + if (value >= temp_map[0][0]) + return temp_map[0][1]; + + for (i = 0; i < CPCAP_MAX_TEMP_LVL - 1; i++) { + if ((value <= temp_map[i][0]) && + (value >= temp_map[i + 1][0])) { + if (value == temp_map[i][0]) { + result = temp_map[i][1]; + } else if (value == temp_map[i + 1][0]) { + result = temp_map[i + 1][1]; + } else { + alpha = ((value - temp_map[i][0]) * 1000) / + (temp_map[i + 1][0] - temp_map[i][0]); + + result = temp_map[i][1] + + ((alpha * (temp_map[i + 1][1] - + temp_map[i][1])) / 1000); + } + break; + } + } + + return result; +} + +static void cpcap_adc_convert(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP_PI16: + index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + index = CPCAP_ADC_BATTI; + break; + default: + break; + } + + /* No conversion for raw channels */ + if (conv_tbl[index].conv_type == IIO_CHAN_INFO_RAW) + return; + + /* Temperatures use a lookup table instead of conversion table */ + if ((req->channel == CPCAP_ADC_AD0_BATTDETB) || + (req->channel == CPCAP_ADC_AD3)) { + req->result = + cpcap_adc_table_to_millicelcius(req->result); + + return; + } + + /* All processed channels use a conversion table */ + req->result *= conv_tbl[index].multiplier; + if (conv_tbl[index].divider == 0) + return; + req->result /= conv_tbl[index].divider; + req->result += conv_tbl[index].conv_offset; +} + +/* + * REVISIT: Check if timed sampling can use multiple channels at the + * same time. If not, replace channel_mask with just channel. + */ +static int cpcap_adc_read_bank_scaled(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int calibration_data, error, addr; + + if (ddata->vendor == CPCAP_VENDOR_TI) { + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL1, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset = + ((short)calibration_data * -1) + 512; + + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL2, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_BATTI].cal_offset = + ((short)calibration_data * -1) + 512; + } + + addr = CPCAP_REG_ADCD0 + req->bank_index * 4; + + error = regmap_read(ddata->reg, addr, &req->result); + if (error) + return error; + + req->result &= 0x3ff; + cpcap_adc_phase(req); + cpcap_adc_convert(req); + + return 0; +} + +static int cpcap_adc_init_request(struct cpcap_adc_request *req, + int channel) +{ + req->channel = channel; + req->phase_tbl = bank_phasing; + req->conv_tbl = bank_conversion; + + switch (channel) { + case CPCAP_ADC_AD0_BATTDETB ... CPCAP_ADC_USB_ID: + req->bank_index = channel; + break; + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + req->bank_index = channel - 8; + break; + case CPCAP_ADC_BATTP_PI16: + req->bank_index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + req->bank_index = CPCAP_ADC_BATTI; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_adc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cpcap_adc *ddata = iio_priv(indio_dev); + struct cpcap_adc_request req; + int error; + + error = cpcap_adc_init_request(&req, chan->channel); + if (error) + return error; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + error = regmap_read(ddata->reg, chan->address, val); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + break; + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + error = cpcap_adc_read_bank_scaled(ddata, &req); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + *val = req.result; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + +err_unlock: + mutex_unlock(&ddata->lock); + dev_err(ddata->dev, "error reading ADC: %i\n", error); + + return error; +} + +static const struct iio_info cpcap_adc_info = { + .read_raw = &cpcap_adc_read, + .driver_module = THIS_MODULE, +}; + +/* + * Configuration for Motorola mapphone series such as droid 4. + * Copied from the Motorola mapphone kernel tree. + */ +static const struct cpcap_adc_ato mapphone_adc = { + .ato_in = 0x0480, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0, + .atox_out = 0, + .adc_ps_factor_out = 0, + .atox_ps_factor_out = 0, +}; + +static const struct of_device_id cpcap_adc_id_table[] = { + { + .compatible = "motorola,cpcap-adc", + }, + { + .compatible = "motorola,mapphone-cpcap-adc", + .data = &mapphone_adc, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cpcap_adc_id_table); + +static int cpcap_adc_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct cpcap_adc *ddata; + struct iio_dev *indio_dev; + int error; + + match = of_match_device(of_match_ptr(cpcap_adc_id_table), + &pdev->dev); + if (!match) + return -EINVAL; + + if (!match->data) { + dev_err(&pdev->dev, "no configuration data found\n"); + + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ddata)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + + return -ENOMEM; + } + ddata = iio_priv(indio_dev); + ddata->ato = match->data; + ddata->dev = &pdev->dev; + + mutex_init(&ddata->lock); + init_waitqueue_head(&ddata->wq_data_avail); + + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->channels = cpcap_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(cpcap_adc_channels); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &cpcap_adc_info; + + ddata->reg = dev_get_regmap(pdev->dev.parent, NULL); + if (!ddata->reg) + return -ENODEV; + + error = cpcap_get_vendor(ddata->dev, ddata->reg, &ddata->vendor); + if (error) + return error; + + platform_set_drvdata(pdev, indio_dev); + + ddata->irq = platform_get_irq_byname(pdev, "adcdone"); + if (!ddata->irq) + return -ENODEV; + + error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL, + cpcap_adc_irq_thread, + IRQF_TRIGGER_NONE, + "cpcap-adc", indio_dev); + if (error) { + dev_err(&pdev->dev, "could not get irq: %i\n", + error); + + return error; + } + + error = cpcap_adc_calibrate(ddata); + if (error) + return error; + + dev_info(&pdev->dev, "CPCAP ADC device probed\n"); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static struct platform_driver cpcap_adc_driver = { + .driver = { + .name = "cpcap_adc", + .of_match_table = of_match_ptr(cpcap_adc_id_table), + }, + .probe = cpcap_adc_probe, +}; + +module_platform_driver(cpcap_adc_driver); + +MODULE_ALIAS("platform:cpcap_adc"); +MODULE_DESCRIPTION("CPCAP ADC driver"); +MODULE_AUTHOR("Tony Lindgren Date: Tue, 28 Mar 2017 14:52:58 -0700 Subject: Documentation: dt-bindings: Document bindings for Aspeed ADC Signed-off-by: Rick Altherr Acked-by: Joel Stanley Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/aspeed_adc.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt b/Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt new file mode 100644 index 000000000000..674e133b7cd7 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt @@ -0,0 +1,20 @@ +Aspeed ADC + +This device is a 10-bit converter for 16 voltage channels. All inputs are +single ended. + +Required properties: +- compatible: Should be "aspeed,ast2400-adc" or "aspeed,ast2500-adc" +- reg: memory window mapping address and length +- clocks: Input clock used to derive the sample clock. Expected to be the + SoC's APB clock. +- #io-channel-cells: Must be set to <1> to indicate channels are selected + by index. + +Example: + adc@1e6e9000 { + compatible = "aspeed,ast2400-adc"; + reg = <0x1e6e9000 0xb0>; + clocks = <&clk_apb>; + #io-channel-cells = <1>; + }; -- cgit v1.2.3 From c49089b71de354eaeef2a2f075d6d53dec86e165 Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Mon, 27 Mar 2017 17:40:46 +0800 Subject: dt-bindings: iio: rockchip-saradc: add support for rk3328 The rk3328 saradc is the same as rk3399. Signed-off-by: Liang Chen Reviewed-by: Heiko Stuebner Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt index 205593f56fe7..e0a9b9d6d6fd 100644 --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt @@ -4,6 +4,7 @@ Required properties: - compatible: should be "rockchip,-saradc" or "rockchip,rk3066-tsadc" - "rockchip,saradc": for rk3188, rk3288 - "rockchip,rk3066-tsadc": for rk3036 + - "rockchip,rk3328-saradc", "rockchip,rk3399-saradc": for rk3328 - "rockchip,rk3399-saradc": for rk3399 - reg: physical base address of the controller and length of memory mapped -- cgit v1.2.3 From 3cc17e61d4dd9594fc6a2d15d88bb91f4bc61e75 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Mar 2017 12:10:59 +0100 Subject: iio:imu:mpu6050 update i2c bindings to reflect i2c-gate and supported parts These bindings are somewhat lagging the state of the driver. The i2c-gate bindings are documented elsewhere, but it seems sensible to at least have an example and cross reference in here. SPI bindings will need to be the subject of a future patch. Signed-off-by: Jonathan Cameron Acked-by: Rob Herring --- .../devicetree/bindings/iio/imu/inv_mpu6050.txt | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index a9fc11e43b45..e28cb3abb343 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -3,14 +3,20 @@ InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device http://www.invensense.com/mems/gyro/mpu6050.html Required properties: - - compatible : should be "invensense,mpu6050" + - compatible : should be one of + "invensense,mpu6050" + "invensense,mpu6500" + "invensense,mpu9150" + "invensense,icm20608" - reg : the I2C address of the sensor - interrupt-parent : should be the phandle for the interrupt controller - interrupts : interrupt mapping for GPIO IRQ Optional properties: - mount-matrix: an optional 3x3 mounting rotation matrix - + - i2c-gate node. These devices also support an auxiliary i2c bus. This is + simple enough to be described using the i2c-gate binding. See + i2c/i2c-gate.txt for more details. Example: mpu6050@68 { @@ -28,3 +34,19 @@ Example: "0", /* y2 */ "0.984807753012208"; /* z2 */ }; + + + mpu9250@68 { + compatible = "invensense,mpu9250"; + reg = <0x68>; + interrupt-parent = <&gpio3>; + interrupts = <21 1>; + i2c-gate { + #address-cells = <1>; + #size-cells = <0>; + ax8975@c { + compatible = "ak,ak8975"; + reg = <0x0c>; + }; + }; + }; -- cgit v1.2.3 From 0c8f492dd7ddf0dd381fa63b76066c0966d8dd73 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Mar 2017 12:11:00 +0100 Subject: iio:imu:mpu6050 add explicit mpu9250 support The mpu9250 is a SIP containing an mpu6500 and an ak8975. If this was all there was too it there would be no need for explicit handling in the driver. Arguably the bindings would also only reflect the presence of an mpu6500 with the ak8975 hanging off it, as the kernel doesn't care that they are in one package. However, the WHOAMI value changes as well so best to add explicit support. Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt | 1 + drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 6 ++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 5 +++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2 ++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 1 + 5 files changed, 15 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index e28cb3abb343..2b4514592f83 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -7,6 +7,7 @@ Required properties: "invensense,mpu6050" "invensense,mpu6500" "invensense,mpu9150" + "invensense,mpu9250" "invensense,icm20608" - reg : the I2C address of the sensor - interrupt-parent : should be the phandle for the interrupt controller diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index b9fcbf18aa99..96dabbd2f004 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -113,6 +113,12 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6050, .config = &chip_config_6050, }, + { + .whoami = INV_MPU9250_WHOAMI_VALUE, + .name = "MPU9250", + .reg = ®_set_6500, + .config = &chip_config_6050, + }, { .whoami = INV_ICM20608_WHOAMI_VALUE, .name = "ICM20608", diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index a8e6330cb906..64b5f5b92200 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -178,6 +178,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"mpu6050", INV_MPU6050}, {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, + {"mpu9250", INV_MPU9250}, {"icm20608", INV_ICM20608}, {} }; @@ -197,6 +198,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,mpu9150", .data = (void *)INV_MPU9150 }, + { + .compatible = "invensense,mpu9250", + .data = (void *)INV_MPU9250 + }, { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index f0e8c5dd9fae..ef13de7a2c20 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -70,6 +70,7 @@ enum inv_devices { INV_MPU6500, INV_MPU6000, INV_MPU9150, + INV_MPU9250, INV_ICM20608, INV_NUM_PARTS }; @@ -226,6 +227,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_WHOAMI_VALUE 0x68 #define INV_MPU6500_WHOAMI_VALUE 0x70 #define INV_MPU9150_WHOAMI_VALUE 0x68 +#define INV_MPU9250_WHOAMI_VALUE 0x71 #define INV_ICM20608_WHOAMI_VALUE 0xAF /* scan element definition */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 6e6476dfa188..74506e5ac0db 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -82,6 +82,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"mpu6000", INV_MPU6000}, {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, + {"mpu9250", INV_MPU9250}, {"icm20608", INV_ICM20608}, {} }; -- cgit v1.2.3 From bc82222fcca1ec3702caf98eb4a3ab0edfa6f789 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 29 Mar 2017 11:42:58 +0200 Subject: iio:adc: Driver for Linear Technology LTC2497 ADC This patch adds support for the Analog Devices / Linear Technology LTC2497 ADCs. The LTC2497 is a 16-channel (eight differential), 16-bit, high precision, delta-sigma ADC with an automatic, differential, input current cancellation front end and a 2-wire, I2C interface. Signed-off-by: Michael Hennerich Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/ltc2497.txt | 13 + MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2497.c | 279 +++++++++++++++++++++ 5 files changed, 304 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ltc2497.txt create mode 100644 drivers/iio/adc/ltc2497.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/ltc2497.txt b/Documentation/devicetree/bindings/iio/adc/ltc2497.txt new file mode 100644 index 000000000000..a237ed99c0d8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ltc2497.txt @@ -0,0 +1,13 @@ +* Linear Technology / Analog Devices LTC2497 ADC + +Required properties: + - compatible: Must be "lltc,ltc2497" + - reg: Must contain the ADC I2C address + - vref-supply: The regulator supply for ADC reference voltage + +Example: + ltc2497: adc@76 { + compatible = "lltc,ltc2497"; + reg = <0x76>; + vref-supply = <<c2497_reg>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index a7d6f9a4c951..173043cc582f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -813,6 +813,7 @@ W: http://wiki.analog.com/ W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/iio/*/ad* +F: drivers/iio/adc/ltc2497* X: drivers/iio/*/adjd* F: drivers/staging/iio/*/ad* F: drivers/staging/iio/trigger/iio-trig-bfin-timer.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 38c76ea683ea..79b04693a5d1 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -348,6 +348,16 @@ config LTC2485 To compile this driver as a module, choose M here: the module will be called ltc2485. +config LTC2497 + tristate "Linear Technology LTC2497 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2497 + 16-Bit 8-/16-Channel Delta Sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2497. + config MAX1027 tristate "Maxim max1027 ADC driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index fd8e2a9f9e8b..3d9174ab26c8 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o obj-$(CONFIG_LTC2485) += ltc2485.o +obj-$(CONFIG_LTC2497) += ltc2497.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX11100) += max11100.o obj-$(CONFIG_MAX1363) += max1363.o diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c new file mode 100644 index 000000000000..2691b10023f5 --- /dev/null +++ b/drivers/iio/adc/ltc2497.c @@ -0,0 +1,279 @@ +/* + * ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC + * + * Copyright (C) 2017 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LTC2497_ENABLE 0xA0 +#define LTC2497_SGL BIT(4) +#define LTC2497_DIFF 0 +#define LTC2497_SIGN BIT(3) +#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE +#define LTC2497_CONVERSION_TIME_MS 150ULL + +struct ltc2497_st { + struct i2c_client *client; + struct regulator *ref; + ktime_t time_prev; + u8 addr_prev; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be32 buf ____cacheline_aligned; +}; + +static int ltc2497_wait_conv(struct ltc2497_st *st) +{ + s64 time_elapsed; + + time_elapsed = ktime_ms_delta(ktime_get(), st->time_prev); + + if (time_elapsed < LTC2497_CONVERSION_TIME_MS) { + /* delay if conversion time not passed + * since last read or write + */ + if (msleep_interruptible( + LTC2497_CONVERSION_TIME_MS - time_elapsed)) + return -ERESTARTSYS; + + return 0; + } + + if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) { + /* We're in automatic mode - + * so the last reading is stil not outdated + */ + return 0; + } + + return 1; +} + +static int ltc2497_read(struct ltc2497_st *st, u8 address, int *val) +{ + struct i2c_client *client = st->client; + int ret; + + ret = ltc2497_wait_conv(st); + if (ret < 0) + return ret; + + if (ret || st->addr_prev != address) { + ret = i2c_smbus_write_byte(st->client, + LTC2497_ENABLE | address); + if (ret < 0) + return ret; + st->addr_prev = address; + if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS)) + return -ERESTARTSYS; + } + ret = i2c_master_recv(client, (char *)&st->buf, 3); + if (ret < 0) { + dev_err(&client->dev, "i2c_master_recv failed\n"); + return ret; + } + st->time_prev = ktime_get(); + + /* convert and shift the result, + * and finally convert from offset binary to signed integer + */ + *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); + + return ret; +} + +static int ltc2497_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2497_st *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = ltc2497_read(st, chan->address, val); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(st->ref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = 17; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +#define LTC2497_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +#define LTC2497_CHAN_DIFF(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \ + .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\ + .address = (_addr | _chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .differential = 1, \ +} + +static const struct iio_chan_spec ltc2497_channel[] = { + LTC2497_CHAN(0, LTC2497_SGL), + LTC2497_CHAN(1, LTC2497_SGL), + LTC2497_CHAN(2, LTC2497_SGL), + LTC2497_CHAN(3, LTC2497_SGL), + LTC2497_CHAN(4, LTC2497_SGL), + LTC2497_CHAN(5, LTC2497_SGL), + LTC2497_CHAN(6, LTC2497_SGL), + LTC2497_CHAN(7, LTC2497_SGL), + LTC2497_CHAN(8, LTC2497_SGL), + LTC2497_CHAN(9, LTC2497_SGL), + LTC2497_CHAN(10, LTC2497_SGL), + LTC2497_CHAN(11, LTC2497_SGL), + LTC2497_CHAN(12, LTC2497_SGL), + LTC2497_CHAN(13, LTC2497_SGL), + LTC2497_CHAN(14, LTC2497_SGL), + LTC2497_CHAN(15, LTC2497_SGL), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN), +}; + +static const struct iio_info ltc2497_info = { + .read_raw = ltc2497_read_raw, + .driver_module = THIS_MODULE, +}; + +static int ltc2497_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ltc2497_st *st; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + st->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->info = <c2497_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2497_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel); + + st->ref = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(st->ref)) + return PTR_ERR(st->ref); + + ret = regulator_enable(st->ref); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT); + if (ret < 0) + goto err_regulator_disable; + + st->addr_prev = LTC2497_CONFIG_DEFAULT; + st->time_prev = ktime_get(); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto err_regulator_disable; + + return 0; + +err_regulator_disable: + regulator_disable(st->ref); + + return ret; +} + +static int ltc2497_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ltc2497_st *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(st->ref); + + return 0; +} + +static const struct i2c_device_id ltc2497_id[] = { + { "ltc2497", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2497_id); + +static const struct of_device_id ltc2497_of_match[] = { + { .compatible = "lltc,ltc2497", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltc2497_of_match); + +static struct i2c_driver ltc2497_driver = { + .driver = { + .name = "ltc2497", + .of_match_table = of_match_ptr(ltc2497_of_match), + }, + .probe = ltc2497_probe, + .remove = ltc2497_remove, + .id_table = ltc2497_id, +}; +module_i2c_driver(ltc2497_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 021087309d2801d6d94bbd6be7e3f8297edb3190 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Fri, 31 Mar 2017 14:32:37 +0200 Subject: dt-bindings: iio: stm32-adc: add option to set resolution Add documentation for 'assigned-resolution-bits' dt optional property. Signed-off-by: Fabrice Gasnier Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt index 5dfc88ec24a4..e35f9f1b3200 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt @@ -57,6 +57,9 @@ Optional properties: - dmas: Phandle to dma channel for this ADC instance. See ../../dma/dma.txt for details. - dma-names: Must be "rx" when dmas property is being used. +- assigned-resolution-bits: Resolution (bits) to use for conversions. Must + match device available resolutions (e.g. can be 6, 8, 10 or 12 on stm32f4). + Default is maximum resolution if unset. Example: adc: adc@40012000 { @@ -84,6 +87,7 @@ Example: st,adc-channels = <8>; dmas = <&dma2 0 0 0x400 0x0>; dma-names = "rx"; + assigned-resolution-bits = <8>; }; ... other adc child nodes follow... -- cgit v1.2.3 From 2a70d55029e467faaab6659f21d0e0cdf27576cf Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 6 Apr 2017 16:20:51 +0200 Subject: dt-bindings: iio: adc: Add max9611 ADC Add device tree bindings documentation for Maxim max9611/max9612 current sense amplifier. Signed-off-by: Jacopo Mondi Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/max9611.txt | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/max9611.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/max9611.txt b/Documentation/devicetree/bindings/iio/adc/max9611.txt new file mode 100644 index 000000000000..ab4f43145ae5 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/max9611.txt @@ -0,0 +1,27 @@ +* Maxim max9611/max9612 current sense amplifier with 12-bits ADC interface + +Maxim max9611/max9612 is an high-side current sense amplifier with integrated +12-bits ADC communicating over I2c bus. +The device node for this driver shall be a child of a I2c controller. + +Required properties + - compatible: Should be "maxim,max9611" or "maxim,max9612" + - reg: The 7-bits long I2c address of the device + - shunt-resistor-micro-ohms: Value, in micro Ohms, of the current sense shunt + resistor + +Example: + +&i2c4 { + csa: adc@7c { + compatible = "maxim,max9611"; + reg = <0x7c>; + + shunt-resistor-micro-ohms = <5000>; + }; +}; + +This device node describes a current sense amplifier sitting on I2c4 bus +with address 0x7c (read address is 0xf9, write address is 0xf8). +A sense resistor of 0,005 Ohm is installed between RS+ and RS- current-sensing +inputs. -- cgit v1.2.3 From 3fed601c1c5730a7d8a508b80f9902a7332786dc Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 6 Apr 2017 16:20:52 +0200 Subject: iio: Documentation: Add max9611 sysfs documentation Add documentation for max9611 driver. Document attributes describing value of shunt resistor installed between RS+ and RS- voltage sense inputs. Signed-off-by: Jacopo Mondi Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 b/Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 new file mode 100644 index 000000000000..6d2d2b094941 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-max9611 @@ -0,0 +1,17 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_power_shunt_resistor +Date: March 2017 +KernelVersion: 4.12 +Contact: linux-iio@vger.kernel.org +Description: The value of the shunt resistor used to compute power drain on + common input voltage pin (RS+). In Ohms. + +What: /sys/bus/iio/devices/iio:deviceX/in_current_shunt_resistor +Date: March 2017 +KernelVersion: 4.12 +Contact: linux-iio@vger.kernel.org +Description: The value of the shunt resistor used to compute current flowing + between RS+ and RS- voltage sense inputs. In Ohms. + +These attributes describe a single physical component, exposed as two distinct +attributes as it is used to calculate two different values: power load and +current flowing between RS+ and RS- inputs. -- cgit v1.2.3 From 96b3c833035ee3e591361c1b424d3ae87241133c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 4 Apr 2017 14:08:17 +0200 Subject: iio: xoadc: augment DT bindings a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to accommodate in a logical manner for the premuxed channels in PM8921 and the similarly addressed channels in later PMICs, we need a twocell arrangement with premux and analog mux setting as a tuple to uniquely identify a hardware channel. These bindings are not yet in use, so it should be fine to augment them before we actually start using it in drivers and device trees. This scheme came out of lengthy discussions and reverse-engineering and reading of the few information sources we have. Cc: devicetree@vger.kernel.org Acked-by: Rob Herring Suggested-by: Björn Andersson Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/qcom,pm8xxx-xoadc.txt | 76 ++++++++++++---------- 1 file changed, 42 insertions(+), 34 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt index 53cd146d8096..3ae06127789e 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt +++ b/Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt @@ -19,32 +19,42 @@ Required properties: with PMIC variant but is typically something like 2.2 or 1.8V. The following required properties are standard for IO channels, see -iio-bindings.txt for more details: +iio-bindings.txt for more details, but notice that this particular +ADC has a special addressing scheme that require two cells for +identifying each ADC channel: -- #address-cells: should be set to <1> +- #address-cells: should be set to <2>, the first cell is the + prescaler (on PM8058) or premux (on PM8921) with two valid bits + so legal values are 0x00, 0x01 or 0x02. The second cell + is the main analog mux setting (0x00..0x0f). The combination + of prescaler/premux and analog mux uniquely addresses a hardware + channel on all systems. - #size-cells: should be set to <0> -- #io-channel-cells: should be set to <1> +- #io-channel-cells: should be set to <2>, again the cells are + precaler or premux followed by the analog muxing line. - interrupts: should refer to the parent PMIC interrupt controller and reference the proper ADC interrupt. Required subnodes: -The ADC channels are configured as subnodes of the ADC. Since some of -them are used for calibrating the ADC, these nodes are compulsory: +The ADC channels are configured as subnodes of the ADC. + +Since some of them are used for calibrating the ADC, these nodes are +compulsory: adc-channel@c { - reg = <0x0c>; + reg = <0x00 0x0c>; }; adc-channel@d { - reg = <0x0d>; + reg = <0x00 0x0d>; }; adc-channel@f { - reg = <0x0f>; + reg = <0x00 0x0f>; }; These three nodes are used for absolute and ratiometric calibration @@ -52,13 +62,13 @@ and only need to have these reg values: they are by hardware definition 1:1 ratio converters that sample 625, 1250 and 0 milliV and create an interpolation calibration for all other ADCs. -Optional subnodes: any channels other than channel 0x0c, 0x0d and -0x0f are optional. +Optional subnodes: any channels other than channels [0x00 0x0c], +[0x00 0x0d] and [0x00 0x0f] are optional. Required channel node properties: - reg: should contain the hardware channel number in the range - 0 .. 0x0f (4 bits). The hardware only supports 16 channels. + 0 .. 0xff (8 bits). Optional channel node properties: @@ -94,56 +104,54 @@ Example: xoadc: xoadc@197 { compatible = "qcom,pm8058-adc"; reg = <0x197>; - interrupt-parent = <&pm8058>; - interrupts = <76 1>; - #address-cells = <1>; + interrupts-extended = <&pm8058 76 IRQ_TYPE_EDGE_RISING>; + #address-cells = <2>; #size-cells = <0>; - #io-channel-cells = <1>; + #io-channel-cells = <2>; vcoin: adc-channel@0 { - reg = <0x00>; + reg = <0x00 0x00>; }; vbat: adc-channel@1 { - reg = <0x01>; + reg = <0x00 0x01>; }; dcin: adc-channel@2 { - reg = <0x02>; + reg = <0x00 0x02>; }; ichg: adc-channel@3 { - reg = <0x03>; + reg = <0x00 0x03>; }; vph_pwr: adc-channel@4 { - reg = <0x04>; + reg = <0x00 0x04>; }; usb_vbus: adc-channel@a { - reg = <0x0a>; + reg = <0x00 0x0a>; }; die_temp: adc-channel@b { - reg = <0x0b>; + reg = <0x00 0x0b>; }; ref_625mv: adc-channel@c { - reg = <0x0c>; + reg = <0x00 0x0c>; }; ref_1250mv: adc-channel@d { - reg = <0x0d>; + reg = <0x00 0x0d>; }; ref_325mv: adc-channel@e { - reg = <0x0e>; + reg = <0x00 0x0e>; }; ref_muxoff: adc-channel@f { - reg = <0x0f>; + reg = <0x00 0x0f>; }; }; - /* IIO client node */ iio-hwmon { compatible = "iio-hwmon"; - io-channels = <&xoadc 0x01>, /* Battery */ - <&xoadc 0x02>, /* DC in (charger) */ - <&xoadc 0x04>, /* VPH the main system voltage */ - <&xoadc 0x0b>, /* Die temperature */ - <&xoadc 0x0c>, /* Reference voltage 1.25V */ - <&xoadc 0x0d>, /* Reference voltage 0.625V */ - <&xoadc 0x0e>; /* Reference voltage 0.325V */ + io-channels = <&xoadc 0x00 0x01>, /* Battery */ + <&xoadc 0x00 0x02>, /* DC in (charger) */ + <&xoadc 0x00 0x04>, /* VPH the main system voltage */ + <&xoadc 0x00 0x0b>, /* Die temperature */ + <&xoadc 0x00 0x0c>, /* Reference voltage 1.25V */ + <&xoadc 0x00 0x0d>, /* Reference voltage 0.625V */ + <&xoadc 0x00 0x0e>; /* Reference voltage 0.325V */ }; -- cgit v1.2.3 From 4adec7da0536a345d901d7ba55b6c93a14eeeaff Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 4 Apr 2017 09:47:51 +0200 Subject: iio: stm32 trigger: Add quadrature encoder device One of the features of STM32 trigger hardware block is a quadrature encoder that can counts up/down depending of the levels and edges of the selected external pins. This patch allow to read/write the counter, get it direction, set/get quadrature modes and get scale factor. When counting up preset value is the limit of the counter. When counting down the counter start from preset value down to 0. This preset value could be set/get by using /sys/bus/iio/devices/iio:deviceX/in_count0_preset attribute. Signed-off-by: Benjamin Gaignard Reviewed-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-timer-stm32 | 46 +++- drivers/iio/trigger/stm32-timer-trigger.c | 244 ++++++++++++++++++++- include/linux/mfd/stm32-timers.h | 2 + 3 files changed, 282 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 index 6534a60037ff..a55ad44446c5 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 @@ -3,11 +3,15 @@ KernelVersion: 4.11 Contact: benjamin.gaignard@st.com Description: Reading returns the list possible master modes which are: - - "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO). - - "enable" : The Counter Enable signal CNT_EN is used as trigger output. + - "reset" : The UG bit from the TIMx_EGR register is + used as trigger output (TRGO). + - "enable" : The Counter Enable signal CNT_EN is used + as trigger output. - "update" : The update event is selected as trigger output. - For instance a master timer can then be used as a prescaler for a slave timer. - - "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set. + For instance a master timer can then be used + as a prescaler for a slave timer. + - "compare_pulse" : The trigger output send a positive pulse + when the CC1IF flag is to be set. - "OC1REF" : OC1REF signal is used as trigger output. - "OC2REF" : OC2REF signal is used as trigger output. - "OC3REF" : OC3REF signal is used as trigger output. @@ -27,3 +31,37 @@ Description: Reading returns the current sampling frequency. Writing an value different of 0 set and start sampling. Writing 0 stop sampling. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_preset +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the current preset value. + Writing sets the preset value. + When counting up the counter starts from 0 and fires an + event when reach preset value. + When counting down the counter start from preset value + and fire event when reach 0. + +What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the list possible quadrature modes. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Configure the device counter quadrature modes: + channel_A: + Encoder A input servers as the count input and B as + the UP/DOWN direction control input. + + channel_B: + Encoder B input serves as the count input and A as + the UP/DOWN direction control input. + + quadrature: + Encoder A and B inputs are mixed to get direction + and count with a scale of 0.25. diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 994b96d19750..7db904cae926 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -15,6 +15,7 @@ #include #define MAX_TRIGGERS 6 +#define MAX_VALIDS 5 /* List the triggers created by each timer */ static const void *triggers_table[][MAX_TRIGGERS] = { @@ -32,12 +33,29 @@ static const void *triggers_table[][MAX_TRIGGERS] = { { TIM12_TRGO, TIM12_CH1, TIM12_CH2,}, }; +/* List the triggers accepted by each timer */ +static const void *valids_table[][MAX_VALIDS] = { + { TIM5_TRGO, TIM2_TRGO, TIM3_TRGO, TIM4_TRGO,}, + { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,}, + { TIM1_TRGO, TIM2_TRGO, TIM5_TRGO, TIM4_TRGO,}, + { TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,}, + { TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,}, + { }, /* timer 6 */ + { }, /* timer 7 */ + { TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,}, + { TIM2_TRGO, TIM3_TRGO,}, + { }, /* timer 10 */ + { }, /* timer 11 */ + { TIM4_TRGO, TIM5_TRGO,}, +}; + struct stm32_timer_trigger { struct device *dev; struct regmap *regmap; struct clk *clk; u32 max_arr; const void *triggers; + const void *valids; }; static int stm32_timer_start(struct stm32_timer_trigger *priv, @@ -180,8 +198,7 @@ static ssize_t stm32_tt_show_master_mode(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct stm32_timer_trigger *priv = iio_priv(indio_dev); + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); u32 cr2; regmap_read(priv->regmap, TIM_CR2, &cr2); @@ -194,8 +211,7 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct stm32_timer_trigger *priv = iio_priv(indio_dev); + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); int i; for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) { @@ -275,6 +291,216 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) return 0; } +static int stm32_counter_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + { + u32 cnt; + + regmap_read(priv->regmap, TIM_CNT, &cnt); + *val = cnt; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + { + u32 smcr; + + regmap_read(priv->regmap, TIM_SMCR, &smcr); + smcr &= TIM_SMCR_SMS; + + *val = 1; + *val2 = 0; + + /* in quadrature case scale = 0.25 */ + if (smcr == 3) + *val2 = 2; + + return IIO_VAL_FRACTIONAL_LOG2; + } + } + + return -EINVAL; +} + +static int stm32_counter_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + regmap_write(priv->regmap, TIM_CNT, val); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* fixed scale */ + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info stm32_trigger_info = { + .driver_module = THIS_MODULE, + .read_raw = stm32_counter_read_raw, + .write_raw = stm32_counter_write_raw +}; + +static const char *const stm32_quadrature_modes[] = { + "channel_A", + "channel_B", + "quadrature", +}; + +static int stm32_set_quadrature_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, mode + 1); + + return 0; +} + +static int stm32_get_quadrature_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 smcr; + + regmap_read(priv->regmap, TIM_SMCR, &smcr); + smcr &= TIM_SMCR_SMS; + + return smcr - 1; +} + +static const struct iio_enum stm32_quadrature_mode_enum = { + .items = stm32_quadrature_modes, + .num_items = ARRAY_SIZE(stm32_quadrature_modes), + .set = stm32_set_quadrature_mode, + .get = stm32_get_quadrature_mode +}; + +static const char *const stm32_count_direction_states[] = { + "up", + "down" +}; + +static int stm32_set_count_direction(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, mode); + + return 0; +} + +static int stm32_get_count_direction(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 cr1; + + regmap_read(priv->regmap, TIM_CR1, &cr1); + + return (cr1 & TIM_CR1_DIR); +} + +static const struct iio_enum stm32_count_direction_enum = { + .items = stm32_count_direction_states, + .num_items = ARRAY_SIZE(stm32_count_direction_states), + .set = stm32_set_count_direction, + .get = stm32_get_count_direction +}; + +static ssize_t stm32_count_get_preset(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 arr; + + regmap_read(priv->regmap, TIM_ARR, &arr); + + return snprintf(buf, PAGE_SIZE, "%u\n", arr); +} + +static ssize_t stm32_count_set_preset(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + unsigned int preset; + int ret; + + ret = kstrtouint(buf, 0, &preset); + if (ret) + return ret; + + regmap_write(priv->regmap, TIM_ARR, preset); + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + + return len; +} + +static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { + { + .name = "preset", + .shared = IIO_SEPARATE, + .read = stm32_count_get_preset, + .write = stm32_count_set_preset + }, + IIO_ENUM("count_direction", IIO_SEPARATE, &stm32_count_direction_enum), + IIO_ENUM_AVAILABLE("count_direction", &stm32_count_direction_enum), + IIO_ENUM("quadrature_mode", IIO_SEPARATE, &stm32_quadrature_mode_enum), + IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_quadrature_mode_enum), + {} +}; + +static const struct iio_chan_spec stm32_trigger_channel = { + .type = IIO_COUNT, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .ext_info = stm32_trigger_count_info, + .indexed = 1 +}; + +static struct stm32_timer_trigger *stm32_setup_counter_device(struct device *dev) +{ + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, + sizeof(struct stm32_timer_trigger)); + if (!indio_dev) + return NULL; + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->info = &stm32_trigger_info; + indio_dev->num_channels = 1; + indio_dev->channels = &stm32_trigger_channel; + indio_dev->dev.of_node = dev->of_node; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return NULL; + + return iio_priv(indio_dev); +} + /** * is_stm32_timer_trigger * @trig: trigger to be checked @@ -299,10 +525,15 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) if (of_property_read_u32(dev->of_node, "reg", &index)) return -EINVAL; - if (index >= ARRAY_SIZE(triggers_table)) + if (index >= ARRAY_SIZE(triggers_table) || + index >= ARRAY_SIZE(valids_table)) return -EINVAL; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + /* Create an IIO device only if we have triggers to be validated */ + if (*valids_table[index]) + priv = stm32_setup_counter_device(dev); + else + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -312,6 +543,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; priv->triggers = triggers_table[index]; + priv->valids = valids_table[index]; ret = stm32_setup_iio_triggers(priv); if (ret) diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index d0300045f04a..4a0abbc10ef6 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -21,6 +21,7 @@ #define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ #define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ #define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_CNT 0x24 /* Counter */ #define TIM_PSC 0x28 /* Prescaler */ #define TIM_ARR 0x2c /* Auto-Reload Register */ #define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ @@ -30,6 +31,7 @@ #define TIM_BDTR 0x44 /* Break and Dead-Time Reg */ #define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_DIR BIT(4) /* Counter Direction */ #define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ #define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ #define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ -- cgit v1.2.3 From 2a830a45ffb05d1aa8d7866235501f635678bec6 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 4 Apr 2017 09:47:52 +0200 Subject: iio: stm32 trigger: Add counter enable modes Device counting could be controlled by the level or the edges of a trigger. in_count0_enable_mode attibute allow to set the control mode. Signed-off-by: Benjamin Gaignard Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-timer-stm32 | 25 ++++++++ drivers/iio/trigger/stm32-timer-trigger.c | 70 ++++++++++++++++++++++ 2 files changed, 95 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 index a55ad44446c5..230020e06677 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 @@ -65,3 +65,28 @@ Description: quadrature: Encoder A and B inputs are mixed to get direction and count with a scale of 0.25. + +What: /sys/bus/iio/devices/iio:deviceX/in_count_enable_mode_available +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the list possible enable modes. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_enable_mode +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Configure the device counter enable modes, in all case + counting direction is set by in_count0_count_direction + attribute and the counter is clocked by the internal clock. + always: + Counter is always ON. + + gated: + Counting is enabled when connected trigger signal + level is high else counting is disabled. + + triggered: + Counting is enabled on rising edge of the connected + trigger, and remains enabled for the duration of this + selected mode. diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 7db904cae926..0f1a2cf334bf 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -353,6 +353,74 @@ static const struct iio_info stm32_trigger_info = { .write_raw = stm32_counter_write_raw }; +static const char *const stm32_enable_modes[] = { + "always", + "gated", + "triggered", +}; + +static int stm32_enable_mode2sms(int mode) +{ + switch (mode) { + case 0: + return 0; + case 1: + return 5; + case 2: + return 6; + } + + return -EINVAL; +} + +static int stm32_set_enable_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + int sms = stm32_enable_mode2sms(mode); + + if (sms < 0) + return sms; + + regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); + + return 0; +} + +static int stm32_sms2enable_mode(int mode) +{ + switch (mode) { + case 0: + return 0; + case 5: + return 1; + case 6: + return 2; + } + + return -EINVAL; +} + +static int stm32_get_enable_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 smcr; + + regmap_read(priv->regmap, TIM_SMCR, &smcr); + smcr &= TIM_SMCR_SMS; + + return stm32_sms2enable_mode(smcr); +} + +static const struct iio_enum stm32_enable_mode_enum = { + .items = stm32_enable_modes, + .num_items = ARRAY_SIZE(stm32_enable_modes), + .set = stm32_set_enable_mode, + .get = stm32_get_enable_mode +}; + static const char *const stm32_quadrature_modes[] = { "channel_A", "channel_B", @@ -466,6 +534,8 @@ static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { IIO_ENUM_AVAILABLE("count_direction", &stm32_count_direction_enum), IIO_ENUM("quadrature_mode", IIO_SEPARATE, &stm32_quadrature_mode_enum), IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_quadrature_mode_enum), + IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum), + IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum), {} }; -- cgit v1.2.3 From a9e9c7153e96ee7f2777b3d5d99a23bf74dcaa21 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 29 Mar 2017 01:34:48 +0900 Subject: iio: adc: add max1117/max1118/max1119 ADC driver This adds max1117/max1118/max1119 8-bit, dual-channel ADC driver. This new driver uses the zero length spi_transfers with the cs_change flag set and/or the non-zero delay_usecs. 1. The zero length transfer with the spi_transfer.cs_change set is required in order to select CH1. The chip select line must be brought high and low again without transfer. 2. The zero length transfer with the spi_transfer.delay_usecs > 0 is required for waiting the conversion to be complete. The conversion begins with the falling edge of the chip select. During the conversion process, SCLK is ignored. These two usages are unusual. But the spi controller drivers that use a default implementation of transfer_one_message() are likely to work. (I've tested this adc driver with spi-omap2-mcspi and spi-xilinx) On the other hand, some spi controller drivers that have their own transfer_one_message() may not work. But at least for the zero length transfer with delay_usecs > 0, I'm proposing a new testcase for the spi-loopback-test that can test whether the delay_usecs setting has taken effect. Acked-by: Rob Herring Signed-off-by: Akinobu Mita Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Rob Herring Cc: Mark Rutland Cc: Mark Brown Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/max1118.txt | 21 ++ drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/max1118.c | 307 +++++++++++++++++++++ 4 files changed, 341 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/max1118.txt create mode 100644 drivers/iio/adc/max1118.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/max1118.txt b/Documentation/devicetree/bindings/iio/adc/max1118.txt new file mode 100644 index 000000000000..cf33d0b15a6d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/max1118.txt @@ -0,0 +1,21 @@ +* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs + +Required properties: + - compatible: Should be one of + * "maxim,max1117" + * "maxim,max1118" + * "maxim,max1119" + - reg: spi chip select number for the device + - (max1118 only) vref-supply: The regulator supply for ADC reference voltage + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: +adc@0 { + compatible = "maxim,max1118"; + reg = <0>; + vref-supply = <&vdd_supply>; + spi-max-frequency = <1000000>; +}; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 385fb732dade..0b8915298bf1 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -379,6 +379,18 @@ config MAX11100 To compile this driver as a module, choose M here: the module will be called max11100. +config MAX1118 + tristate "Maxim max1117/max1118/max1119 ADCs driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Maxim max1117/max1118/max1119 + 8-bit, dual-channel ADCs. + + To compile this driver as a module, choose M here: the module will be + called max1118. + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 78a7fd84cc59..56e5fabece4c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_LTC2497) += ltc2497.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX11100) += max11100.o +obj-$(CONFIG_MAX1118) += max1118.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c new file mode 100644 index 000000000000..2e9648a078c4 --- /dev/null +++ b/drivers/iio/adc/max1118.c @@ -0,0 +1,307 @@ +/* + * MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver + * + * Copyright (c) 2017 Akinobu Mita + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf + * + * SPI interface connections + * + * SPI MAXIM + * Master Direction MAX1117/8/9 + * ------ --------- ----------- + * nCS --> CNVST + * SCK --> SCLK + * MISO <-- DOUT + * ------ --------- ----------- + */ + +#include +#include +#include +#include +#include +#include +#include + +enum max1118_id { + max1117, + max1118, + max1119, +}; + +struct max1118 { + struct spi_device *spi; + struct mutex lock; + struct regulator *reg; + + u8 data ____cacheline_aligned; +}; + +#define MAX1118_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = ch, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ + } + +static const struct iio_chan_spec max1118_channels[] = { + MAX1118_CHANNEL(0), + MAX1118_CHANNEL(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static int max1118_read(struct spi_device *spi, int channel) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max1118 *adc = iio_priv(indio_dev); + struct spi_transfer xfers[] = { + /* + * To select CH1 for conversion, CNVST pin must be brought high + * and low for a second time. + */ + { + .len = 0, + .delay_usecs = 1, /* > CNVST Low Time 100 ns */ + .cs_change = 1, + }, + /* + * The acquisition interval begins with the falling edge of + * CNVST. The total acquisition and conversion process takes + * <7.5us. + */ + { + .len = 0, + .delay_usecs = 8, + }, + { + .rx_buf = &adc->data, + .len = 1, + }, + }; + int ret; + + if (channel == 0) + ret = spi_sync_transfer(spi, xfers + 1, 2); + else + ret = spi_sync_transfer(spi, xfers, 3); + + if (ret) + return ret; + + return adc->data; +} + +static int max1118_get_vref_mV(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max1118 *adc = iio_priv(indio_dev); + const struct spi_device_id *id = spi_get_device_id(spi); + int vref_uV; + + switch (id->driver_data) { + case max1117: + return 2048; + case max1119: + return 4096; + case max1118: + vref_uV = regulator_get_voltage(adc->reg); + if (vref_uV < 0) + return vref_uV; + return vref_uV / 1000; + } + + return -ENODEV; +} + +static int max1118_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max1118 *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *val = max1118_read(adc->spi, chan->channel); + mutex_unlock(&adc->lock); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = max1118_get_vref_mV(adc->spi); + if (*val < 0) + return *val; + *val2 = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info max1118_info = { + .read_raw = max1118_read_raw, + .driver_module = THIS_MODULE, +}; + +static irqreturn_t max1118_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1118 *adc = iio_priv(indio_dev); + u8 data[16] = { }; /* 2x 8-bit ADC data + padding + 8 bytes timestamp */ + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + int ret = max1118_read(adc->spi, scan_chan->channel); + + if (ret < 0) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + + data[i] = ret; + i++; + } + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int max1118_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max1118 *adc; + const struct spi_device_id *id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + if (id->driver_data == max1118) { + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) { + dev_err(&spi->dev, "failed to get vref regulator\n"); + return PTR_ERR(adc->reg); + } + ret = regulator_enable(adc->reg); + if (ret) + return ret; + } + + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &max1118_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max1118_channels; + indio_dev->num_channels = ARRAY_SIZE(max1118_channels); + + /* + * To reinitiate a conversion on CH0, it is necessary to allow for a + * conversion to be complete and all of the data to be read out. Once + * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go + * into AutoShutdown mode until the next conversion is initiated. + */ + max1118_read(spi, 0); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + max1118_trigger_handler, NULL); + if (ret) + goto err_reg_disable; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_buffer_cleanup; + + return 0; + +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +err_reg_disable: + if (id->driver_data == max1118) + regulator_disable(adc->reg); + + return ret; +} + +static int max1118_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max1118 *adc = iio_priv(indio_dev); + const struct spi_device_id *id = spi_get_device_id(spi); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (id->driver_data == max1118) + return regulator_disable(adc->reg); + + return 0; +} + +static const struct spi_device_id max1118_id[] = { + { "max1117", max1117 }, + { "max1118", max1118 }, + { "max1119", max1119 }, + {} +}; +MODULE_DEVICE_TABLE(spi, max1118_id); + +#ifdef CONFIG_OF + +static const struct of_device_id max1118_dt_ids[] = { + { .compatible = "maxim,max1117" }, + { .compatible = "maxim,max1118" }, + { .compatible = "maxim,max1119" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max1118_dt_ids); + +#endif + +static struct spi_driver max1118_spi_driver = { + .driver = { + .name = "max1118", + .of_match_table = of_match_ptr(max1118_dt_ids), + }, + .probe = max1118_probe, + .remove = max1118_remove, + .id_table = max1118_id, +}; +module_spi_driver(max1118_spi_driver); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 48428d17325291f5be8c0420efca7e312587d9ad Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 10 Apr 2017 17:49:49 +0200 Subject: dt-bindings: iio: stm32-dac: Add support for STM32 DAC Document STMicroelectronics STM32 DAC (digital-to-analog converter). Signed-off-by: Fabrice Gasnier Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/st,stm32-dac.txt | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt b/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt new file mode 100644 index 000000000000..bcee71f808d0 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt @@ -0,0 +1,61 @@ +STMicroelectronics STM32 DAC + +The STM32 DAC is a 12-bit voltage output digital-to-analog converter. The DAC +may be configured in 8 or 12-bit mode. It has two output channels, each with +its own converter. +It has built-in noise and triangle waveform generator and supports external +triggers for conversions. The DAC's output buffer allows a high drive output +current. + +Contents of a stm32 dac root node: +----------------------------------- +Required properties: +- compatible: Must be "st,stm32h7-dac-core". +- reg: Offset and length of the device's register set. +- clocks: Must contain an entry for pclk (which feeds the peripheral bus + interface) +- clock-names: Must be "pclk". +- vref-supply: Phandle to the vref+ input analog reference supply. +- #address-cells = <1>; +- #size-cells = <0>; + +Optional properties: +- resets: Must contain the phandle to the reset controller. +- A pinctrl state named "default" for each DAC channel may be defined to set + DAC_OUTx pin in mode of operation for analog output on external pin. + +Contents of a stm32 dac child node: +----------------------------------- +DAC core node should contain at least one subnode, representing a +DAC instance/channel available on the machine. + +Required properties: +- compatible: Must be "st,stm32-dac". +- reg: Must be either 1 or 2, to define (single) channel in use +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in + Documentation/devicetree/bindings/iio/iio-bindings.txt + +Example: + dac: dac@40007400 { + compatible = "st,stm32h7-dac-core"; + reg = <0x40007400 0x400>; + clocks = <&clk>; + clock-names = "pclk"; + vref-supply = <®_vref>; + pinctrl-names = "default"; + pinctrl-0 = <&dac_out1 &dac_out2>; + #address-cells = <1>; + #size-cells = <0>; + + dac1: dac@1 { + compatible = "st,stm32-dac"; + #io-channels-cells = <1>; + reg = <1>; + }; + + dac2: dac@2 { + compatible = "st,stm32-dac"; + #io-channels-cells = <1>; + reg = <2>; + }; + }; -- cgit v1.2.3