* [PATCH] arm: allwinner: a20: Add Drejo DS167 initial support
From: stulluk @ 2020-05-31 11:05 UTC (permalink / raw)
To: mripard; +Cc: robh+dt, wens, devicetree, linux-kernel, linux-sunxi,
Sertac TULLUK
From: Sertac TULLUK <stulluk@gmail.com>
Drejo DS167 is an Allwinner A20 based IoT device,
which support
- Allwinner A20 Cortex-A7
- Mali-400MP2 GPU
- AXP209 PMIC
- 1GB DDR3 RAM
- 8GB eMMC
- 10/100M Ethernet
- SATA
- HDMI
- 10.1inch and 7.0inch LVDS LCD and Touch screens
- CSI: OV5640 sensor
- USB OTG
- 2x USB2.0
- built-in KNX Transceiver
- 3x Dry Contact Input
- 3x Relay output
- IR RX/TX
- UART3
- SPI1
- RTC Battery
- 8x GPIO
- Analogue + Digital Microphone
- PAM8620 speaker Amplifier
- 12V DC power supply
- 3.7V Battery Operable
Signed-off-by: Sertac TULLUK <stulluk@gmail.com>
---
arch/arm/boot/dts/Makefile | 2 +
.../boot/dts/sun7i-a20-drejo-ds167-emmc.dts | 69 +++++
arch/arm/boot/dts/sun7i-a20-drejo-ds167.dts | 288 ++++++++++++++++++
3 files changed, 359 insertions(+)
create mode 100644 arch/arm/boot/dts/sun7i-a20-drejo-ds167-emmc.dts
create mode 100644 arch/arm/boot/dts/sun7i-a20-drejo-ds167.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 3823090d07e7..d81e685dee38 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -1097,6 +1097,8 @@ dtb-$(CONFIG_MACH_SUN7I) += \
sun7i-a20-bananapro.dtb \
sun7i-a20-cubieboard2.dtb \
sun7i-a20-cubietruck.dtb \
+ sun7i-a20-drejo-ds167.dtb \
+ sun7i-a20-drejo-ds167-emmc.dtb \
sun7i-a20-hummingbird.dtb \
sun7i-a20-itead-ibox.dtb \
sun7i-a20-i12-tvbox.dtb \
diff --git a/arch/arm/boot/dts/sun7i-a20-drejo-ds167-emmc.dts b/arch/arm/boot/dts/sun7i-a20-drejo-ds167-emmc.dts
new file mode 100644
index 000000000000..b6147eb013b0
--- /dev/null
+++ b/arch/arm/boot/dts/sun7i-a20-drejo-ds167-emmc.dts
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020 Sertac TULLUK
+ *
+ * Sertac TULLUK <stulluk@gmail.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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.
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "sun7i-a20-drejo-ds167.dts"
+
+/ {
+ model = "drejo DS167-eMMC";
+ compatible = "drejo,sun7i-a20-drejo-ds167-emmc", "allwinner,sun7i-a20";
+
+ mmc2_pwrseq: pwrseq {
+ compatible = "mmc-pwrseq-emmc";
+ reset-gpios = <&pio 2 24 GPIO_ACTIVE_LOW>;
+ };
+};
+
+&mmc2 {
+ vmmc-supply = <®_vcc3v3>;
+ bus-width = <4>;
+ non-removable;
+ mmc-pwrseq = <&mmc2_pwrseq>;
+ status = "okay";
+
+ emmc: emmc@0 {
+ reg = <0>;
+ compatible = "mmc-card";
+ broken-hpi;
+ };
+};
diff --git a/arch/arm/boot/dts/sun7i-a20-drejo-ds167.dts b/arch/arm/boot/dts/sun7i-a20-drejo-ds167.dts
new file mode 100644
index 000000000000..79db92f88251
--- /dev/null
+++ b/arch/arm/boot/dts/sun7i-a20-drejo-ds167.dts
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2020 Sertac TULLUK
+ *
+ * Sertac TULLUK <stulluk@gmail.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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.
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "sun7i-a20.dtsi"
+#include "sunxi-common-regulators.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/ {
+ model = "drejo DS167";
+ compatible = "drejo,sun7i-a20-drejo-ds167", "allwinner,sun7i-a20";
+
+ aliases {
+ serial0 = &uart0;
+ serial1 = &uart3;
+
+ spi0 = &spi1;
+ spi1 = &spi2;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ hdmi-connector {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_con_in: endpoint {
+ remote-endpoint = <&hdmi_out_con>;
+ };
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&led_pins_ds167>;
+
+ red {
+ label = "ds167-status";
+ gpios = <&pio 8 9 GPIO_ACTIVE_LOW>; /* PI9 STATUS LED NEAR A20 SERTAC */
+ default-state = "on";
+ };
+ };
+};
+
+&ahci {
+ target-supply = <®_ahci_5v>;
+ status = "okay";
+};
+
+&codec {
+ status = "okay";
+};
+
+&cpu0 {
+ cpu-supply = <®_dcdc2>;
+};
+
+&de {
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&gmac {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gmac_mii_pins>, <&gmac_txerr>;
+ phy-handle = <&phy1>;
+ phy-mode = "mii";
+ status = "okay";
+};
+
+&hdmi {
+ status = "okay";
+};
+
+&hdmi_out {
+ hdmi_out_con: endpoint {
+ remote-endpoint = <&hdmi_con_in>;
+ };
+};
+
+&i2c0 {
+ status = "okay";
+
+ axp209: pmic@34 {
+ reg = <0x34>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+};
+
+&lradc {
+ vref-supply = <®_vcc3v0>;
+ status = "okay";
+};
+
+&gmac_mdio {
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ };
+};
+
+&mmc0 {
+ vmmc-supply = <®_vcc3v3>;
+ bus-width = <4>;
+ cd-gpios = <&pio 7 1 GPIO_ACTIVE_LOW>; /* PH1 */
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&otg_sram {
+ status = "okay";
+};
+
+&pio {
+ gmac_txerr: gmac-txerr-pin {
+ pins = "PA17";
+ function = "gmac";
+ };
+
+ led_pins_ds167: led-pins {
+ pins = "PI9"; /* Status led, on schematic, this is LED_EN */
+ function = "gpio_out";
+ drive-strength = <20>;
+ };
+};
+
+&pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm0_pin>, <&pwm1_pin>;
+ status = "okay";
+};
+
+#include "axp209.dtsi"
+
+&ac_power_supply {
+ status = "okay";
+};
+
+&battery_power_supply {
+ status = "okay";
+};
+
+®_dcdc2 {
+ regulator-always-on;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-name = "vdd-cpu";
+};
+
+®_dcdc3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1400000>;
+ regulator-name = "vdd-int-dll";
+};
+
+®_ldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-name = "avcc";
+};
+
+®_ahci_5v {
+ status = "okay";
+};
+
+®_usb0_vbus {
+ status = "okay";
+};
+
+®_usb1_vbus {
+ status = "okay";
+};
+
+®_usb2_vbus {
+ status = "okay";
+};
+
+&spi1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi1_pi_pins>,
+ <&spi1_cs0_pi_pin>;
+ status = "okay";
+};
+
+&spi2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi2_pc_pins>,
+ <&spi2_cs0_pc_pin>;
+ status = "okay";
+};
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pb_pins>;
+ status = "okay";
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pg_pins>;
+ status = "okay";
+};
+
+&usb_otg {
+ dr_mode = "otg";
+ status = "okay";
+};
+
+&usb_power_supply {
+ status = "okay";
+};
+
+&usbphy {
+ usb0_id_det-gpios = <&pio 7 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>; /* PH4 */
+ usb0_vbus_det-gpios = <&pio 7 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; /* PH5 */
+ usb0_vbus-supply = <®_usb0_vbus>;
+ usb1_vbus-supply = <®_usb1_vbus>;
+ usb2_vbus-supply = <®_usb2_vbus>;
+ status = "okay";
+};
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v7 3/5] dt-bindings: iio: magnetometer: ak8975: add gpio reset support
From: Jonathan Cameron @ 2020-05-31 11:01 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: linux-kernel, ~postmarketos/upstreaming, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Rob Herring,
open list:IIO SUBSYSTEM AND DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
In-Reply-To: <20200528145930.11860-1-jonathan.albrieux@gmail.com>
On Thu, 28 May 2020 16:59:28 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> Add reset-gpio support.
>
> Without reset's deassertion during ak8975_power_on(), driver's probe fails
> on ak8975_who_i_am() while checking for device identity for AK09911 chip.
>
> AK09911 has an active low reset gpio to handle register's reset.
> AK09911 datasheet says that, if not used, reset pin should be connected
> to VID. This patch emulates this situation.
>
> Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.
thanks,
Jonathan
> ---
> .../bindings/iio/magnetometer/asahi-kasei,ak8975.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml
> index 55b18784e503..e8af53d60759 100644
> --- a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml
> +++ b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml
> @@ -47,6 +47,11 @@ properties:
> mount-matrix:
> description: an optional 3x3 mounting rotation matrix.
>
> + reset-gpios:
> + description: |
> + an optional pin needed for AK09911 to set the reset state. This should
> + be usually active low
> +
> required:
> - compatible
> - reg
> @@ -54,6 +59,7 @@ required:
> examples:
> - |
> #include <dt-bindings/interrupt-controller/irq.h>
> + #include <dt-bindings/gpio/gpio.h>
> i2c {
> #address-cells = <1>;
> #size-cells = <0>;
> @@ -64,6 +70,7 @@ examples:
> interrupt-parent = <&gpio6>;
> interrupts = <15 IRQ_TYPE_EDGE_RISING>;
> vdd-supply = <&ldo_3v3_gnss>;
> + reset-gpios = <&msmgpio 111 GPIO_ACTIVE_LOW>;
> mount-matrix = "-0.984807753012208", /* x0 */
> "0", /* y0 */
> "-0.173648177666930", /* z0 */
^ permalink raw reply
* Re: [PATCH V6 1/7] iio: adc: Convert the QCOM SPMI ADC bindings to .yaml format
From: Jonathan Cameron @ 2020-05-31 10:44 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland, Amit Kucheria,
linux-iio, linux-arm-msm, linux-arm-msm-owner
In-Reply-To: <20200531113612.7bff6147@archlinux>
On Sun, 31 May 2020 11:36:12 +0100
Jonathan Cameron <jic23@kernel.org> wrote:
> On Thu, 28 May 2020 22:24:23 +0530
> Jishnu Prakash <jprakash@codeaurora.org> wrote:
>
> > Convert the adc bindings from .txt to .yaml format.
> >
> > Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
> > Reviewed-by: Amit Kucheria <amit.kucheria@linaro.org>
> > Reviewed-by: Rob Herring <robh@kernel.org>
> > Acked-by: Linus Walleij <linus.walleij@linaro.org>
>
> Jishnu, Patch is fine, but I'd like to have seen a cover
> letter and clear statement of changes from v5.
>
Applied to the togreg branch of iio.git and pushed out as
testing. Note we've missed the merge window now for IIO so this
will be in the following cycle.
Thanks,
J
> Thanks,
>
> Jonathan
>
> > ---
> > .../devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 173 --------------
> > .../bindings/iio/adc/qcom,spmi-vadc.yaml | 252 +++++++++++++++++++++
> > 2 files changed, 252 insertions(+), 173 deletions(-)
> > delete mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> > create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> > deleted file mode 100644
> > index c878768..0000000
> > --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> > +++ /dev/null
> > @@ -1,173 +0,0 @@
> > -Qualcomm's SPMI PMIC ADC
> > -
> > -- SPMI PMIC voltage ADC (VADC) provides interface to clients to read
> > - voltage. The VADC is a 15-bit sigma-delta ADC.
> > -- SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read
> > - voltage. The VADC is a 16-bit sigma-delta ADC.
> > -
> > -VADC node:
> > -
> > -- compatible:
> > - Usage: required
> > - Value type: <string>
> > - Definition: Should contain "qcom,spmi-vadc".
> > - Should contain "qcom,spmi-adc5" for PMIC5 ADC driver.
> > - Should contain "qcom,spmi-adc-rev2" for PMIC rev2 ADC driver.
> > - Should contain "qcom,pms405-adc" for PMS405 PMIC
> > -
> > -- reg:
> > - Usage: required
> > - Value type: <prop-encoded-array>
> > - Definition: VADC base address in the SPMI PMIC register map.
> > -
> > -- #address-cells:
> > - Usage: required
> > - Value type: <u32>
> > - Definition: Must be one. Child node 'reg' property should define ADC
> > - channel number.
> > -
> > -- #size-cells:
> > - Usage: required
> > - Value type: <u32>
> > - Definition: Must be zero.
> > -
> > -- #io-channel-cells:
> > - Usage: required
> > - Value type: <u32>
> > - Definition: Must be one. For details about IIO bindings see:
> > - Documentation/devicetree/bindings/iio/iio-bindings.txt
> > -
> > -- interrupts:
> > - Usage: optional
> > - Value type: <prop-encoded-array>
> > - Definition: End of conversion interrupt.
> > -
> > -Channel node properties:
> > -
> > -- reg:
> > - Usage: required
> > - Value type: <u32>
> > - Definition: ADC channel number.
> > - See include/dt-bindings/iio/qcom,spmi-vadc.h
> > -
> > -- label:
> > - Usage: required for "qcom,spmi-adc5" and "qcom,spmi-adc-rev2"
> > - Value type: <empty>
> > - Definition: ADC input of the platform as seen in the schematics.
> > - For thermistor inputs connected to generic AMUX or GPIO inputs
> > - these can vary across platform for the same pins. Hence select
> > - the platform schematics name for this channel.
> > -
> > -- qcom,decimation:
> > - Usage: optional
> > - Value type: <u32>
> > - Definition: This parameter is used to decrease ADC sampling rate.
> > - Quicker measurements can be made by reducing decimation ratio.
> > - - For compatible property "qcom,spmi-vadc", valid values are
> > - 512, 1024, 2048, 4096. If property is not found, default value
> > - of 512 will be used.
> > - - For compatible property "qcom,spmi-adc5", valid values are 250, 420
> > - and 840. If property is not found, default value of 840 is used.
> > - - For compatible property "qcom,spmi-adc-rev2", valid values are 256,
> > - 512 and 1024. If property is not present, default value is 1024.
> > -
> > -- qcom,pre-scaling:
> > - Usage: optional
> > - Value type: <u32 array>
> > - Definition: Used for scaling the channel input signal before the signal is
> > - fed to VADC. The configuration for this node is to know the
> > - pre-determined ratio and use it for post scaling. Select one from
> > - the following options.
> > - <1 1>, <1 3>, <1 4>, <1 6>, <1 20>, <1 8>, <10 81>, <1 10>
> > - If property is not found default value depending on chip will be used.
> > -
> > -- qcom,ratiometric:
> > - Usage: optional
> > - Value type: <empty>
> > - Definition: Channel calibration type.
> > - - For compatible property "qcom,spmi-vadc", if this property is
> > - specified VADC will use the VDD reference (1.8V) and GND for
> > - channel calibration. If property is not found, channel will be
> > - calibrated with 0.625V and 1.25V reference channels, also
> > - known as absolute calibration.
> > - - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
> > - if this property is specified VADC will use the VDD reference
> > - (1.875V) and GND for channel calibration. If property is not found,
> > - channel will be calibrated with 0V and 1.25V reference channels,
> > - also known as absolute calibration.
> > -
> > -- qcom,hw-settle-time:
> > - Usage: optional
> > - Value type: <u32>
> > - Definition: Time between AMUX getting configured and the ADC starting
> > - conversion. The 'hw_settle_time' is an index used from valid values
> > - and programmed in hardware to achieve the hardware settling delay.
> > - - For compatible property "qcom,spmi-vadc" and "qcom,spmi-adc-rev2",
> > - Delay = 100us * (hw_settle_time) for hw_settle_time < 11,
> > - and 2ms * (hw_settle_time - 10) otherwise.
> > - Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800,
> > - 900 us and 1, 2, 4, 6, 8, 10 ms.
> > - If property is not found, channel will use 0us.
> > - - For compatible property "qcom,spmi-adc5", delay = 15us for
> > - value 0, 100us * (value) for values < 11,
> > - and 2ms * (value - 10) otherwise.
> > - Valid values are: 15, 100, 200, 300, 400, 500, 600, 700, 800,
> > - 900 us and 1, 2, 4, 6, 8, 10 ms
> > - Certain controller digital versions have valid values of
> > - 15, 100, 200, 300, 400, 500, 600, 700, 1, 2, 4, 8, 16, 32, 64, 128 ms
> > - If property is not found, channel will use 15us.
> > -
> > -- qcom,avg-samples:
> > - Usage: optional
> > - Value type: <u32>
> > - Definition: Number of samples to be used for measurement.
> > - Averaging provides the option to obtain a single measurement
> > - from the ADC that is an average of multiple samples. The value
> > - selected is 2^(value).
> > - - For compatible property "qcom,spmi-vadc", valid values
> > - are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
> > - If property is not found, 1 sample will be used.
> > - - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
> > - valid values are: 1, 2, 4, 8, 16
> > - If property is not found, 1 sample will be used.
> > -
> > -NOTE:
> > -
> > -For compatible property "qcom,spmi-vadc" following channels, also known as
> > -reference point channels, are used for result calibration and their channel
> > -configuration nodes should be defined:
> > -VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
> > -VADC_GND_REF and VADC_VDD_VADC.
> > -
> > -Example:
> > -
> > -#include <dt-bindings/iio/qcom,spmi-vadc.h>
> > -#include <linux/irq.h>
> > -/* ... */
> > -
> > - /* VADC node */
> > - pmic_vadc: vadc@3100 {
> > - compatible = "qcom,spmi-vadc";
> > - reg = <0x3100>;
> > - interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
> > - #address-cells = <1>;
> > - #size-cells = <0>;
> > - #io-channel-cells = <1>;
> > - io-channel-ranges;
> > -
> > - /* Channel node */
> > - adc-chan@VADC_LR_MUX10_USB_ID {
> > - reg = <VADC_LR_MUX10_USB_ID>;
> > - qcom,decimation = <512>;
> > - qcom,ratiometric;
> > - qcom,hw-settle-time = <200>;
> > - qcom,avg-samples = <1>;
> > - qcom,pre-scaling = <1 3>;
> > - };
> > - };
> > -
> > - /* IIO client node */
> > - usb {
> > - io-channels = <&pmic_vadc VADC_LR_MUX10_USB_ID>;
> > - io-channel-names = "vadc";
> > - };
> > diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> > new file mode 100644
> > index 0000000..de8d243
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> > @@ -0,0 +1,252 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-vadc.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Qualcomm's SPMI PMIC ADC
> > +
> > +maintainers:
> > + - Andy Gross <agross@kernel.org>
> > + - Bjorn Andersson <bjorn.andersson@linaro.org>
> > +
> > +description: |
> > + SPMI PMIC voltage ADC (VADC) provides interface to clients to read
> > + voltage. The VADC is a 15-bit sigma-delta ADC.
> > + SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read
> > + voltage. The VADC is a 16-bit sigma-delta ADC.
> > +
> > +properties:
> > + compatible:
> > + oneOf:
> > + - items:
> > + - const: qcom,pms405-adc
> > + - const: qcom,spmi-adc-rev2
> > +
> > + - items:
> > + - enum:
> > + - qcom,spmi-vadc
> > + - qcom,spmi-adc5
> > + - qcom,spmi-adc-rev2
> > +
> > + reg:
> > + description: VADC base address in the SPMI PMIC register map
> > + maxItems: 1
> > +
> > + '#address-cells':
> > + const: 1
> > +
> > + '#size-cells':
> > + const: 0
> > +
> > + '#io-channel-cells':
> > + const: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > + description:
> > + End of conversion interrupt.
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - '#address-cells'
> > + - '#size-cells'
> > + - '#io-channel-cells'
> > +
> > +patternProperties:
> > + "^.*@[0-9a-f]+$":
> > + type: object
> > + description: |
> > + Represents the external channels which are connected to the ADC.
> > + For compatible property "qcom,spmi-vadc" following channels, also known as
> > + reference point channels, are used for result calibration and their channel
> > + configuration nodes should be defined:
> > + VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
> > + VADC_GND_REF and VADC_VDD_VADC.
> > +
> > + properties:
> > + reg:
> > + description: |
> > + ADC channel number.
> > + See include/dt-bindings/iio/qcom,spmi-vadc.h
> > +
> > + label:
> > + $ref: /schemas/types.yaml#/definitions/string
> > + description: |
> > + ADC input of the platform as seen in the schematics.
> > + For thermistor inputs connected to generic AMUX or GPIO inputs
> > + these can vary across platform for the same pins. Hence select
> > + the platform schematics name for this channel.
> > +
> > + qcom,decimation:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + description: |
> > + This parameter is used to decrease ADC sampling rate.
> > + Quicker measurements can be made by reducing decimation ratio.
> > +
> > + qcom,pre-scaling:
> > + description: |
> > + Used for scaling the channel input signal before the signal is
> > + fed to VADC. The configuration for this node is to know the
> > + pre-determined ratio and use it for post scaling. It is a pair of
> > + integers, denoting the numerator and denominator of the fraction by which
> > + input signal is multiplied. For example, <1 3> indicates the signal is scaled
> > + down to 1/3 of its value before ADC measurement.
> > + If property is not found default value depending on chip will be used.
> > + allOf:
> > + - $ref: /schemas/types.yaml#/definitions/uint32-array
> > + oneOf:
> > + - items:
> > + - const: 1
> > + - enum: [ 1, 3, 4, 6, 20, 8, 10 ]
> > +
> > + - items:
> > + - const: 10
> > + - const: 81
> > +
> > + qcom,ratiometric:
> > + description: |
> > + Channel calibration type.
> > + - For compatible property "qcom,spmi-vadc", if this property is
> > + specified VADC will use the VDD reference (1.8V) and GND for
> > + channel calibration. If property is not found, channel will be
> > + calibrated with 0.625V and 1.25V reference channels, also
> > + known as absolute calibration.
> > + - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
> > + if this property is specified VADC will use the VDD reference (1.875V)
> > + and GND for channel calibration. If property is not found, channel
> > + will be calibrated with 0V and 1.25V reference channels, also known
> > + as absolute calibration.
> > + type: boolean
> > +
> > + qcom,hw-settle-time:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + description: |
> > + Time between AMUX getting configured and the ADC starting
> > + conversion. The 'hw_settle_time' is an index used from valid values
> > + and programmed in hardware to achieve the hardware settling delay.
> > +
> > + qcom,avg-samples:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + description: |
> > + Number of samples to be used for measurement.
> > + Averaging provides the option to obtain a single measurement
> > + from the ADC that is an average of multiple samples. The value
> > + selected is 2^(value).
> > +
> > + required:
> > + - reg
> > +
> > +allOf:
> > + - if:
> > + properties:
> > + compatible:
> > + contains:
> > + const: qcom,spmi-vadc
> > +
> > + then:
> > + patternProperties:
> > + "^.*@[0-9a-f]+$":
> > + properties:
> > + qcom,decimation:
> > + enum: [ 512, 1024, 2048, 4096 ]
> > + default: 512
> > +
> > + qcom,hw-settle-time:
> > + enum: [ 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1, 2,
> > + 4, 6, 8, 10 ]
> > + default: 0
> > +
> > + qcom,avg-samples:
> > + enum: [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 ]
> > + default: 1
> > +
> > + - if:
> > + properties:
> > + compatible:
> > + contains:
> > + const: qcom,spmi-adc-rev2
> > +
> > + then:
> > + patternProperties:
> > + "^.*@[0-9a-f]+$":
> > + properties:
> > + qcom,decimation:
> > + enum: [ 256, 512, 1024 ]
> > + default: 1024
> > +
> > + qcom,hw-settle-time:
> > + enum: [ 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1, 2,
> > + 4, 6, 8, 10 ]
> > + default: 0
> > +
> > + qcom,avg-samples:
> > + enum: [ 1, 2, 4, 8, 16 ]
> > + default: 1
> > +
> > + - if:
> > + properties:
> > + compatible:
> > + contains:
> > + const: qcom,spmi-adc5
> > +
> > + then:
> > + patternProperties:
> > + "^.*@[0-9a-f]+$":
> > + properties:
> > + qcom,decimation:
> > + enum: [ 250, 420, 840 ]
> > + default: 840
> > +
> > + qcom,hw-settle-time:
> > + enum: [ 15, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1, 2,
> > + 4, 6, 8, 10, 16, 32, 64, 128 ]
> > + default: 15
> > +
> > + qcom,avg-samples:
> > + enum: [ 1, 2, 4, 8, 16 ]
> > + default: 1
> > +
> > +examples:
> > + - |
> > + spmi_bus {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > + /* VADC node */
> > + pmic_vadc: adc@3100 {
> > + compatible = "qcom,spmi-vadc";
> > + reg = <0x3100>;
> > + interrupts = <0x0 0x31 0x0 0x1>;
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > + #io-channel-cells = <1>;
> > + io-channel-ranges;
> > +
> > + /* Channel node */
> > + adc-chan@39 {
> > + reg = <0x39>;
> > + qcom,decimation = <512>;
> > + qcom,ratiometric;
> > + qcom,hw-settle-time = <200>;
> > + qcom,avg-samples = <1>;
> > + qcom,pre-scaling = <1 3>;
> > + };
> > +
> > + adc-chan@9 {
> > + reg = <0x9>;
> > + };
> > +
> > + adc-chan@a {
> > + reg = <0xa>;
> > + };
> > +
> > + adc-chan@e {
> > + reg = <0xe>;
> > + };
> > +
> > + adc-chan@f {
> > + reg = <0xf>;
> > + };
> > + };
> > + };
>
^ permalink raw reply
* Re: [PATCH V6 6/7] iio: adc: Update debug prints
From: Jonathan Cameron @ 2020-05-31 10:53 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-arm-msm, linux-iio,
linux-arm-msm-owner
In-Reply-To: <1590684869-15400-7-git-send-email-jprakash@codeaurora.org>
On Thu, 28 May 2020 22:24:28 +0530
Jishnu Prakash <jprakash@codeaurora.org> wrote:
> Change pr_err/pr_debug statements to dev_err/dev_dbg for
> increased clarity.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Applied. Thanks, J
> ---
> drivers/iio/adc/qcom-spmi-adc5.c | 18 +++++++++---------
> 1 file changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
> index 3022313..0f9af66 100644
> --- a/drivers/iio/adc/qcom-spmi-adc5.c
> +++ b/drivers/iio/adc/qcom-spmi-adc5.c
> @@ -246,11 +246,11 @@ static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
> *data = (rslt_msb << 8) | rslt_lsb;
>
> if (*data == ADC5_USR_DATA_CHECK) {
> - pr_err("Invalid data:0x%x\n", *data);
> + dev_err(adc->dev, "Invalid data:0x%x\n", *data);
> return -EINVAL;
> }
>
> - pr_debug("voltage raw code:0x%x\n", *data);
> + dev_dbg(adc->dev, "voltage raw code:0x%x\n", *data);
>
> return 0;
> }
> @@ -382,24 +382,24 @@ static int adc5_do_conversion(struct adc5_chip *adc,
>
> ret = adc5_configure(adc, prop);
> if (ret) {
> - pr_err("ADC configure failed with %d\n", ret);
> + dev_err(adc->dev, "ADC configure failed with %d\n", ret);
> goto unlock;
> }
>
> if (adc->poll_eoc) {
> ret = adc5_poll_wait_eoc(adc);
> if (ret) {
> - pr_err("EOC bit not set\n");
> + dev_err(adc->dev, "EOC bit not set\n");
> goto unlock;
> }
> } else {
> ret = wait_for_completion_timeout(&adc->complete,
> ADC5_CONV_TIMEOUT);
> if (!ret) {
> - pr_debug("Did not get completion timeout.\n");
> + dev_dbg(adc->dev, "Did not get completion timeout.\n");
> ret = adc5_poll_wait_eoc(adc);
> if (ret) {
> - pr_err("EOC bit not set\n");
> + dev_err(adc->dev, "EOC bit not set\n");
> goto unlock;
> }
> }
> @@ -721,7 +721,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
> channel_name = of_get_property(node,
> "label", NULL) ? : node->name;
> if (!channel_name) {
> - pr_err("Invalid channel name\n");
> + dev_err(dev, "Invalid channel name\n");
> return -EINVAL;
> }
> prop->datasheet_name = channel_name;
> @@ -764,7 +764,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
> return ret;
> }
>
> - pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
> + dev_dbg(dev, "dig_ver:minor:%d, major:%d\n", dig_version[0],
> dig_version[1]);
> /* Digital controller >= 5.3 have hw_settle_2 option */
> if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
> @@ -966,7 +966,7 @@ static int adc5_probe(struct platform_device *pdev)
>
> ret = adc5_get_dt_data(adc, node);
> if (ret) {
> - pr_err("adc get dt data failed\n");
> + dev_err(dev, "adc get dt data failed\n");
> return ret;
> }
>
^ permalink raw reply
* Re: [PATCH V6 7/7] iio: adc: Add a common read function for PMIC5 and PMIC7
From: Jonathan Cameron @ 2020-05-31 10:52 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-arm-msm, linux-iio,
linux-arm-msm-owner
In-Reply-To: <1590684869-15400-8-git-send-email-jprakash@codeaurora.org>
On Thu, 28 May 2020 22:24:29 +0530
Jishnu Prakash <jprakash@codeaurora.org> wrote:
> Add a common function used for read_raw callback for both PMIC5
> and PMIC7 ADCs.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
Hmm. I'm not completely sold on this one. Suggestions below.
Jonathan
> ---
> drivers/iio/adc/qcom-spmi-adc5.c | 53 +++++++++++++++++++---------------------
> 1 file changed, 25 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
> index 0f9af66..fe49741 100644
> --- a/drivers/iio/adc/qcom-spmi-adc5.c
> +++ b/drivers/iio/adc/qcom-spmi-adc5.c
> @@ -449,6 +449,13 @@ static int adc7_do_conversion(struct adc5_chip *adc,
> return ret;
> }
>
> +struct adc_do_conversion {
> + int (*adc_do_conversion)(struct adc5_chip *adc,
> + struct adc5_channel_prop *prop,
> + struct iio_chan_spec const *chan,
> + u16 *data_volt, u16 *data_cur);
Why use a structure for this? It's just a function pointer.
If they form is too long you can always use a typedef.
This is fine if you have other stuff coming shortly that will add
to this structure but for now it's just a bit confusing.
Directly passing the function pointer will reduce the amount
of coded added here and make the argument in favour of refactoring
rather stronger.
> +};
> +
> static irqreturn_t adc5_isr(int irq, void *dev_id)
> {
> struct adc5_chip *adc = dev_id;
> @@ -487,9 +494,9 @@ static int adc7_of_xlate(struct iio_dev *indio_dev,
> return -EINVAL;
> }
>
> -static int adc5_read_raw(struct iio_dev *indio_dev,
> +static int adc_read_raw_common(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val, int *val2,
> - long mask)
> + long mask, struct adc_do_conversion do_conv)
> {
> struct adc5_chip *adc = iio_priv(indio_dev);
> struct adc5_channel_prop *prop;
> @@ -500,8 +507,8 @@ static int adc5_read_raw(struct iio_dev *indio_dev,
>
> switch (mask) {
> case IIO_CHAN_INFO_PROCESSED:
> - ret = adc5_do_conversion(adc, prop, chan,
> - &adc_code_volt, &adc_code_cur);
> + ret = do_conv.adc_do_conversion(adc, prop, chan,
> + &adc_code_volt, &adc_code_cur);
> if (ret)
> return ret;
>
> @@ -518,36 +525,26 @@ static int adc5_read_raw(struct iio_dev *indio_dev,
> }
> }
>
> -static int adc7_read_raw(struct iio_dev *indio_dev,
> +static int adc5_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val, int *val2,
> long mask)
> {
> - struct adc5_chip *adc = iio_priv(indio_dev);
> - struct adc5_channel_prop *prop;
> - u16 adc_code_volt, adc_code_cur;
> - int ret;
> -
> - prop = &adc->chan_props[chan->address];
> -
> - switch (mask) {
> - case IIO_CHAN_INFO_PROCESSED:
> - ret = adc7_do_conversion(adc, prop, chan,
> - &adc_code_volt, &adc_code_cur);
> - if (ret)
> - return ret;
> + struct adc_do_conversion do_conv;
>
> - ret = qcom_adc5_hw_scale(prop->scale_fn_type,
> - &adc5_prescale_ratios[prop->prescale],
> - adc->data,
> - adc_code_volt, val);
> + do_conv.adc_do_conversion = adc5_do_conversion;
> + return adc_read_raw_common(indio_dev, chan, val, val2,
> + mask, do_conv);
> +}
>
> - if (ret)
> - return ret;
> +static int adc7_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val, int *val2,
> + long mask)
> +{
> + struct adc_do_conversion do_conv;
>
> - return IIO_VAL_INT;
> - default:
> - return -EINVAL;
> - }
> + do_conv.adc_do_conversion = adc7_do_conversion;
> + return adc_read_raw_common(indio_dev, chan, val, val2,
> + mask, do_conv);
> }
>
> static const struct iio_info adc5_info = {
^ permalink raw reply
* Re: [PATCH V6 5/7] iio: adc: Update return value checks
From: Jonathan Cameron @ 2020-05-31 10:49 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-arm-msm, linux-iio,
linux-arm-msm-owner
In-Reply-To: <1590684869-15400-6-git-send-email-jprakash@codeaurora.org>
On Thu, 28 May 2020 22:24:27 +0530
Jishnu Prakash <jprakash@codeaurora.org> wrote:
> Clean up some return value checks to make code more compact.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
Applied.
Thanks,
J
> ---
> drivers/iio/adc/qcom-spmi-adc5.c | 10 ++++------
> 1 file changed, 4 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
> index dcc7599..3022313 100644
> --- a/drivers/iio/adc/qcom-spmi-adc5.c
> +++ b/drivers/iio/adc/qcom-spmi-adc5.c
> @@ -301,7 +301,7 @@ static int adc5_configure(struct adc5_chip *adc,
>
> /* Read registers 0x42 through 0x46 */
> ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
> - if (ret < 0)
> + if (ret)
> return ret;
>
> /* Digital param selection */
> @@ -388,7 +388,7 @@ static int adc5_do_conversion(struct adc5_chip *adc,
>
> if (adc->poll_eoc) {
> ret = adc5_poll_wait_eoc(adc);
> - if (ret < 0) {
> + if (ret) {
> pr_err("EOC bit not set\n");
> goto unlock;
> }
> @@ -398,7 +398,7 @@ static int adc5_do_conversion(struct adc5_chip *adc,
> if (!ret) {
> pr_debug("Did not get completion timeout.\n");
> ret = adc5_poll_wait_eoc(adc);
> - if (ret < 0) {
> + if (ret) {
> pr_err("EOC bit not set\n");
> goto unlock;
> }
> @@ -516,8 +516,6 @@ static int adc5_read_raw(struct iio_dev *indio_dev,
> default:
> return -EINVAL;
> }
> -
> - return 0;
> }
>
> static int adc7_read_raw(struct iio_dev *indio_dev,
> @@ -761,7 +759,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>
> ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version,
> sizeof(dig_version));
> - if (ret < 0) {
> + if (ret) {
> dev_err(dev, "Invalid dig version read %d\n", ret);
> return ret;
> }
^ permalink raw reply
* Re: [PATCH V6 3/7] iio: adc: Add info property under adc_data
From: Jonathan Cameron @ 2020-05-31 10:48 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-arm-msm, linux-iio,
linux-arm-msm-owner
In-Reply-To: <1590684869-15400-4-git-send-email-jprakash@codeaurora.org>
On Thu, 28 May 2020 22:24:25 +0530
Jishnu Prakash <jprakash@codeaurora.org> wrote:
> Add info property under adc_data to support adding ADC variants
> which may use different iio_info than the one defined for PMIC5.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
Applied.
Thanks,
Jonathan
> ---
> drivers/iio/adc/qcom-spmi-adc5.c | 4 +++-
> drivers/iio/adc/qcom-vadc-common.h | 1 +
> 2 files changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
> index 21fdcde..0fa1d37 100644
> --- a/drivers/iio/adc/qcom-spmi-adc5.c
> +++ b/drivers/iio/adc/qcom-spmi-adc5.c
> @@ -629,6 +629,7 @@ static const struct adc5_data adc5_data_pmic = {
> .full_scale_code_volt = 0x70e4,
> .full_scale_code_cur = 0x2710,
> .adc_chans = adc5_chans_pmic,
> + .info = &adc5_info,
> .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
> {250, 420, 840},
> .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
> @@ -643,6 +644,7 @@ static const struct adc5_data adc5_data_pmic_rev2 = {
> .full_scale_code_volt = 0x4000,
> .full_scale_code_cur = 0x1800,
> .adc_chans = adc5_chans_rev2,
> + .info = &adc5_info,
> .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
> {256, 512, 1024},
> .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
> @@ -777,7 +779,7 @@ static int adc5_probe(struct platform_device *pdev)
> indio_dev->dev.of_node = node;
> indio_dev->name = pdev->name;
> indio_dev->modes = INDIO_DIRECT_MODE;
> - indio_dev->info = &adc5_info;
> + indio_dev->info = adc->data->info;
> indio_dev->channels = adc->iio_chans;
> indio_dev->num_channels = adc->nchannels;
>
> diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
> index e074902a..6a7553f 100644
> --- a/drivers/iio/adc/qcom-vadc-common.h
> +++ b/drivers/iio/adc/qcom-vadc-common.h
> @@ -136,6 +136,7 @@ struct adc5_data {
> const u32 full_scale_code_volt;
> const u32 full_scale_code_cur;
> const struct adc5_channels *adc_chans;
> + const struct iio_info *info;
> unsigned int *decimation;
> unsigned int *hw_settle_1;
> unsigned int *hw_settle_2;
^ permalink raw reply
* Re: [PATCH V6 4/7] iio: adc: Add support for PMIC7 ADC
From: Jonathan Cameron @ 2020-05-31 10:47 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-arm-msm, linux-iio,
linux-arm-msm-owner
In-Reply-To: <1590684869-15400-5-git-send-email-jprakash@codeaurora.org>
On Thu, 28 May 2020 22:24:26 +0530
Jishnu Prakash <jprakash@codeaurora.org> wrote:
> The ADC architecture on PMIC7 is changed as compared to PMIC5. The
> major change from PMIC5 is that all SW communication to ADC goes through
> PMK8350, which communicates with other PMICs through PBS when the ADC
> on PMK8350 works in master mode. The SID register is used to identify the
> PMICs with which the PBS needs to communicate. Add support for the same.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
one nitpick inline. But otherwise looks good to me.
Nitpick is trivial so I'll ignore it.
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.
Thanks,
Jonathan
> ---
> drivers/iio/adc/qcom-spmi-adc5.c | 215 +++++++++++++++++++++++++++++-
> drivers/iio/adc/qcom-vadc-common.c | 262 +++++++++++++++++++++++++++++++++++++
> drivers/iio/adc/qcom-vadc-common.h | 14 ++
> 3 files changed, 488 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
> index 0fa1d37..dcc7599 100644
> --- a/drivers/iio/adc/qcom-spmi-adc5.c
> +++ b/drivers/iio/adc/qcom-spmi-adc5.c
> @@ -1,6 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0
> /*
> - * Copyright (c) 2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
> */
>
> #include <linux/bitops.h>
> @@ -23,6 +23,7 @@
>
> #define ADC5_USR_REVISION1 0x0
> #define ADC5_USR_STATUS1 0x8
> +#define ADC5_USR_STATUS1_CONV_FAULT BIT(7)
> #define ADC5_USR_STATUS1_REQ_STS BIT(1)
> #define ADC5_USR_STATUS1_EOC BIT(0)
> #define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3
> @@ -65,6 +66,9 @@
>
> #define ADC5_USR_IBAT_DATA1 0x53
>
> +#define ADC_CHANNEL_OFFSET 0x8
> +#define ADC_CHANNEL_MASK GENMASK(7, 0)
> +
> /*
> * Conversion time varies based on the decimation, clock rate, fast average
> * samples and measurements queued across different VADC peripherals.
> @@ -79,6 +83,11 @@
> #define ADC5_HW_SETTLE_DIFF_MINOR 3
> #define ADC5_HW_SETTLE_DIFF_MAJOR 5
>
> +/* For PMIC7 */
> +#define ADC_APP_SID 0x40
> +#define ADC_APP_SID_MASK GENMASK(3, 0)
> +#define ADC7_CONV_TIMEOUT msecs_to_jiffies(10)
> +
> enum adc5_cal_method {
> ADC5_NO_CAL = 0,
> ADC5_RATIOMETRIC_CAL,
> @@ -96,6 +105,7 @@ enum adc5_cal_val {
> * @cal_method: calibration method.
> * @cal_val: calibration value
> * @decimation: sampling rate supported for the channel.
> + * @sid: slave id of PMIC owning the channel, for PMIC7.
> * @prescale: channel scaling performed on the input signal.
> * @hw_settle_time: the time between AMUX being configured and the
> * start of conversion.
> @@ -110,6 +120,7 @@ struct adc5_channel_prop {
> enum adc5_cal_method cal_method;
> enum adc5_cal_val cal_val;
> unsigned int decimation;
> + unsigned int sid;
> unsigned int prescale;
> unsigned int hw_settle_time;
> unsigned int avg_samples;
> @@ -165,6 +176,11 @@ static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
> return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
> }
>
> +static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
> +{
> + return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
> +}
> +
> static int adc5_prescaling_from_dt(u32 num, u32 den)
> {
> unsigned int pre;
> @@ -314,6 +330,47 @@ static int adc5_configure(struct adc5_chip *adc,
> return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
> }
>
> +static int adc7_configure(struct adc5_chip *adc,
> + struct adc5_channel_prop *prop)
> +{
> + int ret;
> + u8 conv_req = 0, buf[4];
> +
> + ret = adc5_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid);
> + if (ret)
> + return ret;
> +
> + ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
> + if (ret)
> + return ret;
> +
> + /* Digital param selection */
> + adc5_update_dig_param(adc, prop, &buf[0]);
> +
> + /* Update fast average sample value */
> + buf[1] &= ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
> + buf[1] |= prop->avg_samples;
> +
> + /* Select ADC channel */
> + buf[2] = prop->channel;
> +
> + /* Select HW settle delay for channel */
> + buf[3] &= ~ADC5_USR_HW_SETTLE_DELAY_MASK;
> + buf[3] |= prop->hw_settle_time;
> +
> + /* Select CONV request */
> + conv_req = ADC5_USR_CONV_REQ_REQ;
> +
> + if (!adc->poll_eoc)
> + reinit_completion(&adc->complete);
> +
> + ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
> + if (ret)
> + return ret;
> +
> + return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1);
> +}
> +
> static int adc5_do_conversion(struct adc5_chip *adc,
> struct adc5_channel_prop *prop,
> struct iio_chan_spec const *chan,
> @@ -355,6 +412,43 @@ static int adc5_do_conversion(struct adc5_chip *adc,
> return ret;
> }
>
> +static int adc7_do_conversion(struct adc5_chip *adc,
> + struct adc5_channel_prop *prop,
> + struct iio_chan_spec const *chan,
> + u16 *data_volt, u16 *data_cur)
> +{
> + int ret;
> + u8 status;
> +
> + mutex_lock(&adc->lock);
> +
> + ret = adc7_configure(adc, prop);
> + if (ret) {
> + dev_err(adc->dev, "ADC configure failed with %d\n", ret);
> + goto unlock;
> + }
> +
> + /* No support for polling mode at present */
> + wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT);
> +
> + ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
> + if (ret)
> + goto unlock;
> +
> + if (status & ADC5_USR_STATUS1_CONV_FAULT) {
> + dev_err(adc->dev, "Unexpected conversion fault\n");
> + ret = -EIO;
> + goto unlock;
> + }
> +
> + ret = adc5_read_voltage_data(adc, data_volt);
> +
> +unlock:
> + mutex_unlock(&adc->lock);
> +
> + return ret;
> +}
> +
> static irqreturn_t adc5_isr(int irq, void *dev_id)
> {
> struct adc5_chip *adc = dev_id;
> @@ -377,6 +471,22 @@ static int adc5_of_xlate(struct iio_dev *indio_dev,
> return -EINVAL;
> }
>
> +static int adc7_of_xlate(struct iio_dev *indio_dev,
> + const struct of_phandle_args *iiospec)
> +{
> + struct adc5_chip *adc = iio_priv(indio_dev);
> + int i, v_channel;
> +
> + for (i = 0; i < adc->nchannels; i++) {
> + v_channel = (adc->chan_props[i].sid << ADC_CHANNEL_OFFSET) |
> + adc->chan_props[i].channel;
> + if (v_channel == iiospec->args[0])
> + return i;
> + }
> +
> + return -EINVAL;
> +}
> +
> static int adc5_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val, int *val2,
> long mask)
> @@ -410,11 +520,48 @@ static int adc5_read_raw(struct iio_dev *indio_dev,
> return 0;
> }
>
> +static int adc7_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val, int *val2,
> + long mask)
> +{
> + struct adc5_chip *adc = iio_priv(indio_dev);
> + struct adc5_channel_prop *prop;
> + u16 adc_code_volt, adc_code_cur;
> + int ret;
> +
> + prop = &adc->chan_props[chan->address];
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_PROCESSED:
> + ret = adc7_do_conversion(adc, prop, chan,
> + &adc_code_volt, &adc_code_cur);
> + if (ret)
> + return ret;
> +
> + ret = qcom_adc5_hw_scale(prop->scale_fn_type,
> + &adc5_prescale_ratios[prop->prescale],
> + adc->data,
> + adc_code_volt, val);
> +
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static const struct iio_info adc5_info = {
> .read_raw = adc5_read_raw,
> .of_xlate = adc5_of_xlate,
> };
>
> +static const struct iio_info adc7_info = {
> + .read_raw = adc7_read_raw,
> + .of_xlate = adc7_of_xlate,
> +};
> +
> struct adc5_channels {
> const char *datasheet_name;
> unsigned int prescale_index;
> @@ -477,6 +624,39 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
> SCALE_HW_CALIB_PM5_SMB_TEMP)
> };
>
> +static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
> + [ADC7_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
> + SCALE_HW_CALIB_DEFAULT)
> + [ADC7_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
> + SCALE_HW_CALIB_DEFAULT)
> + [ADC7_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
> + SCALE_HW_CALIB_DEFAULT)
> + [ADC7_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
> + SCALE_HW_CALIB_DEFAULT)
> + [ADC7_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
> + SCALE_HW_CALIB_PMIC_THERM_PM7)
> + [ADC7_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_AMUX_THM6_100K_PU] = ADC5_CHAN_TEMP("amux_thm6_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> + [ADC7_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_pu2", 0,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +};
> +
> static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
> [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
> SCALE_HW_CALIB_DEFAULT)
> @@ -511,6 +691,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
> {
> const char *name = node->name, *channel_name;
> u32 chan, value, varr[2];
> + u32 sid = 0;
> int ret;
> struct device *dev = adc->dev;
>
> @@ -520,6 +701,15 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
> return ret;
> }
>
> + /* Value read from "reg" is virtual channel number */
> +
> + /* virtual channel number = sid << 8 | channel number */
> +
> + if (adc->data->info == &adc7_info) {
> + sid = chan >> ADC_CHANNEL_OFFSET;
> + chan = chan & ADC_CHANNEL_MASK;
> + }
> +
> if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
> !data->adc_chans[chan].datasheet_name) {
> dev_err(dev, "%s invalid channel number %d\n", name, chan);
> @@ -528,6 +718,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>
> /* the channel has DT description */
> prop->channel = chan;
> + prop->sid = sid;
>
> channel_name = of_get_property(node,
> "label", NULL) ? : node->name;
> @@ -578,8 +769,9 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
> pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
> dig_version[1]);
> /* Digital controller >= 5.3 have hw_settle_2 option */
> - if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
> - dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
> + if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
> + dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
> + adc->data->info == &adc7_info)
> ret = adc5_hw_settle_time_from_dt(value,
> data->hw_settle_2);
> else
> @@ -640,6 +832,18 @@ static const struct adc5_data adc5_data_pmic = {
> 1, 2, 4, 8, 16, 32, 64, 128},
> };
>
> +static const struct adc5_data adc7_data_pmic = {
> + .full_scale_code_volt = 0x70e4,
> + .adc_chans = adc7_chans_pmic,
> + .info = &adc7_info,
> + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
> + {85, 340, 1360},
> + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
> + {15, 100, 200, 300, 400, 500, 600, 700,
> + 1000, 2000, 4000, 8000, 16000, 32000,
> + 64000, 128000},
> +};
> +
> static const struct adc5_data adc5_data_pmic_rev2 = {
> .full_scale_code_volt = 0x4000,
> .full_scale_code_cur = 0x1800,
> @@ -661,6 +865,10 @@ static const struct of_device_id adc5_match_table[] = {
> .data = &adc5_data_pmic,
> },
> {
> + .compatible = "qcom,spmi-adc7",
> + .data = &adc7_data_pmic,
> + },
> + {
> .compatible = "qcom,spmi-adc-rev2",
> .data = &adc5_data_pmic_rev2,
> },
> @@ -754,6 +962,7 @@ static int adc5_probe(struct platform_device *pdev)
> adc->regmap = regmap;
> adc->dev = dev;
> adc->base = reg;
> +
nitpick but should not be in this patch. Please try to clean these out
as they just add noise during review.
> init_completion(&adc->complete);
> mutex_init(&adc->lock);
>
> diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
> index 2bb78d1..5113aaa 100644
> --- a/drivers/iio/adc/qcom-vadc-common.c
> +++ b/drivers/iio/adc/qcom-vadc-common.c
> @@ -89,6 +89,195 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
> { 46, 125000 },
> };
>
> +static const struct vadc_map_pt adcmap7_die_temp[] = {
> + { 433700, 1967},
> + { 473100, 1964},
> + { 512400, 1957},
> + { 551500, 1949},
> + { 590500, 1940},
> + { 629300, 1930},
> + { 667900, 1921},
> + { 706400, 1910},
> + { 744600, 1896},
> + { 782500, 1878},
> + { 820100, 1859},
> + { 857300, 0},
> +};
> +
> +/*
> + * Resistance to temperature table for 100k pull up for NTCG104EF104.
> + */
> +static const struct vadc_map_pt adcmap7_100k[] = {
> + { 4250657, -40960 },
> + { 3962085, -39936 },
> + { 3694875, -38912 },
> + { 3447322, -37888 },
> + { 3217867, -36864 },
> + { 3005082, -35840 },
> + { 2807660, -34816 },
> + { 2624405, -33792 },
> + { 2454218, -32768 },
> + { 2296094, -31744 },
> + { 2149108, -30720 },
> + { 2012414, -29696 },
> + { 1885232, -28672 },
> + { 1766846, -27648 },
> + { 1656598, -26624 },
> + { 1553884, -25600 },
> + { 1458147, -24576 },
> + { 1368873, -23552 },
> + { 1285590, -22528 },
> + { 1207863, -21504 },
> + { 1135290, -20480 },
> + { 1067501, -19456 },
> + { 1004155, -18432 },
> + { 944935, -17408 },
> + { 889550, -16384 },
> + { 837731, -15360 },
> + { 789229, -14336 },
> + { 743813, -13312 },
> + { 701271, -12288 },
> + { 661405, -11264 },
> + { 624032, -10240 },
> + { 588982, -9216 },
> + { 556100, -8192 },
> + { 525239, -7168 },
> + { 496264, -6144 },
> + { 469050, -5120 },
> + { 443480, -4096 },
> + { 419448, -3072 },
> + { 396851, -2048 },
> + { 375597, -1024 },
> + { 355598, 0 },
> + { 336775, 1024 },
> + { 319052, 2048 },
> + { 302359, 3072 },
> + { 286630, 4096 },
> + { 271806, 5120 },
> + { 257829, 6144 },
> + { 244646, 7168 },
> + { 232209, 8192 },
> + { 220471, 9216 },
> + { 209390, 10240 },
> + { 198926, 11264 },
> + { 189040, 12288 },
> + { 179698, 13312 },
> + { 170868, 14336 },
> + { 162519, 15360 },
> + { 154622, 16384 },
> + { 147150, 17408 },
> + { 140079, 18432 },
> + { 133385, 19456 },
> + { 127046, 20480 },
> + { 121042, 21504 },
> + { 115352, 22528 },
> + { 109960, 23552 },
> + { 104848, 24576 },
> + { 100000, 25600 },
> + { 95402, 26624 },
> + { 91038, 27648 },
> + { 86897, 28672 },
> + { 82965, 29696 },
> + { 79232, 30720 },
> + { 75686, 31744 },
> + { 72316, 32768 },
> + { 69114, 33792 },
> + { 66070, 34816 },
> + { 63176, 35840 },
> + { 60423, 36864 },
> + { 57804, 37888 },
> + { 55312, 38912 },
> + { 52940, 39936 },
> + { 50681, 40960 },
> + { 48531, 41984 },
> + { 46482, 43008 },
> + { 44530, 44032 },
> + { 42670, 45056 },
> + { 40897, 46080 },
> + { 39207, 47104 },
> + { 37595, 48128 },
> + { 36057, 49152 },
> + { 34590, 50176 },
> + { 33190, 51200 },
> + { 31853, 52224 },
> + { 30577, 53248 },
> + { 29358, 54272 },
> + { 28194, 55296 },
> + { 27082, 56320 },
> + { 26020, 57344 },
> + { 25004, 58368 },
> + { 24033, 59392 },
> + { 23104, 60416 },
> + { 22216, 61440 },
> + { 21367, 62464 },
> + { 20554, 63488 },
> + { 19776, 64512 },
> + { 19031, 65536 },
> + { 18318, 66560 },
> + { 17636, 67584 },
> + { 16982, 68608 },
> + { 16355, 69632 },
> + { 15755, 70656 },
> + { 15180, 71680 },
> + { 14628, 72704 },
> + { 14099, 73728 },
> + { 13592, 74752 },
> + { 13106, 75776 },
> + { 12640, 76800 },
> + { 12192, 77824 },
> + { 11762, 78848 },
> + { 11350, 79872 },
> + { 10954, 80896 },
> + { 10574, 81920 },
> + { 10209, 82944 },
> + { 9858, 83968 },
> + { 9521, 84992 },
> + { 9197, 86016 },
> + { 8886, 87040 },
> + { 8587, 88064 },
> + { 8299, 89088 },
> + { 8023, 90112 },
> + { 7757, 91136 },
> + { 7501, 92160 },
> + { 7254, 93184 },
> + { 7017, 94208 },
> + { 6789, 95232 },
> + { 6570, 96256 },
> + { 6358, 97280 },
> + { 6155, 98304 },
> + { 5959, 99328 },
> + { 5770, 100352 },
> + { 5588, 101376 },
> + { 5412, 102400 },
> + { 5243, 103424 },
> + { 5080, 104448 },
> + { 4923, 105472 },
> + { 4771, 106496 },
> + { 4625, 107520 },
> + { 4484, 108544 },
> + { 4348, 109568 },
> + { 4217, 110592 },
> + { 4090, 111616 },
> + { 3968, 112640 },
> + { 3850, 113664 },
> + { 3736, 114688 },
> + { 3626, 115712 },
> + { 3519, 116736 },
> + { 3417, 117760 },
> + { 3317, 118784 },
> + { 3221, 119808 },
> + { 3129, 120832 },
> + { 3039, 121856 },
> + { 2952, 122880 },
> + { 2868, 123904 },
> + { 2787, 124928 },
> + { 2709, 125952 },
> + { 2633, 126976 },
> + { 2560, 128000 },
> + { 2489, 129024 },
> + { 2420, 130048 }
> +};
> +
> static int qcom_vadc_scale_hw_calib_volt(
> const struct vadc_prescale_ratio *prescale,
> const struct adc5_data *data,
> @@ -97,6 +286,10 @@ static int qcom_vadc_scale_hw_calib_therm(
> const struct vadc_prescale_ratio *prescale,
> const struct adc5_data *data,
> u16 adc_code, int *result_mdec);
> +static int qcom_vadc7_scale_hw_calib_therm(
> + const struct vadc_prescale_ratio *prescale,
> + const struct adc5_data *data,
> + u16 adc_code, int *result_mdec);
> static int qcom_vadc_scale_hw_smb_temp(
> const struct vadc_prescale_ratio *prescale,
> const struct adc5_data *data,
> @@ -109,12 +302,20 @@ static int qcom_vadc_scale_hw_calib_die_temp(
> const struct vadc_prescale_ratio *prescale,
> const struct adc5_data *data,
> u16 adc_code, int *result_mdec);
> +static int qcom_vadc7_scale_hw_calib_die_temp(
> + const struct vadc_prescale_ratio *prescale,
> + const struct adc5_data *data,
> + u16 adc_code, int *result_mdec);
>
> static struct qcom_adc5_scale_type scale_adc5_fn[] = {
> [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
> [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
> [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
> + [SCALE_HW_CALIB_THERM_100K_PU_PM7] = {
> + qcom_vadc7_scale_hw_calib_therm},
> [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
> + [SCALE_HW_CALIB_PMIC_THERM_PM7] = {
> + qcom_vadc7_scale_hw_calib_die_temp},
> [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
> [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
> };
> @@ -291,6 +492,32 @@ static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
> return (int) voltage;
> }
>
> +static int qcom_vadc7_scale_hw_calib_therm(
> + const struct vadc_prescale_ratio *prescale,
> + const struct adc5_data *data,
> + u16 adc_code, int *result_mdec)
> +{
> + s64 resistance = adc_code;
> + int ret, result;
> +
> + if (adc_code >= RATIO_MAX_ADC7)
> + return -EINVAL;
> +
> + /* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
> + resistance *= R_PU_100K;
> + resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code);
> +
> + ret = qcom_vadc_map_voltage_temp(adcmap7_100k,
> + ARRAY_SIZE(adcmap7_100k),
> + resistance, &result);
> + if (ret)
> + return ret;
> +
> + *result_mdec = result;
> +
> + return 0;
> +}
> +
> static int qcom_vadc_scale_hw_calib_volt(
> const struct vadc_prescale_ratio *prescale,
> const struct adc5_data *data,
> @@ -330,6 +557,41 @@ static int qcom_vadc_scale_hw_calib_die_temp(
> return 0;
> }
>
> +static int qcom_vadc7_scale_hw_calib_die_temp(
> + const struct vadc_prescale_ratio *prescale,
> + const struct adc5_data *data,
> + u16 adc_code, int *result_mdec)
> +{
> +
> + int voltage, vtemp0, temp, i;
> +
> + voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
> + prescale, data, 1);
> +
> + if (adcmap7_die_temp[0].x > voltage) {
> + *result_mdec = DIE_TEMP_ADC7_SCALE_1;
> + return 0;
> + }
> +
> + if (adcmap7_die_temp[ARRAY_SIZE(adcmap7_die_temp) - 1].x <= voltage) {
> + *result_mdec = DIE_TEMP_ADC7_MAX;
> + return 0;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++)
> + if (adcmap7_die_temp[i].x > voltage)
> + break;
> +
> + vtemp0 = adcmap7_die_temp[i - 1].x;
> + voltage = voltage - vtemp0;
> + temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
> + adcmap7_die_temp[i - 1].y);
> + temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1));
> + *result_mdec = temp;
> +
> + return 0;
> +}
> +
> static int qcom_vadc_scale_hw_smb_temp(
> const struct vadc_prescale_ratio *prescale,
> const struct adc5_data *data,
> diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
> index 6a7553f..17b2fc4 100644
> --- a/drivers/iio/adc/qcom-vadc-common.h
> +++ b/drivers/iio/adc/qcom-vadc-common.h
> @@ -49,6 +49,14 @@
> #define ADC5_FULL_SCALE_CODE 0x70e4
> #define ADC5_USR_DATA_CHECK 0x8000
>
> +#define R_PU_100K 100000
> +#define RATIO_MAX_ADC7 BIT(14)
> +
> +#define DIE_TEMP_ADC7_SCALE_1 -60000
> +#define DIE_TEMP_ADC7_SCALE_2 20000
> +#define DIE_TEMP_ADC7_SCALE_FACTOR 1000
> +#define DIE_TEMP_ADC7_MAX 160000
> +
> /**
> * struct vadc_map_pt - Map the graph representation for ADC channel
> * @x: Represent the ADC digitized code.
> @@ -110,8 +118,12 @@ struct vadc_prescale_ratio {
> * lookup table. The hardware applies offset/slope to adc code.
> * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
> * 100k pullup. The hardware applies offset/slope to adc code.
> + * SCALE_HW_CALIB_THERM_100K_PU_PM7: Returns temperature in millidegC using
> + * lookup table for PMIC7. The hardware applies offset/slope to adc code.
> * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
> * The hardware applies offset/slope to adc code.
> + * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
> + * The hardware applies offset/slope to adc code. This is for PMIC7.
> * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
> * charger temperature.
> * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
> @@ -126,7 +138,9 @@ enum vadc_scale_fn_type {
> SCALE_HW_CALIB_DEFAULT,
> SCALE_HW_CALIB_THERM_100K_PULLUP,
> SCALE_HW_CALIB_XOTHERM,
> + SCALE_HW_CALIB_THERM_100K_PU_PM7,
> SCALE_HW_CALIB_PMIC_THERM,
> + SCALE_HW_CALIB_PMIC_THERM_PM7,
> SCALE_HW_CALIB_PM5_CHG_TEMP,
> SCALE_HW_CALIB_PM5_SMB_TEMP,
> SCALE_HW_CALIB_INVALID,
^ permalink raw reply
* Re: [PATCH V6 2/7] iio: adc: Add PMIC7 ADC bindings
From: Jonathan Cameron @ 2020-05-31 10:45 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland, Amit Kucheria,
linux-iio, linux-arm-msm, linux-arm-msm-owner
In-Reply-To: <1590684869-15400-3-git-send-email-jprakash@codeaurora.org>
On Thu, 28 May 2020 22:24:24 +0530
Jishnu Prakash <jprakash@codeaurora.org> wrote:
> Add documentation for PMIC7 ADC peripheral.
> For the PMIC7-type PMICs, ADC peripheral is present in HW for the
> following PMICs: PMK8350, PM8350, PM8350b, PMR735a and PMR735b.
> Of these, only the ADC peripheral on PMK8350 is exposed directly to SW.
> If SW needs to communicate with ADCs on other PMICs, it specifies the
> PMIC to PMK8350 through the newly added SID register and communication
> between PMK8350 ADC and other PMIC ADCs is carried out through
> PBS(Programmable Boot Sequence) at the firmware level.
>
> In addition, add definitions for ADC channels and virtual channel
> definitions (combination of ADC channel number and PMIC SID number)
> per PMIC, to be used by ADC clients for PMIC7.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
> Reviewed-by: Amit Kucheria <amit.kucheria@linaro.org>
> Reviewed-by: Rob Herring <robh@kernel.org>
Applied.
Thanks,
J
> ---
> .../bindings/iio/adc/qcom,spmi-vadc.yaml | 38 ++++++++--
> include/dt-bindings/iio/qcom,spmi-adc7-pm8350.h | 67 ++++++++++++++++
> include/dt-bindings/iio/qcom,spmi-adc7-pm8350b.h | 88 ++++++++++++++++++++++
> include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h | 46 +++++++++++
> include/dt-bindings/iio/qcom,spmi-adc7-pmr735a.h | 28 +++++++
> include/dt-bindings/iio/qcom,spmi-adc7-pmr735b.h | 28 +++++++
> include/dt-bindings/iio/qcom,spmi-vadc.h | 78 ++++++++++++++++++-
> 7 files changed, 366 insertions(+), 7 deletions(-)
> create mode 100644 include/dt-bindings/iio/qcom,spmi-adc7-pm8350.h
> create mode 100644 include/dt-bindings/iio/qcom,spmi-adc7-pm8350b.h
> create mode 100644 include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h
> create mode 100644 include/dt-bindings/iio/qcom,spmi-adc7-pmr735a.h
> create mode 100644 include/dt-bindings/iio/qcom,spmi-adc7-pmr735b.h
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> index de8d243..e6263b6 100644
> --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> @@ -13,7 +13,7 @@ maintainers:
> description: |
> SPMI PMIC voltage ADC (VADC) provides interface to clients to read
> voltage. The VADC is a 15-bit sigma-delta ADC.
> - SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read
> + SPMI PMIC5/PMIC7 voltage ADC (ADC) provides interface to clients to read
> voltage. The VADC is a 16-bit sigma-delta ADC.
>
> properties:
> @@ -28,6 +28,7 @@ properties:
> - qcom,spmi-vadc
> - qcom,spmi-adc5
> - qcom,spmi-adc-rev2
> + - qcom,spmi-adc7
>
> reg:
> description: VADC base address in the SPMI PMIC register map
> @@ -70,6 +71,8 @@ patternProperties:
> description: |
> ADC channel number.
> See include/dt-bindings/iio/qcom,spmi-vadc.h
> + For PMIC7 ADC, the channel numbers are specified separately per PMIC
> + in the PMIC-specific files in include/dt-bindings/iio/.
>
> label:
> $ref: /schemas/types.yaml#/definitions/string
> @@ -113,11 +116,11 @@ patternProperties:
> channel calibration. If property is not found, channel will be
> calibrated with 0.625V and 1.25V reference channels, also
> known as absolute calibration.
> - - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
> - if this property is specified VADC will use the VDD reference (1.875V)
> - and GND for channel calibration. If property is not found, channel
> - will be calibrated with 0V and 1.25V reference channels, also known
> - as absolute calibration.
> + - For compatible property "qcom,spmi-adc5", "qcom,spmi-adc7" and
> + "qcom,spmi-adc-rev2", if this property is specified VADC will use
> + the VDD reference (1.875V) and GND for channel calibration. If
> + property is not found, channel will be calibrated with 0V and 1.25V
> + reference channels, also known as absolute calibration.
> type: boolean
>
> qcom,hw-settle-time:
> @@ -208,6 +211,29 @@ allOf:
> enum: [ 1, 2, 4, 8, 16 ]
> default: 1
>
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: qcom,spmi-adc7
> +
> + then:
> + patternProperties:
> + "^.*@[0-9a-f]+$":
> + properties:
> + qcom,decimation:
> + enum: [ 85, 340, 1360 ]
> + default: 1360
> +
> + qcom,hw-settle-time:
> + enum: [ 15, 100, 200, 300, 400, 500, 600, 700, 1000, 2000, 4000,
> + 8000, 16000, 32000, 64000, 128000 ]
> + default: 15
> +
> + qcom,avg-samples:
> + enum: [ 1, 2, 4, 8, 16 ]
> + default: 1
> +
> examples:
> - |
> spmi_bus {
> diff --git a/include/dt-bindings/iio/qcom,spmi-adc7-pm8350.h b/include/dt-bindings/iio/qcom,spmi-adc7-pm8350.h
> new file mode 100644
> index 0000000..9426f27
> --- /dev/null
> +++ b/include/dt-bindings/iio/qcom,spmi-adc7-pm8350.h
> @@ -0,0 +1,67 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2020, The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
> +#define _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
> +
> +#ifndef PM8350_SID
> +#define PM8350_SID 1
> +#endif
> +
> +/* ADC channels for PM8350_ADC for PMIC7 */
> +#define PM8350_ADC7_REF_GND (PM8350_SID << 8 | 0x0)
> +#define PM8350_ADC7_1P25VREF (PM8350_SID << 8 | 0x01)
> +#define PM8350_ADC7_VREF_VADC (PM8350_SID << 8 | 0x02)
> +#define PM8350_ADC7_DIE_TEMP (PM8350_SID << 8 | 0x03)
> +
> +#define PM8350_ADC7_AMUX_THM1 (PM8350_SID << 8 | 0x04)
> +#define PM8350_ADC7_AMUX_THM2 (PM8350_SID << 8 | 0x05)
> +#define PM8350_ADC7_AMUX_THM3 (PM8350_SID << 8 | 0x06)
> +#define PM8350_ADC7_AMUX_THM4 (PM8350_SID << 8 | 0x07)
> +#define PM8350_ADC7_AMUX_THM5 (PM8350_SID << 8 | 0x08)
> +#define PM8350_ADC7_GPIO1 (PM8350_SID << 8 | 0x0a)
> +#define PM8350_ADC7_GPIO2 (PM8350_SID << 8 | 0x0b)
> +#define PM8350_ADC7_GPIO3 (PM8350_SID << 8 | 0x0c)
> +#define PM8350_ADC7_GPIO4 (PM8350_SID << 8 | 0x0d)
> +
> +/* 30k pull-up1 */
> +#define PM8350_ADC7_AMUX_THM1_30K_PU (PM8350_SID << 8 | 0x24)
> +#define PM8350_ADC7_AMUX_THM2_30K_PU (PM8350_SID << 8 | 0x25)
> +#define PM8350_ADC7_AMUX_THM3_30K_PU (PM8350_SID << 8 | 0x26)
> +#define PM8350_ADC7_AMUX_THM4_30K_PU (PM8350_SID << 8 | 0x27)
> +#define PM8350_ADC7_AMUX_THM5_30K_PU (PM8350_SID << 8 | 0x28)
> +#define PM8350_ADC7_GPIO1_30K_PU (PM8350_SID << 8 | 0x2a)
> +#define PM8350_ADC7_GPIO2_30K_PU (PM8350_SID << 8 | 0x2b)
> +#define PM8350_ADC7_GPIO3_30K_PU (PM8350_SID << 8 | 0x2c)
> +#define PM8350_ADC7_GPIO4_30K_PU (PM8350_SID << 8 | 0x2d)
> +
> +/* 100k pull-up2 */
> +#define PM8350_ADC7_AMUX_THM1_100K_PU (PM8350_SID << 8 | 0x44)
> +#define PM8350_ADC7_AMUX_THM2_100K_PU (PM8350_SID << 8 | 0x45)
> +#define PM8350_ADC7_AMUX_THM3_100K_PU (PM8350_SID << 8 | 0x46)
> +#define PM8350_ADC7_AMUX_THM4_100K_PU (PM8350_SID << 8 | 0x47)
> +#define PM8350_ADC7_AMUX_THM5_100K_PU (PM8350_SID << 8 | 0x48)
> +#define PM8350_ADC7_GPIO1_100K_PU (PM8350_SID << 8 | 0x4a)
> +#define PM8350_ADC7_GPIO2_100K_PU (PM8350_SID << 8 | 0x4b)
> +#define PM8350_ADC7_GPIO3_100K_PU (PM8350_SID << 8 | 0x4c)
> +#define PM8350_ADC7_GPIO4_100K_PU (PM8350_SID << 8 | 0x4d)
> +
> +/* 400k pull-up3 */
> +#define PM8350_ADC7_AMUX_THM1_400K_PU (PM8350_SID << 8 | 0x64)
> +#define PM8350_ADC7_AMUX_THM2_400K_PU (PM8350_SID << 8 | 0x65)
> +#define PM8350_ADC7_AMUX_THM3_400K_PU (PM8350_SID << 8 | 0x66)
> +#define PM8350_ADC7_AMUX_THM4_400K_PU (PM8350_SID << 8 | 0x67)
> +#define PM8350_ADC7_AMUX_THM5_400K_PU (PM8350_SID << 8 | 0x68)
> +#define PM8350_ADC7_GPIO1_400K_PU (PM8350_SID << 8 | 0x6a)
> +#define PM8350_ADC7_GPIO2_400K_PU (PM8350_SID << 8 | 0x6b)
> +#define PM8350_ADC7_GPIO3_400K_PU (PM8350_SID << 8 | 0x6c)
> +#define PM8350_ADC7_GPIO4_400K_PU (PM8350_SID << 8 | 0x6d)
> +
> +/* 1/3 Divider */
> +#define PM8350_ADC7_GPIO4_DIV3 (PM8350_SID << 8 | 0x8d)
> +
> +#define PM8350_ADC7_VPH_PWR (PM8350_SID << 8 | 0x8e)
> +
> +#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H */
> diff --git a/include/dt-bindings/iio/qcom,spmi-adc7-pm8350b.h b/include/dt-bindings/iio/qcom,spmi-adc7-pm8350b.h
> new file mode 100644
> index 0000000..dc2497c
> --- /dev/null
> +++ b/include/dt-bindings/iio/qcom,spmi-adc7-pm8350b.h
> @@ -0,0 +1,88 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2020 The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PM8350B_H
> +#define _DT_BINDINGS_QCOM_SPMI_VADC_PM8350B_H
> +
> +#ifndef PM8350B_SID
> +#define PM8350B_SID 3
> +#endif
> +
> +/* ADC channels for PM8350B_ADC for PMIC7 */
> +#define PM8350B_ADC7_REF_GND (PM8350B_SID << 8 | 0x0)
> +#define PM8350B_ADC7_1P25VREF (PM8350B_SID << 8 | 0x01)
> +#define PM8350B_ADC7_VREF_VADC (PM8350B_SID << 8 | 0x02)
> +#define PM8350B_ADC7_DIE_TEMP (PM8350B_SID << 8 | 0x03)
> +
> +#define PM8350B_ADC7_AMUX_THM1 (PM8350B_SID << 8 | 0x04)
> +#define PM8350B_ADC7_AMUX_THM2 (PM8350B_SID << 8 | 0x05)
> +#define PM8350B_ADC7_AMUX_THM3 (PM8350B_SID << 8 | 0x06)
> +#define PM8350B_ADC7_AMUX_THM4 (PM8350B_SID << 8 | 0x07)
> +#define PM8350B_ADC7_AMUX_THM5 (PM8350B_SID << 8 | 0x08)
> +#define PM8350B_ADC7_AMUX_THM6 (PM8350B_SID << 8 | 0x09)
> +#define PM8350B_ADC7_GPIO1 (PM8350B_SID << 8 | 0x0a)
> +#define PM8350B_ADC7_GPIO2 (PM8350B_SID << 8 | 0x0b)
> +#define PM8350B_ADC7_GPIO3 (PM8350B_SID << 8 | 0x0c)
> +#define PM8350B_ADC7_GPIO4 (PM8350B_SID << 8 | 0x0d)
> +
> +#define PM8350B_ADC7_CHG_TEMP (PM8350B_SID << 8 | 0x10)
> +#define PM8350B_ADC7_USB_IN_V_16 (PM8350B_SID << 8 | 0x11)
> +#define PM8350B_ADC7_VDC_16 (PM8350B_SID << 8 | 0x12)
> +#define PM8350B_ADC7_CC1_ID (PM8350B_SID << 8 | 0x13)
> +#define PM8350B_ADC7_VREF_BAT_THERM (PM8350B_SID << 8 | 0x15)
> +#define PM8350B_ADC7_IIN_FB (PM8350B_SID << 8 | 0x17)
> +
> +/* 30k pull-up1 */
> +#define PM8350B_ADC7_AMUX_THM1_30K_PU (PM8350B_SID << 8 | 0x24)
> +#define PM8350B_ADC7_AMUX_THM2_30K_PU (PM8350B_SID << 8 | 0x25)
> +#define PM8350B_ADC7_AMUX_THM3_30K_PU (PM8350B_SID << 8 | 0x26)
> +#define PM8350B_ADC7_AMUX_THM4_30K_PU (PM8350B_SID << 8 | 0x27)
> +#define PM8350B_ADC7_AMUX_THM5_30K_PU (PM8350B_SID << 8 | 0x28)
> +#define PM8350B_ADC7_AMUX_THM6_30K_PU (PM8350B_SID << 8 | 0x29)
> +#define PM8350B_ADC7_GPIO1_30K_PU (PM8350B_SID << 8 | 0x2a)
> +#define PM8350B_ADC7_GPIO2_30K_PU (PM8350B_SID << 8 | 0x2b)
> +#define PM8350B_ADC7_GPIO3_30K_PU (PM8350B_SID << 8 | 0x2c)
> +#define PM8350B_ADC7_GPIO4_30K_PU (PM8350B_SID << 8 | 0x2d)
> +#define PM8350B_ADC7_CC1_ID_30K_PU (PM8350B_SID << 8 | 0x33)
> +
> +/* 100k pull-up2 */
> +#define PM8350B_ADC7_AMUX_THM1_100K_PU (PM8350B_SID << 8 | 0x44)
> +#define PM8350B_ADC7_AMUX_THM2_100K_PU (PM8350B_SID << 8 | 0x45)
> +#define PM8350B_ADC7_AMUX_THM3_100K_PU (PM8350B_SID << 8 | 0x46)
> +#define PM8350B_ADC7_AMUX_THM4_100K_PU (PM8350B_SID << 8 | 0x47)
> +#define PM8350B_ADC7_AMUX_THM5_100K_PU (PM8350B_SID << 8 | 0x48)
> +#define PM8350B_ADC7_AMUX_THM6_100K_PU (PM8350B_SID << 8 | 0x49)
> +#define PM8350B_ADC7_GPIO1_100K_PU (PM8350B_SID << 8 | 0x4a)
> +#define PM8350B_ADC7_GPIO2_100K_PU (PM8350B_SID << 8 | 0x4b)
> +#define PM8350B_ADC7_GPIO3_100K_PU (PM8350B_SID << 8 | 0x4c)
> +#define PM8350B_ADC7_GPIO4_100K_PU (PM8350B_SID << 8 | 0x4d)
> +#define PM8350B_ADC7_CC1_ID_100K_PU (PM8350B_SID << 8 | 0x53)
> +
> +/* 400k pull-up3 */
> +#define PM8350B_ADC7_AMUX_THM1_400K_PU (PM8350B_SID << 8 | 0x64)
> +#define PM8350B_ADC7_AMUX_THM2_400K_PU (PM8350B_SID << 8 | 0x65)
> +#define PM8350B_ADC7_AMUX_THM3_400K_PU (PM8350B_SID << 8 | 0x66)
> +#define PM8350B_ADC7_AMUX_THM4_400K_PU (PM8350B_SID << 8 | 0x67)
> +#define PM8350B_ADC7_AMUX_THM5_400K_PU (PM8350B_SID << 8 | 0x68)
> +#define PM8350B_ADC7_AMUX_THM6_400K_PU (PM8350B_SID << 8 | 0x69)
> +#define PM8350B_ADC7_GPIO1_400K_PU (PM8350B_SID << 8 | 0x6a)
> +#define PM8350B_ADC7_GPIO2_400K_PU (PM8350B_SID << 8 | 0x6b)
> +#define PM8350B_ADC7_GPIO3_400K_PU (PM8350B_SID << 8 | 0x6c)
> +#define PM8350B_ADC7_GPIO4_400K_PU (PM8350B_SID << 8 | 0x6d)
> +#define PM8350B_ADC7_CC1_ID_400K_PU (PM8350B_SID << 8 | 0x73)
> +
> +/* 1/3 Divider */
> +#define PM8350B_ADC7_GPIO1_DIV3 (PM8350B_SID << 8 | 0x8a)
> +#define PM8350B_ADC7_GPIO2_DIV3 (PM8350B_SID << 8 | 0x8b)
> +#define PM8350B_ADC7_GPIO3_DIV3 (PM8350B_SID << 8 | 0x8c)
> +#define PM8350B_ADC7_GPIO4_DIV3 (PM8350B_SID << 8 | 0x8d)
> +
> +#define PM8350B_ADC7_VPH_PWR (PM8350B_SID << 8 | 0x8e)
> +#define PM8350B_ADC7_VBAT_SNS (PM8350B_SID << 8 | 0x8f)
> +
> +#define PM8350B_ADC7_SBUx (PM8350B_SID << 8 | 0x94)
> +#define PM8350B_ADC7_VBAT_2S_MID (PM8350B_SID << 8 | 0x96)
> +
> +#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350B_H */
> diff --git a/include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h b/include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h
> new file mode 100644
> index 0000000..6c29687
> --- /dev/null
> +++ b/include/dt-bindings/iio/qcom,spmi-adc7-pmk8350.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2020 The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H
> +#define _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H
> +
> +#ifndef PMK8350_SID
> +#define PMK8350_SID 0
> +#endif
> +
> +/* ADC channels for PMK8350_ADC for PMIC7 */
> +#define PMK8350_ADC7_REF_GND (PMK8350_SID << 8 | 0x0)
> +#define PMK8350_ADC7_1P25VREF (PMK8350_SID << 8 | 0x01)
> +#define PMK8350_ADC7_VREF_VADC (PMK8350_SID << 8 | 0x02)
> +#define PMK8350_ADC7_DIE_TEMP (PMK8350_SID << 8 | 0x03)
> +
> +#define PMK8350_ADC7_AMUX_THM1 (PMK8350_SID << 8 | 0x04)
> +#define PMK8350_ADC7_AMUX_THM2 (PMK8350_SID << 8 | 0x05)
> +#define PMK8350_ADC7_AMUX_THM3 (PMK8350_SID << 8 | 0x06)
> +#define PMK8350_ADC7_AMUX_THM4 (PMK8350_SID << 8 | 0x07)
> +#define PMK8350_ADC7_AMUX_THM5 (PMK8350_SID << 8 | 0x08)
> +
> +/* 30k pull-up1 */
> +#define PMK8350_ADC7_AMUX_THM1_30K_PU (PMK8350_SID << 8 | 0x24)
> +#define PMK8350_ADC7_AMUX_THM2_30K_PU (PMK8350_SID << 8 | 0x25)
> +#define PMK8350_ADC7_AMUX_THM3_30K_PU (PMK8350_SID << 8 | 0x26)
> +#define PMK8350_ADC7_AMUX_THM4_30K_PU (PMK8350_SID << 8 | 0x27)
> +#define PMK8350_ADC7_AMUX_THM5_30K_PU (PMK8350_SID << 8 | 0x28)
> +
> +/* 100k pull-up2 */
> +#define PMK8350_ADC7_AMUX_THM1_100K_PU (PMK8350_SID << 8 | 0x44)
> +#define PMK8350_ADC7_AMUX_THM2_100K_PU (PMK8350_SID << 8 | 0x45)
> +#define PMK8350_ADC7_AMUX_THM3_100K_PU (PMK8350_SID << 8 | 0x46)
> +#define PMK8350_ADC7_AMUX_THM4_100K_PU (PMK8350_SID << 8 | 0x47)
> +#define PMK8350_ADC7_AMUX_THM5_100K_PU (PMK8350_SID << 8 | 0x48)
> +
> +/* 400k pull-up3 */
> +#define PMK8350_ADC7_AMUX_THM1_400K_PU (PMK8350_SID << 8 | 0x64)
> +#define PMK8350_ADC7_AMUX_THM2_400K_PU (PMK8350_SID << 8 | 0x65)
> +#define PMK8350_ADC7_AMUX_THM3_400K_PU (PMK8350_SID << 8 | 0x66)
> +#define PMK8350_ADC7_AMUX_THM4_400K_PU (PMK8350_SID << 8 | 0x67)
> +#define PMK8350_ADC7_AMUX_THM5_400K_PU (PMK8350_SID << 8 | 0x68)
> +
> +#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H */
> diff --git a/include/dt-bindings/iio/qcom,spmi-adc7-pmr735a.h b/include/dt-bindings/iio/qcom,spmi-adc7-pmr735a.h
> new file mode 100644
> index 0000000..d6df1b1
> --- /dev/null
> +++ b/include/dt-bindings/iio/qcom,spmi-adc7-pmr735a.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2020 The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PMR735A_H
> +#define _DT_BINDINGS_QCOM_SPMI_VADC_PMR735A_H
> +
> +#ifndef PMR735A_SID
> +#define PMR735A_SID 4
> +#endif
> +
> +/* ADC channels for PMR735A_ADC for PMIC7 */
> +#define PMR735A_ADC7_REF_GND (PMR735A_SID << 8 | 0x0)
> +#define PMR735A_ADC7_1P25VREF (PMR735A_SID << 8 | 0x01)
> +#define PMR735A_ADC7_VREF_VADC (PMR735A_SID << 8 | 0x02)
> +#define PMR735A_ADC7_DIE_TEMP (PMR735A_SID << 8 | 0x03)
> +
> +#define PMR735A_ADC7_GPIO1 (PMR735A_SID << 8 | 0x0a)
> +#define PMR735A_ADC7_GPIO2 (PMR735A_SID << 8 | 0x0b)
> +#define PMR735A_ADC7_GPIO3 (PMR735A_SID << 8 | 0x0c)
> +
> +/* 100k pull-up2 */
> +#define PMR735A_ADC7_GPIO1_100K_PU (PMR735A_SID << 8 | 0x4a)
> +#define PMR735A_ADC7_GPIO2_100K_PU (PMR735A_SID << 8 | 0x4b)
> +#define PMR735A_ADC7_GPIO3_100K_PU (PMR735A_SID << 8 | 0x4c)
> +
> +#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735A_H */
> diff --git a/include/dt-bindings/iio/qcom,spmi-adc7-pmr735b.h b/include/dt-bindings/iio/qcom,spmi-adc7-pmr735b.h
> new file mode 100644
> index 0000000..8da0e7d
> --- /dev/null
> +++ b/include/dt-bindings/iio/qcom,spmi-adc7-pmr735b.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2020 The Linux Foundation. All rights reserved.
> + */
> +
> +#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PMR735B_H
> +#define _DT_BINDINGS_QCOM_SPMI_VADC_PMR735B_H
> +
> +#ifndef PMR735B_SID
> +#define PMR735B_SID 5
> +#endif
> +
> +/* ADC channels for PMR735B_ADC for PMIC7 */
> +#define PMR735B_ADC7_REF_GND (PMR735B_SID << 8 | 0x0)
> +#define PMR735B_ADC7_1P25VREF (PMR735B_SID << 8 | 0x01)
> +#define PMR735B_ADC7_VREF_VADC (PMR735B_SID << 8 | 0x02)
> +#define PMR735B_ADC7_DIE_TEMP (PMR735B_SID << 8 | 0x03)
> +
> +#define PMR735B_ADC7_GPIO1 (PMR735B_SID << 8 | 0x0a)
> +#define PMR735B_ADC7_GPIO2 (PMR735B_SID << 8 | 0x0b)
> +#define PMR735B_ADC7_GPIO3 (PMR735B_SID << 8 | 0x0c)
> +
> +/* 100k pull-up2 */
> +#define PMR735B_ADC7_GPIO1_100K_PU (PMR735B_SID << 8 | 0x4a)
> +#define PMR735B_ADC7_GPIO2_100K_PU (PMR735B_SID << 8 | 0x4b)
> +#define PMR735B_ADC7_GPIO3_100K_PU (PMR735B_SID << 8 | 0x4c)
> +
> +#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735B_H */
> diff --git a/include/dt-bindings/iio/qcom,spmi-vadc.h b/include/dt-bindings/iio/qcom,spmi-vadc.h
> index 61d556d..08adfe2 100644
> --- a/include/dt-bindings/iio/qcom,spmi-vadc.h
> +++ b/include/dt-bindings/iio/qcom,spmi-vadc.h
> @@ -1,6 +1,6 @@
> /* SPDX-License-Identifier: GPL-2.0 */
> /*
> - * Copyright (c) 2012-2014,2018 The Linux Foundation. All rights reserved.
> + * Copyright (c) 2012-2014,2018,2020 The Linux Foundation. All rights reserved.
> */
>
> #ifndef _DT_BINDINGS_QCOM_SPMI_VADC_H
> @@ -221,4 +221,80 @@
>
> #define ADC5_MAX_CHANNEL 0xc0
>
> +/* ADC channels for ADC for PMIC7 */
> +
> +#define ADC7_REF_GND 0x00
> +#define ADC7_1P25VREF 0x01
> +#define ADC7_VREF_VADC 0x02
> +#define ADC7_DIE_TEMP 0x03
> +
> +#define ADC7_AMUX_THM1 0x04
> +#define ADC7_AMUX_THM2 0x05
> +#define ADC7_AMUX_THM3 0x06
> +#define ADC7_AMUX_THM4 0x07
> +#define ADC7_AMUX_THM5 0x08
> +#define ADC7_AMUX_THM6 0x09
> +#define ADC7_GPIO1 0x0a
> +#define ADC7_GPIO2 0x0b
> +#define ADC7_GPIO3 0x0c
> +#define ADC7_GPIO4 0x0d
> +
> +#define ADC7_CHG_TEMP 0x10
> +#define ADC7_USB_IN_V_16 0x11
> +#define ADC7_VDC_16 0x12
> +#define ADC7_CC1_ID 0x13
> +#define ADC7_VREF_BAT_THERM 0x15
> +#define ADC7_IIN_FB 0x17
> +
> +/* 30k pull-up1 */
> +#define ADC7_AMUX_THM1_30K_PU 0x24
> +#define ADC7_AMUX_THM2_30K_PU 0x25
> +#define ADC7_AMUX_THM3_30K_PU 0x26
> +#define ADC7_AMUX_THM4_30K_PU 0x27
> +#define ADC7_AMUX_THM5_30K_PU 0x28
> +#define ADC7_AMUX_THM6_30K_PU 0x29
> +#define ADC7_GPIO1_30K_PU 0x2a
> +#define ADC7_GPIO2_30K_PU 0x2b
> +#define ADC7_GPIO3_30K_PU 0x2c
> +#define ADC7_GPIO4_30K_PU 0x2d
> +#define ADC7_CC1_ID_30K_PU 0x33
> +
> +/* 100k pull-up2 */
> +#define ADC7_AMUX_THM1_100K_PU 0x44
> +#define ADC7_AMUX_THM2_100K_PU 0x45
> +#define ADC7_AMUX_THM3_100K_PU 0x46
> +#define ADC7_AMUX_THM4_100K_PU 0x47
> +#define ADC7_AMUX_THM5_100K_PU 0x48
> +#define ADC7_AMUX_THM6_100K_PU 0x49
> +#define ADC7_GPIO1_100K_PU 0x4a
> +#define ADC7_GPIO2_100K_PU 0x4b
> +#define ADC7_GPIO3_100K_PU 0x4c
> +#define ADC7_GPIO4_100K_PU 0x4d
> +#define ADC7_CC1_ID_100K_PU 0x53
> +
> +/* 400k pull-up3 */
> +#define ADC7_AMUX_THM1_400K_PU 0x64
> +#define ADC7_AMUX_THM2_400K_PU 0x65
> +#define ADC7_AMUX_THM3_400K_PU 0x66
> +#define ADC7_AMUX_THM4_400K_PU 0x67
> +#define ADC7_AMUX_THM5_400K_PU 0x68
> +#define ADC7_AMUX_THM6_400K_PU 0x69
> +#define ADC7_GPIO1_400K_PU 0x6a
> +#define ADC7_GPIO2_400K_PU 0x6b
> +#define ADC7_GPIO3_400K_PU 0x6c
> +#define ADC7_GPIO4_400K_PU 0x6d
> +#define ADC7_CC1_ID_400K_PU 0x73
> +
> +/* 1/3 Divider */
> +#define ADC7_GPIO1_DIV3 0x8a
> +#define ADC7_GPIO2_DIV3 0x8b
> +#define ADC7_GPIO3_DIV3 0x8c
> +#define ADC7_GPIO4_DIV3 0x8d
> +
> +#define ADC7_VPH_PWR 0x8e
> +#define ADC7_VBAT_SNS 0x8f
> +
> +#define ADC7_SBUx 0x94
> +#define ADC7_VBAT_2S_MID 0x96
> +
> #endif /* _DT_BINDINGS_QCOM_SPMI_VADC_H */
^ permalink raw reply
* Re: [PATCH V6 1/7] iio: adc: Convert the QCOM SPMI ADC bindings to .yaml format
From: Jonathan Cameron @ 2020-05-31 10:36 UTC (permalink / raw)
To: Jishnu Prakash
Cc: agross, bjorn.andersson, devicetree, linux-kernel, mka,
linus.walleij, Jonathan.Cameron, andy.shevchenko, amit.kucheria,
smohanad, kgunda, aghayal, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland, Amit Kucheria,
linux-iio, linux-arm-msm, linux-arm-msm-owner
In-Reply-To: <1590684869-15400-2-git-send-email-jprakash@codeaurora.org>
On Thu, 28 May 2020 22:24:23 +0530
Jishnu Prakash <jprakash@codeaurora.org> wrote:
> Convert the adc bindings from .txt to .yaml format.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
> Reviewed-by: Amit Kucheria <amit.kucheria@linaro.org>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
Jishnu, Patch is fine, but I'd like to have seen a cover
letter and clear statement of changes from v5.
Thanks,
Jonathan
> ---
> .../devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 173 --------------
> .../bindings/iio/adc/qcom,spmi-vadc.yaml | 252 +++++++++++++++++++++
> 2 files changed, 252 insertions(+), 173 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> create mode 100644 Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> deleted file mode 100644
> index c878768..0000000
> --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> +++ /dev/null
> @@ -1,173 +0,0 @@
> -Qualcomm's SPMI PMIC ADC
> -
> -- SPMI PMIC voltage ADC (VADC) provides interface to clients to read
> - voltage. The VADC is a 15-bit sigma-delta ADC.
> -- SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read
> - voltage. The VADC is a 16-bit sigma-delta ADC.
> -
> -VADC node:
> -
> -- compatible:
> - Usage: required
> - Value type: <string>
> - Definition: Should contain "qcom,spmi-vadc".
> - Should contain "qcom,spmi-adc5" for PMIC5 ADC driver.
> - Should contain "qcom,spmi-adc-rev2" for PMIC rev2 ADC driver.
> - Should contain "qcom,pms405-adc" for PMS405 PMIC
> -
> -- reg:
> - Usage: required
> - Value type: <prop-encoded-array>
> - Definition: VADC base address in the SPMI PMIC register map.
> -
> -- #address-cells:
> - Usage: required
> - Value type: <u32>
> - Definition: Must be one. Child node 'reg' property should define ADC
> - channel number.
> -
> -- #size-cells:
> - Usage: required
> - Value type: <u32>
> - Definition: Must be zero.
> -
> -- #io-channel-cells:
> - Usage: required
> - Value type: <u32>
> - Definition: Must be one. For details about IIO bindings see:
> - Documentation/devicetree/bindings/iio/iio-bindings.txt
> -
> -- interrupts:
> - Usage: optional
> - Value type: <prop-encoded-array>
> - Definition: End of conversion interrupt.
> -
> -Channel node properties:
> -
> -- reg:
> - Usage: required
> - Value type: <u32>
> - Definition: ADC channel number.
> - See include/dt-bindings/iio/qcom,spmi-vadc.h
> -
> -- label:
> - Usage: required for "qcom,spmi-adc5" and "qcom,spmi-adc-rev2"
> - Value type: <empty>
> - Definition: ADC input of the platform as seen in the schematics.
> - For thermistor inputs connected to generic AMUX or GPIO inputs
> - these can vary across platform for the same pins. Hence select
> - the platform schematics name for this channel.
> -
> -- qcom,decimation:
> - Usage: optional
> - Value type: <u32>
> - Definition: This parameter is used to decrease ADC sampling rate.
> - Quicker measurements can be made by reducing decimation ratio.
> - - For compatible property "qcom,spmi-vadc", valid values are
> - 512, 1024, 2048, 4096. If property is not found, default value
> - of 512 will be used.
> - - For compatible property "qcom,spmi-adc5", valid values are 250, 420
> - and 840. If property is not found, default value of 840 is used.
> - - For compatible property "qcom,spmi-adc-rev2", valid values are 256,
> - 512 and 1024. If property is not present, default value is 1024.
> -
> -- qcom,pre-scaling:
> - Usage: optional
> - Value type: <u32 array>
> - Definition: Used for scaling the channel input signal before the signal is
> - fed to VADC. The configuration for this node is to know the
> - pre-determined ratio and use it for post scaling. Select one from
> - the following options.
> - <1 1>, <1 3>, <1 4>, <1 6>, <1 20>, <1 8>, <10 81>, <1 10>
> - If property is not found default value depending on chip will be used.
> -
> -- qcom,ratiometric:
> - Usage: optional
> - Value type: <empty>
> - Definition: Channel calibration type.
> - - For compatible property "qcom,spmi-vadc", if this property is
> - specified VADC will use the VDD reference (1.8V) and GND for
> - channel calibration. If property is not found, channel will be
> - calibrated with 0.625V and 1.25V reference channels, also
> - known as absolute calibration.
> - - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
> - if this property is specified VADC will use the VDD reference
> - (1.875V) and GND for channel calibration. If property is not found,
> - channel will be calibrated with 0V and 1.25V reference channels,
> - also known as absolute calibration.
> -
> -- qcom,hw-settle-time:
> - Usage: optional
> - Value type: <u32>
> - Definition: Time between AMUX getting configured and the ADC starting
> - conversion. The 'hw_settle_time' is an index used from valid values
> - and programmed in hardware to achieve the hardware settling delay.
> - - For compatible property "qcom,spmi-vadc" and "qcom,spmi-adc-rev2",
> - Delay = 100us * (hw_settle_time) for hw_settle_time < 11,
> - and 2ms * (hw_settle_time - 10) otherwise.
> - Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800,
> - 900 us and 1, 2, 4, 6, 8, 10 ms.
> - If property is not found, channel will use 0us.
> - - For compatible property "qcom,spmi-adc5", delay = 15us for
> - value 0, 100us * (value) for values < 11,
> - and 2ms * (value - 10) otherwise.
> - Valid values are: 15, 100, 200, 300, 400, 500, 600, 700, 800,
> - 900 us and 1, 2, 4, 6, 8, 10 ms
> - Certain controller digital versions have valid values of
> - 15, 100, 200, 300, 400, 500, 600, 700, 1, 2, 4, 8, 16, 32, 64, 128 ms
> - If property is not found, channel will use 15us.
> -
> -- qcom,avg-samples:
> - Usage: optional
> - Value type: <u32>
> - Definition: Number of samples to be used for measurement.
> - Averaging provides the option to obtain a single measurement
> - from the ADC that is an average of multiple samples. The value
> - selected is 2^(value).
> - - For compatible property "qcom,spmi-vadc", valid values
> - are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
> - If property is not found, 1 sample will be used.
> - - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
> - valid values are: 1, 2, 4, 8, 16
> - If property is not found, 1 sample will be used.
> -
> -NOTE:
> -
> -For compatible property "qcom,spmi-vadc" following channels, also known as
> -reference point channels, are used for result calibration and their channel
> -configuration nodes should be defined:
> -VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
> -VADC_GND_REF and VADC_VDD_VADC.
> -
> -Example:
> -
> -#include <dt-bindings/iio/qcom,spmi-vadc.h>
> -#include <linux/irq.h>
> -/* ... */
> -
> - /* VADC node */
> - pmic_vadc: vadc@3100 {
> - compatible = "qcom,spmi-vadc";
> - reg = <0x3100>;
> - interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
> - #address-cells = <1>;
> - #size-cells = <0>;
> - #io-channel-cells = <1>;
> - io-channel-ranges;
> -
> - /* Channel node */
> - adc-chan@VADC_LR_MUX10_USB_ID {
> - reg = <VADC_LR_MUX10_USB_ID>;
> - qcom,decimation = <512>;
> - qcom,ratiometric;
> - qcom,hw-settle-time = <200>;
> - qcom,avg-samples = <1>;
> - qcom,pre-scaling = <1 3>;
> - };
> - };
> -
> - /* IIO client node */
> - usb {
> - io-channels = <&pmic_vadc VADC_LR_MUX10_USB_ID>;
> - io-channel-names = "vadc";
> - };
> diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> new file mode 100644
> index 0000000..de8d243
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.yaml
> @@ -0,0 +1,252 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/adc/qcom,spmi-vadc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm's SPMI PMIC ADC
> +
> +maintainers:
> + - Andy Gross <agross@kernel.org>
> + - Bjorn Andersson <bjorn.andersson@linaro.org>
> +
> +description: |
> + SPMI PMIC voltage ADC (VADC) provides interface to clients to read
> + voltage. The VADC is a 15-bit sigma-delta ADC.
> + SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read
> + voltage. The VADC is a 16-bit sigma-delta ADC.
> +
> +properties:
> + compatible:
> + oneOf:
> + - items:
> + - const: qcom,pms405-adc
> + - const: qcom,spmi-adc-rev2
> +
> + - items:
> + - enum:
> + - qcom,spmi-vadc
> + - qcom,spmi-adc5
> + - qcom,spmi-adc-rev2
> +
> + reg:
> + description: VADC base address in the SPMI PMIC register map
> + maxItems: 1
> +
> + '#address-cells':
> + const: 1
> +
> + '#size-cells':
> + const: 0
> +
> + '#io-channel-cells':
> + const: 1
> +
> + interrupts:
> + maxItems: 1
> + description:
> + End of conversion interrupt.
> +
> +required:
> + - compatible
> + - reg
> + - '#address-cells'
> + - '#size-cells'
> + - '#io-channel-cells'
> +
> +patternProperties:
> + "^.*@[0-9a-f]+$":
> + type: object
> + description: |
> + Represents the external channels which are connected to the ADC.
> + For compatible property "qcom,spmi-vadc" following channels, also known as
> + reference point channels, are used for result calibration and their channel
> + configuration nodes should be defined:
> + VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
> + VADC_GND_REF and VADC_VDD_VADC.
> +
> + properties:
> + reg:
> + description: |
> + ADC channel number.
> + See include/dt-bindings/iio/qcom,spmi-vadc.h
> +
> + label:
> + $ref: /schemas/types.yaml#/definitions/string
> + description: |
> + ADC input of the platform as seen in the schematics.
> + For thermistor inputs connected to generic AMUX or GPIO inputs
> + these can vary across platform for the same pins. Hence select
> + the platform schematics name for this channel.
> +
> + qcom,decimation:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + This parameter is used to decrease ADC sampling rate.
> + Quicker measurements can be made by reducing decimation ratio.
> +
> + qcom,pre-scaling:
> + description: |
> + Used for scaling the channel input signal before the signal is
> + fed to VADC. The configuration for this node is to know the
> + pre-determined ratio and use it for post scaling. It is a pair of
> + integers, denoting the numerator and denominator of the fraction by which
> + input signal is multiplied. For example, <1 3> indicates the signal is scaled
> + down to 1/3 of its value before ADC measurement.
> + If property is not found default value depending on chip will be used.
> + allOf:
> + - $ref: /schemas/types.yaml#/definitions/uint32-array
> + oneOf:
> + - items:
> + - const: 1
> + - enum: [ 1, 3, 4, 6, 20, 8, 10 ]
> +
> + - items:
> + - const: 10
> + - const: 81
> +
> + qcom,ratiometric:
> + description: |
> + Channel calibration type.
> + - For compatible property "qcom,spmi-vadc", if this property is
> + specified VADC will use the VDD reference (1.8V) and GND for
> + channel calibration. If property is not found, channel will be
> + calibrated with 0.625V and 1.25V reference channels, also
> + known as absolute calibration.
> + - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
> + if this property is specified VADC will use the VDD reference (1.875V)
> + and GND for channel calibration. If property is not found, channel
> + will be calibrated with 0V and 1.25V reference channels, also known
> + as absolute calibration.
> + type: boolean
> +
> + qcom,hw-settle-time:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Time between AMUX getting configured and the ADC starting
> + conversion. The 'hw_settle_time' is an index used from valid values
> + and programmed in hardware to achieve the hardware settling delay.
> +
> + qcom,avg-samples:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Number of samples to be used for measurement.
> + Averaging provides the option to obtain a single measurement
> + from the ADC that is an average of multiple samples. The value
> + selected is 2^(value).
> +
> + required:
> + - reg
> +
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: qcom,spmi-vadc
> +
> + then:
> + patternProperties:
> + "^.*@[0-9a-f]+$":
> + properties:
> + qcom,decimation:
> + enum: [ 512, 1024, 2048, 4096 ]
> + default: 512
> +
> + qcom,hw-settle-time:
> + enum: [ 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1, 2,
> + 4, 6, 8, 10 ]
> + default: 0
> +
> + qcom,avg-samples:
> + enum: [ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 ]
> + default: 1
> +
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: qcom,spmi-adc-rev2
> +
> + then:
> + patternProperties:
> + "^.*@[0-9a-f]+$":
> + properties:
> + qcom,decimation:
> + enum: [ 256, 512, 1024 ]
> + default: 1024
> +
> + qcom,hw-settle-time:
> + enum: [ 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1, 2,
> + 4, 6, 8, 10 ]
> + default: 0
> +
> + qcom,avg-samples:
> + enum: [ 1, 2, 4, 8, 16 ]
> + default: 1
> +
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: qcom,spmi-adc5
> +
> + then:
> + patternProperties:
> + "^.*@[0-9a-f]+$":
> + properties:
> + qcom,decimation:
> + enum: [ 250, 420, 840 ]
> + default: 840
> +
> + qcom,hw-settle-time:
> + enum: [ 15, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1, 2,
> + 4, 6, 8, 10, 16, 32, 64, 128 ]
> + default: 15
> +
> + qcom,avg-samples:
> + enum: [ 1, 2, 4, 8, 16 ]
> + default: 1
> +
> +examples:
> + - |
> + spmi_bus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + /* VADC node */
> + pmic_vadc: adc@3100 {
> + compatible = "qcom,spmi-vadc";
> + reg = <0x3100>;
> + interrupts = <0x0 0x31 0x0 0x1>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + #io-channel-cells = <1>;
> + io-channel-ranges;
> +
> + /* Channel node */
> + adc-chan@39 {
> + reg = <0x39>;
> + qcom,decimation = <512>;
> + qcom,ratiometric;
> + qcom,hw-settle-time = <200>;
> + qcom,avg-samples = <1>;
> + qcom,pre-scaling = <1 3>;
> + };
> +
> + adc-chan@9 {
> + reg = <0x9>;
> + };
> +
> + adc-chan@a {
> + reg = <0xa>;
> + };
> +
> + adc-chan@e {
> + reg = <0xe>;
> + };
> +
> + adc-chan@f {
> + reg = <0xf>;
> + };
> + };
> + };
^ permalink raw reply
* Re: [PATCH v2 4/4] dt-bindings: iio: scd30: add device binding file
From: Jonathan Cameron @ 2020-05-31 10:19 UTC (permalink / raw)
To: Tomasz Duszynski
Cc: linux-iio, linux-kernel, devicetree, robh+dt, andy.shevchenko,
pmeerw
In-Reply-To: <20200530213630.87159-5-tomasz.duszynski@octakon.com>
On Sat, 30 May 2020 23:36:30 +0200
Tomasz Duszynski <tomasz.duszynski@octakon.com> wrote:
> Add SCD30 sensor binding file.
>
> Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
> ---
> .../iio/chemical/sensirion,scd30.yaml | 68 +++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 69 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
> new file mode 100644
> index 000000000000..34cc3925d64d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
> @@ -0,0 +1,68 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/chemical/sensirion,scd30.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Sensirion SCD30 carbon dioxide sensor
> +
> +maintainers:
> + - Tomasz Duszynski <tomasz.duszynski@octakon.com>
> +
> +description: |
> + Air quality sensor capable of measuring co2 concentration, temperature
> + and relative humidity.
> +
> +properties:
> + compatible:
> + enum:
> + - sensirion,scd30
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + vdd-supply: true
> +
> + sensirion,sel-gpios:
> + description: GPIO connected to the SEL line
> + maxItems: 1
> +
> + sensirion,pwm-gpios:
> + description: GPIO connected to the PWM line
> + maxItems: 1
> +
> +required:
> + - compatible
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + # include <dt-bindings/interrupt-controller/irq.h>
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + scd30@61 {
Nodes should have generic names. Not sure we have an appropriate
one in the spec, but as main focus of people using this will be
c02 herpas
c02@61?
Rob may well have a better suggestion!
> + compatible = "sensirion,scd30";
> + reg = <0x61>;
> + vdd-supply = <&vdd>;
> + interrupt-parent = <&gpio0>;
> + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
> + };
> + };
> + - |
> + # include <dt-bindings/interrupt-controller/irq.h>
> + serial {
> + scd30 {
> + compatible = "sensirion,scd30";
> + vdd-supply = <&vdd>;
> + interrupt-parent = <&gpio0>;
> + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
> + };
> + };
> +
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5db4b446c8ba..0ab9cf39e051 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15140,6 +15140,7 @@ F: include/uapi/linux/phantom.h
> SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
> M: Tomasz Duszynski <tomasz.duszynski@octakon.com>
> S: Maintained
> +F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
> F: drivers/iio/chemical/scd30.h
> F: drivers/iio/chemical/scd30_core.c
> F: drivers/iio/chemical/scd30_i2c.c
^ permalink raw reply
* Re: [PATCH v2 1/4] iio: chemical: scd30: add core driver
From: Jonathan Cameron @ 2020-05-31 10:16 UTC (permalink / raw)
To: Tomasz Duszynski
Cc: linux-iio, linux-kernel, devicetree, robh+dt, andy.shevchenko,
pmeerw
In-Reply-To: <20200531105840.27e17f3d@archlinux>
On Sun, 31 May 2020 10:58:40 +0100
Jonathan Cameron <jic23@kernel.org> wrote:
> On Sat, 30 May 2020 23:36:27 +0200
> Tomasz Duszynski <tomasz.duszynski@octakon.com> wrote:
>
> > Add Sensirion SCD30 carbon dioxide core driver.
> >
> > Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
>
> Hi Tomasz
>
> A few things inline. Includes the alignment issue on
> x86_32 that I fell into whilst trying to fix timestamp
> alignment issues. Suggested resolution inline for that.
>
> Thanks,
>
> Jonathan
>
Update below after looking at the way this works with the serial dev.
> > +int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
> > + scd30_command_t command)
> > +{
> > + static const unsigned long scd30_scan_masks[] = { 0x07, 0x00 };
> > + struct scd30_state *state;
> > + struct iio_dev *indio_dev;
> > + int ret;
> > + u16 val;
> > +
> > + indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + state = iio_priv(indio_dev);
> > + state->dev = dev;
>
> Doesn't seem to be used.
>
> > + state->priv = priv;
>
> What's this for? At least at first glance I can't find it being used
> anywhere.
Ah. Used in the serial module. Maybe add a comment to the structure definition
about that.
As is the dev etc. Is it possible to use the
>
> > + state->irq = irq;
> > + state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT;
> > + state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT;
> > + state->command = command;
> > + mutex_init(&state->lock);
> > + init_completion(&state->meas_ready);
> > +
> > + dev_set_drvdata(dev, indio_dev);
> > +
> > + indio_dev->dev.parent = dev;
>
> Side note that there is a series moving this into the core under revision at
> the moment. Hopefully I'll remember to fix this up when applying your patch
> if that one has gone in ahead of it.
>
> > + indio_dev->info = &scd30_info;
> > + indio_dev->name = name;
> > + indio_dev->channels = scd30_channels;
> > + indio_dev->num_channels = ARRAY_SIZE(scd30_channels);
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > + indio_dev->available_scan_masks = scd30_scan_masks;
> > +
> > + state->vdd = devm_regulator_get(dev, "vdd");
> > + if (IS_ERR(state->vdd)) {
> > + if (PTR_ERR(state->vdd) == -EPROBE_DEFER)
> > + return -EPROBE_DEFER;
> > +
> > + dev_err(dev, "failed to get regulator\n");
> > + return PTR_ERR(state->vdd);
> > + }
> > +
> > + ret = regulator_enable(state->vdd);
> > + if (ret)
> > + return ret;
> > +
> > + ret = devm_add_action_or_reset(dev, scd30_disable_regulator, state);
> > + if (ret)
> > + return ret;
> > +
...
^ permalink raw reply
* Re: [PATCH v2 3/4] iio: chemical: scd30: add serial interface driver
From: Jonathan Cameron @ 2020-05-31 10:15 UTC (permalink / raw)
To: Tomasz Duszynski
Cc: linux-iio, linux-kernel, devicetree, robh+dt, andy.shevchenko,
pmeerw
In-Reply-To: <20200530213630.87159-4-tomasz.duszynski@octakon.com>
On Sat, 30 May 2020 23:36:29 +0200
Tomasz Duszynski <tomasz.duszynski@octakon.com> wrote:
> Add serial interface driver for the SCD30 sensor.
>
> Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
Ah Now I see why you had those extra elements in the iio_priv
structure.
Hmm. serdev_device callbacks using the top level device drvdata
is a bit annoying. Really feels to me like they should have
their own priv data for those callbacks given the device
drvdata gets used for so many other things.
Oh well. Guess this is the best we can do!
Jonathan
> ---
> MAINTAINERS | 1 +
> drivers/iio/chemical/Kconfig | 11 ++
> drivers/iio/chemical/Makefile | 1 +
> drivers/iio/chemical/scd30_serial.c | 266 ++++++++++++++++++++++++++++
> 4 files changed, 279 insertions(+)
> create mode 100644 drivers/iio/chemical/scd30_serial.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 13aed3473b7e..5db4b446c8ba 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15143,6 +15143,7 @@ S: Maintained
> F: drivers/iio/chemical/scd30.h
> F: drivers/iio/chemical/scd30_core.c
> F: drivers/iio/chemical/scd30_i2c.c
> +F: drivers/iio/chemical/scd30_serial.c
>
> SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
> M: Tomasz Duszynski <tduszyns@gmail.com>
> diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
> index 970d34888c2e..10bb431bc3ce 100644
> --- a/drivers/iio/chemical/Kconfig
> +++ b/drivers/iio/chemical/Kconfig
> @@ -107,6 +107,17 @@ config SCD30_I2C
> To compile this driver as a module, choose M here: the module will
> be called scd30_i2c.
>
> +config SCD30_SERIAL
> + tristate "SCD30 carbon dioxide sensor serial driver"
> + depends on SCD30_CORE && SERIAL_DEV_BUS
> + select CRC16
> + help
> + Say Y here to build support for the Sensirion SCD30 serial interface
> + driver.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called scd30_serial.
> +
> config SENSIRION_SGP30
> tristate "Sensirion SGPxx gas sensors"
> depends on I2C
> diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
> index 0966ca34e34b..fef63dd5bf92 100644
> --- a/drivers/iio/chemical/Makefile
> +++ b/drivers/iio/chemical/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
> obj-$(CONFIG_PMS7003) += pms7003.o
> obj-$(CONFIG_SCD30_CORE) += scd30_core.o
> obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o
> +obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o
> obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
> obj-$(CONFIG_SPS30) += sps30.o
> obj-$(CONFIG_VZ89X) += vz89x.o
> diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c
> new file mode 100644
> index 000000000000..07d7d3110fe0
> --- /dev/null
> +++ b/drivers/iio/chemical/scd30_serial.c
> @@ -0,0 +1,266 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sensirion SCD30 carbon dioxide sensor serial driver
> + *
> + * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
> + */
> +#include <linux/crc16.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/iio/iio.h>
> +#include <linux/jiffies.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/serdev.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <asm/unaligned.h>
> +
> +#include "scd30.h"
> +
> +#define SCD30_SERDEV_ADDR 0x61
> +#define SCD30_SERDEV_WRITE 0x06
> +#define SCD30_SERDEV_READ 0x03
> +#define SCD30_SERDEV_MAX_BUF_SIZE 17
> +#define SCD30_SERDEV_RX_HEADER_SIZE 3
> +#define SCD30_SERDEV_CRC_SIZE 2
> +#define SCD30_SERDEV_TIMEOUT msecs_to_jiffies(200)
> +
> +struct scd30_serdev_priv {
> + struct completion meas_ready;
> + char *buf;
> + int num_expected;
> + int num;
> +};
> +
> +static u16 scd30_serdev_cmd_lookup_tbl[] = {
> + [CMD_START_MEAS] = 0x0036,
> + [CMD_STOP_MEAS] = 0x0037,
> + [CMD_MEAS_INTERVAL] = 0x0025,
> + [CMD_MEAS_READY] = 0x0027,
> + [CMD_READ_MEAS] = 0x0028,
> + [CMD_ASC] = 0x003a,
> + [CMD_FRC] = 0x0039,
> + [CMD_TEMP_OFFSET] = 0x003b,
> + [CMD_FW_VERSION] = 0x0020,
> + [CMD_RESET] = 0x0034,
> +};
> +
> +static u16 scd30_serdev_calc_crc(const char *buf, int size)
> +{
> + return crc16(0xffff, buf, size);
> +}
> +
> +static int scd30_serdev_xfer(struct scd30_state *state, char *txbuf, int txsize,
> + char *rxbuf, int rxsize)
> +{
> + struct serdev_device *serdev = to_serdev_device(state->dev);
> + struct scd30_serdev_priv *priv = state->priv;
> + int ret;
> +
> + priv->buf = rxbuf;
> + priv->num_expected = rxsize;
> + priv->num = 0;
> +
> + ret = serdev_device_write(serdev, txbuf, txsize, SCD30_SERDEV_TIMEOUT);
> + if (ret < txsize)
> + return ret < 0 ? ret : -EIO;
> +
> + ret = wait_for_completion_interruptible_timeout(&priv->meas_ready,
> + SCD30_SERDEV_TIMEOUT);
> + if (ret > 0)
> + ret = 0;
> + else if (!ret)
> + ret = -ETIMEDOUT;
> +
> + return ret;
> +}
> +
> +static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd,
> + u16 arg, void *response, int size)
> +{
> + /*
> + * Communication over serial line is based on modbus protocol (or rather
> + * its variation called modbus over serial to be precise). Upon
> + * receiving a request device should reply with response.
> + *
> + * Frame below represents a request message. Each field takes
> + * exactly one byte.
> + *
> + * +------+------+-----+-----+-------+-------+-----+-----+
> + * | dev | op | reg | reg | byte1 | byte0 | crc | crc |
> + * | addr | code | msb | lsb | | | lsb | msb |
> + * +------+------+-----+-----+-------+-------+-----+-----+
> + *
> + * The message device replies with depends on the 'op code' field from
> + * the request. In case it was set to SCD30_SERDEV_WRITE sensor should
> + * reply with unchanged request. Otherwise 'op code' was set to
> + * SCD30_SERDEV_READ and response looks like the one below. As with
> + * request, each field takes one byte.
> + *
> + * +------+------+--------+-------+-----+-------+-----+-----+
> + * | dev | op | num of | byte0 | ... | byteN | crc | crc |
> + * | addr | code | bytes | | | | lsb | msb |
> + * +------+------+--------+-------+-----+-------+-----+-----+
> + */
> + char txbuf[SCD30_SERDEV_MAX_BUF_SIZE] = { SCD30_SERDEV_ADDR },
> + rxbuf[SCD30_SERDEV_MAX_BUF_SIZE], *rsp = response;
> + int ret, rxsize, txsize = 2;
> + u16 crc;
> +
> + put_unaligned_be16(scd30_serdev_cmd_lookup_tbl[cmd], txbuf + txsize);
> + txsize += 2;
> +
> + if (rsp) {
> + txbuf[1] = SCD30_SERDEV_READ;
> + if (cmd == CMD_READ_MEAS)
> + /* number of u16 words to read */
> + put_unaligned_be16(size / 2, txbuf + txsize);
> + else
> + put_unaligned_be16(0x0001, txbuf + txsize);
> + txsize += 2;
> + crc = scd30_serdev_calc_crc(txbuf, txsize);
> + put_unaligned_le16(crc, txbuf + txsize);
> + txsize += 2;
> + rxsize = SCD30_SERDEV_RX_HEADER_SIZE + size +
> + SCD30_SERDEV_CRC_SIZE;
> + } else {
> + if ((cmd == CMD_STOP_MEAS) || (cmd == CMD_RESET))
> + arg = 0x0001;
> +
> + txbuf[1] = SCD30_SERDEV_WRITE;
> + put_unaligned_be16(arg, txbuf + txsize);
> + txsize += 2;
> + crc = scd30_serdev_calc_crc(txbuf, txsize);
> + put_unaligned_le16(crc, txbuf + txsize);
> + txsize += 2;
> + rxsize = txsize;
> + }
> +
> + ret = scd30_serdev_xfer(state, txbuf, txsize, rxbuf, rxsize);
> + if (ret)
> + return ret;
> +
> + switch (txbuf[1]) {
> + case SCD30_SERDEV_WRITE:
> + if (memcmp(txbuf, txbuf, txsize)) {
> + dev_err(state->dev, "wrong message received\n");
> + return -EIO;
> + }
> + break;
> + case SCD30_SERDEV_READ:
> + if (rxbuf[2] != (rxsize -
> + SCD30_SERDEV_RX_HEADER_SIZE -
> + SCD30_SERDEV_CRC_SIZE)) {
> + dev_err(state->dev,
> + "received data size does not match header\n");
> + return -EIO;
> + }
> +
> + rxsize -= SCD30_SERDEV_CRC_SIZE;
> + crc = get_unaligned_le16(rxbuf + rxsize);
> + if (crc != scd30_serdev_calc_crc(rxbuf, rxsize)) {
> + dev_err(state->dev, "data integrity check failed\n");
> + return -EIO;
> + }
> +
> + rxsize -= SCD30_SERDEV_RX_HEADER_SIZE;
> + memcpy(rsp, rxbuf + SCD30_SERDEV_RX_HEADER_SIZE, rxsize);
> + break;
> + default:
> + dev_err(state->dev, "received unknown op code\n");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int scd30_serdev_receive_buf(struct serdev_device *serdev,
> + const unsigned char *buf, size_t size)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev);
> + struct scd30_serdev_priv *priv;
> + struct scd30_state *state;
> + int num;
> +
> + if (!indio_dev)
> + return 0;
> +
> + state = iio_priv(indio_dev);
> + priv = state->priv;
> +
> + /* just in case sensor puts some unexpected bytes on the bus */
> + if (!priv->buf)
> + return 0;
> +
> + if (priv->num + size >= priv->num_expected)
> + num = priv->num_expected - priv->num;
> + else
> + num = size;
> +
> + memcpy(priv->buf + priv->num, buf, num);
> + priv->num += num;
> +
> + if (priv->num == priv->num_expected) {
> + priv->buf = NULL;
> + complete(&priv->meas_ready);
> + }
> +
> + return num;
> +}
> +
> +static const struct serdev_device_ops scd30_serdev_ops = {
> + .receive_buf = scd30_serdev_receive_buf,
> + .write_wakeup = serdev_device_write_wakeup,
> +};
> +
> +static int scd30_serdev_probe(struct serdev_device *serdev)
> +{
> + struct device *dev = &serdev->dev;
> + struct scd30_serdev_priv *priv;
> + int irq, ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + init_completion(&priv->meas_ready);
> + serdev_device_set_client_ops(serdev, &scd30_serdev_ops);
> +
> + ret = devm_serdev_device_open(dev, serdev);
> + if (ret)
> + return ret;
> +
> + serdev_device_set_baudrate(serdev, 19200);
> + serdev_device_set_flow_control(serdev, false);
> +
> + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
> + if (ret)
> + return ret;
> +
> + irq = fwnode_irq_get(dev_fwnode(dev), 0);
> +
> + return scd30_probe(dev, irq, KBUILD_MODNAME, priv,
> + scd30_serdev_command);
> +}
> +
> +static const struct of_device_id scd30_serdev_of_match[] = {
> + { .compatible = "sensirion,scd30" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, scd30_serdev_of_match);
> +
> +static struct serdev_device_driver scd30_serdev_driver = {
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = scd30_serdev_of_match,
> + .pm = &scd30_pm_ops,
> + },
> + .probe = scd30_serdev_probe,
> +};
> +module_serdev_device_driver(scd30_serdev_driver);
> +
> +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
> +MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver");
> +MODULE_LICENSE("GPL v2");
^ permalink raw reply
* Re: [PATCH v2 2/4] iio: chemical: scd30: add I2C interface driver
From: Jonathan Cameron @ 2020-05-31 10:02 UTC (permalink / raw)
To: Tomasz Duszynski
Cc: linux-iio, linux-kernel, devicetree, robh+dt, andy.shevchenko,
pmeerw
In-Reply-To: <20200530213630.87159-3-tomasz.duszynski@octakon.com>
On Sat, 30 May 2020 23:36:28 +0200
Tomasz Duszynski <tomasz.duszynski@octakon.com> wrote:
> Add I2C interface driver for the SCD30 sensor.
>
> Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
Looks good to me.
J
> ---
> MAINTAINERS | 1 +
> drivers/iio/chemical/Kconfig | 11 +++
> drivers/iio/chemical/Makefile | 1 +
> drivers/iio/chemical/scd30_i2c.c | 134 +++++++++++++++++++++++++++++++
> 4 files changed, 147 insertions(+)
> create mode 100644 drivers/iio/chemical/scd30_i2c.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 41a509cca6f1..13aed3473b7e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15142,6 +15142,7 @@ M: Tomasz Duszynski <tomasz.duszynski@octakon.com>
> S: Maintained
> F: drivers/iio/chemical/scd30.h
> F: drivers/iio/chemical/scd30_core.c
> +F: drivers/iio/chemical/scd30_i2c.c
>
> SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
> M: Tomasz Duszynski <tduszyns@gmail.com>
> diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
> index 99e852b67e55..970d34888c2e 100644
> --- a/drivers/iio/chemical/Kconfig
> +++ b/drivers/iio/chemical/Kconfig
> @@ -96,6 +96,17 @@ config SCD30_CORE
> To compile this driver as a module, choose M here: the module will
> be called scd30_core.
>
> +config SCD30_I2C
> + tristate "SCD30 carbon dioxide sensor I2C driver"
> + depends on SCD30_CORE && I2C
> + select CRC8
> + help
> + Say Y here to build support for the Sensirion SCD30 I2C interface
> + driver.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called scd30_i2c.
> +
> config SENSIRION_SGP30
> tristate "Sensirion SGPxx gas sensors"
> depends on I2C
> diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
> index c9804b041ecd..0966ca34e34b 100644
> --- a/drivers/iio/chemical/Makefile
> +++ b/drivers/iio/chemical/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_CCS811) += ccs811.o
> obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
> obj-$(CONFIG_PMS7003) += pms7003.o
> obj-$(CONFIG_SCD30_CORE) += scd30_core.o
> +obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o
> obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
> obj-$(CONFIG_SPS30) += sps30.o
> obj-$(CONFIG_VZ89X) += vz89x.o
> diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c
> new file mode 100644
> index 000000000000..a6b532b83669
> --- /dev/null
> +++ b/drivers/iio/chemical/scd30_i2c.c
> @@ -0,0 +1,134 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sensirion SCD30 carbon dioxide sensor i2c driver
> + *
> + * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
> + *
> + * I2C slave address: 0x61
> + */
> +#include <linux/crc8.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <asm/unaligned.h>
> +
> +#include "scd30.h"
> +
> +#define SCD30_I2C_MAX_BUF_SIZE 18
> +#define SCD30_I2C_CRC8_POLYNOMIAL 0x31
> +
> +static u16 scd30_i2c_cmd_lookup_tbl[] = {
> + [CMD_START_MEAS] = 0x0010,
> + [CMD_STOP_MEAS] = 0x0104,
> + [CMD_MEAS_INTERVAL] = 0x4600,
> + [CMD_MEAS_READY] = 0x0202,
> + [CMD_READ_MEAS] = 0x0300,
> + [CMD_ASC] = 0x5306,
> + [CMD_FRC] = 0x5204,
> + [CMD_TEMP_OFFSET] = 0x5403,
> + [CMD_FW_VERSION] = 0xd100,
> + [CMD_RESET] = 0xd304,
> +};
> +
> +DECLARE_CRC8_TABLE(scd30_i2c_crc8_tbl);
> +
> +static int scd30_i2c_xfer(struct scd30_state *state, char *txbuf, int txsize,
> + char *rxbuf, int rxsize)
> +{
> + struct i2c_client *client = to_i2c_client(state->dev);
> + int ret;
> +
> + /*
> + * repeated start is not supported hence instead of sending two i2c
> + * messages in a row we send one by one
> + */
> + ret = i2c_master_send(client, txbuf, txsize);
> + if (ret != txsize)
> + return ret < 0 ? ret : -EIO;
> +
> + if (!rxbuf)
> + return 0;
> +
> + ret = i2c_master_recv(client, rxbuf, rxsize);
> + if (ret != rxsize)
> + return ret < 0 ? ret : -EIO;
> +
> + return 0;
> +}
> +
> +static int scd30_i2c_command(struct scd30_state *state, enum scd30_cmd cmd,
> + u16 arg, void *response, int size)
> +{
> + char crc, buf[SCD30_I2C_MAX_BUF_SIZE], *rsp = response;
> + int i, ret;
> +
> + put_unaligned_be16(scd30_i2c_cmd_lookup_tbl[cmd], buf);
> + i = 2;
> +
> + if (rsp) {
> + /* each two bytes are followed by a crc8 */
> + size += size / 2;
> + } else {
> + put_unaligned_be16(arg, buf + i);
> + crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE);
> + i += 2;
> + buf[i] = crc;
> + i += 1;
> +
> + /* commands below don't take an argument */
> + if ((cmd == CMD_STOP_MEAS) || (cmd == CMD_RESET))
> + i -= 3;
> + }
> +
> + ret = scd30_i2c_xfer(state, buf, i, buf, size);
> + if (ret)
> + return ret;
> +
> + /* validate received data and strip off crc bytes */
> + for (i = 0; i < size; i += 3) {
> + crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE);
> + if (crc != buf[i + 2]) {
> + dev_err(state->dev, "data integrity check failed\n");
> + return -EIO;
> + }
> +
> + *rsp++ = buf[i];
> + *rsp++ = buf[i + 1];
> + }
> +
> + return 0;
> +}
> +
> +static int scd30_i2c_probe(struct i2c_client *client)
> +{
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> + return -EOPNOTSUPP;
> +
> + crc8_populate_msb(scd30_i2c_crc8_tbl, SCD30_I2C_CRC8_POLYNOMIAL);
> +
> + return scd30_probe(&client->dev, client->irq, client->name, NULL,
> + scd30_i2c_command);
> +}
> +
> +static const struct of_device_id scd30_i2c_of_match[] = {
> + { .compatible = "sensirion,scd30" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, scd30_i2c_of_match);
> +
> +static struct i2c_driver scd30_i2c_driver = {
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = scd30_i2c_of_match,
> + .pm = &scd30_pm_ops,
> + },
> + .probe_new = scd30_i2c_probe,
> +};
> +module_i2c_driver(scd30_i2c_driver);
> +
> +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
> +MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver");
> +MODULE_LICENSE("GPL v2");
^ permalink raw reply
* Re: [PATCH v2 1/4] iio: chemical: scd30: add core driver
From: Jonathan Cameron @ 2020-05-31 9:58 UTC (permalink / raw)
To: Tomasz Duszynski
Cc: linux-iio, linux-kernel, devicetree, robh+dt, andy.shevchenko,
pmeerw
In-Reply-To: <20200530213630.87159-2-tomasz.duszynski@octakon.com>
On Sat, 30 May 2020 23:36:27 +0200
Tomasz Duszynski <tomasz.duszynski@octakon.com> wrote:
> Add Sensirion SCD30 carbon dioxide core driver.
>
> Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
Hi Tomasz
A few things inline. Includes the alignment issue on
x86_32 that I fell into whilst trying to fix timestamp
alignment issues. Suggested resolution inline for that.
Thanks,
Jonathan
> ---
> Documentation/ABI/testing/sysfs-bus-iio-scd30 | 20 +
> MAINTAINERS | 6 +
> drivers/iio/chemical/Kconfig | 11 +
> drivers/iio/chemical/Makefile | 1 +
> drivers/iio/chemical/scd30.h | 75 ++
> drivers/iio/chemical/scd30_core.c | 764 ++++++++++++++++++
> 6 files changed, 877 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-scd30
> create mode 100644 drivers/iio/chemical/scd30.h
> create mode 100644 drivers/iio/chemical/scd30_core.c
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-scd30 b/Documentation/ABI/testing/sysfs-bus-iio-scd30
> new file mode 100644
> index 000000000000..a05b1d28e94a
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-scd30
> @@ -0,0 +1,20 @@
> +What: /sys/bus/iio/devices/iio:deviceX/calibration
> +Date: June 2020
> +KernelVersion: 5.8
> +Contact: linux-iio@vger.kernel.org
> +Description:
> + Contaminants build-up in the measurement chamber or optical
> + elements deterioration leads to sensor drift.
> +
> + One can compensate for sensor drift by using either automatic
> + self calibration (asc) or forced recalibration (frc). If used
> + at once one will overwrite the other.
> +
> + Writing 1 or 0 to this attribute will respectively activate or
> + deactivate asc.
> +
> + Picking value from the range [400 1 2000] and writing it to the
> + sensor will set frc.
Seems to me like this would be more intuitive as two separate parameters
perhaps:
calibration_auto_enable
calibration_forced_value
?
> +
> + Upon reading current asc status and frc value are returned
> + respectively.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 60ed2963efaa..41a509cca6f1 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15137,6 +15137,12 @@ S: Maintained
> F: drivers/misc/phantom.c
> F: include/uapi/linux/phantom.h
>
> +SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
> +M: Tomasz Duszynski <tomasz.duszynski@octakon.com>
> +S: Maintained
> +F: drivers/iio/chemical/scd30.h
> +F: drivers/iio/chemical/scd30_core.c
> +
> SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
> M: Tomasz Duszynski <tduszyns@gmail.com>
> S: Maintained
> diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
> index 7f21afd73b1c..99e852b67e55 100644
> --- a/drivers/iio/chemical/Kconfig
> +++ b/drivers/iio/chemical/Kconfig
> @@ -85,6 +85,17 @@ config PMS7003
> To compile this driver as a module, choose M here: the module will
> be called pms7003.
>
> +config SCD30_CORE
> + tristate "SCD30 carbon dioxide sensor driver"
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Say Y here to build support for the Sensirion SCD30 sensor with carbon
> + dioxide, relative humidity and temperature sensing capabilities.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called scd30_core.
> +
> config SENSIRION_SGP30
> tristate "Sensirion SGPxx gas sensors"
> depends on I2C
> diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
> index aba4167db745..c9804b041ecd 100644
> --- a/drivers/iio/chemical/Makefile
> +++ b/drivers/iio/chemical/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_BME680_SPI) += bme680_spi.o
> obj-$(CONFIG_CCS811) += ccs811.o
> obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
> obj-$(CONFIG_PMS7003) += pms7003.o
> +obj-$(CONFIG_SCD30_CORE) += scd30_core.o
> obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
> obj-$(CONFIG_SPS30) += sps30.o
> obj-$(CONFIG_VZ89X) += vz89x.o
> diff --git a/drivers/iio/chemical/scd30.h b/drivers/iio/chemical/scd30.h
> new file mode 100644
> index 000000000000..9b25f7423142
> --- /dev/null
> +++ b/drivers/iio/chemical/scd30.h
> @@ -0,0 +1,75 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _SCD30_H
> +#define _SCD30_H
> +
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include <linux/pm.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/types.h>
> +
> +struct scd30_state;
> +
> +enum scd30_cmd {
> + /* start continuous measurement with pressure compensation */
> + CMD_START_MEAS,
> + /* stop continuous measurement */
> + CMD_STOP_MEAS,
> + /* set/get measurement interval */
> + CMD_MEAS_INTERVAL,
> + /* check whether new measurement is ready */
> + CMD_MEAS_READY,
> + /* get measurement */
> + CMD_READ_MEAS,
> + /* turn on/off automatic self calibration */
> + CMD_ASC,
> + /* set/get forced recalibration value */
> + CMD_FRC,
> + /* set/get temperature offset */
> + CMD_TEMP_OFFSET,
> + /* get firmware version */
> + CMD_FW_VERSION,
> + /* reset sensor */
> + CMD_RESET,
> + /*
> + * Command for altitude compensation was omitted intentionally because
> + * the same can be achieved by means of CMD_START_MEAS which takes
> + * pressure above the sea level as an argument.
> + */
> +};
> +
> +#define SCD30_MEAS_COUNT 3
> +
> +typedef int (*scd30_command_t)(struct scd30_state *state, enum scd30_cmd cmd,
> + u16 arg, void *response, int size);
> +
> +struct scd30_state {
> + /* serialize access to the device */
> + struct mutex lock;
> + struct device *dev;
> + struct regulator *vdd;
> + struct completion meas_ready;
> + void *priv;
> + int irq;
> + /*
> + * no way to retrieve current ambient pressure compensation value from
> + * the sensor so keep one around
> + */
> + u16 pressure_comp;
> + u16 meas_interval;
> + int meas[SCD30_MEAS_COUNT];
> +
> + scd30_command_t command;
> +};
> +
> +int scd30_suspend(struct device *dev);
> +int scd30_resume(struct device *dev);
> +
> +static __maybe_unused SIMPLE_DEV_PM_OPS(scd30_pm_ops, scd30_suspend,
> + scd30_resume);
> +
> +int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
> + scd30_command_t command);
> +
> +#endif
> diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c
> new file mode 100644
> index 000000000000..3b7d0a7ea7ae
> --- /dev/null
> +++ b/drivers/iio/chemical/scd30_core.c
> @@ -0,0 +1,764 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sensirion SCD30 carbon dioxide sensor core driver
> + *
> + * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
> + */
> +#include <linux/bits.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/export.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/types.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqreturn.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/string.h>
> +#include <linux/sysfs.h>
> +#include <linux/types.h>
> +#include <asm/byteorder.h>
> +
> +#include "scd30.h"
> +
> +#define SCD30_PRESSURE_COMP_MIN_MBAR 700
> +#define SCD30_PRESSURE_COMP_MAX_MBAR 1400
> +#define SCD30_PRESSURE_COMP_DEFAULT 1013
> +#define SCD30_MEAS_INTERVAL_MIN_S 2
> +#define SCD30_MEAS_INTERVAL_MAX_S 1800
> +#define SCD30_MEAS_INTERVAL_DEFAULT SCD30_MEAS_INTERVAL_MIN_S
> +#define SCD30_FRC_MIN_PPM 400
> +#define SCD30_FRC_MAX_PPM 2000
> +#define SCD30_TEMP_OFFSET_MAX 655360
> +#define SCD30_EXTRA_TIMEOUT_PER_S 250
> +
> +enum {
> + SCD30_CONC,
> + SCD30_TEMP,
> + SCD30_HR,
> +};
> +
> +static int scd30_command_write(struct scd30_state *state, enum scd30_cmd cmd,
> + u16 arg)
> +{
> + return state->command(state, cmd, arg, NULL, 0);
> +}
> +
> +static int scd30_command_read(struct scd30_state *state, enum scd30_cmd cmd,
> + u16 *val)
> +{
> + int ret;
> +
> + ret = state->command(state, cmd, 0, val, sizeof(*val));
> + *val = be16_to_cpup((__be16 *)val);
Please use a local variable for the __be16 as it makes thing more readable
and easier to check for endian problems.
> +
> + return ret;
> +}
> +
> +static int scd30_reset(struct scd30_state *state)
> +{
> + int ret;
> + u16 val;
> +
> + ret = scd30_command_write(state, CMD_RESET, 0);
> + if (ret)
> + return ret;
> +
> + /* sensor boots up within 2 secs */
> + msleep(2000);
> + /*
> + * Power-on-reset causes sensor to produce some glitch on i2c bus and
> + * some controllers end up in error state. Try to recover by placing
> + * any data on the bus.
> + */
> + scd30_command_read(state, CMD_MEAS_READY, &val);
> +
> + return 0;
> +}
> +
> +/* simplified float to fixed point conversion with a scaling factor of 0.01 */
> +static int scd30_float_to_fp(int float32)
> +{
> + int fraction, shift,
> + mantissa = float32 & GENMASK(22, 0),
> + sign = float32 & BIT(31) ? -1 : 1,
> + exp = (float32 & ~BIT(31)) >> 23;
> +
> + /* special case 0 */
> + if (!exp && !mantissa)
> + return 0;
> +
> + exp -= 127;
> + if (exp < 0) {
> + exp = -exp;
> + /* return values ranging from 1 to 99 */
> + return sign * ((((BIT(23) + mantissa) * 100) >> 23) >> exp);
> + }
> +
> + /* return values starting at 100 */
> + shift = 23 - exp;
> + float32 = BIT(exp) + (mantissa >> shift);
> + fraction = mantissa & GENMASK(shift - 1, 0);
> +
> + return sign * (float32 * 100 + ((fraction * 100) >> shift));
> +}
> +
> +static int scd30_read_meas(struct scd30_state *state)
> +{
> + int i, ret;
> +
> + ret = state->command(state, CMD_READ_MEAS, 0, state->meas,
> + sizeof(state->meas));
> + if (ret)
> + return ret;
> +
> + be32_to_cpu_array(state->meas, state->meas, ARRAY_SIZE(state->meas));
> +
> + for (i = 0; i < ARRAY_SIZE(state->meas); i++)
> + state->meas[i] = scd30_float_to_fp(state->meas[i]);
> +
> + return 0;
> +}
> +
> +static int scd30_wait_meas_irq(struct scd30_state *state)
> +{
> + int ret, timeout;
> +
> + timeout = state->meas_interval * (1000 + SCD30_EXTRA_TIMEOUT_PER_S);
> + timeout = msecs_to_jiffies(timeout);
> + reinit_completion(&state->meas_ready);
> + enable_irq(state->irq);
> + ret = wait_for_completion_interruptible_timeout(&state->meas_ready,
> + timeout);
> + if (ret > 0)
> + ret = 0;
> + else if (!ret)
> + ret = -ETIMEDOUT;
> +
> + disable_irq(state->irq);
> +
> + return ret;
> +}
> +
> +static int scd30_wait_meas_poll(struct scd30_state *state)
> +{
> + int timeout = state->meas_interval * SCD30_EXTRA_TIMEOUT_PER_S;
> + int tries = 5;
> +
> + do {
> + int ret;
> + u16 val;
> +
> + ret = scd30_command_read(state, CMD_MEAS_READY, &val);
> + if (ret)
> + return -EIO;
> +
> + /* new measurement available */
> + if (val)
> + break;
> +
> + msleep_interruptible(timeout);
> + } while (--tries);
> +
> + return tries ? 0 : -ETIMEDOUT;
> +}
> +
> +static int scd30_read_poll(struct scd30_state *state)
> +{
> + int ret;
> +
> + ret = scd30_wait_meas_poll(state);
> + if (ret)
> + return ret;
> +
> + return scd30_read_meas(state);
> +}
> +
> +static int scd30_read(struct scd30_state *state)
> +{
> + if (state->irq > 0)
> + return scd30_wait_meas_irq(state);
> +
> + return scd30_read_poll(state);
> +}
> +
> +static int scd30_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct scd30_state *state = iio_priv(indio_dev);
> + int ret, meas[SCD30_MEAS_COUNT];
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + case IIO_CHAN_INFO_PROCESSED:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&state->lock);
> + ret = scd30_read(state);
> + memcpy(meas, state->meas, SCD30_MEAS_COUNT * sizeof(*meas));
The local copy seems a bit excessive. This isn't likely to be a particularly
fast path so perhaps skip the copy but hold the locks until we are
done with the buffer?
> + mutex_unlock(&state->lock);
> + iio_device_release_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> + switch (chan->type) {
> + case IIO_CONCENTRATION:
> + *val = meas[chan->address] / 1000000;
> + *val2 = meas[chan->address] % 1000000;
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_TEMP:
> + case IIO_HUMIDITYRELATIVE:
> + *val = meas[chan->address] * 10;
> +
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> + case IIO_CHAN_INFO_SCALE:
> + switch (chan->type) {
> + case IIO_CONCENTRATION:
> + *val = 0;
> + *val2 = 1;
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_TEMP:
> + case IIO_HUMIDITYRELATIVE:
> + *val = 10;
> +
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *val = 0;
> + *val2 = 0;
> +
> + mutex_lock(&state->lock);
> + ret = scd30_command_read(state, CMD_MEAS_INTERVAL, (u16 *)val2);
See below. I'll assume you'll fix all of these.
> + mutex_unlock(&state->lock);
> + if (ret)
> + return ret;
> +
> + *val2 = 1000000000 / *val2;
> +
> + return IIO_VAL_INT_PLUS_NANO;
> + case IIO_CHAN_INFO_CALIBSCALE:
> + mutex_lock(&state->lock);
> + *val = state->pressure_comp / 10;
> + *val2 = (state->pressure_comp % 10) * 100000;
> + mutex_unlock(&state->lock);
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_CALIBBIAS:
> + *val = 0;
> + mutex_lock(&state->lock);
> + ret = scd30_command_read(state, CMD_TEMP_OFFSET, (u16 *)val);
Reading a u16 directly into a int is not a good idea. What you get will
depend on the endianness of the machine.
Use an intermediate variable of the right size.
> + mutex_unlock(&state->lock);
> +
> + return IIO_VAL_INT;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int scd30_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct scd30_state *state = iio_priv(indio_dev);
> + int ret = -EINVAL;
> +
> + mutex_lock(&state->lock);
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + if (val)
> + break;
> +
> + val = 1000000000 / val2;
> + if (val < SCD30_MEAS_INTERVAL_MIN_S ||
> + val > SCD30_MEAS_INTERVAL_MAX_S)
> + break;
> +
> + ret = scd30_command_write(state, CMD_MEAS_INTERVAL, val);
> + if (ret)
> + break;
> +
> + state->meas_interval = val;
> + break;
> + case IIO_CHAN_INFO_CALIBSCALE:
> + val = (val * 1000000 + val2) / 100000;
> + if (val < SCD30_PRESSURE_COMP_MIN_MBAR ||
> + val > SCD30_PRESSURE_COMP_MAX_MBAR)
> + break;
> +
> + ret = scd30_command_write(state, CMD_START_MEAS, val);
> + if (ret)
> + break;
> +
> + state->pressure_comp = val;
> + break;
> + case IIO_CHAN_INFO_CALIBBIAS:
> + if (val < 0 || val > SCD30_TEMP_OFFSET_MAX)
> + break;
> + /*
> + * Manufacturer does not explicitly specify min/max sensible
> + * values hence check is omitted for simplicity.
> + */
> + ret = scd30_command_write(state, CMD_TEMP_OFFSET / 10, val);
> + }
> + mutex_unlock(&state->lock);
> +
> + return ret;
> +}
> +
> +static int scd30_write_raw_get_fmt(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + case IIO_CHAN_INFO_CALIBBIAS:
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_CALIBSCALE:
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return IIO_VAL_INT_PLUS_NANO;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const int scd30_pressure_calibscale_available[] = {
> + SCD30_PRESSURE_COMP_MIN_MBAR / 10, 0,
> + 0, 100000,
> + SCD30_PRESSURE_COMP_MAX_MBAR / 10, 0,
> +};
> +
> +static const int scd30_temp_calibbias_available[] = {
> + 0, 10, SCD30_TEMP_OFFSET_MAX,
> +};
> +
> +static int scd30_read_avail(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, const int **vals,
> + int *type, int *length, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_CALIBSCALE:
> + *vals = scd30_pressure_calibscale_available;
> + *type = IIO_VAL_INT_PLUS_MICRO;
> +
> + return IIO_AVAIL_RANGE;
> + case IIO_CHAN_INFO_CALIBBIAS:
> + *vals = scd30_temp_calibbias_available;
> + *type = IIO_VAL_INT;
> +
> + return IIO_AVAIL_RANGE;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static ssize_t sampling_frequency_available_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + int i = SCD30_MEAS_INTERVAL_MIN_S;
> + ssize_t len = 0;
> +
> + do {
> + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09u ",
> + 1000000000 / i);
> + /*
> + * Not all values fit PAGE_SIZE buffer hence print every 6th
> + * (each frequency differs by 6s in time domain from the
> + * adjecent). Unlisted but valid ones are still accepted.
adjacent
Hmm. Maybe we need to think about some description for inverse of linear
cases as they are likely to be fairly common.
This will work in meantime.
> + */
> + i += 6;
> + } while (i <= SCD30_MEAS_INTERVAL_MAX_S);
> +
> + buf[len - 1] = '\n';
> +
> + return len;
> +}
> +
> +static ssize_t calibration_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct scd30_state *state = iio_priv(indio_dev);
> + u16 asc, frc;
> + int ret;
> +
> + mutex_lock(&state->lock);
> + ret = scd30_command_read(state, CMD_ASC, &asc);
> + if (ret)
> + goto out;
> +
> + ret = scd30_command_read(state, CMD_FRC, &frc);
> +out:
> + mutex_unlock(&state->lock);
> +
> + return ret ?: sprintf(buf, "%d %d\n", asc, frc);
> +}
> +
> +static ssize_t calibration_store(struct device *dev,
> + struct device_attribute *attr, const char *buf,
> + size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct scd30_state *state = iio_priv(indio_dev);
> + int ret;
> + u16 val;
As commented above, this interface doesn't win on the
obvious front so needs a rethink!
> +
> + ret = kstrtou16(buf, 0, &val);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&state->lock);
> + if (val == 0 || val == 1)
> + ret = scd30_command_write(state, CMD_ASC, val);
> + else if (val >= SCD30_FRC_MIN_PPM && val <= SCD30_FRC_MAX_PPM)
> + ret = scd30_command_write(state, CMD_FRC, val);
> + else
> + ret = -EINVAL;
> + mutex_unlock(&state->lock);
> +
> + return ret ?: len;
> +}
> +
> +static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
> +static IIO_DEVICE_ATTR_RW(calibration, 0);
> +
> +static struct attribute *scd30_attrs[] = {
> + &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> + &iio_dev_attr_calibration.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group scd30_attr_group = {
> + .attrs = scd30_attrs,
> +};
> +
> +static const struct iio_info scd30_info = {
> + .attrs = &scd30_attr_group,
> + .read_raw = scd30_read_raw,
> + .write_raw = scd30_write_raw,
> + .write_raw_get_fmt = scd30_write_raw_get_fmt,
> + .read_avail = scd30_read_avail,
> +};
> +
> +#define SCD30_CHAN_SCAN_TYPE(_sign, _realbits) .scan_type = { \
> + .sign = _sign, \
> + .realbits = _realbits, \
> + .storagebits = 32, \
> + .endianness = IIO_CPU, \
> +}
> +
> +static const struct iio_chan_spec scd30_channels[] = {
> + {
> + .type = IIO_PRESSURE,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE),
> + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE),
> + .scan_index = -1,
> + },
> + {
> + .type = IIO_CONCENTRATION,
> + .channel2 = IIO_MOD_CO2,
> + .address = SCD30_CONC,
> + .scan_index = SCD30_CONC,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> + .modified = 1,
> +
> + SCD30_CHAN_SCAN_TYPE('u', 16),
> + },
> + {
> + .type = IIO_TEMP,
> + .address = SCD30_TEMP,
> + .scan_index = SCD30_TEMP,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> + BIT(IIO_CHAN_INFO_CALIBBIAS) |
> + BIT(IIO_CHAN_INFO_SCALE),
Combination of processed and scale is unusual. Normally scale provides
a conversion factor or a _RAW reading.
I 'think' these units are otherwise fine (milli degrees centigrade)
> + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +
> + SCD30_CHAN_SCAN_TYPE('s', 14),
> + },
> + {
> + .type = IIO_HUMIDITYRELATIVE,
> + .address = SCD30_HR,
> + .scan_index = SCD30_HR,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> + BIT(IIO_CHAN_INFO_SCALE),
As above. Not normal to see scale and processed.
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +
> + SCD30_CHAN_SCAN_TYPE('u', 14),
> + },
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +int __maybe_unused scd30_suspend(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct scd30_state *state = iio_priv(indio_dev);
> + int ret;
> +
> + ret = scd30_command_write(state, CMD_STOP_MEAS, 0);
> + if (ret)
> + return ret;
> +
> + return regulator_disable(state->vdd);
> +}
> +EXPORT_SYMBOL(scd30_suspend);
> +
> +int __maybe_unused scd30_resume(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct scd30_state *state = iio_priv(indio_dev);
> + int ret;
> +
> + ret = regulator_enable(state->vdd);
> + if (ret)
> + return ret;
> +
> + return scd30_command_write(state, CMD_START_MEAS, state->pressure_comp);
> +}
> +EXPORT_SYMBOL(scd30_resume);
> +
> +static void scd30_stop_meas(void *data)
> +{
> + struct scd30_state *state = data;
> +
> + scd30_command_write(state, CMD_STOP_MEAS, 0);
> +}
> +
> +static void scd30_disable_regulator(void *data)
> +{
> + struct scd30_state *state = data;
> +
> + regulator_disable(state->vdd);
> +}
> +
> +static irqreturn_t scd30_irq_handler(int irq, void *priv)
> +{
> + struct iio_dev *indio_dev = priv;
> +
> + if (iio_buffer_enabled(indio_dev)) {
There is a potential quirk here. It's possible that
this device is using a different trigger, but another device
is registered to use this one. If that happens this check
will be a bit counter intuitive.
As such you might want to provide the validate callback so
that this device is the only device allowed to use it's
own trigger.
> + iio_trigger_poll(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> + }
> +
> + return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t scd30_irq_thread_handler(int irq, void *priv)
> +{
> + struct iio_dev *indio_dev = priv;
> + struct scd30_state *state = iio_priv(indio_dev);
> + int ret;
> +
> + ret = scd30_read_meas(state);
> + if (ret)
> + goto out;
> +
> + complete_all(&state->meas_ready);
> +out:
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t scd30_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct scd30_state *state = iio_priv(indio_dev);
> + struct {
> + int data[SCD30_MEAS_COUNT];
> + u64 ts;
Turns out I was wrong when suggesting this approach for drivers. On x86_32
this will result in there not being any padding between the
data and the timestamp (and in IIO rule of naturally aligned there
needs to be 4 bytes there). Result is that this structure is
too short. (thanks btw to Andy who pointed out this issue!)
So, to force that my current preference is.
struct {
int data[SCD30_MEAS_COUNT];
s64 ts __aligned(8);
} scan;
However, given we do have a hole in the structure there is
a kernel data leak. So either you need to zero it here,
or move it into the iio_priv() structure. Doing that
will ensure it is zeroed at allocation.
> + } scan;
> + int ret;
> +
> + mutex_lock(&state->lock);
> + if (!iio_trigger_using_own(indio_dev))
> + ret = scd30_read_poll(state);
> + else
> + ret = scd30_read_meas(state);
> + memcpy(scan.data, state->meas, sizeof(state->meas));
> + mutex_unlock(&state->lock);
> + if (ret)
> + goto out;
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, &scan,
> + iio_get_time_ns(indio_dev));
> +out:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
> +
> +static int scd30_set_trigger_state(struct iio_trigger *trig, bool state)
> +{
> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> + struct scd30_state *st = iio_priv(indio_dev);
> +
> + if (state)
> + enable_irq(st->irq);
> + else
> + disable_irq(st->irq);
> +
> + return 0;
> +}
> +
> +static const struct iio_trigger_ops scd30_trigger_ops = {
> + .set_trigger_state = scd30_set_trigger_state,
> +};
> +
> +static int scd30_setup_trigger(struct iio_dev *indio_dev)
> +{
> + struct scd30_state *state = iio_priv(indio_dev);
> + struct device *dev = indio_dev->dev.parent;
> + struct iio_trigger *trig;
> + int ret;
> +
> + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
> + indio_dev->id);
> + if (!trig) {
> + dev_err(dev, "failed to allocate trigger\n");
> + return -ENOMEM;
> + }
> +
> + trig->dev.parent = dev;
> + trig->ops = &scd30_trigger_ops;
> + iio_trigger_set_drvdata(trig, indio_dev);
> +
> + ret = devm_iio_trigger_register(dev, trig);
> + if (ret)
> + return ret;
> +
> + indio_dev->trig = iio_trigger_get(trig);
> +
> + ret = devm_request_threaded_irq(dev, state->irq, scd30_irq_handler,
> + scd30_irq_thread_handler,
> + IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
> + indio_dev->name, indio_dev);
> + if (ret)
> + dev_err(dev, "failed to request irq\n");
> +
> + disable_irq(state->irq);
Given there is a gap between the request above and this disable, this
disable needs a comment explaining why it is here.
I'm assuming it's an optimization?
> +
> + return ret;
> +}
> +
> +int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
> + scd30_command_t command)
> +{
> + static const unsigned long scd30_scan_masks[] = { 0x07, 0x00 };
> + struct scd30_state *state;
> + struct iio_dev *indio_dev;
> + int ret;
> + u16 val;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + state = iio_priv(indio_dev);
> + state->dev = dev;
Doesn't seem to be used.
> + state->priv = priv;
What's this for? At least at first glance I can't find it being used
anywhere.
> + state->irq = irq;
> + state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT;
> + state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT;
> + state->command = command;
> + mutex_init(&state->lock);
> + init_completion(&state->meas_ready);
> +
> + dev_set_drvdata(dev, indio_dev);
> +
> + indio_dev->dev.parent = dev;
Side note that there is a series moving this into the core under revision at
the moment. Hopefully I'll remember to fix this up when applying your patch
if that one has gone in ahead of it.
> + indio_dev->info = &scd30_info;
> + indio_dev->name = name;
> + indio_dev->channels = scd30_channels;
> + indio_dev->num_channels = ARRAY_SIZE(scd30_channels);
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->available_scan_masks = scd30_scan_masks;
> +
> + state->vdd = devm_regulator_get(dev, "vdd");
> + if (IS_ERR(state->vdd)) {
> + if (PTR_ERR(state->vdd) == -EPROBE_DEFER)
> + return -EPROBE_DEFER;
> +
> + dev_err(dev, "failed to get regulator\n");
> + return PTR_ERR(state->vdd);
> + }
> +
> + ret = regulator_enable(state->vdd);
> + if (ret)
> + return ret;
> +
> + ret = devm_add_action_or_reset(dev, scd30_disable_regulator, state);
> + if (ret)
> + return ret;
> +
A comment here on why it makes sense to register this here. What
started mesurement? It seems that happens well below here so
we should really call this after that start all.
> + ret = devm_add_action_or_reset(dev, scd30_stop_meas, state);
> + if (ret)
> + return ret;
> +
> + ret = scd30_reset(state);
> + if (ret) {
> + dev_err(dev, "failed to reset device: %d\n", ret);
> + return ret;
> + }
> +
> + if (state->irq > 0) {
> + ret = scd30_setup_trigger(indio_dev);
> + if (ret) {
> + dev_err(dev, "failed to setup trigger: %d\n", ret);
> + return ret;
> + }
> + }
> +
> + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> + scd30_trigger_handler, NULL);
> + if (ret)
> + return ret;
> +
> + ret = scd30_command_read(state, CMD_FW_VERSION, &val);
> + if (ret) {
> + dev_err(dev, "failed to read firmware version: %d\n", ret);
> + return ret;
> + }
> + dev_info(dev, "firmware version: %d.%d\n", val >> 8, (char)val);
> +
> + ret = scd30_command_write(state, CMD_MEAS_INTERVAL,
> + state->meas_interval);
> + if (ret) {
> + dev_err(dev, "failed to set measurement interval: %d\n", ret);
> + return ret;
> + }
> +
> + ret = scd30_command_write(state, CMD_START_MEAS, state->pressure_comp);
> + if (ret) {
> + dev_err(dev, "failed to start measurement: %d\n", ret);
> + return ret;
> + }
> +
> + return devm_iio_device_register(dev, indio_dev);
> +}
> +EXPORT_SYMBOL(scd30_probe);
> +
> +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
> +MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.26.2
>
^ permalink raw reply
* Re: [PATCH v2 2/3] media: rockchip: Introduce driver for Rockhip's camera interface
From: kbuild test robot @ 2020-05-31 4:39 UTC (permalink / raw)
To: Maxime Chevallier, Mauro Carvalho Chehab, Robin Murphy,
Rob Herring, Mark Rutland, Heiko Stuebner, Hans Verkuil
Cc: kbuild-all, linux-media, Maxime Chevallier, devicetree,
linux-arm-kernel
In-Reply-To: <20200529130405.929429-3-maxime.chevallier@bootlin.com>
[-- Attachment #1: Type: text/plain, Size: 3528 bytes --]
Hi Maxime,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linuxtv-media/master]
[also build test WARNING on robh/for-next v5.7-rc7 next-20200529]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Maxime-Chevallier/media-rockchip-Introduce-driver-for-the-camera-interface-on-PX30/20200531-081952
base: git://linuxtv.org/media_tree.git master
config: i386-allyesconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-13) 9.3.0
reproduce (this is a W=1 build):
# save the attached .config to linux build tree
make W=1 ARCH=i386
If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <lkp@intel.com>
All warnings (new ones prefixed by >>, old ones prefixed by <<):
In file included from drivers/media/platform/rockchip/cif/dev.c:22:
>> drivers/media/platform/rockchip/cif/regs.h:113: warning: "FRAME_END" redefined
113 | #define FRAME_END (0x01 << 0)
|
In file included from arch/x86/include/asm/paravirt.h:18,
from arch/x86/include/asm/spinlock.h:10,
from include/linux/spinlock.h:89,
from include/linux/rwsem.h:16,
from include/linux/notifier.h:15,
from include/linux/clk.h:14,
from drivers/media/platform/rockchip/cif/dev.c:8:
arch/x86/include/asm/frame.h:60: note: this is the location of the previous definition
60 | #define FRAME_END "pop %" _ASM_BP "n"
|
In file included from include/media/v4l2-subdev.h:15,
from include/media/v4l2-device.h:13,
from drivers/media/platform/rockchip/cif/dev.h:15,
from drivers/media/platform/rockchip/cif/dev.c:21:
drivers/media/platform/rockchip/cif/dev.c: In function 'rkcif_plat_probe':
include/media/v4l2-common.h:58:32: warning: 'v4l2_dev' may be used uninitialized in this function [-Wmaybe-uninitialized]
58 | printk(level "%s: " fmt, (dev)->name , ## arg)
| ^~
drivers/media/platform/rockchip/cif/dev.c:216:22: note: 'v4l2_dev' was declared here
216 | struct v4l2_device *v4l2_dev;
| ^~~~~~~~
--
In file included from drivers/media/platform/rockchip/cif/capture.c:22:
>> drivers/media/platform/rockchip/cif/regs.h:113: warning: "FRAME_END" redefined
113 | #define FRAME_END (0x01 << 0)
|
In file included from arch/x86/include/asm/paravirt.h:18,
from arch/x86/include/asm/spinlock.h:10,
from include/linux/spinlock.h:89,
from include/linux/wait.h:9,
from include/linux/pid.h:6,
from include/linux/sched.h:14,
from include/linux/ratelimit.h:6,
from include/linux/dev_printk.h:16,
from include/linux/device.h:15,
from include/linux/pm_runtime.h:11,
from drivers/media/platform/rockchip/cif/capture.c:10:
arch/x86/include/asm/frame.h:60: note: this is the location of the previous definition
60 | #define FRAME_END "pop %" _ASM_BP "n"
|
vim +/FRAME_END +113 drivers/media/platform/rockchip/cif/regs.h
110
111 /* CIF INTSTAT */
112 #define INTSTAT_CLS (0x3FF)
> 113 #define FRAME_END (0x01 << 0)
114 #define LINE_ERR (0x01 << 2)
115 #define PST_INF_FRAME_END (0x01 << 9)
116 #define FRAME_END_CLR (0x01 << 0)
117 #define LINE_ERR_CLR (0x01 << 2)
118 #define PST_INF_FRAME_END_CLR (0x01 << 9)
119 #define INTSTAT_ERR (0xFC)
120
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 72524 bytes --]
^ permalink raw reply
* [PATCH v3 4/4] pinctrl: bcm2835: Add support for wake-up interrupts
From: Florian Fainelli @ 2020-05-31 0:11 UTC (permalink / raw)
To: linux-kernel
Cc: Florian Fainelli, Linus Walleij, Rob Herring, Ray Jui,
Scott Branden,
maintainer:BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITE...,
Nicolas Saenz Julienne, Stefan Wahren, Geert Uytterhoeven,
Matti Vaittinen, open list:PIN CONTROL SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
In-Reply-To: <20200531001101.24945-1-f.fainelli@gmail.com>
Leverage the IRQCHIP_MASK_ON_SUSPEND flag in order to avoid having to
specifically treat the GPIO interrupts during suspend and resume, and
simply implement an irq_set_wake() callback that is responsible for
enabling the parent wake-up interrupt as a wake-up interrupt.
To avoid allocating unnecessary resources for other chips, the wake-up
interrupts are only initialized if we have a brcm,bcm7211-gpio
compatibility string.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/pinctrl/bcm/pinctrl-bcm2835.c | 76 ++++++++++++++++++++++++++-
1 file changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index 1b00d93aa66e..1d21129f7751 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -76,6 +77,7 @@
struct bcm2835_pinctrl {
struct device *dev;
void __iomem *base;
+ int *wake_irq;
/* note: locking assumes each bank will have its own unsigned long */
unsigned long enabled_irq_map[BCM2835_NUM_BANKS];
@@ -435,6 +437,11 @@ static void bcm2835_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(host_chip, desc);
}
+static irqreturn_t bcm2835_gpio_wake_irq_handler(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
unsigned reg, unsigned offset, bool enable)
{
@@ -634,6 +641,34 @@ static void bcm2835_gpio_irq_ack(struct irq_data *data)
bcm2835_gpio_set_bit(pc, GPEDS0, gpio);
}
+static int bcm2835_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+ struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
+ unsigned gpio = irqd_to_hwirq(data);
+ unsigned int irqgroup;
+ int ret = -EINVAL;
+
+ if (!pc->wake_irq)
+ return ret;
+
+ if (gpio <= 27)
+ irqgroup = 0;
+ else if (gpio >= 28 && gpio <= 45)
+ irqgroup = 1;
+ else if (gpio >= 46 && gpio <= 57)
+ irqgroup = 2;
+ else
+ return ret;
+
+ if (on)
+ ret = enable_irq_wake(pc->wake_irq[irqgroup]);
+ else
+ ret = disable_irq_wake(pc->wake_irq[irqgroup]);
+
+ return ret;
+}
+
static struct irq_chip bcm2835_gpio_irq_chip = {
.name = MODULE_NAME,
.irq_enable = bcm2835_gpio_irq_enable,
@@ -642,6 +677,8 @@ static struct irq_chip bcm2835_gpio_irq_chip = {
.irq_ack = bcm2835_gpio_irq_ack,
.irq_mask = bcm2835_gpio_irq_disable,
.irq_unmask = bcm2835_gpio_irq_enable,
+ .irq_set_wake = bcm2835_gpio_irq_set_wake,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
};
static int bcm2835_pctl_get_groups_count(struct pinctrl_dev *pctldev)
@@ -1154,6 +1191,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
struct resource iomem;
int err, i;
const struct of_device_id *match;
+ int is_7211 = 0;
BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_pins) != BCM2711_NUM_GPIOS);
BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_groups) != BCM2711_NUM_GPIOS);
@@ -1180,6 +1218,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
return -EINVAL;
pdata = match->data;
+ is_7211 = of_device_is_compatible(np, "brcm,bcm7211-gpio");
pc->gpio_chip = *pdata->gpio_chip;
pc->gpio_chip.parent = dev;
@@ -1214,6 +1253,15 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
+
+ if (is_7211) {
+ pc->wake_irq = devm_kcalloc(dev, BCM2835_NUM_IRQS,
+ sizeof(*pc->wake_irq),
+ GFP_KERNEL);
+ if (!pc->wake_irq)
+ return -ENOMEM;
+ }
+
/*
* Use the same handler for all groups: this is necessary
* since we use one gpiochip to cover all lines - the
@@ -1221,8 +1269,34 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
* bank that was firing the IRQ and look up the per-group
* and bank data.
*/
- for (i = 0; i < BCM2835_NUM_IRQS; i++)
+ for (i = 0; i < BCM2835_NUM_IRQS; i++) {
+ int len;
+ char *name;
+
girq->parents[i] = irq_of_parse_and_map(np, i);
+ if (!is_7211)
+ continue;
+
+ /* Skip over the all banks interrupts */
+ pc->wake_irq[i] = irq_of_parse_and_map(np, i +
+ BCM2835_NUM_IRQS + 1);
+
+ len = strlen(dev_name(pc->dev)) + 16;
+ name = devm_kzalloc(pc->dev, len, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ snprintf(name, len, "%s:bank%d", dev_name(pc->dev), i);
+
+ /* These are optional interrupts */
+ err = devm_request_irq(dev, pc->wake_irq[i],
+ bcm2835_gpio_wake_irq_handler,
+ IRQF_SHARED, name, pc);
+ if (err)
+ dev_warn(dev, "unable to request wake IRQ %d\n",
+ pc->wake_irq[i]);
+ }
+
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_level_irq;
--
2.17.1
^ permalink raw reply related
* [PATCH v3 2/4] dt-bindings: pinctrl: Document optional BCM7211 wake-up interrupts
From: Florian Fainelli @ 2020-05-31 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Florian Fainelli, Linus Walleij, Rob Herring, Ray Jui,
Scott Branden,
maintainer:BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITE...,
Nicolas Saenz Julienne, Stefan Wahren, Geert Uytterhoeven,
Matti Vaittinen, open list:PIN CONTROL SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
In-Reply-To: <20200531001101.24945-1-f.fainelli@gmail.com>
BCM7211 supports wake-up interrupts in the form of optional interrupt
lines, one per bank, plus the "all banks" interrupt line.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
.../devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt b/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
index dfc67b90591c..5682b2010e50 100644
--- a/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
@@ -16,7 +16,9 @@ Required properties:
second cell is used to specify optional parameters:
- bit 0 specifies polarity (0 for normal, 1 for inverted)
- interrupts : The interrupt outputs from the controller. One interrupt per
- individual bank followed by the "all banks" interrupt.
+ individual bank followed by the "all banks" interrupt. For BCM7211, an
+ additional set of per-bank interrupt line and an "all banks" wake-up
+ interrupt may be specified.
- interrupt-controller: Marks the device node as an interrupt controller.
- #interrupt-cells : Should be 2.
The first cell is the GPIO number.
--
2.17.1
^ permalink raw reply related
* [PATCH v3 3/4] pinctrl: bcm2835: Match BCM7211 compatible string
From: Florian Fainelli @ 2020-05-31 0:11 UTC (permalink / raw)
To: linux-kernel
Cc: Florian Fainelli, Linus Walleij, Rob Herring, Ray Jui,
Scott Branden,
maintainer:BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITE...,
Nicolas Saenz Julienne, Stefan Wahren, Geert Uytterhoeven,
Matti Vaittinen, open list:PIN CONTROL SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
In-Reply-To: <20200531001101.24945-1-f.fainelli@gmail.com>
The BCM7211 SoC uses the same pinconf_ops as the ones defined for the
BCM2711 SoC, match the compatible string and use the correct set of
options.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/pinctrl/bcm/pinctrl-bcm2835.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index 06bd2b70af3c..1b00d93aa66e 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -1137,6 +1137,10 @@ static const struct of_device_id bcm2835_pinctrl_match[] = {
.compatible = "brcm,bcm2711-gpio",
.data = &bcm2711_plat_data,
},
+ {
+ .compatible = "brcm,bcm7211-gpio",
+ .data = &bcm2711_plat_data,
+ },
{}
};
--
2.17.1
^ permalink raw reply related
* [PATCH v3 1/4] dt-bindings: pinctrl: Document 7211 compatible for brcm,bcm2835-gpio.txt
From: Florian Fainelli @ 2020-05-31 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Florian Fainelli, Linus Walleij, Rob Herring, Ray Jui,
Scott Branden,
maintainer:BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITE...,
Nicolas Saenz Julienne, Stefan Wahren, Geert Uytterhoeven,
Matti Vaittinen, open list:PIN CONTROL SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
In-Reply-To: <20200531001101.24945-1-f.fainelli@gmail.com>
Document the brcm,bcm7211-gpio compatible string in the
brcm,bcm2835-gpio.txt document.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt b/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
index 3cab7336a326..dfc67b90591c 100644
--- a/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
@@ -9,6 +9,7 @@ Required properties:
"brcm,bcm2835-gpio" - BCM2835 compatible pinctrl
"brcm,bcm7211-gpio" - BCM7211 compatible pinctrl
"brcm,bcm2711-gpio" - BCM2711 compatible pinctrl
+ "brcm,bcm7211-gpio" - BCM7211 compatible pinctrl
- reg: Should contain the physical address of the GPIO module's registers.
- gpio-controller: Marks the device node as a GPIO controller.
- #gpio-cells : Should be two. The first cell is the pin number and the
--
2.17.1
^ permalink raw reply related
* [PATCH v3 0/4] pinctrl: bcm2835: Add support for wake-up interrupts
From: Florian Fainelli @ 2020-05-31 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Florian Fainelli, Linus Walleij, Rob Herring, Ray Jui,
Scott Branden,
maintainer:BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITE...,
Nicolas Saenz Julienne, Stefan Wahren, Geert Uytterhoeven,
Matti Vaittinen, open list:PIN CONTROL SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
Hi Linus,
This patch series updates the bcm2835 pinctrl driver to support
the BCM7211 SoC which is quite similar to 2711 (Raspberry Pi 4)
except that it also supports wake-up interrupts.
Thanks!
Changes in v3:
- added Rob's Acked-by for the binding patches (#1 and #2)
- correct check on the number of GPIOs in irq_set_irq_wake (Stefan)
Changes in v2:
- fixed patch #3 to reference the correct data structure (Stefan)
- fixed patch #4 to use conditional initialization and fetching of
interrupt resources to limit the memory overhead for non-7211 chips
Florian Fainelli (4):
dt-bindings: pinctrl: Document 7211 compatible for
brcm,bcm2835-gpio.txt
dt-bindings: pinctrl: Document optional BCM7211 wake-up interrupts
pinctrl: bcm2835: Match BCM7211 compatible string
pinctrl: bcm2835: Add support for wake-up interrupts
.../bindings/pinctrl/brcm,bcm2835-gpio.txt | 5 +-
drivers/pinctrl/bcm/pinctrl-bcm2835.c | 80 ++++++++++++++++++-
2 files changed, 83 insertions(+), 2 deletions(-)
--
2.17.1
^ permalink raw reply
* Re: [PATCH v2 4/4] pinctrl: bcm2835: Add support for wake-up interrupts
From: Stefan Wahren @ 2020-05-30 23:30 UTC (permalink / raw)
To: Florian Fainelli, linux-kernel
Cc: moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Geert Uytterhoeven, Scott Branden, Ray Jui, Linus Walleij,
Matti Vaittinen, open list:PIN CONTROL SUBSYSTEM, Rob Herring,
maintainer:BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITE...,
moderated list:BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE,
Nicolas Saenz Julienne
In-Reply-To: <bf0ceef7-4015-69f1-336c-4d296f96adae@gmail.com>
Hi Florian,
Am 30.05.20 um 23:19 schrieb Florian Fainelli:
>
> On 5/30/2020 12:49 AM, Stefan Wahren wrote:
>> Hi Florian,
>>
>> Am 29.05.20 um 21:15 schrieb Florian Fainelli:
>>> }
>>>
>>> +static int bcm2835_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
>>> +{
>>> + struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
>>> + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
>>> + unsigned gpio = irqd_to_hwirq(data);
>>> + unsigned int irqgroup;
>>> + int ret = -EINVAL;
>>> +
>>> + if (!pc->wake_irq)
>>> + return ret;
>>> +
>>> + if (gpio <= 27)
>>> + irqgroup = 0;
>>> + else if (gpio >= 28 && gpio <= 45)
>>> + irqgroup = 1;
>>> + else if (gpio >= 46 && gpio <= 53)
>>> + irqgroup = 2;
>> in case the BCM7211 has 58 GPIOs, but the wake up interrupts are only
>> available for the first 54 this should deserve a comment.
> irqgroup 2 covers GPIOs 46 through 57, thanks for noticing. Do you have
> more comments before I spin a v3? Thank you for reviewing.
no, i don't.
Regards
Stefan
^ permalink raw reply
* [PATCH v2 4/4] dt-bindings: iio: scd30: add device binding file
From: Tomasz Duszynski @ 2020-05-30 21:36 UTC (permalink / raw)
To: linux-iio
Cc: linux-kernel, devicetree, robh+dt, jic23, andy.shevchenko, pmeerw,
Tomasz Duszynski
In-Reply-To: <20200530213630.87159-1-tomasz.duszynski@octakon.com>
Add SCD30 sensor binding file.
Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
---
.../iio/chemical/sensirion,scd30.yaml | 68 +++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 69 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
new file mode 100644
index 000000000000..34cc3925d64d
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/chemical/sensirion,scd30.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sensirion SCD30 carbon dioxide sensor
+
+maintainers:
+ - Tomasz Duszynski <tomasz.duszynski@octakon.com>
+
+description: |
+ Air quality sensor capable of measuring co2 concentration, temperature
+ and relative humidity.
+
+properties:
+ compatible:
+ enum:
+ - sensirion,scd30
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ vdd-supply: true
+
+ sensirion,sel-gpios:
+ description: GPIO connected to the SEL line
+ maxItems: 1
+
+ sensirion,pwm-gpios:
+ description: GPIO connected to the PWM line
+ maxItems: 1
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ # include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ scd30@61 {
+ compatible = "sensirion,scd30";
+ reg = <0x61>;
+ vdd-supply = <&vdd>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
+ - |
+ # include <dt-bindings/interrupt-controller/irq.h>
+ serial {
+ scd30 {
+ compatible = "sensirion,scd30";
+ vdd-supply = <&vdd>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 5db4b446c8ba..0ab9cf39e051 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15140,6 +15140,7 @@ F: include/uapi/linux/phantom.h
SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
M: Tomasz Duszynski <tomasz.duszynski@octakon.com>
S: Maintained
+F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd30.yaml
F: drivers/iio/chemical/scd30.h
F: drivers/iio/chemical/scd30_core.c
F: drivers/iio/chemical/scd30_i2c.c
--
2.26.2
^ permalink raw reply related
* [PATCH v2 3/4] iio: chemical: scd30: add serial interface driver
From: Tomasz Duszynski @ 2020-05-30 21:36 UTC (permalink / raw)
To: linux-iio
Cc: linux-kernel, devicetree, robh+dt, jic23, andy.shevchenko, pmeerw,
Tomasz Duszynski
In-Reply-To: <20200530213630.87159-1-tomasz.duszynski@octakon.com>
Add serial interface driver for the SCD30 sensor.
Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
---
MAINTAINERS | 1 +
drivers/iio/chemical/Kconfig | 11 ++
drivers/iio/chemical/Makefile | 1 +
drivers/iio/chemical/scd30_serial.c | 266 ++++++++++++++++++++++++++++
4 files changed, 279 insertions(+)
create mode 100644 drivers/iio/chemical/scd30_serial.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 13aed3473b7e..5db4b446c8ba 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15143,6 +15143,7 @@ S: Maintained
F: drivers/iio/chemical/scd30.h
F: drivers/iio/chemical/scd30_core.c
F: drivers/iio/chemical/scd30_i2c.c
+F: drivers/iio/chemical/scd30_serial.c
SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
M: Tomasz Duszynski <tduszyns@gmail.com>
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 970d34888c2e..10bb431bc3ce 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -107,6 +107,17 @@ config SCD30_I2C
To compile this driver as a module, choose M here: the module will
be called scd30_i2c.
+config SCD30_SERIAL
+ tristate "SCD30 carbon dioxide sensor serial driver"
+ depends on SCD30_CORE && SERIAL_DEV_BUS
+ select CRC16
+ help
+ Say Y here to build support for the Sensirion SCD30 serial interface
+ driver.
+
+ To compile this driver as a module, choose M here: the module will
+ be called scd30_serial.
+
config SENSIRION_SGP30
tristate "Sensirion SGPxx gas sensors"
depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index 0966ca34e34b..fef63dd5bf92 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_PMS7003) += pms7003.o
obj-$(CONFIG_SCD30_CORE) += scd30_core.o
obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o
+obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
obj-$(CONFIG_SPS30) += sps30.o
obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c
new file mode 100644
index 000000000000..07d7d3110fe0
--- /dev/null
+++ b/drivers/iio/chemical/scd30_serial.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sensirion SCD30 carbon dioxide sensor serial driver
+ *
+ * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
+ */
+#include <linux/crc16.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/iio.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/serdev.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <asm/unaligned.h>
+
+#include "scd30.h"
+
+#define SCD30_SERDEV_ADDR 0x61
+#define SCD30_SERDEV_WRITE 0x06
+#define SCD30_SERDEV_READ 0x03
+#define SCD30_SERDEV_MAX_BUF_SIZE 17
+#define SCD30_SERDEV_RX_HEADER_SIZE 3
+#define SCD30_SERDEV_CRC_SIZE 2
+#define SCD30_SERDEV_TIMEOUT msecs_to_jiffies(200)
+
+struct scd30_serdev_priv {
+ struct completion meas_ready;
+ char *buf;
+ int num_expected;
+ int num;
+};
+
+static u16 scd30_serdev_cmd_lookup_tbl[] = {
+ [CMD_START_MEAS] = 0x0036,
+ [CMD_STOP_MEAS] = 0x0037,
+ [CMD_MEAS_INTERVAL] = 0x0025,
+ [CMD_MEAS_READY] = 0x0027,
+ [CMD_READ_MEAS] = 0x0028,
+ [CMD_ASC] = 0x003a,
+ [CMD_FRC] = 0x0039,
+ [CMD_TEMP_OFFSET] = 0x003b,
+ [CMD_FW_VERSION] = 0x0020,
+ [CMD_RESET] = 0x0034,
+};
+
+static u16 scd30_serdev_calc_crc(const char *buf, int size)
+{
+ return crc16(0xffff, buf, size);
+}
+
+static int scd30_serdev_xfer(struct scd30_state *state, char *txbuf, int txsize,
+ char *rxbuf, int rxsize)
+{
+ struct serdev_device *serdev = to_serdev_device(state->dev);
+ struct scd30_serdev_priv *priv = state->priv;
+ int ret;
+
+ priv->buf = rxbuf;
+ priv->num_expected = rxsize;
+ priv->num = 0;
+
+ ret = serdev_device_write(serdev, txbuf, txsize, SCD30_SERDEV_TIMEOUT);
+ if (ret < txsize)
+ return ret < 0 ? ret : -EIO;
+
+ ret = wait_for_completion_interruptible_timeout(&priv->meas_ready,
+ SCD30_SERDEV_TIMEOUT);
+ if (ret > 0)
+ ret = 0;
+ else if (!ret)
+ ret = -ETIMEDOUT;
+
+ return ret;
+}
+
+static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd,
+ u16 arg, void *response, int size)
+{
+ /*
+ * Communication over serial line is based on modbus protocol (or rather
+ * its variation called modbus over serial to be precise). Upon
+ * receiving a request device should reply with response.
+ *
+ * Frame below represents a request message. Each field takes
+ * exactly one byte.
+ *
+ * +------+------+-----+-----+-------+-------+-----+-----+
+ * | dev | op | reg | reg | byte1 | byte0 | crc | crc |
+ * | addr | code | msb | lsb | | | lsb | msb |
+ * +------+------+-----+-----+-------+-------+-----+-----+
+ *
+ * The message device replies with depends on the 'op code' field from
+ * the request. In case it was set to SCD30_SERDEV_WRITE sensor should
+ * reply with unchanged request. Otherwise 'op code' was set to
+ * SCD30_SERDEV_READ and response looks like the one below. As with
+ * request, each field takes one byte.
+ *
+ * +------+------+--------+-------+-----+-------+-----+-----+
+ * | dev | op | num of | byte0 | ... | byteN | crc | crc |
+ * | addr | code | bytes | | | | lsb | msb |
+ * +------+------+--------+-------+-----+-------+-----+-----+
+ */
+ char txbuf[SCD30_SERDEV_MAX_BUF_SIZE] = { SCD30_SERDEV_ADDR },
+ rxbuf[SCD30_SERDEV_MAX_BUF_SIZE], *rsp = response;
+ int ret, rxsize, txsize = 2;
+ u16 crc;
+
+ put_unaligned_be16(scd30_serdev_cmd_lookup_tbl[cmd], txbuf + txsize);
+ txsize += 2;
+
+ if (rsp) {
+ txbuf[1] = SCD30_SERDEV_READ;
+ if (cmd == CMD_READ_MEAS)
+ /* number of u16 words to read */
+ put_unaligned_be16(size / 2, txbuf + txsize);
+ else
+ put_unaligned_be16(0x0001, txbuf + txsize);
+ txsize += 2;
+ crc = scd30_serdev_calc_crc(txbuf, txsize);
+ put_unaligned_le16(crc, txbuf + txsize);
+ txsize += 2;
+ rxsize = SCD30_SERDEV_RX_HEADER_SIZE + size +
+ SCD30_SERDEV_CRC_SIZE;
+ } else {
+ if ((cmd == CMD_STOP_MEAS) || (cmd == CMD_RESET))
+ arg = 0x0001;
+
+ txbuf[1] = SCD30_SERDEV_WRITE;
+ put_unaligned_be16(arg, txbuf + txsize);
+ txsize += 2;
+ crc = scd30_serdev_calc_crc(txbuf, txsize);
+ put_unaligned_le16(crc, txbuf + txsize);
+ txsize += 2;
+ rxsize = txsize;
+ }
+
+ ret = scd30_serdev_xfer(state, txbuf, txsize, rxbuf, rxsize);
+ if (ret)
+ return ret;
+
+ switch (txbuf[1]) {
+ case SCD30_SERDEV_WRITE:
+ if (memcmp(txbuf, txbuf, txsize)) {
+ dev_err(state->dev, "wrong message received\n");
+ return -EIO;
+ }
+ break;
+ case SCD30_SERDEV_READ:
+ if (rxbuf[2] != (rxsize -
+ SCD30_SERDEV_RX_HEADER_SIZE -
+ SCD30_SERDEV_CRC_SIZE)) {
+ dev_err(state->dev,
+ "received data size does not match header\n");
+ return -EIO;
+ }
+
+ rxsize -= SCD30_SERDEV_CRC_SIZE;
+ crc = get_unaligned_le16(rxbuf + rxsize);
+ if (crc != scd30_serdev_calc_crc(rxbuf, rxsize)) {
+ dev_err(state->dev, "data integrity check failed\n");
+ return -EIO;
+ }
+
+ rxsize -= SCD30_SERDEV_RX_HEADER_SIZE;
+ memcpy(rsp, rxbuf + SCD30_SERDEV_RX_HEADER_SIZE, rxsize);
+ break;
+ default:
+ dev_err(state->dev, "received unknown op code\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int scd30_serdev_receive_buf(struct serdev_device *serdev,
+ const unsigned char *buf, size_t size)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev);
+ struct scd30_serdev_priv *priv;
+ struct scd30_state *state;
+ int num;
+
+ if (!indio_dev)
+ return 0;
+
+ state = iio_priv(indio_dev);
+ priv = state->priv;
+
+ /* just in case sensor puts some unexpected bytes on the bus */
+ if (!priv->buf)
+ return 0;
+
+ if (priv->num + size >= priv->num_expected)
+ num = priv->num_expected - priv->num;
+ else
+ num = size;
+
+ memcpy(priv->buf + priv->num, buf, num);
+ priv->num += num;
+
+ if (priv->num == priv->num_expected) {
+ priv->buf = NULL;
+ complete(&priv->meas_ready);
+ }
+
+ return num;
+}
+
+static const struct serdev_device_ops scd30_serdev_ops = {
+ .receive_buf = scd30_serdev_receive_buf,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+static int scd30_serdev_probe(struct serdev_device *serdev)
+{
+ struct device *dev = &serdev->dev;
+ struct scd30_serdev_priv *priv;
+ int irq, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ init_completion(&priv->meas_ready);
+ serdev_device_set_client_ops(serdev, &scd30_serdev_ops);
+
+ ret = devm_serdev_device_open(dev, serdev);
+ if (ret)
+ return ret;
+
+ serdev_device_set_baudrate(serdev, 19200);
+ serdev_device_set_flow_control(serdev, false);
+
+ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
+ if (ret)
+ return ret;
+
+ irq = fwnode_irq_get(dev_fwnode(dev), 0);
+
+ return scd30_probe(dev, irq, KBUILD_MODNAME, priv,
+ scd30_serdev_command);
+}
+
+static const struct of_device_id scd30_serdev_of_match[] = {
+ { .compatible = "sensirion,scd30" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, scd30_serdev_of_match);
+
+static struct serdev_device_driver scd30_serdev_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = scd30_serdev_of_match,
+ .pm = &scd30_pm_ops,
+ },
+ .probe = scd30_serdev_probe,
+};
+module_serdev_device_driver(scd30_serdev_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
+MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor serial driver");
+MODULE_LICENSE("GPL v2");
--
2.26.2
^ permalink raw reply related
* [PATCH v2 2/4] iio: chemical: scd30: add I2C interface driver
From: Tomasz Duszynski @ 2020-05-30 21:36 UTC (permalink / raw)
To: linux-iio
Cc: linux-kernel, devicetree, robh+dt, jic23, andy.shevchenko, pmeerw,
Tomasz Duszynski
In-Reply-To: <20200530213630.87159-1-tomasz.duszynski@octakon.com>
Add I2C interface driver for the SCD30 sensor.
Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
---
MAINTAINERS | 1 +
drivers/iio/chemical/Kconfig | 11 +++
drivers/iio/chemical/Makefile | 1 +
drivers/iio/chemical/scd30_i2c.c | 134 +++++++++++++++++++++++++++++++
4 files changed, 147 insertions(+)
create mode 100644 drivers/iio/chemical/scd30_i2c.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 41a509cca6f1..13aed3473b7e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15142,6 +15142,7 @@ M: Tomasz Duszynski <tomasz.duszynski@octakon.com>
S: Maintained
F: drivers/iio/chemical/scd30.h
F: drivers/iio/chemical/scd30_core.c
+F: drivers/iio/chemical/scd30_i2c.c
SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
M: Tomasz Duszynski <tduszyns@gmail.com>
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 99e852b67e55..970d34888c2e 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -96,6 +96,17 @@ config SCD30_CORE
To compile this driver as a module, choose M here: the module will
be called scd30_core.
+config SCD30_I2C
+ tristate "SCD30 carbon dioxide sensor I2C driver"
+ depends on SCD30_CORE && I2C
+ select CRC8
+ help
+ Say Y here to build support for the Sensirion SCD30 I2C interface
+ driver.
+
+ To compile this driver as a module, choose M here: the module will
+ be called scd30_i2c.
+
config SENSIRION_SGP30
tristate "Sensirion SGPxx gas sensors"
depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index c9804b041ecd..0966ca34e34b 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_CCS811) += ccs811.o
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_PMS7003) += pms7003.o
obj-$(CONFIG_SCD30_CORE) += scd30_core.o
+obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
obj-$(CONFIG_SPS30) += sps30.o
obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/scd30_i2c.c b/drivers/iio/chemical/scd30_i2c.c
new file mode 100644
index 000000000000..a6b532b83669
--- /dev/null
+++ b/drivers/iio/chemical/scd30_i2c.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sensirion SCD30 carbon dioxide sensor i2c driver
+ *
+ * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
+ *
+ * I2C slave address: 0x61
+ */
+#include <linux/crc8.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <asm/unaligned.h>
+
+#include "scd30.h"
+
+#define SCD30_I2C_MAX_BUF_SIZE 18
+#define SCD30_I2C_CRC8_POLYNOMIAL 0x31
+
+static u16 scd30_i2c_cmd_lookup_tbl[] = {
+ [CMD_START_MEAS] = 0x0010,
+ [CMD_STOP_MEAS] = 0x0104,
+ [CMD_MEAS_INTERVAL] = 0x4600,
+ [CMD_MEAS_READY] = 0x0202,
+ [CMD_READ_MEAS] = 0x0300,
+ [CMD_ASC] = 0x5306,
+ [CMD_FRC] = 0x5204,
+ [CMD_TEMP_OFFSET] = 0x5403,
+ [CMD_FW_VERSION] = 0xd100,
+ [CMD_RESET] = 0xd304,
+};
+
+DECLARE_CRC8_TABLE(scd30_i2c_crc8_tbl);
+
+static int scd30_i2c_xfer(struct scd30_state *state, char *txbuf, int txsize,
+ char *rxbuf, int rxsize)
+{
+ struct i2c_client *client = to_i2c_client(state->dev);
+ int ret;
+
+ /*
+ * repeated start is not supported hence instead of sending two i2c
+ * messages in a row we send one by one
+ */
+ ret = i2c_master_send(client, txbuf, txsize);
+ if (ret != txsize)
+ return ret < 0 ? ret : -EIO;
+
+ if (!rxbuf)
+ return 0;
+
+ ret = i2c_master_recv(client, rxbuf, rxsize);
+ if (ret != rxsize)
+ return ret < 0 ? ret : -EIO;
+
+ return 0;
+}
+
+static int scd30_i2c_command(struct scd30_state *state, enum scd30_cmd cmd,
+ u16 arg, void *response, int size)
+{
+ char crc, buf[SCD30_I2C_MAX_BUF_SIZE], *rsp = response;
+ int i, ret;
+
+ put_unaligned_be16(scd30_i2c_cmd_lookup_tbl[cmd], buf);
+ i = 2;
+
+ if (rsp) {
+ /* each two bytes are followed by a crc8 */
+ size += size / 2;
+ } else {
+ put_unaligned_be16(arg, buf + i);
+ crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE);
+ i += 2;
+ buf[i] = crc;
+ i += 1;
+
+ /* commands below don't take an argument */
+ if ((cmd == CMD_STOP_MEAS) || (cmd == CMD_RESET))
+ i -= 3;
+ }
+
+ ret = scd30_i2c_xfer(state, buf, i, buf, size);
+ if (ret)
+ return ret;
+
+ /* validate received data and strip off crc bytes */
+ for (i = 0; i < size; i += 3) {
+ crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE);
+ if (crc != buf[i + 2]) {
+ dev_err(state->dev, "data integrity check failed\n");
+ return -EIO;
+ }
+
+ *rsp++ = buf[i];
+ *rsp++ = buf[i + 1];
+ }
+
+ return 0;
+}
+
+static int scd30_i2c_probe(struct i2c_client *client)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ crc8_populate_msb(scd30_i2c_crc8_tbl, SCD30_I2C_CRC8_POLYNOMIAL);
+
+ return scd30_probe(&client->dev, client->irq, client->name, NULL,
+ scd30_i2c_command);
+}
+
+static const struct of_device_id scd30_i2c_of_match[] = {
+ { .compatible = "sensirion,scd30" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, scd30_i2c_of_match);
+
+static struct i2c_driver scd30_i2c_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = scd30_i2c_of_match,
+ .pm = &scd30_pm_ops,
+ },
+ .probe_new = scd30_i2c_probe,
+};
+module_i2c_driver(scd30_i2c_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
+MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver");
+MODULE_LICENSE("GPL v2");
--
2.26.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox