From a8cf1723c4b794f49d0906f9ddc9d1ac52af4ee2 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 15 Jan 2020 14:45:41 +0100 Subject: [PATCH 01/98] dt-bindings: iio: adc: stm32-adc: convert bindings to json-schema Convert the STM32 ADC binding to DT schema format using json-schema Signed-off-by: Fabrice Gasnier Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/st,stm32-adc.txt | 149 ------ .../bindings/iio/adc/st,stm32-adc.yaml | 458 ++++++++++++++++++ 2 files changed, 458 insertions(+), 149 deletions(-) delete mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt deleted file mode 100644 index 8de933146771..000000000000 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt +++ /dev/null @@ -1,149 +0,0 @@ -STMicroelectronics STM32 ADC device driver - -STM32 ADC is a successive approximation analog-to-digital converter. -It has several multiplexed input channels. Conversions can be performed -in single, continuous, scan or discontinuous mode. Result of the ADC is -stored in a left-aligned or right-aligned 32-bit data register. -Conversions can be launched in software or using hardware triggers. - -The analog watchdog feature allows the application to detect if the input -voltage goes beyond the user-defined, higher or lower thresholds. - -Each STM32 ADC block can have up to 3 ADC instances. - -Each instance supports two contexts to manage conversions, each one has its -own configurable sequence and trigger: -- regular conversion can be done in sequence, running in background -- injected conversions have higher priority, and so have the ability to - interrupt regular conversion sequence (either triggered in SW or HW). - Regular sequence is resumed, in case it has been interrupted. - -Contents of a stm32 adc root node: ------------------------------------ -Required properties: -- compatible: Should be one of: - "st,stm32f4-adc-core" - "st,stm32h7-adc-core" - "st,stm32mp1-adc-core" -- reg: Offset and length of the ADC block register set. -- interrupts: One or more interrupts for ADC block. Some parts like stm32f4 - and stm32h7 share a common ADC interrupt line. stm32mp1 has two separate - interrupt lines, one for each ADC within ADC block. -- clocks: Core can use up to two clocks, depending on part used: - - "adc" clock: for the analog circuitry, common to all ADCs. - It's required on stm32f4. - It's optional on stm32h7. - - "bus" clock: for registers access, common to all ADCs. - It's not present on stm32f4. - It's required on stm32h7. -- clock-names: Must be "adc" and/or "bus" depending on part used. -- interrupt-controller: Identifies the controller node as interrupt-parent -- vdda-supply: Phandle to the vdda input analog voltage. -- vref-supply: Phandle to the vref input analog reference voltage. -- #interrupt-cells = <1>; -- #address-cells = <1>; -- #size-cells = <0>; - -Optional properties: -- A pinctrl state named "default" for each ADC channel may be defined to set - inX ADC pins in mode of operation for analog input on external pin. -- booster-supply: Phandle to the embedded booster regulator that can be used - to supply ADC analog input switches on stm32h7 and stm32mp1. -- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC - analog input switches on stm32mp1. -- st,syscfg: Phandle to system configuration controller. It can be used to - control the analog circuitry on stm32mp1. -- st,max-clk-rate-hz: Allow to specify desired max clock rate used by analog - circuitry. - -Contents of a stm32 adc child node: ------------------------------------ -An ADC block node should contain at least one subnode, representing an -ADC instance available on the machine. - -Required properties: -- compatible: Should be one of: - "st,stm32f4-adc" - "st,stm32h7-adc" - "st,stm32mp1-adc" -- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200). -- clocks: Input clock private to this ADC instance. It's required only on - stm32f4, that has per instance clock input for registers access. -- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or - 2 for adc@200). -- st,adc-channels: List of single-ended channels muxed for this ADC. - It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered - from 0 to 15 or 19 (resp. for in0..in15 or in0..in19). -- st,adc-diff-channels: List of differential channels muxed for this ADC. - Depending on part used, some channels can be configured as differential - instead of single-ended (e.g. stm32h7). List here positive and negative - inputs pairs as , ,... vinp and vinn are numbered - from 0 to 19 on stm32h7) - Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is required. - Both properties can be used together. Some channels can be used as - single-ended and some other ones as differential (mixed). But channels - can't be configured both as single-ended and differential (invalid). -- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in - Documentation/devicetree/bindings/iio/iio-bindings.txt - -Optional properties: -- dmas: Phandle to dma channel for this ADC instance. - See ../../dma/dma.txt for details. -- dma-names: Must be "rx" when dmas property is being used. -- assigned-resolution-bits: Resolution (bits) to use for conversions. Must - match device available resolutions: - * can be 6, 8, 10 or 12 on stm32f4 - * can be 8, 10, 12, 14 or 16 on stm32h7 - Default is maximum resolution if unset. -- st,min-sample-time-nsecs: Minimum sampling time in nanoseconds. - Depending on hardware (board) e.g. high/low analog input source impedance, - fine tune of ADC sampling time may be recommended. - This can be either one value or an array that matches 'st,adc-channels' list, - to set sample time resp. for all channels, or independently for each channel. - -Example: - adc: adc@40012000 { - compatible = "st,stm32f4-adc-core"; - reg = <0x40012000 0x400>; - interrupts = <18>; - clocks = <&rcc 0 168>; - clock-names = "adc"; - vref-supply = <®_vref>; - interrupt-controller; - pinctrl-names = "default"; - pinctrl-0 = <&adc3_in8_pin>; - - #interrupt-cells = <1>; - #address-cells = <1>; - #size-cells = <0>; - - adc@0 { - compatible = "st,stm32f4-adc"; - #io-channel-cells = <1>; - reg = <0x0>; - clocks = <&rcc 0 168>; - interrupt-parent = <&adc>; - interrupts = <0>; - st,adc-channels = <8>; - dmas = <&dma2 0 0 0x400 0x0>; - dma-names = "rx"; - assigned-resolution-bits = <8>; - }; - ... - other adc child nodes follow... - }; - -Example to setup: -- channel 1 as single-ended -- channels 2 & 3 as differential (with resp. 6 & 7 negative inputs) - - adc: adc@40022000 { - compatible = "st,stm32h7-adc-core"; - ... - adc1: adc@0 { - compatible = "st,stm32h7-adc"; - ... - st,adc-channels = <1>; - st,adc-diff-channels = <2 6>, <3 7>; - }; - }; diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml new file mode 100644 index 000000000000..933ba37944d7 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -0,0 +1,458 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/bindings/iio/adc/st,stm32-adc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: STMicroelectronics STM32 ADC bindings + +description: | + STM32 ADC is a successive approximation analog-to-digital converter. + It has several multiplexed input channels. Conversions can be performed + in single, continuous, scan or discontinuous mode. Result of the ADC is + stored in a left-aligned or right-aligned 32-bit data register. + Conversions can be launched in software or using hardware triggers. + + The analog watchdog feature allows the application to detect if the input + voltage goes beyond the user-defined, higher or lower thresholds. + + Each STM32 ADC block can have up to 3 ADC instances. + +maintainers: + - Fabrice Gasnier + +properties: + compatible: + enum: + - st,stm32f4-adc-core + - st,stm32h7-adc-core + - st,stm32mp1-adc-core + + reg: + maxItems: 1 + + interrupts: + description: | + One or more interrupts for ADC block, depending on part used: + - stm32f4 and stm32h7 share a common ADC interrupt line. + - stm32mp1 has two separate interrupt lines, one for each ADC within + ADC block. + minItems: 1 + maxItems: 2 + + clocks: + description: | + Core can use up to two clocks, depending on part used: + - "adc" clock: for the analog circuitry, common to all ADCs. + It's required on stm32f4. + It's optional on stm32h7 and stm32mp1. + - "bus" clock: for registers access, common to all ADCs. + It's not present on stm32f4. + It's required on stm32h7 and stm32mp1. + + clock-names: true + + st,max-clk-rate-hz: + description: + Allow to specify desired max clock rate used by analog circuitry. + + vdda-supply: + description: Phandle to the vdda input analog voltage. + + vref-supply: + description: Phandle to the vref input analog reference voltage. + + booster-supply: + description: + Phandle to the embedded booster regulator that can be used to supply ADC + analog input switches on stm32h7 and stm32mp1. + + vdd-supply: + description: + Phandle to the vdd input voltage. It can be used to supply ADC analog + input switches on stm32mp1. + + st,syscfg: + description: + Phandle to system configuration controller. It can be used to control the + analog circuitry on stm32mp1. + allOf: + - $ref: "/schemas/types.yaml#/definitions/phandle-array" + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +allOf: + - if: + properties: + compatible: + contains: + const: st,stm32f4-adc-core + + then: + properties: + clocks: + maxItems: 1 + + clock-names: + const: adc + + interrupts: + items: + - description: interrupt line common for all ADCs + + st,max-clk-rate-hz: + minimum: 600000 + maximum: 36000000 + default: 36000000 + + booster-supply: false + + vdd-supply: false + + st,syscfg: false + + - if: + properties: + compatible: + contains: + const: st,stm32h7-adc-core + + then: + properties: + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: bus + - const: adc + minItems: 1 + maxItems: 2 + + interrupts: + items: + - description: interrupt line common for all ADCs + + st,max-clk-rate-hz: + minimum: 120000 + maximum: 36000000 + default: 36000000 + + vdd-supply: false + + st,syscfg: false + + - if: + properties: + compatible: + contains: + const: st,stm32mp1-adc-core + + then: + properties: + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: bus + - const: adc + minItems: 1 + maxItems: 2 + + interrupts: + items: + - description: interrupt line for ADC1 + - description: interrupt line for ADC2 + + st,max-clk-rate-hz: + minimum: 120000 + maximum: 36000000 + default: 36000000 + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - vdda-supply + - vref-supply + - interrupt-controller + - '#interrupt-cells' + - '#address-cells' + - '#size-cells' + +patternProperties: + "^adc@[0-9]+$": + type: object + description: + An ADC block node should contain at least one subnode, representing an + ADC instance available on the machine. + + properties: + compatible: + enum: + - st,stm32f4-adc + - st,stm32h7-adc + - st,stm32mp1-adc + + reg: + description: | + Offset of ADC instance in ADC block. Valid values are: + - 0x0: ADC1 + - 0x100: ADC2 + - 0x200: ADC3 (stm32f4 only) + maxItems: 1 + + '#io-channel-cells': + const: 1 + + interrupts: + description: | + IRQ Line for the ADC instance. Valid values are: + - 0 for adc@0 + - 1 for adc@100 + - 2 for adc@200 (stm32f4 only) + maxItems: 1 + + clocks: + description: + Input clock private to this ADC instance. It's required only on + stm32f4, that has per instance clock input for registers access. + maxItems: 1 + + dmas: + description: RX DMA Channel + maxItems: 1 + + dma-names: + const: rx + + assigned-resolution-bits: + description: | + Resolution (bits) to use for conversions: + - can be 6, 8, 10 or 12 on stm32f4 + - can be 8, 10, 12, 14 or 16 on stm32h7 and stm32mp1 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + + st,adc-channels: + description: | + List of single-ended channels muxed for this ADC. It can have up to: + - 16 channels, numbered from 0 to 15 (for in0..in15) on stm32f4 + - 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7 and + stm32mp1. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + + st,adc-diff-channels: + description: | + List of differential channels muxed for this ADC. Some channels can + be configured as differential instead of single-ended on stm32h7 and + on stm32mp1. Positive and negative inputs pairs are listed: + , ,... vinp and vinn are numbered from 0 to 19. + + Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is + required. Both properties can be used together. Some channels can be + used as single-ended and some other ones as differential (mixed). But + channels can't be configured both as single-ended and differential. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-matrix + - items: + items: + - description: | + "vinp" indicates positive input number + minimum: 0 + maximum: 19 + - description: | + "vinn" indicates negative input number + minimum: 0 + maximum: 19 + + st,min-sample-time-nsecs: + description: + Minimum sampling time in nanoseconds. Depending on hardware (board) + e.g. high/low analog input source impedance, fine tune of ADC + sampling time may be recommended. This can be either one value or an + array that matches "st,adc-channels" and/or "st,adc-diff-channels" + list, to set sample time resp. for all channels, or independently for + each channel. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + + allOf: + - if: + properties: + compatible: + contains: + const: st,stm32f4-adc + + then: + properties: + reg: + enum: + - 0x0 + - 0x100 + - 0x200 + + interrupts: + minimum: 0 + maximum: 2 + + assigned-resolution-bits: + enum: [6, 8, 10, 12] + default: 12 + + st,adc-channels: + minItems: 1 + maxItems: 16 + items: + minimum: 0 + maximum: 15 + + st,adc-diff-channels: false + + st,min-sample-time-nsecs: + minItems: 1 + maxItems: 16 + items: + minimum: 80 + + required: + - clocks + + - if: + properties: + compatible: + contains: + enum: + - st,stm32h7-adc + - st,stm32mp1-adc + + then: + properties: + reg: + enum: + - 0x0 + - 0x100 + + interrupts: + minimum: 0 + maximum: 1 + + assigned-resolution-bits: + enum: [8, 10, 12, 14, 16] + default: 16 + + st,adc-channels: + minItems: 1 + maxItems: 20 + items: + minimum: 0 + maximum: 19 + + st,min-sample-time-nsecs: + minItems: 1 + maxItems: 20 + items: + minimum: 40 + + additionalProperties: false + + anyOf: + - required: + - st,adc-channels + - required: + - st,adc-diff-channels + + required: + - compatible + - reg + - interrupts + - '#io-channel-cells' + +examples: + - | + // Example 1: with stm32f429, ADC1, single-ended channel 8 + adc123: adc@40012000 { + compatible = "st,stm32f4-adc-core"; + reg = <0x40012000 0x400>; + interrupts = <18>; + clocks = <&rcc 0 168>; + clock-names = "adc"; + st,max-clk-rate-hz = <36000000>; + vdda-supply = <&vdda>; + vref-supply = <&vref>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "st,stm32f4-adc"; + #io-channel-cells = <1>; + reg = <0x0>; + clocks = <&rcc 0 168>; + interrupt-parent = <&adc123>; + interrupts = <0>; + st,adc-channels = <8>; + dmas = <&dma2 0 0 0x400 0x0>; + dma-names = "rx"; + assigned-resolution-bits = <8>; + }; + // ... + // other adc child nodes follow... + }; + + - | + // Example 2: with stm32mp157c to setup ADC1 with: + // - channels 0 & 1 as single-ended + // - channels 2 & 3 as differential (with resp. 6 & 7 negative inputs) + #include + #include + adc12: adc@48003000 { + compatible = "st,stm32mp1-adc-core"; + reg = <0x48003000 0x400>; + interrupts = , + ; + clocks = <&rcc ADC12>, <&rcc ADC12_K>; + clock-names = "bus", "adc"; + booster-supply = <&booster>; + vdd-supply = <&vdd>; + vdda-supply = <&vdda>; + vref-supply = <&vref>; + st,syscfg = <&syscfg>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "st,stm32mp1-adc"; + #io-channel-cells = <1>; + reg = <0x0>; + interrupt-parent = <&adc12>; + interrupts = <0>; + st,adc-channels = <0 1>; + st,adc-diff-channels = <2 6>, <3 7>; + st,min-sample-time-nsecs = <5000>; + dmas = <&dmamux1 9 0x400 0x05>; + dma-names = "rx"; + }; + // ... + // other adc child node follow... + }; + +... From 357528156207bbd21df89a287ef98eccd5fb64c3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 20 Jan 2020 11:25:50 +0200 Subject: [PATCH 02/98] iio: accel: st_accel: Use st_sensors_dev_name_probe() Use st_sensors_dev_name_probe() instead open coded variant. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 633955d764cc..8c489312f668 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -147,12 +147,9 @@ static int st_accel_i2c_probe(struct i2c_client *client) const struct st_sensor_settings *settings; struct st_sensor_data *adata; struct iio_dev *indio_dev; - const char *match; int ret; - match = device_get_match_data(&client->dev); - if (match) - strlcpy(client->name, match, sizeof(client->name)); + st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name)); settings = st_accel_get_settings(client->name); if (!settings) { From 5de6570b8abb5c29be52d119eb5f653df15c7338 Mon Sep 17 00:00:00 2001 From: JieunKim Date: Mon, 27 Jan 2020 21:03:59 +0900 Subject: [PATCH 03/98] iio: imu: st_lsm6dsx: Fix mismatched comments odr to odr_table gain to fs_table 'gain' is actually in 'st_lsm6dsx_fs' structure of 'fs_table' Signed-off-by: JieunKim Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 9c3486a8134f..f2113a63721a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -230,8 +230,8 @@ enum st_lsm6dsx_ext_sensor_id { * @i2c_addr: I2c slave address list. * @wai: Wai address info. * @id: external sensor id. - * @odr: Output data rate of the sensor [Hz]. - * @gain: Configured sensor sensitivity. + * @odr_table: Output data rate of the sensor [Hz]. + * @fs_table: Configured sensor sensitivity table depending on full scale. * @temp_comp: Temperature compensation register info (addr + mask). * @pwr_table: Power on register info (addr + mask). * @off_canc: Offset cancellation register info (addr + mask). From a2ff6e0347a9f1cbf930a1b121027c950c68c4ea Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 26 Jan 2020 16:05:47 +0100 Subject: [PATCH 04/98] iio: light: Add DT bindings for GP2AP002 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds device tree bindings for the GP2AP002 light and proximity sensor. As with other early proximity sensors (~2010) the light sensor and proximity sensors were combined into a single component. Cc: Stephan Gerhold Cc: Minkyu Kang Cc: Paweł Chmiel Cc: Jonathan Bakker Cc: Oskar Andero Cc: Dmitry Torokhov Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/sharp,gp2ap002.yaml | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml diff --git a/Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml b/Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml new file mode 100644 index 000000000000..12aa16f24772 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/sharp,gp2ap002.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sharp GP2AP002A00F and GP2AP002S00F proximity and ambient light sensors + +maintainers: + - Linus Walleij + +description: | + Proximity and ambient light sensor with IR LED for the proximity + sensing and an analog output for light intensity. The ambient light + sensor output is not available on the GP2AP002S00F variant. + +properties: + compatible: + enum: + - sharp,gp2ap002a00f + - sharp,gp2ap002s00f + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: an interrupt for proximity, usually a GPIO line + + vdd-supply: + description: VDD power supply a phandle to a regulator + + vio-supply: + description: VIO power supply a phandle to a regulator + + io-channels: + maxItems: 1 + description: ALSOUT ADC channel to read the ambient light + + io-channel-names: + const: alsout + + sharp,proximity-far-hysteresis: + $ref: /schemas/types.yaml#/definitions/uint8 + description: | + Hysteresis setting for "far" object detection, this setting is + device-unique and adjust the optical setting for proximity detection + of a "far away" object in front of the sensor. + + sharp,proximity-close-hysteresis: + $ref: /schemas/types.yaml#/definitions/uint8 + description: | + Hysteresis setting for "close" object detection, this setting is + device-unique and adjust the optical setting for proximity detection + of a "close" object in front of the sensor. + +required: + - compatible + - reg + - interrupts + - sharp,proximity-far-hysteresis + - sharp,proximity-close-hysteresis + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@44 { + compatible = "sharp,gp2ap002a00f"; + reg = <0x44>; + interrupts = <18 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&vdd_regulator>; + vio-supply = <&vio_regulator>; + io-channels = <&adc_channel>; + io-channel-names = "alsout"; + sharp,proximity-far-hysteresis = /bits/ 8 <0x2f>; + sharp,proximity-close-hysteresis = /bits/ 8 <0x0f>; + }; + }; + +... From 97d642e23037c5545266f9564c9b81e6db81b122 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 26 Jan 2020 16:05:48 +0100 Subject: [PATCH 05/98] iio: light: Add a driver for Sharp GP2AP002x00F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver handles two different Sharp sensors that have been proposed for merging to the mainline kernel over the years, and already has a limited proximity-only driver in the input subsystem. These components are completely different from the confusingly similarly named Sharp GP2AP020A00F, for which we have a driver in drivers/iio/light/gp2ap020a00f.c The two components GP2AP002A00F and GP2AP002S00F are distinctively different but similar: they share the same set of registers but differ slightly in the I2C protocol. Instead of the approach by the previous input driver, we create a combined IIO proximity and light sensor driver. The plan is to merge this driver and delete the input driver. The pieces for the driver are picked all over the place after researching and grepping through a few different vendor trees and driver submissions. We merge it under the light sensors because: - It has similarities with the Capella CM3605 light sensor and proximity driver which is there. - It is related to the GP2AP020A00F driver which is also there. This driver was tested with the Samsung GT-S7710 mobile phone which has the GP2AP002S00F proximity sensor mounted. The suspend/resume cycle will disable the interrupt from the sensor. If someone would desire to use this interrupt for wakeup, the driver will need modifications. Cc: Stephan Gerhold Cc: Minkyu Kang Cc: Paweł Chmiel Cc: Jonathan Bakker Cc: Oskar Andero Cc: Dmitry Torokhov Tested-by: Jonathan Bakker Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 + drivers/iio/light/Kconfig | 11 + drivers/iio/light/Makefile | 1 + drivers/iio/light/gp2ap002.c | 784 +++++++++++++++++++++++++++++++++++ 4 files changed, 804 insertions(+) create mode 100644 drivers/iio/light/gp2ap002.c diff --git a/MAINTAINERS b/MAINTAINERS index 9a4c715d1e50..d8ffbb4bf920 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15060,6 +15060,14 @@ W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: net/smc/ +SHARP GP2AP002A00F/GP2AP002S00F SENSOR DRIVER +M: Linus Walleij +L: linux-iio@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git +S: Maintained +F: drivers/iio/light/gp2ap002.c +F: Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml + SHARP RJ54N1CB0C SENSOR DRIVER M: Jacopo Mondi L: linux-media@vger.kernel.org diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 9968f982fbc7..307412c79c53 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -159,6 +159,17 @@ config IIO_CROS_EC_LIGHT_PROX To compile this driver as a module, choose M here: the module will be called cros_ec_light_prox. +config GP2AP002 + tristate "Sharp GP2AP002 Proximity/ALS sensor" + depends on I2C + select REGMAP + help + Say Y here if you have a Sharp GP2AP002 proximity/ALS combo-chip + hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called gp2ap002. + config GP2AP020A00F tristate "Sharp GP2AP020A00F Proximity/ALS sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c98d1cefb861..264a76039006 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_CM3323) += cm3323.o obj-$(CONFIG_CM3605) += cm3605.o obj-$(CONFIG_CM36651) += cm36651.o obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o +obj-$(CONFIG_GP2AP002) += gp2ap002.o obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c new file mode 100644 index 000000000000..433907619268 --- /dev/null +++ b/drivers/iio/light/gp2ap002.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * These are the two Sharp GP2AP002 variants supported by this driver: + * GP2AP002A00F Ambient Light and Proximity Sensor + * GP2AP002S00F Proximity Sensor + * + * Copyright (C) 2020 Linaro Ltd. + * Author: Linus Walleij + * + * Based partly on the code in Sony Ericssons GP2AP00200F driver by + * Courtney Cavin and Oskar Andero in drivers/input/misc/gp2ap002a00f.c + * Based partly on a Samsung misc driver submitted by + * Donggeun Kim & Minkyu Kang in 2011: + * https://lore.kernel.org/lkml/1315556546-7445-1-git-send-email-dg77.kim@samsung.com/ + * Based partly on a submission by + * Jonathan Bakker and Paweł Chmiel in january 2019: + * https://lore.kernel.org/linux-input/20190125175045.22576-1-pawel.mikolaj.chmiel@gmail.com/ + * Based partly on code from the Samsung GT-S7710 by + * Based partly on the code in LG Electronics GP2AP00200F driver by + * Kenobi Lee and EunYoung Cho + */ +#include +#include +#include +#include +#include +#include +#include /* To get our ADC channel */ +#include /* To deal with our ADC channel */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define GP2AP002_PROX_CHANNEL 0 +#define GP2AP002_ALS_CHANNEL 1 + +/* ------------------------------------------------------------------------ */ +/* ADDRESS SYMBOL DATA Init R/W */ +/* D7 D6 D5 D4 D3 D2 D1 D0 */ +/* ------------------------------------------------------------------------ */ +/* 0 PROX X X X X X X X VO H'00 R */ +/* 1 GAIN X X X X LED0 X X X H'00 W */ +/* 2 HYS HYSD HYSC1 HYSC0 X HYSF3 HYSF2 HYSF1 HYSF0 H'00 W */ +/* 3 CYCLE X X CYCL2 CYCL1 CYCL0 OSC2 X X H'00 W */ +/* 4 OPMOD X X X ASD X X VCON SSD H'00 W */ +/* 6 CON X X X OCON1 OCON0 X X X H'00 W */ +/* ------------------------------------------------------------------------ */ +/* VO :Proximity sensing result(0: no detection, 1: detection) */ +/* LED0 :Select switch for LED driver's On-registence(0:2x higher, 1:normal)*/ +/* HYSD/HYSF :Adjusts the receiver sensitivity */ +/* OSC :Select switch internal clocl frequency hoppling(0:effective) */ +/* CYCL :Determine the detection cycle(typically 8ms, up to 128x) */ +/* SSD :Software Shutdown function(0:shutdown, 1:operating) */ +/* VCON :VOUT output method control(0:normal, 1:interrupt) */ +/* ASD :Select switch for analog sleep function(0:ineffective, 1:effective)*/ +/* OCON :Select switch for enabling/disabling VOUT (00:enable, 11:disable) */ + +#define GP2AP002_PROX 0x00 +#define GP2AP002_GAIN 0x01 +#define GP2AP002_HYS 0x02 +#define GP2AP002_CYCLE 0x03 +#define GP2AP002_OPMOD 0x04 +#define GP2AP002_CON 0x06 + +#define GP2AP002_PROX_VO_DETECT BIT(0) + +/* Setting this bit to 0 means 2x higher LED resistance */ +#define GP2AP002_GAIN_LED_NORMAL BIT(3) + +/* + * These bits adjusts the proximity sensitivity, determining characteristics + * of the detection distance and its hysteresis. + */ +#define GP2AP002_HYS_HYSD_SHIFT 7 +#define GP2AP002_HYS_HYSD_MASK BIT(7) +#define GP2AP002_HYS_HYSC_SHIFT 5 +#define GP2AP002_HYS_HYSC_MASK GENMASK(6, 5) +#define GP2AP002_HYS_HYSF_SHIFT 0 +#define GP2AP002_HYS_HYSF_MASK GENMASK(3, 0) +#define GP2AP002_HYS_MASK (GP2AP002_HYS_HYSD_MASK | \ + GP2AP002_HYS_HYSC_MASK | \ + GP2AP002_HYS_HYSF_MASK) + +/* + * These values determine the detection cycle response time + * 0: 8ms, 1: 16ms, 2: 32ms, 3: 64ms, 4: 128ms, + * 5: 256ms, 6: 512ms, 7: 1024ms + */ +#define GP2AP002_CYCLE_CYCL_SHIFT 3 +#define GP2AP002_CYCLE_CYCL_MASK GENMASK(5, 3) + +/* + * Select switch for internal clock frequency hopping + * 0: effective, + * 1: ineffective + */ +#define GP2AP002_CYCLE_OSC_EFFECTIVE 0 +#define GP2AP002_CYCLE_OSC_INEFFECTIVE BIT(2) +#define GP2AP002_CYCLE_OSC_MASK BIT(2) + +/* Analog sleep effective */ +#define GP2AP002_OPMOD_ASD BIT(4) +/* Enable chip */ +#define GP2AP002_OPMOD_SSD_OPERATING BIT(0) +/* IRQ mode */ +#define GP2AP002_OPMOD_VCON_IRQ BIT(1) +#define GP2AP002_OPMOD_MASK (BIT(0) | BIT(1) | BIT(4)) + +/* + * Select switch for enabling/disabling Vout pin + * 0: enable + * 2: force to go Low + * 3: force to go High + */ +#define GP2AP002_CON_OCON_SHIFT 3 +#define GP2AP002_CON_OCON_ENABLE (0x0 << GP2AP002_CON_OCON_SHIFT) +#define GP2AP002_CON_OCON_LOW (0x2 << GP2AP002_CON_OCON_SHIFT) +#define GP2AP002_CON_OCON_HIGH (0x3 << GP2AP002_CON_OCON_SHIFT) +#define GP2AP002_CON_OCON_MASK (0x3 << GP2AP002_CON_OCON_SHIFT) + +/** + * struct gp2ap002 - GP2AP002 state + * @map: regmap pointer for the i2c regmap + * @dev: pointer to parent device + * @vdd: regulator controlling VDD + * @vio: regulator controlling VIO + * @alsout: IIO ADC channel to convert the ALSOUT signal + * @hys_far: hysteresis control from device tree + * @hys_close: hysteresis control from device tree + * @is_gp2ap002s00f: this is the GP2AP002F variant of the chip + * @irq: the IRQ line used by this device + * @enabled: we cannot read the status of the hardware so we need to + * keep track of whether the event is enabled using this state variable + */ +struct gp2ap002 { + struct regmap *map; + struct device *dev; + struct regulator *vdd; + struct regulator *vio; + struct iio_channel *alsout; + u8 hys_far; + u8 hys_close; + bool is_gp2ap002s00f; + int irq; + bool enabled; +}; + +static irqreturn_t gp2ap002_prox_irq(int irq, void *d) +{ + struct iio_dev *indio_dev = d; + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + u64 ev; + int val; + int ret; + + ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val); + if (ret) { + dev_err(gp2ap002->dev, "error reading proximity\n"); + goto err_retrig; + } + + if (val & GP2AP002_PROX_VO_DETECT) { + /* Close */ + dev_dbg(gp2ap002->dev, "close\n"); + ret = regmap_write(gp2ap002->map, GP2AP002_HYS, + gp2ap002->hys_far); + if (ret) + dev_err(gp2ap002->dev, + "error setting up proximity hysteresis\n"); + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING); + } else { + /* Far */ + dev_dbg(gp2ap002->dev, "far\n"); + ret = regmap_write(gp2ap002->map, GP2AP002_HYS, + gp2ap002->hys_close); + if (ret) + dev_err(gp2ap002->dev, + "error setting up proximity hysteresis\n"); + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING); + } + iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); + + /* + * After changing hysteresis, we need to wait for one detection + * cycle to see if anything changed, or we will just trigger the + * previous interrupt again. A detection cycle depends on the CYCLE + * register, we are hard-coding ~8 ms in probe() so wait some more + * than this, 20-30 ms. + */ + usleep_range(20000, 30000); + +err_retrig: + ret = regmap_write(gp2ap002->map, GP2AP002_CON, + GP2AP002_CON_OCON_ENABLE); + if (ret) + dev_err(gp2ap002->dev, "error setting up VOUT control\n"); + + return IRQ_HANDLED; +} + +struct gp2ap002_illuminance { + unsigned int curr; + unsigned int lux; +}; + +/* + * This array maps current and lux. + * + * Ambient light sensing range is 3 to 55000 lux. + * + * This mapping is based on the following formula. + * illuminance = 10 ^ (current / 10) + */ +static const struct gp2ap002_illuminance gp2ap002_illuminance_table[] = { + { .curr = 5, .lux = 3 }, + { .curr = 6, .lux = 4 }, + { .curr = 7, .lux = 5 }, + { .curr = 8, .lux = 6 }, + { .curr = 9, .lux = 8 }, + { .curr = 10, .lux = 10 }, + { .curr = 11, .lux = 12 }, + { .curr = 12, .lux = 16 }, + { .curr = 13, .lux = 20 }, + { .curr = 14, .lux = 25 }, + { .curr = 15, .lux = 32 }, + { .curr = 16, .lux = 40 }, + { .curr = 17, .lux = 50 }, + { .curr = 18, .lux = 63 }, + { .curr = 19, .lux = 79 }, + { .curr = 20, .lux = 100 }, + { .curr = 21, .lux = 126 }, + { .curr = 22, .lux = 158 }, + { .curr = 23, .lux = 200 }, + { .curr = 24, .lux = 251 }, + { .curr = 25, .lux = 316 }, + { .curr = 26, .lux = 398 }, + { .curr = 27, .lux = 501 }, + { .curr = 28, .lux = 631 }, + { .curr = 29, .lux = 794 }, + { .curr = 30, .lux = 1000 }, + { .curr = 31, .lux = 1259 }, + { .curr = 32, .lux = 1585 }, + { .curr = 33, .lux = 1995 }, + { .curr = 34, .lux = 2512 }, + { .curr = 35, .lux = 3162 }, + { .curr = 36, .lux = 3981 }, + { .curr = 37, .lux = 5012 }, + { .curr = 38, .lux = 6310 }, + { .curr = 39, .lux = 7943 }, + { .curr = 40, .lux = 10000 }, + { .curr = 41, .lux = 12589 }, + { .curr = 42, .lux = 15849 }, + { .curr = 43, .lux = 19953 }, + { .curr = 44, .lux = 25119 }, + { .curr = 45, .lux = 31623 }, + { .curr = 46, .lux = 39811 }, + { .curr = 47, .lux = 50119 }, +}; + +static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002) +{ + const struct gp2ap002_illuminance *ill1; + const struct gp2ap002_illuminance *ill2; + int ret, res; + int i; + + ret = iio_read_channel_processed(gp2ap002->alsout, &res); + if (ret < 0) + return ret; + + dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res); + + ill1 = &gp2ap002_illuminance_table[0]; + if (res < ill1->curr) { + dev_dbg(gp2ap002->dev, "total darkness\n"); + return 0; + } + for (i = 0; i < ARRAY_SIZE(gp2ap002_illuminance_table) - 1; i++) { + ill1 = &gp2ap002_illuminance_table[i]; + ill2 = &gp2ap002_illuminance_table[i + 1]; + + if (res > ill2->curr) + continue; + if ((res <= ill1->curr) && (res >= ill2->curr)) + break; + } + if (res > ill2->curr) { + dev_info_once(gp2ap002->dev, "max current overflow\n"); + return ill2->curr; + } + /* Interpolate and return */ + dev_dbg(gp2ap002->dev, "interpolate index %d and %d\n", i, i + 1); + /* How many steps along the curve */ + i = res - ill1->curr; /* x - x0 */ + /* Linear interpolation */ + return ill1->lux + i * + ((ill2->lux - ill1->lux) / (ill2->curr - ill1->curr)); +} + +static int gp2ap002_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + ret = gp2ap002_get_lux(gp2ap002); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int gp2ap002_init(struct gp2ap002 *gp2ap002) +{ + int ret; + + /* Set up the IR LED resistance */ + ret = regmap_write(gp2ap002->map, GP2AP002_GAIN, + GP2AP002_GAIN_LED_NORMAL); + if (ret) { + dev_err(gp2ap002->dev, "error setting up LED gain\n"); + return ret; + } + ret = regmap_write(gp2ap002->map, GP2AP002_HYS, gp2ap002->hys_far); + if (ret) { + dev_err(gp2ap002->dev, + "error setting up proximity hysteresis\n"); + return ret; + } + + /* Disable internal frequency hopping */ + ret = regmap_write(gp2ap002->map, GP2AP002_CYCLE, + GP2AP002_CYCLE_OSC_INEFFECTIVE); + if (ret) { + dev_err(gp2ap002->dev, + "error setting up internal frequency hopping\n"); + return ret; + } + + /* Enable chip and IRQ, disable analog sleep */ + ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, + GP2AP002_OPMOD_SSD_OPERATING | + GP2AP002_OPMOD_VCON_IRQ); + if (ret) { + dev_err(gp2ap002->dev, "error setting up operation mode\n"); + return ret; + } + + /* Interrupt on VOUT enabled */ + ret = regmap_write(gp2ap002->map, GP2AP002_CON, + GP2AP002_CON_OCON_ENABLE); + if (ret) + dev_err(gp2ap002->dev, "error setting up VOUT control\n"); + + return ret; +} + +static int gp2ap002_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + + /* + * We just keep track of this internally, as it is not possible to + * query the hardware. + */ + return gp2ap002->enabled; +} + +static int gp2ap002_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + + if (state) { + /* + * This will bring the regulators up (unless they are on + * already) and reintialize the sensor by using runtime_pm + * callbacks. + */ + pm_runtime_get_sync(gp2ap002->dev); + gp2ap002->enabled = true; + } else { + pm_runtime_mark_last_busy(gp2ap002->dev); + pm_runtime_put_autosuspend(gp2ap002->dev); + gp2ap002->enabled = false; + } + + return 0; +} + +static const struct iio_info gp2ap002_info = { + .read_raw = gp2ap002_read_raw, + .read_event_config = gp2ap002_read_event_config, + .write_event_config = gp2ap002_write_event_config, +}; + +static const struct iio_event_spec gp2ap002_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec gp2ap002_channels[] = { + { + .type = IIO_PROXIMITY, + .event_spec = gp2ap002_events, + .num_event_specs = ARRAY_SIZE(gp2ap002_events), + }, + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .channel = GP2AP002_ALS_CHANNEL, + }, +}; + +/* + * We need a special regmap because this hardware expects to + * write single bytes to registers but read a 16bit word on some + * variants and discard the lower 8 bits so combine + * i2c_smbus_read_word_data() with i2c_smbus_write_byte_data() + * selectively like this. + */ +static int gp2ap002_regmap_i2c_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_read_word_data(i2c, reg); + if (ret < 0) + return ret; + + *val = (ret >> 8) & 0xFF; + + return 0; +} + +static int gp2ap002_regmap_i2c_write(void *context, unsigned int reg, + unsigned int val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(i2c, reg, val); +} + +static struct regmap_bus gp2ap002_regmap_bus = { + .reg_read = gp2ap002_regmap_i2c_read, + .reg_write = gp2ap002_regmap_i2c_write, +}; + +static int gp2ap002_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct gp2ap002 *gp2ap002; + struct iio_dev *indio_dev; + struct device *dev = &client->dev; + enum iio_chan_type ch_type; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = GP2AP002_CON, + }; + struct regmap *regmap; + int num_chan; + const char *compat; + u8 val; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002)); + if (!indio_dev) + return -ENOMEM; + i2c_set_clientdata(client, indio_dev); + + gp2ap002 = iio_priv(indio_dev); + gp2ap002->dev = dev; + + /* + * Check the device compatible like this makes it possible to use + * ACPI PRP0001 for registering the sensor using device tree + * properties. + */ + ret = device_property_read_string(dev, "compatible", &compat); + if (ret) { + dev_err(dev, "cannot check compatible\n"); + return ret; + } + gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f"); + + regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + gp2ap002->map = regmap; + + /* + * The hysteresis settings are coded into the device tree as values + * to be written into the hysteresis register. The datasheet defines + * modes "A", "B1" and "B2" with fixed values to be use but vendor + * code trees for actual devices are tweaking these values and refer to + * modes named things like "B1.5". To be able to support any devices, + * we allow passing an arbitrary hysteresis setting for "near" and + * "far". + */ + + /* Check the device tree for the IR LED hysteresis */ + ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis", + &val); + if (ret) { + dev_err(dev, "failed to obtain proximity far setting\n"); + return ret; + } + dev_dbg(dev, "proximity far setting %02x\n", val); + gp2ap002->hys_far = val; + + ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis", + &val); + if (ret) { + dev_err(dev, "failed to obtain proximity close setting\n"); + return ret; + } + dev_dbg(dev, "proximity close setting %02x\n", val); + gp2ap002->hys_close = val; + + /* The GP2AP002A00F has a light sensor too */ + if (!gp2ap002->is_gp2ap002s00f) { + gp2ap002->alsout = devm_iio_channel_get(dev, "alsout"); + if (IS_ERR(gp2ap002->alsout)) { + if (PTR_ERR(gp2ap002->alsout) == -ENODEV) { + dev_err(dev, "no ADC, deferring...\n"); + return -EPROBE_DEFER; + } + dev_err(dev, "failed to get ALSOUT ADC channel\n"); + return PTR_ERR(gp2ap002->alsout); + } + ret = iio_get_channel_type(gp2ap002->alsout, &ch_type); + if (ret < 0) + return ret; + if (ch_type != IIO_CURRENT) { + dev_err(dev, + "wrong type of IIO channel specified for ALSOUT\n"); + return -EINVAL; + } + } + + gp2ap002->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(gp2ap002->vdd)) { + dev_err(dev, "failed to get VDD regulator\n"); + return PTR_ERR(gp2ap002->vdd); + } + gp2ap002->vio = devm_regulator_get(dev, "vio"); + if (IS_ERR(gp2ap002->vio)) { + dev_err(dev, "failed to get VIO regulator\n"); + return PTR_ERR(gp2ap002->vio); + } + + /* Operating voltage 2.4V .. 3.6V according to datasheet */ + ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000); + if (ret) { + dev_err(dev, "failed to sett VDD voltage\n"); + return ret; + } + + /* VIO should be between 1.65V and VDD */ + ret = regulator_get_voltage(gp2ap002->vdd); + if (ret < 0) { + dev_err(dev, "failed to get VDD voltage\n"); + return ret; + } + ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret); + if (ret) { + dev_err(dev, "failed to set VIO voltage\n"); + return ret; + } + + ret = regulator_enable(gp2ap002->vdd); + if (ret) { + dev_err(dev, "failed to enable VDD regulator\n"); + return ret; + } + ret = regulator_enable(gp2ap002->vio); + if (ret) { + dev_err(dev, "failed to enable VIO regulator\n"); + goto out_disable_vdd; + } + + msleep(20); + + /* + * Initialize the device and signal to runtime PM that now we are + * definately up and using power. + */ + ret = gp2ap002_init(gp2ap002); + if (ret) { + dev_err(dev, "initialization failed\n"); + goto out_disable_vio; + } + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + gp2ap002->enabled = false; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + gp2ap002_prox_irq, IRQF_ONESHOT, + "gp2ap002", indio_dev); + if (ret) { + dev_err(dev, "unable to request IRQ\n"); + goto out_disable_vio; + } + gp2ap002->irq = client->irq; + + /* + * As the device takes 20 ms + regulator delay to come up with a fresh + * measurement after power-on, do not shut it down unnecessarily. + * Set autosuspend to a one second. + */ + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + indio_dev->dev.parent = dev; + indio_dev->info = &gp2ap002_info; + indio_dev->name = "gp2ap002"; + indio_dev->channels = gp2ap002_channels; + /* Skip light channel for the proximity-only sensor */ + num_chan = ARRAY_SIZE(gp2ap002_channels); + if (gp2ap002->is_gp2ap002s00f) + num_chan--; + indio_dev->num_channels = num_chan; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(indio_dev); + if (ret) + goto out_disable_pm; + dev_dbg(dev, "Sharp GP2AP002 probed successfully\n"); + + return 0; + +out_disable_pm: + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); +out_disable_vio: + regulator_disable(gp2ap002->vio); +out_disable_vdd: + regulator_disable(gp2ap002->vdd); + return ret; +} + +static int gp2ap002_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + struct device *dev = &client->dev; + + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + iio_device_unregister(indio_dev); + regulator_disable(gp2ap002->vio); + regulator_disable(gp2ap002->vdd); + + return 0; +} + +static int __maybe_unused gp2ap002_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + /* Deactivate the IRQ */ + disable_irq(gp2ap002->irq); + + /* Disable chip and IRQ, everything off */ + ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00); + if (ret) { + dev_err(gp2ap002->dev, "error setting up operation mode\n"); + return ret; + } + /* + * As these regulators may be shared, at least we are now in + * sleep even if the regulators aren't really turned off. + */ + regulator_disable(gp2ap002->vio); + regulator_disable(gp2ap002->vdd); + + return 0; +} + +static int __maybe_unused gp2ap002_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(gp2ap002->vdd); + if (ret) { + dev_err(dev, "failed to enable VDD regulator in resume path\n"); + return ret; + } + ret = regulator_enable(gp2ap002->vio); + if (ret) { + dev_err(dev, "failed to enable VIO regulator in resume path\n"); + return ret; + } + + msleep(20); + + ret = gp2ap002_init(gp2ap002); + if (ret) { + dev_err(dev, "re-initialization failed\n"); + return ret; + } + + /* Re-activate the IRQ */ + enable_irq(gp2ap002->irq); + + return 0; +} + +static const struct dev_pm_ops gp2ap002_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(gp2ap002_runtime_suspend, + gp2ap002_runtime_resume, NULL) +}; + +static const struct i2c_device_id gp2ap002_id_table[] = { + { "gp2ap002", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, gp2ap002_id_table); + +static const struct of_device_id gp2ap002_of_match[] = { + { .compatible = "sharp,gp2ap002a00f" }, + { .compatible = "sharp,gp2ap002s00f" }, + { }, +}; +MODULE_DEVICE_TABLE(of, gp2ap002_of_match); + +static struct i2c_driver gp2ap002_driver = { + .driver = { + .name = "gp2ap002", + .of_match_table = gp2ap002_of_match, + .pm = &gp2ap002_dev_pm_ops, + }, + .probe = gp2ap002_probe, + .remove = gp2ap002_remove, + .id_table = gp2ap002_id_table, +}; +module_i2c_driver(gp2ap002_driver); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver"); +MODULE_LICENSE("GPL v2"); From ebf7a1126717a19fb638806d37dbdf27124a272d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 23 Jan 2020 09:19:54 +0000 Subject: [PATCH 06/98] iio: ad5755: fix spelling mistake "to" -> "too" and grammar plus formatting There is a spelling mistake and grammar mistake in a dev_err message. Fix it. Also include Joe Perches' additional suggestions around: * Missing newlines * Excess braces * Odd indentation * Some grammar Signed-off-by: Colin Ian King Reviewed-by: Alexandru Ardelean Suggested-by: Joe Perches # Additional cleanups etc. Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5755.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index b9175fb4c8ab..388ddd14bfd0 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -631,10 +631,9 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) } } - if (i == ARRAY_SIZE(ad5755_dcdc_freq_table)) { + if (i == ARRAY_SIZE(ad5755_dcdc_freq_table)) dev_err(dev, - "adi,dc-dc-freq out of range selecting 410kHz"); - } + "adi,dc-dc-freq out of range selecting 410kHz\n"); } pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V; @@ -645,17 +644,16 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) break; } } - if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table)) { + if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table)) dev_err(dev, - "adi,dc-dc-maxv out of range selecting 23V"); - } + "adi,dc-dc-maxv out of range selecting 23V\n"); } devnr = 0; for_each_child_of_node(np, pp) { if (devnr >= AD5755_NUM_CHANNELS) { dev_err(dev, - "There is to many channels defined in DT\n"); + "There are too many channels defined in DT\n"); goto error_out; } @@ -681,11 +679,10 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) break; } } - if (i == ARRAY_SIZE(ad5755_slew_rate_table)) { + if (i == ARRAY_SIZE(ad5755_slew_rate_table)) dev_err(dev, - "channel %d slew rate out of range selecting 64kHz", + "channel %d slew rate out of range selecting 64kHz\n", devnr); - } pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1; for (i = 0; i < ARRAY_SIZE(ad5755_slew_step_table); i++) { @@ -695,11 +692,10 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) break; } } - if (i == ARRAY_SIZE(ad5755_slew_step_table)) { + if (i == ARRAY_SIZE(ad5755_slew_step_table)) dev_err(dev, - "channel %d slew step size out of range selecting 1 LSB", + "channel %d slew step size out of range selecting 1 LSB\n", devnr); - } } else { pdata->dac[devnr].slew.enable = false; pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; From cef2760954cf4f8228d14af9bd0b77a021979278 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 22 Jan 2020 10:54:14 +0200 Subject: [PATCH 07/98] iio: adc: ad7124: add 3db filter This patch adds the LOW_PASS_FILTER_3DB_FREQUENCY attribute in iio_chan_spec for each channel. The used filters are sinc3 or sinc4. The filter type with the highest output data rate is used when setting a low pass frequency in the channel's sysfs. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 69 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index d9915dc71d1e..f0206d819fda 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -70,6 +70,11 @@ /* AD7124_FILTER_X */ #define AD7124_FILTER_FS_MSK GENMASK(10, 0) #define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x) +#define AD7124_FILTER_TYPE_MSK GENMASK(23, 21) +#define AD7124_FILTER_TYPE_SEL(x) FIELD_PREP(AD7124_FILTER_TYPE_MSK, x) + +#define AD7124_SINC3_FILTER 2 +#define AD7124_SINC4_FILTER 0 enum ad7124_ids { ID_AD7124_4, @@ -119,6 +124,7 @@ struct ad7124_channel_config { unsigned int vref_mv; unsigned int pga_bits; unsigned int odr; + unsigned int filter_type; }; struct ad7124_state { @@ -138,7 +144,8 @@ static const struct iio_chan_spec ad7124_channel_template = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .scan_type = { .sign = 'u', .realbits = 24, @@ -281,6 +288,58 @@ static int ad7124_set_channel_gain(struct ad7124_state *st, return 0; } +static int ad7124_get_3db_filter_freq(struct ad7124_state *st, + unsigned int channel) +{ + unsigned int fadc; + + fadc = st->channel_config[channel].odr; + + switch (st->channel_config[channel].filter_type) { + case AD7124_SINC3_FILTER: + return DIV_ROUND_CLOSEST(fadc * 230, 1000); + case AD7124_SINC4_FILTER: + return DIV_ROUND_CLOSEST(fadc * 262, 1000); + default: + return -EINVAL; + } +} + +static int ad7124_set_3db_filter_freq(struct ad7124_state *st, + unsigned int channel, + unsigned int freq) +{ + unsigned int sinc4_3db_odr; + unsigned int sinc3_3db_odr; + unsigned int new_filter; + unsigned int new_odr; + + sinc4_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 230); + sinc3_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 262); + + if (sinc4_3db_odr > sinc3_3db_odr) { + new_filter = AD7124_SINC3_FILTER; + new_odr = sinc4_3db_odr; + } else { + new_filter = AD7124_SINC4_FILTER; + new_odr = sinc3_3db_odr; + } + + if (st->channel_config[channel].filter_type != new_filter) { + int ret; + + st->channel_config[channel].filter_type = new_filter; + ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), + AD7124_FILTER_TYPE_MSK, + AD7124_FILTER_TYPE_SEL(new_filter), + 3); + if (ret < 0) + return ret; + } + + return ad7124_set_channel_odr(st, channel, new_odr); +} + static int ad7124_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -322,6 +381,9 @@ static int ad7124_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: *val = st->channel_config[chan->address].odr; + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = ad7124_get_3db_filter_freq(st, chan->scan_index); return IIO_VAL_INT; default: return -EINVAL; @@ -355,6 +417,11 @@ static int ad7124_write_raw(struct iio_dev *indio_dev, gain = DIV_ROUND_CLOSEST(res, val2); return ad7124_set_channel_gain(st, chan->address, gain); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + if (val2 != 0) + return -EINVAL; + + return ad7124_set_3db_filter_freq(st, chan->address, val); default: return -EINVAL; } From 9960c70949d4356abed8747a20637e0946bb0bad Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 21 Jan 2020 16:17:57 +0000 Subject: [PATCH 08/98] iio: st_sensors: handle memory allocation failure to fix null pointer dereference A null pointer deference on pdata can occur if the allocation of pdata fails. Fix this by adding a null pointer check and handle the -ENOMEM failure in the caller. Addresses-Coverity: ("Dereference null return value") Fixes: 3ce85cc4fbb7 ("iio: st_sensors: get platform data from device tree") Signed-off-by: Colin Ian King Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index e051edbc43c1..0e35ff06f9af 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -328,6 +328,8 @@ static struct st_sensors_platform_data *st_sensors_dev_probe(struct device *dev, return NULL; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); if (!device_property_read_u32(dev, "st,drdy-int-pin", &val) && (val <= 2)) pdata->drdy_int_pin = (u8) val; else @@ -371,6 +373,8 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, /* If OF/DT pdata exists, it will take precedence of anything else */ of_pdata = st_sensors_dev_probe(indio_dev->dev.parent, pdata); + if (IS_ERR(of_pdata)) + return PTR_ERR(of_pdata); if (of_pdata) pdata = of_pdata; From 66053d5d1b74be17b133775a4373888ba227e902 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Mon, 20 Jan 2020 17:44:55 +0100 Subject: [PATCH 09/98] dt-bindings: devantech-srf04.yaml: add pm feature Add GPIO line and startup time for usage of power management Reviewed-by: Rob Herring Signed-off-by: Andreas Klinger Signed-off-by: Jonathan Cameron --- .../iio/proximity/devantech-srf04.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml index 4e80ea7c1475..8afbac24c34e 100644 --- a/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml @@ -51,6 +51,24 @@ properties: the time between two interrupts is measured in the driver. maxItems: 1 + power-gpios: + description: + Definition of the GPIO for power management of connected peripheral + (output). + This GPIO can be used by the external hardware for power management. + When the device gets suspended it's switched off and when it resumes + it's switched on again. After some period of inactivity the driver + get suspended automatically (autosuspend feature). + maxItems: 1 + + startup-time-ms: + description: + This is the startup time the device needs after a resume to be up and + running. + minimum: 0 + maximum: 1000 + default: 100 + required: - compatible - trig-gpios From 2251157b335b46bb3ea36994301f906e32e2d2b0 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Mon, 20 Jan 2020 17:45:41 +0100 Subject: [PATCH 10/98] iio: srf04: add power management feature Add suspend and resume operations for being used by optional power management. The suspend function is switching off an GPIO which can be used by the hardware to switch power off. The resume function is switching the GPIO on and sleeps an adjustable time to give the device a chance to be up and running. If activated the driver gets into autosuspend after some time of inactivity. Suggested-by: Franz Parzer Signed-off-by: Andreas Klinger Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/srf04.c | 96 ++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 01eb8cc63076..568b76e06385 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ struct srf04_data { struct device *dev; struct gpio_desc *gpiod_trig; struct gpio_desc *gpiod_echo; + struct gpio_desc *gpiod_power; struct mutex lock; int irqnr; ktime_t ts_rising; @@ -63,6 +65,7 @@ struct srf04_data { struct completion rising; struct completion falling; const struct srf04_cfg *cfg; + int startup_time_ms; }; static const struct srf04_cfg srf04_cfg = { @@ -97,6 +100,9 @@ static int srf04_read(struct srf04_data *data) u64 dt_ns; u32 time_ns, distance_mm; + if (data->gpiod_power) + pm_runtime_get_sync(data->dev); + /* * just one read-echo-cycle can take place at a time * ==> lock against concurrent reading calls @@ -110,6 +116,11 @@ static int srf04_read(struct srf04_data *data) udelay(data->cfg->trigger_pulse_us); gpiod_set_value(data->gpiod_trig, 0); + if (data->gpiod_power) { + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + } + /* it should not take more than 20 ms until echo is rising */ ret = wait_for_completion_killable_timeout(&data->rising, HZ/50); if (ret < 0) { @@ -268,6 +279,22 @@ static int srf04_probe(struct platform_device *pdev) return PTR_ERR(data->gpiod_echo); } + data->gpiod_power = devm_gpiod_get_optional(dev, "power", + GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_power)) { + dev_err(dev, "failed to get power-gpios: err=%ld\n", + PTR_ERR(data->gpiod_power)); + return PTR_ERR(data->gpiod_power); + } + if (data->gpiod_power) { + + if (of_property_read_u32(dev->of_node, "startup-time-ms", + &data->startup_time_ms)) + data->startup_time_ms = 100; + dev_dbg(dev, "using power gpio: startup-time-ms=%d\n", + data->startup_time_ms); + } + if (gpiod_cansleep(data->gpiod_echo)) { dev_err(data->dev, "cansleep-GPIOs not supported\n"); return -ENODEV; @@ -296,14 +323,81 @@ static int srf04_probe(struct platform_device *pdev) indio_dev->channels = srf04_chan_spec; indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec); - return devm_iio_device_register(dev, indio_dev); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(data->dev, "iio_device_register: %d\n", ret); + return ret; + } + + if (data->gpiod_power) { + pm_runtime_set_autosuspend_delay(data->dev, 1000); + pm_runtime_use_autosuspend(data->dev); + + ret = pm_runtime_set_active(data->dev); + if (ret) { + dev_err(data->dev, "pm_runtime_set_active: %d\n", ret); + iio_device_unregister(indio_dev); + } + + pm_runtime_enable(data->dev); + pm_runtime_idle(data->dev); + } + + return ret; } +static int srf04_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct srf04_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (data->gpiod_power) { + pm_runtime_disable(data->dev); + pm_runtime_set_suspended(data->dev); + } + + return 0; +} + +static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct srf04_data *data = iio_priv(indio_dev); + + gpiod_set_value(data->gpiod_power, 0); + + return 0; +} + +static int __maybe_unused srf04_pm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct srf04_data *data = iio_priv(indio_dev); + + gpiod_set_value(data->gpiod_power, 1); + msleep(data->startup_time_ms); + + return 0; +} + +static const struct dev_pm_ops srf04_pm_ops = { + SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend, + srf04_pm_runtime_resume, NULL) +}; + static struct platform_driver srf04_driver = { .probe = srf04_probe, + .remove = srf04_remove, .driver = { .name = "srf04-gpio", .of_match_table = of_srf04_match, + .pm = &srf04_pm_ops, }, }; From b7dec2cb19f3114c9e34431bb52ebe88db9b6363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Jan 2020 14:15:47 +0100 Subject: [PATCH 11/98] dt-bindings: iio: ltc2632: expand for ltc2636 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ltc2636 family of devices is register compatible with the ltc2636 chips, it just features 8 instead of 2 channels. Signed-off-by: Uwe Kleine-König Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/ltc2632.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/dac/ltc2632.txt b/Documentation/devicetree/bindings/iio/dac/ltc2632.txt index e0d5fea33031..338c3220f01a 100644 --- a/Documentation/devicetree/bindings/iio/dac/ltc2632.txt +++ b/Documentation/devicetree/bindings/iio/dac/ltc2632.txt @@ -1,4 +1,4 @@ -Linear Technology LTC2632 DAC device driver +Linear Technology LTC2632/2636 DAC Required properties: - compatible: Has to contain one of the following: @@ -8,6 +8,12 @@ Required properties: lltc,ltc2632-h12 lltc,ltc2632-h10 lltc,ltc2632-h8 + lltc,ltc2636-l12 + lltc,ltc2636-l10 + lltc,ltc2636-l8 + lltc,ltc2636-h12 + lltc,ltc2636-h10 + lltc,ltc2636-h8 Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt apply. In particular, "reg" and "spi-max-frequency" properties must be given. From f243d0f0bd16c281ba5cce19eae86b433257fc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Jan 2020 14:15:48 +0100 Subject: [PATCH 12/98] iio: dac: ltc2632: drop some duplicated data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The channel data for ltc2632l12 and ltc2632h12 are identical. So there is no gain in using two different (but identical) channel descriptions. The only side effect of this change is some memory savings. Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc2632.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 643d1ce956ce..7ab92e178496 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -192,37 +192,33 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { LTC2632_CHANNEL(1, _bits), \ } -static DECLARE_LTC2632_CHANNELS(ltc2632l12, 12); -static DECLARE_LTC2632_CHANNELS(ltc2632l10, 10); -static DECLARE_LTC2632_CHANNELS(ltc2632l8, 8); - -static DECLARE_LTC2632_CHANNELS(ltc2632h12, 12); -static DECLARE_LTC2632_CHANNELS(ltc2632h10, 10); -static DECLARE_LTC2632_CHANNELS(ltc2632h8, 8); +static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); +static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10); +static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { [ID_LTC2632L12] = { - .channels = ltc2632l12_channels, + .channels = ltc2632x12_channels, .vref_mv = 2500, }, [ID_LTC2632L10] = { - .channels = ltc2632l10_channels, + .channels = ltc2632x10_channels, .vref_mv = 2500, }, [ID_LTC2632L8] = { - .channels = ltc2632l8_channels, + .channels = ltc2632x8_channels, .vref_mv = 2500, }, [ID_LTC2632H12] = { - .channels = ltc2632h12_channels, + .channels = ltc2632x12_channels, .vref_mv = 4096, }, [ID_LTC2632H10] = { - .channels = ltc2632h10_channels, + .channels = ltc2632x10_channels, .vref_mv = 4096, }, [ID_LTC2632H8] = { - .channels = ltc2632h8_channels, + .channels = ltc2632x8_channels, .vref_mv = 4096, }, }; From 9f15a4a0adc910a7bd535b755185f89c3e25b37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Jan 2020 14:15:49 +0100 Subject: [PATCH 13/98] iio: dac: ltc2632: add support for LTC2636 family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only difference between LTC2632 and LTC2636 is that the former has two DAC channels while the latter has eight. Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 5 ++- drivers/iio/dac/ltc2632.c | 77 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 979070196da9..3adbd2de38a1 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -132,11 +132,12 @@ config LTC1660 module will be called ltc1660. config LTC2632 - tristate "Linear Technology LTC2632-12/10/8 DAC spi driver" + tristate "Linear Technology LTC2632-12/10/8 and LTC2636-12/10/8 DAC spi driver" depends on SPI help Say yes here to build support for Linear Technology - LTC2632-12, LTC2632-10, LTC2632-8 converters (DAC). + LTC2632-12, LTC2632-10, LTC2632-8, LTC2636-12, LTC2636-10 and + LTC2636-8 converters (DAC). To compile this driver as a module, choose M here: the module will be called ltc2632. diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 7ab92e178496..1e7ffa0f6071 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -12,8 +12,6 @@ #include #include -#define LTC2632_DAC_CHANNELS 2 - #define LTC2632_ADDR_DAC0 0x0 #define LTC2632_ADDR_DAC1 0x1 @@ -33,6 +31,7 @@ */ struct ltc2632_chip_info { const struct iio_chan_spec *channels; + const size_t num_channels; const int vref_mv; }; @@ -57,6 +56,12 @@ enum ltc2632_supported_device_ids { ID_LTC2632H12, ID_LTC2632H10, ID_LTC2632H8, + ID_LTC2636L12, + ID_LTC2636L10, + ID_LTC2636L8, + ID_LTC2636H12, + ID_LTC2636H10, + ID_LTC2636H8, }; static int ltc2632_spi_write(struct spi_device *spi, @@ -190,6 +195,12 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { const struct iio_chan_spec _name ## _channels[] = { \ LTC2632_CHANNEL(0, _bits), \ LTC2632_CHANNEL(1, _bits), \ + LTC2632_CHANNEL(2, _bits), \ + LTC2632_CHANNEL(3, _bits), \ + LTC2632_CHANNEL(4, _bits), \ + LTC2632_CHANNEL(5, _bits), \ + LTC2632_CHANNEL(6, _bits), \ + LTC2632_CHANNEL(7, _bits), \ } static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); @@ -199,26 +210,62 @@ static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { [ID_LTC2632L12] = { .channels = ltc2632x12_channels, + .num_channels = 2, .vref_mv = 2500, }, [ID_LTC2632L10] = { .channels = ltc2632x10_channels, + .num_channels = 2, .vref_mv = 2500, }, [ID_LTC2632L8] = { .channels = ltc2632x8_channels, + .num_channels = 2, .vref_mv = 2500, }, [ID_LTC2632H12] = { .channels = ltc2632x12_channels, + .num_channels = 2, .vref_mv = 4096, }, [ID_LTC2632H10] = { .channels = ltc2632x10_channels, + .num_channels = 2, .vref_mv = 4096, }, [ID_LTC2632H8] = { .channels = ltc2632x8_channels, + .num_channels = 2, + .vref_mv = 4096, + }, + [ID_LTC2636L12] = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 2500, + }, + [ID_LTC2636L10] = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 2500, + }, + [ID_LTC2636L8] = { + .channels = ltc2632x8_channels, + .num_channels = 8, + .vref_mv = 2500, + }, + [ID_LTC2636H12] = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 4096, + }, + [ID_LTC2636H10] = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 4096, + }, + [ID_LTC2636H8] = { + .channels = ltc2632x8_channels, + .num_channels = 8, .vref_mv = 4096, }, }; @@ -287,7 +334,7 @@ static int ltc2632_probe(struct spi_device *spi) indio_dev->info = <c2632_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = chip_info->channels; - indio_dev->num_channels = LTC2632_DAC_CHANNELS; + indio_dev->num_channels = chip_info->num_channels; return iio_device_register(indio_dev); } @@ -312,6 +359,12 @@ static const struct spi_device_id ltc2632_id[] = { { "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] }, { "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] }, { "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] }, + { "ltc2636-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L12] }, + { "ltc2636-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L10] }, + { "ltc2636-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L8] }, + { "ltc2636-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H12] }, + { "ltc2636-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H10] }, + { "ltc2636-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H8] }, {} }; MODULE_DEVICE_TABLE(spi, ltc2632_id); @@ -335,6 +388,24 @@ static const struct of_device_id ltc2632_of_match[] = { }, { .compatible = "lltc,ltc2632-h8", .data = <c2632_chip_info_tbl[ID_LTC2632H8] + }, { + .compatible = "lltc,ltc2636-l12", + .data = <c2632_chip_info_tbl[ID_LTC2636L12] + }, { + .compatible = "lltc,ltc2636-l10", + .data = <c2632_chip_info_tbl[ID_LTC2636L10] + }, { + .compatible = "lltc,ltc2636-l8", + .data = <c2632_chip_info_tbl[ID_LTC2636L8] + }, { + .compatible = "lltc,ltc2636-h12", + .data = <c2632_chip_info_tbl[ID_LTC2636H12] + }, { + .compatible = "lltc,ltc2636-h10", + .data = <c2632_chip_info_tbl[ID_LTC2636H10] + }, { + .compatible = "lltc,ltc2636-h8", + .data = <c2632_chip_info_tbl[ID_LTC2636H8] }, {} }; From 55f82c00b2f345eed031ed189b16b1b2d8689901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 3 Feb 2020 21:31:26 +0100 Subject: [PATCH 14/98] iio: dac: ltc2632: remove some unused defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two defines are unused since the driver was introduced in commit 02b829f9e11f ("iio: dac: Add support for ltc2632 DACs"). Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc2632.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 1e7ffa0f6071..7adc91056aa1 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -12,9 +12,6 @@ #include #include -#define LTC2632_ADDR_DAC0 0x0 -#define LTC2632_ADDR_DAC1 0x1 - #define LTC2632_CMD_WRITE_INPUT_N 0x0 #define LTC2632_CMD_UPDATE_DAC_N 0x1 #define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 From 4a818643e9886c66ba65439a1cd9001c800d6fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 3 Feb 2020 10:17:01 +0100 Subject: [PATCH 15/98] iio: vcnl4000: Use a single return when getting IIO_CHAN_INFO_RAW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be useful when introducing runtime pm. Signed-off-by: Guido Günther Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index b0e241aaefb4..8f198383626b 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -335,17 +335,18 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_LIGHT: ret = data->chip_spec->measure_light(data, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; + if (!ret) + ret = IIO_VAL_INT; + break; case IIO_PROXIMITY: ret = data->chip_spec->measure_proximity(data, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; + if (!ret) + ret = IIO_VAL_INT; + break; default: - return -EINVAL; + ret = -EINVAL; } + return ret; case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) return -EINVAL; From 5e00708df3816d47d0de8b3837a33d64ff3582ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 3 Feb 2020 10:17:02 +0100 Subject: [PATCH 16/98] iio: vcnl4000: Enable runtime pm for vcnl4200/4040 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is modelled after the vcnl4035 driver. For the vcnl40{0,1,2}0 we don't do anything since they use on demand measurement. Signed-off-by: Guido Günther Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 129 +++++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 12 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 8f198383626b..38fcd9a26046 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,8 @@ #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ +#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ + enum vcnl4000_device_ids { VCNL4000, VCNL4010, @@ -87,6 +90,7 @@ struct vcnl4000_chip_spec { int (*init)(struct vcnl4000_data *data); int (*measure_light)(struct vcnl4000_data *data, int *val); int (*measure_proximity)(struct vcnl4000_data *data, int *val); + int (*set_power_state)(struct vcnl4000_data *data, bool on); }; static const struct i2c_device_id vcnl4000_id[] = { @@ -99,6 +103,12 @@ static const struct i2c_device_id vcnl4000_id[] = { }; MODULE_DEVICE_TABLE(i2c, vcnl4000_id); +static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) +{ + /* no suspend op */ + return 0; +} + static int vcnl4000_init(struct vcnl4000_data *data) { int ret, prod_id; @@ -127,9 +137,31 @@ static int vcnl4000_init(struct vcnl4000_data *data) data->al_scale = 250000; mutex_init(&data->vcnl4000_lock); - return 0; + return data->chip_spec->set_power_state(data, true); }; +static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) +{ + u16 val = on ? 0 /* power on */ : 1 /* shut down */; + int ret; + + ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); + if (ret < 0) + return ret; + + if (on) { + /* Wait at least one integration cycle before fetching data */ + data->vcnl4200_al.last_measurement = ktime_get(); + data->vcnl4200_ps.last_measurement = ktime_get(); + } + + return 0; +} + static int vcnl4200_init(struct vcnl4000_data *data) { int ret, id; @@ -155,14 +187,6 @@ static int vcnl4200_init(struct vcnl4000_data *data) data->rev = (ret >> 8) & 0xf; - /* Set defaults and enable both channels */ - ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 0); - if (ret < 0) - return ret; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 0); - if (ret < 0) - return ret; - data->vcnl4200_al.reg = VCNL4200_AL_DATA; data->vcnl4200_ps.reg = VCNL4200_PS_DATA; switch (id) { @@ -180,11 +204,13 @@ static int vcnl4200_init(struct vcnl4000_data *data) data->al_scale = 120000; break; } - data->vcnl4200_al.last_measurement = ktime_set(0, 0); - data->vcnl4200_ps.last_measurement = ktime_set(0, 0); mutex_init(&data->vcnl4200_al.lock); mutex_init(&data->vcnl4200_ps.lock); + ret = data->chip_spec->set_power_state(data, true); + if (ret < 0) + return ret; + return 0; }; @@ -291,24 +317,28 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .init = vcnl4000_init, .measure_light = vcnl4000_measure_light, .measure_proximity = vcnl4000_measure_proximity, + .set_power_state = vcnl4000_set_power_state, }, [VCNL4010] = { .prod = "VCNL4010/4020", .init = vcnl4000_init, .measure_light = vcnl4000_measure_light, .measure_proximity = vcnl4000_measure_proximity, + .set_power_state = vcnl4000_set_power_state, }, [VCNL4040] = { .prod = "VCNL4040", .init = vcnl4200_init, .measure_light = vcnl4200_measure_light, .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, }, [VCNL4200] = { .prod = "VCNL4200", .init = vcnl4200_init, .measure_light = vcnl4200_measure_light, .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, }, }; @@ -323,6 +353,23 @@ static const struct iio_chan_spec vcnl4000_channels[] = { } }; +static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) +{ + struct device *dev = &data->client->dev; + int ret; + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret; +} + static int vcnl4000_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -332,6 +379,10 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + ret = vcnl4000_set_pm_runtime_state(data, true); + if (ret < 0) + return ret; + switch (chan->type) { case IIO_LIGHT: ret = data->chip_spec->measure_light(data, val); @@ -346,6 +397,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, default: ret = -EINVAL; } + vcnl4000_set_pm_runtime_state(data, false); return ret; case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) @@ -394,7 +446,22 @@ static int vcnl4000_probe(struct i2c_client *client, indio_dev->name = VCNL4000_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - return devm_iio_device_register(&client->dev, indio_dev); + ret = pm_runtime_set_active(&client->dev); + if (ret < 0) + goto fail_poweroff; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto fail_poweroff; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + return 0; +fail_poweroff: + data->chip_spec->set_power_state(data, false); + return ret; } static const struct of_device_id vcnl_4000_of_match[] = { @@ -422,13 +489,51 @@ static const struct of_device_id vcnl_4000_of_match[] = { }; MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); +static int vcnl4000_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct vcnl4000_data *data = iio_priv(indio_dev); + + pm_runtime_dont_use_autosuspend(&client->dev); + pm_runtime_disable(&client->dev); + iio_device_unregister(indio_dev); + pm_runtime_set_suspended(&client->dev); + + return data->chip_spec->set_power_state(data, false); +} + +static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct vcnl4000_data *data = iio_priv(indio_dev); + + return data->chip_spec->set_power_state(data, false); +} + +static int __maybe_unused vcnl4000_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct vcnl4000_data *data = iio_priv(indio_dev); + + return data->chip_spec->set_power_state(data, true); +} + +static const struct dev_pm_ops vcnl4000_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend, + vcnl4000_runtime_resume, NULL) +}; + static struct i2c_driver vcnl4000_driver = { .driver = { .name = VCNL4000_DRV_NAME, + .pm = &vcnl4000_pm_ops, .of_match_table = vcnl_4000_of_match, }, .probe = vcnl4000_probe, .id_table = vcnl4000_id, + .remove = vcnl4000_remove, }; module_i2c_driver(vcnl4000_driver); From 816abe42b8bde912c58d862d540a2f57ba967477 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 3 Feb 2020 17:09:16 +0200 Subject: [PATCH 17/98] dt-binding: iio: add NPCM ADC reset support Add NPCM ADC reset binding documentation. Signed-off-by: Tomer Maimon Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt index eb939fe77836..ef8eeec1a997 100644 --- a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt @@ -6,6 +6,7 @@ Required properties: - compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC. - reg: specifies physical base address and size of the registers. - interrupts: Contain the ADC interrupt with flags for falling edge. +- resets : phandle to the reset control for this device. Optional properties: - clocks: phandle of ADC reference clock, in case the clock is not @@ -21,4 +22,5 @@ adc: adc@f000c000 { reg = <0xf000c000 0x8>; interrupts = ; clocks = <&clk NPCM7XX_CLK_ADC>; + resets = <&rstc NPCM7XX_RESET_IPSRST1 NPCM7XX_RESET_ADC>; }; From db6bcb8c25884a29f57660857c788afca20a32e1 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 3 Feb 2020 17:09:17 +0200 Subject: [PATCH 18/98] iio: adc: modify NPCM reset support Modify NPCM ADC reset support from direct register access to reset controller support. please make sure to modify NPCM adc device tree parameters as described at nuvoton,npcm-adc.txt document for using this change. Note for anyone noting that this is a breaking change, this is on a BMC and effectively in a close ecosystem so it is fine to rely on DT and kernel being updated together. Signed-off-by: Tomer Maimon Signed-off-by: Jonathan Cameron --- drivers/iio/adc/npcm_adc.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index a6170a37ebe8..83bad2d5575d 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -14,6 +14,7 @@ #include #include #include +#include struct npcm_adc { bool int_status; @@ -23,13 +24,9 @@ struct npcm_adc { struct clk *adc_clk; wait_queue_head_t wq; struct regulator *vref; - struct regmap *rst_regmap; + struct reset_control *reset; }; -/* NPCM7xx reset module */ -#define NPCM7XX_IPSRST1_OFFSET 0x020 -#define NPCM7XX_IPSRST1_ADC_RST BIT(27) - /* ADC registers */ #define NPCM_ADCCON 0x00 #define NPCM_ADCDATA 0x04 @@ -106,13 +103,11 @@ static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel) msecs_to_jiffies(10)); if (ret == 0) { regtemp = ioread32(info->regs + NPCM_ADCCON); - if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) { + if (regtemp & NPCM_ADCCON_ADC_CONV) { /* if conversion failed - reset ADC module */ - regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, - NPCM7XX_IPSRST1_ADC_RST); + reset_control_assert(info->reset); msleep(100); - regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, - 0x0); + reset_control_deassert(info->reset); msleep(100); /* Enable ADC and start conversion module */ @@ -186,7 +181,6 @@ static int npcm_adc_probe(struct platform_device *pdev) struct npcm_adc *info; struct iio_dev *indio_dev; struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); if (!indio_dev) @@ -199,6 +193,10 @@ static int npcm_adc_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + info->reset = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(info->reset)) + return PTR_ERR(info->reset); + info->adc_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(info->adc_clk)) { dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); @@ -211,16 +209,6 @@ static int npcm_adc_probe(struct platform_device *pdev) div = div >> NPCM_ADCCON_DIV_SHIFT; info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); - if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) { - info->rst_regmap = syscon_regmap_lookup_by_compatible - ("nuvoton,npcm750-rst"); - if (IS_ERR(info->rst_regmap)) { - dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n"); - ret = PTR_ERR(info->rst_regmap); - goto err_disable_clk; - } - } - irq = platform_get_irq(pdev, 0); if (irq <= 0) { ret = -EINVAL; From 4235cc30c906a4ddc59e9aa3db2e06d3faffcb3a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:00 +0100 Subject: [PATCH 19/98] iio: imu: inv_mpu6050: cleanup of/acpi support Use device_get_match_data for using of and acpi tables. In spi support add missing mpu6515 and of match table. Reorganize Kconfig to display chips grouped by generations. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 8 ++--- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 26 +++----------- drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 44 +++++++++++++++++++++-- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 017bc0fcc365..003134734646 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -15,8 +15,8 @@ config INV_MPU6050_I2C select INV_MPU6050_IIO select REGMAP_I2C help - This driver supports the Invensense MPU6050/6500/6515, - MPU9150/9250/9255 and ICM20608/20602 motion tracking devices + This driver supports the Invensense MPU6050/9150, + MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -27,8 +27,8 @@ config INV_MPU6050_SPI select INV_MPU6050_IIO select REGMAP_SPI help - This driver supports the Invensense MPU6000/6500/6515, - MPU9250/9255 and ICM20608/20602 motion tracking devices + This driver supports the Invensense MPU6000, + MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index f47a28b4be23..49e9aa777ba4 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "inv_mpu_iio.h" static const struct regmap_config inv_mpu_regmap_config = { @@ -54,20 +55,6 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) return 0; } -static const char *inv_mpu_match_acpi_device(struct device *dev, - enum inv_devices *chip_id) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - *chip_id = (int)id->driver_data; - - return dev_name(dev); -} - static bool inv_mpu_i2c_aux_bus(struct device *dev) { struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); @@ -130,6 +117,7 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev) static int inv_mpu_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const void *match; struct inv_mpu6050_state *st; int result; enum inv_devices chip_type; @@ -140,18 +128,14 @@ static int inv_mpu_probe(struct i2c_client *client, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EOPNOTSUPP; - if (client->dev.of_node) { - chip_type = (enum inv_devices) - of_device_get_match_data(&client->dev); + match = device_get_match_data(&client->dev); + if (match) { + chip_type = (enum inv_devices)match; name = client->name; } else if (id) { chip_type = (enum inv_devices) id->driver_data; name = id->name; - } else if (ACPI_HANDLE(&client->dev)) { - name = inv_mpu_match_acpi_device(&client->dev, &chip_type); - if (!name) - return -ENODEV; } else { return -ENOSYS; } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index ec102d5a5c77..834bfa8e8684 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -4,6 +4,8 @@ */ #include #include +#include +#include #include #include #include @@ -41,17 +43,18 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) static int inv_mpu_probe(struct spi_device *spi) { + const void *match; struct regmap *regmap; const struct spi_device_id *spi_id; - const struct acpi_device_id *acpi_id; const char *name = NULL; enum inv_devices chip_type; if ((spi_id = spi_get_device_id(spi))) { chip_type = (enum inv_devices)spi_id->driver_data; name = spi_id->name; - } else if ((acpi_id = acpi_match_device(spi->dev.driver->acpi_match_table, &spi->dev))) { - chip_type = (enum inv_devices)acpi_id->driver_data; + } else if ((match = device_get_match_data(&spi->dev))) { + chip_type = (enum inv_devices)match; + name = dev_name(&spi->dev); } else { return -ENODEV; } @@ -74,6 +77,7 @@ static int inv_mpu_probe(struct spi_device *spi) static const struct spi_device_id inv_mpu_id[] = { {"mpu6000", INV_MPU6000}, {"mpu6500", INV_MPU6500}, + {"mpu6515", INV_MPU6515}, {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, @@ -83,6 +87,39 @@ static const struct spi_device_id inv_mpu_id[] = { MODULE_DEVICE_TABLE(spi, inv_mpu_id); +static const struct of_device_id inv_of_match[] = { + { + .compatible = "invensense,mpu6000", + .data = (void *)INV_MPU6000 + }, + { + .compatible = "invensense,mpu6500", + .data = (void *)INV_MPU6500 + }, + { + .compatible = "invensense,mpu6515", + .data = (void *)INV_MPU6515 + }, + { + .compatible = "invensense,mpu9250", + .data = (void *)INV_MPU9250 + }, + { + .compatible = "invensense,mpu9255", + .data = (void *)INV_MPU9255 + }, + { + .compatible = "invensense,icm20608", + .data = (void *)INV_ICM20608 + }, + { + .compatible = "invensense,icm20602", + .data = (void *)INV_ICM20602 + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_of_match); + static const struct acpi_device_id inv_acpi_match[] = { {"INVN6000", INV_MPU6000}, { }, @@ -93,6 +130,7 @@ static struct spi_driver inv_mpu_driver = { .probe = inv_mpu_probe, .id_table = inv_mpu_id, .driver = { + .of_match_table = inv_of_match, .acpi_match_table = ACPI_PTR(inv_acpi_match), .name = "inv-mpu6000-spi", .pm = &inv_mpu_pmops, From 23db8d6959314568b243fd2bc76bee50136afa4c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:01 +0100 Subject: [PATCH 20/98] iio: imu: inv_mpu6050: add support of ICM20609 & ICM20689 They are similar to ICM20608 chip. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 8 +++---- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 26 ++++++++++++++++++---- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 12 ++++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 5 +++++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 10 +++++++++ 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 003134734646..e933db3a5a89 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -16,8 +16,8 @@ config INV_MPU6050_I2C select REGMAP_I2C help This driver supports the Invensense MPU6050/9150, - MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices - over I2C. + MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -28,7 +28,7 @@ config INV_MPU6050_SPI select REGMAP_SPI help This driver supports the Invensense MPU6000, - MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices - over SPI. + MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 5096fc49012d..5fd57a1bee90 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -176,6 +176,22 @@ static const struct inv_mpu6050_hw hw_info[] = { .fifo_size = 512, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, + { + .whoami = INV_ICM20609_WHOAMI_VALUE, + .name = "ICM20609", + .reg = ®_set_6500, + .config = &chip_config_6050, + .fifo_size = 4 * 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, + { + .whoami = INV_ICM20689_WHOAMI_VALUE, + .name = "ICM20689", + .reg = ®_set_6500, + .config = &chip_config_6050, + .fifo_size = 4 * 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, { .whoami = INV_ICM20602_WHOAMI_VALUE, .name = "ICM20602", @@ -286,20 +302,22 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, if (result) return result; + /* set accel lpf */ switch (st->chip_type) { case INV_MPU6050: case INV_MPU6000: case INV_MPU9150: /* old chips, nothing to do */ - result = 0; + return 0; + case INV_ICM20689: + /* set FIFO size to maximum value */ + val |= INV_ICM20689_BITS_FIFO_SIZE_MAX; break; default: - /* set accel lpf */ - result = regmap_write(st->map, st->reg->accel_lpf, val); break; } - return result; + return regmap_write(st->map, st->reg->accel_lpf, val); } /** diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 49e9aa777ba4..dd054e21216a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -61,6 +61,8 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) switch (st->chip_type) { case INV_ICM20608: + case INV_ICM20609: + case INV_ICM20689: case INV_ICM20602: /* no i2c auxiliary bus on the chip */ return false; @@ -202,6 +204,8 @@ static const struct i2c_device_id inv_mpu_id[] = { {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, + {"icm20609", INV_ICM20609}, + {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, {} }; @@ -237,6 +241,14 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 }, + { + .compatible = "invensense,icm20609", + .data = (void *)INV_ICM20609 + }, + { + .compatible = "invensense,icm20689", + .data = (void *)INV_ICM20689 + }, { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 6158fca7f70e..75b8903450eb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -75,6 +75,8 @@ enum inv_devices { INV_MPU9250, INV_MPU9255, INV_ICM20608, + INV_ICM20609, + INV_ICM20689, INV_ICM20602, INV_NUM_PARTS }; @@ -276,6 +278,7 @@ struct inv_mpu6050_state { /* mpu6500 registers */ #define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D +#define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0 #define INV_MPU6500_REG_ACCEL_OFFSET 0x77 /* delay time in milliseconds */ @@ -340,6 +343,8 @@ struct inv_mpu6050_state { #define INV_MPU9255_WHOAMI_VALUE 0x73 #define INV_MPU6515_WHOAMI_VALUE 0x74 #define INV_ICM20608_WHOAMI_VALUE 0xAF +#define INV_ICM20609_WHOAMI_VALUE 0xA6 +#define INV_ICM20689_WHOAMI_VALUE 0x98 #define INV_ICM20602_WHOAMI_VALUE 0x12 /* scan element definition for generic MPU6xxx devices */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 834bfa8e8684..3803c40e7b24 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -81,6 +81,8 @@ static const struct spi_device_id inv_mpu_id[] = { {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, + {"icm20609", INV_ICM20609}, + {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, {} }; @@ -112,6 +114,14 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 }, + { + .compatible = "invensense,icm20609", + .data = (void *)INV_ICM20609 + }, + { + .compatible = "invensense,icm20689", + .data = (void *)INV_ICM20689 + }, { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 From d31f74e60804cba1d7b265708b820e524e574611 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:02 +0100 Subject: [PATCH 21/98] iio: imu: inv_mpu6050: add support of IAM20680 Automotive certified IMU, similar to ICM20608. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 4 ++-- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 8 ++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 6 ++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2 ++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 5 +++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index e933db3a5a89..e65474a64675 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -16,7 +16,7 @@ config INV_MPU6050_I2C select REGMAP_I2C help This driver supports the Invensense MPU6050/9150, - MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -28,7 +28,7 @@ config INV_MPU6050_SPI select REGMAP_SPI help This driver supports the Invensense MPU6000, - MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 5fd57a1bee90..e8e6beef3f04 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -200,6 +200,14 @@ static const struct inv_mpu6050_hw hw_info[] = { .fifo_size = 1008, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, + { + .whoami = INV_IAM20680_WHOAMI_VALUE, + .name = "IAM20680", + .reg = ®_set_6500, + .config = &chip_config_6050, + .fifo_size = 512, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, }; int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index dd054e21216a..cd0eeb5dc7f6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -64,6 +64,7 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) case INV_ICM20609: case INV_ICM20689: case INV_ICM20602: + case INV_IAM20680: /* no i2c auxiliary bus on the chip */ return false; case INV_MPU9150: @@ -207,6 +208,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"iam20680", INV_IAM20680}, {} }; @@ -253,6 +255,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,iam20680", + .data = (void *)INV_IAM20680 + }, { } }; MODULE_DEVICE_TABLE(of, inv_of_match); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 75b8903450eb..404c289f8c84 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -78,6 +78,7 @@ enum inv_devices { INV_ICM20609, INV_ICM20689, INV_ICM20602, + INV_IAM20680, INV_NUM_PARTS }; @@ -346,6 +347,7 @@ struct inv_mpu6050_state { #define INV_ICM20609_WHOAMI_VALUE 0xA6 #define INV_ICM20689_WHOAMI_VALUE 0x98 #define INV_ICM20602_WHOAMI_VALUE 0x12 +#define INV_IAM20680_WHOAMI_VALUE 0xA9 /* scan element definition for generic MPU6xxx devices */ enum inv_mpu6050_scan { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 3803c40e7b24..393cec555be6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -84,6 +84,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"iam20680", INV_IAM20680}, {} }; @@ -126,6 +127,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,iam20680", + .data = (void *)INV_IAM20680 + }, { } }; MODULE_DEVICE_TABLE(of, inv_of_match); From 8abce87d39912e57ade3c141049ffa2eea85846b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:03 +0100 Subject: [PATCH 22/98] iio: imu: inv_mpu6050: add support of ICM20690 Same generation as ICM20602 but different registers. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 8 ++--- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 36 +++++++++++++++++++--- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 5 +++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 3 ++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 5 +++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index e65474a64675..7137ea6f25db 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -16,8 +16,8 @@ config INV_MPU6050_I2C select REGMAP_I2C help This driver supports the Invensense MPU6050/9150, - MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 - motion tracking devices over I2C. + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and + IAM20680 motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -28,7 +28,7 @@ config INV_MPU6050_SPI select REGMAP_SPI help This driver supports the Invensense MPU6000, - MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 - motion tracking devices over SPI. + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and + IAM20680 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index e8e6beef3f04..9ecc667debbe 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -200,6 +200,14 @@ static const struct inv_mpu6050_hw hw_info[] = { .fifo_size = 1008, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, + { + .whoami = INV_ICM20690_WHOAMI_VALUE, + .name = "ICM20690", + .reg = ®_set_icm20602, + .config = &chip_config_6050, + .fifo_size = 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, { .whoami = INV_IAM20680_WHOAMI_VALUE, .name = "IAM20680", @@ -295,6 +303,25 @@ int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) } EXPORT_SYMBOL_GPL(inv_mpu6050_set_power_itg); +static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st, + enum inv_mpu6050_fsr_e val) +{ + unsigned int gyro_shift; + u8 data; + + switch (st->chip_type) { + case INV_ICM20690: + gyro_shift = INV_ICM20690_GYRO_CONFIG_FSR_SHIFT; + break; + default: + gyro_shift = INV_MPU6050_GYRO_CONFIG_FSR_SHIFT; + break; + } + + data = val << gyro_shift; + return regmap_write(st->map, st->reg->gyro_config, data); +} + /** * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent * @@ -318,6 +345,7 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, /* old chips, nothing to do */ return 0; case INV_ICM20689: + case INV_ICM20690: /* set FIFO size to maximum value */ val |= INV_ICM20689_BITS_FIFO_SIZE_MAX; break; @@ -346,8 +374,8 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) result = inv_mpu6050_set_power_itg(st, true); if (result) return result; - d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = regmap_write(st->map, st->reg->gyro_config, d); + + result = inv_mpu6050_set_gyro_fsr(st, INV_MPU6050_FSR_2000DPS); if (result) goto error_power_off; @@ -559,12 +587,10 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val) { int result, i; - u8 d; for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) { if (gyro_scale_6050[i] == val) { - d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = regmap_write(st->map, st->reg->gyro_config, d); + result = inv_mpu6050_set_gyro_fsr(st, i); if (result) return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index cd0eeb5dc7f6..1363d3776523 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -208,6 +208,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"icm20690", INV_ICM20690}, {"iam20680", INV_IAM20680}, {} }; @@ -255,6 +256,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,icm20690", + .data = (void *)INV_ICM20690 + }, { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 404c289f8c84..7ae614052210 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -78,6 +78,7 @@ enum inv_devices { INV_ICM20609, INV_ICM20689, INV_ICM20602, + INV_ICM20690, INV_IAM20680, INV_NUM_PARTS }; @@ -297,6 +298,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_MAX_ACCL_FS_PARAM 3 #define INV_MPU6050_THREE_AXIS 3 #define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3 +#define INV_ICM20690_GYRO_CONFIG_FSR_SHIFT 2 #define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3 #define INV_MPU6500_TEMP_OFFSET 7011 @@ -347,6 +349,7 @@ struct inv_mpu6050_state { #define INV_ICM20609_WHOAMI_VALUE 0xA6 #define INV_ICM20689_WHOAMI_VALUE 0x98 #define INV_ICM20602_WHOAMI_VALUE 0x12 +#define INV_ICM20690_WHOAMI_VALUE 0x20 #define INV_IAM20680_WHOAMI_VALUE 0xA9 /* scan element definition for generic MPU6xxx devices */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 393cec555be6..bc351dd58c53 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -84,6 +84,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"icm20690", INV_ICM20690}, {"iam20680", INV_IAM20680}, {} }; @@ -127,6 +128,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,icm20690", + .data = (void *)INV_ICM20690 + }, { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 From 9fc89f796513ca6ce2a3a8f56b90ede7c3d42ba5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:04 +0100 Subject: [PATCH 23/98] iio: imu: inv_mpu6050: update LPF bandwidth settings As every chip has some little variant in LPF bandwidth values, use common values that are working for all chips. Simplify the LPF setting function. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 30 ++++++++++++---------- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 10 ++++---- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 9ecc667debbe..c4db9086775c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -707,30 +707,32 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, /** * inv_mpu6050_set_lpf() - set low pass filer based on fifo rate. * - * Based on the Nyquist principle, the sampling rate must - * exceed twice of the bandwidth of the signal, or there - * would be alising. This function basically search for the - * correct low pass parameters based on the fifo rate, e.g, - * sampling frequency. + * Based on the Nyquist principle, the bandwidth of the low + * pass filter must not exceed the signal sampling rate divided + * by 2, or there would be aliasing. + * This function basically search for the correct low pass + * parameters based on the fifo rate, e.g, sampling frequency. * * lpf is set automatically when setting sampling rate to avoid any aliases. */ static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate) { - static const int hz[] = {188, 98, 42, 20, 10, 5}; + static const int hz[] = {400, 200, 90, 40, 20, 10}; static const int d[] = { - INV_MPU6050_FILTER_188HZ, INV_MPU6050_FILTER_98HZ, - INV_MPU6050_FILTER_42HZ, INV_MPU6050_FILTER_20HZ, + INV_MPU6050_FILTER_200HZ, INV_MPU6050_FILTER_100HZ, + INV_MPU6050_FILTER_45HZ, INV_MPU6050_FILTER_20HZ, INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ }; - int i, h, result; + int i, result; u8 data; - h = (rate >> 1); - i = 0; - while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1)) - i++; - data = d[i]; + data = INV_MPU6050_FILTER_5HZ; + for (i = 0; i < ARRAY_SIZE(hz); ++i) { + if (rate >= hz[i]) { + data = d[i]; + break; + } + } result = inv_mpu6050_set_lpf_regs(st, data); if (result) return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 7ae614052210..9a81098a8b4d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -370,14 +370,14 @@ enum inv_mpu6050_scan { }; enum inv_mpu6050_filter_e { - INV_MPU6050_FILTER_256HZ_NOLPF2 = 0, - INV_MPU6050_FILTER_188HZ, - INV_MPU6050_FILTER_98HZ, - INV_MPU6050_FILTER_42HZ, + INV_MPU6050_FILTER_NOLPF2 = 0, + INV_MPU6050_FILTER_200HZ, + INV_MPU6050_FILTER_100HZ, + INV_MPU6050_FILTER_45HZ, INV_MPU6050_FILTER_20HZ, INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ, - INV_MPU6050_FILTER_2100HZ_NOLPF, + INV_MPU6050_FILTER_NOLPF, NUM_MPU6050_FILTER }; From b9cd7be04b0e6cb844f4c64ce05ba892095eb521 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:05 +0100 Subject: [PATCH 24/98] dt-bindings: add description for new supported chips Add missing mpu6000 and new icm20609, icm20689, icm20690, and iam20680. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index c5ee8a20af9f..f2f64749e818 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -4,6 +4,7 @@ http://www.invensense.com/mems/gyro/mpu6050.html Required properties: - compatible : should be one of + "invensense,mpu6000" "invensense,mpu6050" "invensense,mpu6500" "invensense,mpu6515" @@ -11,7 +12,11 @@ Required properties: "invensense,mpu9250" "invensense,mpu9255" "invensense,icm20608" + "invensense,icm20609" + "invensense,icm20689" "invensense,icm20602" + "invensense,icm20690" + "invensense,iam20680" - reg : the I2C address of the sensor - interrupts: interrupt mapping for IRQ. It should be configured with flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or From c5b8425514da595763850ae2f3ef78ababe4be80 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 10 Feb 2020 18:19:58 +0100 Subject: [PATCH 25/98] counter: stm32-timer-cnt: add power management support Add suspend/resume PM sleep ops. When going to low power, enforce the counter isn't active. Gracefully restore its state upon resume in case it's been left enabled prior to suspend. Acked-by: William Breathitt Gray Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/counter/stm32-timer-cnt.c | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 3eafccec3beb..50496f453d6d 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #define TIM_CCMR_CCXS (BIT(8) | BIT(0)) @@ -20,11 +21,20 @@ #define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \ TIM_CCER_CC2P | TIM_CCER_CC2NP) +struct stm32_timer_regs { + u32 cr1; + u32 cnt; + u32 smcr; + u32 arr; +}; + struct stm32_timer_cnt { struct counter_device counter; struct regmap *regmap; struct clk *clk; u32 ceiling; + bool enabled; + struct stm32_timer_regs bak; }; /** @@ -224,6 +234,9 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter, clk_disable(priv->clk); } + /* Keep enabled state to properly handle low power states */ + priv->enabled = enable; + return len; } @@ -358,10 +371,59 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) priv->counter.num_signals = ARRAY_SIZE(stm32_signals); priv->counter.priv = priv; + platform_set_drvdata(pdev, priv); + /* Register Counter device */ return devm_counter_register(dev, &priv->counter); } +static int __maybe_unused stm32_timer_cnt_suspend(struct device *dev) +{ + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); + + /* Only take care of enabled counter: don't disturb other MFD child */ + if (priv->enabled) { + /* Backup registers that may get lost in low power mode */ + regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr); + regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr); + regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt); + regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1); + + /* Disable the counter */ + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + clk_disable(priv->clk); + } + + return pinctrl_pm_select_sleep_state(dev); +} + +static int __maybe_unused stm32_timer_cnt_resume(struct device *dev) +{ + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + return ret; + + if (priv->enabled) { + clk_enable(priv->clk); + + /* Restore registers that may have been lost */ + regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); + regmap_write(priv->regmap, TIM_ARR, priv->bak.arr); + regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt); + + /* Also re-enables the counter */ + regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_timer_cnt_pm_ops, stm32_timer_cnt_suspend, + stm32_timer_cnt_resume); + static const struct of_device_id stm32_timer_cnt_of_match[] = { { .compatible = "st,stm32-timer-counter", }, {}, @@ -373,6 +435,7 @@ static struct platform_driver stm32_timer_cnt_driver = { .driver = { .name = "stm32-timer-counter", .of_match_table = stm32_timer_cnt_of_match, + .pm = &stm32_timer_cnt_pm_ops, }, }; module_platform_driver(stm32_timer_cnt_driver); From 15e8573d3b3f7f60b8b4e2c58e5bfc0413c9a89d Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Tue, 11 Feb 2020 11:56:06 +0100 Subject: [PATCH 26/98] counter: stm32-timer-cnt: remove iio headers The stm32-timer-cnt driver doesn't use the iio interface. The iio headers aren't relevant and can be removed as reported by William in [1]. With this change, mod_devicetable.h needs to be added to define the 'of_device_id' struct. [1] https://lkml.org/lkml/2020/2/10/1516 Reported-by: William Breathitt Gray Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/counter/stm32-timer-cnt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 50496f453d6d..ef2a974a2f10 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -8,9 +8,8 @@ * */ #include -#include -#include #include +#include #include #include #include From b8528224741b53100381b1382d35422f4b696abb Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Thu, 6 Feb 2020 17:11:45 +0200 Subject: [PATCH 27/98] iio: core: Handle 'dB' suffix in core This patch handles the db suffix used for writing micro db values. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 39 ++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 65ff0d067018..0b14666dff09 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -769,17 +769,18 @@ static ssize_t iio_read_channel_info_avail(struct device *dev, } /** - * iio_str_to_fixpoint() - Parse a fixed-point number from a string + * __iio_str_to_fixpoint() - Parse a fixed-point number from a string * @str: The string to parse * @fract_mult: Multiplier for the first decimal place, should be a power of 10 * @integer: The integer part of the number * @fract: The fractional part of the number + * @scale_db: True if this should parse as dB * * Returns 0 on success, or a negative error code if the string could not be * parsed. */ -int iio_str_to_fixpoint(const char *str, int fract_mult, - int *integer, int *fract) +static int __iio_str_to_fixpoint(const char *str, int fract_mult, + int *integer, int *fract, bool scale_db) { int i = 0, f = 0; bool integer_part = true, negative = false; @@ -810,6 +811,14 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, break; else return -EINVAL; + } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) { + /* Ignore the dB suffix */ + str += sizeof(" dB") - 1; + continue; + } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) { + /* Ignore the dB suffix */ + str += sizeof("dB") - 1; + continue; } else if (*str == '.' && integer_part) { integer_part = false; } else { @@ -830,6 +839,22 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, return 0; } + +/** + * iio_str_to_fixpoint() - Parse a fixed-point number from a string + * @str: The string to parse + * @fract_mult: Multiplier for the first decimal place, should be a power of 10 + * @integer: The integer part of the number + * @fract: The fractional part of the number + * + * Returns 0 on success, or a negative error code if the string could not be + * parsed. + */ +int iio_str_to_fixpoint(const char *str, int fract_mult, + int *integer, int *fract) +{ + return __iio_str_to_fixpoint(str, fract_mult, integer, fract, false); +} EXPORT_SYMBOL_GPL(iio_str_to_fixpoint); static ssize_t iio_write_channel_info(struct device *dev, @@ -842,6 +867,7 @@ static ssize_t iio_write_channel_info(struct device *dev, int ret, fract_mult = 100000; int integer, fract = 0; bool is_char = false; + bool scale_db = false; /* Assumes decimal - precision based on number of digits */ if (!indio_dev->info->write_raw) @@ -853,6 +879,9 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_INT: fract_mult = 0; break; + case IIO_VAL_INT_PLUS_MICRO_DB: + scale_db = true; + /* fall through */ case IIO_VAL_INT_PLUS_MICRO: fract_mult = 100000; break; @@ -877,6 +906,10 @@ static ssize_t iio_write_channel_info(struct device *dev, if (ret) return ret; } + ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract, + scale_db); + if (ret) + return ret; ret = indio_dev->info->write_raw(indio_dev, this_attr->c, integer, fract, this_attr->address); From 0cc97f2e46e0b374bbf7efc30bad638d8974cf1e Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Thu, 6 Feb 2020 17:11:46 +0200 Subject: [PATCH 28/98] iio: amplifiers: ad8366: Add write_raw_get_fmt function This patch add write_raw_get_fmt function to specify conversion for hardware gain. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 0176d3d8cc9c..95972ab60f42 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -180,9 +180,22 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, return ret; } +static int ad8366_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + return IIO_VAL_INT_PLUS_MICRO_DB; + default: + return -EINVAL; + } +} + static const struct iio_info ad8366_info = { .read_raw = &ad8366_read_raw, .write_raw = &ad8366_write_raw, + .write_raw_get_fmt = &ad8366_write_raw_get_fmt, }; #define AD8366_CHAN(_channel) { \ From a76838dfc7b7ea1f06de51fbe8e653eb34f65c94 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Thu, 6 Feb 2020 17:11:47 +0200 Subject: [PATCH 29/98] iio: amplifiers: hmc425a: Add support for HMC425A attenuator This patch adds support for the HMC425A 0.5 dB LSB GaAs MMIC 6-BIT DIGITAL POSITIVE CONTROL ATTENUATOR, 2.2 - 8.0 GHz. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/hmc425A.pdf Signed-off-by: Michael Hennerich Signed-off-by: Alexandru Ardelean Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/Kconfig | 10 ++ drivers/iio/amplifiers/Makefile | 1 + drivers/iio/amplifiers/hmc425a.c | 248 +++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+) create mode 100644 drivers/iio/amplifiers/hmc425a.c diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index da7f126d197b..9b02c9a2bc8a 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -22,4 +22,14 @@ config AD8366 To compile this driver as a module, choose M here: the module will be called ad8366. +config HMC425 + tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers" + depends on GPIOLIB + help + Say yes here to build support for Analog Devices HMC425A and similar + gain amplifiers or step attenuators. + + To compile this driver as a module, choose M here: the + module will be called hmc425a. + endmenu diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile index 9abef2ebe9bc..cb551d82f56b 100644 --- a/drivers/iio/amplifiers/Makefile +++ b/drivers/iio/amplifiers/Makefile @@ -5,3 +5,4 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD8366) += ad8366.o +obj-$(CONFIG_HMC425) += hmc425a.o diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c new file mode 100644 index 000000000000..d9e6e9678ffc --- /dev/null +++ b/drivers/iio/amplifiers/hmc425a.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HMC425A and similar Gain Amplifiers + * + * Copyright 2020 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum hmc425a_type { + ID_HMC425A, +}; + +struct hmc425a_chip_info { + const char *name; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int num_gpios; + int gain_min; + int gain_max; + int default_gain; +}; + +struct hmc425a_state { + struct regulator *reg; + struct mutex lock; /* protect sensor state */ + struct hmc425a_chip_info *chip_info; + struct gpio_descs *gpios; + enum hmc425a_type type; + u32 gain; +}; + +static int hmc425a_write(struct iio_dev *indio_dev, u32 value) +{ + struct hmc425a_state *st = iio_priv(indio_dev); + DECLARE_BITMAP(values, BITS_PER_TYPE(value)); + + values[0] = value; + + gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc, + NULL, values); + return 0; +} + +static int hmc425a_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long m) +{ + struct hmc425a_state *st = iio_priv(indio_dev); + int code, gain = 0; + int ret; + + mutex_lock(&st->lock); + switch (m) { + case IIO_CHAN_INFO_HARDWAREGAIN: + code = st->gain; + + switch (st->type) { + case ID_HMC425A: + gain = ~code * -500; + break; + } + + *val = gain / 1000; + *val2 = (gain % 1000) * 1000; + + ret = IIO_VAL_INT_PLUS_MICRO_DB; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&st->lock); + + return ret; +}; + +static int hmc425a_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct hmc425a_state *st = iio_priv(indio_dev); + struct hmc425a_chip_info *inf = st->chip_info; + int code = 0, gain; + int ret; + + if (val < 0) + gain = (val * 1000) - (val2 / 1000); + else + gain = (val * 1000) + (val2 / 1000); + + if (gain > inf->gain_max || gain < inf->gain_min) + return -EINVAL; + + switch (st->type) { + case ID_HMC425A: + code = ~((abs(gain) / 500) & 0x3F); + break; + } + + mutex_lock(&st->lock); + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + st->gain = code; + + ret = hmc425a_write(indio_dev, st->gain); + break; + default: + ret = -EINVAL; + } + mutex_unlock(&st->lock); + + return ret; +} + +static int hmc425a_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + return IIO_VAL_INT_PLUS_MICRO_DB; + default: + return -EINVAL; + } +} + +static const struct iio_info hmc425a_info = { + .read_raw = &hmc425a_read_raw, + .write_raw = &hmc425a_write_raw, + .write_raw_get_fmt = &hmc425a_write_raw_get_fmt, +}; + +#define HMC425A_CHAN(_channel) \ +{ \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ +} + +static const struct iio_chan_spec hmc425a_channels[] = { + HMC425A_CHAN(0), +}; + +/* Match table for of_platform binding */ +static const struct of_device_id hmc425a_of_match[] = { + { .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A }, + {}, +}; +MODULE_DEVICE_TABLE(of, hmc425a_of_match); + +static void hmc425a_reg_disable(void *data) +{ + struct hmc425a_state *st = data; + + regulator_disable(st->reg); +} + +static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { + [ID_HMC425A] = { + .name = "hmc425a", + .channels = hmc425a_channels, + .num_channels = ARRAY_SIZE(hmc425a_channels), + .num_gpios = 6, + .gain_min = -31500, + .gain_max = 0, + .default_gain = -0x40, /* set default gain -31.5db*/ + }, +}; + +static int hmc425a_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct hmc425a_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->type = (enum hmc425a_type)of_device_get_match_data(&pdev->dev); + + st->chip_info = &hmc425a_chip_info_tbl[st->type]; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->channels = st->chip_info->channels; + indio_dev->name = st->chip_info->name; + st->gain = st->chip_info->default_gain; + + st->gpios = devm_gpiod_get_array(&pdev->dev, "ctrl", GPIOD_OUT_LOW); + if (IS_ERR(st->gpios)) { + ret = PTR_ERR(st->gpios); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get gpios\n"); + return ret; + } + + if (st->gpios->ndescs != st->chip_info->num_gpios) { + dev_err(&pdev->dev, "%d GPIOs needed to operate\n", + st->chip_info->num_gpios); + return -ENODEV; + } + + st->reg = devm_regulator_get(&pdev->dev, "vcc-supply"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + ret = devm_add_action_or_reset(&pdev->dev, hmc425a_reg_disable, st); + if (ret) + return ret; + + mutex_init(&st->lock); + + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &hmc425a_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static struct platform_driver hmc425a_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = hmc425a_of_match, + }, + .probe = hmc425a_probe, +}; +module_platform_driver(hmc425a_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices HMC425A and similar GPIO control Gain Amplifiers"); +MODULE_LICENSE("GPL v2"); From 8aba25394cbf8a2bfd45b43f36f2a7a573f6d213 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Thu, 6 Feb 2020 17:11:48 +0200 Subject: [PATCH 30/98] dt-bindings: iio: amplifiers: Add docs for HMC425A Step Attenuator Document support for Analog Devices MC425A Step Attenuator. Signed-off-by: Michael Hennerich Signed-off-by: Beniamin Bia Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/amplifiers/adi,hmc425a.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml new file mode 100644 index 000000000000..1c6d49685e9f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/amplifiers/adi,hmc425a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HMC425A 6-bit Digital Step Attenuator + +maintainers: +- Michael Hennerich +- Beniamin Bia + +description: | + Digital Step Attenuator IIO device with gpio interface. + HMC425A 0.5 dB LSB GaAs MMIC 6-BIT DIGITAL POSITIVE CONTROL ATTENUATOR, 2.2 - 8.0 GHz + https://www.analog.com/media/en/technical-documentation/data-sheets/hmc425A.pdf + +properties: + compatible: + enum: + - adi,hmc425a + + vcc-supply: true + + ctrl-gpios: + description: + Must contain an array of 6 GPIO specifiers, referring to the GPIO pins + connected to the control pins V1-V6. + minItems: 6 + maxItems: 6 + +required: + - compatible + - ctrl-gpios + +examples: + - | + #include + gpio_hmc425a: hmc425a { + compatible = "adi,hmc425a"; + ctrl-gpios = <&gpio 40 GPIO_ACTIVE_HIGH>, + <&gpio 39 GPIO_ACTIVE_HIGH>, + <&gpio 38 GPIO_ACTIVE_HIGH>, + <&gpio 37 GPIO_ACTIVE_HIGH>, + <&gpio 36 GPIO_ACTIVE_HIGH>, + <&gpio 35 GPIO_ACTIVE_HIGH>; + vcc-supply = <&foo>; + }; +... From 16256a73744de2e79052403297a36664a547b5e9 Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Thu, 6 Feb 2020 17:11:49 +0200 Subject: [PATCH 31/98] MAINTAINERS: add entry for hmc425a driver. Add Beniamin Bia and Michael Hennerich as maintainers for HMC425A attenuator. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d8ffbb4bf920..11b7d21eaa6c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1081,6 +1081,15 @@ F: drivers/iio/adc/ltc249* X: drivers/iio/*/adjd* F: drivers/staging/iio/*/ad* +ANALOG DEVICES INC HMC425A DRIVER +M: Beniamin Bia +M: Michael Hennerich +L: linux-iio@vger.kernel.org +S: Supported +W: http://ez.analog.com/community/linux-device-drivers +F: Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml +F: drivers/iio/amplifiers/hmc425a.c + ANALOGBITS PLL LIBRARIES M: Paul Walmsley S: Supported From f20dbe11e2e904547597ae7371c1f635e3be9cad Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 7 Feb 2020 13:37:54 +0100 Subject: [PATCH 32/98] iio: imu: st_lsm6dsx: check return value from st_lsm6dsx_sensor_set_enable Add missing return value check in st_lsm6dsx_shub_read_oneshot disabling the slave device connected to the st_lsm6dsx i2c controller. The issue is reported by coverity with the following error: Unchecked return value: If the function returns an error value, the error value may be mistaken for a normal value. Addresses-Coverity-ID: 1456767 ("Unchecked return value") Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index eea555617d4a..95ddd19d1aa7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -464,9 +464,10 @@ st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor, len = min_t(int, sizeof(data), ch->scan_type.realbits >> 3); err = st_lsm6dsx_shub_read(sensor, ch->address, data, len); + if (err < 0) + return err; - st_lsm6dsx_shub_set_enable(sensor, false); - + err = st_lsm6dsx_shub_set_enable(sensor, false); if (err < 0) return err; From 97928677fe35814ead7e77f1bdaf0da82215c8f4 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 10 Feb 2020 15:25:58 +0200 Subject: [PATCH 33/98] iio: imu: adis16480: initialize adis_data statically This change overrides commit 380b107bbf944 ("iio: adis: Introduce timeouts structure"). It removes the memory allocation and moves the 'adis_data' information to be static on the chip_info struct. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16480.c | 140 ++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 78 deletions(-) diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index dac87f1001fd..4c4de1b62769 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -138,7 +138,7 @@ struct adis16480_chip_info { unsigned int max_dec_rate; const unsigned int *filter_freqs; bool has_pps_clk_mode; - const struct adis_timeout *timeouts; + const struct adis_data adis_data; }; enum adis16480_int_pin { @@ -796,6 +796,55 @@ enum adis16480_variant { ADIS16497_3, }; +#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0 +#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1 +#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2 +#define ADIS16480_DIAG_STAT_XACCL_FAIL 3 +#define ADIS16480_DIAG_STAT_YACCL_FAIL 4 +#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5 +#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8 +#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9 +#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10 +#define ADIS16480_DIAG_STAT_BARO_FAIL 11 + +static const char * const adis16480_status_error_msgs[] = { + [ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure", + [ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure", + [ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure", + [ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure", + [ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure", + [ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure", + [ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure", + [ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure", + [ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure", + [ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure", +}; + +static int adis16480_enable_irq(struct adis *adis, bool enable); + +#define ADIS16480_DATA(_timeouts) \ +{ \ + .diag_stat_reg = ADIS16480_REG_DIAG_STS, \ + .glob_cmd_reg = ADIS16480_REG_GLOB_CMD, \ + .has_paging = true, \ + .read_delay = 5, \ + .write_delay = 5, \ + .self_test_mask = BIT(1), \ + .status_error_msgs = adis16480_status_error_msgs, \ + .status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) | \ + BIT(ADIS16480_DIAG_STAT_BARO_FAIL), \ + .enable_irq = adis16480_enable_irq, \ + .timeouts = (_timeouts), \ +} + static const struct adis_timeout adis16485_timeouts = { .reset_ms = 560, .sw_reset_ms = 120, @@ -838,7 +887,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .timeouts = &adis16485_timeouts, + .adis_data = ADIS16480_DATA(&adis16485_timeouts), }, [ADIS16480] = { .channels = adis16480_channels, @@ -851,7 +900,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .timeouts = &adis16480_timeouts, + .adis_data = ADIS16480_DATA(&adis16480_timeouts), }, [ADIS16485] = { .channels = adis16485_channels, @@ -864,7 +913,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .timeouts = &adis16485_timeouts, + .adis_data = ADIS16480_DATA(&adis16485_timeouts), }, [ADIS16488] = { .channels = adis16480_channels, @@ -877,7 +926,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .timeouts = &adis16485_timeouts, + .adis_data = ADIS16480_DATA(&adis16485_timeouts), }, [ADIS16490] = { .channels = adis16485_channels, @@ -891,7 +940,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .timeouts = &adis16495_timeouts, + .adis_data = ADIS16480_DATA(&adis16495_timeouts), }, [ADIS16495_1] = { .channels = adis16485_channels, @@ -905,7 +954,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .timeouts = &adis16495_1_timeouts, + .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), }, [ADIS16495_2] = { .channels = adis16485_channels, @@ -919,7 +968,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .timeouts = &adis16495_1_timeouts, + .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), }, [ADIS16495_3] = { .channels = adis16485_channels, @@ -933,7 +982,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .timeouts = &adis16495_1_timeouts, + .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), }, [ADIS16497_1] = { .channels = adis16485_channels, @@ -947,7 +996,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .timeouts = &adis16495_1_timeouts, + .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), }, [ADIS16497_2] = { .channels = adis16485_channels, @@ -961,7 +1010,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .timeouts = &adis16495_1_timeouts, + .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), }, [ADIS16497_3] = { .channels = adis16485_channels, @@ -975,7 +1024,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .timeouts = &adis16495_1_timeouts, + .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), }, }; @@ -1048,53 +1097,6 @@ static int adis16480_initial_setup(struct iio_dev *indio_dev) return 0; } -#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0 -#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1 -#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2 -#define ADIS16480_DIAG_STAT_XACCL_FAIL 3 -#define ADIS16480_DIAG_STAT_YACCL_FAIL 4 -#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5 -#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8 -#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9 -#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10 -#define ADIS16480_DIAG_STAT_BARO_FAIL 11 - -static const char * const adis16480_status_error_msgs[] = { - [ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure", - [ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure", - [ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure", - [ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure", - [ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure", - [ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure", - [ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure", - [ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure", - [ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure", - [ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure", -}; - -static const struct adis_data adis16480_data = { - .diag_stat_reg = ADIS16480_REG_DIAG_STS, - .glob_cmd_reg = ADIS16480_REG_GLOB_CMD, - .has_paging = true, - - .read_delay = 5, - .write_delay = 5, - - .status_error_msgs = adis16480_status_error_msgs, - .status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | - BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | - BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) | - BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) | - BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) | - BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) | - BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) | - BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) | - BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) | - BIT(ADIS16480_DIAG_STAT_BARO_FAIL), - - .enable_irq = adis16480_enable_irq, -}; - static int adis16480_config_irq_pin(struct device_node *of_node, struct adis16480 *st) { @@ -1245,22 +1247,6 @@ static int adis16480_get_ext_clocks(struct adis16480 *st) return 0; } -static struct adis_data *adis16480_adis_data_alloc(struct adis16480 *st, - struct device *dev) -{ - struct adis_data *data; - - data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - - memcpy(data, &adis16480_data, sizeof(*data)); - - data->timeouts = st->chip_info->timeouts; - - return data; -} - static int adis16480_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -1285,9 +1271,7 @@ static int adis16480_probe(struct spi_device *spi) indio_dev->info = &adis16480_info; indio_dev->modes = INDIO_DIRECT_MODE; - adis16480_data = adis16480_adis_data_alloc(st, &spi->dev); - if (IS_ERR(adis16480_data)) - return PTR_ERR(adis16480_data); + adis16480_data = &st->chip_info->adis_data; ret = adis_init(&st->adis, indio_dev, spi, adis16480_data); if (ret) From 99460853a894ca4fd4d2573ab49ce1f57b7817e1 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 10 Feb 2020 15:25:59 +0200 Subject: [PATCH 34/98] iio: imu: adis16400: initialize adis_data statically This change overrides commit 380b107bbf944 ("iio: adis: Introduce timeouts structure"). It removes the memory allocation and moves the 'adis_data' information to be static on the chip_info struct. This also adds a timeout structure to ADIS16334, since it was initially omitted. This was omitted (by accident) when the change was done. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16400.c | 139 ++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 76 deletions(-) diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index cfb1c19eb930..1c0770e03ec9 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -156,7 +156,7 @@ struct adis16400_state; struct adis16400_chip_info { const struct iio_chan_spec *channels; - const struct adis_timeout *timeouts; + const struct adis_data adis_data; const int num_channels; const long flags; unsigned int gyro_scale_micro; @@ -930,12 +930,63 @@ static const struct iio_chan_spec adis16334_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP), }; +static const char * const adis16400_status_error_msgs[] = { + [ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure", + [ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure", + [ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure", + [ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure", + [ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure", + [ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure", + [ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active", + [ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active", + [ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error", + [ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error", + [ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange", + [ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure", + [ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed", + [ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V", + [ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V", +}; + +#define ADIS16400_DATA(_timeouts) \ +{ \ + .msc_ctrl_reg = ADIS16400_MSC_CTRL, \ + .glob_cmd_reg = ADIS16400_GLOB_CMD, \ + .diag_stat_reg = ADIS16400_DIAG_STAT, \ + .read_delay = 50, \ + .write_delay = 50, \ + .self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST, \ + .status_error_msgs = adis16400_status_error_msgs, \ + .status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) | \ + BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) | \ + BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) | \ + BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) | \ + BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) | \ + BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) | \ + BIT(ADIS16400_DIAG_STAT_ALARM2) | \ + BIT(ADIS16400_DIAG_STAT_ALARM1) | \ + BIT(ADIS16400_DIAG_STAT_FLASH_CHK) | \ + BIT(ADIS16400_DIAG_STAT_SELF_TEST) | \ + BIT(ADIS16400_DIAG_STAT_OVERFLOW) | \ + BIT(ADIS16400_DIAG_STAT_SPI_FAIL) | \ + BIT(ADIS16400_DIAG_STAT_FLASH_UPT) | \ + BIT(ADIS16400_DIAG_STAT_POWER_HIGH) | \ + BIT(ADIS16400_DIAG_STAT_POWER_LOW), \ + .timeouts = (_timeouts), \ +} + static const struct adis_timeout adis16300_timeouts = { .reset_ms = ADIS16400_STARTUP_DELAY, .sw_reset_ms = ADIS16400_STARTUP_DELAY, .self_test_ms = ADIS16400_STARTUP_DELAY, }; +static const struct adis_timeout adis16334_timeouts = { + .reset_ms = 60, + .sw_reset_ms = 60, + .self_test_ms = 14, +}; + static const struct adis_timeout adis16362_timeouts = { .reset_ms = 130, .sw_reset_ms = 130, @@ -972,7 +1023,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */ .set_freq = adis16400_set_freq, .get_freq = adis16400_get_freq, - .timeouts = &adis16300_timeouts, + .adis_data = ADIS16400_DATA(&adis16300_timeouts), }, [ADIS16334] = { .channels = adis16334_channels, @@ -985,6 +1036,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 25000000 / 67850, /* 25 C = 0x00 */ .set_freq = adis16334_set_freq, .get_freq = adis16334_get_freq, + .adis_data = ADIS16400_DATA(&adis16334_timeouts), }, [ADIS16350] = { .channels = adis16350_channels, @@ -996,7 +1048,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE, .set_freq = adis16400_set_freq, .get_freq = adis16400_get_freq, - .timeouts = &adis16300_timeouts, + .adis_data = ADIS16400_DATA(&adis16300_timeouts), }, [ADIS16360] = { .channels = adis16350_channels, @@ -1009,7 +1061,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ .set_freq = adis16400_set_freq, .get_freq = adis16400_get_freq, - .timeouts = &adis16300_timeouts, + .adis_data = ADIS16400_DATA(&adis16300_timeouts), }, [ADIS16362] = { .channels = adis16350_channels, @@ -1022,7 +1074,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ .set_freq = adis16400_set_freq, .get_freq = adis16400_get_freq, - .timeouts = &adis16362_timeouts, + .adis_data = ADIS16400_DATA(&adis16362_timeouts), }, [ADIS16364] = { .channels = adis16350_channels, @@ -1035,7 +1087,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ .set_freq = adis16400_set_freq, .get_freq = adis16400_get_freq, - .timeouts = &adis16362_timeouts, + .adis_data = ADIS16400_DATA(&adis16362_timeouts), }, [ADIS16367] = { .channels = adis16350_channels, @@ -1048,7 +1100,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ .set_freq = adis16400_set_freq, .get_freq = adis16400_get_freq, - .timeouts = &adis16300_timeouts, + .adis_data = ADIS16400_DATA(&adis16300_timeouts), }, [ADIS16400] = { .channels = adis16400_channels, @@ -1060,7 +1112,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */ .set_freq = adis16400_set_freq, .get_freq = adis16400_get_freq, - .timeouts = &adis16400_timeouts, + .adis_data = ADIS16400_DATA(&adis16400_timeouts), }, [ADIS16445] = { .channels = adis16445_channels, @@ -1074,7 +1126,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */ .set_freq = adis16334_set_freq, .get_freq = adis16334_get_freq, - .timeouts = &adis16445_timeouts, + .adis_data = ADIS16400_DATA(&adis16445_timeouts), }, [ADIS16448] = { .channels = adis16448_channels, @@ -1088,7 +1140,7 @@ static struct adis16400_chip_info adis16400_chips[] = { .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */ .set_freq = adis16334_set_freq, .get_freq = adis16334_get_freq, - .timeouts = &adis16448_timeouts, + .adis_data = ADIS16400_DATA(&adis16448_timeouts), } }; @@ -1099,52 +1151,6 @@ static const struct iio_info adis16400_info = { .debugfs_reg_access = adis_debugfs_reg_access, }; -static const char * const adis16400_status_error_msgs[] = { - [ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure", - [ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure", - [ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure", - [ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure", - [ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure", - [ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure", - [ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active", - [ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active", - [ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error", - [ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error", - [ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange", - [ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure", - [ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed", - [ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V", - [ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V", -}; - -static const struct adis_data adis16400_data = { - .msc_ctrl_reg = ADIS16400_MSC_CTRL, - .glob_cmd_reg = ADIS16400_GLOB_CMD, - .diag_stat_reg = ADIS16400_DIAG_STAT, - - .read_delay = 50, - .write_delay = 50, - - .self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST, - - .status_error_msgs = adis16400_status_error_msgs, - .status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) | - BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) | - BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) | - BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) | - BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) | - BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) | - BIT(ADIS16400_DIAG_STAT_ALARM2) | - BIT(ADIS16400_DIAG_STAT_ALARM1) | - BIT(ADIS16400_DIAG_STAT_FLASH_CHK) | - BIT(ADIS16400_DIAG_STAT_SELF_TEST) | - BIT(ADIS16400_DIAG_STAT_OVERFLOW) | - BIT(ADIS16400_DIAG_STAT_SPI_FAIL) | - BIT(ADIS16400_DIAG_STAT_FLASH_UPT) | - BIT(ADIS16400_DIAG_STAT_POWER_HIGH) | - BIT(ADIS16400_DIAG_STAT_POWER_LOW), -}; - static void adis16400_setup_chan_mask(struct adis16400_state *st) { const struct adis16400_chip_info *chip_info = st->variant; @@ -1158,23 +1164,6 @@ static void adis16400_setup_chan_mask(struct adis16400_state *st) st->avail_scan_mask[0] |= BIT(ch->scan_index); } } - -static struct adis_data *adis16400_adis_data_alloc(struct adis16400_state *st, - struct device *dev) -{ - struct adis_data *data; - - data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - - memcpy(data, &adis16400_data, sizeof(*data)); - - data->timeouts = st->variant->timeouts; - - return data; -} - static int adis16400_probe(struct spi_device *spi) { struct adis16400_state *st; @@ -1207,9 +1196,7 @@ static int adis16400_probe(struct spi_device *spi) st->adis.burst->extra_len = sizeof(u16); } - adis16400_data = adis16400_adis_data_alloc(st, &spi->dev); - if (IS_ERR(adis16400_data)) - return PTR_ERR(adis16400_data); + adis16400_data = &st->variant->adis_data; ret = adis_init(&st->adis, indio_dev, spi, adis16400_data); if (ret) From e914cfdf12ab1a3f7339c95a65c8b5743c951fa6 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 10 Feb 2020 15:26:00 +0200 Subject: [PATCH 35/98] iio: gyro: adis16136: initialize adis_data statically This change overrides commit 380b107bbf944 ("iio: adis: Introduce timeouts structure"). It removes the memory allocation and moves the 'adis_data' information to be static on the chip_info struct. This also adds a timeout structure to ADIS16334, since it was initially omitted. This was omitted (by accident) when the change was done. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adis16136.c | 61 +++++++++++++----------------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index d5e03a406d4a..1db1131e5c67 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -59,7 +59,7 @@ struct adis16136_chip_info { unsigned int precision; unsigned int fullscale; - const struct adis_timeout *timeouts; + const struct adis_data adis_data; }; struct adis16136 { @@ -466,22 +466,21 @@ static const char * const adis16136_status_error_msgs[] = { [ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error", }; -static const struct adis_data adis16136_data = { - .diag_stat_reg = ADIS16136_REG_DIAG_STAT, - .glob_cmd_reg = ADIS16136_REG_GLOB_CMD, - .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, - - .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, - - .read_delay = 10, - .write_delay = 10, - - .status_error_msgs = adis16136_status_error_msgs, - .status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) | - BIT(ADIS16136_DIAG_STAT_SPI_FAIL) | - BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) | - BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL), -}; +#define ADIS16136_DATA(_timeouts) \ +{ \ + .diag_stat_reg = ADIS16136_REG_DIAG_STAT, \ + .glob_cmd_reg = ADIS16136_REG_GLOB_CMD, \ + .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, \ + .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, \ + .read_delay = 10, \ + .write_delay = 10, \ + .status_error_msgs = adis16136_status_error_msgs, \ + .status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) | \ + BIT(ADIS16136_DIAG_STAT_SPI_FAIL) | \ + BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) | \ + BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL), \ + .timeouts = (_timeouts), \ +} enum adis16136_id { ID_ADIS16133, @@ -506,41 +505,25 @@ static const struct adis16136_chip_info adis16136_chip_info[] = { [ID_ADIS16133] = { .precision = IIO_DEGREE_TO_RAD(1200), .fullscale = 24000, - .timeouts = &adis16133_timeouts, + .adis_data = ADIS16136_DATA(&adis16133_timeouts), }, [ID_ADIS16135] = { .precision = IIO_DEGREE_TO_RAD(300), .fullscale = 24000, - .timeouts = &adis16133_timeouts, + .adis_data = ADIS16136_DATA(&adis16133_timeouts), }, [ID_ADIS16136] = { .precision = IIO_DEGREE_TO_RAD(450), .fullscale = 24623, - .timeouts = &adis16136_timeouts, + .adis_data = ADIS16136_DATA(&adis16136_timeouts), }, [ID_ADIS16137] = { .precision = IIO_DEGREE_TO_RAD(1000), .fullscale = 24609, - .timeouts = &adis16136_timeouts, + .adis_data = ADIS16136_DATA(&adis16136_timeouts), }, }; -static struct adis_data *adis16136_adis_data_alloc(struct adis16136 *st, - struct device *dev) -{ - struct adis_data *data; - - data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - - memcpy(data, &adis16136_data, sizeof(*data)); - - data->timeouts = st->chip_info->timeouts; - - return data; -} - static int adis16136_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -565,9 +548,7 @@ static int adis16136_probe(struct spi_device *spi) indio_dev->info = &adis16136_info; indio_dev->modes = INDIO_DIRECT_MODE; - adis16136_data = adis16136_adis_data_alloc(adis16136, &spi->dev); - if (IS_ERR(adis16136_data)) - return PTR_ERR(adis16136_data); + adis16136_data = &adis16136->chip_info->adis_data; ret = adis_init(&adis16136->adis, indio_dev, spi, adis16136_data); if (ret) From 3f17ada8f38c57b6c81d41db6d911932b280d5b5 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 10 Feb 2020 15:26:01 +0200 Subject: [PATCH 36/98] iio: imu: adis: add unlocked __adis_initial_startup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change splits the __adis_initial_startup() away from adis_initial_startup(). The unlocked version can be used in certain calls during probe, where races won't happen since the ADIS driver may not be registered yet with IIO. Signed-off-by: Nuno Sá Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 14 ++++---------- include/linux/iio/imu/adis.h | 13 ++++++++++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 022bb54fb748..e4897dad34ab 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -365,7 +365,7 @@ static int adis_self_test(struct adis *adis) } /** - * adis_inital_startup() - Performs device self-test + * __adis_initial_startup() - Device initial setup * @adis: The adis device * * Returns 0 if the device is operational, a negative error code otherwise. @@ -373,28 +373,22 @@ static int adis_self_test(struct adis *adis) * This function should be called early on in the device initialization sequence * to ensure that the device is in a sane and known state and that it is usable. */ -int adis_initial_startup(struct adis *adis) +int __adis_initial_startup(struct adis *adis) { int ret; - mutex_lock(&adis->state_lock); - ret = adis_self_test(adis); if (ret) { dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n"); __adis_reset(adis); ret = adis_self_test(adis); - if (ret) { + if (ret) dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n"); - goto out_unlock; - } } -out_unlock: - mutex_unlock(&adis->state_lock); return ret; } -EXPORT_SYMBOL_GPL(adis_initial_startup); +EXPORT_SYMBOL_GPL(__adis_initial_startup); /** * adis_single_conversion() - Performs a single sample conversion diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index d2fcf45b4cef..15e75670f923 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -297,6 +297,7 @@ static inline int adis_read_reg_32(struct adis *adis, unsigned int reg, int adis_enable_irq(struct adis *adis, bool enable); int __adis_check_status(struct adis *adis); +int __adis_initial_startup(struct adis *adis); static inline int adis_check_status(struct adis *adis) { @@ -309,7 +310,17 @@ static inline int adis_check_status(struct adis *adis) return ret; } -int adis_initial_startup(struct adis *adis); +/* locked version of __adis_initial_startup() */ +static inline int adis_initial_startup(struct adis *adis) +{ + int ret; + + mutex_lock(&adis->state_lock); + ret = __adis_initial_startup(adis); + mutex_unlock(&adis->state_lock); + + return ret; +} int adis_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int error_mask, From fdcf6bbb4ed388844e74810e117ac87db1347f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 10 Feb 2020 15:26:02 +0200 Subject: [PATCH 37/98] iio: imu: adis: Add self_test_reg variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a dedicated self_test_reg variable. This is also a step to let new drivers make use of `adis_initial_startup()`. Some devices use MSG_CTRL reg to request a self_test command while others use the GLOB_CMD register. Signed-off-by: Nuno Sá Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adis16201.c | 1 + drivers/iio/accel/adis16209.c | 1 + drivers/iio/gyro/adis16136.c | 1 + drivers/iio/gyro/adis16260.c | 1 + drivers/iio/imu/adis.c | 6 +++--- drivers/iio/imu/adis16400.c | 1 + drivers/iio/imu/adis16460.c | 2 ++ drivers/iio/imu/adis16480.c | 1 + drivers/staging/iio/accel/adis16203.c | 1 + drivers/staging/iio/accel/adis16240.c | 1 + include/linux/iio/imu/adis.h | 2 ++ 11 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 0f0f27a8184e..4154e7396bbe 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -246,6 +246,7 @@ static const struct adis_data adis16201_data = { .diag_stat_reg = ADIS16201_DIAG_STAT_REG, .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16201_MSC_CTRL_REG, .self_test_no_autoclear = true, .timeouts = &adis16201_timeouts, diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index c6dbd2424e10..31d45e7c5485 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -256,6 +256,7 @@ static const struct adis_data adis16209_data = { .diag_stat_reg = ADIS16209_STAT_REG, .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16209_MSC_CTRL_REG, .self_test_no_autoclear = true, .timeouts = &adis16209_timeouts, diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 1db1131e5c67..a4c967a5fc5c 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -471,6 +471,7 @@ static const char * const adis16136_status_error_msgs[] = { .diag_stat_reg = ADIS16136_REG_DIAG_STAT, \ .glob_cmd_reg = ADIS16136_REG_GLOB_CMD, \ .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, \ + .self_test_reg = ADIS16136_REG_MSC_CTRL, \ .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, \ .read_delay = 10, \ .write_delay = 10, \ diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index be09b3e5910c..9823573e811a 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -346,6 +346,7 @@ static const struct adis_data adis16260_data = { .diag_stat_reg = ADIS16260_DIAG_STAT, .self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST, + .self_test_reg = ADIS16260_MSC_CTRL, .timeouts = &adis16260_timeouts, .status_error_msgs = adis1620_status_error_msgs, diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index e4897dad34ab..f7845a90f376 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -346,8 +346,8 @@ static int adis_self_test(struct adis *adis) int ret; const struct adis_timeout *timeouts = adis->data->timeouts; - ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, - adis->data->self_test_mask); + ret = __adis_write_reg_16(adis, adis->data->self_test_reg, + adis->data->self_test_mask); if (ret) { dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n", ret); @@ -359,7 +359,7 @@ static int adis_self_test(struct adis *adis) ret = __adis_check_status(adis); if (adis->data->self_test_no_autoclear) - __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00); + __adis_write_reg_16(adis, adis->data->self_test_reg, 0x00); return ret; } diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 1c0770e03ec9..05e70c1c4835 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -956,6 +956,7 @@ static const char * const adis16400_status_error_msgs[] = { .read_delay = 50, \ .write_delay = 50, \ .self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST, \ + .self_test_reg = ADIS16400_MSC_CTRL, \ .status_error_msgs = adis16400_status_error_msgs, \ .status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) | \ BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) | \ diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 9539cfe4a259..42fa473c6d81 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -392,6 +392,8 @@ static const struct adis_timeout adis16460_timeouts = { static const struct adis_data adis16460_data = { .diag_stat_reg = ADIS16460_REG_DIAG_STAT, .glob_cmd_reg = ADIS16460_REG_GLOB_CMD, + .self_test_mask = BIT(2), + .self_test_reg = ADIS16460_REG_GLOB_CMD, .has_paging = false, .read_delay = 5, .write_delay = 5, diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 4c4de1b62769..acbe1701fc2d 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -830,6 +830,7 @@ static int adis16480_enable_irq(struct adis *adis, bool enable); .read_delay = 5, \ .write_delay = 5, \ .self_test_mask = BIT(1), \ + .self_test_reg = ADIS16480_REG_GLOB_CMD, \ .status_error_msgs = adis16480_status_error_msgs, \ .status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | \ BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | \ diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c index 39dfe3f7f254..fef52d9b5346 100644 --- a/drivers/staging/iio/accel/adis16203.c +++ b/drivers/staging/iio/accel/adis16203.c @@ -250,6 +250,7 @@ static const struct adis_data adis16203_data = { .diag_stat_reg = ADIS16203_DIAG_STAT, .self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16203_MSC_CTRL, .self_test_no_autoclear = true, .timeouts = &adis16203_timeouts, diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index 39eb8364aa95..8bd35c6c56a1 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -373,6 +373,7 @@ static const struct adis_data adis16240_data = { .diag_stat_reg = ADIS16240_DIAG_STAT, .self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16240_MSC_CTRL, .self_test_no_autoclear = true, .timeouts = &adis16240_timeouts, diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 15e75670f923..b7feca4e5f26 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -41,6 +41,7 @@ struct adis_timeout { * @glob_cmd_reg: Register address of the GLOB_CMD register * @msc_ctrl_reg: Register address of the MSC_CTRL register * @diag_stat_reg: Register address of the DIAG_STAT register + * @self_test_reg: Register address to request self test command * @status_error_msgs: Array of error messgaes * @status_error_mask: * @timeouts: Chip specific delays @@ -55,6 +56,7 @@ struct adis_data { unsigned int diag_stat_reg; unsigned int self_test_mask; + unsigned int self_test_reg; bool self_test_no_autoclear; const struct adis_timeout *timeouts; From ecb010d441088c447785a1249e69003437f96c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 10 Feb 2020 15:26:03 +0200 Subject: [PATCH 38/98] iio: imu: adis: Refactor adis_initial_startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the ADIS devices perform, at the beginning, a self test to make sure the device is in a sane state. Previously, the logic was that the self-test was performed in adis_initial_startup() and if that failed a reset was done and then a self-test was attempted again. This change unifies the reset mechanism under the adis_initial_startup() call. A HW reset will be done if GPIO is configured, or a SW reset otherwise. This should make sure that the chip is in a sane state for self-test. Once the reset is done, the self-test operation will be performed. If anything goes wrong with self-test, the driver should just bail/error-out (i.e. no second attempt). The chip would likely not be a in a sane state state if the self-test fails after a reset. Signed-off-by: Nuno Sá Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index f7845a90f376..0bd6e32cf577 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -368,6 +369,10 @@ static int adis_self_test(struct adis *adis) * __adis_initial_startup() - Device initial setup * @adis: The adis device * + * The function performs a HW reset via a reset pin that should be specified + * via GPIOLIB. If no pin is configured a SW reset will be performed. + * The RST pin for the ADIS devices should be configured as ACTIVE_LOW. + * * Returns 0 if the device is operational, a negative error code otherwise. * * This function should be called early on in the device initialization sequence @@ -375,18 +380,28 @@ static int adis_self_test(struct adis *adis) */ int __adis_initial_startup(struct adis *adis) { + const struct adis_timeout *timeouts = adis->data->timeouts; + struct gpio_desc *gpio; int ret; - ret = adis_self_test(adis); - if (ret) { - dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n"); - __adis_reset(adis); - ret = adis_self_test(adis); + /* check if the device has rst pin low */ + gpio = devm_gpiod_get_optional(&adis->spi->dev, "reset", GPIOD_ASIS); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (gpio) { + gpiod_set_value_cansleep(gpio, 1); + msleep(10); + /* bring device out of reset */ + gpiod_set_value_cansleep(gpio, 0); + msleep(timeouts->reset_ms); + } else { + ret = __adis_reset(adis); if (ret) - dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n"); + return ret; } - return ret; + return adis_self_test(adis); } EXPORT_SYMBOL_GPL(__adis_initial_startup); From 1fd4567026926d01a94a74d0543e324fc794aa73 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 10 Feb 2020 15:26:04 +0200 Subject: [PATCH 39/98] iio: imu: adis: add support product ID check in adis_initial_startup Each driver/chip that wants to validate it's product id, can now specify a 'prod_id_reg' and an expected 'prod_id' value. The 'prod_id' value is intentionally left 0 (uninitialized). There aren't (yet) any product IDs with value 0; this enforces that both 'prod_id_reg' and 'prod_id' are specified. At the very least, this routine validates that the SPI connection to the ADIS chip[s] works well. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 23 ++++++++++++++++++++++- include/linux/iio/imu/adis.h | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 0bd6e32cf577..a8afd01de4f3 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -373,6 +373,10 @@ static int adis_self_test(struct adis *adis) * via GPIOLIB. If no pin is configured a SW reset will be performed. * The RST pin for the ADIS devices should be configured as ACTIVE_LOW. * + * After the self-test operation is performed, the function will also check + * that the product ID is as expected. This assumes that drivers providing + * 'prod_id_reg' will also provide the 'prod_id'. + * * Returns 0 if the device is operational, a negative error code otherwise. * * This function should be called early on in the device initialization sequence @@ -382,6 +386,7 @@ int __adis_initial_startup(struct adis *adis) { const struct adis_timeout *timeouts = adis->data->timeouts; struct gpio_desc *gpio; + uint16_t prod_id; int ret; /* check if the device has rst pin low */ @@ -401,7 +406,23 @@ int __adis_initial_startup(struct adis *adis) return ret; } - return adis_self_test(adis); + ret = adis_self_test(adis); + if (ret) + return ret; + + if (!adis->data->prod_id_reg) + return 0; + + ret = adis_read_reg_16(adis, adis->data->prod_id_reg, &prod_id); + if (ret) + return ret; + + if (prod_id != adis->data->prod_id) + dev_warn(&adis->spi->dev, + "Device ID(%u) and product ID(%u) do not match.", + adis->data->prod_id, prod_id); + + return 0; } EXPORT_SYMBOL_GPL(__adis_initial_startup); diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index b7feca4e5f26..ac7cfd073804 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -41,6 +41,8 @@ struct adis_timeout { * @glob_cmd_reg: Register address of the GLOB_CMD register * @msc_ctrl_reg: Register address of the MSC_CTRL register * @diag_stat_reg: Register address of the DIAG_STAT register + * @prod_id_reg: Register address of the PROD_ID register + * @prod_id: Product ID code that should be expected when reading @prod_id_reg * @self_test_reg: Register address to request self test command * @status_error_msgs: Array of error messgaes * @status_error_mask: @@ -54,6 +56,9 @@ struct adis_data { unsigned int glob_cmd_reg; unsigned int msc_ctrl_reg; unsigned int diag_stat_reg; + unsigned int prod_id_reg; + + unsigned int prod_id; unsigned int self_test_mask; unsigned int self_test_reg; From 366a543421cfaa78564bff4eb369791c7e9b85e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 10 Feb 2020 15:26:05 +0200 Subject: [PATCH 40/98] iio: adis16480: Make use of __adis_initial_startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All actions done in `adis16480_initial_setup()` are now done in `__adis_initial_startup()` so, there's no need for code duplication. Furthermore, the call to `adis16480_initial_setup()` is done before any device configuration since the device will be reset if not already (via rst pin). This is actually fixing a potential bug since `adis_reset()` was being called after configuring the device which is obviously a problem. Signed-off-by: Nuno Sá Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16480.c | 80 +++++++++++-------------------------- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index acbe1701fc2d..cfae0e4476e7 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -822,10 +822,12 @@ static const char * const adis16480_status_error_msgs[] = { static int adis16480_enable_irq(struct adis *adis, bool enable); -#define ADIS16480_DATA(_timeouts) \ +#define ADIS16480_DATA(_prod_id, _timeouts) \ { \ .diag_stat_reg = ADIS16480_REG_DIAG_STS, \ .glob_cmd_reg = ADIS16480_REG_GLOB_CMD, \ + .prod_id_reg = ADIS16480_REG_PROD_ID, \ + .prod_id = (_prod_id), \ .has_paging = true, \ .read_delay = 5, \ .write_delay = 5, \ @@ -888,7 +890,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(&adis16485_timeouts), + .adis_data = ADIS16480_DATA(16375, &adis16485_timeouts), }, [ADIS16480] = { .channels = adis16480_channels, @@ -901,7 +903,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(&adis16480_timeouts), + .adis_data = ADIS16480_DATA(16480, &adis16480_timeouts), }, [ADIS16485] = { .channels = adis16485_channels, @@ -914,7 +916,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(&adis16485_timeouts), + .adis_data = ADIS16480_DATA(16485, &adis16485_timeouts), }, [ADIS16488] = { .channels = adis16480_channels, @@ -927,7 +929,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(&adis16485_timeouts), + .adis_data = ADIS16480_DATA(16488, &adis16485_timeouts), }, [ADIS16490] = { .channels = adis16485_channels, @@ -941,7 +943,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(&adis16495_timeouts), + .adis_data = ADIS16480_DATA(16490, &adis16495_timeouts), }, [ADIS16495_1] = { .channels = adis16485_channels, @@ -955,7 +957,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), }, [ADIS16495_2] = { .channels = adis16485_channels, @@ -969,7 +971,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), }, [ADIS16495_3] = { .channels = adis16485_channels, @@ -983,7 +985,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), }, [ADIS16497_1] = { .channels = adis16485_channels, @@ -997,7 +999,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), }, [ADIS16497_2] = { .channels = adis16485_channels, @@ -1011,7 +1013,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), }, [ADIS16497_3] = { .channels = adis16485_channels, @@ -1025,7 +1027,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(&adis16495_1_timeouts), + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), }, }; @@ -1064,40 +1066,6 @@ static int adis16480_enable_irq(struct adis *adis, bool enable) return __adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, val); } -static int adis16480_initial_setup(struct iio_dev *indio_dev) -{ - struct adis16480 *st = iio_priv(indio_dev); - uint16_t prod_id; - unsigned int device_id; - int ret; - - adis_reset(&st->adis); - msleep(70); - - ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1)); - if (ret) - return ret; - msleep(30); - - ret = adis_check_status(&st->adis); - if (ret) - return ret; - - ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id); - if (ret) - return ret; - - ret = sscanf(indio_dev->name, "adis%u\n", &device_id); - if (ret != 1) - return -EINVAL; - - if (prod_id != device_id) - dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", - device_id, prod_id); - - return 0; -} - static int adis16480_config_irq_pin(struct device_node *of_node, struct adis16480 *st) { @@ -1278,18 +1246,22 @@ static int adis16480_probe(struct spi_device *spi) if (ret) return ret; - ret = adis16480_config_irq_pin(spi->dev.of_node, st); + ret = __adis_initial_startup(&st->adis); if (ret) return ret; + ret = adis16480_config_irq_pin(spi->dev.of_node, st); + if (ret) + goto error_stop_device; + ret = adis16480_get_ext_clocks(st); if (ret) - return ret; + goto error_stop_device; if (!IS_ERR_OR_NULL(st->ext_clk)) { ret = adis16480_ext_clk_config(st, spi->dev.of_node, true); if (ret) - return ret; + goto error_stop_device; st->clk_freq = clk_get_rate(st->ext_clk); st->clk_freq *= 1000; /* micro */ @@ -1301,24 +1273,20 @@ static int adis16480_probe(struct spi_device *spi) if (ret) goto error_clk_disable_unprepare; - ret = adis16480_initial_setup(indio_dev); - if (ret) - goto error_cleanup_buffer; - ret = iio_device_register(indio_dev); if (ret) - goto error_stop_device; + goto error_cleanup_buffer; adis16480_debugfs_init(indio_dev); return 0; -error_stop_device: - adis16480_stop_device(indio_dev); error_cleanup_buffer: adis_cleanup_buffer_and_trigger(&st->adis, indio_dev); error_clk_disable_unprepare: clk_disable_unprepare(st->ext_clk); +error_stop_device: + adis16480_stop_device(indio_dev); return ret; } From 62504d1b44ecc58bcf4aa8455e94f8ea1c5b823f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 10 Feb 2020 15:26:06 +0200 Subject: [PATCH 41/98] iio: adis16460: Make use of __adis_initial_startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of the actions done in `adis16460_initial_setup()` are now done in `__adis_initial_startup()` so, there's no need for code duplication. Signed-off-by: Nuno Sá Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16460.c | 38 +++---------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 42fa473c6d81..0027683d0256 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -333,40 +333,6 @@ static int adis16460_enable_irq(struct adis *adis, bool enable) return 0; } -static int adis16460_initial_setup(struct iio_dev *indio_dev) -{ - struct adis16460 *st = iio_priv(indio_dev); - uint16_t prod_id; - unsigned int device_id; - int ret; - - adis_reset(&st->adis); - msleep(222); - - ret = adis_write_reg_16(&st->adis, ADIS16460_REG_GLOB_CMD, BIT(1)); - if (ret) - return ret; - msleep(75); - - ret = adis_check_status(&st->adis); - if (ret) - return ret; - - ret = adis_read_reg_16(&st->adis, ADIS16460_REG_PROD_ID, &prod_id); - if (ret) - return ret; - - ret = sscanf(indio_dev->name, "adis%u\n", &device_id); - if (ret != 1) - return -EINVAL; - - if (prod_id != device_id) - dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", - device_id, prod_id); - - return 0; -} - #define ADIS16460_DIAG_STAT_IN_CLK_OOS 7 #define ADIS16460_DIAG_STAT_FLASH_MEM 6 #define ADIS16460_DIAG_STAT_SELF_TEST 5 @@ -392,6 +358,8 @@ static const struct adis_timeout adis16460_timeouts = { static const struct adis_data adis16460_data = { .diag_stat_reg = ADIS16460_REG_DIAG_STAT, .glob_cmd_reg = ADIS16460_REG_GLOB_CMD, + .prod_id_reg = ADIS16460_REG_PROD_ID, + .prod_id = 16460, .self_test_mask = BIT(2), .self_test_reg = ADIS16460_REG_GLOB_CMD, .has_paging = false, @@ -441,7 +409,7 @@ static int adis16460_probe(struct spi_device *spi) adis16460_enable_irq(&st->adis, 0); - ret = adis16460_initial_setup(indio_dev); + ret = __adis_initial_startup(&st->adis); if (ret) goto error_cleanup_buffer; From 1d8690fe61c9e83a59b74a223ab94de3f889bc74 Mon Sep 17 00:00:00 2001 From: Mircea Caprioru Date: Tue, 11 Feb 2020 10:44:53 +0200 Subject: [PATCH 42/98] iio: adc: ad7124: Add direct reg access This patch adds the posibility do read and write registers from userspace using the kernel debug direct register access option. Signed-off-by: Mircea Caprioru Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index f0206d819fda..a3c0647a5391 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -98,6 +98,14 @@ static const unsigned int ad7124_gain[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; +static const unsigned int ad7124_reg_size[] = { + 1, 2, 3, 3, 2, 1, 3, 3, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 +}; + static const int ad7124_master_clk_freq_hz[3] = { [AD7124_LOW_POWER] = 76800, [AD7124_MID_POWER] = 153600, @@ -427,6 +435,27 @@ static int ad7124_write_raw(struct iio_dev *indio_dev, } } +static int ad7124_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7124_state *st = iio_priv(indio_dev); + int ret; + + if (reg >= ARRAY_SIZE(ad7124_reg_size)) + return -EINVAL; + + if (readval) + ret = ad_sd_read_reg(&st->sd, reg, ad7124_reg_size[reg], + readval); + else + ret = ad_sd_write_reg(&st->sd, reg, ad7124_reg_size[reg], + writeval); + + return ret; +} + static IIO_CONST_ATTR(in_voltage_scale_available, "0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023"); @@ -442,6 +471,7 @@ static const struct attribute_group ad7124_attrs_group = { static const struct iio_info ad7124_info = { .read_raw = ad7124_read_raw, .write_raw = ad7124_write_raw, + .debugfs_reg_access = &ad7124_reg_access, .validate_trigger = ad_sd_validate_trigger, .attrs = &ad7124_attrs_group, }; From ab0afa65bbc7eee9a4f5d864781e949820e0aa2c Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 12 Feb 2020 18:17:17 +0200 Subject: [PATCH 43/98] staging: iio: adc: ad7192: fail probe on get_voltage This patch makes the ad7192_probe fail in case regulator_get_voltage will return an error or voltage is set to 0. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7192.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index bf3e2a9cc07f..41da8b4cdc48 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -899,10 +899,13 @@ static int ad7192_probe(struct spi_device *spi) voltage_uv = regulator_get_voltage(st->avdd); - if (voltage_uv) + if (voltage_uv > 0) { st->int_vref_mv = voltage_uv / 1000; - else + } else { + ret = voltage_uv; dev_err(&spi->dev, "Device tree error, reference voltage undefined\n"); + goto error_disable_avdd; + } spi_set_drvdata(spi, indio_dev); st->devid = spi_get_device_id(spi)->driver_data; From 893ac1a02b19b7a42681f7cfb760dedb3d1611ab Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 12 Feb 2020 18:17:18 +0200 Subject: [PATCH 44/98] staging: iio: adc: ad7192: modify iio_chan_spec array This patch changes the static const struct iio_chan_spec arrays in a way that all the necessary attributes are set at compile time. Now ad7192_channels_config only makes the channels attribute of iio_dev point to the right array depending on the chip. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7192.c | 112 ++++++++++++++++++------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index 41da8b4cdc48..8fca8915543d 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -786,73 +786,93 @@ static const struct iio_info ad7195_info = { .validate_trigger = ad_sd_validate_trigger, }; +#define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _extend_name, \ + _type, _mask_type_av, _ext_info) \ + { \ + .type = (_type), \ + .differential = ((_channel2) == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = (_mask_type_av), \ + .ext_info = (_ext_info), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD719x_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \ + __AD719x_CHANNEL(_si, _channel1, _channel2, _address, NULL, \ + IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE), \ + ad7192_calibsys_ext_info) + +#define AD719x_CHANNEL(_si, _channel1, _address) \ + __AD719x_CHANNEL(_si, _channel1, -1, _address, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) + +#define AD719x_SHORTED_CHANNEL(_si, _channel1, _address) \ + __AD719x_CHANNEL(_si, _channel1, -1, _address, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) + +#define AD719x_TEMP_CHANNEL(_si, _address) \ + __AD719x_CHANNEL(_si, 0, -1, _address, NULL, IIO_TEMP, 0, NULL) + static const struct iio_chan_spec ad7192_channels[] = { - AD_SD_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M, 24, 32, 0), - AD_SD_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M, 24, 32, 0), - AD_SD_TEMP_CHANNEL(2, AD7192_CH_TEMP, 24, 32, 0), - AD_SD_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M, 24, 32, 0), - AD_SD_CHANNEL(4, 1, AD7192_CH_AIN1, 24, 32, 0), - AD_SD_CHANNEL(5, 2, AD7192_CH_AIN2, 24, 32, 0), - AD_SD_CHANNEL(6, 3, AD7192_CH_AIN3, 24, 32, 0), - AD_SD_CHANNEL(7, 4, AD7192_CH_AIN4, 24, 32, 0), + AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M), + AD719x_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M), + AD719x_TEMP_CHANNEL(2, AD7192_CH_TEMP), + AD719x_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M), + AD719x_CHANNEL(4, 1, AD7192_CH_AIN1), + AD719x_CHANNEL(5, 2, AD7192_CH_AIN2), + AD719x_CHANNEL(6, 3, AD7192_CH_AIN3), + AD719x_CHANNEL(7, 4, AD7192_CH_AIN4), IIO_CHAN_SOFT_TIMESTAMP(8), }; static const struct iio_chan_spec ad7193_channels[] = { - AD_SD_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M, 24, 32, 0), - AD_SD_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M, 24, 32, 0), - AD_SD_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M, 24, 32, 0), - AD_SD_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M, 24, 32, 0), - AD_SD_TEMP_CHANNEL(4, AD7193_CH_TEMP, 24, 32, 0), - AD_SD_SHORTED_CHANNEL(5, 2, AD7193_CH_AIN2P_AIN2M, 24, 32, 0), - AD_SD_CHANNEL(6, 1, AD7193_CH_AIN1, 24, 32, 0), - AD_SD_CHANNEL(7, 2, AD7193_CH_AIN2, 24, 32, 0), - AD_SD_CHANNEL(8, 3, AD7193_CH_AIN3, 24, 32, 0), - AD_SD_CHANNEL(9, 4, AD7193_CH_AIN4, 24, 32, 0), - AD_SD_CHANNEL(10, 5, AD7193_CH_AIN5, 24, 32, 0), - AD_SD_CHANNEL(11, 6, AD7193_CH_AIN6, 24, 32, 0), - AD_SD_CHANNEL(12, 7, AD7193_CH_AIN7, 24, 32, 0), - AD_SD_CHANNEL(13, 8, AD7193_CH_AIN8, 24, 32, 0), + AD719x_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M), + AD719x_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M), + AD719x_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M), + AD719x_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M), + AD719x_TEMP_CHANNEL(4, AD7193_CH_TEMP), + AD719x_SHORTED_CHANNEL(5, 2, AD7193_CH_AIN2P_AIN2M), + AD719x_CHANNEL(6, 1, AD7193_CH_AIN1), + AD719x_CHANNEL(7, 2, AD7193_CH_AIN2), + AD719x_CHANNEL(8, 3, AD7193_CH_AIN3), + AD719x_CHANNEL(9, 4, AD7193_CH_AIN4), + AD719x_CHANNEL(10, 5, AD7193_CH_AIN5), + AD719x_CHANNEL(11, 6, AD7193_CH_AIN6), + AD719x_CHANNEL(12, 7, AD7193_CH_AIN7), + AD719x_CHANNEL(13, 8, AD7193_CH_AIN8), IIO_CHAN_SOFT_TIMESTAMP(14), }; static int ad7192_channels_config(struct iio_dev *indio_dev) { struct ad7192_state *st = iio_priv(indio_dev); - const struct iio_chan_spec *channels; - struct iio_chan_spec *chan; - int i; switch (st->devid) { case ID_AD7193: - channels = ad7193_channels; + indio_dev->channels = ad7193_channels; indio_dev->num_channels = ARRAY_SIZE(ad7193_channels); break; default: - channels = ad7192_channels; + indio_dev->channels = ad7192_channels; indio_dev->num_channels = ARRAY_SIZE(ad7192_channels); break; } - chan = devm_kcalloc(indio_dev->dev.parent, indio_dev->num_channels, - sizeof(*chan), GFP_KERNEL); - if (!chan) - return -ENOMEM; - - indio_dev->channels = chan; - - for (i = 0; i < indio_dev->num_channels; i++) { - *chan = channels[i]; - chan->info_mask_shared_by_all |= - BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY); - if (chan->type != IIO_TEMP) { - chan->info_mask_shared_by_type_available |= - BIT(IIO_CHAN_INFO_SCALE); - chan->ext_info = ad7192_calibsys_ext_info; - } - chan++; - } - return 0; } From 66614ab2be38ac292c6f314110bde0be79751b2b Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 12 Feb 2020 18:17:19 +0200 Subject: [PATCH 45/98] staging: iio: adc: ad7192: removed spi_device_id This patch removes spi_device_id table and moves the init data (id of the chip) in the .data field of of_device_id table. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7192.c | 33 +++++++++++--------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index 8fca8915543d..8ec28aa8fa8a 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -876,6 +877,15 @@ static int ad7192_channels_config(struct iio_dev *indio_dev) return 0; } +static const struct of_device_id ad7192_of_match[] = { + { .compatible = "adi,ad7190", .data = (void *)ID_AD7190 }, + { .compatible = "adi,ad7192", .data = (void *)ID_AD7192 }, + { .compatible = "adi,ad7193", .data = (void *)ID_AD7193 }, + { .compatible = "adi,ad7195", .data = (void *)ID_AD7195 }, + {} +}; +MODULE_DEVICE_TABLE(of, ad7192_of_match); + static int ad7192_probe(struct spi_device *spi) { struct ad7192_state *st; @@ -928,7 +938,7 @@ static int ad7192_probe(struct spi_device *spi) } spi_set_drvdata(spi, indio_dev); - st->devid = spi_get_device_id(spi)->driver_data; + st->devid = (unsigned long)of_device_get_match_data(&spi->dev); indio_dev->dev.parent = &spi->dev; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1009,26 +1019,6 @@ static int ad7192_remove(struct spi_device *spi) return 0; } -static const struct spi_device_id ad7192_id[] = { - {"ad7190", ID_AD7190}, - {"ad7192", ID_AD7192}, - {"ad7193", ID_AD7193}, - {"ad7195", ID_AD7195}, - {} -}; - -MODULE_DEVICE_TABLE(spi, ad7192_id); - -static const struct of_device_id ad7192_of_match[] = { - { .compatible = "adi,ad7190" }, - { .compatible = "adi,ad7192" }, - { .compatible = "adi,ad7193" }, - { .compatible = "adi,ad7195" }, - {} -}; - -MODULE_DEVICE_TABLE(of, ad7192_of_match); - static struct spi_driver ad7192_driver = { .driver = { .name = "ad7192", @@ -1036,7 +1026,6 @@ static struct spi_driver ad7192_driver = { }, .probe = ad7192_probe, .remove = ad7192_remove, - .id_table = ad7192_id, }; module_spi_driver(ad7192_driver); From 7e4d75935ddb61b1d2de5dea2dbd951cd1a90263 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 12 Feb 2020 18:17:20 +0200 Subject: [PATCH 46/98] Documentation: ABI: testing: ad7192: update sysfs docs Updated mainline documentation on ad7192 userspace sysfs. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-adc-ad7192 | 24 ++++++++++++++----- .../iio/Documentation/sysfs-bus-iio-ad7192 | 20 ---------------- 2 files changed, 18 insertions(+), 26 deletions(-) delete mode 100644 drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 index 7627d3be08f5..f8315202c8f0 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 @@ -2,17 +2,22 @@ What: /sys/bus/iio/devices/iio:deviceX/ac_excitation_en KernelVersion: Contact: linux-iio@vger.kernel.org Description: - Reading gives the state of AC excitation. - Writing '1' enables AC excitation. + This attribute, if available, is used to enable the AC + excitation mode found on some converters. In ac excitation mode, + the polarity of the excitation voltage is reversed on + alternate cycles, to eliminate DC errors. What: /sys/bus/iio/devices/iio:deviceX/bridge_switch_en KernelVersion: Contact: linux-iio@vger.kernel.org Description: - This bridge switch is used to disconnect it when there is a - need to minimize the system current consumption. - Reading gives the state of the bridge switch. - Writing '1' enables the bridge switch. + This attribute, if available, is used to close or open the + bridge power down switch found on some converters. + In bridge applications, such as strain gauges and load cells, + the bridge itself consumes the majority of the current in the + system. To minimize the current consumption of the system, + the bridge can be disconnected (when it is not being used + using the bridge_switch_en attribute. What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration KernelVersion: @@ -21,6 +26,13 @@ Description: Initiates the system calibration procedure. This is done on a single channel at a time. Write '1' to start the calibration. +What: /sys/bus/iio/devices/iio:deviceX/in_voltage2-voltage2_shorted_raw +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Measure voltage from AIN2 pin connected to AIN(+) + and AIN(-) shorted. + What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode_available KernelVersion: Contact: linux-iio@vger.kernel.org diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 b/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 deleted file mode 100644 index 1c35c507cc05..000000000000 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 +++ /dev/null @@ -1,20 +0,0 @@ -What: /sys/.../iio:deviceX/ac_excitation_en -KernelVersion: 3.1.0 -Contact: linux-iio@vger.kernel.org -Description: - This attribute, if available, is used to enable the AC - excitation mode found on some converters. In ac excitation mode, - the polarity of the excitation voltage is reversed on - alternate cycles, to eliminate DC errors. - -What: /sys/.../iio:deviceX/bridge_switch_en -KernelVersion: 3.1.0 -Contact: linux-iio@vger.kernel.org -Description: - This attribute, if available, is used to close or open the - bridge power down switch found on some converters. - In bridge applications, such as strain gauges and load cells, - the bridge itself consumes the majority of the current in the - system. To minimize the current consumption of the system, - the bridge can be disconnected (when it is not being used - using the bridge_switch_en attribute. From b581f748cce00f4cbd6c76c4f301840403dece7b Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 12 Feb 2020 18:17:21 +0200 Subject: [PATCH 47/98] staging: iio: adc: ad7192: move out of staging Move ad7192 ADC driver out of staging into mainline. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 ++++++++ drivers/iio/adc/Kconfig | 12 ++++++++++++ drivers/iio/adc/Makefile | 1 + drivers/{staging => }/iio/adc/ad7192.c | 0 drivers/staging/iio/adc/Kconfig | 12 ------------ drivers/staging/iio/adc/Makefile | 1 - 6 files changed, 21 insertions(+), 13 deletions(-) rename drivers/{staging => }/iio/adc/ad7192.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 11b7d21eaa6c..8fa40c3eb72a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -931,6 +931,14 @@ S: Supported F: drivers/iio/adc/ad7124.c F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml +ANALOG DEVICES INC AD7192 DRIVER +M: Alexandru Tachici +L: linux-iio@vger.kernel.org +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/adc/ad7192.c +F: Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml + ANALOG DEVICES INC AD7292 DRIVER M: Marcelo Schmitt L: linux-iio@vger.kernel.org diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 82e33082958c..f4da821c4022 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -39,6 +39,18 @@ config AD7124 To compile this driver as a module, choose M here: the module will be called ad7124. +config AD7192 + tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7190, + AD7192, AD7193 or AD7195 SPI analog to digital converters (ADC). + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7192. + config AD7266 tristate "Analog Devices AD7265/AD7266 ADC driver" depends on SPI_MASTER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 919228900df9..8462455b4228 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o obj-$(CONFIG_AD7124) += ad7124.o +obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7292) += ad7292.o diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c similarity index 100% rename from drivers/staging/iio/adc/ad7192.c rename to drivers/iio/adc/ad7192.c diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 31cd9a12f40f..b25f41053fac 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -15,18 +15,6 @@ config AD7816 To compile this driver as a module, choose M here: the module will be called ad7816. -config AD7192 - tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver" - depends on SPI - select AD_SIGMA_DELTA - help - Say yes here to build support for Analog Devices AD7190, - AD7192, AD7193 or AD7195 SPI analog to digital converters (ADC). - If unsure, say N (but it's safe to say "Y"). - - To compile this driver as a module, choose M here: the - module will be called ad7192. - config AD7280 tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System" depends on SPI diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 4b76769b32bc..6436a62b6278 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -4,5 +4,4 @@ # obj-$(CONFIG_AD7816) += ad7816.o -obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7280) += ad7280a.o From 4af495dad54f6d0e5515612eedd81f4a785155c1 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 11 Feb 2020 20:11:55 +0100 Subject: [PATCH 48/98] dt-bindings: iio: light: add support for Dyna-Image AL3320A This commit add dt-bindings support to al3320a driver and vendor-prefix dynaimage. Partly based on unmerged commit: "iio: Add Dyna-Image AP3223 ambient light and proximity driver" Signed-off-by: David Heidelberg Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/dynaimage,al3320a.yaml | 43 +++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/dynaimage,al3320a.yaml diff --git a/Documentation/devicetree/bindings/iio/light/dynaimage,al3320a.yaml b/Documentation/devicetree/bindings/iio/light/dynaimage,al3320a.yaml new file mode 100644 index 000000000000..497300239d93 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/dynaimage,al3320a.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/dynaimage,al3320a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Dyna-Image AL3320A sensor + +maintainers: + - David Heidelberg + +properties: + compatible: + const: dynaimage,al3320a + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: + description: Regulator that provides power to the sensor + +required: + - compatible + - reg + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@1c { + compatible = "dynaimage,al3320a"; + reg = <0x1c>; + vdd-supply = <&vdd_reg>; + interrupts = <0 99 4>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 9e67944bec9c..561768ca874c 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -267,6 +267,8 @@ patternProperties: description: Dragino Technology Co., Limited "^dserve,.*": description: dServe Technology B.V. + "^dynaimage,.*": + description: Dyna-Image "^ea,.*": description: Embedded Artists AB "^ebs-systart,.*": From 2295338b4a3e7276faf6006217a85f17e86aee05 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 11 Feb 2020 20:11:56 +0100 Subject: [PATCH 49/98] dt-bindings: iio: light: add support for Dyna-Image AL3010 The Dyna-Image AL3010 is a 16-bit digital ambient light sensor which provides a multiple gain function with linear response over a dynamic range 1216/4863/19452/77806. Signed-off-by: David Heidelberg Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/dynaimage,al3010.yaml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/dynaimage,al3010.yaml diff --git a/Documentation/devicetree/bindings/iio/light/dynaimage,al3010.yaml b/Documentation/devicetree/bindings/iio/light/dynaimage,al3010.yaml new file mode 100644 index 000000000000..f671edda6641 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/dynaimage,al3010.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/dynaimage,al3010.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Dyna-Image AL3010 sensor + +maintainers: + - David Heidelberg + +properties: + compatible: + const: dynaimage,al3010 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: + description: Regulator that provides power to the sensor + +required: + - compatible + - reg + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@1c { + compatible = "dynaimage,al3010"; + reg = <0x1c>; + vdd-supply = <&vdd_reg>; + interrupts = <0 99 4>; + }; + }; From 58d22a911d9158dea83c3da3e4ee2ab47af5d6cb Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 11 Feb 2020 20:11:57 +0100 Subject: [PATCH 50/98] iio: light: al3320a slightly improve code formatting - modified to be in part with al3010 driver - cleanup using bitfield, no functionality change intended Signed-off-by: David Heidelberg Signed-off-by: Jonathan Cameron --- drivers/iio/light/al3320a.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index a21aa99e74e4..cd3e25dd8153 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -9,9 +9,9 @@ * TODO: interrupt support, thresholds */ -#include -#include +#include #include +#include #include #include @@ -36,8 +36,7 @@ #define AL3320A_CONFIG_DISABLE 0x00 #define AL3320A_CONFIG_ENABLE 0x01 -#define AL3320A_GAIN_SHIFT 1 -#define AL3320A_GAIN_MASK (BIT(2) | BIT(1)) +#define AL3320A_GAIN_MASK GENMASK(2, 1) /* chip params default values */ #define AL3320A_DEFAULT_MEAN_TIME 4 @@ -90,7 +89,8 @@ static int al3320a_init(struct al3320a_data *data) return ret; ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE, - AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT); + FIELD_PREP(AL3320A_GAIN_MASK, + AL3320A_RANGE_3)); if (ret < 0) return ret; @@ -133,7 +133,7 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT; + ret = FIELD_GET(AL3320A_GAIN_MASK, ret); *val = al3320a_scales[ret][0]; *val2 = al3320a_scales[ret][1]; @@ -152,11 +152,13 @@ static int al3320a_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SCALE: for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) { - if (val == al3320a_scales[i][0] && - val2 == al3320a_scales[i][1]) - return i2c_smbus_write_byte_data(data->client, + if (val != al3320a_scales[i][0] || + val2 != al3320a_scales[i][1]) + continue; + + return i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE, - i << AL3320A_GAIN_SHIFT); + FIELD_PREP(AL3320A_GAIN_MASK, i)); } break; } From c36b5195ab7035cc7ee0b79f621a3a9ecd71cf9c Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Fri, 14 Feb 2020 23:09:17 +0100 Subject: [PATCH 51/98] iio: light: add Dyna-Image AL3010 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on: - 3320A in-kernel driver - https://www.spinics.net/lists/linux-iio/msg25145.html - https://lore.kernel.org/patchwork/patch/684179/ I decided to keep it aside of AL3320A due to different approach and much simpler design of 3010. Tested on Nexus 7 2012 (grouper/tilapia). Tested-by: David Heidelberg Tested-by: Dmitry Osipenko Tested-by: Michał Mirosław Reviewed-by: Dmitry Osipenko Signed-off-by: David Heidelberg Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 10 ++ drivers/iio/light/Makefile | 1 + drivers/iio/light/al3010.c | 242 +++++++++++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 drivers/iio/light/al3010.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 307412c79c53..74970f18a93b 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -43,6 +43,16 @@ config ADUX1020 To compile this driver as a module, choose M here: the module will be called adux1020. +config AL3010 + tristate "AL3010 ambient light sensor" + depends on I2C + help + Say Y here if you want to build a driver for the Dyna Image AL3010 + ambient light sensor. + + To compile this driver as a module, choose M here: the + module will be called al3010. + config AL3320A tristate "AL3320A ambient light sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 264a76039006..5c1ebaf578fb 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ACPI_ALS) += acpi-als.o obj-$(CONFIG_ADJD_S311) += adjd_s311.o obj-$(CONFIG_ADUX1020) += adux1020.o +obj-$(CONFIG_AL3010) += al3010.o obj-$(CONFIG_AL3320A) += al3320a.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9960) += apds9960.o diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c new file mode 100644 index 000000000000..b1ed7658cc46 --- /dev/null +++ b/drivers/iio/light/al3010.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AL3010 - Dyna Image Ambient Light Sensor + * + * Copyright (c) 2014, Intel Corporation. + * Copyright (c) 2016, Dyna-Image Corp. + * Copyright (c) 2020, David Heidelberg, Michał Mirosław, Dmitry Osipenko + * + * IIO driver for AL3010 (7-bit I2C slave address 0x1C). + * + * TODO: interrupt support, thresholds + * When the driver will get support for interrupt handling, then interrupt + * will need to be disabled before turning sensor OFF in order to avoid + * potential races with the interrupt handling. + */ + +#include +#include +#include +#include + +#include +#include + +#define AL3010_DRV_NAME "al3010" + +#define AL3010_REG_SYSTEM 0x00 +#define AL3010_REG_DATA_LOW 0x0c +#define AL3010_REG_CONFIG 0x10 + +#define AL3010_CONFIG_DISABLE 0x00 +#define AL3010_CONFIG_ENABLE 0x01 + +#define AL3010_GAIN_MASK GENMASK(6,4) + +#define AL3010_SCALE_AVAILABLE "1.1872 0.2968 0.0742 0.018" + +enum al3xxxx_range { + AL3XXX_RANGE_1, /* 77806 lx */ + AL3XXX_RANGE_2, /* 19542 lx */ + AL3XXX_RANGE_3, /* 4863 lx */ + AL3XXX_RANGE_4 /* 1216 lx */ +}; + +static const int al3010_scales[][2] = { + {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600} +}; + +struct al3010_data { + struct i2c_client *client; +}; + +static const struct iio_chan_spec al3010_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + } +}; + +static IIO_CONST_ATTR(in_illuminance_scale_available, AL3010_SCALE_AVAILABLE); + +static struct attribute *al3010_attributes[] = { + &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group al3010_attribute_group = { + .attrs = al3010_attributes, +}; + +static int al3010_set_pwr(struct i2c_client *client, bool pwr) +{ + u8 val = pwr ? AL3010_CONFIG_ENABLE : AL3010_CONFIG_DISABLE; + return i2c_smbus_write_byte_data(client, AL3010_REG_SYSTEM, val); +} + +static void al3010_set_pwr_off(void *_data) +{ + struct al3010_data *data = _data; + + al3010_set_pwr(data->client, false); +} + +static int al3010_init(struct al3010_data *data) +{ + int ret; + + ret = al3010_set_pwr(data->client, true); + + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG, + FIELD_PREP(AL3010_GAIN_MASK, + AL3XXX_RANGE_3)); + if (ret < 0) + return ret; + + return 0; +} + +static int al3010_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct al3010_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* + * ALS ADC value is stored in two adjacent registers: + * - low byte of output is stored at AL3010_REG_DATA_LOW + * - high byte of output is stored at AL3010_REG_DATA_LOW + 1 + */ + ret = i2c_smbus_read_word_data(data->client, + AL3010_REG_DATA_LOW); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = i2c_smbus_read_byte_data(data->client, + AL3010_REG_CONFIG); + if (ret < 0) + return ret; + + ret = FIELD_GET(AL3010_GAIN_MASK, ret); + *val = al3010_scales[ret][0]; + *val2 = al3010_scales[ret][1]; + + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static int al3010_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct al3010_data *data = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) { + if (val != al3010_scales[i][0] || + val2 != al3010_scales[i][1]) + continue; + + return i2c_smbus_write_byte_data(data->client, + AL3010_REG_CONFIG, + FIELD_PREP(AL3010_GAIN_MASK, i)); + } + break; + } + return -EINVAL; +} + +static const struct iio_info al3010_info = { + .read_raw = al3010_read_raw, + .write_raw = al3010_write_raw, + .attrs = &al3010_attribute_group, +}; + +static int al3010_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct al3010_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &al3010_info; + indio_dev->name = AL3010_DRV_NAME; + indio_dev->channels = al3010_channels; + indio_dev->num_channels = ARRAY_SIZE(al3010_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = al3010_init(data); + if (ret < 0) { + dev_err(&client->dev, "al3010 chip init failed\n"); + return ret; + } + + ret = devm_add_action_or_reset(&client->dev, + al3010_set_pwr_off, + data); + if (ret < 0) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static int __maybe_unused al3010_suspend(struct device *dev) +{ + return al3010_set_pwr(to_i2c_client(dev), false); +} + +static int __maybe_unused al3010_resume(struct device *dev) +{ + return al3010_set_pwr(to_i2c_client(dev), true); +} + +static SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume); + +static const struct i2c_device_id al3010_id[] = { + {"al3010", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, al3010_id); + +static const struct of_device_id al3010_of_match[] = { + { .compatible = "dynaimage,al3010", }, + {}, +}; +MODULE_DEVICE_TABLE(of, al3010_of_match); + +static struct i2c_driver al3010_driver = { + .driver = { + .name = AL3010_DRV_NAME, + .of_match_table = al3010_of_match, + .pm = &al3010_pm_ops, + }, + .probe = al3010_probe, + .id_table = al3010_id, +}; +module_i2c_driver(al3010_driver); + +MODULE_AUTHOR("Daniel Baluta "); +MODULE_AUTHOR("David Heidelberg "); +MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver"); +MODULE_LICENSE("GPL v2"); From c39a05e35263a2c8abea81df40b42aef33d1a759 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 11 Feb 2020 20:11:59 +0100 Subject: [PATCH 52/98] iio: light: al3320a implement suspend support AL3320a is fairly simple chip, so for suspend is enough to disable and later enable it again. Signed-off-by: David Heidelberg Signed-off-by: Jonathan Cameron --- drivers/iio/light/al3320a.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index cd3e25dd8153..2659a352b55e 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -7,6 +7,9 @@ * IIO driver for AL3320A (7-bit I2C slave address 0x1C). * * TODO: interrupt support, thresholds + * When the driver will get support for interrupt handling, then interrupt + * will need to be disabled before turning sensor OFF in order to avoid + * potential races with the interrupt handling. */ #include @@ -78,13 +81,18 @@ static const struct attribute_group al3320a_attribute_group = { .attrs = al3320a_attributes, }; +static int al3320a_set_pwr(struct i2c_client *client, bool pwr) +{ + u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE; + return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val); +} + static int al3320a_init(struct al3320a_data *data) { int ret; - /* power on */ - ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG, - AL3320A_CONFIG_ENABLE); + ret = al3320a_set_pwr(data->client, true); + if (ret < 0) return ret; @@ -203,10 +211,21 @@ static int al3320a_probe(struct i2c_client *client, static int al3320a_remove(struct i2c_client *client) { - return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, - AL3320A_CONFIG_DISABLE); + return al3320a_set_pwr(client, false); } +static int __maybe_unused al3320a_suspend(struct device *dev) +{ + return al3320a_set_pwr(to_i2c_client(dev), false); +} + +static int __maybe_unused al3320a_resume(struct device *dev) +{ + return al3320a_set_pwr(to_i2c_client(dev), true); +} + +static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume); + static const struct i2c_device_id al3320a_id[] = { {"al3320a", 0}, {} @@ -216,6 +235,7 @@ MODULE_DEVICE_TABLE(i2c, al3320a_id); static struct i2c_driver al3320a_driver = { .driver = { .name = AL3320A_DRV_NAME, + .pm = &al3320a_pm_ops, }, .probe = al3320a_probe, .remove = al3320a_remove, From 3a271275fa747bafc931ddd872e51d0f3cb15828 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 11 Feb 2020 20:12:00 +0100 Subject: [PATCH 53/98] iio: light: al3320a implement devm_add_action_or_reset Use devm_add_action_or_reset to automatically disable the device and allow you to get rid of the remove function entirely. Signed-off-by: David Heidelberg Signed-off-by: Jonathan Cameron --- drivers/iio/light/al3320a.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index 2659a352b55e..8cec6841cefa 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -87,6 +87,13 @@ static int al3320a_set_pwr(struct i2c_client *client, bool pwr) return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val); } +static void al3320a_set_pwr_off(void *_data) +{ + struct al3320a_data *data = _data; + + al3320a_set_pwr(data->client, false); +} + static int al3320a_init(struct al3320a_data *data) { int ret; @@ -206,12 +213,14 @@ static int al3320a_probe(struct i2c_client *client, dev_err(&client->dev, "al3320a chip init failed\n"); return ret; } - return devm_iio_device_register(&client->dev, indio_dev); -} -static int al3320a_remove(struct i2c_client *client) -{ - return al3320a_set_pwr(client, false); + ret = devm_add_action_or_reset(&client->dev, + al3320a_set_pwr_off, + data); + if (ret < 0) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); } static int __maybe_unused al3320a_suspend(struct device *dev) @@ -238,7 +247,6 @@ static struct i2c_driver al3320a_driver = { .pm = &al3320a_pm_ops, }, .probe = al3320a_probe, - .remove = al3320a_remove, .id_table = al3320a_id, }; From 9c088c020d8181a0c8376444b740c99366eec04d Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Tue, 11 Feb 2020 20:12:01 +0100 Subject: [PATCH 54/98] iio: light: al3320a allow module autoload and polish - allow autoloading when build as module and defined inside DT Signed-off-by: David Heidelberg Signed-off-by: Jonathan Cameron --- drivers/iio/light/al3320a.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index 8cec6841cefa..20ed0a73c390 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -241,9 +242,16 @@ static const struct i2c_device_id al3320a_id[] = { }; MODULE_DEVICE_TABLE(i2c, al3320a_id); +static const struct of_device_id al3320a_of_match[] = { + { .compatible = "dynaimage,al3320a", }, + {}, +}; +MODULE_DEVICE_TABLE(of, al3320a_of_match); + static struct i2c_driver al3320a_driver = { .driver = { .name = AL3320A_DRV_NAME, + .of_match_table = al3320a_of_match, .pm = &al3320a_pm_ops, }, .probe = al3320a_probe, From 3192ade7b6f6306b66139109e0d455f98a7f695f Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Fri, 14 Feb 2020 17:23:57 +0100 Subject: [PATCH 55/98] iio: trigger: stm32-timer: enable clock when in master mode Clock should be enabled as soon as using master modes, even before enabling timer. Or, this may provoke bad behavior on the other end (slave timer). Then, introduce 'clk_enabled' flag, instead of relying on CR1 EN bit, to keep track of clock being enabled (balanced refcount). Propagate this anywhere else in the driver. Also add 'remove' routine to stop timer and disable clock in case it has been left enabled. Enforce the user interface has been unregistered in the remove routine, before disabling the hardware to avoid possible race. So, remove use of devm_ variant to register triggers and unregister them before the hardware gets disabled [1]. [1] https://patchwork.kernel.org/patch/9956247/ Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/stm32-timer-trigger.c | 98 ++++++++++++++++++----- 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 2e0d32aa8436..16a3b6bed7b6 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -79,10 +79,13 @@ struct stm32_timer_trigger { struct device *dev; struct regmap *regmap; struct clk *clk; + bool clk_enabled; u32 max_arr; const void *triggers; const void *valids; bool has_trgo2; + struct mutex lock; /* concurrent sysfs configuration */ + struct list_head tr_list; }; struct stm32_timer_trigger_cfg { @@ -106,7 +109,7 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, { unsigned long long prd, div; int prescaler = 0; - u32 ccer, cr1; + u32 ccer; /* Period and prescaler values depends of clock rate */ div = (unsigned long long)clk_get_rate(priv->clk); @@ -136,9 +139,11 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, if (ccer & TIM_CCER_CCXE) return -EBUSY; - regmap_read(priv->regmap, TIM_CR1, &cr1); - if (!(cr1 & TIM_CR1_CEN)) + mutex_lock(&priv->lock); + if (!priv->clk_enabled) { + priv->clk_enabled = true; clk_enable(priv->clk); + } regmap_write(priv->regmap, TIM_PSC, prescaler); regmap_write(priv->regmap, TIM_ARR, prd - 1); @@ -157,22 +162,20 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, /* Enable controller */ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); + mutex_unlock(&priv->lock); return 0; } static void stm32_timer_stop(struct stm32_timer_trigger *priv) { - u32 ccer, cr1; + u32 ccer; regmap_read(priv->regmap, TIM_CCER, &ccer); if (ccer & TIM_CCER_CCXE) return; - regmap_read(priv->regmap, TIM_CR1, &cr1); - if (cr1 & TIM_CR1_CEN) - clk_disable(priv->clk); - + mutex_lock(&priv->lock); /* Stop timer */ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); @@ -181,6 +184,12 @@ static void stm32_timer_stop(struct stm32_timer_trigger *priv) /* Make sure that registers are updated */ regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); + + if (priv->clk_enabled) { + priv->clk_enabled = false; + clk_disable(priv->clk); + } + mutex_unlock(&priv->lock); } static ssize_t stm32_tt_store_frequency(struct device *dev, @@ -295,8 +304,15 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, for (i = 0; i <= master_mode_max; i++) { if (!strncmp(master_mode_table[i], buf, strlen(master_mode_table[i]))) { + mutex_lock(&priv->lock); + if (!priv->clk_enabled) { + /* Clock should be enabled first */ + priv->clk_enabled = true; + clk_enable(priv->clk); + } regmap_update_bits(priv->regmap, TIM_CR2, mask, i << shift); + mutex_unlock(&priv->lock); return len; } } @@ -354,11 +370,21 @@ static const struct attribute_group *stm32_trigger_attr_groups[] = { static const struct iio_trigger_ops timer_trigger_ops = { }; -static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) +static void stm32_unregister_iio_triggers(struct stm32_timer_trigger *priv) +{ + struct iio_trigger *tr; + + list_for_each_entry(tr, &priv->tr_list, alloc_list) + iio_trigger_unregister(tr); +} + +static int stm32_register_iio_triggers(struct stm32_timer_trigger *priv) { int ret; const char * const *cur = priv->triggers; + INIT_LIST_HEAD(&priv->tr_list); + while (cur && *cur) { struct iio_trigger *trig; bool cur_is_trgo = stm32_timer_is_trgo_name(*cur); @@ -385,9 +411,13 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) iio_trigger_set_drvdata(trig, priv); - ret = devm_iio_trigger_register(priv->dev, trig); - if (ret) + ret = iio_trigger_register(trig); + if (ret) { + stm32_unregister_iio_triggers(priv); return ret; + } + + list_add_tail(&trig->alloc_list, &priv->tr_list); cur++; } @@ -434,7 +464,6 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); - u32 dat; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -445,19 +474,23 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, return -EINVAL; case IIO_CHAN_INFO_ENABLE: + mutex_lock(&priv->lock); if (val) { - regmap_read(priv->regmap, TIM_CR1, &dat); - if (!(dat & TIM_CR1_CEN)) + if (!priv->clk_enabled) { + priv->clk_enabled = true; clk_enable(priv->clk); + } regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); } else { - regmap_read(priv->regmap, TIM_CR1, &dat); regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); - if (dat & TIM_CR1_CEN) + if (priv->clk_enabled) { + priv->clk_enabled = false; clk_disable(priv->clk); + } } + mutex_unlock(&priv->lock); return 0; } @@ -553,7 +586,6 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, { struct stm32_timer_trigger *priv = iio_priv(indio_dev); int sms = stm32_enable_mode2sms(mode); - u32 val; if (sms < 0) return sms; @@ -561,11 +593,12 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, * Triggered mode sets CEN bit automatically by hardware. So, first * enable counter clock, so it can use it. Keeps it in sync with CEN. */ - if (sms == 6) { - regmap_read(priv->regmap, TIM_CR1, &val); - if (!(val & TIM_CR1_CEN)) - clk_enable(priv->clk); + mutex_lock(&priv->lock); + if (sms == 6 && !priv->clk_enabled) { + clk_enable(priv->clk); + priv->clk_enabled = true; } + mutex_unlock(&priv->lock); regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); @@ -749,8 +782,9 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) priv->triggers = triggers_table[index]; priv->valids = cfg->valids_table[index]; stm32_timer_detect_trgo2(priv); + mutex_init(&priv->lock); - ret = stm32_setup_iio_triggers(priv); + ret = stm32_register_iio_triggers(priv); if (ret) return ret; @@ -759,6 +793,25 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) return 0; } +static int stm32_timer_trigger_remove(struct platform_device *pdev) +{ + struct stm32_timer_trigger *priv = platform_get_drvdata(pdev); + u32 val; + + /* Unregister triggers before everything can be safely turned off */ + stm32_unregister_iio_triggers(priv); + + /* Check if nobody else use the timer, then disable it */ + regmap_read(priv->regmap, TIM_CCER, &val); + if (!(val & TIM_CCER_CCXE)) + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + + if (priv->clk_enabled) + clk_disable(priv->clk); + + return 0; +} + static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = { .valids_table = valids_table, .num_valids_table = ARRAY_SIZE(valids_table), @@ -783,6 +836,7 @@ MODULE_DEVICE_TABLE(of, stm32_trig_of_match); static struct platform_driver stm32_timer_trigger_driver = { .probe = stm32_timer_trigger_probe, + .remove = stm32_timer_trigger_remove, .driver = { .name = "stm32-timer-trigger", .of_match_table = stm32_trig_of_match, From e3d2d8ece5f01852e4a377cf3fa228d2fd1482cc Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 8 Feb 2020 17:23:12 +0000 Subject: [PATCH 56/98] dt-bindings: iio: adc: max1363 etc i2c ADC binding conversion Split the binding in two to reflect the threshold monitor capabilities and hence interrupts vs the more straight forward parts that don't have this facility. Signed-off-by: Jonathan Cameron Reviewed-by: Rob Herring --- .../devicetree/bindings/iio/adc/max1363.txt | 63 --------------- .../bindings/iio/adc/maxim,max1238.yaml | 76 +++++++++++++++++++ .../bindings/iio/adc/maxim,max1363.yaml | 50 ++++++++++++ 3 files changed, 126 insertions(+), 63 deletions(-) delete mode 100644 Documentation/devicetree/bindings/iio/adc/max1363.txt create mode 100644 Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml create mode 100644 Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/max1363.txt b/Documentation/devicetree/bindings/iio/adc/max1363.txt deleted file mode 100644 index 94a9011dd860..000000000000 --- a/Documentation/devicetree/bindings/iio/adc/max1363.txt +++ /dev/null @@ -1,63 +0,0 @@ -* Maxim 1x3x/136x/116xx Analog to Digital Converter (ADC) - -The node for this driver must be a child node of a I2C controller, hence -all mandatory properties for your controller must be specified. See directory: - - Documentation/devicetree/bindings/i2c - -for more details. - -Required properties: - - compatible: Should be one of - "maxim,max1361" - "maxim,max1362" - "maxim,max1363" - "maxim,max1364" - "maxim,max1036" - "maxim,max1037" - "maxim,max1038" - "maxim,max1039" - "maxim,max1136" - "maxim,max1137" - "maxim,max1138" - "maxim,max1139" - "maxim,max1236" - "maxim,max1237" - "maxim,max1238" - "maxim,max1239" - "maxim,max11600" - "maxim,max11601" - "maxim,max11602" - "maxim,max11603" - "maxim,max11604" - "maxim,max11605" - "maxim,max11606" - "maxim,max11607" - "maxim,max11608" - "maxim,max11609" - "maxim,max11610" - "maxim,max11611" - "maxim,max11612" - "maxim,max11613" - "maxim,max11614" - "maxim,max11615" - "maxim,max11616" - "maxim,max11617" - "maxim,max11644" - "maxim,max11645" - "maxim,max11646" - "maxim,max11647" - - reg: Should contain the ADC I2C address - -Optional properties: - - vcc-supply: phandle to the regulator that provides power to the ADC. - - vref-supply: phandle to the regulator for ADC reference voltage. - - interrupts: IRQ line for the ADC. If not used the driver will use - polling. - -Example: -adc: max11644@36 { - compatible = "maxim,max11644"; - reg = <0x36>; - vref-supply = <&adc_vref>; -}; diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml new file mode 100644 index 000000000000..a0ebb4680140 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/maxim,max1238.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX1238 and similar ADCs + +maintainers: + - Jonathan Cameron + +description: | + Family of simple ADCs with i2c inteface and internal references. + +properties: + compatible: + enum: + - maxim,max1036 + - maxim,max1037 + - maxim,max1038 + - maxim,max1039 + - maxim,max1136 + - maxim,max1137 + - maxim,max1138 + - maxim,max1139 + - maxim,max1236 + - maxim,max1237 + - maxim,max1238 + - maxim,max1239 + - maxim,max11600 + - maxim,max11601 + - maxim,max11602 + - maxim,max11603 + - maxim,max11604 + - maxim,max11605 + - maxim,max11606 + - maxim,max11607 + - maxim,max11608 + - maxim,max11609 + - maxim,max11610 + - maxim,max11611 + - maxim,max11612 + - maxim,max11613 + - maxim,max11614 + - maxim,max11615 + - maxim,max11616 + - maxim,max11617 + - maxim,max11644 + - maxim,max11645 + - maxim,max11646 + - maxim,max11647 + + reg: + maxItems: 1 + + vcc-supply: true + vref-supply: + description: Optional external reference. If not supplied, internal + reference will be used. + +required: + - compatible + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@36 { + compatible = "maxim,max1238"; + reg = <0x36>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml new file mode 100644 index 000000000000..48377549c39a --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/maxim,max1363.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX1363 and similar ADCs + +maintainers: + - Jonathan Cameron + +description: | + Family of ADCs with i2c inteface, internal references and threshold + monitoring. + +properties: + compatible: + enum: + - maxim,max1361 + - maxim,max1362 + - maxim,max1363 + - maxim,max1364 + + reg: + maxItems: 1 + + vcc-supply: true + vref-supply: + description: Optional external reference. If not supplied, internal + reference will be used. + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@36 { + compatible = "maxim,max1363"; + reg = <0x36>; + }; + }; +... From ddfd781dc4d851e5cfc114b64219d05796f0e809 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:46 +0100 Subject: [PATCH 57/98] iio: imu: inv_mpu6050: enable i2c aux mux bypass only once i2c auxiliary mux is done by analog switches. You do not need to set them for every i2c transfer. Just set i2c bypass bit at init and do noting in i2c de/select. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 71 +++++++++-------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 1363d3776523..24df880248f2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -20,38 +20,6 @@ static const struct regmap_config inv_mpu_regmap_config = { static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct iio_dev *indio_dev = i2c_mux_priv(muxc); - struct inv_mpu6050_state *st = iio_priv(indio_dev); - int ret; - - mutex_lock(&st->lock); - - ret = inv_mpu6050_set_power_itg(st, true); - if (ret) - goto error_unlock; - - ret = regmap_write(st->map, st->reg->int_pin_cfg, - st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); - -error_unlock: - mutex_unlock(&st->lock); - - return ret; -} - -static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) -{ - struct iio_dev *indio_dev = i2c_mux_priv(muxc); - struct inv_mpu6050_state *st = iio_priv(indio_dev); - - mutex_lock(&st->lock); - - /* It doesn't really matter if any of the calls fail */ - regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask); - inv_mpu6050_set_power_itg(st, false); - - mutex_unlock(&st->lock); - return 0; } @@ -79,19 +47,20 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) } } -/* - * MPU9xxx magnetometer support requires to disable i2c auxiliary bus support. - * To ensure backward compatibility with existing setups, do not disable - * i2c auxiliary bus if it used. - * Check for i2c-gate node in devicetree and set magnetometer disabled. - * Only MPU6500 is supported by ACPI, no need to check. - */ -static int inv_mpu_magn_disable(struct iio_dev *indio_dev) +static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev) { struct inv_mpu6050_state *st = iio_priv(indio_dev); struct device *dev = indio_dev->dev.parent; struct device_node *mux_node; + int ret; + /* + * MPU9xxx magnetometer support requires to disable i2c auxiliary bus. + * To ensure backward compatibility with existing setups, do not disable + * i2c auxiliary bus if it used. + * Check for i2c-gate node in devicetree and set magnetometer disabled. + * Only MPU6500 is supported by ACPI, no need to check. + */ switch (st->chip_type) { case INV_MPU9150: case INV_MPU9250: @@ -107,7 +76,24 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev) break; } + /* enable i2c bypass when using i2c auxiliary bus */ + if (inv_mpu_i2c_aux_bus(dev)) { + ret = inv_mpu6050_set_power_itg(st, true); + if (ret) + return ret; + ret = regmap_write(st->map, st->reg->int_pin_cfg, + st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); + if (ret) + goto error; + ret = inv_mpu6050_set_power_itg(st, false); + if (ret) + goto error; + } + return 0; +error: + inv_mpu6050_set_power_itg(st, false); + return ret; } /** @@ -151,7 +137,7 @@ static int inv_mpu_probe(struct i2c_client *client, } result = inv_mpu_core_probe(regmap, client->irq, name, - inv_mpu_magn_disable, chip_type); + inv_mpu_i2c_aux_setup, chip_type); if (result < 0) return result; @@ -160,8 +146,7 @@ static int inv_mpu_probe(struct i2c_client *client, /* declare i2c auxiliary bus */ st->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, - inv_mpu6050_select_bypass, - inv_mpu6050_deselect_bypass); + inv_mpu6050_select_bypass, NULL); if (!st->muxc) return -ENOMEM; st->muxc->priv = dev_get_drvdata(&client->dev); From 14c046ed17111b50aa9c5d8bd25d742421ac843c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:47 +0100 Subject: [PATCH 58/98] iio: imu: inv_mpu6050: delete useless check If we are here it means we have fifo enabled for 1 sensor at least. And interrupt is always required for using trigger. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index f9fdf4302a91..d7397705974e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -123,14 +123,11 @@ int inv_reset_fifo(struct iio_dev *indio_dev) goto reset_fifo_fail; /* enable interrupt */ - if (st->chip_config.accl_fifo_enable || - st->chip_config.gyro_fifo_enable || - st->chip_config.magn_fifo_enable) { - result = regmap_write(st->map, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); - if (result) - return result; - } + result = regmap_write(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN); + if (result) + return result; + /* enable FIFO reading */ d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN; result = regmap_write(st->map, st->reg->user_ctrl, d); From a3aaf7770a8887cedf216becc5392e481ee1fcaa Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:48 +0100 Subject: [PATCH 59/98] iio: imu: inv_mpu6050: set power on/off only once during all init This way there is no need anymore to export the power function to i2c and spi modules. Bus setup is done inside init when power is on and the result is now checked. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 41 ++++++++++++---------- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 11 +----- drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 10 +----- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index c4db9086775c..0b06d6aa6469 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -301,7 +301,6 @@ int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) return 0; } -EXPORT_SYMBOL_GPL(inv_mpu6050_set_power_itg); static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st, enum inv_mpu6050_fsr_e val) @@ -371,27 +370,23 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) u8 d; struct inv_mpu6050_state *st = iio_priv(indio_dev); - result = inv_mpu6050_set_power_itg(st, true); + result = inv_mpu6050_set_gyro_fsr(st, INV_MPU6050_FSR_2000DPS); if (result) return result; - result = inv_mpu6050_set_gyro_fsr(st, INV_MPU6050_FSR_2000DPS); - if (result) - goto error_power_off; - result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ); if (result) - goto error_power_off; + return result; d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE); result = regmap_write(st->map, st->reg->sample_rate_div, d); if (result) - goto error_power_off; + return result; d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); result = regmap_write(st->map, st->reg->accl_config, d); if (result) - goto error_power_off; + return result; result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask); if (result) @@ -410,13 +405,9 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) /* magn chip init, noop if not present in the chip */ result = inv_mpu_magn_probe(st); if (result) - goto error_power_off; + return result; - return inv_mpu6050_set_power_itg(st, false); - -error_power_off: - inv_mpu6050_set_power_itg(st, false); - return result; + return 0; } static int inv_mpu6050_sensor_set(struct inv_mpu6050_state *st, int reg, @@ -1176,7 +1167,7 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) if (result) goto error_power_off; - return inv_mpu6050_set_power_itg(st, false); + return 0; error_power_off: inv_mpu6050_set_power_itg(st, false); @@ -1341,7 +1332,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, result = inv_mpu6050_init_config(indio_dev); if (result) { dev_err(dev, "Could not initialize device.\n"); - return result; + goto error_power_off; } dev_set_drvdata(dev, indio_dev); @@ -1353,8 +1344,16 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, indio_dev->name = dev_name(dev); /* requires parent device set in indio_dev */ - if (inv_mpu_bus_setup) - inv_mpu_bus_setup(indio_dev); + if (inv_mpu_bus_setup) { + result = inv_mpu_bus_setup(indio_dev); + if (result) + goto error_power_off; + } + + /* chip init is done, turning off */ + result = inv_mpu6050_set_power_itg(st, false); + if (result) + return result; switch (chip_type) { case INV_MPU9150: @@ -1413,6 +1412,10 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, } return 0; + +error_power_off: + inv_mpu6050_set_power_itg(st, false); + return result; } EXPORT_SYMBOL_GPL(inv_mpu_core_probe); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 24df880248f2..6993d3b87bb0 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -78,22 +78,13 @@ static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev) /* enable i2c bypass when using i2c auxiliary bus */ if (inv_mpu_i2c_aux_bus(dev)) { - ret = inv_mpu6050_set_power_itg(st, true); - if (ret) - return ret; ret = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); if (ret) - goto error; - ret = inv_mpu6050_set_power_itg(st, false); - if (ret) - goto error; + return ret; } return 0; -error: - inv_mpu6050_set_power_itg(st, false); - return ret; } /** diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index bc351dd58c53..673b198e6368 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -21,10 +21,6 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) struct inv_mpu6050_state *st = iio_priv(indio_dev); int ret = 0; - ret = inv_mpu6050_set_power_itg(st, true); - if (ret) - return ret; - if (st->reg->i2c_if) { ret = regmap_write(st->map, st->reg->i2c_if, INV_ICM20602_BIT_I2C_IF_DIS); @@ -33,12 +29,8 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) ret = regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); } - if (ret) { - inv_mpu6050_set_power_itg(st, false); - return ret; - } - return inv_mpu6050_set_power_itg(st, false); + return ret; } static int inv_mpu_probe(struct spi_device *spi) From 3c1024aa99c3210ddddf5f2fefd99dddfc5c887c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:49 +0100 Subject: [PATCH 60/98] iio: imu: inv_mpu6050: simplify polling magnetometer Do not change the sampling rate value. Let userspace decide what is the sampling rate to use. Read only the requested axis. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 34 +++++++++------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 4f192352521e..607104a2631e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -319,36 +319,36 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val) { unsigned int user_ctrl, status; - __be16 data[3]; + __be16 data; uint8_t addr; - uint8_t d; - unsigned int period_ms; + unsigned int freq_hz, period_ms; int ret; /* quit if chip is not supported */ if (!inv_magn_supported(st)) return -ENODEV; - /* Mag data: X - Y - Z */ + /* Mag data: XH,XL,YH,YL,ZH,ZL */ switch (axis) { case IIO_MOD_X: addr = 0; break; case IIO_MOD_Y: - addr = 1; + addr = 2; break; case IIO_MOD_Z: - addr = 2; + addr = 4; break; default: return -EINVAL; } + addr += INV_MPU6050_REG_EXT_SENS_DATA; - /* set sample rate to max mag freq */ - d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU_MAGN_FREQ_HZ_MAX); - ret = regmap_write(st->map, st->reg->sample_rate_div, d); - if (ret) - return ret; + /* compute period depending on current sampling rate */ + freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); + if (freq_hz > INV_MPU_MAGN_FREQ_HZ_MAX) + freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX; + period_ms = 1000 / freq_hz; /* start i2c master, wait for xfer, stop */ user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN; @@ -357,19 +357,12 @@ int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val) return ret; /* need to wait 2 periods + half-period margin */ - period_ms = 1000 / INV_MPU_MAGN_FREQ_HZ_MAX; msleep(period_ms * 2 + period_ms / 2); user_ctrl = st->chip_config.user_ctrl; ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); if (ret) return ret; - /* restore sample rate */ - d = st->chip_config.divider; - ret = regmap_write(st->map, st->reg->sample_rate_div, d); - if (ret) - return ret; - /* check i2c status and read raw data */ ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); if (ret) @@ -379,12 +372,11 @@ int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val) status & INV_MPU6050_BIT_I2C_SLV1_NACK) return -EIO; - ret = regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, - data, sizeof(data)); + ret = regmap_bulk_read(st->map, addr, &data, sizeof(data)); if (ret) return ret; - *val = (int16_t)be16_to_cpu(data[addr]); + *val = (int16_t)be16_to_cpu(data); return IIO_VAL_INT; } From 5621a63a017f665225b14ecd728c36e63ada710b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:50 +0100 Subject: [PATCH 61/98] iio: imu: inv_mpu6050: early init of chip_config for use at setup Init chip_config early and use its values for initial setup. More coherent, prevent possible mistakes. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 15 +++++++-------- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 1 - 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 0b06d6aa6469..85872e55154f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -101,7 +101,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = { static const struct inv_mpu6050_chip_config chip_config_6050 = { .fsr = INV_MPU6050_FSR_2000DPS, .lpf = INV_MPU6050_FILTER_20HZ, - .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE), + .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(50), .gyro_fifo_enable = false, .accl_fifo_enable = false, .temp_fifo_enable = false, @@ -370,20 +370,20 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) u8 d; struct inv_mpu6050_state *st = iio_priv(indio_dev); - result = inv_mpu6050_set_gyro_fsr(st, INV_MPU6050_FSR_2000DPS); + result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr); if (result) return result; - result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ); + result = inv_mpu6050_set_lpf_regs(st, st->chip_config.lpf); if (result) return result; - d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE); + d = st->chip_config.divider; result = regmap_write(st->map, st->reg->sample_rate_div, d); if (result) return result; - d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); + d = (st->chip_config.accl_fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); result = regmap_write(st->map, st->reg->accl_config, d); if (result) return result; @@ -392,9 +392,6 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) if (result) return result; - memcpy(&st->chip_config, hw_info[st->chip_type].config, - sizeof(struct inv_mpu6050_chip_config)); - /* * Internal chip period is 1ms (1kHz). * Let's use at the beginning the theorical value before measuring @@ -1116,6 +1113,8 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) st->hw = &hw_info[st->chip_type]; st->reg = hw_info[st->chip_type].reg; + memcpy(&st->chip_config, hw_info[st->chip_type].config, + sizeof(st->chip_config)); /* check chip self-identification */ result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, ®val); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 9a81098a8b4d..d5edf903c076 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -321,7 +321,6 @@ struct inv_mpu6050_state { #define INV_MPU6050_TS_PERIOD_JITTER 4 /* init parameters */ -#define INV_MPU6050_INIT_FIFO_RATE 50 #define INV_MPU6050_MAX_FIFO_RATE 1000 #define INV_MPU6050_MIN_FIFO_RATE 4 From c1bfe9c81812c94b9816537995e12e0406ec7420 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:51 +0100 Subject: [PATCH 62/98] iio: imu: inv_mpu6050: add all signal path resets at init Old chips using spi require for a full reset to manually reset all signal path. This does not harm when using i2c so do it inconditionally. Exclude i2c only chips. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 18 ++++++++++++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 85872e55154f..3502b996671c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -1146,6 +1146,24 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) if (result) return result; msleep(INV_MPU6050_POWER_UP_TIME); + switch (st->chip_type) { + case INV_MPU6000: + case INV_MPU6500: + case INV_MPU6515: + case INV_MPU9250: + case INV_MPU9255: + /* reset signal path (required for spi connection) */ + regval = INV_MPU6050_BIT_TEMP_RST | INV_MPU6050_BIT_ACCEL_RST | + INV_MPU6050_BIT_GYRO_RST; + result = regmap_write(st->map, INV_MPU6050_REG_SIGNAL_PATH_RESET, + regval); + if (result) + return result; + msleep(INV_MPU6050_POWER_UP_TIME); + break; + default: + break; + } /* * Turn power on. After reset, the sleep bit could be on diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index d5edf903c076..17f1f6a15f95 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -245,7 +245,13 @@ struct inv_mpu6050_state { #define INV_MPU6050_BIT_I2C_SLV3_DLY_EN 0x08 #define INV_MPU6050_BIT_DELAY_ES_SHADOW 0x80 +#define INV_MPU6050_REG_SIGNAL_PATH_RESET 0x68 +#define INV_MPU6050_BIT_TEMP_RST BIT(0) +#define INV_MPU6050_BIT_ACCEL_RST BIT(1) +#define INV_MPU6050_BIT_GYRO_RST BIT(2) + #define INV_MPU6050_REG_USER_CTRL 0x6A +#define INV_MPU6050_BIT_SIG_COND_RST 0x01 #define INV_MPU6050_BIT_FIFO_RST 0x04 #define INV_MPU6050_BIT_DMP_RST 0x08 #define INV_MPU6050_BIT_I2C_MST_EN 0x20 From 398da9942359efad600b9efb33e6573fedc9276d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:52 +0100 Subject: [PATCH 63/98] iio: imu: inv_mpu6050: reduce sleep time when turning regulators on Turning vdd regulator on requires a consequent sleep for the chip to power on correctly. Turning vddio regulator is much faster. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 3502b996671c..63cdde20df7e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -1201,7 +1201,7 @@ static int inv_mpu_core_enable_regulator_vddio(struct inv_mpu6050_state *st) "Failed to enable vddio regulator: %d\n", result); } else { /* Give the device a little bit of time to start up. */ - usleep_range(35000, 70000); + usleep_range(3000, 5000); } return result; @@ -1321,6 +1321,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, dev_err(dev, "Failed to enable vdd regulator: %d\n", result); return result; } + msleep(INV_MPU6050_POWER_UP_TIME); result = inv_mpu_core_enable_regulator_vddio(st); if (result) { From 92e7407ab1bc5024eb467d5788aace0ceff78a9d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:53 +0100 Subject: [PATCH 64/98] iio: imu: inv_mpu6050: rewrite power and engine management Rewrite clock management to use automatic clock switching present since MPU6500. Sensors engine management can now turn on or off a batch of sensors which simplifies usage a lot. Temperature sensor is now turned on/off depending on usage. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 264 +++++++++++++----- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 24 +- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 12 +- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c | 91 +++--- 5 files changed, 262 insertions(+), 131 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 63cdde20df7e..a51efc4c941b 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -99,9 +99,31 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = { }; static const struct inv_mpu6050_chip_config chip_config_6050 = { + .clk = INV_CLK_INTERNAL, .fsr = INV_MPU6050_FSR_2000DPS, .lpf = INV_MPU6050_FILTER_20HZ, .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(50), + .gyro_en = true, + .accl_en = true, + .temp_en = true, + .magn_en = false, + .gyro_fifo_enable = false, + .accl_fifo_enable = false, + .temp_fifo_enable = false, + .magn_fifo_enable = false, + .accl_fs = INV_MPU6050_FS_02G, + .user_ctrl = 0, +}; + +static const struct inv_mpu6050_chip_config chip_config_6500 = { + .clk = INV_CLK_PLL, + .fsr = INV_MPU6050_FSR_2000DPS, + .lpf = INV_MPU6050_FILTER_20HZ, + .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(50), + .gyro_en = true, + .accl_en = true, + .temp_en = true, + .magn_en = false, .gyro_fifo_enable = false, .accl_fifo_enable = false, .temp_fifo_enable = false, @@ -124,7 +146,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_MPU6500_WHOAMI_VALUE, .name = "MPU6500", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 512, .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, @@ -132,7 +154,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_MPU6515_WHOAMI_VALUE, .name = "MPU6515", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 512, .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, @@ -156,7 +178,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_MPU9250_WHOAMI_VALUE, .name = "MPU9250", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 512, .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, @@ -164,7 +186,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_MPU9255_WHOAMI_VALUE, .name = "MPU9255", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 512, .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, @@ -172,7 +194,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_ICM20608_WHOAMI_VALUE, .name = "ICM20608", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 512, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, @@ -180,7 +202,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_ICM20609_WHOAMI_VALUE, .name = "ICM20609", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 4 * 1024, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, @@ -188,7 +210,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_ICM20689_WHOAMI_VALUE, .name = "ICM20689", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 4 * 1024, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, @@ -196,15 +218,15 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_ICM20602_WHOAMI_VALUE, .name = "ICM20602", .reg = ®_set_icm20602, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 1008, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, { .whoami = INV_ICM20690_WHOAMI_VALUE, .name = "ICM20690", - .reg = ®_set_icm20602, - .config = &chip_config_6050, + .reg = ®_set_6500, + .config = &chip_config_6500, .fifo_size = 1024, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, @@ -212,61 +234,162 @@ static const struct inv_mpu6050_hw hw_info[] = { .whoami = INV_IAM20680_WHOAMI_VALUE, .name = "IAM20680", .reg = ®_set_6500, - .config = &chip_config_6050, + .config = &chip_config_6500, .fifo_size = 512, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, }; -int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) +static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep, + int clock, int temp_dis) { - unsigned int d, mgmt_1; - int result; - /* - * switch clock needs to be careful. Only when gyro is on, can - * clock source be switched to gyro. Otherwise, it must be set to - * internal clock - */ - if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) { - result = regmap_read(st->map, st->reg->pwr_mgmt_1, &mgmt_1); - if (result) - return result; + u8 val; - mgmt_1 &= ~INV_MPU6050_BIT_CLK_MASK; + if (clock < 0) + clock = st->chip_config.clk; + if (temp_dis < 0) + temp_dis = !st->chip_config.temp_en; + + val = clock & INV_MPU6050_BIT_CLK_MASK; + if (temp_dis) + val |= INV_MPU6050_BIT_TEMP_DIS; + if (sleep) + val |= INV_MPU6050_BIT_SLEEP; + + dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val); + return regmap_write(st->map, st->reg->pwr_mgmt_1, val); +} + +static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st, + unsigned int clock) +{ + int ret; + + switch (st->chip_type) { + case INV_MPU6050: + case INV_MPU6000: + case INV_MPU9150: + /* old chips: switch clock manually */ + ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1); + if (ret) + return ret; + st->chip_config.clk = clock; + break; + default: + /* automatic clock switching, nothing to do */ + break; } - if ((mask == INV_MPU6050_BIT_PWR_GYRO_STBY) && (!en)) { - /* - * turning off gyro requires switch to internal clock first. - * Then turn off gyro engine - */ - mgmt_1 |= INV_CLK_INTERNAL; - result = regmap_write(st->map, st->reg->pwr_mgmt_1, mgmt_1); - if (result) - return result; + return 0; +} + +int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, + unsigned int mask) +{ + unsigned int sleep; + u8 pwr_mgmt2, user_ctrl; + int ret; + + /* delete useless requests */ + if (mask & INV_MPU6050_SENSOR_ACCL && en == st->chip_config.accl_en) + mask &= ~INV_MPU6050_SENSOR_ACCL; + if (mask & INV_MPU6050_SENSOR_GYRO && en == st->chip_config.gyro_en) + mask &= ~INV_MPU6050_SENSOR_GYRO; + if (mask & INV_MPU6050_SENSOR_TEMP && en == st->chip_config.temp_en) + mask &= ~INV_MPU6050_SENSOR_TEMP; + if (mask & INV_MPU6050_SENSOR_MAGN && en == st->chip_config.magn_en) + mask &= ~INV_MPU6050_SENSOR_MAGN; + if (mask == 0) + return 0; + + /* turn on/off temperature sensor */ + if (mask & INV_MPU6050_SENSOR_TEMP) { + ret = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, !en); + if (ret) + return ret; + st->chip_config.temp_en = en; } - result = regmap_read(st->map, st->reg->pwr_mgmt_2, &d); - if (result) - return result; - if (en) - d &= ~mask; - else - d |= mask; - result = regmap_write(st->map, st->reg->pwr_mgmt_2, d); - if (result) - return result; + /* update user_crtl for driving magnetometer */ + if (mask & INV_MPU6050_SENSOR_MAGN) { + user_ctrl = st->chip_config.user_ctrl; + if (en) + user_ctrl |= INV_MPU6050_BIT_I2C_MST_EN; + else + user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + return ret; + st->chip_config.user_ctrl = user_ctrl; + st->chip_config.magn_en = en; + } - if (en) { - /* Wait for output to stabilize */ - msleep(INV_MPU6050_TEMP_UP_TIME); - if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) { - /* switch internal clock to PLL */ - mgmt_1 |= INV_CLK_PLL; - result = regmap_write(st->map, - st->reg->pwr_mgmt_1, mgmt_1); - if (result) - return result; + /* manage accel & gyro engines */ + if (mask & (INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO)) { + /* compute power management 2 current value */ + pwr_mgmt2 = 0; + if (!st->chip_config.accl_en) + pwr_mgmt2 |= INV_MPU6050_BIT_PWR_ACCL_STBY; + if (!st->chip_config.gyro_en) + pwr_mgmt2 |= INV_MPU6050_BIT_PWR_GYRO_STBY; + + /* update to new requested value */ + if (mask & INV_MPU6050_SENSOR_ACCL) { + if (en) + pwr_mgmt2 &= ~INV_MPU6050_BIT_PWR_ACCL_STBY; + else + pwr_mgmt2 |= INV_MPU6050_BIT_PWR_ACCL_STBY; + } + if (mask & INV_MPU6050_SENSOR_GYRO) { + if (en) + pwr_mgmt2 &= ~INV_MPU6050_BIT_PWR_GYRO_STBY; + else + pwr_mgmt2 |= INV_MPU6050_BIT_PWR_GYRO_STBY; + } + + /* switch clock to internal when turning gyro off */ + if (mask & INV_MPU6050_SENSOR_GYRO && !en) { + ret = inv_mpu6050_clock_switch(st, INV_CLK_INTERNAL); + if (ret) + return ret; + } + + /* update sensors engine */ + dev_dbg(regmap_get_device(st->map), "pwr_mgmt_2: 0x%x\n", + pwr_mgmt2); + ret = regmap_write(st->map, st->reg->pwr_mgmt_2, pwr_mgmt2); + if (ret) + return ret; + if (mask & INV_MPU6050_SENSOR_ACCL) + st->chip_config.accl_en = en; + if (mask & INV_MPU6050_SENSOR_GYRO) + st->chip_config.gyro_en = en; + + /* compute required time to have sensors stabilized */ + sleep = 0; + if (en) { + if (mask & INV_MPU6050_SENSOR_ACCL) { + if (sleep < INV_MPU6050_ACCEL_UP_TIME) + sleep = INV_MPU6050_ACCEL_UP_TIME; + } + if (mask & INV_MPU6050_SENSOR_GYRO) { + if (sleep < INV_MPU6050_GYRO_UP_TIME) + sleep = INV_MPU6050_GYRO_UP_TIME; + } + } else { + if (mask & INV_MPU6050_SENSOR_GYRO) { + if (sleep < INV_MPU6050_GYRO_DOWN_TIME) + sleep = INV_MPU6050_GYRO_DOWN_TIME; + } + } + if (sleep) + msleep(sleep); + + /* switch clock to PLL when turning gyro on */ + if (mask & INV_MPU6050_SENSOR_GYRO && en) { + ret = inv_mpu6050_clock_switch(st, INV_CLK_PLL); + if (ret) + return ret; } } @@ -279,7 +402,7 @@ int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) if (power_on) { if (!st->powerup_count) { - result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); + result = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, -1); if (result) return result; usleep_range(INV_MPU6050_REG_UP_TIME_MIN, @@ -288,8 +411,7 @@ int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) st->powerup_count++; } else { if (st->powerup_count == 1) { - result = regmap_write(st->map, st->reg->pwr_mgmt_1, - INV_MPU6050_BIT_SLEEP); + result = inv_mpu6050_pwr_mgmt_1_write(st, true, -1, -1); if (result) return result; } @@ -451,33 +573,41 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, switch (chan->type) { case IIO_ANGL_VEL: result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_BIT_PWR_GYRO_STBY); + INV_MPU6050_SENSOR_GYRO); if (result) goto error_power_off; ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, chan->channel2, val); result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_GYRO_STBY); + INV_MPU6050_SENSOR_GYRO); if (result) goto error_power_off; break; case IIO_ACCEL: result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_BIT_PWR_ACCL_STBY); + INV_MPU6050_SENSOR_ACCL); if (result) goto error_power_off; ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, chan->channel2, val); result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_ACCL_STBY); + INV_MPU6050_SENSOR_ACCL); if (result) goto error_power_off; break; case IIO_TEMP: + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_SENSOR_TEMP); + if (result) + goto error_power_off; /* wait for stablization */ - msleep(INV_MPU6050_SENSOR_UP_TIME); + msleep(INV_MPU6050_TEMP_UP_TIME); ret = inv_mpu6050_sensor_show(st, st->reg->temperature, IIO_MOD_X, val); + result = inv_mpu6050_switch_engine(st, false, + INV_MPU6050_SENSOR_TEMP); + if (result) + goto error_power_off; break; case IIO_MAGN: ret = inv_mpu_magn_read(st, chan->channel2, val); @@ -1108,7 +1238,7 @@ static const struct iio_info mpu_info = { static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) { int result; - unsigned int regval; + unsigned int regval, mask; int i; st->hw = &hw_info[st->chip_type]; @@ -1174,13 +1304,9 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) result = inv_mpu6050_set_power_itg(st, true); if (result) return result; - - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_ACCL_STBY); - if (result) - goto error_power_off; - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_GYRO_STBY); + mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO | + INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN; + result = inv_mpu6050_switch_engine(st, false, mask); if (result) goto error_power_off; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 17f1f6a15f95..a578789c9210 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -83,11 +83,22 @@ enum inv_devices { INV_NUM_PARTS }; +/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */ +#define INV_MPU6050_SENSOR_ACCL BIT(0) +#define INV_MPU6050_SENSOR_GYRO BIT(1) +#define INV_MPU6050_SENSOR_TEMP BIT(2) +#define INV_MPU6050_SENSOR_MAGN BIT(3) + /** * struct inv_mpu6050_chip_config - Cached chip configuration data. + * @clk: selected chip clock * @fsr: Full scale range. * @lpf: Digital low pass filter frequency. * @accl_fs: accel full scale range. + * @accl_en: accel engine enabled + * @gyro_en: gyro engine enabled + * @temp_en: temperature sensor enabled + * @magn_en: magn engine (i2c master) enabled * @accl_fifo_enable: enable accel data output * @gyro_fifo_enable: enable gyro data output * @temp_fifo_enable: enable temp data output @@ -95,9 +106,14 @@ enum inv_devices { * @divider: chip sample rate divider (sample rate divider - 1) */ struct inv_mpu6050_chip_config { + unsigned int clk:3; unsigned int fsr:2; unsigned int lpf:3; unsigned int accl_fs:2; + unsigned int accl_en:1; + unsigned int gyro_en:1; + unsigned int temp_en:1; + unsigned int magn_en:1; unsigned int accl_fifo_enable:1; unsigned int gyro_fifo_enable:1; unsigned int temp_fifo_enable:1; @@ -262,6 +278,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_PWR_MGMT_1 0x6B #define INV_MPU6050_BIT_H_RESET 0x80 #define INV_MPU6050_BIT_SLEEP 0x40 +#define INV_MPU6050_BIT_TEMP_DIS 0x08 #define INV_MPU6050_BIT_CLK_MASK 0x7 #define INV_MPU6050_REG_PWR_MGMT_2 0x6C @@ -292,7 +309,9 @@ struct inv_mpu6050_state { /* delay time in milliseconds */ #define INV_MPU6050_POWER_UP_TIME 100 #define INV_MPU6050_TEMP_UP_TIME 100 -#define INV_MPU6050_SENSOR_UP_TIME 30 +#define INV_MPU6050_ACCEL_UP_TIME 20 +#define INV_MPU6050_GYRO_UP_TIME 35 +#define INV_MPU6050_GYRO_DOWN_TIME 150 /* delay time in microseconds */ #define INV_MPU6050_REG_UP_TIME_MIN 5000 @@ -417,7 +436,8 @@ enum inv_mpu6050_clock_sel_e { irqreturn_t inv_mpu6050_read_fifo(int irq, void *p); int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type); int inv_reset_fifo(struct iio_dev *indio_dev); -int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask); +int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, + unsigned int mask); int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on); int inv_mpu_acpi_create_mux_client(struct i2c_client *client); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 607104a2631e..3f402fa56d95 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -316,9 +316,9 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) * * Returns 0 on success, a negative error code otherwise */ -int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val) +int inv_mpu_magn_read(struct inv_mpu6050_state *st, int axis, int *val) { - unsigned int user_ctrl, status; + unsigned int status; __be16 data; uint8_t addr; unsigned int freq_hz, period_ms; @@ -350,16 +350,14 @@ int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val) freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX; period_ms = 1000 / freq_hz; - /* start i2c master, wait for xfer, stop */ - user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN; - ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + ret = inv_mpu6050_switch_engine(st, true, INV_MPU6050_SENSOR_MAGN); if (ret) return ret; /* need to wait 2 periods + half-period margin */ msleep(period_ms * 2 + period_ms / 2); - user_ctrl = st->chip_config.user_ctrl; - ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + + ret = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_MAGN); if (ret) return ret; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h index b41bd0578478..f7ad50ca6c1b 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h @@ -31,6 +31,6 @@ int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate); int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st); -int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val); +int inv_mpu_magn_read(struct inv_mpu6050_state *st, int axis, int *val); #endif /* INV_MPU_MAGN_H_ */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index 5199fe790c30..cfd7243159f6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -5,9 +5,10 @@ #include "inv_mpu_iio.h" -static void inv_scan_query_mpu6050(struct iio_dev *indio_dev) +static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + unsigned int mask; st->chip_config.gyro_fifo_enable = test_bit(INV_MPU6050_SCAN_GYRO_X, @@ -27,17 +28,28 @@ static void inv_scan_query_mpu6050(struct iio_dev *indio_dev) st->chip_config.temp_fifo_enable = test_bit(INV_MPU6050_SCAN_TEMP, indio_dev->active_scan_mask); + + mask = 0; + if (st->chip_config.gyro_fifo_enable) + mask |= INV_MPU6050_SENSOR_GYRO; + if (st->chip_config.accl_fifo_enable) + mask |= INV_MPU6050_SENSOR_ACCL; + if (st->chip_config.temp_fifo_enable) + mask |= INV_MPU6050_SENSOR_TEMP; + + return mask; } -static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev) +static unsigned int inv_scan_query_mpu9x50(struct iio_dev *indio_dev) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + unsigned int mask; - inv_scan_query_mpu6050(indio_dev); + mask = inv_scan_query_mpu6050(indio_dev); /* no magnetometer if i2c auxiliary bus is used */ if (st->magn_disabled) - return; + return mask; st->chip_config.magn_fifo_enable = test_bit(INV_MPU9X50_SCAN_MAGN_X, @@ -46,9 +58,13 @@ static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev) indio_dev->active_scan_mask) || test_bit(INV_MPU9X50_SCAN_MAGN_Z, indio_dev->active_scan_mask); + if (st->chip_config.magn_fifo_enable) + mask |= INV_MPU6050_SENSOR_MAGN; + + return mask; } -static void inv_scan_query(struct iio_dev *indio_dev) +static unsigned int inv_scan_query(struct iio_dev *indio_dev) { struct inv_mpu6050_state *st = iio_priv(indio_dev); @@ -92,62 +108,40 @@ static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st) static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) { struct inv_mpu6050_state *st = iio_priv(indio_dev); - uint8_t d; + unsigned int scan; int result; + scan = inv_scan_query(indio_dev); + if (enable) { result = inv_mpu6050_set_power_itg(st, true); if (result) return result; - inv_scan_query(indio_dev); - if (st->chip_config.gyro_fifo_enable) { - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_BIT_PWR_GYRO_STBY); - if (result) - goto error_power_off; - } - if (st->chip_config.accl_fifo_enable) { - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_BIT_PWR_ACCL_STBY); - if (result) - goto error_gyro_off; - } - if (st->chip_config.magn_fifo_enable) { - d = st->chip_config.user_ctrl | - INV_MPU6050_BIT_I2C_MST_EN; - result = regmap_write(st->map, st->reg->user_ctrl, d); - if (result) - goto error_accl_off; - st->chip_config.user_ctrl = d; - } + result = inv_mpu6050_switch_engine(st, true, scan); + if (result) + goto error_power_off; st->skip_samples = inv_compute_skip_samples(st); result = inv_reset_fifo(indio_dev); if (result) - goto error_magn_off; + goto error_sensors_off; } else { result = regmap_write(st->map, st->reg->fifo_en, 0); if (result) - goto error_magn_off; + goto error_fifo_off; result = regmap_write(st->map, st->reg->int_enable, 0); if (result) - goto error_magn_off; + goto error_fifo_off; - d = st->chip_config.user_ctrl & ~INV_MPU6050_BIT_I2C_MST_EN; - result = regmap_write(st->map, st->reg->user_ctrl, d); + /* restore user_ctrl for disabling FIFO reading */ + result = regmap_write(st->map, st->reg->user_ctrl, + st->chip_config.user_ctrl); if (result) - goto error_magn_off; - st->chip_config.user_ctrl = d; + goto error_sensors_off; - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_ACCL_STBY); + result = inv_mpu6050_switch_engine(st, false, scan); if (result) - goto error_accl_off; - - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_GYRO_STBY); - if (result) - goto error_gyro_off; + goto error_power_off; result = inv_mpu6050_set_power_itg(st, false); if (result) @@ -156,18 +150,11 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) return 0; -error_magn_off: +error_fifo_off: /* always restore user_ctrl to disable fifo properly */ - st->chip_config.user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN; regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); -error_accl_off: - if (st->chip_config.accl_fifo_enable) - inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_ACCL_STBY); -error_gyro_off: - if (st->chip_config.gyro_fifo_enable) - inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_GYRO_STBY); +error_sensors_off: + inv_mpu6050_switch_engine(st, false, scan); error_power_off: inv_mpu6050_set_power_itg(st, false); return result; From ffc9648a52c4cfae3eaadf629d79a0356f3f5900 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:54 +0100 Subject: [PATCH 65/98] iio: imu: inv_mpu6050: fix data polling interface When reading data with the polling interface, we need to wait at 1 sampling period to have a sample. For gyroscope and magnetometer, we need to wait for 2 periods before having a correct sample. Not suitable for stable or backporting. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 36 ++++++++++++++++++++-- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 21 ------------- drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h | 3 ++ 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index a51efc4c941b..aeee39696d3a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -563,9 +563,14 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, int *val) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + unsigned int freq_hz, period_us, min_sleep_us, max_sleep_us; int result; int ret; + /* compute sample period */ + freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); + period_us = 1000000 / freq_hz; + result = inv_mpu6050_set_power_itg(st, true); if (result) return result; @@ -576,6 +581,10 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, INV_MPU6050_SENSOR_GYRO); if (result) goto error_power_off; + /* need to wait 2 periods to have first valid sample */ + min_sleep_us = 2 * period_us; + max_sleep_us = 2 * (period_us + period_us / 2); + usleep_range(min_sleep_us, max_sleep_us); ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, chan->channel2, val); result = inv_mpu6050_switch_engine(st, false, @@ -588,6 +597,10 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, INV_MPU6050_SENSOR_ACCL); if (result) goto error_power_off; + /* wait 1 period for first sample availability */ + min_sleep_us = period_us; + max_sleep_us = period_us + period_us / 2; + usleep_range(min_sleep_us, max_sleep_us); ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, chan->channel2, val); result = inv_mpu6050_switch_engine(st, false, @@ -600,8 +613,10 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, INV_MPU6050_SENSOR_TEMP); if (result) goto error_power_off; - /* wait for stablization */ - msleep(INV_MPU6050_TEMP_UP_TIME); + /* wait 1 period for first sample availability */ + min_sleep_us = period_us; + max_sleep_us = period_us + period_us / 2; + usleep_range(min_sleep_us, max_sleep_us); ret = inv_mpu6050_sensor_show(st, st->reg->temperature, IIO_MOD_X, val); result = inv_mpu6050_switch_engine(st, false, @@ -610,7 +625,24 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, goto error_power_off; break; case IIO_MAGN: + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_SENSOR_MAGN); + if (result) + goto error_power_off; + /* frequency is limited for magnetometer */ + if (freq_hz > INV_MPU_MAGN_FREQ_HZ_MAX) { + freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX; + period_us = 1000000 / freq_hz; + } + /* need to wait 2 periods to have first valid sample */ + min_sleep_us = 2 * period_us; + max_sleep_us = 2 * (period_us + period_us / 2); + usleep_range(min_sleep_us, max_sleep_us); ret = inv_mpu_magn_read(st, chan->channel2, val); + result = inv_mpu6050_switch_engine(st, false, + INV_MPU6050_SENSOR_MAGN); + if (result) + goto error_power_off; break; default: ret = -EINVAL; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index 3f402fa56d95..f282e9cc34c5 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -44,9 +44,6 @@ #define INV_MPU_MAGN_REG_ASAY 0x11 #define INV_MPU_MAGN_REG_ASAZ 0x12 -/* Magnetometer maximum frequency */ -#define INV_MPU_MAGN_FREQ_HZ_MAX 50 - static bool inv_magn_supported(const struct inv_mpu6050_state *st) { switch (st->chip_type) { @@ -321,7 +318,6 @@ int inv_mpu_magn_read(struct inv_mpu6050_state *st, int axis, int *val) unsigned int status; __be16 data; uint8_t addr; - unsigned int freq_hz, period_ms; int ret; /* quit if chip is not supported */ @@ -344,23 +340,6 @@ int inv_mpu_magn_read(struct inv_mpu6050_state *st, int axis, int *val) } addr += INV_MPU6050_REG_EXT_SENS_DATA; - /* compute period depending on current sampling rate */ - freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); - if (freq_hz > INV_MPU_MAGN_FREQ_HZ_MAX) - freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX; - period_ms = 1000 / freq_hz; - - ret = inv_mpu6050_switch_engine(st, true, INV_MPU6050_SENSOR_MAGN); - if (ret) - return ret; - - /* need to wait 2 periods + half-period margin */ - msleep(period_ms * 2 + period_ms / 2); - - ret = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_MAGN); - if (ret) - return ret; - /* check i2c status and read raw data */ ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); if (ret) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h index f7ad50ca6c1b..185c000c697c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h @@ -10,6 +10,9 @@ #include "inv_mpu_iio.h" +/* Magnetometer maximum frequency */ +#define INV_MPU_MAGN_FREQ_HZ_MAX 50 + int inv_mpu_magn_probe(struct inv_mpu6050_state *st); /** From 4c1e0147015c19566b5c6a4b4f656a57ce897ea8 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:55 +0100 Subject: [PATCH 66/98] iio: imu: inv_mpu6050: factorize fifo enable/disable Rework fifo enable/disable in a separate function. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 54 ++------------- drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c | 66 ++++++++++++++----- 3 files changed, 55 insertions(+), 67 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index a578789c9210..e328c98e362c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -435,7 +435,7 @@ enum inv_mpu6050_clock_sel_e { irqreturn_t inv_mpu6050_read_fifo(int irq, void *p); int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type); -int inv_reset_fifo(struct iio_dev *indio_dev); +int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable); int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, unsigned int mask); int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index d7397705974e..9511e4715e2c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -90,60 +90,14 @@ static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st) return ts; } -int inv_reset_fifo(struct iio_dev *indio_dev) +static int inv_reset_fifo(struct iio_dev *indio_dev) { int result; - u8 d; struct inv_mpu6050_state *st = iio_priv(indio_dev); - /* reset it timestamp validation */ - st->it_timestamp = 0; - - /* disable interrupt */ - result = regmap_write(st->map, st->reg->int_enable, 0); - if (result) { - dev_err(regmap_get_device(st->map), "int_enable failed %d\n", - result); - return result; - } - /* disable the sensor output to FIFO */ - result = regmap_write(st->map, st->reg->fifo_en, 0); - if (result) - goto reset_fifo_fail; - /* disable fifo reading */ - result = regmap_write(st->map, st->reg->user_ctrl, - st->chip_config.user_ctrl); - if (result) - goto reset_fifo_fail; - - /* reset FIFO*/ - d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; - result = regmap_write(st->map, st->reg->user_ctrl, d); - if (result) - goto reset_fifo_fail; - - /* enable interrupt */ - result = regmap_write(st->map, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); - if (result) - return result; - - /* enable FIFO reading */ - d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN; - result = regmap_write(st->map, st->reg->user_ctrl, d); - if (result) - goto reset_fifo_fail; - /* enable sensor output to FIFO */ - d = 0; - if (st->chip_config.gyro_fifo_enable) - d |= INV_MPU6050_BITS_GYRO_OUT; - if (st->chip_config.accl_fifo_enable) - d |= INV_MPU6050_BIT_ACCEL_OUT; - if (st->chip_config.temp_fifo_enable) - d |= INV_MPU6050_BIT_TEMP_OUT; - if (st->chip_config.magn_fifo_enable) - d |= INV_MPU6050_BIT_SLAVE_0; - result = regmap_write(st->map, st->reg->fifo_en, d); + /* disable fifo and reenable it */ + inv_mpu6050_prepare_fifo(st, false); + result = inv_mpu6050_prepare_fifo(st, true); if (result) goto reset_fifo_fail; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index cfd7243159f6..f53f50d08b9e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -100,6 +100,54 @@ static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st) return skip_samples; } +int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) +{ + uint8_t d; + int ret; + + if (enable) { + st->it_timestamp = 0; + /* reset FIFO */ + d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; + ret = regmap_write(st->map, st->reg->user_ctrl, d); + if (ret) + return ret; + /* enable sensor output to FIFO */ + d = 0; + if (st->chip_config.gyro_fifo_enable) + d |= INV_MPU6050_BITS_GYRO_OUT; + if (st->chip_config.accl_fifo_enable) + d |= INV_MPU6050_BIT_ACCEL_OUT; + if (st->chip_config.temp_fifo_enable) + d |= INV_MPU6050_BIT_TEMP_OUT; + if (st->chip_config.magn_fifo_enable) + d |= INV_MPU6050_BIT_SLAVE_0; + ret = regmap_write(st->map, st->reg->fifo_en, d); + if (ret) + return ret; + /* enable FIFO reading */ + d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN; + ret = regmap_write(st->map, st->reg->user_ctrl, d); + if (ret) + return ret; + /* enable interrupt */ + ret = regmap_write(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN); + } else { + ret = regmap_write(st->map, st->reg->int_enable, 0); + if (ret) + return ret; + ret = regmap_write(st->map, st->reg->fifo_en, 0); + if (ret) + return ret; + /* restore user_ctrl for disabling FIFO reading */ + ret = regmap_write(st->map, st->reg->user_ctrl, + st->chip_config.user_ctrl); + } + + return ret; +} + /** * inv_mpu6050_set_enable() - enable chip functions. * @indio_dev: Device driver instance. @@ -121,24 +169,13 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (result) goto error_power_off; st->skip_samples = inv_compute_skip_samples(st); - result = inv_reset_fifo(indio_dev); + result = inv_mpu6050_prepare_fifo(st, true); if (result) goto error_sensors_off; } else { - result = regmap_write(st->map, st->reg->fifo_en, 0); - if (result) - goto error_fifo_off; - - result = regmap_write(st->map, st->reg->int_enable, 0); - if (result) - goto error_fifo_off; - - /* restore user_ctrl for disabling FIFO reading */ - result = regmap_write(st->map, st->reg->user_ctrl, - st->chip_config.user_ctrl); + result = inv_mpu6050_prepare_fifo(st, false); if (result) goto error_sensors_off; - result = inv_mpu6050_switch_engine(st, false, scan); if (result) goto error_power_off; @@ -150,9 +187,6 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) return 0; -error_fifo_off: - /* always restore user_ctrl to disable fifo properly */ - regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); error_sensors_off: inv_mpu6050_switch_engine(st, false, scan); error_power_off: From 5e95ca3637d6f706d157a5f12e51f3e1c175285a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:56 +0100 Subject: [PATCH 67/98] iio: imu: inv_mpu6050: dynamic sampling rate change Sampling rate can be changed while the chip is running. It can be useful thus do not prevent it. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index aeee39696d3a..9076b6bb099c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -910,10 +910,6 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, fifo_rate > INV_MPU6050_MAX_FIFO_RATE) return -EINVAL; - result = iio_device_claim_direct_mode(indio_dev); - if (result) - return result; - /* compute the chip sample rate divider */ d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate); /* compute back the fifo rate to handle truncation cases */ @@ -946,7 +942,6 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, result |= inv_mpu6050_set_power_itg(st, false); fifo_rate_fail_unlock: mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); if (result) return result; From 4599cac8461468a10dc375f4d4e12442e2d16d3f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:57 +0100 Subject: [PATCH 68/98] iio: imu: inv_mpu6050: use runtime pm with autosuspend Use runtime power management for handling chip power and sensor engines on/off. Simplifies things a lot since pm runtime already has reference counter. Usage of autosuspend reduces the number of power on/off. This makes polling interface now usable to get data at low frequency. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 270 ++++++++++++------ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 5 +- drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c | 33 ++- 3 files changed, 196 insertions(+), 112 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 9076b6bb099c..56e5731eb7de 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "inv_mpu_iio.h" #include "inv_mpu_magn.h" @@ -396,30 +398,18 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, return 0; } -int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) +static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, + bool power_on) { int result; - if (power_on) { - if (!st->powerup_count) { - result = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, -1); - if (result) - return result; - usleep_range(INV_MPU6050_REG_UP_TIME_MIN, - INV_MPU6050_REG_UP_TIME_MAX); - } - st->powerup_count++; - } else { - if (st->powerup_count == 1) { - result = inv_mpu6050_pwr_mgmt_1_write(st, true, -1, -1); - if (result) - return result; - } - st->powerup_count--; - } + result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1); + if (result) + return result; - dev_dbg(regmap_get_device(st->map), "set power %d, count=%u\n", - power_on, st->powerup_count); + if (power_on) + usleep_range(INV_MPU6050_REG_UP_TIME_MIN, + INV_MPU6050_REG_UP_TIME_MAX); return 0; } @@ -563,6 +553,7 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, int *val) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct device *pdev = regmap_get_device(st->map); unsigned int freq_hz, period_us, min_sleep_us, max_sleep_us; int result; int ret; @@ -571,92 +562,85 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); period_us = 1000000 / freq_hz; - result = inv_mpu6050_set_power_itg(st, true); - if (result) + result = pm_runtime_get_sync(pdev); + if (result < 0) { + pm_runtime_put_noidle(pdev); return result; + } switch (chan->type) { case IIO_ANGL_VEL: - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_SENSOR_GYRO); - if (result) - goto error_power_off; - /* need to wait 2 periods to have first valid sample */ - min_sleep_us = 2 * period_us; - max_sleep_us = 2 * (period_us + period_us / 2); - usleep_range(min_sleep_us, max_sleep_us); + if (!st->chip_config.gyro_en) { + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_SENSOR_GYRO); + if (result) + goto error_power_off; + /* need to wait 2 periods to have first valid sample */ + min_sleep_us = 2 * period_us; + max_sleep_us = 2 * (period_us + period_us / 2); + usleep_range(min_sleep_us, max_sleep_us); + } ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, chan->channel2, val); - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_SENSOR_GYRO); - if (result) - goto error_power_off; break; case IIO_ACCEL: - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_SENSOR_ACCL); - if (result) - goto error_power_off; - /* wait 1 period for first sample availability */ - min_sleep_us = period_us; - max_sleep_us = period_us + period_us / 2; - usleep_range(min_sleep_us, max_sleep_us); + if (!st->chip_config.accl_en) { + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_SENSOR_ACCL); + if (result) + goto error_power_off; + /* wait 1 period for first sample availability */ + min_sleep_us = period_us; + max_sleep_us = period_us + period_us / 2; + usleep_range(min_sleep_us, max_sleep_us); + } ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, chan->channel2, val); - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_SENSOR_ACCL); - if (result) - goto error_power_off; break; case IIO_TEMP: - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_SENSOR_TEMP); - if (result) - goto error_power_off; - /* wait 1 period for first sample availability */ - min_sleep_us = period_us; - max_sleep_us = period_us + period_us / 2; - usleep_range(min_sleep_us, max_sleep_us); + if (!st->chip_config.temp_en) { + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_SENSOR_TEMP); + if (result) + goto error_power_off; + /* wait 1 period for first sample availability */ + min_sleep_us = period_us; + max_sleep_us = period_us + period_us / 2; + usleep_range(min_sleep_us, max_sleep_us); + } ret = inv_mpu6050_sensor_show(st, st->reg->temperature, IIO_MOD_X, val); - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_SENSOR_TEMP); - if (result) - goto error_power_off; break; case IIO_MAGN: - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_SENSOR_MAGN); - if (result) - goto error_power_off; - /* frequency is limited for magnetometer */ - if (freq_hz > INV_MPU_MAGN_FREQ_HZ_MAX) { - freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX; - period_us = 1000000 / freq_hz; + if (!st->chip_config.magn_en) { + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_SENSOR_MAGN); + if (result) + goto error_power_off; + /* frequency is limited for magnetometer */ + if (freq_hz > INV_MPU_MAGN_FREQ_HZ_MAX) { + freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX; + period_us = 1000000 / freq_hz; + } + /* need to wait 2 periods to have first valid sample */ + min_sleep_us = 2 * period_us; + max_sleep_us = 2 * (period_us + period_us / 2); + usleep_range(min_sleep_us, max_sleep_us); } - /* need to wait 2 periods to have first valid sample */ - min_sleep_us = 2 * period_us; - max_sleep_us = 2 * (period_us + period_us / 2); - usleep_range(min_sleep_us, max_sleep_us); ret = inv_mpu_magn_read(st, chan->channel2, val); - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_SENSOR_MAGN); - if (result) - goto error_power_off; break; default: ret = -EINVAL; break; } - result = inv_mpu6050_set_power_itg(st, false); - if (result) - goto error_power_off; + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); return ret; error_power_off: - inv_mpu6050_set_power_itg(st, false); + pm_runtime_put_autosuspend(pdev); return result; } @@ -795,6 +779,7 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct device *pdev = regmap_get_device(st->map); int result; /* @@ -806,9 +791,11 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, return result; mutex_lock(&st->lock); - result = inv_mpu6050_set_power_itg(st, true); - if (result) + result = pm_runtime_get_sync(pdev); + if (result < 0) { + pm_runtime_put_noidle(pdev); goto error_write_raw_unlock; + } switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -846,7 +833,8 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, break; } - result |= inv_mpu6050_set_power_itg(st, false); + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); error_write_raw_unlock: mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); @@ -903,6 +891,7 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, int result; struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct device *pdev = regmap_get_device(st->map); if (kstrtoint(buf, 10, &fifo_rate)) return -EINVAL; @@ -920,9 +909,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, result = 0; goto fifo_rate_fail_unlock; } - result = inv_mpu6050_set_power_itg(st, true); - if (result) + result = pm_runtime_get_sync(pdev); + if (result < 0) { + pm_runtime_put_noidle(pdev); goto fifo_rate_fail_unlock; + } result = regmap_write(st->map, st->reg->sample_rate_div, d); if (result) @@ -938,8 +929,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, if (result) goto fifo_rate_fail_power_off; + pm_runtime_mark_last_busy(pdev); fifo_rate_fail_power_off: - result |= inv_mpu6050_set_power_itg(st, false); + pm_runtime_put_autosuspend(pdev); fifo_rate_fail_unlock: mutex_unlock(&st->lock); if (result) @@ -1385,6 +1377,14 @@ static void inv_mpu_core_disable_regulator_action(void *_data) inv_mpu_core_disable_regulator_vddio(st); } +static void inv_mpu_pm_disable(void *data) +{ + struct device *dev = data; + + pm_runtime_put_sync_suspend(dev); + pm_runtime_disable(dev); +} + int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type) { @@ -1409,7 +1409,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, st = iio_priv(indio_dev); mutex_init(&st->lock); st->chip_type = chip_type; - st->powerup_count = 0; st->irq = irq; st->map = regmap; @@ -1521,8 +1520,16 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, goto error_power_off; } - /* chip init is done, turning off */ - result = inv_mpu6050_set_power_itg(st, false); + /* chip init is done, turning on runtime power management */ + result = pm_runtime_set_active(dev); + if (result) + goto error_power_off; + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, INV_MPU6050_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + result = devm_add_action_or_reset(dev, inv_mpu_pm_disable, dev); if (result) return result; @@ -1590,11 +1597,10 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, } EXPORT_SYMBOL_GPL(inv_mpu_core_probe); -#ifdef CONFIG_PM_SLEEP - -static int inv_mpu_resume(struct device *dev) +static int __maybe_unused inv_mpu_resume(struct device *dev) { - struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_mpu6050_state *st = iio_priv(indio_dev); int result; mutex_lock(&st->lock); @@ -1603,27 +1609,101 @@ static int inv_mpu_resume(struct device *dev) goto out_unlock; result = inv_mpu6050_set_power_itg(st, true); + if (result) + goto out_unlock; + + result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors); + if (result) + goto out_unlock; + + if (iio_buffer_enabled(indio_dev)) + result = inv_mpu6050_prepare_fifo(st, true); + out_unlock: mutex_unlock(&st->lock); return result; } -static int inv_mpu_suspend(struct device *dev) +static int __maybe_unused inv_mpu_suspend(struct device *dev) { - struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_mpu6050_state *st = iio_priv(indio_dev); int result; mutex_lock(&st->lock); + + if (iio_buffer_enabled(indio_dev)) { + result = inv_mpu6050_prepare_fifo(st, false); + if (result) + goto out_unlock; + } + + st->suspended_sensors = 0; + if (st->chip_config.accl_en) + st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL; + if (st->chip_config.gyro_en) + st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO; + if (st->chip_config.temp_en) + st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP; + if (st->chip_config.magn_en) + st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN; + result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors); + if (result) + goto out_unlock; + result = inv_mpu6050_set_power_itg(st, false); + if (result) + goto out_unlock; + inv_mpu_core_disable_regulator_vddio(st); +out_unlock: mutex_unlock(&st->lock); return result; } -#endif /* CONFIG_PM_SLEEP */ -SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume); +static int __maybe_unused inv_mpu_runtime_suspend(struct device *dev) +{ + struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); + unsigned int sensors; + int ret; + + mutex_lock(&st->lock); + + sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO | + INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN; + ret = inv_mpu6050_switch_engine(st, false, sensors); + if (ret) + goto out_unlock; + + ret = inv_mpu6050_set_power_itg(st, false); + if (ret) + goto out_unlock; + + inv_mpu_core_disable_regulator_vddio(st); + +out_unlock: + mutex_unlock(&st->lock); + return ret; +} + +static int __maybe_unused inv_mpu_runtime_resume(struct device *dev) +{ + struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = inv_mpu_core_enable_regulator_vddio(st); + if (ret) + return ret; + + return inv_mpu6050_set_power_itg(st, true); +} + +const struct dev_pm_ops inv_mpu_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume) + SET_RUNTIME_PM_OPS(inv_mpu_runtime_suspend, inv_mpu_runtime_resume, NULL) +}; EXPORT_SYMBOL_GPL(inv_mpu_pmops); MODULE_AUTHOR("Invensense Corporation"); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index e328c98e362c..cd38b3fccc7b 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -164,6 +164,7 @@ struct inv_mpu6050_hw { * @magn_disabled: magnetometer disabled for backward compatibility reason. * @magn_raw_to_gauss: coefficient to convert mag raw value to Gauss. * @magn_orient: magnetometer sensor chip orientation if available. + * @suspended_sensors: sensors mask of sensors turned off for suspend */ struct inv_mpu6050_state { struct mutex lock; @@ -174,7 +175,6 @@ struct inv_mpu6050_state { enum inv_devices chip_type; struct i2c_mux_core *muxc; struct i2c_client *mux_client; - unsigned int powerup_count; struct inv_mpu6050_platform_data plat_data; struct iio_mount_matrix orientation; struct regmap *map; @@ -189,6 +189,7 @@ struct inv_mpu6050_state { bool magn_disabled; s32 magn_raw_to_gauss[3]; struct iio_mount_matrix magn_orient; + unsigned int suspended_sensors; }; /*register and associated bit definition*/ @@ -312,6 +313,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_ACCEL_UP_TIME 20 #define INV_MPU6050_GYRO_UP_TIME 35 #define INV_MPU6050_GYRO_DOWN_TIME 150 +#define INV_MPU6050_SUSPEND_DELAY_MS 2000 /* delay time in microseconds */ #define INV_MPU6050_REG_UP_TIME_MIN 5000 @@ -439,7 +441,6 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable); int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, unsigned int mask); int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); -int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on); int inv_mpu_acpi_create_mux_client(struct i2c_client *client); void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index f53f50d08b9e..f7b5a70be30f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -3,6 +3,7 @@ * Copyright (C) 2012 Invensense, Inc. */ +#include #include "inv_mpu_iio.h" static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev) @@ -156,41 +157,43 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct device *pdev = regmap_get_device(st->map); unsigned int scan; int result; - scan = inv_scan_query(indio_dev); - if (enable) { - result = inv_mpu6050_set_power_itg(st, true); - if (result) + scan = inv_scan_query(indio_dev); + result = pm_runtime_get_sync(pdev); + if (result < 0) { + pm_runtime_put_noidle(pdev); return result; + } + /* + * In case autosuspend didn't trigger, turn off first not + * required sensors. + */ + result = inv_mpu6050_switch_engine(st, false, ~scan); + if (result) + goto error_power_off; result = inv_mpu6050_switch_engine(st, true, scan); if (result) goto error_power_off; st->skip_samples = inv_compute_skip_samples(st); result = inv_mpu6050_prepare_fifo(st, true); if (result) - goto error_sensors_off; + goto error_power_off; } else { result = inv_mpu6050_prepare_fifo(st, false); - if (result) - goto error_sensors_off; - result = inv_mpu6050_switch_engine(st, false, scan); - if (result) - goto error_power_off; - - result = inv_mpu6050_set_power_itg(st, false); if (result) goto error_power_off; + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); } return 0; -error_sensors_off: - inv_mpu6050_switch_engine(st, false, scan); error_power_off: - inv_mpu6050_set_power_itg(st, false); + pm_runtime_put_autosuspend(pdev); return result; } From 74a40e19f9db6606b5263aa08af2c684dd67b3d3 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Wed, 19 Feb 2020 15:39:58 +0100 Subject: [PATCH 69/98] iio: imu: inv_mpu6050: temperature only work with accel/gyro Temperature sensor works correctly only when accel and/or gyro is turned on. Prevent polling value if they are not running. Anyway it doesn't make sense to use it without sensor engines on. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 56e5731eb7de..7cb9ff3d3e94 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -598,6 +598,11 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, chan->channel2, val); break; case IIO_TEMP: + /* temperature sensor work only with accel and/or gyro */ + if (!st->chip_config.accl_en && !st->chip_config.gyro_en) { + result = -EBUSY; + goto error_power_off; + } if (!st->chip_config.temp_en) { result = inv_mpu6050_switch_engine(st, true, INV_MPU6050_SENSOR_TEMP); From d4578dc051037ffffa7725330303fed670a63b20 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Mon, 17 Feb 2020 19:42:37 -0800 Subject: [PATCH 70/98] iio: chemical: atlas-sensor: allow probe without interrupt line Sensors don't actually need a interrupt line to give valid readings, and can triggered with CONFIG_IIO_HRTIMER_TRIGGER as well. Remove the required check for interrupt, and continue along in the probe function. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 33 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 2f0a6fed2589..d95818b74770 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -76,6 +76,7 @@ struct atlas_data { struct atlas_device *chip; struct regmap *regmap; struct irq_work work; + unsigned int interrupt_enabled; __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ }; @@ -304,6 +305,9 @@ static int atlas_set_powermode(struct atlas_data *data, int on) static int atlas_set_interrupt(struct atlas_data *data, bool state) { + if (!data->interrupt_enabled) + return 0; + return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL_EN, state ? ATLAS_REG_INT_CONTROL_EN : 0); @@ -572,11 +576,6 @@ static int atlas_probe(struct i2c_client *client, if (ret) return ret; - if (client->irq <= 0) { - dev_err(&client->dev, "no valid irq defined\n"); - return -EINVAL; - } - ret = chip->calibration(data); if (ret) return ret; @@ -596,16 +595,20 @@ static int atlas_probe(struct i2c_client *client, init_irq_work(&data->work, atlas_work_handler); - /* interrupt pin toggles on new conversion */ - ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, atlas_interrupt_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "atlas_irq", - indio_dev); - if (ret) { - dev_err(&client->dev, "request irq (%d) failed\n", client->irq); - goto unregister_buffer; + if (client->irq > 0) { + /* interrupt pin toggles on new conversion */ + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, atlas_interrupt_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "atlas_irq", + indio_dev); + + if (ret) + dev_warn(&client->dev, + "request irq (%d) failed\n", client->irq); + else + data->interrupt_enabled = 1; } ret = atlas_set_powermode(data, 1); From a751b8e48018263d7aa5021c67b2e03a683ae1df Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Mon, 17 Feb 2020 19:42:38 -0800 Subject: [PATCH 71/98] iio: chemical: atlas-sensor: add DO-SM module support Atlas Scientific DO-SM OEM sensor reads disolved oxygen in a solution. This is reported back as mg/L which maps directly to ppm and so the IIO_CONCENTRATION channel type can be used. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 64 +++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index d95818b74770..82d470561ad3 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -48,6 +48,11 @@ #define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2) #define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3) +#define ATLAS_REG_DO_CALIB_STATUS 0x09 +#define ATLAS_REG_DO_CALIB_STATUS_MASK 0x03 +#define ATLAS_REG_DO_CALIB_STATUS_PRESSURE BIT(0) +#define ATLAS_REG_DO_CALIB_STATUS_DO BIT(1) + #define ATLAS_REG_PH_TEMP_DATA 0x0e #define ATLAS_REG_PH_DATA 0x16 @@ -60,14 +65,19 @@ #define ATLAS_REG_ORP_CALIB_STATUS 0x0d #define ATLAS_REG_ORP_DATA 0x0e +#define ATLAS_REG_DO_TEMP_DATA 0x12 +#define ATLAS_REG_DO_DATA 0x22 + #define ATLAS_PH_INT_TIME_IN_MS 450 #define ATLAS_EC_INT_TIME_IN_MS 650 #define ATLAS_ORP_INT_TIME_IN_MS 450 +#define ATLAS_DO_INT_TIME_IN_MS 450 enum { ATLAS_PH_SM, ATLAS_EC_SM, ATLAS_ORP_SM, + ATLAS_DO_SM, }; struct atlas_data { @@ -122,7 +132,7 @@ static const struct iio_chan_spec atlas_ph_channels[] = { }, }; -#define ATLAS_EC_CHANNEL(_idx, _addr) \ +#define ATLAS_CONCENTRATION_CHANNEL(_idx, _addr) \ {\ .type = IIO_CONCENTRATION, \ .indexed = 1, \ @@ -153,8 +163,8 @@ static const struct iio_chan_spec atlas_ec_channels[] = { .endianness = IIO_BE, }, }, - ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA), - ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA), + ATLAS_CONCENTRATION_CHANNEL(0, ATLAS_REG_TDS_DATA), + ATLAS_CONCENTRATION_CHANNEL(1, ATLAS_REG_PSS_DATA), IIO_CHAN_SOFT_TIMESTAMP(3), { .type = IIO_TEMP, @@ -183,6 +193,19 @@ static const struct iio_chan_spec atlas_orp_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1), }; +static const struct iio_chan_spec atlas_do_channels[] = { + ATLAS_CONCENTRATION_CHANNEL(0, ATLAS_REG_DO_DATA), + IIO_CHAN_SOFT_TIMESTAMP(1), + { + .type = IIO_TEMP, + .address = ATLAS_REG_DO_TEMP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .output = 1, + .scan_index = -1 + }, +}; + static int atlas_check_ph_calibration(struct atlas_data *data) { struct device *dev = &data->client->dev; @@ -263,7 +286,31 @@ static int atlas_check_orp_calibration(struct atlas_data *data) dev_warn(dev, "device has not been calibrated\n"); return 0; -}; +} + +static int atlas_check_do_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_DO_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_DO_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_DO_CALIB_STATUS_PRESSURE)) + dev_warn(dev, "device missing atmospheric pressure calibration\n"); + + if (!(val & ATLAS_REG_DO_CALIB_STATUS_DO)) + dev_warn(dev, "device missing dissolved oxygen calibration\n"); + + return 0; +} struct atlas_device { const struct iio_chan_spec *channels; @@ -296,6 +343,13 @@ static struct atlas_device atlas_devices[] = { .calibration = &atlas_check_orp_calibration, .delay = ATLAS_ORP_INT_TIME_IN_MS, }, + [ATLAS_DO_SM] = { + .channels = atlas_do_channels, + .num_channels = 3, + .data_reg = ATLAS_REG_DO_DATA, + .calibration = &atlas_check_do_calibration, + .delay = ATLAS_DO_INT_TIME_IN_MS, + }, }; static int atlas_set_powermode(struct atlas_data *data, int on) @@ -511,6 +565,7 @@ static const struct i2c_device_id atlas_id[] = { { "atlas-ph-sm", ATLAS_PH_SM}, { "atlas-ec-sm", ATLAS_EC_SM}, { "atlas-orp-sm", ATLAS_ORP_SM}, + { "atlas-do-sm", ATLAS_DO_SM}, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); @@ -519,6 +574,7 @@ static const struct of_device_id atlas_dt_ids[] = { { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, }, + { .compatible = "atlas,do-sm", .data = (void *)ATLAS_DO_SM, }, { } }; MODULE_DEVICE_TABLE(of, atlas_dt_ids); From 189d4dab640b0bb5c8ae6b0496ec8d523d02a59c Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Mon, 17 Feb 2020 19:42:39 -0800 Subject: [PATCH 72/98] dt-bindings: iio: chemical: consolidate atlas-sensor docs Since Atlas Scientific device support only varies from the compatible string is ideal all the respective docs are merged into a single doc named atlas,sensor.yaml Reviewed-by: Rob Herring Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- .../bindings/iio/chemical/atlas,ec-sm.txt | 21 -------- .../bindings/iio/chemical/atlas,orp-sm.txt | 21 -------- .../bindings/iio/chemical/atlas,ph-sm.txt | 21 -------- .../bindings/iio/chemical/atlas,sensor.yaml | 53 +++++++++++++++++++ 4 files changed, 53 insertions(+), 63 deletions(-) delete mode 100644 Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt delete mode 100644 Documentation/devicetree/bindings/iio/chemical/atlas,orp-sm.txt delete mode 100644 Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt create mode 100644 Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt deleted file mode 100644 index f4320595b851..000000000000 --- a/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt +++ /dev/null @@ -1,21 +0,0 @@ -* Atlas Scientific EC-SM OEM sensor - -http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf - -Required properties: - - - compatible: must be "atlas,ec-sm" - - reg: the I2C address of the sensor - - interrupts: the sole interrupt generated by the device - - Refer to interrupt-controller/interrupts.txt for generic interrupt client - node bindings. - -Example: - -atlas@64 { - compatible = "atlas,ec-sm"; - reg = <0x64>; - interrupt-parent = <&gpio1>; - interrupts = <16 2>; -}; diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,orp-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,orp-sm.txt deleted file mode 100644 index af1f5a9aa4da..000000000000 --- a/Documentation/devicetree/bindings/iio/chemical/atlas,orp-sm.txt +++ /dev/null @@ -1,21 +0,0 @@ -* Atlas Scientific ORP-SM OEM sensor - -https://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf - -Required properties: - - - compatible: must be "atlas,orp-sm" - - reg: the I2C address of the sensor - - interrupts: the sole interrupt generated by the device - - Refer to interrupt-controller/interrupts.txt for generic interrupt client - node bindings. - -Example: - -atlas@66 { - compatible = "atlas,orp-sm"; - reg = <0x66>; - interrupt-parent = <&gpio1>; - interrupts = <16 2>; -}; diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt deleted file mode 100644 index 79d90f060327..000000000000 --- a/Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt +++ /dev/null @@ -1,21 +0,0 @@ -* Atlas Scientific pH-SM OEM sensor - -http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf - -Required properties: - - - compatible: must be "atlas,ph-sm" - - reg: the I2C address of the sensor - - interrupts: the sole interrupt generated by the device - - Refer to interrupt-controller/interrupts.txt for generic interrupt client - node bindings. - -Example: - -atlas@65 { - compatible = "atlas,ph-sm"; - reg = <0x65>; - interrupt-parent = <&gpio1>; - interrupts = <16 2>; -}; diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml b/Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml new file mode 100644 index 000000000000..edcd2904d50e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/chemical/atlas,sensor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atlas Scientific OEM sensors + +maintainers: + - Matt Ranostay + +description: | + Atlas Scientific OEM sensors connected via I2C + + Datasheets: + http://www.atlas-scientific.com/_files/_datasheets/_oem/DO_oem_datasheet.pdf + http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf + http://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf + http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf + +properties: + compatible: + enum: + - atlas,do-sm + - atlas,ec-sm + - atlas,orp-sm + - atlas,ph-sm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + atlas@66 { + compatible = "atlas,orp-sm"; + reg = <0x66>; + interrupt-parent = <&gpio1>; + interrupts = <16 2>; + }; + }; From 317d4bcca76956d177ae584d95482a329225ff03 Mon Sep 17 00:00:00 2001 From: Daniel Junho Date: Tue, 18 Feb 2020 08:06:47 -0300 Subject: [PATCH 73/98] dt-bindings: iio: adc: ad7923: Add binding documentation for AD7928 This patch add device tree binding documentation for AD7923 adc in YAML format. Signed-off-by: Daniel Junho Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/adi,ad7923.yaml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml new file mode 100644 index 000000000000..a11b918e0016 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad7923.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD7923 and similars with 4 and 8 Channel ADCs. + +maintainers: + - Michael Hennerich + - Patrick Vasseur + +description: | + Analog Devices AD7904, AD7914, AD7923, AD7924 4 Channel ADCs, and AD7908, + AD7918, AD7928 8 Channels ADCs. + + Specifications about the part can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7923.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7904_7914_7924.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7908_7918_7928.pdf + +properties: + compatible: + enum: + - adi,ad7904 + - adi,ad7914 + - adi,ad7923 + - adi,ad7924 + - adi,ad7908 + - adi,ad7918 + - adi,ad7928 + + reg: + maxItems: 1 + + refin-supply: + description: | + The regulator supply for ADC reference voltage. + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +required: + - compatible + - reg + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + ad7928: adc@0 { + compatible = "adi,ad7928"; + reg = <0>; + spi-max-frequency = <25000000>; + refin-supply = <&adc_vref>; + + #address-cells = <1>; + #size-cells = <0>; + }; + }; From 070bd30a518a106c8bb3c8a71de1db7f714bed02 Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Thu, 20 Feb 2020 22:20:53 +0100 Subject: [PATCH 74/98] iio: light: Simplify the current to lux LUT The look-up-table for currens to lux is simply specifying all currents from 5..47 mA, if we add some values for 0..5 we can just select the index for the lux value in an array from the mA value. Use clamp() to get the value in the range of values in the array. Cc: Jonathan Bakker Signed-off-by: Gregor Riepl Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 90 ++++++------------------------------ 1 file changed, 13 insertions(+), 77 deletions(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index 433907619268..b7ef16b28280 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -205,71 +205,27 @@ static irqreturn_t gp2ap002_prox_irq(int irq, void *d) return IRQ_HANDLED; } -struct gp2ap002_illuminance { - unsigned int curr; - unsigned int lux; -}; - /* * This array maps current and lux. * * Ambient light sensing range is 3 to 55000 lux. * * This mapping is based on the following formula. - * illuminance = 10 ^ (current / 10) + * illuminance = 10 ^ (current[mA] / 10) + * + * When the ADC measures 0, return 0 lux. */ -static const struct gp2ap002_illuminance gp2ap002_illuminance_table[] = { - { .curr = 5, .lux = 3 }, - { .curr = 6, .lux = 4 }, - { .curr = 7, .lux = 5 }, - { .curr = 8, .lux = 6 }, - { .curr = 9, .lux = 8 }, - { .curr = 10, .lux = 10 }, - { .curr = 11, .lux = 12 }, - { .curr = 12, .lux = 16 }, - { .curr = 13, .lux = 20 }, - { .curr = 14, .lux = 25 }, - { .curr = 15, .lux = 32 }, - { .curr = 16, .lux = 40 }, - { .curr = 17, .lux = 50 }, - { .curr = 18, .lux = 63 }, - { .curr = 19, .lux = 79 }, - { .curr = 20, .lux = 100 }, - { .curr = 21, .lux = 126 }, - { .curr = 22, .lux = 158 }, - { .curr = 23, .lux = 200 }, - { .curr = 24, .lux = 251 }, - { .curr = 25, .lux = 316 }, - { .curr = 26, .lux = 398 }, - { .curr = 27, .lux = 501 }, - { .curr = 28, .lux = 631 }, - { .curr = 29, .lux = 794 }, - { .curr = 30, .lux = 1000 }, - { .curr = 31, .lux = 1259 }, - { .curr = 32, .lux = 1585 }, - { .curr = 33, .lux = 1995 }, - { .curr = 34, .lux = 2512 }, - { .curr = 35, .lux = 3162 }, - { .curr = 36, .lux = 3981 }, - { .curr = 37, .lux = 5012 }, - { .curr = 38, .lux = 6310 }, - { .curr = 39, .lux = 7943 }, - { .curr = 40, .lux = 10000 }, - { .curr = 41, .lux = 12589 }, - { .curr = 42, .lux = 15849 }, - { .curr = 43, .lux = 19953 }, - { .curr = 44, .lux = 25119 }, - { .curr = 45, .lux = 31623 }, - { .curr = 46, .lux = 39811 }, - { .curr = 47, .lux = 50119 }, +static const u16 gp2ap002_illuminance_table[] = { + 0, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 25, 32, 40, 50, 63, 79, + 100, 126, 158, 200, 251, 316, 398, 501, 631, 794, 1000, 1259, 1585, + 1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000, 12589, 15849, 19953, + 25119, 31623, 39811, 50119, }; static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002) { - const struct gp2ap002_illuminance *ill1; - const struct gp2ap002_illuminance *ill2; int ret, res; - int i; + u16 lux; ret = iio_read_channel_processed(gp2ap002->alsout, &res); if (ret < 0) @@ -277,31 +233,11 @@ static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002) dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res); - ill1 = &gp2ap002_illuminance_table[0]; - if (res < ill1->curr) { - dev_dbg(gp2ap002->dev, "total darkness\n"); - return 0; - } - for (i = 0; i < ARRAY_SIZE(gp2ap002_illuminance_table) - 1; i++) { - ill1 = &gp2ap002_illuminance_table[i]; - ill2 = &gp2ap002_illuminance_table[i + 1]; + /* ensure we don't under/overflow */ + res = clamp(res, 0, (int)ARRAY_SIZE(gp2ap002_illuminance_table) - 1); + lux = gp2ap002_illuminance_table[res]; - if (res > ill2->curr) - continue; - if ((res <= ill1->curr) && (res >= ill2->curr)) - break; - } - if (res > ill2->curr) { - dev_info_once(gp2ap002->dev, "max current overflow\n"); - return ill2->curr; - } - /* Interpolate and return */ - dev_dbg(gp2ap002->dev, "interpolate index %d and %d\n", i, i + 1); - /* How many steps along the curve */ - i = res - ill1->curr; /* x - x0 */ - /* Linear interpolation */ - return ill1->lux + i * - ((ill2->lux - ill1->lux) / (ill2->curr - ill1->curr)); + return (int)lux; } static int gp2ap002_read_raw(struct iio_dev *indio_dev, From cbbb819837f6a2e13808d0529d14be460a7a4a07 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Tue, 18 Feb 2020 14:10:30 +0200 Subject: [PATCH 75/98] iio: dac: ad5770r: Add AD5770R support The AD5770R is a 6-channel, 14-bit resolution, low noise, programmable current output digital-to-analog converter (DAC) for photonics control applications. It contains five 14-bit resolution current sourcing DAC channels and one 14-bit resolution current sourcing/sinking DAC channel. Signed-off-by: Mircea Caprioru Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5770r.c | 695 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 706 insertions(+) create mode 100644 drivers/iio/dac/ad5770r.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3adbd2de38a1..63916a6142ec 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -209,6 +209,16 @@ config AD5764 To compile this driver as a module, choose M here: the module will be called ad5764. +config AD5770R + tristate "Analog Devices AD5770R IDAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5770R Digital to + Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5770r. + config AD5791 tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 1369fa1d2f0e..2fc481167724 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5755) += ad5758.o obj-$(CONFIG_AD5761) += ad5761.o obj-$(CONFIG_AD5764) += ad5764.o +obj-$(CONFIG_AD5770R) += ad5770r.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c new file mode 100644 index 000000000000..a98ea76732e7 --- /dev/null +++ b/drivers/iio/dac/ad5770r.c @@ -0,0 +1,695 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD5770R Digital to analog converters driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADI_SPI_IF_CONFIG_A 0x00 +#define ADI_SPI_IF_CONFIG_B 0x01 +#define ADI_SPI_IF_DEVICE_CONFIG 0x02 +#define ADI_SPI_IF_CHIP_TYPE 0x03 +#define ADI_SPI_IF_PRODUCT_ID_L 0x04 +#define ADI_SPI_IF_PRODUCT_ID_H 0x05 +#define ADI_SPI_IF_CHIP_GRADE 0x06 +#define ADI_SPI_IF_SCRACTH_PAD 0x0A +#define ADI_SPI_IF_SPI_REVISION 0x0B +#define ADI_SPI_IF_SPI_VENDOR_L 0x0C +#define ADI_SPI_IF_SPI_VENDOR_H 0x0D +#define ADI_SPI_IF_SPI_STREAM_MODE 0x0E +#define ADI_SPI_IF_CONFIG_C 0x10 +#define ADI_SPI_IF_STATUS_A 0x11 + +/* ADI_SPI_IF_CONFIG_A */ +#define ADI_SPI_IF_SW_RESET_MSK (BIT(0) | BIT(7)) +#define ADI_SPI_IF_SW_RESET_SEL(x) ((x) & ADI_SPI_IF_SW_RESET_MSK) +#define ADI_SPI_IF_ADDR_ASC_MSK (BIT(2) | BIT(5)) +#define ADI_SPI_IF_ADDR_ASC_SEL(x) (((x) << 2) & ADI_SPI_IF_ADDR_ASC_MSK) + +/* ADI_SPI_IF_CONFIG_B */ +#define ADI_SPI_IF_SINGLE_INS_MSK BIT(7) +#define ADI_SPI_IF_SINGLE_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x) +#define ADI_SPI_IF_SHORT_INS_MSK BIT(7) +#define ADI_SPI_IF_SHORT_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x) + +/* ADI_SPI_IF_CONFIG_C */ +#define ADI_SPI_IF_STRICT_REG_MSK BIT(5) +#define ADI_SPI_IF_STRICT_REG_GET(x) FIELD_GET(ADI_SPI_IF_STRICT_REG_MSK, x) + +/* AD5770R configuration registers */ +#define AD5770R_CHANNEL_CONFIG 0x14 +#define AD5770R_OUTPUT_RANGE(ch) (0x15 + (ch)) +#define AD5770R_FILTER_RESISTOR(ch) (0x1D + (ch)) +#define AD5770R_REFERENCE 0x1B +#define AD5770R_DAC_LSB(ch) (0x26 + 2 * (ch)) +#define AD5770R_DAC_MSB(ch) (0x27 + 2 * (ch)) +#define AD5770R_CH_SELECT 0x34 +#define AD5770R_CH_ENABLE 0x44 + +/* AD5770R_CHANNEL_CONFIG */ +#define AD5770R_CFG_CH0_SINK_EN(x) (((x) & 0x1) << 7) +#define AD5770R_CFG_SHUTDOWN_B(x, ch) (((x) & 0x1) << (ch)) + +/* AD5770R_OUTPUT_RANGE */ +#define AD5770R_RANGE_OUTPUT_SCALING(x) (((x) & GENMASK(5, 0)) << 2) +#define AD5770R_RANGE_MODE(x) ((x) & GENMASK(1, 0)) + +/* AD5770R_REFERENCE */ +#define AD5770R_REF_RESISTOR_SEL(x) (((x) & 0x1) << 2) +#define AD5770R_REF_SEL(x) ((x) & GENMASK(1, 0)) + +/* AD5770R_CH_ENABLE */ +#define AD5770R_CH_SET(x, ch) (((x) & 0x1) << (ch)) + +#define AD5770R_MAX_CHANNELS 6 +#define AD5770R_MAX_CH_MODES 14 +#define AD5770R_LOW_VREF_mV 1250 +#define AD5770R_HIGH_VREF_mV 2500 + +enum ad5770r_ch0_modes { + AD5770R_CH0_0_300 = 0, + AD5770R_CH0_NEG_60_0, + AD5770R_CH0_NEG_60_300 +}; + +enum ad5770r_ch1_modes { + AD5770R_CH1_0_140_LOW_HEAD = 1, + AD5770R_CH1_0_140_LOW_NOISE, + AD5770R_CH1_0_250 +}; + +enum ad5770r_ch2_5_modes { + AD5770R_CH_LOW_RANGE = 0, + AD5770R_CH_HIGH_RANGE +}; + +enum ad5770r_ref_v { + AD5770R_EXT_2_5_V = 0, + AD5770R_INT_1_25_V_OUT_ON, + AD5770R_EXT_1_25_V, + AD5770R_INT_1_25_V_OUT_OFF +}; + +enum ad5770r_output_filter_resistor { + AD5770R_FILTER_60_OHM = 0x0, + AD5770R_FILTER_5_6_KOHM = 0x5, + AD5770R_FILTER_11_2_KOHM, + AD5770R_FILTER_22_2_KOHM, + AD5770R_FILTER_44_4_KOHM, + AD5770R_FILTER_104_KOHM, +}; + +struct ad5770r_out_range { + u8 out_scale; + u8 out_range_mode; +}; + +/** + * struct ad5770R_state - driver instance specific data + * @spi: spi_device + * @regmap: regmap + * @vref_reg: fixed regulator for reference configuration + * @gpio_reset: gpio descriptor + * @output_mode: array contains channels output ranges + * @vref: reference value + * @ch_pwr_down: powerdown flags + * @internal_ref: internal reference flag + * @external_res: external 2.5k resistor flag + * @transf_buf: cache aligned buffer for spi read/write + */ +struct ad5770r_state { + struct spi_device *spi; + struct regmap *regmap; + struct regulator *vref_reg; + struct gpio_desc *gpio_reset; + struct ad5770r_out_range output_mode[AD5770R_MAX_CHANNELS]; + int vref; + bool ch_pwr_down[AD5770R_MAX_CHANNELS]; + bool internal_ref; + bool external_res; + u8 transf_buf[2] ____cacheline_aligned; +}; + +static const struct regmap_config ad5770r_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = BIT(7), +}; + +struct ad5770r_output_modes { + unsigned int ch; + u8 mode; + int min; + int max; +}; + +static struct ad5770r_output_modes ad5770r_rng_tbl[] = { + { 0, AD5770R_CH0_0_300, 0, 300 }, + { 0, AD5770R_CH0_NEG_60_0, -60, 0 }, + { 0, AD5770R_CH0_NEG_60_300, -60, 300 }, + { 1, AD5770R_CH1_0_140_LOW_HEAD, 0, 140 }, + { 1, AD5770R_CH1_0_140_LOW_NOISE, 0, 140 }, + { 1, AD5770R_CH1_0_250, 0, 250 }, + { 2, AD5770R_CH_LOW_RANGE, 0, 55 }, + { 2, AD5770R_CH_HIGH_RANGE, 0, 150 }, + { 3, AD5770R_CH_LOW_RANGE, 0, 45 }, + { 3, AD5770R_CH_HIGH_RANGE, 0, 100 }, + { 4, AD5770R_CH_LOW_RANGE, 0, 45 }, + { 4, AD5770R_CH_HIGH_RANGE, 0, 100 }, + { 5, AD5770R_CH_LOW_RANGE, 0, 45 }, + { 5, AD5770R_CH_HIGH_RANGE, 0, 100 }, +}; + +static const unsigned int ad5770r_filter_freqs[] = { + 153, 357, 715, 1400, 2800, 262000, +}; + +static const unsigned int ad5770r_filter_reg_vals[] = { + AD5770R_FILTER_104_KOHM, + AD5770R_FILTER_44_4_KOHM, + AD5770R_FILTER_22_2_KOHM, + AD5770R_FILTER_11_2_KOHM, + AD5770R_FILTER_5_6_KOHM, + AD5770R_FILTER_60_OHM +}; + +static int ad5770r_set_output_mode(struct ad5770r_state *st, + const struct ad5770r_out_range *out_mode, + int channel) +{ + unsigned int regval; + + regval = AD5770R_RANGE_OUTPUT_SCALING(out_mode->out_scale) | + AD5770R_RANGE_MODE(out_mode->out_range_mode); + + return regmap_write(st->regmap, + AD5770R_OUTPUT_RANGE(channel), regval); +} + +static int ad5770r_set_reference(struct ad5770r_state *st) +{ + unsigned int regval; + + regval = AD5770R_REF_RESISTOR_SEL(st->external_res); + + if (st->internal_ref) { + regval |= AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF); + } else { + switch (st->vref) { + case AD5770R_LOW_VREF_mV: + regval |= AD5770R_REF_SEL(AD5770R_EXT_1_25_V); + break; + case AD5770R_HIGH_VREF_mV: + regval |= AD5770R_REF_SEL(AD5770R_EXT_2_5_V); + break; + default: + regval = AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF); + break; + } + } + + return regmap_write(st->regmap, AD5770R_REFERENCE, regval); +} + +static int ad5770r_soft_reset(struct ad5770r_state *st) +{ + return regmap_write(st->regmap, ADI_SPI_IF_CONFIG_A, + ADI_SPI_IF_SW_RESET_SEL(1)); +} + +static int ad5770r_reset(struct ad5770r_state *st) +{ + /* Perform software reset if no GPIO provided */ + if (!st->gpio_reset) + return ad5770r_soft_reset(st); + + gpiod_set_value_cansleep(st->gpio_reset, 0); + usleep_range(10, 20); + gpiod_set_value_cansleep(st->gpio_reset, 1); + + /* data must not be written during reset timeframe */ + usleep_range(100, 200); + + return 0; +} + +static int ad5770r_get_range(struct ad5770r_state *st, + int ch, int *min, int *max) +{ + int i; + u8 tbl_ch, tbl_mode, out_range; + + out_range = st->output_mode[ch].out_range_mode; + + for (i = 0; i < AD5770R_MAX_CH_MODES; i++) { + tbl_ch = ad5770r_rng_tbl[i].ch; + tbl_mode = ad5770r_rng_tbl[i].mode; + if (tbl_ch == ch && tbl_mode == out_range) { + *min = ad5770r_rng_tbl[i].min; + *max = ad5770r_rng_tbl[i].max; + return 0; + } + } + + return -EINVAL; +} + +static int ad5770r_get_filter_freq(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *freq) +{ + struct ad5770r_state *st = iio_priv(indio_dev); + int ret; + unsigned int regval, i; + + ret = regmap_read(st->regmap, + AD5770R_FILTER_RESISTOR(chan->channel), ®val); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(ad5770r_filter_reg_vals); i++) + if (regval == ad5770r_filter_reg_vals[i]) + break; + if (i == ARRAY_SIZE(ad5770r_filter_reg_vals)) + return -EINVAL; + + *freq = ad5770r_filter_freqs[i]; + + return IIO_VAL_INT; +} + +static int ad5770r_set_filter_freq(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int freq) +{ + struct ad5770r_state *st = iio_priv(indio_dev); + unsigned int regval, i; + + for (i = 0; i < ARRAY_SIZE(ad5770r_filter_freqs); i++) + if (ad5770r_filter_freqs[i] >= freq) + break; + if (i == ARRAY_SIZE(ad5770r_filter_freqs)) + return -EINVAL; + + regval = ad5770r_filter_reg_vals[i]; + + return regmap_write(st->regmap, AD5770R_FILTER_RESISTOR(chan->channel), + regval); +} + +static int ad5770r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad5770r_state *st = iio_priv(indio_dev); + int max, min, ret; + u16 buf16; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = regmap_bulk_read(st->regmap, + chan->address, + st->transf_buf, 2); + if (ret) + return 0; + + buf16 = st->transf_buf[0] + (st->transf_buf[1] << 8); + *val = buf16 >> 2; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = ad5770r_get_range(st, chan->channel, &min, &max); + if (ret < 0) + return ret; + *val = max - min; + /* There is no sign bit. (negative current is mapped from 0) + * (sourced/sinked) current = raw * scale + offset + * where offset in case of CH0 can be negative. + */ + *val2 = 14; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return ad5770r_get_filter_freq(indio_dev, chan, val); + case IIO_CHAN_INFO_OFFSET: + ret = ad5770r_get_range(st, chan->channel, &min, &max); + if (ret < 0) + return ret; + *val = min; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad5770r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad5770r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + st->transf_buf[0] = ((u16)val >> 6); + st->transf_buf[1] = (val & GENMASK(5, 0)) << 2; + return regmap_bulk_write(st->regmap, chan->address, + st->transf_buf, 2); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return ad5770r_set_filter_freq(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int ad5770r_read_freq_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_LOW_PASS_FILTER_3DB_FREQUENCY: + *type = IIO_VAL_INT; + *vals = ad5770r_filter_freqs; + *length = ARRAY_SIZE(ad5770r_filter_freqs); + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + +static int ad5770r_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad5770r_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static const struct iio_info ad5770r_info = { + .read_raw = ad5770r_read_raw, + .write_raw = ad5770r_write_raw, + .read_avail = ad5770r_read_freq_avail, + .debugfs_reg_access = &ad5770r_reg_access, +}; + +static int ad5770r_store_output_range(struct ad5770r_state *st, + int min, int max, int index) +{ + int i; + + for (i = 0; i < AD5770R_MAX_CH_MODES; i++) { + if (ad5770r_rng_tbl[i].ch != index) + continue; + if (ad5770r_rng_tbl[i].min != min || + ad5770r_rng_tbl[i].max != max) + continue; + st->output_mode[index].out_range_mode = ad5770r_rng_tbl[i].mode; + + return 0; + } + + return -EINVAL; +} + +static ssize_t ad5770r_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5770r_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->ch_pwr_down[chan->channel]); +} + +static ssize_t ad5770r_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5770r_state *st = iio_priv(indio_dev); + unsigned int regval; + unsigned int mask; + bool readin; + int ret; + + ret = kstrtobool(buf, &readin); + if (ret) + return ret; + + readin = !readin; + + regval = AD5770R_CFG_SHUTDOWN_B(readin, chan->channel); + if (chan->channel == 0 && + st->output_mode[0].out_range_mode > AD5770R_CH0_0_300) { + regval |= AD5770R_CFG_CH0_SINK_EN(readin); + mask = BIT(chan->channel) + BIT(7); + } else { + mask = BIT(chan->channel); + } + ret = regmap_update_bits(st->regmap, AD5770R_CHANNEL_CONFIG, mask, + regval); + if (ret) + return ret; + + regval = AD5770R_CH_SET(readin, chan->channel); + ret = regmap_update_bits(st->regmap, AD5770R_CH_ENABLE, + BIT(chan->channel), regval); + if (ret) + return ret; + + st->ch_pwr_down[chan->channel] = !readin; + + return len; +} + +static const struct iio_chan_spec_ext_info ad5770r_ext_info[] = { + { + .name = "powerdown", + .read = ad5770r_read_dac_powerdown, + .write = ad5770r_write_dac_powerdown, + .shared = IIO_SEPARATE, + }, + { } +}; + +#define AD5770R_IDAC_CHANNEL(index, reg) { \ + .type = IIO_CURRENT, \ + .address = reg, \ + .indexed = 1, \ + .channel = index, \ + .output = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .ext_info = ad5770r_ext_info, \ +} + +static const struct iio_chan_spec ad5770r_channels[] = { + AD5770R_IDAC_CHANNEL(0, AD5770R_DAC_MSB(0)), + AD5770R_IDAC_CHANNEL(1, AD5770R_DAC_MSB(1)), + AD5770R_IDAC_CHANNEL(2, AD5770R_DAC_MSB(2)), + AD5770R_IDAC_CHANNEL(3, AD5770R_DAC_MSB(3)), + AD5770R_IDAC_CHANNEL(4, AD5770R_DAC_MSB(4)), + AD5770R_IDAC_CHANNEL(5, AD5770R_DAC_MSB(5)), +}; + +static int ad5770r_channel_config(struct ad5770r_state *st) +{ + int ret, tmp[2], min, max; + unsigned int num; + struct fwnode_handle *child; + + num = device_get_child_node_count(&st->spi->dev); + if (num != AD5770R_MAX_CHANNELS) + return -EINVAL; + + device_for_each_child_node(&st->spi->dev, child) { + ret = fwnode_property_read_u32(child, "num", &num); + if (ret) + return ret; + if (num > AD5770R_MAX_CHANNELS) + return -EINVAL; + + ret = fwnode_property_read_u32_array(child, + "adi,range-microamp", + tmp, 2); + if (ret) + return ret; + + min = tmp[0] / 1000; + max = tmp[1] / 1000; + ret = ad5770r_store_output_range(st, min, max, num); + if (ret) + return ret; + } + + return ret; +} + +static int ad5770r_init(struct ad5770r_state *st) +{ + int ret, i; + + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); + + /* Perform a reset */ + ret = ad5770r_reset(st); + if (ret) + return ret; + + /* Set output range */ + ret = ad5770r_channel_config(st); + if (ret) + return ret; + + for (i = 0; i < AD5770R_MAX_CHANNELS; i++) { + ret = ad5770r_set_output_mode(st, &st->output_mode[i], i); + if (ret) + return ret; + } + + st->external_res = fwnode_property_read_bool(st->spi->dev.fwnode, + "adi,external-resistor"); + + ret = ad5770r_set_reference(st); + if (ret) + return ret; + + /* Set outputs off */ + ret = regmap_write(st->regmap, AD5770R_CHANNEL_CONFIG, 0x00); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD5770R_CH_ENABLE, 0x00); + if (ret) + return ret; + + for (i = 0; i < AD5770R_MAX_CHANNELS; i++) + st->ch_pwr_down[i] = true; + + return ret; +} + +static void ad5770r_disable_regulator(void *data) +{ + struct ad5770r_state *st = data; + + regulator_disable(st->vref_reg); +} + +static int ad5770r_probe(struct spi_device *spi) +{ + struct ad5770r_state *st; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + regmap = devm_regmap_init_spi(spi, &ad5770r_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + st->regmap = regmap; + + st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->vref_reg)) { + ret = regulator_enable(st->vref_reg); + if (ret) { + dev_err(&spi->dev, + "Failed to enable vref regulators: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, + ad5770r_disable_regulator, + st); + if (ret < 0) + return ret; + + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) + return ret; + + st->vref = ret / 1000; + } else { + if (PTR_ERR(st->vref_reg) == -ENODEV) { + st->vref = AD5770R_LOW_VREF_mV; + st->internal_ref = true; + } else { + return PTR_ERR(st->vref_reg); + } + } + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5770r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad5770r_channels; + indio_dev->num_channels = ARRAY_SIZE(ad5770r_channels); + + ret = ad5770r_init(st); + if (ret < 0) { + dev_err(&spi->dev, "AD5770R init failed\n"); + return ret; + } + + return devm_iio_device_register(&st->spi->dev, indio_dev); +} + +static const struct of_device_id ad5770r_of_id[] = { + { .compatible = "adi,ad5770r", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad5770r_of_id); + +static const struct spi_device_id ad5770r_id[] = { + { "ad5770r", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(spi, ad5770r_id); + +static struct spi_driver ad5770r_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ad5770r_of_id, + }, + .probe = ad5770r_probe, + .id_table = ad5770r_id, +}; + +module_spi_driver(ad5770r_driver); + +MODULE_AUTHOR("Mircea Caprioru "); +MODULE_DESCRIPTION("Analog Devices AD5770R IDAC"); +MODULE_LICENSE("GPL v2"); From ea52c21268e68cfdc1aabe686b154d73d8bf4d7e Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Tue, 18 Feb 2020 14:10:31 +0200 Subject: [PATCH 76/98] dt-bindings: iio: dac: Add docs for AD5770R DAC Adding dt-bindings documentation for AD5770R DAC. Signed-off-by: Mircea Caprioru Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- .../bindings/iio/dac/adi,ad5770r.yaml | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml new file mode 100644 index 000000000000..d9c25cf4b92f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml @@ -0,0 +1,185 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2020 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bindings/iio/dac/adi,ad5770r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD5770R DAC device driver + +maintainers: + - Mircea Caprioru + +description: | + Bindings for the Analog Devices AD5770R current DAC device. Datasheet can be + found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD5770R.pdf + +properties: + compatible: + enum: + - adi,ad5770r + + reg: + maxItems: 1 + + avdd-supply: + description: + AVdd voltage supply. Represents two different supplies in the datasheet + that are in fact the same. + + iovdd-supply: + description: + Voltage supply for the chip interface. + + vref-supply: + description: Specify the voltage of the external reference used. + Available reference options are 1.25 V or 2.5 V. If no + external reference declared then the device will use the + internal reference of 1.25 V. + + adi,external-resistor: + description: Specify if an external 2.5k ohm resistor is used. If not + specified the device will use an internal 2.5k ohm resistor. + The precision resistor is used for reference current generation. + type: boolean + + reset-gpios: + description: GPIO spec for the RESET pin. If specified, it will be + asserted during driver probe. + maxItems: 1 + + channel0: + description: Represents an external channel which are + connected to the DAC. Channel 0 can act both as a current + source and sink. + type: object + + properties: + num: + description: This represents the channel number. + items: + const: 0 + + adi,range-microamp: + description: Output range of the channel. + oneOf: + - $ref: /schemas/types.yaml#/definitions/int32-array + - items: + - enum: [0 300000] + - enum: [-60000 0] + - enum: [-60000 300000] + + channel1: + description: Represents an external channel which are + connected to the DAC. + type: object + + properties: + num: + description: This represents the channel number. + items: + const: 1 + + adi,range-microamp: + description: Output range of the channel. + oneOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - items: + - enum: [0 140000] + - enum: [0 250000] + + channel2: + description: Represents an external channel which are + connected to the DAC. + type: object + + properties: + num: + description: This represents the channel number. + items: + const: 2 + + adi,range-microamp: + description: Output range of the channel. + oneOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - items: + - enum: [0 140000] + - enum: [0 250000] + +patternProperties: + "^channel@([3-5])$": + type: object + description: Represents the external channels which are connected to the DAC. + properties: + num: + description: This represents the channel number. + items: + minimum: 3 + maximum: 5 + + adi,range-microamp: + description: Output range of the channel. + oneOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - items: + - enum: [0 45000] + - enum: [0 100000] + +required: +- reg +- diff-channels +- channel0 +- channel1 +- channel2 +- channel3 +- channel4 +- channel5 + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + ad5770r@0 { + compatible = "ad5770r"; + reg = <0>; + spi-max-frequency = <1000000>; + vref-supply = <&vref>; + adi,external-resistor; + reset-gpios = <&gpio 22 0>; + + channel@0 { + num = <0>; + adi,range-microamp = <(-60000) 300000>; + }; + + channel@1 { + num = <1>; + adi,range-microamp = <0 140000>; + }; + + channel@2 { + num = <2>; + adi,range-microamp = <0 55000>; + }; + + channel@3 { + num = <3>; + adi,range-microamp = <0 45000>; + }; + + channel@4 { + num = <4>; + adi,range-microamp = <0 45000>; + }; + + channel@5 { + num = <5>; + adi,range-microamp = <0 45000>; + }; + }; + }; +... From 328b50e9a0ad1fe8accdf8c19923deebab5e0c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Roussin-B=C3=A9langer?= Date: Wed, 19 Feb 2020 12:40:08 -0500 Subject: [PATCH 77/98] iio: si1133: read 24-bit signed integer for measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chip is configured in 24 bit mode. The values read from it must always be treated as is. This fixes the issue by replacing the previous 16 bits value by a 24 bits buffer. This changes affects the value output by previous version of the driver, since the least significant byte was missing. The upper half of 16 bit values previously output are now the upper half of a 24 bit value. Fixes: e01e7eaf37d8 ("iio: light: introduce si1133") Reported-by: Simon Goyette Co-authored-by: Guillaume Champagne Signed-off-by: Maxime Roussin-Bélanger Signed-off-by: Guillaume Champagne Signed-off-by: Jonathan Cameron --- drivers/iio/light/si1133.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index 015a21f0c2ef..9174ab928880 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -102,6 +102,9 @@ #define SI1133_INPUT_FRACTION_LOW 15 #define SI1133_LUX_OUTPUT_FRACTION 12 #define SI1133_LUX_BUFFER_SIZE 9 +#define SI1133_MEASURE_BUFFER_SIZE 3 + +#define SI1133_SIGN_BIT_INDEX 23 static const int si1133_scale_available[] = { 1, 2, 4, 8, 16, 32, 64, 128}; @@ -234,13 +237,13 @@ static const struct si1133_lux_coeff lux_coeff = { } }; -static int si1133_calculate_polynomial_inner(u32 input, u8 fraction, u16 mag, +static int si1133_calculate_polynomial_inner(s32 input, u8 fraction, u16 mag, s8 shift) { return ((input << fraction) / mag) << shift; } -static int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order, +static int si1133_calculate_output(s32 x, s32 y, u8 x_order, u8 y_order, u8 input_fraction, s8 sign, const struct si1133_coeff *coeffs) { @@ -276,7 +279,7 @@ static int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order, * The algorithm is from: * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00716 */ -static int si1133_calc_polynomial(u32 x, u32 y, u8 input_fraction, u8 num_coeff, +static int si1133_calc_polynomial(s32 x, s32 y, u8 input_fraction, u8 num_coeff, const struct si1133_coeff *coeffs) { u8 x_order, y_order; @@ -614,7 +617,7 @@ static int si1133_measure(struct si1133_data *data, { int err; - __be16 resp; + u8 buffer[SI1133_MEASURE_BUFFER_SIZE]; err = si1133_set_adcmux(data, 0, chan->channel); if (err) @@ -625,12 +628,13 @@ static int si1133_measure(struct si1133_data *data, if (err) return err; - err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(resp), - (u8 *)&resp); + err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(buffer), + buffer); if (err) return err; - *val = be16_to_cpu(resp); + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2], + SI1133_SIGN_BIT_INDEX); return err; } @@ -704,9 +708,9 @@ static int si1133_get_lux(struct si1133_data *data, int *val) { int err; int lux; - u32 high_vis; - u32 low_vis; - u32 ir; + s32 high_vis; + s32 low_vis; + s32 ir; u8 buffer[SI1133_LUX_BUFFER_SIZE]; /* Activate lux channels */ @@ -719,9 +723,16 @@ static int si1133_get_lux(struct si1133_data *data, int *val) if (err) return err; - high_vis = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; - low_vis = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5]; - ir = (buffer[6] << 16) | (buffer[7] << 8) | buffer[8]; + high_vis = + sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2], + SI1133_SIGN_BIT_INDEX); + + low_vis = + sign_extend32((buffer[3] << 16) | (buffer[4] << 8) | buffer[5], + SI1133_SIGN_BIT_INDEX); + + ir = sign_extend32((buffer[6] << 16) | (buffer[7] << 8) | buffer[8], + SI1133_SIGN_BIT_INDEX); if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD) lux = si1133_calc_polynomial(high_vis, ir, From cc74a38d85fe295468e73452ca892e6d760dc33e Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Fri, 21 Feb 2020 18:18:26 +0200 Subject: [PATCH 78/98] iio: amplifiers: ad8366: add support for HMC1119 Attenuator This change adds support for the HMC1119 Silicon Digial Attenuator. The HMC1119 is a broadband, highly accurate, 7-bit digital attenuator, operating from 0.1 GHz to 6.0 GHz with 31.5 dB attenuation control range in 0.25 dB steps. Link: https://www.analog.com/media/en/technical-documentation/data-sheets/hmc1119.pdf Signed-off-by: Sergiu Cuciurean Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 95972ab60f42..62167b87caea 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -5,6 +5,7 @@ * AD8366 Dual-Digital Variable Gain Amplifier (VGA) * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) * ADL5240 Digitally controlled variable gain amplifier (VGA) + * HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator * * Copyright 2012-2019 Analog Devices Inc. */ @@ -27,6 +28,7 @@ enum ad8366_type { ID_AD8366, ID_ADA4961, ID_ADL5240, + ID_HMC1119, }; struct ad8366_info { @@ -62,6 +64,10 @@ static struct ad8366_info ad8366_infos[] = { .gain_min = -11500, .gain_max = 20000, }, + [ID_HMC1119] = { + .gain_min = -31750, + .gain_max = 0, + }, }; static int ad8366_write(struct iio_dev *indio_dev, @@ -84,6 +90,9 @@ static int ad8366_write(struct iio_dev *indio_dev, case ID_ADL5240: st->data[0] = (ch_a & 0x3F); break; + case ID_HMC1119: + st->data[0] = ch_a; + break; } ret = spi_write(st->spi, st->data, indio_dev->num_channels); @@ -118,6 +127,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, case ID_ADL5240: gain = 20000 - 31500 + code * 500; break; + case ID_HMC1119: + gain = -1 * code * 250; + break; } /* Values in dB */ @@ -164,6 +176,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, case ID_ADL5240: code = ((gain - 500 - 20000) / 500) & 0x3F; break; + case ID_HMC1119: + code = (abs(gain) / 250) & 0x7F; + break; } mutex_lock(&st->lock); @@ -246,6 +261,7 @@ static int ad8366_probe(struct spi_device *spi) break; case ID_ADA4961: case ID_ADL5240: + case ID_HMC1119: st->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH); indio_dev->channels = ada4961_channels; @@ -298,6 +314,7 @@ static const struct spi_device_id ad8366_id[] = { {"ad8366", ID_AD8366}, {"ada4961", ID_ADA4961}, {"adl5240", ID_ADL5240}, + {"hmc1119", ID_HMC1119}, {} }; MODULE_DEVICE_TABLE(spi, ad8366_id); From f5572bd7edeaa86a493949e18e907b6cecfea63d Mon Sep 17 00:00:00 2001 From: Rohit Sarkar Date: Sat, 29 Feb 2020 19:35:45 +0530 Subject: [PATCH 79/98] staging: iio: update TODO Since there are no uses of the old GPIO API, remove the item from the TODO. Signed-off-by: Rohit Sarkar Signed-off-by: Jonathan Cameron --- drivers/staging/iio/TODO | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/staging/iio/TODO b/drivers/staging/iio/TODO index 1b8ebf2c1b69..4d469016a13a 100644 --- a/drivers/staging/iio/TODO +++ b/drivers/staging/iio/TODO @@ -1,10 +1,4 @@ -2018-04-15 - -All affected drivers: -Convert all uses of the old GPIO API from to the -GPIO descriptor API in and look up GPIO -lines from device tree, ACPI or board files, board files should -use . +2020-02-25 ADI Drivers: From ba1b4c8e8092fbb9933aa5598f1b231853813a7e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 28 Feb 2020 10:21:21 +0100 Subject: [PATCH 80/98] iio: adc: exynos: Silence warning about regulators during deferred probe Don't confuse user with meaningless warning about the failure in getting regulators in case of deferred probe. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jonathan Cameron --- drivers/iio/adc/exynos_adc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 2df7d057b249..22131a677445 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -836,8 +836,10 @@ static int exynos_adc_probe(struct platform_device *pdev) info->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(info->vdd)) { - dev_err(&pdev->dev, "failed getting regulator, err = %ld\n", - PTR_ERR(info->vdd)); + if (PTR_ERR(info->vdd) != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed getting regulator, err = %ld\n", + PTR_ERR(info->vdd)); return PTR_ERR(info->vdd); } From e89602c32e195ea1b256fc33a2c522deb46bfedb Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Thu, 27 Feb 2020 14:18:14 +0200 Subject: [PATCH 81/98] iio: adc: ad9292: Use new structure for SPI transfer delays In a recent change to the SPI subsystem [1], a new `delay` struct was added to replace the `delay_usecs`. This change replaces the current `delay_usecs` with `delay` for this driver. The `spi_transfer_delay_exec()` function [in the SPI framework] makes sure that both `delay_usecs` & `delay` are used (in this order to preserve backwards compatibility). [1] commit bebcfd272df6 ("spi: introduce `delay` field for `spi_transfer` + spi_transfer_delay_exec()") Signed-off-by: Sergiu Cuciurean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7292.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index a6798f7dfdb8..6595fd196288 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -122,7 +122,10 @@ static int ad7292_single_conversion(struct ad7292_state *st, { .tx_buf = &st->d8, .len = 4, - .delay_usecs = 6, + .delay = { + .value = 6, + .unit = SPI_DELAY_UNIT_USECS + }, }, { .rx_buf = &st->d16, .len = 2, From 5f0ea0f5a63c6f6f4d9cdc458b0c5da5edfcb705 Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Thu, 27 Feb 2020 14:25:15 +0200 Subject: [PATCH 82/98] iio: adc: max1118: Use new structure for SPI transfer delays In a recent change to the SPI subsystem [1], a new `delay` struct was added to replace the `delay_usecs`. This change replaces the current `delay_usecs` with `delay` for this driver. The `spi_transfer_delay_exec()` function [in the SPI framework] makes sure that both `delay_usecs` & `delay` are used (in this order to preserve backwards compatibility). [1] commit bebcfd272df6 ("spi: introduce `delay` field for `spi_transfer` + spi_transfer_delay_exec()") Signed-off-by: Sergiu Cuciurean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1118.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 3b6f3b9a6c5b..0c5d7aaf6826 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -71,7 +71,10 @@ static int max1118_read(struct spi_device *spi, int channel) */ { .len = 0, - .delay_usecs = 1, /* > CNVST Low Time 100 ns */ + .delay = { /* > CNVST Low Time 100 ns */ + .value = 1, + .unit = SPI_DELAY_UNIT_USECS + }, .cs_change = 1, }, /* @@ -81,7 +84,10 @@ static int max1118_read(struct spi_device *spi, int channel) */ { .len = 0, - .delay_usecs = 8, + .delay = { + .value = 8, + .unit = SPI_DELAY_UNIT_USECS + }, }, { .rx_buf = &adc->data, From eccd071836b310b267b735d4c4ea7d4774adc1bc Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Thu, 27 Feb 2020 14:29:40 +0200 Subject: [PATCH 83/98] iio: adc: mcp320x: Use new structure for SPI transfer delays In a recent change to the SPI subsystem [1], a new `delay` struct was added to replace the `delay_usecs`. This change replaces the current `delay_usecs` with `delay` for this driver. The `spi_transfer_delay_exec()` function [in the SPI framework] makes sure that both `delay_usecs` & `delay` are used (in this order to preserve backwards compatibility). [1] commit bebcfd272df6 ("spi: introduce `delay` field for `spi_transfer` + spi_transfer_delay_exec()") Signed-off-by: Sergiu Cuciurean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp320x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index 465c7625a55a..2c0eb5de110c 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -421,7 +421,8 @@ static int mcp320x_probe(struct spi_device *spi) adc->transfer[1].len++; /* conversions are started by asserting CS pin for 8 usec */ - adc->start_conv_transfer.delay_usecs = 8; + adc->start_conv_transfer.delay.value = 8; + adc->start_conv_transfer.delay.unit = SPI_DELAY_UNIT_USECS; spi_message_init_with_transfers(&adc->start_conv_msg, &adc->start_conv_transfer, 1); From ccea1f5acff94e0796300670a390b4b61e00480a Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Thu, 27 Feb 2020 14:34:27 +0200 Subject: [PATCH 84/98] iio: adc: ti-tlc4541: Use new structure for SPI transfer delays In a recent change to the SPI subsystem [1], a new `delay` struct was added to replace the `delay_usecs`. This change replaces the current `delay_usecs` with `delay` for this driver. The `spi_transfer_delay_exec()` function [in the SPI framework] makes sure that both `delay_usecs` & `delay` are used (in this order to preserve backwards compatibility). [1] commit bebcfd272df6 ("spi: introduce `delay` field for `spi_transfer` + spi_transfer_delay_exec()") Signed-off-by: Sergiu Cuciurean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-tlc4541.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c index 4965246808bd..77620359b54c 100644 --- a/drivers/iio/adc/ti-tlc4541.c +++ b/drivers/iio/adc/ti-tlc4541.c @@ -189,7 +189,8 @@ static int tlc4541_probe(struct spi_device *spi) /* Setup default message */ st->scan_single_xfer[0].rx_buf = &st->rx_buf[0]; st->scan_single_xfer[0].len = 3; - st->scan_single_xfer[1].delay_usecs = 3; + st->scan_single_xfer[1].delay.value = 3; + st->scan_single_xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS; st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; st->scan_single_xfer[2].len = 2; From 61e618be6a0efa8728e2172dc47c47c72217d49b Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Thu, 27 Feb 2020 14:38:59 +0200 Subject: [PATCH 85/98] iio: imu: adis_buffer: Use new structure for SPI transfer delays In a recent change to the SPI subsystem [1], a new `delay` struct was added to replace the `delay_usecs`. This change replaces the current `delay_usecs` with `delay` for this driver. The `spi_transfer_delay_exec()` function [in the SPI framework] makes sure that both `delay_usecs` & `delay` are used (in this order to preserve backwards compatibility). [1] commit bebcfd272df6 ("spi: introduce `delay` field for `spi_transfer` + spi_transfer_delay_exec()") Signed-off-by: Sergiu Cuciurean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis_buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 3f4dd5c00b03..04e5e2a0fd6b 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -97,7 +97,8 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, if (j != scan_count) adis->xfer[j].cs_change = 1; adis->xfer[j].len = 2; - adis->xfer[j].delay_usecs = adis->data->read_delay; + adis->xfer[j].delay.value = adis->data->read_delay; + adis->xfer[j].delay.unit = SPI_DELAY_UNIT_USECS; if (j < scan_count) adis->xfer[j].tx_buf = &tx[j]; if (j >= 1) From 3543b1998dd396c66c7a9bce1a7962c10648de6e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 25 Feb 2020 12:33:17 +0200 Subject: [PATCH 86/98] iio: imu: adis: add doc-string for 'adis' struct This change adds a doc-string for the 'adis' struct. It details the fields and their roles. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- include/linux/iio/imu/adis.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index ac7cfd073804..63e456aa5c8a 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -73,6 +73,20 @@ struct adis_data { bool has_paging; }; +/** + * struct adis - ADIS device instance data + * @spi: Reference to SPI device which owns this ADIS IIO device + * @trig: IIO trigger object data + * @data: ADIS chip variant specific data + * @burst: ADIS burst transfer information + * @state_lock: Lock used by the device to protect state + * @msg: SPI message object + * @xfer: SPI transfer objects to be used for a @msg + * @current_page: Some ADIS devices have registers, this selects current page + * @buffer: Data buffer for information read from the device + * @tx: DMA safe TX buffer for SPI transfers + * @rx: DMA safe RX buffer for SPI transfers + */ struct adis { struct spi_device *spi; struct iio_trigger *trig; From 2dd86ba821330840661e7102e3304309299aa635 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 25 Feb 2020 12:33:18 +0200 Subject: [PATCH 87/98] iio: imu: adis: update 'adis_data' struct doc-string The doc-string has been neglected over time. This change updates it with all the missing info. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- include/linux/iio/imu/adis.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 63e456aa5c8a..20339025c75f 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -43,10 +43,14 @@ struct adis_timeout { * @diag_stat_reg: Register address of the DIAG_STAT register * @prod_id_reg: Register address of the PROD_ID register * @prod_id: Product ID code that should be expected when reading @prod_id_reg + * @self_test_mask: Bitmask of supported self-test operations * @self_test_reg: Register address to request self test command + * @self_test_no_autoclear: True if device's self-test needs clear of ctrl reg * @status_error_msgs: Array of error messgaes - * @status_error_mask: + * @status_error_mask: Bitmask of errors supported by the device * @timeouts: Chip specific delays + * @enable_irq: Hook for ADIS devices that have a special IRQ enable/disable + * @has_paging: True if ADIS device has paged registers */ struct adis_data { unsigned int read_delay; From 2303248b291f537f0414a644a53483c71023136f Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 25 Feb 2020 12:33:19 +0200 Subject: [PATCH 88/98] iio: imu: adis: add a note better explaining state_lock The 'state_lock' mutex was renamed from 'txrx_lock' in a previous patch and is intended to be used by ADIS drivers to protect the state of devices during consecutive R/W ops. The initial patch that introduced this change did not do a good [well, any] job at explaining this. This patch adds a comment to the 'state_lock' better explaining it's use. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- include/linux/iio/imu/adis.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 20339025c75f..dd8219138c2e 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -98,6 +98,17 @@ struct adis { const struct adis_data *data; struct adis_burst *burst; + /** + * The state_lock is meant to be used during operations that require + * a sequence of SPI R/W in order to protect the SPI transfer + * information (fields 'xfer', 'msg' & 'current_page') between + * potential concurrent accesses. + * This lock is used by all "adis_{functions}" that have to read/write + * registers. These functions also have unlocked variants + * (see "__adis_{functions}"), which don't hold this lock. + * This allows users of the ADIS library to group SPI R/W into + * the drivers, but they also must manage this lock themselves. + */ struct mutex state_lock; struct spi_message msg; struct spi_transfer *xfer; From 2ddc982a6c172c141183ac954d44f01bc4f2fe72 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Fri, 21 Feb 2020 14:06:55 +0200 Subject: [PATCH 89/98] iio: industrialio-core: Fix debugfs read Currently iio_debugfs_read_reg calls debugfs_reg_access every time it is ran. Reading the same hardware register multiple times during the same reading of a debugfs file can cause unintended effects. For example for each: cat iio:device0/direct_reg_access the file_operations.read function will be called at least twice. First will return the full length of the string in bytes and the second will return 0. This patch makes iio_debugfs_read_reg to call debugfs_reg_access only when the user's buffer position (*ppos) is 0. (meaning it is the beginning of a new reading of the debugfs file). Fixes: e553f182d55b ("staging: iio: core: Introduce debugfs support, add support for direct register access") Signed-off-by: Alexandru Ardelean Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 15 +++++++++++---- include/linux/iio/iio.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0b14666dff09..eac63c1bb8da 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -301,11 +301,14 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct iio_dev *indio_dev = file->private_data; - char buf[20]; unsigned val = 0; - ssize_t len; int ret; + if (*ppos > 0) + return simple_read_from_buffer(userbuf, count, ppos, + indio_dev->read_buf, + indio_dev->read_buf_len); + ret = indio_dev->info->debugfs_reg_access(indio_dev, indio_dev->cached_reg_addr, 0, &val); @@ -314,9 +317,13 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, return ret; } - len = snprintf(buf, sizeof(buf), "0x%X\n", val); + indio_dev->read_buf_len = snprintf(indio_dev->read_buf, + sizeof(indio_dev->read_buf), + "0x%X\n", val); - return simple_read_from_buffer(userbuf, count, ppos, buf, len); + return simple_read_from_buffer(userbuf, count, ppos, + indio_dev->read_buf, + indio_dev->read_buf_len); } static ssize_t iio_debugfs_write_reg(struct file *file, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 862ce0019eba..eed58ed2f368 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -568,6 +568,8 @@ struct iio_dev { #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned cached_reg_addr; + char read_buf[20]; + unsigned int read_buf_len; #endif }; From 95c72b78c3365d8f949c3ae315e7b1ded912a6da Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Fri, 21 Feb 2020 21:11:23 +0100 Subject: [PATCH 90/98] iio: pressure: icp10100: add driver for InvenSense ICP-101xx InvenSense ICP-101xx sensors are a family of barometric pressure and temperature sensor. These devices are I2C only and use a specific protocol of commands/responses. Data transfer is secured by using crc8. Driver provides processed pressure and raw temperature data. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 11 + drivers/iio/pressure/Makefile | 1 + drivers/iio/pressure/icp10100.c | 658 ++++++++++++++++++++++++++++++++ 3 files changed, 670 insertions(+) create mode 100644 drivers/iio/pressure/icp10100.c diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 9c2d9bf8f100..689b978db4f9 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -101,6 +101,17 @@ config HP03 To compile this driver as a module, choose M here: the module will be called hp03. +config ICP10100 + tristate "InvenSense ICP-101xx pressure and temperature sensor" + depends on I2C + select CRC8 + help + Say yes here to build support for InvenSense ICP-101xx barometric + pressure and temperature sensor. + + To compile this driver as a module, choose M here: the module + will be called icp10100. + config MPL115 tristate diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 5a79192d8cb5..083ae87ff48f 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_DPS310) += dps310.o obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_HP03) += hp03.o +obj-$(CONFIG_ICP10100) += icp10100.o obj-$(CONFIG_MPL115) += mpl115.o obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c new file mode 100644 index 000000000000..3ce4dc2f77ee --- /dev/null +++ b/drivers/iio/pressure/icp10100.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 InvenSense, Inc. + * + * Driver for InvenSense ICP-1010xx barometric pressure and temperature sensor. + * + * Datasheet: + * http://www.invensense.com/wp-content/uploads/2018/01/DS-000186-ICP-101xx-v1.2.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ICP10100_ID_REG_GET(_reg) ((_reg) & 0x003F) +#define ICP10100_ID_REG 0x08 +#define ICP10100_RESPONSE_WORD_LENGTH 3 +#define ICP10100_CRC8_WORD_LENGTH 2 +#define ICP10100_CRC8_POLYNOMIAL 0x31 +#define ICP10100_CRC8_INIT 0xFF + +enum icp10100_mode { + ICP10100_MODE_LP, /* Low power mode: 1x sampling */ + ICP10100_MODE_N, /* Normal mode: 2x sampling */ + ICP10100_MODE_LN, /* Low noise mode: 4x sampling */ + ICP10100_MODE_ULN, /* Ultra low noise mode: 8x sampling */ + ICP10100_MODE_NB, +}; + +struct icp10100_state { + struct mutex lock; + struct i2c_client *client; + struct regulator *vdd; + enum icp10100_mode mode; + int16_t cal[4]; +}; + +struct icp10100_command { + __be16 cmd; + unsigned long wait_us; + unsigned long wait_max_us; + size_t response_word_nb; +}; + +static const struct icp10100_command icp10100_cmd_soft_reset = { + .cmd = cpu_to_be16(0x805D), + .wait_us = 170, + .wait_max_us = 200, + .response_word_nb = 0, +}; + +static const struct icp10100_command icp10100_cmd_read_id = { + .cmd = cpu_to_be16(0xEFC8), + .wait_us = 0, + .response_word_nb = 1, +}; + +static const struct icp10100_command icp10100_cmd_read_otp = { + .cmd = cpu_to_be16(0xC7F7), + .wait_us = 0, + .response_word_nb = 1, +}; + +static const struct icp10100_command icp10100_cmd_measure[] = { + [ICP10100_MODE_LP] = { + .cmd = cpu_to_be16(0x401A), + .wait_us = 1800, + .wait_max_us = 2000, + .response_word_nb = 3, + }, + [ICP10100_MODE_N] = { + .cmd = cpu_to_be16(0x48A3), + .wait_us = 6300, + .wait_max_us = 6500, + .response_word_nb = 3, + }, + [ICP10100_MODE_LN] = { + .cmd = cpu_to_be16(0x5059), + .wait_us = 23800, + .wait_max_us = 24000, + .response_word_nb = 3, + }, + [ICP10100_MODE_ULN] = { + .cmd = cpu_to_be16(0x58E0), + .wait_us = 94500, + .wait_max_us = 94700, + .response_word_nb = 3, + }, +}; + +static const uint8_t icp10100_switch_mode_otp[] = + {0xC5, 0x95, 0x00, 0x66, 0x9c}; + +DECLARE_CRC8_TABLE(icp10100_crc8_table); + +static inline int icp10100_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + int ret; + + ret = i2c_transfer(adap, msgs, num); + if (ret < 0) + return ret; + + if (ret != num) + return -EIO; + + return 0; +} + +static int icp10100_send_cmd(struct icp10100_state *st, + const struct icp10100_command *cmd, + __be16 *buf, size_t buf_len) +{ + size_t size = cmd->response_word_nb * ICP10100_RESPONSE_WORD_LENGTH; + uint8_t data[16]; + uint8_t *ptr; + uint8_t *buf_ptr = (uint8_t *)buf; + struct i2c_msg msgs[2] = { + { + .addr = st->client->addr, + .flags = 0, + .len = 2, + .buf = (uint8_t *)&cmd->cmd, + }, { + .addr = st->client->addr, + .flags = I2C_M_RD, + .len = size, + .buf = data, + }, + }; + uint8_t crc; + unsigned int i; + int ret; + + if (size > sizeof(data)) + return -EINVAL; + + if (cmd->response_word_nb > 0 && + (buf == NULL || buf_len < (cmd->response_word_nb * 2))) + return -EINVAL; + + dev_dbg(&st->client->dev, "sending cmd %#x\n", be16_to_cpu(cmd->cmd)); + + if (cmd->response_word_nb > 0 && cmd->wait_us == 0) { + /* direct command-response without waiting */ + ret = icp10100_i2c_xfer(st->client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (ret) + return ret; + } else { + /* transfer command write */ + ret = icp10100_i2c_xfer(st->client->adapter, &msgs[0], 1); + if (ret) + return ret; + if (cmd->wait_us > 0) + usleep_range(cmd->wait_us, cmd->wait_max_us); + /* transfer response read if needed */ + if (cmd->response_word_nb > 0) { + ret = icp10100_i2c_xfer(st->client->adapter, &msgs[1], 1); + if (ret) + return ret; + } else { + return 0; + } + } + + /* process read words with crc checking */ + for (i = 0; i < cmd->response_word_nb; ++i) { + ptr = &data[i * ICP10100_RESPONSE_WORD_LENGTH]; + crc = crc8(icp10100_crc8_table, ptr, ICP10100_CRC8_WORD_LENGTH, + ICP10100_CRC8_INIT); + if (crc != ptr[ICP10100_CRC8_WORD_LENGTH]) { + dev_err(&st->client->dev, "crc error recv=%#x calc=%#x\n", + ptr[ICP10100_CRC8_WORD_LENGTH], crc); + return -EIO; + } + *buf_ptr++ = ptr[0]; + *buf_ptr++ = ptr[1]; + } + + return 0; +} + +static int icp10100_read_cal_otp(struct icp10100_state *st) +{ + __be16 val; + int i; + int ret; + + /* switch into OTP read mode */ + ret = i2c_master_send(st->client, icp10100_switch_mode_otp, + ARRAY_SIZE(icp10100_switch_mode_otp)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(icp10100_switch_mode_otp)) + return -EIO; + + /* read 4 calibration values */ + for (i = 0; i < 4; ++i) { + ret = icp10100_send_cmd(st, &icp10100_cmd_read_otp, + &val, sizeof(val)); + if (ret) + return ret; + st->cal[i] = be16_to_cpu(val); + dev_dbg(&st->client->dev, "cal[%d] = %d\n", i, st->cal[i]); + } + + return 0; +} + +static int icp10100_init_chip(struct icp10100_state *st) +{ + __be16 val; + uint16_t id; + int ret; + + /* read and check id */ + ret = icp10100_send_cmd(st, &icp10100_cmd_read_id, &val, sizeof(val)); + if (ret) + return ret; + id = ICP10100_ID_REG_GET(be16_to_cpu(val)); + if (id != ICP10100_ID_REG) { + dev_err(&st->client->dev, "invalid id %#x\n", id); + return -ENODEV; + } + + /* read calibration data from OTP */ + ret = icp10100_read_cal_otp(st); + if (ret) + return ret; + + /* reset chip */ + return icp10100_send_cmd(st, &icp10100_cmd_soft_reset, NULL, 0); +} + +static int icp10100_get_measures(struct icp10100_state *st, + uint32_t *pressure, uint16_t *temperature) +{ + const struct icp10100_command *cmd; + __be16 measures[3]; + int ret; + + pm_runtime_get_sync(&st->client->dev); + + mutex_lock(&st->lock); + cmd = &icp10100_cmd_measure[st->mode]; + ret = icp10100_send_cmd(st, cmd, measures, sizeof(measures)); + mutex_unlock(&st->lock); + if (ret) + goto error_measure; + + *pressure = (be16_to_cpu(measures[0]) << 8) | + (be16_to_cpu(measures[1]) >> 8); + *temperature = be16_to_cpu(measures[2]); + + pm_runtime_mark_last_busy(&st->client->dev); +error_measure: + pm_runtime_put_autosuspend(&st->client->dev); + return ret; +} + +static uint32_t icp10100_get_pressure(struct icp10100_state *st, + uint32_t raw_pressure, uint16_t raw_temp) +{ + static int32_t p_calib[] = {45000, 80000, 105000}; + static int32_t lut_lower = 3670016; + static int32_t lut_upper = 12058624; + static int32_t inv_quadr_factor = 16777216; + static int32_t offset_factor = 2048; + int64_t val1, val2; + int32_t p_lut[3]; + int32_t t, t_square; + int64_t a, b, c; + uint32_t pressure_mPa; + + dev_dbg(&st->client->dev, "raw: pressure = %u, temp = %u\n", + raw_pressure, raw_temp); + + /* compute p_lut values */ + t = (int32_t)raw_temp - 32768; + t_square = t * t; + val1 = (int64_t)st->cal[0] * (int64_t)t_square; + p_lut[0] = lut_lower + (int32_t)div_s64(val1, inv_quadr_factor); + val1 = (int64_t)st->cal[1] * (int64_t)t_square; + p_lut[1] = offset_factor * st->cal[3] + + (int32_t)div_s64(val1, inv_quadr_factor); + val1 = (int64_t)st->cal[2] * (int64_t)t_square; + p_lut[2] = lut_upper + (int32_t)div_s64(val1, inv_quadr_factor); + dev_dbg(&st->client->dev, "p_lut = [%d, %d, %d]\n", + p_lut[0], p_lut[1], p_lut[2]); + + /* compute a, b, c factors */ + val1 = (int64_t)p_lut[0] * (int64_t)p_lut[1] * + (int64_t)(p_calib[0] - p_calib[1]) + + (int64_t)p_lut[1] * (int64_t)p_lut[2] * + (int64_t)(p_calib[1] - p_calib[2]) + + (int64_t)p_lut[2] * (int64_t)p_lut[0] * + (int64_t)(p_calib[2] - p_calib[0]); + val2 = (int64_t)p_lut[2] * (int64_t)(p_calib[0] - p_calib[1]) + + (int64_t)p_lut[0] * (int64_t)(p_calib[1] - p_calib[2]) + + (int64_t)p_lut[1] * (int64_t)(p_calib[2] - p_calib[0]); + c = div64_s64(val1, val2); + dev_dbg(&st->client->dev, "val1 = %lld, val2 = %lld, c = %lld\n", + val1, val2, c); + val1 = (int64_t)p_calib[0] * (int64_t)p_lut[0] - + (int64_t)p_calib[1] * (int64_t)p_lut[1] - + (int64_t)(p_calib[1] - p_calib[0]) * c; + val2 = (int64_t)p_lut[0] - (int64_t)p_lut[1]; + a = div64_s64(val1, val2); + dev_dbg(&st->client->dev, "val1 = %lld, val2 = %lld, a = %lld\n", + val1, val2, a); + b = ((int64_t)p_calib[0] - a) * ((int64_t)p_lut[0] + c); + dev_dbg(&st->client->dev, "b = %lld\n", b); + + /* + * pressure_Pa = a + (b / (c + raw_pressure)) + * pressure_mPa = 1000 * pressure_Pa + */ + pressure_mPa = 1000LL * a + div64_s64(1000LL * b, c + raw_pressure); + + return pressure_mPa; +} + +static int icp10100_read_raw_measures(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct icp10100_state *st = iio_priv(indio_dev); + uint32_t raw_pressure; + uint16_t raw_temp; + uint32_t pressure_mPa; + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = icp10100_get_measures(st, &raw_pressure, &raw_temp); + if (ret) + goto error_release; + + switch (chan->type) { + case IIO_PRESSURE: + pressure_mPa = icp10100_get_pressure(st, raw_pressure, + raw_temp); + /* mPa to kPa */ + *val = pressure_mPa / 1000000; + *val2 = pressure_mPa % 1000000; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_TEMP: + *val = raw_temp; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + }; + +error_release: + iio_device_release_direct_mode(indio_dev); + return ret; +} + +static int icp10100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct icp10100_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + return icp10100_read_raw_measures(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* 1000 * 175°C / 65536 in m°C */ + *val = 2; + *val2 = 670288; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + /* 1000 * -45°C in m°C */ + *val = -45000; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + mutex_lock(&st->lock); + *val = 1 << st->mode; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int icp10100_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + static int oversamplings[] = {1, 2, 4, 8}; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = oversamplings; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(oversamplings); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int icp10100_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct icp10100_state *st = iio_priv(indio_dev); + unsigned int mode; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + /* oversampling is always positive and a power of 2 */ + if (val <= 0 || !is_power_of_2(val)) + return -EINVAL; + mode = ilog2(val); + if (mode >= ICP10100_MODE_NB) + return -EINVAL; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + st->mode = mode; + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + return 0; + default: + return -EINVAL; + } +} + +static int icp10100_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info icp10100_info = { + .read_raw = icp10100_read_raw, + .read_avail = icp10100_read_avail, + .write_raw = icp10100_write_raw, + .write_raw_get_fmt = icp10100_write_raw_get_fmt, +}; + +static const struct iio_chan_spec icp10100_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, +}; + +static int icp10100_enable_regulator(struct icp10100_state *st) +{ + int ret; + + ret = regulator_enable(st->vdd); + if (ret) + return ret; + msleep(100); + + return 0; +} + +static void icp10100_disable_regulator_action(void *data) +{ + struct icp10100_state *st = data; + int ret; + + ret = regulator_disable(st->vdd); + if (ret) + dev_err(&st->client->dev, "error %d disabling vdd\n", ret); +} + +static void icp10100_pm_disable(void *data) +{ + struct device *dev = data; + + pm_runtime_put_sync_suspend(dev); + pm_runtime_disable(dev); +} + +static int icp10100_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct icp10100_state *st; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "plain i2c transactions not supported\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + i2c_set_clientdata(client, indio_dev); + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = icp10100_channels; + indio_dev->num_channels = ARRAY_SIZE(icp10100_channels); + indio_dev->info = &icp10100_info; + + st = iio_priv(indio_dev); + mutex_init(&st->lock); + st->client = client; + st->mode = ICP10100_MODE_N; + + st->vdd = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(st->vdd)) + return PTR_ERR(st->vdd); + + ret = icp10100_enable_regulator(st); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, + icp10100_disable_regulator_action, st); + if (ret) + return ret; + + /* has to be done before the first i2c communication */ + crc8_populate_msb(icp10100_crc8_table, ICP10100_CRC8_POLYNOMIAL); + + ret = icp10100_init_chip(st); + if (ret) { + dev_err(&client->dev, "init chip error %d\n", ret); + return ret; + } + + /* enable runtime pm with autosuspend delay of 2s */ + pm_runtime_get_noresume(&client->dev); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 2000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put(&client->dev); + ret = devm_add_action_or_reset(&client->dev, icp10100_pm_disable, + &client->dev); + if (ret) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static int __maybe_unused icp10100_suspend(struct device *dev) +{ + struct icp10100_state *st = iio_priv(dev_get_drvdata(dev)); + int ret; + + mutex_lock(&st->lock); + ret = regulator_disable(st->vdd); + mutex_unlock(&st->lock); + + return ret; +} + +static int __maybe_unused icp10100_resume(struct device *dev) +{ + struct icp10100_state *st = iio_priv(dev_get_drvdata(dev)); + int ret; + + mutex_lock(&st->lock); + + ret = icp10100_enable_regulator(st); + if (ret) + goto out_unlock; + + /* reset chip */ + ret = icp10100_send_cmd(st, &icp10100_cmd_soft_reset, NULL, 0); + +out_unlock: + mutex_unlock(&st->lock); + return ret; +} + +static UNIVERSAL_DEV_PM_OPS(icp10100_pm, icp10100_suspend, icp10100_resume, + NULL); + +static const struct of_device_id icp10100_of_match[] = { + { + .compatible = "invensense,icp10100", + }, + { } +}; +MODULE_DEVICE_TABLE(of, icp10100_of_match); + +static const struct i2c_device_id icp10100_id[] = { + { "icp10100", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, icp10100_id); + +static struct i2c_driver icp10100_driver = { + .driver = { + .name = "icp10100", + .pm = &icp10100_pm, + .of_match_table = of_match_ptr(icp10100_of_match), + }, + .probe = icp10100_probe, + .id_table = icp10100_id, +}; +module_i2c_driver(icp10100_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense icp10100 driver"); +MODULE_LICENSE("GPL"); From de65d0556343132222a9f48417c2bad82e3a7503 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Sat, 22 Feb 2020 11:49:58 -0500 Subject: [PATCH 91/98] counter: 104-quad-8: Support Filter Clock Prescaler The ACCES 104-QUAD-8 series does active filtering on the quadrature input signals via the PC/104 bus clock (OSC 14.318 MHz). This patch exposes the filter clock prescaler available on each channel. Signed-off-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-counter-104-quad-8 | 7 +++ drivers/counter/104-quad-8.c | 61 ++++++++++++++++++- drivers/iio/pressure/icp10100.c | 2 +- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 index 46b1f33b2fce..3c905d3cf5d7 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 +++ b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 @@ -1,3 +1,10 @@ +What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler +KernelVersion: 5.7 +Contact: linux-iio@vger.kernel.org +Description: + Filter clock factor for input Signal Y. This prescaler value + affects the inputs of both quadrature pair signals. + What: /sys/bus/counter/devices/counterX/signalY/index_polarity KernelVersion: 5.2 Contact: linux-iio@vger.kernel.org diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 17e67a84777d..0cfc813ee2cb 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -43,6 +43,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); */ struct quad8_iio { struct counter_device counter; + unsigned int fck_prescaler[QUAD8_NUM_COUNTERS]; unsigned int preset[QUAD8_NUM_COUNTERS]; unsigned int count_mode[QUAD8_NUM_COUNTERS]; unsigned int quadrature_mode[QUAD8_NUM_COUNTERS]; @@ -84,6 +85,8 @@ struct quad8_iio { #define QUAD8_RLD_PRESET_CNTR 0x08 /* Transfer Counter to Output Latch */ #define QUAD8_RLD_CNTR_OUT 0x10 +/* Transfer Preset Register LSB to FCK Prescaler */ +#define QUAD8_RLD_PRESET_PSC 0x18 #define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 #define QUAD8_CMR_QUADRATURE_X1 0x08 @@ -1140,6 +1143,50 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, return len; } +static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter, + struct counter_signal *signal, void *private, char *buf) +{ + const struct quad8_iio *const priv = counter->priv; + const size_t channel_id = signal->id / 2; + + return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]); +} + +static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, + struct counter_signal *signal, void *private, const char *buf, + size_t len) +{ + struct quad8_iio *const priv = counter->priv; + const size_t channel_id = signal->id / 2; + const int base_offset = priv->base + 2 * channel_id; + u8 prescaler; + int ret; + + ret = kstrtou8(buf, 0, &prescaler); + if (ret) + return ret; + + priv->fck_prescaler[channel_id] = prescaler; + + /* Reset Byte Pointer */ + outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); + + /* Set filter clock factor */ + outb(prescaler, base_offset); + outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC, + base_offset + 1); + + return len; +} + +static const struct counter_signal_ext quad8_signal_ext[] = { + { + .name = "filter_clock_prescaler", + .read = quad8_signal_fck_prescaler_read, + .write = quad8_signal_fck_prescaler_write + } +}; + static const struct counter_signal_ext quad8_index_ext[] = { COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum), COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity", &quad8_index_pol_enum), @@ -1147,9 +1194,11 @@ static const struct counter_signal_ext quad8_index_ext[] = { COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum) }; -#define QUAD8_QUAD_SIGNAL(_id, _name) { \ - .id = (_id), \ - .name = (_name) \ +#define QUAD8_QUAD_SIGNAL(_id, _name) { \ + .id = (_id), \ + .name = (_name), \ + .ext = quad8_signal_ext, \ + .num_ext = ARRAY_SIZE(quad8_signal_ext) \ } #define QUAD8_INDEX_SIGNAL(_id, _name) { \ @@ -1314,6 +1363,12 @@ static int quad8_probe(struct device *dev, unsigned int id) base_offset = base[id] + 2 * i; /* Reset Byte Pointer */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); + /* Reset filter clock factor */ + outb(0, base_offset); + outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC, + base_offset + 1); + /* Reset Byte Pointer */ + outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); /* Reset Preset Register */ for (j = 0; j < 3; j++) outb(0x00, base_offset); diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c index 3ce4dc2f77ee..06cb5b63a189 100644 --- a/drivers/iio/pressure/icp10100.c +++ b/drivers/iio/pressure/icp10100.c @@ -364,7 +364,7 @@ static int icp10100_read_raw_measures(struct iio_dev *indio_dev, default: ret = -EINVAL; break; - }; + } error_release: iio_device_release_direct_mode(indio_dev); From 954ab5cc5f3e0bc764df81a13f065936a85e2084 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Sun, 1 Mar 2020 17:07:19 -0500 Subject: [PATCH 92/98] counter: 104-quad-8: Support Differential Encoder Cable Status The ACCES 104-QUAD-8 series provides status information about the connection state of the differential encoder cable inputs. This patch implements support to expose such information from these devices. Signed-off-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-counter-104-quad-8 | 18 +++++ drivers/counter/104-quad-8.c | 75 +++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 index 3c905d3cf5d7..eac32180c40d 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 +++ b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 @@ -1,3 +1,21 @@ +What: /sys/bus/counter/devices/counterX/signalY/cable_fault +KernelVersion: 5.7 +Contact: linux-iio@vger.kernel.org +Description: + Read-only attribute that indicates whether a differential + encoder cable fault (not connected or loose wires) is detected + for the respective channel of Signal Y. Valid attribute values + are boolean. Detection must first be enabled via the + corresponding cable_fault_enable attribute. + +What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable +KernelVersion: 5.7 +Contact: linux-iio@vger.kernel.org +Description: + Whether detection of differential encoder cable faults for the + respective channel of Signal Y is enabled. Valid attribute + values are boolean. + What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler KernelVersion: 5.7 Contact: linux-iio@vger.kernel.org diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 0cfc813ee2cb..9dab190c49b0 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -31,6 +31,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); /** * struct quad8_iio - IIO device private data structure * @counter: instance of the counter_device + * @fck_prescaler: array of filter clock prescaler configurations * @preset: array of preset values * @count_mode: array of count mode configurations * @quadrature_mode: array of quadrature mode configurations @@ -39,6 +40,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); * @preset_enable: array of set_to_preset_on_index attribute configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations + * @cable_fault_enable: differential encoder cable status enable configurations * @base: base port address of the IIO device */ struct quad8_iio { @@ -52,11 +54,13 @@ struct quad8_iio { unsigned int preset_enable[QUAD8_NUM_COUNTERS]; unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; unsigned int index_polarity[QUAD8_NUM_COUNTERS]; + unsigned int cable_fault_enable; unsigned int base; }; #define QUAD8_REG_CHAN_OP 0x11 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16 +#define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17 /* Borrow Toggle flip-flop */ #define QUAD8_FLAG_BT BIT(0) /* Carry Toggle flip-flop */ @@ -1143,6 +1147,66 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, return len; } +static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter, + struct counter_signal *signal, + void *private, char *buf) +{ + const struct quad8_iio *const priv = counter->priv; + const size_t channel_id = signal->id / 2; + const bool disabled = !(priv->cable_fault_enable & BIT(channel_id)); + unsigned int status; + unsigned int fault; + + if (disabled) + return -EINVAL; + + /* Logic 0 = cable fault */ + status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); + + /* Mask respective channel and invert logic */ + fault = !(status & BIT(channel_id)); + + return sprintf(buf, "%u\n", fault); +} + +static ssize_t quad8_signal_cable_fault_enable_read( + struct counter_device *counter, struct counter_signal *signal, + void *private, char *buf) +{ + const struct quad8_iio *const priv = counter->priv; + const size_t channel_id = signal->id / 2; + const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id)); + + return sprintf(buf, "%u\n", enb); +} + +static ssize_t quad8_signal_cable_fault_enable_write( + struct counter_device *counter, struct counter_signal *signal, + void *private, const char *buf, size_t len) +{ + struct quad8_iio *const priv = counter->priv; + const size_t channel_id = signal->id / 2; + bool enable; + int ret; + unsigned int cable_fault_enable; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + if (enable) + priv->cable_fault_enable |= BIT(channel_id); + else + priv->cable_fault_enable &= ~BIT(channel_id); + + /* Enable is active low in Differential Encoder Cable Status register */ + cable_fault_enable = ~priv->cable_fault_enable; + + outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); + + return len; +} + static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter, struct counter_signal *signal, void *private, char *buf) { @@ -1180,6 +1244,15 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, } static const struct counter_signal_ext quad8_signal_ext[] = { + { + .name = "cable_fault", + .read = quad8_signal_cable_fault_read + }, + { + .name = "cable_fault_enable", + .read = quad8_signal_cable_fault_enable_read, + .write = quad8_signal_cable_fault_enable_write + }, { .name = "filter_clock_prescaler", .read = quad8_signal_fck_prescaler_read, @@ -1383,6 +1456,8 @@ static int quad8_probe(struct device *dev, unsigned int id) /* Disable index function; negative index polarity */ outb(QUAD8_CTR_IDR, base_offset + 1); } + /* Disable Differential Encoder Cable Status for all channels */ + outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS); /* Enable all counters */ outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); From 5da06e6cd94d040755f086aa95062b7fc4750434 Mon Sep 17 00:00:00 2001 From: Rohit Sarkar Date: Sat, 29 Feb 2020 20:07:21 +0530 Subject: [PATCH 93/98] iio: add a TODO This patch adds a TODO file with some work items added with reference to the conversation in [1]. A TODO file is immensely useful while onboarding new contributors who are looking for some low hanging fruit to get their foot into the door. Since these items affect all drivers the file has been placed in the root iio directory instead of augmenting the staging TODO. Thanks, Rohit [1]: https://marc.info/?l=linux-iio&m=158256721009892&w=2 Signed-off-by: Rohit Sarkar Signed-off-by: Jonathan Cameron --- drivers/iio/TODO | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 drivers/iio/TODO diff --git a/drivers/iio/TODO b/drivers/iio/TODO new file mode 100644 index 000000000000..7d7326b7085a --- /dev/null +++ b/drivers/iio/TODO @@ -0,0 +1,19 @@ +2020-02-29 + +Documentation + - Binding docs for devices that are obviously used via device +tree + - Yaml conversions for abandoned drivers + - ABI Documentation + - Audit driviers/iio/staging/Documentation + +- Replace iio_dev->mlock by either a local lock or use +iio_claim_direct.(Requires analysis of the purpose of the lock.) + +- Converting drivers from device tree centric to more generic +property handlers. + +- Refactor old platform_data constructs from drivers and convert it +to state struct and using property handlers and readers. + +Mailing list: linux-iio@vger.kernel.org From e23aaafcdb395a3114f5f6c25070170d8c349d33 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Tue, 3 Mar 2020 15:59:44 +0100 Subject: [PATCH 94/98] iio: trigger: stm32-timer: rename enabled flag "clk_enabled" flag reflects enabled state of the timer, for master mode, slave mode or trigger (with sampling_frequency). So rename it to "enabled". Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/stm32-timer-trigger.c | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 16a3b6bed7b6..32e1249f2b91 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -79,7 +79,7 @@ struct stm32_timer_trigger { struct device *dev; struct regmap *regmap; struct clk *clk; - bool clk_enabled; + bool enabled; u32 max_arr; const void *triggers; const void *valids; @@ -140,8 +140,8 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, return -EBUSY; mutex_lock(&priv->lock); - if (!priv->clk_enabled) { - priv->clk_enabled = true; + if (!priv->enabled) { + priv->enabled = true; clk_enable(priv->clk); } @@ -185,8 +185,8 @@ static void stm32_timer_stop(struct stm32_timer_trigger *priv) /* Make sure that registers are updated */ regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); - if (priv->clk_enabled) { - priv->clk_enabled = false; + if (priv->enabled) { + priv->enabled = false; clk_disable(priv->clk); } mutex_unlock(&priv->lock); @@ -305,9 +305,9 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, if (!strncmp(master_mode_table[i], buf, strlen(master_mode_table[i]))) { mutex_lock(&priv->lock); - if (!priv->clk_enabled) { + if (!priv->enabled) { /* Clock should be enabled first */ - priv->clk_enabled = true; + priv->enabled = true; clk_enable(priv->clk); } regmap_update_bits(priv->regmap, TIM_CR2, mask, @@ -476,8 +476,8 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_ENABLE: mutex_lock(&priv->lock); if (val) { - if (!priv->clk_enabled) { - priv->clk_enabled = true; + if (!priv->enabled) { + priv->enabled = true; clk_enable(priv->clk); } regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, @@ -485,8 +485,8 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, } else { regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); - if (priv->clk_enabled) { - priv->clk_enabled = false; + if (priv->enabled) { + priv->enabled = false; clk_disable(priv->clk); } } @@ -594,9 +594,9 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, * enable counter clock, so it can use it. Keeps it in sync with CEN. */ mutex_lock(&priv->lock); - if (sms == 6 && !priv->clk_enabled) { + if (sms == 6 && !priv->enabled) { clk_enable(priv->clk); - priv->clk_enabled = true; + priv->enabled = true; } mutex_unlock(&priv->lock); @@ -806,7 +806,7 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev) if (!(val & TIM_CCER_CCXE)) regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); - if (priv->clk_enabled) + if (priv->enabled) clk_disable(priv->clk); return 0; From 736e19e684b3359b61740f3b75014c375c57bf9d Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Tue, 3 Mar 2020 15:59:45 +0100 Subject: [PATCH 95/98] iio: trigger: stm32-timer: add power management support Add suspend/resume PM sleep ops to stm32-timer-trigger driver. Register contents may be lost depending on low power modes. When going to low power, enforce the timer isn't active. Gracefully restore its state upon resume in case it's been left enabled prior to suspend. Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/stm32-timer-trigger.c | 63 +++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 32e1249f2b91..37545a8a02b7 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -75,6 +75,15 @@ static const void *stm32h7_valids_table[][MAX_VALIDS] = { { }, /* timer 17 */ }; +struct stm32_timer_trigger_regs { + u32 cr1; + u32 cr2; + u32 psc; + u32 arr; + u32 cnt; + u32 smcr; +}; + struct stm32_timer_trigger { struct device *dev; struct regmap *regmap; @@ -86,6 +95,7 @@ struct stm32_timer_trigger { bool has_trgo2; struct mutex lock; /* concurrent sysfs configuration */ struct list_head tr_list; + struct stm32_timer_trigger_regs bak; }; struct stm32_timer_trigger_cfg { @@ -812,6 +822,58 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev) +{ + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); + + /* Only take care of enabled timer: don't disturb other MFD child */ + if (priv->enabled) { + /* Backup registers that may get lost in low power mode */ + regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1); + regmap_read(priv->regmap, TIM_CR2, &priv->bak.cr2); + regmap_read(priv->regmap, TIM_PSC, &priv->bak.psc); + regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr); + regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt); + regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr); + + /* Disable the timer */ + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + clk_disable(priv->clk); + } + + return 0; +} + +static int __maybe_unused stm32_timer_trigger_resume(struct device *dev) +{ + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); + int ret; + + if (priv->enabled) { + ret = clk_enable(priv->clk); + if (ret) + return ret; + + /* restore master/slave modes */ + regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); + regmap_write(priv->regmap, TIM_CR2, priv->bak.cr2); + + /* restore sampling_frequency (trgo / trgo2 triggers) */ + regmap_write(priv->regmap, TIM_PSC, priv->bak.psc); + regmap_write(priv->regmap, TIM_ARR, priv->bak.arr); + regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt); + + /* Also re-enables the timer */ + regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops, + stm32_timer_trigger_suspend, + stm32_timer_trigger_resume); + static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = { .valids_table = valids_table, .num_valids_table = ARRAY_SIZE(valids_table), @@ -840,6 +902,7 @@ static struct platform_driver stm32_timer_trigger_driver = { .driver = { .name = "stm32-timer-trigger", .of_match_table = stm32_trig_of_match, + .pm = &stm32_timer_trigger_pm_ops, }, }; module_platform_driver(stm32_timer_trigger_driver); From d7cf5f60474cf591db771bc15e3b7320be5399e0 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 4 Mar 2020 11:41:05 +0200 Subject: [PATCH 96/98] iio: potentiostat: lmp9100: fix iio_triggered_buffer_{predisable,postenable} positions The iio_triggered_buffer_{predisable,postenable} functions attach/detach the poll functions. For the predisable hook, the disable code should occur before detaching the poll func, and for the postenable hook, the poll func should be attached before the enable code. The lmp9100 was attaching a poll function but never detaching it via any IIO disable hook. This change adds the detaching of the poll function, and moves/renames lmp91000_buffer_preenable() function to lmp91000_buffer_postenable(). The idea is to make it more symmetrical, so that when the iio_triggered_buffer_{predisable,postenable} functions get removed, it's easier to see. Fixes: 67e17300dc1d7 ("iio: potentiostat: add LMP91000 support") Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/potentiostat/lmp91000.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index a0e5f530faa9..2cb11da18e0f 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -275,11 +275,20 @@ static int lmp91000_buffer_cb(const void *val, void *private) static const struct iio_trigger_ops lmp91000_trigger_ops = { }; -static int lmp91000_buffer_preenable(struct iio_dev *indio_dev) +static int lmp91000_buffer_postenable(struct iio_dev *indio_dev) { struct lmp91000_data *data = iio_priv(indio_dev); + int err; - return iio_channel_start_all_cb(data->cb_buffer); + err = iio_triggered_buffer_postenable(indio_dev); + if (err) + return err; + + err = iio_channel_start_all_cb(data->cb_buffer); + if (err) + iio_triggered_buffer_predisable(indio_dev); + + return err; } static int lmp91000_buffer_predisable(struct iio_dev *indio_dev) @@ -288,12 +297,11 @@ static int lmp91000_buffer_predisable(struct iio_dev *indio_dev) iio_channel_stop_all_cb(data->cb_buffer); - return 0; + return iio_triggered_buffer_predisable(indio_dev); } static const struct iio_buffer_setup_ops lmp91000_buffer_setup_ops = { - .preenable = lmp91000_buffer_preenable, - .postenable = iio_triggered_buffer_postenable, + .postenable = lmp91000_buffer_postenable, .predisable = lmp91000_buffer_predisable, }; From a305b0c9edd29ec6f571179c34d5529fae50d6b9 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 6 Mar 2020 13:11:37 +0200 Subject: [PATCH 97/98] iio: light: gp2ap020a00f: fix iio_triggered_buffer_{predisable,postenable} positions The iio_triggered_buffer_{predisable,postenable} functions attach/detach the poll functions. For the predisable hook, the disable code should occur before detaching the poll func, and for the postenable hook, the poll func should be attached before the enable code. This change moves the postenable/predisable hooks into the correct positions. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 4d70c5bf35da..7fbbce0d4bc7 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1390,6 +1390,12 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) mutex_lock(&data->lock); + err = iio_triggered_buffer_postenable(indio_dev); + if (err < 0) { + mutex_unlock(&data->lock); + return err; + } + /* * Enable triggers according to the scan_mask. Enabling either * LIGHT_CLEAR or LIGHT_IR scan mode results in enabling ALS @@ -1420,14 +1426,12 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) goto error_unlock; data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); - if (!data->buffer) { + if (!data->buffer) err = -ENOMEM; - goto error_unlock; - } - - err = iio_triggered_buffer_postenable(indio_dev); error_unlock: + if (err < 0) + iio_triggered_buffer_predisable(indio_dev); mutex_unlock(&data->lock); return err; @@ -1436,14 +1440,10 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) { struct gp2ap020a00f_data *data = iio_priv(indio_dev); - int i, err; + int i, err = 0; mutex_lock(&data->lock); - err = iio_triggered_buffer_predisable(indio_dev); - if (err < 0) - goto error_unlock; - for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) { switch (i) { @@ -1465,7 +1465,8 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) if (err == 0) kfree(data->buffer); -error_unlock: + iio_triggered_buffer_predisable(indio_dev); + mutex_unlock(&data->lock); return err; From fe297f8f048a7a0663479dcf6447ec450b53b905 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 4 Mar 2020 15:55:44 +0200 Subject: [PATCH 98/98] iio: dac: Kconfig: sort symbols alphabetically While adding a new device, I noticed these aren't sorted alphabetically [as in the Makefile], which messed my head-up. This change sorts the devices alphabetically in the drivers/iio/dac/Kconfig file as well. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 62 ++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 63916a6142ec..93744011b63f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -121,27 +121,6 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. -config LTC1660 - tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver" - depends on SPI - help - Say yes here to build support for Linear Technology - LTC1660 and LTC1665 Digital to Analog Converters. - - To compile this driver as a module, choose M here: the - module will be called ltc1660. - -config LTC2632 - tristate "Linear Technology LTC2632-12/10/8 and LTC2636-12/10/8 DAC spi driver" - depends on SPI - help - Say yes here to build support for Linear Technology - LTC2632-12, LTC2632-10, LTC2632-8, LTC2636-12, LTC2636-10 and - LTC2636-8 converters (DAC). - - To compile this driver as a module, choose M here: the - module will be called ltc2632. - config AD5686 tristate @@ -240,16 +219,6 @@ config AD7303 To compile this driver as module choose M here: the module will be called ad7303. -config CIO_DAC - tristate "Measurement Computing CIO-DAC IIO driver" - depends on X86 && (ISA_BUS || PC104) - select ISA_BUS_API - help - Say yes here to build support for the Measurement Computing CIO-DAC - analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The - base port addresses for the devices may be configured via the base - array module parameter. - config AD8801 tristate "Analog Devices AD8801/AD8803 DAC driver" depends on SPI_MASTER @@ -260,6 +229,16 @@ config AD8801 To compile this driver as a module choose M here: the module will be called ad8801. +config CIO_DAC + tristate "Measurement Computing CIO-DAC IIO driver" + depends on X86 && (ISA_BUS || PC104) + select ISA_BUS_API + help + Say yes here to build support for the Measurement Computing CIO-DAC + analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The + base port addresses for the devices may be configured via the base + array module parameter. + config DPOT_DAC tristate "DAC emulation using a DPOT" depends on OF @@ -289,6 +268,27 @@ config LPC18XX_DAC To compile this driver as a module, choose M here: the module will be called lpc18xx_dac. +config LTC1660 + tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver" + depends on SPI + help + Say yes here to build support for Linear Technology + LTC1660 and LTC1665 Digital to Analog Converters. + + To compile this driver as a module, choose M here: the + module will be called ltc1660. + +config LTC2632 + tristate "Linear Technology LTC2632-12/10/8 and LTC2636-12/10/8 DAC spi driver" + depends on SPI + help + Say yes here to build support for Linear Technology + LTC2632-12, LTC2632-10, LTC2632-8, LTC2636-12, LTC2636-10 and + LTC2636-8 converters (DAC). + + To compile this driver as a module, choose M here: the + module will be called ltc2632. + config M62332 tristate "Mitsubishi M62332 DAC driver" depends on I2C