diff options
90 files changed, 7368 insertions, 2729 deletions
diff --git a/Documentation/devicetree/bindings/counter/ti-eqep.yaml b/Documentation/devicetree/bindings/counter/ti-eqep.yaml new file mode 100644 index 000000000000..85f1ff83afe7 --- /dev/null +++ b/Documentation/devicetree/bindings/counter/ti-eqep.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/ti-eqep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) Module + +maintainers: + - David Lechner <david@lechnology.com> + +properties: + compatible: + const: ti,am3352-eqep + + reg: + maxItems: 1 + + interrupts: + description: The eQEP event interrupt + maxItems: 1 + + clocks: + description: The clock that determines the SYSCLKOUT rate for the eQEP + peripheral. + maxItems: 1 + + clock-names: + const: sysclkout + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + eqep0: counter@180 { + compatible = "ti,am3352-eqep"; + reg = <0x180 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "sysclkout"; + interrupts = <79>; + }; + +... diff --git a/Documentation/devicetree/bindings/iio/adc/max1027-adc.txt b/Documentation/devicetree/bindings/iio/adc/max1027-adc.txt deleted file mode 100644 index e680c61dfb84..000000000000 --- a/Documentation/devicetree/bindings/iio/adc/max1027-adc.txt +++ /dev/null @@ -1,20 +0,0 @@ -* Maxim 1027/1029/1031 Analog to Digital Converter (ADC) - -Required properties: - - compatible: Should be "maxim,max1027" or "maxim,max1029" or "maxim,max1031" - - reg: SPI chip select number for the device - - interrupts: IRQ line for the ADC - see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt - -Recommended properties: -- spi-max-frequency: Definition as per - Documentation/devicetree/bindings/spi/spi-bus.txt - -Example: -adc@0 { - compatible = "maxim,max1027"; - reg = <0>; - interrupt-parent = <&gpio5>; - interrupts = <15 IRQ_TYPE_EDGE_RISING>; - spi-max-frequency = <1000000>; -}; diff --git a/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml new file mode 100644 index 000000000000..63bcb73ae309 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/imu/nxp,fxos8700.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale FXOS8700 Inertial Measurement Unit + +maintainers: + - Robert Jones <rjones@gateworks.com> + +description: | + Accelerometer and magnetometer combo device with an i2c and SPI interface. + https://www.nxp.com/products/sensors/motion-sensors/6-axis/digital-motion-sensor-3d-accelerometer-2g-4g-8g-plus-3d-magnetometer:FXOS8700CQ + +properties: + compatible: + enum: + - nxp,fxos8700 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: + - INT1 + - INT2 + + drive-open-drain: + type: boolean + +required: + - compatible + - reg + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + fxos8700@1e { + compatible = "nxp,fxos8700"; + reg = <0x1e>; + + interrupt-parent = <&gpio2>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT1"; + }; + }; + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi0 { + #address-cells = <1>; + #size-cells = <0>; + + fxos8700@0 { + compatible = "nxp,fxos8700"; + reg = <0>; + + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT2"; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt index 6d0c050d89fe..cef4bc16fce1 100644 --- a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -14,6 +14,8 @@ Required properties: "st,lsm6ds3tr-c" "st,ism330dhcx" "st,lsm9ds1-imu" + "st,lsm6ds0" + "st,lsm6dsrx" - reg: i2c address of the sensor / spi cs line Optional properties: @@ -31,6 +33,7 @@ Optional properties: - interrupts: interrupt mapping for IRQ. It should be configured with flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or IRQ_TYPE_EDGE_FALLING. +- wakeup-source: Enables wake up of host system on event. Refer to interrupt-controller/interrupts.txt for generic interrupt client node bindings. diff --git a/Documentation/devicetree/bindings/iio/light/adux1020.yaml b/Documentation/devicetree/bindings/iio/light/adux1020.yaml new file mode 100644 index 000000000000..69bd5c06319d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/adux1020.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/adux1020.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADUX1020 Photometric sensor + +maintainers: + - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + +description: | + Photometric sensor over an i2c interface. + https://www.analog.com/media/en/technical-documentation/data-sheets/ADUX1020.pdf + +properties: + compatible: + enum: + - adi,adux1020 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + + #address-cells = <1>; + #size-cells = <0>; + + adux1020@64 { + compatible = "adi,adux1020"; + reg = <0x64>; + interrupt-parent = <&msmgpio>; + interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt b/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt deleted file mode 100644 index dd1058fbe9c3..000000000000 --- a/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.txt +++ /dev/null @@ -1,29 +0,0 @@ -* MaxBotix I2CXL-MaxSonar ultrasonic distance sensor of type mb1202, - mb1212, mb1222, mb1232, mb1242, mb7040 or mb7137 using the i2c interface - for ranging - -Required properties: - - compatible: "maxbotix,mb1202", - "maxbotix,mb1212", - "maxbotix,mb1222", - "maxbotix,mb1232", - "maxbotix,mb1242", - "maxbotix,mb7040" or - "maxbotix,mb7137" - - - reg: i2c address of the device, see also i2c/i2c.txt - -Optional properties: - - interrupts: Interrupt used to announce the preceding reading - request has finished and that data is available. - If no interrupt is specified the device driver - falls back to wait a fixed amount of time until - data can be retrieved. - -Example: -proximity@70 { - compatible = "maxbotix,mb1232"; - reg = <0x70>; - interrupt-parent = <&gpio2>; - interrupts = <2 IRQ_TYPE_EDGE_FALLING>; -}; diff --git a/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml b/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml new file mode 100644 index 000000000000..3eac248f291d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/proximity/maxbotix,mb1232.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MaxBotix I2CXL-MaxSonar ultrasonic distance sensor + +maintainers: + - Andreas Klinger <ak@it-klinger.de> + +description: | + MaxBotix I2CXL-MaxSonar ultrasonic distance sensor of type mb1202, + mb1212, mb1222, mb1232, mb1242, mb7040 or mb7137 using the i2c interface + for ranging + + Specifications about the devices can be found at: + https://www.maxbotix.com/documents/I2CXL-MaxSonar-EZ_Datasheet.pdf + +properties: + compatible: + enum: + - maxbotix,mb1202 + - maxbotix,mb1212 + - maxbotix,mb1222 + - maxbotix,mb1232 + - maxbotix,mb1242 + - maxbotix,mb7040 + - maxbotix,mb7137 + + reg: + maxItems: 1 + + interrupts: + description: + Interrupt used to announce the preceding reading request has finished + and that data is available. If no interrupt is specified the device + driver falls back to wait a fixed amount of time until data can be + retrieved. + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + proximity@70 { + compatible = "maxbotix,mb1232"; + reg = <0x70>; + interrupt-parent = <&gpio2>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml new file mode 100644 index 000000000000..d4922f9f0376 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml @@ -0,0 +1,480 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/temperature/adi,ltc2983.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices LTC2983 Multi-sensor Temperature system + +maintainers: + - Nuno Sá <nuno.sa@analog.com> + +description: | + Analog Devices LTC2983 Multi-Sensor Digital Temperature Measurement System + https://www.analog.com/media/en/technical-documentation/data-sheets/2983fc.pdf + +properties: + compatible: + enum: + - adi,ltc2983 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + adi,mux-delay-config-us: + description: + The LTC2983 performs 2 or 3 internal conversion cycles per temperature + result. Each conversion cycle is performed with different excitation and + input multiplexer configurations. Prior to each conversion, these + excitation circuits and input switch configurations are changed and an + internal 1ms delay ensures settling prior to the conversion cycle in most + cases. An extra delay can be configured using this property. The value is + rounded to nearest 100us. + maximum: 255 + + adi,filter-notch-freq: + description: + Set's the default setting of the digital filter. The default is + simultaneous 50/60Hz rejection. + 0 - 50/60Hz rejection + 1 - 60Hz rejection + 2 - 50Hz rejection + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - minimum: 0 + maximum: 2 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "@([1-9]|1[0-9]|20)$": + type: object + + properties: + reg: + description: + The channel number. It can be connected to one of the 20 channels of + the device. + minimum: 1 + maximum: 20 + + adi,sensor-type: + description: Identifies the type of sensor connected to the device. + $ref: /schemas/types.yaml#/definitions/uint32 + + required: + - reg + - adi,sensor-type + + "^thermocouple@": + type: object + description: + Represents a thermocouple sensor which is connected to one of the device + channels. + + properties: + adi,sensor-type: + description: | + 1 - Type J Thermocouple + 2 - Type K Thermocouple + 3 - Type E Thermocouple + 4 - Type N Thermocouple + 5 - Type R Thermocouple + 6 - Type S Thermocouple + 7 - Type T Thermocouple + 8 - Type B Thermocouple + 9 - Custom Thermocouple + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 9 + + adi,single-ended: + description: + Boolean property which set's the thermocouple as single-ended. + type: boolean + + adi,sensor-oc-current-microamp: + description: + This property set's the pulsed current value applied during + open-circuit detect. + enum: [10, 100, 500, 1000] + + adi,cold-junction-handle: + description: + Phandle which points to a sensor object responsible for measuring + the thermocouple cold junction temperature. + $ref: "/schemas/types.yaml#/definitions/phandle" + + adi,custom-thermocouple: + description: + This is a table, where each entry should be a pair of + voltage(mv)-temperature(K). The entries must be given in nv and uK + so that, the original values must be multiplied by 1000000. For + more details look at table 69 and 70. + Note should be signed, but dtc doesn't currently maintain the + sign. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint64-matrix + items: + minItems: 3 + maxItems: 64 + items: + minItems: 2 + maxItems: 2 + + "^diode@": + type: object + description: + Represents a diode sensor which is connected to one of the device + channels. + + properties: + adi,sensor-type: + description: Identifies the sensor as a diode. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + const: 28 + + adi,single-ended: + description: Boolean property which set's the diode as single-ended. + type: boolean + + adi,three-conversion-cycles: + description: + Boolean property which set's three conversion cycles removing + parasitic resistance effects between the LTC2983 and the diode. + type: boolean + + adi,average-on: + description: + Boolean property which enables a running average of the diode + temperature reading. This reduces the noise when the diode is used + as a cold junction temperature element on an isothermal block + where temperatures change slowly. + type: boolean + + adi,excitation-current-microamp: + description: + This property controls the magnitude of the excitation current + applied to the diode. Depending on the number of conversions + cycles, this property will assume different predefined values on + each cycle. Just set the value of the first cycle (1l). + enum: [10, 20, 40, 80] + + adi,ideal-factor-value: + description: + This property sets the diode ideality factor. The real value must + be multiplied by 1000000 to remove the fractional part. For more + information look at table 20 of the datasheet. + $ref: /schemas/types.yaml#/definitions/uint32 + + "^rtd@": + type: object + description: + Represents a rtd sensor which is connected to one of the device channels. + + properties: + reg: + minimum: 2 + maximum: 20 + + adi,sensor-type: + description: | + 10 - RTD PT-10 + 11 - RTD PT-50 + 12 - RTD PT-100 + 13 - RTD PT-200 + 14 - RTD PT-500 + 15 - RTD PT-1000 + 16 - RTD PT-1000 (0.00375) + 17 - RTD NI-120 + 18 - RTD Custom + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 10 + maximum: 18 + + adi,rsense-handle: + description: + Phandle pointing to a rsense object associated with this RTD. + $ref: "/schemas/types.yaml#/definitions/phandle" + + adi,number-of-wires: + description: + Identifies the number of wires used by the RTD. Setting this + property to 5 means 4 wires with Kelvin Rsense. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [2, 3, 4, 5] + + adi,rsense-share: + description: + Boolean property which enables Rsense sharing, where one sense + resistor is used for multiple 2-, 3-, and/or 4-wire RTDs. + type: boolean + + adi,current-rotate: + description: + Boolean property which enables excitation current rotation to + automatically remove parasitic thermocouple effects. Note that + this property is not allowed for 2- and 3-wire RTDs. + type: boolean + + adi,excitation-current-microamp: + description: + This property controls the magnitude of the excitation current + applied to the RTD. + enum: [5, 10, 25, 50, 100, 250, 500, 1000] + + adi,rtd-curve: + description: + This property set the RTD curve used and the corresponding + Callendar-VanDusen constants. Look at table 30 of the datasheet. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - minimum: 0 + maximum: 3 + + adi,custom-rtd: + description: + This is a table, where each entry should be a pair of + resistance(ohm)-temperature(K). The entries added here are in uohm + and uK. For more details values look at table 74 and 75. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint64-matrix + items: + minItems: 3 + maxItems: 64 + items: + minItems: 2 + maxItems: 2 + + required: + - adi,rsense-handle + + dependencies: + adi,current-rotate: [ adi,rsense-share ] + + "^thermistor@": + type: object + description: + Represents a thermistor sensor which is connected to one of the device + channels. + + properties: + adi,sensor-type: + description: + 19 - Thermistor 44004/44033 2.252kohm at 25°C + 20 - Thermistor 44005/44030 3kohm at 25°C + 21 - Thermistor 44007/44034 5kohm at 25°C + 22 - Thermistor 44006/44031 10kohm at 25°C + 23 - Thermistor 44008/44032 30kohm at 25°C + 24 - Thermistor YSI 400 2.252kohm at 25°C + 25 - Thermistor Spectrum 1003k 1kohm + 26 - Thermistor Custom Steinhart-Hart + 27 - Custom Thermistor + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 19 + maximum: 27 + + adi,rsense-handle: + description: + Phandle pointing to a rsense object associated with this + thermistor. + $ref: "/schemas/types.yaml#/definitions/phandle" + + adi,single-ended: + description: + Boolean property which set's the thermistor as single-ended. + type: boolean + + adi,rsense-share: + description: + Boolean property which enables Rsense sharing, where one sense + resistor is used for multiple thermistors. Note that this property + is ignored if adi,single-ended is set. + type: boolean + + adi,current-rotate: + description: + Boolean property which enables excitation current rotation to + automatically remove parasitic thermocouple effects. + type: boolean + + adi,excitation-current-nanoamp: + description: + This property controls the magnitude of the excitation current + applied to the thermistor. Value 0 set's the sensor in auto-range + mode. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [0, 250, 500, 1000, 5000, 10000, 25000, 50000, 100000, + 250000, 500000, 1000000] + + adi,custom-thermistor: + description: + This is a table, where each entry should be a pair of + resistance(ohm)-temperature(K). The entries added here are in uohm + and uK only for custom thermistors. For more details look at table + 78 and 79. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint64-matrix + items: + minItems: 3 + maxItems: 64 + items: + minItems: 2 + maxItems: 2 + + adi,custom-steinhart: + description: + Steinhart-Hart coefficients are also supported and can + be programmed into the device memory using this property. For + Steinhart sensors the coefficients are given in the raw + format. Look at table 82 for more information. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + items: + minItems: 6 + maxItems: 6 + + required: + - adi,rsense-handle + + dependencies: + adi,current-rotate: [ adi,rsense-share ] + + "^adc@": + type: object + description: Represents a channel which is being used as a direct adc. + + properties: + adi,sensor-type: + description: Identifies the sensor as a direct adc. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + const: 30 + + adi,single-ended: + description: Boolean property which set's the adc as single-ended. + type: boolean + + "^rsense@": + type: object + description: + Represents a rsense which is connected to one of the device channels. + Rsense are used by thermistors and RTD's. + + properties: + reg: + minimum: 2 + maximum: 20 + + adi,sensor-type: + description: Identifies the sensor as a rsense. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + const: 29 + + adi,rsense-val-milli-ohms: + description: + Sets the value of the sense resistor. Look at table 20 of the + datasheet for information. + + required: + - adi,rsense-val-milli-ohms + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + sensor_ltc2983: ltc2983@0 { + compatible = "adi,ltc2983"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + interrupts = <20 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&gpio>; + + thermocouple@18 { + reg = <18>; + adi,sensor-type = <8>; //Type B + adi,sensor-oc-current-microamp = <10>; + adi,cold-junction-handle = <&diode5>; + }; + + diode5: diode@5 { + reg = <5>; + adi,sensor-type = <28>; + }; + + rsense2: rsense@2 { + reg = <2>; + adi,sensor-type = <29>; + adi,rsense-val-milli-ohms = <1200000>; //1.2Kohms + }; + + rtd@14 { + reg = <14>; + adi,sensor-type = <15>; //PT1000 + /*2-wire, internal gnd, no current rotation*/ + adi,number-of-wires = <2>; + adi,rsense-share; + adi,excitation-current-microamp = <500>; + adi,rsense-handle = <&rsense2>; + }; + + adc@10 { + reg = <10>; + adi,sensor-type = <30>; + adi,single-ended; + }; + + thermistor@12 { + reg = <12>; + adi,sensor-type = <26>; //Steinhart + adi,rsense-handle = <&rsense2>; + adi,custom-steinhart = <0x00F371EC 0x12345678 + 0x2C0F8733 0x10018C66 0xA0FEACCD + 0x90021D99>; //6 entries + }; + + thermocouple@20 { + reg = <20>; + adi,sensor-type = <9>; //custom thermocouple + adi,single-ended; + adi,custom-thermocouple = /bits/ 64 + <(-50220000) 0 + (-30200000) 99100000 + (-5300000) 135400000 + 0 273150000 + 40200000 361200000 + 55300000 522100000 + 88300000 720300000 + 132200000 811200000 + 188700000 922500000 + 460400000 1000000000>; //10 pairs + }; + + }; + }; +... diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index cd9e90c5d171..b6bc30d7777e 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -69,6 +69,18 @@ Required child device properties: - compatible : "stericsson,ab8500-[bm|btemp|charger|fg|gpadc|gpio|ponkey| pwm|regulator|rtc|sysctrl|usb]"; + A few child devices require ADC channels from the GPADC node. Those follow the + standard bindings from iio/iio-bindings.txt and iio/adc/adc.txt + + abx500-temp : io-channels "aux1" and "aux2" for measuring external + temperatures. + ab8500-fg : io-channel "main_bat_v" for measuring main battery voltage, + ab8500-btemp : io-channels "btemp_ball" and "bat_ctrl" for measuring the + battery voltage. + ab8500-charger : io-channels "main_charger_v", "main_charger_c", "vbus_v", + "usb_charger_c" for measuring voltage and current of the + different charging supplies. + Optional child device properties: - interrupts : contains the device IRQ(s) using the 2-cell format (see above) - interrupt-names : contains names of IRQ resource in the order in which they were @@ -102,8 +114,115 @@ ab8500 { 39 0x4>; interrupt-names = "HW_CONV_END", "SW_CONV_END"; vddadc-supply = <&ab8500_ldo_tvout_reg>; + #address-cells = <1>; + #size-cells = <0>; + #io-channel-cells = <1>; + + /* GPADC channels */ + bat_ctrl: channel@1 { + reg = <0x01>; + }; + btemp_ball: channel@2 { + reg = <0x02>; + }; + main_charger_v: channel@3 { + reg = <0x03>; + }; + acc_detect1: channel@4 { + reg = <0x04>; + }; + acc_detect2: channel@5 { + reg = <0x05>; + }; + adc_aux1: channel@6 { + reg = <0x06>; + }; + adc_aux2: channel@7 { + reg = <0x07>; + }; + main_batt_v: channel@8 { + reg = <0x08>; + }; + vbus_v: channel@9 { + reg = <0x09>; + }; + main_charger_c: channel@a { + reg = <0x0a>; + }; + usb_charger_c: channel@b { + reg = <0x0b>; + }; + bk_bat_v: channel@c { + reg = <0x0c>; + }; + die_temp: channel@d { + reg = <0x0d>; + }; + usb_id: channel@e { + reg = <0x0e>; + }; + xtal_temp: channel@12 { + reg = <0x12>; + }; + vbat_true_meas: channel@13 { + reg = <0x13>; + }; + bat_ctrl_and_ibat: channel@1c { + reg = <0x1c>; + }; + vbat_meas_and_ibat: channel@1d { + reg = <0x1d>; + }; + vbat_true_meas_and_ibat: channel@1e { + reg = <0x1e>; + }; + bat_temp_and_ibat: channel@1f { + reg = <0x1f>; + }; }; + ab8500_temp { + compatible = "stericsson,abx500-temp"; + io-channels = <&gpadc 0x06>, + <&gpadc 0x07>; + io-channel-name = "aux1", "aux2"; + }; + + ab8500_battery: ab8500_battery { + stericsson,battery-type = "LIPO"; + thermistor-on-batctrl; + }; + + ab8500_fg { + compatible = "stericsson,ab8500-fg"; + battery = <&ab8500_battery>; + io-channels = <&gpadc 0x08>; + io-channel-name = "main_bat_v"; + }; + + ab8500_btemp { + compatible = "stericsson,ab8500-btemp"; + battery = <&ab8500_battery>; + io-channels = <&gpadc 0x02>, + <&gpadc 0x01>; + io-channel-name = "btemp_ball", + "bat_ctrl"; + }; + + ab8500_charger { + compatible = "stericsson,ab8500-charger"; + battery = <&ab8500_battery>; + vddadc-supply = <&ab8500_ldo_tvout_reg>; + io-channels = <&gpadc 0x03>, + <&gpadc 0x0a>, + <&gpadc 0x09>, + <&gpadc 0x0b>; + io-channel-name = "main_charger_v", + "main_charger_c", + "vbus_v", + "usb_charger_c"; + }; + ab8500-usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 870ac52d2225..765fd1c170df 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -114,6 +114,18 @@ properties: - isil,isl68137 # 5 Bit Programmable, Pulse-Width Modulator - maxim,ds1050 + # 10-bit 8 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1027 + # 10-bit 12 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1029 + # 10-bit 16 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1031 + # 12-bit 8 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1227 + # 12-bit 12 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1229 + # 12-bit 16 channels 300ks/s SPI ADC with temperature sensor + - maxim,max1231 # Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs - maxim,max1237 # PECI-to-I2C translator for PECI-to-SMBus/I2C protocol conversion diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index 8382f01a53e3..e622f8f6e56a 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -7,7 +7,7 @@ Generic Counter Interface Introduction ============ -Counter devices are prevalent within a diverse spectrum of industries. +Counter devices are prevalent among a diverse spectrum of industries. The ubiquitous presence of these devices necessitates a common interface and standard of interaction and exposure. This driver API attempts to resolve the issue of duplicate code found among existing counter device @@ -26,23 +26,72 @@ the Generic Counter interface. There are three core components to a counter: -* Count: - Count data for a set of Signals. - * Signal: - Input data that is evaluated by the counter to determine the count - data. + Stream of data to be evaluated by the counter. * Synapse: - The association of a Signal with a respective Count. + Association of a Signal, and evaluation trigger, with a Count. + +* Count: + Accumulation of the effects of connected Synapses. + +SIGNAL +------ +A Signal represents a stream of data. This is the input data that is +evaluated by the counter to determine the count data; e.g. a quadrature +signal output line of a rotary encoder. Not all counter devices provide +user access to the Signal data, so exposure is optional for drivers. + +When the Signal data is available for user access, the Generic Counter +interface provides the following available signal values: + +* SIGNAL_LOW: + Signal line is in a low state. + +* SIGNAL_HIGH: + Signal line is in a high state. + +A Signal may be associated with one or more Counts. + +SYNAPSE +------- +A Synapse represents the association of a Signal with a Count. Signal +data affects respective Count data, and the Synapse represents this +relationship. + +The Synapse action mode specifies the Signal data condition that +triggers the respective Count's count function evaluation to update the +count data. The Generic Counter interface provides the following +available action modes: + +* None: + Signal does not trigger the count function. In Pulse-Direction count + function mode, this Signal is evaluated as Direction. + +* Rising Edge: + Low state transitions to high state. + +* Falling Edge: + High state transitions to low state. + +* Both Edges: + Any state transition. + +A counter is defined as a set of input signals associated with count +data that are generated by the evaluation of the state of the associated +input signals as defined by the respective count functions. Within the +context of the Generic Counter interface, a counter consists of Counts +each associated with a set of Signals, whose respective Synapse +instances represent the count function update conditions for the +associated Counts. + +A Synapse associates one Signal with one Count. COUNT ----- -A Count represents the count data for a set of Signals. The Generic -Counter interface provides the following available count data types: - -* COUNT_POSITION: - Unsigned integer value representing position. +A Count represents the accumulation of the effects of connected +Synapses; i.e. the count data for a set of Signals. The Generic +Counter interface represents the count data as a natural number. A Count has a count function mode which represents the update behavior for the count data. The Generic Counter interface provides the following @@ -86,60 +135,7 @@ available count function modes: Any state transition on either quadrature pair signals updates the respective count. Quadrature encoding determines the direction. -A Count has a set of one or more associated Signals. - -SIGNAL ------- -A Signal represents a counter input data; this is the input data that is -evaluated by the counter to determine the count data; e.g. a quadrature -signal output line of a rotary encoder. Not all counter devices provide -user access to the Signal data. - -The Generic Counter interface provides the following available signal -data types for when the Signal data is available for user access: - -* SIGNAL_LEVEL: - Signal line state level. The following states are possible: - - - SIGNAL_LEVEL_LOW: - Signal line is in a low state. - - - SIGNAL_LEVEL_HIGH: - Signal line is in a high state. - -A Signal may be associated with one or more Counts. - -SYNAPSE -------- -A Synapse represents the association of a Signal with a respective -Count. Signal data affects respective Count data, and the Synapse -represents this relationship. - -The Synapse action mode specifies the Signal data condition which -triggers the respective Count's count function evaluation to update the -count data. The Generic Counter interface provides the following -available action modes: - -* None: - Signal does not trigger the count function. In Pulse-Direction count - function mode, this Signal is evaluated as Direction. - -* Rising Edge: - Low state transitions to high state. - -* Falling Edge: - High state transitions to low state. - -* Both Edges: - Any state transition. - -A counter is defined as a set of input signals associated with count -data that are generated by the evaluation of the state of the associated -input signals as defined by the respective count functions. Within the -context of the Generic Counter interface, a counter consists of Counts -each associated with a set of Signals, whose respective Synapse -instances represent the count function update conditions for the -associated Counts. +A Count has a set of one or more associated Synapses. Paradigm ======== @@ -286,10 +282,36 @@ if device memory-managed registration is desired. Extension sysfs attributes can be created for auxiliary functionality and data by passing in defined counter_device_ext, counter_count_ext, and counter_signal_ext structures. In these cases, the -counter_device_ext structure is used for global configuration of the -respective Counter device, while the counter_count_ext and -counter_signal_ext structures allow for auxiliary exposure and -configuration of a specific Count or Signal respectively. +counter_device_ext structure is used for global/miscellaneous exposure +and configuration of the respective Counter device, while the +counter_count_ext and counter_signal_ext structures allow for auxiliary +exposure and configuration of a specific Count or Signal respectively. + +Determining the type of extension to create is a matter of scope. + +* Signal extensions are attributes that expose information/control + specific to a Signal. These types of attributes will exist under a + Signal's directory in sysfs. + + For example, if you have an invert feature for a Signal, you can have + a Signal extension called "invert" that toggles that feature: + /sys/bus/counter/devices/counterX/signalY/invert + +* Count extensions are attributes that expose information/control + specific to a Count. These type of attributes will exist under a + Count's directory in sysfs. + + For example, if you want to pause/unpause a Count from updating, you + can have a Count extension called "enable" that toggles such: + /sys/bus/counter/devices/counterX/countY/enable + +* Device extensions are attributes that expose information/control + non-specific to a particular Count or Signal. This is where you would + put your global features or other miscellanous functionality. + + For example, if your device has an overtemp sensor, you can report the + chip overheated via a device extension called "error_overtemp": + /sys/bus/counter/devices/counterX/error_overtemp Architecture ============ diff --git a/MAINTAINERS b/MAINTAINERS index 995ce9f7cf4e..0fca3b055985 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2005,6 +2005,7 @@ F: drivers/dma/ste_dma40* F: drivers/hwspinlock/u8500_hsem.c F: drivers/i2c/busses/i2c-nomadik.c F: drivers/i2c/busses/i2c-stu300.c +F: drivers/iio/adc/ab8500-gpadc.c F: drivers/mfd/ab3100* F: drivers/mfd/ab8500* F: drivers/mfd/abx500* @@ -9624,6 +9625,14 @@ S: Maintained F: Documentation/devicetree/bindings/iio/dac/ltc1660.txt F: drivers/iio/dac/ltc1660.c +LTC2983 IIO TEMPERATURE DRIVER +M: Nuno Sá <nuno.sa@analog.com> +W: http://ez.analog.com/community/linux-device-drivers +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/temperature/ltc2983.c +F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml + LTC4261 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> L: linux-hwmon@vger.kernel.org @@ -16228,6 +16237,12 @@ S: Maintained F: drivers/media/platform/davinci/ F: include/media/davinci/ +TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER +R: David Lechner <david@lechnology.com> +L: linux-iio@vger.kernel.org +F: Documentation/devicetree/bindings/counter/ti-eqep.yaml +F: drivers/counter/ti-eqep.c + TI ETHERNET SWITCH DRIVER (CPSW) R: Grygorii Strashko <grygorii.strashko@ti.com> L: linux-omap@vger.kernel.org diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 6b331061d34b..97ab5ad171d4 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -150,6 +150,15 @@ config TEGRA_GMI Driver for the Tegra Generic Memory Interface bus which can be used to attach devices such as NOR, UART, FPGA and more. +config TI_PWMSS + bool + default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM || TI_EQEP) + help + PWM Subsystem driver support for AM33xx SOC. + + PWM submodules require PWM config space access from submodule + drivers and require common parent driver support. + config TI_SYSC bool "TI sysc interconnect target module driver" depends on ARCH_OMAP2PLUS diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 16b43d3468c6..1320bcf9fa9d 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o obj-$(CONFIG_TEGRA_GMI) += tegra-gmi.o +obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o obj-$(CONFIG_TI_SYSC) += ti-sysc.o obj-$(CONFIG_TS_NBUS) += ts-nbus.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/bus/ti-pwmss.c index e9c26c94251b..e9c26c94251b 100644 --- a/drivers/pwm/pwm-tipwmss.c +++ b/drivers/bus/ti-pwmss.c diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 00b113f4b958..17e67a84777d 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -562,11 +562,10 @@ static const struct iio_chan_spec quad8_channels[] = { }; static int quad8_signal_read(struct counter_device *counter, - struct counter_signal *signal, struct counter_signal_read_value *val) + struct counter_signal *signal, enum counter_signal_value *val) { const struct quad8_iio *const priv = counter->priv; unsigned int state; - enum counter_signal_level level; /* Only Index signal levels can be read */ if (signal->id < 16) @@ -575,22 +574,19 @@ static int quad8_signal_read(struct counter_device *counter, state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS) & BIT(signal->id - 16); - level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW; - - counter_signal_read_value_set(val, COUNTER_SIGNAL_LEVEL, &level); + *val = (state) ? COUNTER_SIGNAL_HIGH : COUNTER_SIGNAL_LOW; return 0; } static int quad8_count_read(struct counter_device *counter, - struct counter_count *count, struct counter_count_read_value *val) + struct counter_count *count, unsigned long *val) { const struct quad8_iio *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; unsigned int flags; unsigned int borrow; unsigned int carry; - unsigned long position; int i; flags = inb(base_offset + 1); @@ -598,36 +594,27 @@ static int quad8_count_read(struct counter_device *counter, carry = !!(flags & QUAD8_FLAG_CT); /* Borrow XOR Carry effectively doubles count range */ - position = (unsigned long)(borrow ^ carry) << 24; + *val = (unsigned long)(borrow ^ carry) << 24; /* Reset Byte Pointer; transfer Counter to Output Latch */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, base_offset + 1); for (i = 0; i < 3; i++) - position |= (unsigned long)inb(base_offset) << (8 * i); - - counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &position); + *val |= (unsigned long)inb(base_offset) << (8 * i); return 0; } static int quad8_count_write(struct counter_device *counter, - struct counter_count *count, struct counter_count_write_value *val) + struct counter_count *count, unsigned long val) { const struct quad8_iio *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; - int err; - unsigned long position; int i; - err = counter_count_write_value_get(&position, COUNTER_COUNT_POSITION, - val); - if (err) - return err; - /* Only 24-bit values are supported */ - if (position > 0xFFFFFF) + if (val > 0xFFFFFF) return -EINVAL; /* Reset Byte Pointer */ @@ -635,7 +622,7 @@ static int quad8_count_write(struct counter_device *counter, /* Counter can only be set via Preset Register */ for (i = 0; i < 3; i++) - outb(position >> (8 * i), base_offset); + outb(val >> (8 * i), base_offset); /* Transfer Preset Register to Counter */ outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1); @@ -644,9 +631,9 @@ static int quad8_count_write(struct counter_device *counter, outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); /* Set Preset Register back to original value */ - position = priv->preset[count->id]; + val = priv->preset[count->id]; for (i = 0; i < 3; i++) - outb(position >> (8 * i), base_offset); + outb(val >> (8 * i), base_offset); /* Reset Borrow, Carry, Compare, and Sign flags */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1); diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 2967d0a9ff91..c80fa76bb531 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -49,6 +49,17 @@ config STM32_LPTIMER_CNT To compile this driver as a module, choose M here: the module will be called stm32-lptimer-cnt. +config TI_EQEP + tristate "TI eQEP counter driver" + depends on (SOC_AM33XX || COMPILE_TEST) + select REGMAP_MMIO + help + Select this option to enable the Texas Instruments Enhanced Quadrature + Encoder Pulse (eQEP) counter driver. + + To compile this driver as a module, choose M here: the module will be + called ti-eqep. + config FTM_QUADDEC tristate "Flex Timer Module Quadrature decoder driver" depends on HAS_IOMEM && OF diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index 40d35522937d..55142d1f4c43 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_COUNTER) += counter.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o +obj-$(CONFIG_TI_EQEP) += ti-eqep.o obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o diff --git a/drivers/counter/counter.c b/drivers/counter/counter.c index 106bc7180cd8..6a683d086008 100644 --- a/drivers/counter/counter.c +++ b/drivers/counter/counter.c @@ -220,86 +220,6 @@ ssize_t counter_device_enum_available_read(struct counter_device *counter, } EXPORT_SYMBOL_GPL(counter_device_enum_available_read); -static const char *const counter_signal_level_str[] = { - [COUNTER_SIGNAL_LEVEL_LOW] = "low", - [COUNTER_SIGNAL_LEVEL_HIGH] = "high" -}; - -/** - * counter_signal_read_value_set - set counter_signal_read_value data - * @val: counter_signal_read_value structure to set - * @type: property Signal data represents - * @data: Signal data - * - * This function sets an opaque counter_signal_read_value structure with the - * provided Signal data. - */ -void counter_signal_read_value_set(struct counter_signal_read_value *const val, - const enum counter_signal_value_type type, - void *const data) -{ - if (type == COUNTER_SIGNAL_LEVEL) - val->len = sprintf(val->buf, "%s\n", - counter_signal_level_str[*(enum counter_signal_level *)data]); - else - val->len = 0; -} -EXPORT_SYMBOL_GPL(counter_signal_read_value_set); - -/** - * counter_count_read_value_set - set counter_count_read_value data - * @val: counter_count_read_value structure to set - * @type: property Count data represents - * @data: Count data - * - * This function sets an opaque counter_count_read_value structure with the - * provided Count data. - */ -void counter_count_read_value_set(struct counter_count_read_value *const val, - const enum counter_count_value_type type, - void *const data) -{ - switch (type) { - case COUNTER_COUNT_POSITION: - val->len = sprintf(val->buf, "%lu\n", *(unsigned long *)data); - break; - default: - val->len = 0; - } -} -EXPORT_SYMBOL_GPL(counter_count_read_value_set); - -/** - * counter_count_write_value_get - get counter_count_write_value data - * @data: Count data - * @type: property Count data represents - * @val: counter_count_write_value structure containing data - * - * This function extracts Count data from the provided opaque - * counter_count_write_value structure and stores it at the address provided by - * @data. - * - * RETURNS: - * 0 on success, negative error number on failure. - */ -int counter_count_write_value_get(void *const data, - const enum counter_count_value_type type, - const struct counter_count_write_value *const val) -{ - int err; - - switch (type) { - case COUNTER_COUNT_POSITION: - err = kstrtoul(val->buf, 0, data); - if (err) - return err; - break; - } - - return 0; -} -EXPORT_SYMBOL_GPL(counter_count_write_value_get); - struct counter_attr_parm { struct counter_device_attr_group *group; const char *prefix; @@ -369,6 +289,11 @@ struct counter_signal_unit { struct counter_signal *signal; }; +static const char *const counter_signal_value_str[] = { + [COUNTER_SIGNAL_LOW] = "low", + [COUNTER_SIGNAL_HIGH] = "high" +}; + static ssize_t counter_signal_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -377,13 +302,13 @@ static ssize_t counter_signal_show(struct device *dev, const struct counter_signal_unit *const component = devattr->component; struct counter_signal *const signal = component->signal; int err; - struct counter_signal_read_value val = { .buf = buf }; + enum counter_signal_value val; err = counter->ops->signal_read(counter, signal, &val); if (err) return err; - return val.len; + return sprintf(buf, "%s\n", counter_signal_value_str[val]); } struct counter_name_unit { @@ -788,13 +713,13 @@ static ssize_t counter_count_show(struct device *dev, const struct counter_count_unit *const component = devattr->component; struct counter_count *const count = component->count; int err; - struct counter_count_read_value val = { .buf = buf }; + unsigned long val; err = counter->ops->count_read(counter, count, &val); if (err) return err; - return val.len; + return sprintf(buf, "%lu\n", val); } static ssize_t counter_count_store(struct device *dev, @@ -806,9 +731,13 @@ static ssize_t counter_count_store(struct device *dev, const struct counter_count_unit *const component = devattr->component; struct counter_count *const count = component->count; int err; - struct counter_count_write_value val = { .buf = buf }; + unsigned long val; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; - err = counter->ops->count_write(counter, count, &val); + err = counter->ops->count_write(counter, count, val); if (err) return err; diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index 4046aa9f9234..c2b3fdfd8b77 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -178,31 +178,25 @@ static const enum counter_count_function ftm_quaddec_count_functions[] = { static int ftm_quaddec_count_read(struct counter_device *counter, struct counter_count *count, - struct counter_count_read_value *val) + unsigned long *val) { struct ftm_quaddec *const ftm = counter->priv; uint32_t cntval; ftm_read(ftm, FTM_CNT, &cntval); - counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cntval); + *val = cntval; return 0; } static int ftm_quaddec_count_write(struct counter_device *counter, struct counter_count *count, - struct counter_count_write_value *val) + const unsigned long val) { struct ftm_quaddec *const ftm = counter->priv; - u32 cnt; - int err; - err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val); - if (err) - return err; - - if (cnt != 0) { + if (val != 0) { dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n"); return -EINVAL; } diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index 28b63645c411..8e276eb655f5 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -377,8 +377,7 @@ static enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = { }; static int stm32_lptim_cnt_read(struct counter_device *counter, - struct counter_count *count, - struct counter_count_read_value *val) + struct counter_count *count, unsigned long *val) { struct stm32_lptim_cnt *const priv = counter->priv; u32 cnt; @@ -388,7 +387,7 @@ static int stm32_lptim_cnt_read(struct counter_device *counter, if (ret) return ret; - counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cnt); + *val = cnt; return 0; } diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index b61135b63ee8..3eafccec3beb 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -48,34 +48,27 @@ static enum counter_count_function stm32_count_functions[] = { }; static int stm32_count_read(struct counter_device *counter, - struct counter_count *count, - struct counter_count_read_value *val) + struct counter_count *count, unsigned long *val) { struct stm32_timer_cnt *const priv = counter->priv; u32 cnt; regmap_read(priv->regmap, TIM_CNT, &cnt); - counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cnt); + *val = cnt; return 0; } static int stm32_count_write(struct counter_device *counter, struct counter_count *count, - struct counter_count_write_value *val) + const unsigned long val) { struct stm32_timer_cnt *const priv = counter->priv; - u32 cnt; - int err; - - err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val); - if (err) - return err; - if (cnt > priv->ceiling) + if (val > priv->ceiling) return -EINVAL; - return regmap_write(priv->regmap, TIM_CNT, cnt); + return regmap_write(priv->regmap, TIM_CNT, val); } static int stm32_count_function_get(struct counter_device *counter, diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c new file mode 100644 index 000000000000..1ff07faef27f --- /dev/null +++ b/drivers/counter/ti-eqep.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 David Lechner <david@lechnology.com> + * + * Counter driver for Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) + */ + +#include <linux/bitops.h> +#include <linux/counter.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +/* 32-bit registers */ +#define QPOSCNT 0x0 +#define QPOSINIT 0x4 +#define QPOSMAX 0x8 +#define QPOSCMP 0xc +#define QPOSILAT 0x10 +#define QPOSSLAT 0x14 +#define QPOSLAT 0x18 +#define QUTMR 0x1c +#define QUPRD 0x20 + +/* 16-bit registers */ +#define QWDTMR 0x0 /* 0x24 */ +#define QWDPRD 0x2 /* 0x26 */ +#define QDECCTL 0x4 /* 0x28 */ +#define QEPCTL 0x6 /* 0x2a */ +#define QCAPCTL 0x8 /* 0x2c */ +#define QPOSCTL 0xa /* 0x2e */ +#define QEINT 0xc /* 0x30 */ +#define QFLG 0xe /* 0x32 */ +#define QCLR 0x10 /* 0x34 */ +#define QFRC 0x12 /* 0x36 */ +#define QEPSTS 0x14 /* 0x38 */ +#define QCTMR 0x16 /* 0x3a */ +#define QCPRD 0x18 /* 0x3c */ +#define QCTMRLAT 0x1a /* 0x3e */ +#define QCPRDLAT 0x1c /* 0x40 */ + +#define QDECCTL_QSRC_SHIFT 14 +#define QDECCTL_QSRC GENMASK(15, 14) +#define QDECCTL_SOEN BIT(13) +#define QDECCTL_SPSEL BIT(12) +#define QDECCTL_XCR BIT(11) +#define QDECCTL_SWAP BIT(10) +#define QDECCTL_IGATE BIT(9) +#define QDECCTL_QAP BIT(8) +#define QDECCTL_QBP BIT(7) +#define QDECCTL_QIP BIT(6) +#define QDECCTL_QSP BIT(5) + +#define QEPCTL_FREE_SOFT GENMASK(15, 14) +#define QEPCTL_PCRM GENMASK(13, 12) +#define QEPCTL_SEI GENMASK(11, 10) +#define QEPCTL_IEI GENMASK(9, 8) +#define QEPCTL_SWI BIT(7) +#define QEPCTL_SEL BIT(6) +#define QEPCTL_IEL GENMASK(5, 4) +#define QEPCTL_PHEN BIT(3) +#define QEPCTL_QCLM BIT(2) +#define QEPCTL_UTE BIT(1) +#define QEPCTL_WDE BIT(0) + +/* EQEP Inputs */ +enum { + TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */ + TI_EQEP_SIGNAL_QEPB, /* QEPB/XDIR */ +}; + +/* Position Counter Input Modes */ +enum { + TI_EQEP_COUNT_FUNC_QUAD_COUNT, + TI_EQEP_COUNT_FUNC_DIR_COUNT, + TI_EQEP_COUNT_FUNC_UP_COUNT, + TI_EQEP_COUNT_FUNC_DOWN_COUNT, +}; + +enum { + TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES, + TI_EQEP_SYNAPSE_ACTION_RISING_EDGE, + TI_EQEP_SYNAPSE_ACTION_NONE, +}; + +struct ti_eqep_cnt { + struct counter_device counter; + struct regmap *regmap32; + struct regmap *regmap16; +}; + +static int ti_eqep_count_read(struct counter_device *counter, + struct counter_count *count, unsigned long *val) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 cnt; + + regmap_read(priv->regmap32, QPOSCNT, &cnt); + *val = cnt; + + return 0; +} + +static int ti_eqep_count_write(struct counter_device *counter, + struct counter_count *count, unsigned long val) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 max; + + regmap_read(priv->regmap32, QPOSMAX, &max); + if (val > max) + return -EINVAL; + + return regmap_write(priv->regmap32, QPOSCNT, val); +} + +static int ti_eqep_function_get(struct counter_device *counter, + struct counter_count *count, size_t *function) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qdecctl; + + regmap_read(priv->regmap16, QDECCTL, &qdecctl); + *function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT; + + return 0; +} + +static int ti_eqep_function_set(struct counter_device *counter, + struct counter_count *count, size_t function) +{ + struct ti_eqep_cnt *priv = counter->priv; + + return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC, + function << QDECCTL_QSRC_SHIFT); +} + +static int ti_eqep_action_get(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, size_t *action) +{ + struct ti_eqep_cnt *priv = counter->priv; + size_t function; + u32 qdecctl; + int err; + + err = ti_eqep_function_get(counter, count, &function); + if (err) + return err; + + switch (function) { + case TI_EQEP_COUNT_FUNC_QUAD_COUNT: + /* In quadrature mode, the rising and falling edge of both + * QEPA and QEPB trigger QCLK. + */ + *action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES; + break; + case TI_EQEP_COUNT_FUNC_DIR_COUNT: + /* In direction-count mode only rising edge of QEPA is counted + * and QEPB gives direction. + */ + switch (synapse->signal->id) { + case TI_EQEP_SIGNAL_QEPA: + *action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE; + break; + default: + *action = TI_EQEP_SYNAPSE_ACTION_NONE; + break; + } + break; + case TI_EQEP_COUNT_FUNC_UP_COUNT: + case TI_EQEP_COUNT_FUNC_DOWN_COUNT: + /* In up/down-count modes only QEPA is counted and QEPB is not + * used. + */ + switch (synapse->signal->id) { + case TI_EQEP_SIGNAL_QEPA: + err = regmap_read(priv->regmap16, QDECCTL, &qdecctl); + if (err) + return err; + + if (qdecctl & QDECCTL_XCR) + *action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES; + else + *action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE; + break; + default: + *action = TI_EQEP_SYNAPSE_ACTION_NONE; + break; + } + break; + } + + return 0; +} + +static const struct counter_ops ti_eqep_counter_ops = { + .count_read = ti_eqep_count_read, + .count_write = ti_eqep_count_write, + .function_get = ti_eqep_function_get, + .function_set = ti_eqep_function_set, + .action_get = ti_eqep_action_get, +}; + +static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, char *buf) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qposmax; + + regmap_read(priv->regmap32, QPOSMAX, &qposmax); + + return sprintf(buf, "%u\n", qposmax); +} + +static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, const char *buf, + size_t len) +{ + struct ti_eqep_cnt *priv = counter->priv; + int err; + u32 res; + + err = kstrtouint(buf, 0, &res); + if (err < 0) + return err; + + regmap_write(priv->regmap32, QPOSMAX, res); + + return len; +} + +static ssize_t ti_eqep_position_floor_read(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, char *buf) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qposinit; + + regmap_read(priv->regmap32, QPOSINIT, &qposinit); + + return sprintf(buf, "%u\n", qposinit); +} + +static ssize_t ti_eqep_position_floor_write(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, const char *buf, + size_t len) +{ + struct ti_eqep_cnt *priv = counter->priv; + int err; + u32 res; + + err = kstrtouint(buf, 0, &res); + if (err < 0) + return err; + + regmap_write(priv->regmap32, QPOSINIT, res); + + return len; +} + +static ssize_t ti_eqep_position_enable_read(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, char *buf) +{ + struct ti_eqep_cnt *priv = counter->priv; + u32 qepctl; + + regmap_read(priv->regmap16, QEPCTL, &qepctl); + + return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN)); +} + +static ssize_t ti_eqep_position_enable_write(struct counter_device *counter, + struct counter_count *count, + void *ext_priv, const char *buf, + size_t len) +{ + struct ti_eqep_cnt *priv = counter->priv; + int err; + bool res; + + err = kstrtobool(buf, &res); + if (err < 0) + return err; + + regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0); + + return len; +} + +static struct counter_count_ext ti_eqep_position_ext[] = { + { + .name = "ceiling", + .read = ti_eqep_position_ceiling_read, + .write = ti_eqep_position_ceiling_write, + }, + { + .name = "floor", + .read = ti_eqep_position_floor_read, + .write = ti_eqep_position_floor_write, + }, + { + .name = "enable", + .read = ti_eqep_position_enable_read, + .write = ti_eqep_position_enable_write, + }, +}; + +static struct counter_signal ti_eqep_signals[] = { + [TI_EQEP_SIGNAL_QEPA] = { + .id = TI_EQEP_SIGNAL_QEPA, + .name = "QEPA" + }, + [TI_EQEP_SIGNAL_QEPB] = { + .id = TI_EQEP_SIGNAL_QEPB, + .name = "QEPB" + }, +}; + +static const enum counter_count_function ti_eqep_position_functions[] = { + [TI_EQEP_COUNT_FUNC_QUAD_COUNT] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4, + [TI_EQEP_COUNT_FUNC_DIR_COUNT] = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION, + [TI_EQEP_COUNT_FUNC_UP_COUNT] = COUNTER_COUNT_FUNCTION_INCREASE, + [TI_EQEP_COUNT_FUNC_DOWN_COUNT] = COUNTER_COUNT_FUNCTION_DECREASE, +}; + +static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = { + [TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES, + [TI_EQEP_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, + [TI_EQEP_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, +}; + +static struct counter_synapse ti_eqep_position_synapses[] = { + { + .actions_list = ti_eqep_position_synapse_actions, + .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPA], + }, + { + .actions_list = ti_eqep_position_synapse_actions, + .num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions), + .signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPB], + }, +}; + +static struct counter_count ti_eqep_counts[] = { + { + .id = 0, + .name = "QPOSCNT", + .functions_list = ti_eqep_position_functions, + .num_functions = ARRAY_SIZE(ti_eqep_position_functions), + .synapses = ti_eqep_position_synapses, + .num_synapses = ARRAY_SIZE(ti_eqep_position_synapses), + .ext = ti_eqep_position_ext, + .num_ext = ARRAY_SIZE(ti_eqep_position_ext), + }, +}; + +static const struct regmap_config ti_eqep_regmap32_config = { + .name = "32-bit", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x24, +}; + +static const struct regmap_config ti_eqep_regmap16_config = { + .name = "16-bit", + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .max_register = 0x1e, +}; + +static int ti_eqep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_eqep_cnt *priv; + void __iomem *base; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap32 = devm_regmap_init_mmio(dev, base, + &ti_eqep_regmap32_config); + if (IS_ERR(priv->regmap32)) + return PTR_ERR(priv->regmap32); + + priv->regmap16 = devm_regmap_init_mmio(dev, base + 0x24, + &ti_eqep_regmap16_config); + if (IS_ERR(priv->regmap16)) + return PTR_ERR(priv->regmap16); + + priv->counter.name = dev_name(dev); + priv->counter.parent = dev; + priv->counter.ops = &ti_eqep_counter_ops; + priv->counter.counts = ti_eqep_counts; + priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts); + priv->counter.signals = ti_eqep_signals; + priv->counter.num_signals = ARRAY_SIZE(ti_eqep_signals); + priv->counter.priv = priv; + + platform_set_drvdata(pdev, priv); + + /* + * Need to make sure power is turned on. On AM33xx, this comes from the + * parent PWMSS bus driver. On AM17xx, this comes from the PSC power + * domain. + */ + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + err = counter_register(&priv->counter); + if (err < 0) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + return err; + } + + return 0; +} + +static int ti_eqep_remove(struct platform_device *pdev) +{ + struct ti_eqep_cnt *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + counter_unregister(&priv->counter); + pm_runtime_put_sync(dev), + pm_runtime_disable(dev); + + return 0; +} + +static const struct of_device_id ti_eqep_of_match[] = { + { .compatible = "ti,am3352-eqep", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ti_eqep_of_match); + +static struct platform_driver ti_eqep_driver = { + .probe = ti_eqep_probe, + .remove = ti_eqep_remove, + .driver = { + .name = "ti-eqep-cnt", + .of_match_table = ti_eqep_of_match, + }, +}; +module_platform_driver(ti_eqep_driver); + +MODULE_AUTHOR("David Lechner <david@lechnology.com>"); +MODULE_DESCRIPTION("TI eQEP counter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 13a6b4afb4b3..5308c59d7001 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -40,7 +40,8 @@ comment "Native drivers" config SENSORS_AB8500 tristate "AB8500 thermal monitoring" - depends on AB8500_GPADC && AB8500_BM + depends on AB8500_GPADC && AB8500_BM && (IIO = y) + default n help If you say yes here you get support for the thermal sensor part of the AB8500 chip. The driver includes thermal management for diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c index 207f77f85a40..53f3379d799d 100644 --- a/drivers/hwmon/ab8500.c +++ b/drivers/hwmon/ab8500.c @@ -17,20 +17,24 @@ #include <linux/hwmon-sysfs.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/power/ab8500.h> #include <linux/reboot.h> #include <linux/slab.h> #include <linux/sysfs.h> +#include <linux/iio/consumer.h> #include "abx500.h" #define DEFAULT_POWER_OFF_DELAY (HZ * 10) #define THERMAL_VCC 1800 #define PULL_UP_RESISTOR 47000 -/* Number of monitored sensors should not greater than NUM_SENSORS */ -#define NUM_MONITORED_SENSORS 4 + +#define AB8500_SENSOR_AUX1 0 +#define AB8500_SENSOR_AUX2 1 +#define AB8500_SENSOR_BTEMP_BALL 2 +#define AB8500_SENSOR_BAT_CTRL 3 +#define NUM_MONITORED_SENSORS 4 struct ab8500_gpadc_cfg { const struct abx500_res_to_temp *temp_tbl; @@ -40,7 +44,8 @@ struct ab8500_gpadc_cfg { }; struct ab8500_temp { - struct ab8500_gpadc *gpadc; + struct iio_channel *aux1; + struct iio_channel *aux2; struct ab8500_btemp *btemp; struct delayed_work power_off_work; struct ab8500_gpadc_cfg cfg; @@ -82,15 +87,21 @@ static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp) int voltage, ret; struct ab8500_temp *ab8500_data = data->plat_data; - if (sensor == BAT_CTRL) { - *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); - } else if (sensor == BTEMP_BALL) { + if (sensor == AB8500_SENSOR_BTEMP_BALL) { *temp = ab8500_btemp_get_temp(ab8500_data->btemp); - } else { - voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor); - if (voltage < 0) - return voltage; - + } else if (sensor == AB8500_SENSOR_BAT_CTRL) { + *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); + } else if (sensor == AB8500_SENSOR_AUX1) { + ret = iio_read_channel_processed(ab8500_data->aux1, &voltage); + if (ret < 0) + return ret; + ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); + if (ret < 0) + return ret; + } else if (sensor == AB8500_SENSOR_AUX2) { + ret = iio_read_channel_processed(ab8500_data->aux2, &voltage); + if (ret < 0) + return ret; ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); if (ret < 0) return ret; @@ -164,10 +175,6 @@ int abx500_hwmon_init(struct abx500_temp *data) if (!ab8500_data) return -ENOMEM; - ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - if (IS_ERR(ab8500_data->gpadc)) - return PTR_ERR(ab8500_data->gpadc); - ab8500_data->btemp = ab8500_btemp_get(); if (IS_ERR(ab8500_data->btemp)) return PTR_ERR(ab8500_data->btemp); @@ -181,15 +188,25 @@ int abx500_hwmon_init(struct abx500_temp *data) ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size; data->plat_data = ab8500_data; + ab8500_data->aux1 = devm_iio_channel_get(&data->pdev->dev, "aux1"); + if (IS_ERR(ab8500_data->aux1)) { + if (PTR_ERR(ab8500_data->aux1) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&data->pdev->dev, "failed to get AUX1 ADC channel\n"); + return PTR_ERR(ab8500_data->aux1); + } + ab8500_data->aux2 = devm_iio_channel_get(&data->pdev->dev, "aux2"); + if (IS_ERR(ab8500_data->aux2)) { + if (PTR_ERR(ab8500_data->aux2) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&data->pdev->dev, "failed to get AUX2 ADC channel\n"); + return PTR_ERR(ab8500_data->aux2); + } - /* - * ADC_AUX1 and ADC_AUX2, connected to external NTC - * BTEMP_BALL and BAT_CTRL, fixed usage - */ - data->gpadc_addr[0] = ADC_AUX1; - data->gpadc_addr[1] = ADC_AUX2; - data->gpadc_addr[2] = BTEMP_BALL; - data->gpadc_addr[3] = BAT_CTRL; + data->gpadc_addr[0] = AB8500_SENSOR_AUX1; + data->gpadc_addr[1] = AB8500_SENSOR_AUX2; + data->gpadc_addr[2] = AB8500_SENSOR_BTEMP_BALL; + data->gpadc_addr[3] = AB8500_SENSOR_BAT_CTRL; data->monitored_sensors = NUM_MONITORED_SENSORS; data->ops.read_sensor = ab8500_read_sensor; diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 2e37f8a6d8cf..7b837641f166 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -15,7 +15,6 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/irq.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index f0af3a42f53c..9554890a3200 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -6,6 +6,16 @@ menu "Analog to digital converters" +config AB8500_GPADC + bool "ST-Ericsson AB8500 GPADC driver" + depends on AB8500_CORE && REGULATOR_AB8500 + default y + help + AB8500 Analog Baseband, mixed signal integrated circuit GPADC + (General Purpose Analog to Digital Converter) driver used to monitor + internal voltages, convert accessory and battery, AC (charger, mains) + and USB voltages integral to the U8500 platform. + config AD_SIGMA_DELTA tristate select IIO_BUFFER @@ -432,6 +442,17 @@ config INGENIC_ADC This driver can also be built as a module. If so, the module will be called ingenic_adc. +config INTEL_MRFLD_ADC + tristate "Intel Merrifield Basin Cove ADC driver" + depends on INTEL_SOC_PMIC_MRFLD + help + Say yes here to have support for Basin Cove power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + + To compile this driver as a module, choose M here: the module will be + called intel_mrfld_adc. + config IMX7D_ADC tristate "Freescale IMX7D ADC driver" depends on ARCH_MXC || COMPILE_TEST @@ -508,8 +529,8 @@ config MAX1027 select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say yes here to build support for Maxim SPI ADC models - max1027, max1029 and max1031. + Say yes here to build support for Maxim SPI {10,12}-bit ADC models: + max1027, max1029, max1031, max1227, max1229 and max1231. To compile this driver as a module, choose M here: the module will be called max1027. diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ef9cc485fb67..5ecc481c2967 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7266) += ad7266.o @@ -42,6 +43,7 @@ obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o +obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c new file mode 100644 index 000000000000..fd5b18d7f0c2 --- /dev/null +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -0,0 +1,1218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Arun R Murthy <arun.murthy@stericsson.com> + * Author: Daniel Willerud <daniel.willerud@stericsson.com> + * Author: Johan Palsson <johan.palsson@stericsson.com> + * Author: M'boumba Cedric Madianga + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * AB8500 General Purpose ADC driver. The AB8500 uses reference voltages: + * VinVADC, and VADC relative to GND to do its job. It monitors main and backup + * battery voltages, AC (mains) voltage, USB cable voltage, as well as voltages + * representing the temperature of the chip die and battery, accessory + * detection by resistance measurements using relative voltages and GSM burst + * information. + * + * Some of the voltages are measured on external pins on the IC, such as + * battery temperature or "ADC aux" 1 and 2. Other voltages are internal rails + * from other parts of the ASIC such as main charger voltage, main and battery + * backup voltage or USB VBUS voltage. For this reason drivers for other + * parts of the system are required to obtain handles to the ADC to do work + * for them and the IIO driver provides arbitration among these consumers. + */ +#include <linux/init.h> +#include <linux/bits.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/regulator/consumer.h> +#include <linux/random.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/abx500/ab8500.h> + +/* GPADC register offsets and bit definitions */ + +#define AB8500_GPADC_CTRL1_REG 0x00 +/* GPADC control register 1 bits */ +#define AB8500_GPADC_CTRL1_DISABLE 0x00 +#define AB8500_GPADC_CTRL1_ENABLE BIT(0) +#define AB8500_GPADC_CTRL1_TRIG_ENA BIT(1) +#define AB8500_GPADC_CTRL1_START_SW_CONV BIT(2) +#define AB8500_GPADC_CTRL1_BTEMP_PULL_UP BIT(3) +/* 0 = use rising edge, 1 = use falling edge */ +#define AB8500_GPADC_CTRL1_TRIG_EDGE BIT(4) +/* 0 = use VTVOUT, 1 = use VRTC as pull-up supply for battery temp NTC */ +#define AB8500_GPADC_CTRL1_PUPSUPSEL BIT(5) +#define AB8500_GPADC_CTRL1_BUF_ENA BIT(6) +#define AB8500_GPADC_CTRL1_ICHAR_ENA BIT(7) + +#define AB8500_GPADC_CTRL2_REG 0x01 +#define AB8500_GPADC_CTRL3_REG 0x02 +/* + * GPADC control register 2 and 3 bits + * the bit layout is the same for SW and HW conversion set-up + */ +#define AB8500_GPADC_CTRL2_AVG_1 0x00 +#define AB8500_GPADC_CTRL2_AVG_4 BIT(5) +#define AB8500_GPADC_CTRL2_AVG_8 BIT(6) +#define AB8500_GPADC_CTRL2_AVG_16 (BIT(5) | BIT(6)) + +enum ab8500_gpadc_channel { + AB8500_GPADC_CHAN_UNUSED = 0x00, + AB8500_GPADC_CHAN_BAT_CTRL = 0x01, + AB8500_GPADC_CHAN_BAT_TEMP = 0x02, + /* This is not used on AB8505 */ + AB8500_GPADC_CHAN_MAIN_CHARGER = 0x03, + AB8500_GPADC_CHAN_ACC_DET_1 = 0x04, + AB8500_GPADC_CHAN_ACC_DET_2 = 0x05, + AB8500_GPADC_CHAN_ADC_AUX_1 = 0x06, + AB8500_GPADC_CHAN_ADC_AUX_2 = 0x07, + AB8500_GPADC_CHAN_VBAT_A = 0x08, + AB8500_GPADC_CHAN_VBUS = 0x09, + AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT = 0x0a, + AB8500_GPADC_CHAN_USB_CHARGER_CURRENT = 0x0b, + AB8500_GPADC_CHAN_BACKUP_BAT = 0x0c, + /* Only on AB8505 */ + AB8505_GPADC_CHAN_DIE_TEMP = 0x0d, + AB8500_GPADC_CHAN_ID = 0x0e, + AB8500_GPADC_CHAN_INTERNAL_TEST_1 = 0x0f, + AB8500_GPADC_CHAN_INTERNAL_TEST_2 = 0x10, + AB8500_GPADC_CHAN_INTERNAL_TEST_3 = 0x11, + /* FIXME: Applicable to all ASIC variants? */ + AB8500_GPADC_CHAN_XTAL_TEMP = 0x12, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS = 0x13, + /* FIXME: Doesn't seem to work with pure AB8500 */ + AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT = 0x1c, + AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT = 0x1d, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT = 0x1e, + AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT = 0x1f, + /* + * Virtual channel used only for ibat conversion to ampere. + * Battery current conversion (ibat) cannot be requested as a + * single conversion but it is always requested in combination + * with other input requests. + */ + AB8500_GPADC_CHAN_IBAT_VIRTUAL = 0xFF, +}; + +#define AB8500_GPADC_AUTO_TIMER_REG 0x03 + +#define AB8500_GPADC_STAT_REG 0x04 +#define AB8500_GPADC_STAT_BUSY BIT(0) + +#define AB8500_GPADC_MANDATAL_REG 0x05 +#define AB8500_GPADC_MANDATAH_REG 0x06 +#define AB8500_GPADC_AUTODATAL_REG 0x07 +#define AB8500_GPADC_AUTODATAH_REG 0x08 +#define AB8500_GPADC_MUX_CTRL_REG 0x09 +#define AB8540_GPADC_MANDATA2L_REG 0x09 +#define AB8540_GPADC_MANDATA2H_REG 0x0A +#define AB8540_GPADC_APEAAX_REG 0x10 +#define AB8540_GPADC_APEAAT_REG 0x11 +#define AB8540_GPADC_APEAAM_REG 0x12 +#define AB8540_GPADC_APEAAH_REG 0x13 +#define AB8540_GPADC_APEAAL_REG 0x14 + +/* + * OTP register offsets + * Bank : 0x15 + */ +#define AB8500_GPADC_CAL_1 0x0F +#define AB8500_GPADC_CAL_2 0x10 +#define AB8500_GPADC_CAL_3 0x11 +#define AB8500_GPADC_CAL_4 0x12 +#define AB8500_GPADC_CAL_5 0x13 +#define AB8500_GPADC_CAL_6 0x14 +#define AB8500_GPADC_CAL_7 0x15 +/* New calibration for 8540 */ +#define AB8540_GPADC_OTP4_REG_7 0x38 +#define AB8540_GPADC_OTP4_REG_6 0x39 +#define AB8540_GPADC_OTP4_REG_5 0x3A + +#define AB8540_GPADC_DIS_ZERO 0x00 +#define AB8540_GPADC_EN_VBIAS_XTAL_TEMP 0x02 + +/* GPADC constants from AB8500 spec, UM0836 */ +#define AB8500_ADC_RESOLUTION 1024 +#define AB8500_ADC_CH_BTEMP_MIN 0 +#define AB8500_ADC_CH_BTEMP_MAX 1350 +#define AB8500_ADC_CH_DIETEMP_MIN 0 +#define AB8500_ADC_CH_DIETEMP_MAX 1350 +#define AB8500_ADC_CH_CHG_V_MIN 0 +#define AB8500_ADC_CH_CHG_V_MAX 20030 +#define AB8500_ADC_CH_ACCDET2_MIN 0 +#define AB8500_ADC_CH_ACCDET2_MAX 2500 +#define AB8500_ADC_CH_VBAT_MIN 2300 +#define AB8500_ADC_CH_VBAT_MAX 4800 +#define AB8500_ADC_CH_CHG_I_MIN 0 +#define AB8500_ADC_CH_CHG_I_MAX 1500 +#define AB8500_ADC_CH_BKBAT_MIN 0 +#define AB8500_ADC_CH_BKBAT_MAX 3200 + +/* GPADC constants from AB8540 spec */ +#define AB8500_ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX 6000 +#define AB8500_ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX_V 60 +#define AB8500_GPADC_IBAT_VDROP_L (-56) /* mV */ +#define AB8500_GPADC_IBAT_VDROP_H 56 + +/* This is used to not lose precision when dividing to get gain and offset */ +#define AB8500_GPADC_CALIB_SCALE 1000 +/* + * Number of bits shift used to not lose precision + * when dividing to get ibat gain. + */ +#define AB8500_GPADC_CALIB_SHIFT_IBAT 20 + +/* Time in ms before disabling regulator */ +#define AB8500_GPADC_AUTOSUSPEND_DELAY 1 + +#define AB8500_GPADC_CONVERSION_TIME 500 /* ms */ + +enum ab8500_cal_channels { + AB8500_CAL_VMAIN = 0, + AB8500_CAL_BTEMP, + AB8500_CAL_VBAT, + AB8500_CAL_IBAT, + AB8500_CAL_NR, +}; + +/** + * struct ab8500_adc_cal_data - Table for storing gain and offset for the + * calibrated ADC channels + * @gain: Gain of the ADC channel + * @offset: Offset of the ADC channel + * @otp_calib_hi: Calibration from OTP + * @otp_calib_lo: Calibration from OTP + */ +struct ab8500_adc_cal_data { + s64 gain; + s64 offset; + u16 otp_calib_hi; + u16 otp_calib_lo; +}; + +/** + * struct ab8500_gpadc_chan_info - per-channel GPADC info + * @name: name of the channel + * @id: the internal AB8500 ID number for the channel + * @hardware_control: indicate that we want to use hardware ADC control + * on this channel, the default is software ADC control. Hardware control + * is normally only used to test the battery voltage during GSM bursts + * and needs a hardware trigger on the GPADCTrig pin of the ASIC. + * @falling_edge: indicate that we want to trigger on falling edge + * rather than rising edge, rising edge is the default + * @avg_sample: how many samples to average: must be 1, 4, 8 or 16. + * @trig_timer: how long to wait for the trigger, in 32kHz periods: + * 0 .. 255 periods + */ +struct ab8500_gpadc_chan_info { + const char *name; + u8 id; + bool hardware_control; + bool falling_edge; + u8 avg_sample; + u8 trig_timer; +}; + +/** + * struct ab8500_gpadc - AB8500 GPADC device information + * @dev: pointer to the containing device + * @ab8500: pointer to the parent AB8500 device + * @chans: internal per-channel information container + * @nchans: number of channels + * @complete: pointer to the completion that indicates + * the completion of an gpadc conversion cycle + * @vddadc: pointer to the regulator supplying VDDADC + * @irq_sw: interrupt number that is used by gpadc for software ADC conversion + * @irq_hw: interrupt number that is used by gpadc for hardware ADC conversion + * @cal_data: array of ADC calibration data structs + */ +struct ab8500_gpadc { + struct device *dev; + struct ab8500 *ab8500; + struct ab8500_gpadc_chan_info *chans; + unsigned int nchans; + struct completion complete; + struct regulator *vddadc; + int irq_sw; + int irq_hw; + struct ab8500_adc_cal_data cal_data[AB8500_CAL_NR]; +}; + +static struct ab8500_gpadc_chan_info * +ab8500_gpadc_get_channel(struct ab8500_gpadc *gpadc, u8 chan) +{ + struct ab8500_gpadc_chan_info *ch; + int i; + + for (i = 0; i < gpadc->nchans; i++) { + ch = &gpadc->chans[i]; + if (ch->id == chan) + break; + } + if (i == gpadc->nchans) + return NULL; + + return ch; +} + +/** + * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage + * @gpadc: GPADC instance + * @ch: the sampled channel this raw value is coming from + * @ad_value: the raw value + */ +static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, + enum ab8500_gpadc_channel ch, + int ad_value) +{ + int res; + + switch (ch) { + case AB8500_GPADC_CHAN_MAIN_CHARGER: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VMAIN].gain) { + res = AB8500_ADC_CH_CHG_V_MIN + (AB8500_ADC_CH_CHG_V_MAX - + AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VMAIN].gain + + gpadc->cal_data[AB8500_CAL_VMAIN].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_BAT_CTRL: + case AB8500_GPADC_CHAN_BAT_TEMP: + case AB8500_GPADC_CHAN_ACC_DET_1: + case AB8500_GPADC_CHAN_ADC_AUX_1: + case AB8500_GPADC_CHAN_ADC_AUX_2: + case AB8500_GPADC_CHAN_XTAL_TEMP: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_BTEMP].gain) { + res = AB8500_ADC_CH_BTEMP_MIN + (AB8500_ADC_CH_BTEMP_MAX - + AB8500_ADC_CH_BTEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_BTEMP].gain + + gpadc->cal_data[AB8500_CAL_BTEMP].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_VBAT_A: + case AB8500_GPADC_CHAN_VBAT_TRUE_MEAS: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VBAT].gain) { + res = AB8500_ADC_CH_VBAT_MIN + (AB8500_ADC_CH_VBAT_MAX - + AB8500_ADC_CH_VBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VBAT].gain + + gpadc->cal_data[AB8500_CAL_VBAT].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8505_GPADC_CHAN_DIE_TEMP: + res = AB8500_ADC_CH_DIETEMP_MIN + + (AB8500_ADC_CH_DIETEMP_MAX - AB8500_ADC_CH_DIETEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_ACC_DET_2: + res = AB8500_ADC_CH_ACCDET2_MIN + + (AB8500_ADC_CH_ACCDET2_MAX - AB8500_ADC_CH_ACCDET2_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_VBUS: + res = AB8500_ADC_CH_CHG_V_MIN + + (AB8500_ADC_CH_CHG_V_MAX - AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + res = AB8500_ADC_CH_CHG_I_MIN + + (AB8500_ADC_CH_CHG_I_MAX - AB8500_ADC_CH_CHG_I_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_BACKUP_BAT: + res = AB8500_ADC_CH_BKBAT_MIN + + (AB8500_ADC_CH_BKBAT_MAX - AB8500_ADC_CH_BKBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_IBAT_VIRTUAL: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_IBAT].gain) { + res = AB8500_ADC_CH_IBAT_MIN + (AB8500_ADC_CH_IBAT_MAX - + AB8500_ADC_CH_IBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_IBAT].gain + + gpadc->cal_data[AB8500_CAL_IBAT].offset) + >> AB8500_GPADC_CALIB_SHIFT_IBAT; + break; + + default: + dev_err(gpadc->dev, + "unknown channel ID: %d, not possible to convert\n", + ch); + res = -EINVAL; + break; + + } + + return res; +} + +static int ab8500_gpadc_read(struct ab8500_gpadc *gpadc, + const struct ab8500_gpadc_chan_info *ch, + int *ibat) +{ + int ret; + int looplimit = 0; + unsigned long completion_timeout; + u8 val; + u8 low_data, high_data, low_data2, high_data2; + u8 ctrl1; + u8 ctrl23; + unsigned int delay_min = 0; + unsigned int delay_max = 0; + u8 data_low_addr, data_high_addr; + + if (!gpadc) + return -ENODEV; + + /* check if conversion is supported */ + if ((gpadc->irq_sw <= 0) && !ch->hardware_control) + return -ENOTSUPP; + if ((gpadc->irq_hw <= 0) && ch->hardware_control) + return -ENOTSUPP; + + /* Enable vddadc by grabbing PM runtime */ + pm_runtime_get_sync(gpadc->dev); + + /* Check if ADC is not busy, lock and proceed */ + do { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); + if (ret < 0) + goto out; + if (!(val & AB8500_GPADC_STAT_BUSY)) + break; + msleep(20); + } while (++looplimit < 10); + if (looplimit >= 10 && (val & AB8500_GPADC_STAT_BUSY)) { + dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); + ret = -EINVAL; + goto out; + } + + /* Enable GPADC */ + ctrl1 = AB8500_GPADC_CTRL1_ENABLE; + + /* Select the channel source and set average samples */ + switch (ch->avg_sample) { + case 1: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_1; + break; + case 4: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_4; + break; + case 8: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_8; + break; + default: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_16; + break; + } + + if (ch->hardware_control) { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL3_REG, ctrl23); + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_ENA; + if (ch->falling_edge) + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_EDGE; + } else { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL2_REG, ctrl23); + } + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set avg samples failed\n"); + goto out; + } + + /* + * Enable ADC, buffering, select rising edge and enable ADC path + * charging current sense if it needed, ABB 3.0 needs some special + * treatment too. + */ + switch (ch->id) { + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_ICHAR_ENA; + break; + case AB8500_GPADC_CHAN_BAT_TEMP: + if (!is_ab8500_2p0_or_earlier(gpadc->ab8500)) { + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_BTEMP_PULL_UP; + /* + * Delay might be needed for ABB8500 cut 3.0, if not, + * remove when hardware will be available + */ + delay_min = 1000; /* Delay in micro seconds */ + delay_max = 10000; /* large range optimises sleepmode */ + break; + } + /* Fall through */ + default: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA; + break; + } + + /* Write configuration to control register 1 */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ctrl1); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set Control register failed\n"); + goto out; + } + + if (delay_min != 0) + usleep_range(delay_min, delay_max); + + if (ch->hardware_control) { + /* Set trigger delay timer */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, + ch->trig_timer); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: trig timer failed\n"); + goto out; + } + completion_timeout = 2 * HZ; + data_low_addr = AB8500_GPADC_AUTODATAL_REG; + data_high_addr = AB8500_GPADC_AUTODATAH_REG; + } else { + /* Start SW conversion */ + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + AB8500_GPADC_CTRL1_START_SW_CONV, + AB8500_GPADC_CTRL1_START_SW_CONV); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: start s/w conv failed\n"); + goto out; + } + completion_timeout = msecs_to_jiffies(AB8500_GPADC_CONVERSION_TIME); + data_low_addr = AB8500_GPADC_MANDATAL_REG; + data_high_addr = AB8500_GPADC_MANDATAH_REG; + } + + /* Wait for completion of conversion */ + if (!wait_for_completion_timeout(&gpadc->complete, + completion_timeout)) { + dev_err(gpadc->dev, + "timeout didn't receive GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; + } + + /* Read the converted RAW data */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_low_addr, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read low data failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_high_addr, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read high data failed\n"); + goto out; + } + + /* Check if double conversion is required */ + if ((ch->id == AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT)) { + + if (ch->hardware_control) { + /* not supported */ + ret = -ENOTSUPP; + dev_err(gpadc->dev, + "gpadc_conversion: only SW double conversion supported\n"); + goto out; + } else { + /* Read the converted RAW data 2 */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG, + &low_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw low data 2 failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG, + &high_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw high data 2 failed\n"); + goto out; + } + if (ibat != NULL) { + *ibat = (high_data2 << 8) | low_data2; + } else { + dev_warn(gpadc->dev, + "gpadc_conversion: ibat not stored\n"); + } + + } + } + + /* Disable GPADC */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); + goto out; + } + + /* This eventually drops the regulator */ + pm_runtime_mark_last_busy(gpadc->dev); + pm_runtime_put_autosuspend(gpadc->dev); + + return (high_data << 8) | low_data; + +out: + /* + * It has shown to be needed to turn off the GPADC if an error occurs, + * otherwise we might have problem when waiting for the busy bit in the + * GPADC status register to go low. In V1.1 there wait_for_completion + * seems to timeout when waiting for an interrupt.. Not seen in V2.0 + */ + (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + pm_runtime_put(gpadc->dev); + dev_err(gpadc->dev, + "gpadc_conversion: Failed to AD convert channel %d\n", ch->id); + + return ret; +} + +/** + * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion + * @irq: irq number + * @data: pointer to the data passed during request irq + * + * This is a interrupt service routine for gpadc conversion completion. + * Notifies the gpadc completion is completed and the converted raw value + * can be read from the registers. + * Returns IRQ status(IRQ_HANDLED) + */ +static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *data) +{ + struct ab8500_gpadc *gpadc = data; + + complete(&gpadc->complete); + + return IRQ_HANDLED; +} + +static int otp_cal_regs[] = { + AB8500_GPADC_CAL_1, + AB8500_GPADC_CAL_2, + AB8500_GPADC_CAL_3, + AB8500_GPADC_CAL_4, + AB8500_GPADC_CAL_5, + AB8500_GPADC_CAL_6, + AB8500_GPADC_CAL_7, +}; + +static int otp4_cal_regs[] = { + AB8540_GPADC_OTP4_REG_7, + AB8540_GPADC_OTP4_REG_6, + AB8540_GPADC_OTP4_REG_5, +}; + +static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) +{ + int i; + int ret[ARRAY_SIZE(otp_cal_regs)]; + u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; + int ret_otp4[ARRAY_SIZE(otp4_cal_regs)]; + u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; + int vmain_high, vmain_low; + int btemp_high, btemp_low; + int vbat_high, vbat_low; + int ibat_high, ibat_low; + s64 V_gain, V_offset, V2A_gain, V2A_offset; + + /* First we read all OTP registers and store the error code */ + for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { + ret[i] = abx500_get_register_interruptible(gpadc->dev, + AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); + if (ret[i] < 0) { + /* Continue anyway: maybe the other registers are OK */ + dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", + __func__, otp_cal_regs[i]); + } else { + /* Put this in the entropy pool as device-unique */ + add_device_randomness(&ret[i], sizeof(ret[i])); + } + } + + /* + * The ADC calibration data is stored in OTP registers. + * The layout of the calibration data is outlined below and a more + * detailed description can be found in UM0836 + * + * vm_h/l = vmain_high/low + * bt_h/l = btemp_high/low + * vb_h/l = vbat_high/low + * + * Data bits 8500/9540: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h9 | vm_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP2 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP4 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | ib_h9 | ib_h8 | ib_h7 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 | + * + * + * Ideal output ADC codes corresponding to injected input voltages + * during manufacturing is: + * + * vmain_high: Vin = 19500mV / ADC ideal code = 997 + * vmain_low: Vin = 315mV / ADC ideal code = 16 + * btemp_high: Vin = 1300mV / ADC ideal code = 985 + * btemp_low: Vin = 21mV / ADC ideal code = 16 + * vbat_high: Vin = 4700mV / ADC ideal code = 982 + * vbat_low: Vin = 2380mV / ADC ideal code = 33 + */ + + if (is_ab8540(gpadc->ab8500)) { + /* Calculate gain and offset for VMAIN if all reads succeeded*/ + if (!(ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + + /* Read IBAT calibration Data */ + for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) { + ret_otp4[i] = abx500_get_register_interruptible( + gpadc->dev, AB8500_OTP_EMUL, + otp4_cal_regs[i], &gpadc_otp4[i]); + if (ret_otp4[i] < 0) + dev_err(gpadc->dev, + "%s: read otp4 reg 0x%02x failed\n", + __func__, otp4_cal_regs[i]); + } + + /* Calculate gain and offset for IBAT if all reads succeeded */ + if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) { + ibat_high = (((gpadc_otp4[0] & 0x07) << 7) | + ((gpadc_otp4[1] & 0xFE) >> 1)); + ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | + ((gpadc_otp4[2] & 0xF8) >> 3)); + + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_hi = + (u16)ibat_high; + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_lo = + (u16)ibat_low; + + V_gain = ((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) + << AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); + + V_offset = (AB8500_GPADC_IBAT_VDROP_H << AB8500_GPADC_CALIB_SHIFT_IBAT) - + (((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) << + AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low)) + * ibat_high; + /* + * Result obtained is in mV (at a scale factor), + * we need to calculate gain and offset to get mA + */ + V2A_gain = (AB8500_ADC_CH_IBAT_MAX - AB8500_ADC_CH_IBAT_MIN)/ + (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + V2A_offset = ((AB8500_ADC_CH_IBAT_MAX_V * AB8500_ADC_CH_IBAT_MIN - + AB8500_ADC_CH_IBAT_MAX * AB8500_ADC_CH_IBAT_MIN_V) + << AB8500_GPADC_CALIB_SHIFT_IBAT) + / (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + + gpadc->cal_data[AB8500_CAL_IBAT].gain = + V_gain * V2A_gain; + gpadc->cal_data[AB8500_CAL_IBAT].offset = + V_offset * V2A_gain + V2A_offset; + } else { + gpadc->cal_data[AB8500_CAL_IBAT].gain = 0; + } + } else { + /* Calculate gain and offset for VMAIN if all reads succeeded */ + if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[0] & 0x03) << 8) | + ((gpadc_cal[1] & 0x3F) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + } + + /* Calculate gain and offset for BTEMP if all reads succeeded */ + if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { + btemp_high = (((gpadc_cal[2] & 0x01) << 9) | + (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); + btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); + + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_hi = (u16)btemp_high; + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_lo = (u16)btemp_low; + + gpadc->cal_data[AB8500_CAL_BTEMP].gain = + AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); + gpadc->cal_data[AB8500_CAL_BTEMP].offset = AB8500_GPADC_CALIB_SCALE * 1300 - + (AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low)) + * btemp_high; + } else { + gpadc->cal_data[AB8500_CAL_BTEMP].gain = 0; + } + + /* Calculate gain and offset for VBAT if all reads succeeded */ + if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { + vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); + vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); + + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_hi = (u16)vbat_high; + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_lo = (u16)vbat_low; + + gpadc->cal_data[AB8500_CAL_VBAT].gain = AB8500_GPADC_CALIB_SCALE * + (4700 - 2380) / (vbat_high - vbat_low); + gpadc->cal_data[AB8500_CAL_VBAT].offset = AB8500_GPADC_CALIB_SCALE * 4700 - + (AB8500_GPADC_CALIB_SCALE * (4700 - 2380) / + (vbat_high - vbat_low)) * vbat_high; + } else { + gpadc->cal_data[AB8500_CAL_VBAT].gain = 0; + } +} + +static int ab8500_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + const struct ab8500_gpadc_chan_info *ch; + int raw_val; + int processed; + + ch = ab8500_gpadc_get_channel(gpadc, chan->address); + if (!ch) { + dev_err(gpadc->dev, "no such channel %lu\n", + chan->address); + return -EINVAL; + } + + raw_val = ab8500_gpadc_read(gpadc, ch, NULL); + if (raw_val < 0) + return raw_val; + + if (mask == IIO_CHAN_INFO_RAW) { + *val = raw_val; + return IIO_VAL_INT; + } + + if (mask == IIO_CHAN_INFO_PROCESSED) { + processed = ab8500_gpadc_ad_to_voltage(gpadc, ch->id, raw_val); + if (processed < 0) + return processed; + + /* Return millivolt or milliamps or millicentigrades */ + *val = processed * 1000; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ab8500_gpadc_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info ab8500_gpadc_info = { + .of_xlate = ab8500_gpadc_of_xlate, + .read_raw = ab8500_gpadc_read_raw, +}; + +#ifdef CONFIG_PM +static int ab8500_gpadc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + regulator_disable(gpadc->vddadc); + + return 0; +} + +static int ab8500_gpadc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(gpadc->vddadc); + if (ret) + dev_err(dev, "Failed to enable vddadc: %d\n", ret); + + return ret; +} +#endif + +/** + * ab8500_gpadc_parse_channel() - process devicetree channel configuration + * @dev: pointer to containing device + * @np: device tree node for the channel to configure + * @ch: channel info to fill in + * @iio_chan: IIO channel specification to fill in + * + * The devicetree will set up the channel for use with the specific device, + * and define usage for things like AUX GPADC inputs more precisely. + */ +static int ab8500_gpadc_parse_channel(struct device *dev, + struct device_node *np, + struct ab8500_gpadc_chan_info *ch, + struct iio_chan_spec *iio_chan) +{ + const char *name = np->name; + u32 chan; + int ret; + + ret = of_property_read_u32(np, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + if (chan > AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT) { + dev_err(dev, "%s channel number out of range %d\n", name, chan); + return -EINVAL; + } + + iio_chan->channel = chan; + iio_chan->datasheet_name = name; + iio_chan->indexed = 1; + iio_chan->address = chan; + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED); + /* Most are voltages (also temperatures), some are currents */ + if ((chan == AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT) || + (chan == AB8500_GPADC_CHAN_USB_CHARGER_CURRENT)) + iio_chan->type = IIO_CURRENT; + else + iio_chan->type = IIO_VOLTAGE; + + ch->id = chan; + + /* Sensible defaults */ + ch->avg_sample = 16; + ch->hardware_control = false; + ch->falling_edge = false; + ch->trig_timer = 0; + + return 0; +} + +/** + * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT + * @gpadc: the GPADC to configure the channels for + * @np: device tree node containing the channel configurations + * @chans: the IIO channels we parsed + * @nchans: the number of IIO channels we parsed + */ +static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, + struct device_node *np, + struct iio_chan_spec **chans_parsed, + unsigned int *nchans_parsed) +{ + struct device_node *child; + struct ab8500_gpadc_chan_info *ch; + struct iio_chan_spec *iio_chans; + unsigned int nchans; + int i; + + nchans = of_get_available_child_count(np); + if (!nchans) { + dev_err(gpadc->dev, "no channel children\n"); + return -ENODEV; + } + dev_info(gpadc->dev, "found %d ADC channels\n", nchans); + + iio_chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*iio_chans), GFP_KERNEL); + if (!iio_chans) + return -ENOMEM; + + gpadc->chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*gpadc->chans), GFP_KERNEL); + if (!gpadc->chans) + return -ENOMEM; + + i = 0; + for_each_available_child_of_node(np, child) { + struct iio_chan_spec *iio_chan; + int ret; + + ch = &gpadc->chans[i]; + iio_chan = &iio_chans[i]; + + ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch, + iio_chan); + if (ret) { + of_node_put(child); + return ret; + } + i++; + } + gpadc->nchans = nchans; + *chans_parsed = iio_chans; + *nchans_parsed = nchans; + + return 0; +} + +static int ab8500_gpadc_probe(struct platform_device *pdev) +{ + struct ab8500_gpadc *gpadc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct iio_chan_spec *iio_chans; + unsigned int n_iio_chans; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + gpadc = iio_priv(indio_dev); + + gpadc->dev = dev; + gpadc->ab8500 = dev_get_drvdata(dev->parent); + + ret = ab8500_gpadc_parse_channels(gpadc, np, &iio_chans, &n_iio_chans); + if (ret) + return ret; + + gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); + if (gpadc->irq_sw < 0) { + dev_err(dev, "failed to get platform sw_conv_end irq\n"); + return gpadc->irq_sw; + } + + gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); + if (gpadc->irq_hw < 0) { + dev_err(dev, "failed to get platform hw_conv_end irq\n"); + return gpadc->irq_hw; + } + + /* Initialize completion used to notify completion of conversion */ + init_completion(&gpadc->complete); + + /* Request interrupts */ + ret = devm_request_threaded_irq(dev, gpadc->irq_sw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-sw", gpadc); + if (ret < 0) { + dev_err(dev, + "failed to request sw conversion irq %d\n", + gpadc->irq_sw); + return ret; + } + + ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-hw", gpadc); + if (ret < 0) { + dev_err(dev, + "Failed to request hw conversion irq: %d\n", + gpadc->irq_hw); + return ret; + } + + /* The VTVout LDO used to power the AB8500 GPADC */ + gpadc->vddadc = devm_regulator_get(dev, "vddadc"); + if (IS_ERR(gpadc->vddadc)) { + ret = PTR_ERR(gpadc->vddadc); + dev_err(dev, "failed to get vddadc\n"); + return ret; + } + + ret = regulator_enable(gpadc->vddadc); + if (ret) { + dev_err(dev, "failed to enable vddadc: %d\n", ret); + return ret; + } + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, AB8500_GPADC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + + ab8500_gpadc_read_calibration_data(gpadc); + + pm_runtime_put(dev); + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = np; + indio_dev->name = "ab8500-gpadc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ab8500_gpadc_info; + indio_dev->channels = iio_chans; + indio_dev->num_channels = n_iio_chans; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + goto out_dis_pm; + + return 0; + +out_dis_pm: + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + regulator_disable(gpadc->vddadc); + + return ret; +} + +static int ab8500_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + pm_runtime_get_sync(gpadc->dev); + pm_runtime_put_noidle(gpadc->dev); + pm_runtime_disable(gpadc->dev); + regulator_disable(gpadc->vddadc); + + return 0; +} + +static const struct dev_pm_ops ab8500_gpadc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend, + ab8500_gpadc_runtime_resume, + NULL) +}; + +static struct platform_driver ab8500_gpadc_driver = { + .probe = ab8500_gpadc_probe, + .remove = ab8500_gpadc_remove, + .driver = { + .name = "ab8500-gpadc", + .pm = &ab8500_gpadc_pm_ops, + }, +}; +builtin_platform_driver(ab8500_gpadc_driver); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index a2837a0e7cba..e1850f3d5cf3 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1483,7 +1483,7 @@ dma_free_area: st->dma_st.rx_buf, st->dma_st.rx_dma_buf); dma_chan_disable: dma_release_channel(st->dma_st.dma_chan); - st->dma_st.dma_chan = 0; + st->dma_st.dma_chan = NULL; dma_exit: dev_info(&pdev->dev, "continuing without DMA support\n"); } @@ -1506,7 +1506,7 @@ static void at91_adc_dma_disable(struct platform_device *pdev) dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, st->dma_st.rx_buf, st->dma_st.rx_dma_buf); dma_release_channel(st->dma_st.dma_chan); - st->dma_st.dma_chan = 0; + st->dma_st.dma_chan = NULL; dev_info(&pdev->dev, "continuing without DMA support\n"); } diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c index 2d616cafe75f..5086a337f4c9 100644 --- a/drivers/iio/adc/cpcap-adc.c +++ b/drivers/iio/adc/cpcap-adc.c @@ -1008,7 +1008,7 @@ static int cpcap_adc_probe(struct platform_device *pdev) error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL, cpcap_adc_irq_thread, - IRQF_TRIGGER_NONE, + IRQF_TRIGGER_NONE | IRQF_ONESHOT, "cpcap-adc", indio_dev); if (error) { dev_err(&pdev->dev, "could not get irq: %i\n", diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 42a3ced11fbd..2df7d057b249 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -651,7 +651,7 @@ static irqreturn_t exynos_ts_isr(int irq, void *dev_id) input_sync(info->input); usleep_range(1000, 1100); - }; + } writel(0, ADC_V1_CLRINTPNDNUP(info->regs)); @@ -769,7 +769,6 @@ static int exynos_adc_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = NULL; - struct resource *mem; bool has_ts = false; int ret = -ENODEV; int irq; @@ -788,8 +787,7 @@ static int exynos_adc_probe(struct platform_device *pdev) return -EINVAL; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->regs = devm_ioremap_resource(&pdev->dev, mem); + info->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 62e6c8badd22..c8686558429b 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -23,6 +23,7 @@ /* gain to pulse and scale conversion */ #define HX711_GAIN_MAX 3 +#define HX711_RESET_GAIN 128 struct hx711_gain_to_scale { int gain; @@ -185,8 +186,7 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data) static int hx711_reset(struct hx711_data *hx711_data) { - int ret; - int val = gpiod_get_value(hx711_data->gpiod_dout); + int val = hx711_wait_for_ready(hx711_data); if (val) { /* @@ -202,22 +202,10 @@ static int hx711_reset(struct hx711_data *hx711_data) msleep(10); gpiod_set_value(hx711_data->gpiod_pd_sck, 0); - ret = hx711_wait_for_ready(hx711_data); - if (ret) - return ret; - /* - * after a reset the gain is 128 so we do a dummy read - * to set the gain for the next read - */ - ret = hx711_read(hx711_data); - if (ret < 0) - return ret; - - /* - * after a dummy read we need to wait vor readiness - * for not mixing gain pulses with the clock - */ val = hx711_wait_for_ready(hx711_data); + + /* after a reset the gain is 128 */ + hx711_data->gain_set = HX711_RESET_GAIN; } return val; diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c new file mode 100644 index 000000000000..67d096f8180d --- /dev/null +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADC driver for Basin Cove PMIC + * + * Copyright (C) 2012 Intel Corporation + * Author: Bin Yang <bin.yang@intel.com> + * + * Rewritten for upstream by: + * Vincent Pelletier <plr.vincent@gmail.com> + * Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/mfd/intel_soc_pmic_mrfld.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <linux/iio/driver.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> + +#include <asm/unaligned.h> + +#define BCOVE_GPADCREQ 0xDC +#define BCOVE_GPADCREQ_BUSY BIT(0) +#define BCOVE_GPADCREQ_IRQEN BIT(1) + +#define BCOVE_ADCIRQ_ALL ( \ + BCOVE_ADCIRQ_BATTEMP | \ + BCOVE_ADCIRQ_SYSTEMP | \ + BCOVE_ADCIRQ_BATTID | \ + BCOVE_ADCIRQ_VIBATT | \ + BCOVE_ADCIRQ_CCTICK) + +#define BCOVE_ADC_TIMEOUT msecs_to_jiffies(1000) + +static const u8 mrfld_adc_requests[] = { + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_BATTID, + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, +}; + +struct mrfld_adc { + struct regmap *regmap; + struct completion completion; + /* Lock to protect the IPC transfers */ + struct mutex lock; +}; + +static irqreturn_t mrfld_adc_thread_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct mrfld_adc *adc = iio_priv(indio_dev); + + complete(&adc->completion); + return IRQ_HANDLED; +} + +static int mrfld_adc_single_conv(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *result) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->regmap; + unsigned int req; + long timeout; + u8 buf[2]; + int ret; + + reinit_completion(&adc->completion); + + regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0); + regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0); + + ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req, + !(req & BCOVE_GPADCREQ_BUSY), + 2000, 1000000); + if (ret) + goto done; + + req = mrfld_adc_requests[chan->channel]; + ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req); + if (ret) + goto done; + + timeout = wait_for_completion_interruptible_timeout(&adc->completion, + BCOVE_ADC_TIMEOUT); + if (timeout < 0) { + ret = timeout; + goto done; + } + if (timeout == 0) { + ret = -ETIMEDOUT; + goto done; + } + + ret = regmap_bulk_read(regmap, chan->address, buf, 2); + if (ret) + goto done; + + *result = get_unaligned_be16(buf); + ret = IIO_VAL_INT; + +done: + regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff); + regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff); + + return ret; +} + +static int mrfld_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = mrfld_adc_single_conv(indio_dev, chan, val); + mutex_unlock(&adc->lock); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info mrfld_adc_iio_info = { + .read_raw = &mrfld_adc_read_raw, +}; + +#define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address) \ + { \ + .indexed = 1, \ + .type = _type, \ + .channel = _channel, \ + .address = _address, \ + .datasheet_name = _datasheet_name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + } + +static const struct iio_chan_spec mrfld_adc_channels[] = { + BCOVE_ADC_CHANNEL(IIO_VOLTAGE, 0, "CH0", 0xE9), + BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB), + BCOVE_ADC_CHANNEL(IIO_CURRENT, 2, "CH2", 0xED), + BCOVE_ADC_CHANNEL(IIO_TEMP, 3, "CH3", 0xCC), + BCOVE_ADC_CHANNEL(IIO_TEMP, 4, "CH4", 0xC8), + BCOVE_ADC_CHANNEL(IIO_TEMP, 5, "CH5", 0xCA), + BCOVE_ADC_CHANNEL(IIO_TEMP, 6, "CH6", 0xC2), + BCOVE_ADC_CHANNEL(IIO_TEMP, 7, "CH7", 0xC4), + BCOVE_ADC_CHANNEL(IIO_TEMP, 8, "CH8", 0xC6), +}; + +static struct iio_map iio_maps[] = { + IIO_MAP("CH0", "bcove-battery", "VBATRSLT"), + IIO_MAP("CH1", "bcove-battery", "BATTID"), + IIO_MAP("CH2", "bcove-battery", "IBATRSLT"), + IIO_MAP("CH3", "bcove-temp", "PMICTEMP"), + IIO_MAP("CH4", "bcove-temp", "BATTEMP0"), + IIO_MAP("CH5", "bcove-temp", "BATTEMP1"), + IIO_MAP("CH6", "bcove-temp", "SYSTEMP0"), + IIO_MAP("CH7", "bcove-temp", "SYSTEMP1"), + IIO_MAP("CH8", "bcove-temp", "SYSTEMP2"), + {} +}; + +static int mrfld_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); + struct iio_dev *indio_dev; + struct mrfld_adc *adc; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*indio_dev)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + + mutex_init(&adc->lock); + init_completion(&adc->completion); + adc->regmap = pmic->regmap; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr, + IRQF_ONESHOT | IRQF_SHARED, pdev->name, + indio_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->dev.parent = dev; + indio_dev->name = pdev->name; + + indio_dev->channels = mrfld_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels); + indio_dev->info = &mrfld_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_map_array_register(indio_dev, iio_maps); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + goto err_array_unregister; + + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + return ret; +} + +static int mrfld_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_map_array_unregister(indio_dev); + + return 0; +} + +static const struct platform_device_id mrfld_adc_id_table[] = { + { .name = "mrfld_bcove_adc" }, + {} +}; +MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table); + +static struct platform_driver mrfld_adc_driver = { + .driver = { + .name = "mrfld_bcove_adc", + }, + .probe = mrfld_adc_probe, + .remove = mrfld_adc_remove, + .id_table = mrfld_adc_id_table, +}; +module_platform_driver(mrfld_adc_driver); + +MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>"); +MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>"); +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 214883458582..e171db20c04a 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -63,12 +63,18 @@ enum max1027_id { max1027, max1029, max1031, + max1227, + max1229, + max1231, }; static const struct spi_device_id max1027_id[] = { {"max1027", max1027}, {"max1029", max1029}, {"max1031", max1031}, + {"max1227", max1227}, + {"max1229", max1229}, + {"max1231", max1231}, {} }; MODULE_DEVICE_TABLE(spi, max1027_id); @@ -78,12 +84,15 @@ static const struct of_device_id max1027_adc_dt_ids[] = { { .compatible = "maxim,max1027" }, { .compatible = "maxim,max1029" }, { .compatible = "maxim,max1031" }, + { .compatible = "maxim,max1227" }, + { .compatible = "maxim,max1229" }, + { .compatible = "maxim,max1231" }, {}, }; MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); #endif -#define MAX1027_V_CHAN(index) \ +#define MAX1027_V_CHAN(index, depth) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -93,7 +102,7 @@ MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); .scan_index = index + 1, \ .scan_type = { \ .sign = 'u', \ - .realbits = 10, \ + .realbits = depth, \ .storagebits = 16, \ .shift = 2, \ .endianness = IIO_BE, \ @@ -115,52 +124,54 @@ MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); }, \ } +#define MAX1X27_CHANNELS(depth) \ + MAX1027_T_CHAN, \ + MAX1027_V_CHAN(0, depth), \ + MAX1027_V_CHAN(1, depth), \ + MAX1027_V_CHAN(2, depth), \ + MAX1027_V_CHAN(3, depth), \ + MAX1027_V_CHAN(4, depth), \ + MAX1027_V_CHAN(5, depth), \ + MAX1027_V_CHAN(6, depth), \ + MAX1027_V_CHAN(7, depth) + +#define MAX1X29_CHANNELS(depth) \ + MAX1X27_CHANNELS(depth), \ + MAX1027_V_CHAN(8, depth), \ + MAX1027_V_CHAN(9, depth), \ + MAX1027_V_CHAN(10, depth), \ + MAX1027_V_CHAN(11, depth) + +#define MAX1X31_CHANNELS(depth) \ + MAX1X27_CHANNELS(depth), \ + MAX1X29_CHANNELS(depth), \ + MAX1027_V_CHAN(12, depth), \ + MAX1027_V_CHAN(13, depth), \ + MAX1027_V_CHAN(14, depth), \ + MAX1027_V_CHAN(15, depth) + static const struct iio_chan_spec max1027_channels[] = { - MAX1027_T_CHAN, - MAX1027_V_CHAN(0), - MAX1027_V_CHAN(1), - MAX1027_V_CHAN(2), - MAX1027_V_CHAN(3), - MAX1027_V_CHAN(4), - MAX1027_V_CHAN(5), - MAX1027_V_CHAN(6), - MAX1027_V_CHAN(7) + MAX1X27_CHANNELS(10), }; static const struct iio_chan_spec max1029_channels[] = { - MAX1027_T_CHAN, - MAX1027_V_CHAN(0), - MAX1027_V_CHAN(1), - MAX1027_V_CHAN(2), - MAX1027_V_CHAN(3), - MAX1027_V_CHAN(4), - MAX1027_V_CHAN(5), - MAX1027_V_CHAN(6), - MAX1027_V_CHAN(7), - MAX1027_V_CHAN(8), - MAX1027_V_CHAN(9), - MAX1027_V_CHAN(10), - MAX1027_V_CHAN(11) + MAX1X29_CHANNELS(10), }; static const struct iio_chan_spec max1031_channels[] = { - MAX1027_T_CHAN, - MAX1027_V_CHAN(0), - MAX1027_V_CHAN(1), - MAX1027_V_CHAN(2), - MAX1027_V_CHAN(3), - MAX1027_V_CHAN(4), - MAX1027_V_CHAN(5), - MAX1027_V_CHAN(6), - MAX1027_V_CHAN(7), - MAX1027_V_CHAN(8), - MAX1027_V_CHAN(9), - MAX1027_V_CHAN(10), - MAX1027_V_CHAN(11), - MAX1027_V_CHAN(12), - MAX1027_V_CHAN(13), - MAX1027_V_CHAN(14), - MAX1027_V_CHAN(15) + MAX1X31_CHANNELS(10), +}; + +static const struct iio_chan_spec max1227_channels[] = { + MAX1X27_CHANNELS(12), +}; + +static const struct iio_chan_spec max1229_channels[] = { + MAX1X29_CHANNELS(12), +}; + +static const struct iio_chan_spec max1231_channels[] = { + MAX1X31_CHANNELS(12), }; static const unsigned long max1027_available_scan_masks[] = { @@ -200,6 +211,21 @@ static const struct max1027_chip_info max1027_chip_info_tbl[] = { .num_channels = ARRAY_SIZE(max1031_channels), .available_scan_masks = max1031_available_scan_masks, }, + [max1227] = { + .channels = max1227_channels, + .num_channels = ARRAY_SIZE(max1227_channels), + .available_scan_masks = max1027_available_scan_masks, + }, + [max1229] = { + .channels = max1229_channels, + .num_channels = ARRAY_SIZE(max1229_channels), + .available_scan_masks = max1029_available_scan_masks, + }, + [max1231] = { + .channels = max1231_channels, + .num_channels = ARRAY_SIZE(max1231_channels), + .available_scan_masks = max1031_available_scan_masks, + }, }; struct max1027_state { @@ -284,7 +310,7 @@ static int max1027_read_raw(struct iio_dev *indio_dev, break; case IIO_VOLTAGE: *val = 2500; - *val2 = 10; + *val2 = chan->scan_type.realbits; ret = IIO_VAL_FRACTIONAL_LOG2; break; default: @@ -309,8 +335,11 @@ static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, struct max1027_state *st = iio_priv(indio_dev); u8 *val = (u8 *)st->buffer; - if (readval != NULL) - return -EINVAL; + if (readval) { + int ret = spi_read(st->spi, val, 2); + *readval = be16_to_cpu(st->buffer[0]); + return ret; + } *val = (u8)writeval; return spi_write(st->spi, val, 1); @@ -427,34 +456,47 @@ static int max1027_probe(struct spi_device *spi) return -ENOMEM; } - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, - &iio_pollfunc_store_time, - &max1027_trigger_handler, NULL); - if (ret < 0) { - dev_err(&indio_dev->dev, "Failed to setup buffer\n"); - return ret; - } + if (spi->irq) { + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &max1027_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to setup buffer\n"); + return ret; + } - st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", - indio_dev->name); - if (st->trig == NULL) { - ret = -ENOMEM; - dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n"); - return ret; - } + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", + indio_dev->name); + if (st->trig == NULL) { + ret = -ENOMEM; + dev_err(&indio_dev->dev, + "Failed to allocate iio trigger\n"); + return ret; + } - st->trig->ops = &max1027_trigger_ops; - st->trig->dev.parent = &spi->dev; - iio_trigger_set_drvdata(st->trig, indio_dev); - iio_trigger_register(st->trig); + st->trig->ops = &max1027_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + iio_trigger_register(st->trig); + + ret = devm_request_threaded_irq(&spi->dev, spi->irq, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_FALLING, + spi->dev.driver->name, + st->trig); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); + return ret; + } + } - ret = devm_request_threaded_irq(&spi->dev, spi->irq, - iio_trigger_generic_data_rdy_poll, - NULL, - IRQF_TRIGGER_FALLING, - spi->dev.driver->name, st->trig); + /* Internal reset */ + st->reg = MAX1027_RST_REG; + ret = spi_write(st->spi, &st->reg, 1); if (ret < 0) { - dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); + dev_err(&indio_dev->dev, "Failed to reset the ADC\n"); return ret; } @@ -480,5 +522,5 @@ static struct spi_driver max1027_driver = { module_spi_driver(max1027_driver); MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>"); -MODULE_DESCRIPTION("MAX1027/MAX1029/MAX1031 ADC"); +MODULE_DESCRIPTION("MAX1X27/MAX1X29/MAX1X31 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index 38bf10085696..465c7625a55a 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -164,7 +164,7 @@ static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel, case mcp3550_60: case mcp3551: case mcp3553: { - u32 raw = be32_to_cpup((u32 *)adc->rx_buf); + u32 raw = be32_to_cpup((__be32 *)adc->rx_buf); if (!(adc->spi->mode & SPI_CPOL)) raw <<= 1; /* strip Data Ready bit in SPI mode 0,0 */ diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 7b27306330a3..22a470db9ef8 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1187,7 +1187,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev) const struct meson_sar_adc_data *match_data; struct meson_sar_adc_priv *priv; struct iio_dev *indio_dev; - struct resource *res; void __iomem *base; int irq, ret; @@ -1214,8 +1213,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &meson_sar_adc_iio_info; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 93a096a91f8c..20c626c289ed 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -38,12 +38,12 @@ #define HAS_ANASWVDD BIT(1) /** - * stm32_adc_common_regs - stm32 common registers, compatible dependent data + * struct stm32_adc_common_regs - stm32 common registers * @csr: common status register offset * @ccr: common control register offset - * @eoc1: adc1 end of conversion flag in @csr - * @eoc2: adc2 end of conversion flag in @csr - * @eoc3: adc3 end of conversion flag in @csr + * @eoc1_msk: adc1 end of conversion flag in @csr + * @eoc2_msk: adc2 end of conversion flag in @csr + * @eoc3_msk: adc3 end of conversion flag in @csr * @ier: interrupt enable register offset for each adc * @eocie_msk: end of conversion interrupt enable mask in @ier */ @@ -60,7 +60,7 @@ struct stm32_adc_common_regs { struct stm32_adc_priv; /** - * stm32_adc_priv_cfg - stm32 core compatible configuration data + * struct stm32_adc_priv_cfg - stm32 core compatible configuration data * @regs: common registers for all instances * @clk_sel: clock selection routine * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) @@ -117,6 +117,7 @@ static int stm32f4_pclk_div[] = {2, 4, 6, 8}; /** * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler + * @pdev: platform device * @priv: stm32 ADC core private data * Select clock prescaler used for analog conversions, before using ADC. */ diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 663f8a5012d6..76a247ba876f 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -102,7 +102,7 @@ struct stm32_adc_calib { }; /** - * stm32_adc_regs - stm32 ADC misc registers & bitfield desc + * struct stm32_adc_regs - stm32 ADC misc registers & bitfield desc * @reg: register offset * @mask: bitfield mask * @shift: left shift @@ -114,7 +114,7 @@ struct stm32_adc_regs { }; /** - * stm32_adc_regspec - stm32 registers definition, compatible dependent data + * struct stm32_adc_regspec - stm32 registers definition * @dr: data register offset * @ier_eoc: interrupt enable register & eocie bitfield * @isr_eoc: interrupt status register & eoc bitfield @@ -140,7 +140,7 @@ struct stm32_adc_regspec { struct stm32_adc; /** - * stm32_adc_cfg - stm32 compatible configuration data + * struct stm32_adc_cfg - stm32 compatible configuration data * @regs: registers descriptions * @adc_info: per instance input channels definitions * @trigs: external trigger sources @@ -183,8 +183,8 @@ struct stm32_adc_cfg { * @rx_buf: dma rx buffer cpu address * @rx_dma_buf: dma rx buffer bus address * @rx_buf_sz: dma rx buffer size - * @difsel bitmask to set single-ended/differential channel - * @pcsel bitmask to preselect channels on some devices + * @difsel: bitmask to set single-ended/differential channel + * @pcsel: bitmask to preselect channels on some devices * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) * @cal: optional calibration data on some devices * @chan_name: channel name array @@ -254,7 +254,7 @@ static const struct stm32_adc_info stm32h7_adc_info = { .num_res = ARRAY_SIZE(stm32h7_adc_resolutions), }; -/** +/* * stm32f4_sq - describe regular sequence registers * - L: sequence len (register & bit field) * - SQ1..SQ16: sequence entries (register & bit field) @@ -301,7 +301,7 @@ static struct stm32_adc_trig_info stm32f4_adc_trigs[] = { {}, /* sentinel */ }; -/** +/* * stm32f4_smp_bits[] - describe sampling time register index & bit fields * Sorted so it can be indexed by channel number. */ @@ -392,7 +392,7 @@ static struct stm32_adc_trig_info stm32h7_adc_trigs[] = { {}, }; -/** +/* * stm32h7_smp_bits - describe sampling time register index & bit fields * Sorted so it can be indexed by channel number. */ @@ -994,6 +994,7 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, /** * stm32_adc_get_trig_extsel() - Get external trigger selection + * @indio_dev: IIO device structure * @trig: trigger * * Returns trigger extsel value, if trig matches, -EINVAL otherwise. @@ -1297,6 +1298,10 @@ static int stm32_adc_of_xlate(struct iio_dev *indio_dev, /** * stm32_adc_debugfs_reg_access - read or write register value + * @indio_dev: IIO device structure + * @reg: register offset + * @writeval: value to write + * @readval: value to read * * To read a value from an ADC register: * echo [ADC reg offset] > direct_reg_access diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c index bd72727fc417..0f88048ea48f 100644 --- a/drivers/iio/adc/stmpe-adc.c +++ b/drivers/iio/adc/stmpe-adc.c @@ -175,7 +175,7 @@ static int stmpe_read_raw(struct iio_dev *indio_dev, static irqreturn_t stmpe_adc_isr(int irq, void *dev_id) { struct stmpe_adc *info = (struct stmpe_adc *)dev_id; - u16 data; + __be16 data; if (info->channel <= STMPE_ADC_LAST_NR) { int int_sta; diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 55c5119fe575..472b08f37fea 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -495,7 +495,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) ret = twl4030_madc_disable_irq(madc, i); if (ret < 0) dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); - madc->requests[i].result_pending = 1; + madc->requests[i].result_pending = true; } for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { r = &madc->requests[i]; @@ -507,8 +507,8 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) len = twl4030_madc_read_channels(madc, method->rbase, r->channels, r->rbuf, r->raw); /* Free request */ - r->result_pending = 0; - r->active = 0; + r->result_pending = false; + r->active = false; } mutex_unlock(&madc->lock); @@ -521,15 +521,15 @@ err_i2c: */ for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { r = &madc->requests[i]; - if (r->active == 0) + if (!r->active) continue; method = &twl4030_conversion_methods[r->method]; /* Read results */ len = twl4030_madc_read_channels(madc, method->rbase, r->channels, r->rbuf, r->raw); /* Free request */ - r->result_pending = 0; - r->active = 0; + r->result_pending = false; + r->active = false; } mutex_unlock(&madc->lock); @@ -652,16 +652,16 @@ static int twl4030_madc_conversion(struct twl4030_madc_request *req) ret = twl4030_madc_start_conversion(twl4030_madc, req->method); if (ret < 0) goto out; - twl4030_madc->requests[req->method].active = 1; + twl4030_madc->requests[req->method].active = true; /* Wait until conversion is ready (ctrl register returns EOC) */ ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); if (ret) { - twl4030_madc->requests[req->method].active = 0; + twl4030_madc->requests[req->method].active = false; goto out; } ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, req->channels, req->rbuf, req->raw); - twl4030_madc->requests[req->method].active = 0; + twl4030_madc->requests[req->method].active = false; out: mutex_unlock(&twl4030_madc->lock); diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 4fd389678dba..ec227b358cd6 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1150,7 +1150,6 @@ static int xadc_probe(struct platform_device *pdev) const struct of_device_id *id; struct iio_dev *indio_dev; unsigned int bipolar_mask; - struct resource *mem; unsigned int conf0; struct xadc *xadc; int ret; @@ -1180,8 +1179,7 @@ static int xadc_probe(struct platform_device *pdev) spin_lock_init(&xadc->lock); INIT_DELAYED_WORK(&xadc->zynq_unmask_work, xadc_zynq_unmask_worker); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - xadc->base = devm_ioremap_resource(&pdev->dev, mem); + xadc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xadc->base)) return PTR_ERR(xadc->base); diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index 8cc8fe5e356d..403e8803471a 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -483,7 +483,7 @@ static void sgp_init(struct sgp_data *data) data->iaq_defval_skip_jiffies = 43 * data->measure_interval_jiffies; break; - }; + } } static const struct iio_info sgp_info = { diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index edbb956e81e8..acb9f8ecbb3d 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -117,7 +117,7 @@ static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size) break; case SPS30_READ_AUTO_CLEANING_PERIOD: buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8; - buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD; + buf[1] = (u8)(SPS30_AUTO_CLEANING_PERIOD & 0xff); /* fall through */ case SPS30_READ_DATA_READY_FLAG: case SPS30_READ_DATA: diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index 8de9f40226e6..14bbac6bee98 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -41,6 +41,7 @@ struct ad7303_state { struct regulator *vdd_reg; struct regulator *vref_reg; + struct mutex lock; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. @@ -79,7 +80,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if (pwr_down) st->config |= AD7303_CFG_POWER_DOWN(chan->channel); @@ -90,7 +91,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, * mode, so just write one of the DAC channels again */ ad7303_write(st, chan->channel, st->dac_cache[chan->channel]); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return len; } @@ -116,7 +117,9 @@ static int ad7303_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); *val = st->dac_cache[chan->channel]; + mutex_unlock(&st->lock); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: vref_uv = ad7303_get_vref(st, chan); @@ -144,11 +147,11 @@ static int ad7303_write_raw(struct iio_dev *indio_dev, if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = ad7303_write(st, chan->address, val); if (ret == 0) st->dac_cache[chan->channel] = val; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); break; default: ret = -EINVAL; @@ -211,6 +214,8 @@ static int ad7303_probe(struct spi_device *spi) st->spi = spi; + mutex_init(&st->lock); + st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd"); if (IS_ERR(st->vdd_reg)) return PTR_ERR(st->vdd_reg); diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index d0fb3124de07..9e6b4cd0a5cc 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -11,6 +11,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/of_platform.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> @@ -50,6 +51,41 @@ static const struct regmap_config stm32_dac_regmap_cfg = { .max_register = 0x3fc, }; +static int stm32_dac_core_hw_start(struct device *dev) +{ + struct stm32_dac_common *common = dev_get_drvdata(dev); + struct stm32_dac_priv *priv = to_stm32_dac_priv(common); + int ret; + + ret = regulator_enable(priv->vref); + if (ret < 0) { + dev_err(dev, "vref enable failed: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(priv->pclk); + if (ret < 0) { + dev_err(dev, "pclk enable failed: %d\n", ret); + goto err_regulator_disable; + } + + return 0; + +err_regulator_disable: + regulator_disable(priv->vref); + + return ret; +} + +static void stm32_dac_core_hw_stop(struct device *dev) +{ + struct stm32_dac_common *common = dev_get_drvdata(dev); + struct stm32_dac_priv *priv = to_stm32_dac_priv(common); + + clk_disable_unprepare(priv->pclk); + regulator_disable(priv->vref); +} + static int stm32_dac_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -66,6 +102,8 @@ static int stm32_dac_probe(struct platform_device *pdev) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + platform_set_drvdata(pdev, &priv->common); + cfg = (const struct stm32_dac_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; @@ -74,11 +112,19 @@ static int stm32_dac_probe(struct platform_device *pdev) if (IS_ERR(mmio)) return PTR_ERR(mmio); - regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg); + regmap = devm_regmap_init_mmio_clk(dev, "pclk", mmio, + &stm32_dac_regmap_cfg); if (IS_ERR(regmap)) return PTR_ERR(regmap); priv->common.regmap = regmap; + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + ret = PTR_ERR(priv->pclk); + dev_err(dev, "pclk get failed\n"); + return ret; + } + priv->vref = devm_regulator_get(dev, "vref"); if (IS_ERR(priv->vref)) { ret = PTR_ERR(priv->vref); @@ -86,33 +132,22 @@ static int stm32_dac_probe(struct platform_device *pdev) return ret; } - ret = regulator_enable(priv->vref); - if (ret < 0) { - dev_err(dev, "vref enable failed\n"); - return ret; - } + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = stm32_dac_core_hw_start(dev); + if (ret) + goto err_pm_stop; ret = regulator_get_voltage(priv->vref); if (ret < 0) { dev_err(dev, "vref get voltage failed, %d\n", ret); - goto err_vref; + goto err_hw_stop; } priv->common.vref_mv = ret / 1000; dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv); - priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - dev_err(dev, "pclk get failed\n"); - goto err_vref; - } - - ret = clk_prepare_enable(priv->pclk); - if (ret < 0) { - dev_err(dev, "pclk enable failed\n"); - goto err_vref; - } - priv->rst = devm_reset_control_get_exclusive(dev, NULL); if (!IS_ERR(priv->rst)) { reset_control_assert(priv->rst); @@ -128,39 +163,79 @@ static int stm32_dac_probe(struct platform_device *pdev) priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0); if (ret) - goto err_pclk; + goto err_hw_stop; } - platform_set_drvdata(pdev, &priv->common); ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev); if (ret < 0) { dev_err(dev, "failed to populate DT children\n"); - goto err_pclk; + goto err_hw_stop; } + pm_runtime_put(dev); + return 0; -err_pclk: - clk_disable_unprepare(priv->pclk); -err_vref: - regulator_disable(priv->vref); +err_hw_stop: + stm32_dac_core_hw_stop(dev); +err_pm_stop: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); return ret; } static int stm32_dac_remove(struct platform_device *pdev) { - struct stm32_dac_common *common = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + of_platform_depopulate(&pdev->dev); + stm32_dac_core_hw_stop(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + return 0; +} + +static int __maybe_unused stm32_dac_core_resume(struct device *dev) +{ + struct stm32_dac_common *common = dev_get_drvdata(dev); struct stm32_dac_priv *priv = to_stm32_dac_priv(common); + int ret; - of_platform_depopulate(&pdev->dev); - clk_disable_unprepare(priv->pclk); - regulator_disable(priv->vref); + if (priv->common.hfsel) { + /* restore hfsel (maybe lost under low power state) */ + ret = regmap_update_bits(priv->common.regmap, STM32_DAC_CR, + STM32H7_DAC_CR_HFSEL, + STM32H7_DAC_CR_HFSEL); + if (ret) + return ret; + } + + return pm_runtime_force_resume(dev); +} + +static int __maybe_unused stm32_dac_core_runtime_suspend(struct device *dev) +{ + stm32_dac_core_hw_stop(dev); return 0; } +static int __maybe_unused stm32_dac_core_runtime_resume(struct device *dev) +{ + return stm32_dac_core_hw_start(dev); +} + +static const struct dev_pm_ops stm32_dac_core_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume) + SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend, + stm32_dac_core_runtime_resume, + NULL) +}; + static const struct stm32_dac_cfg stm32h7_dac_cfg = { .has_hfsel = true, }; @@ -182,6 +257,7 @@ static struct platform_driver stm32_dac_driver = { .driver = { .name = "stm32-dac-core", .of_match_table = stm32_dac_of_match, + .pm = &stm32_dac_core_pm_ops, }, }; module_platform_driver(stm32_dac_driver); diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index cce26a3a6627..f22c1d9129b2 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include "stm32-dac-core.h" @@ -20,6 +21,8 @@ #define STM32_DAC_CHANNEL_2 2 #define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1) +#define STM32_DAC_AUTO_SUSPEND_DELAY_MS 2000 + /** * struct stm32_dac - private data of DAC driver * @common: reference to DAC common data @@ -49,15 +52,34 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, bool enable) { struct stm32_dac *dac = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2; u32 en = enable ? msk : 0; int ret; + /* already enabled / disabled ? */ + mutex_lock(&indio_dev->mlock); + ret = stm32_dac_is_enabled(indio_dev, ch); + if (ret < 0 || enable == !!ret) { + mutex_unlock(&indio_dev->mlock); + return ret < 0 ? ret : 0; + } + + if (enable) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + mutex_unlock(&indio_dev->mlock); + return ret; + } + } + ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en); + mutex_unlock(&indio_dev->mlock); if (ret < 0) { dev_err(&indio_dev->dev, "%s failed\n", en ? "Enable" : "Disable"); - return ret; + goto err_put_pm; } /* @@ -68,7 +90,20 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, if (en && dac->common->hfsel) udelay(1); + if (!enable) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + return 0; + +err_put_pm: + if (enable) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + + return ret; } static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val) @@ -272,6 +307,7 @@ static int stm32_dac_chan_of_init(struct iio_dev *indio_dev) static int stm32_dac_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; struct iio_dev *indio_dev; struct stm32_dac *dac; int ret; @@ -296,9 +332,61 @@ static int stm32_dac_probe(struct platform_device *pdev) if (ret < 0) return ret; - return devm_iio_device_register(&pdev->dev, indio_dev); + /* Get stm32-dac-core PM online */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, STM32_DAC_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_pm_put; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; + +err_pm_put: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + + return ret; } +static int stm32_dac_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + return 0; +} + +static int __maybe_unused stm32_dac_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + int channel = indio_dev->channels[0].channel; + int ret; + + /* Ensure DAC is disabled before suspend */ + ret = stm32_dac_is_enabled(indio_dev, channel); + if (ret) + return ret < 0 ? ret : -EBUSY; + + return pm_runtime_force_suspend(dev); +} + +static const struct dev_pm_ops stm32_dac_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume) +}; + static const struct of_device_id stm32_dac_of_match[] = { { .compatible = "st,stm32-dac", }, {}, @@ -307,9 +395,11 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match); static struct platform_driver stm32_dac_driver = { .probe = stm32_dac_probe, + .remove = stm32_dac_remove, .driver = { .name = "stm32-dac", .of_match_table = stm32_dac_of_match, + .pm = &stm32_dac_pm_ops, }, }; module_platform_driver(stm32_dac_driver); diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 80154bca18b6..8e908a749f95 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -543,7 +543,7 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) toread = bytes_per_datum; offset = 1; /* Put in some dummy value */ - fifo_values[0] = 0xAAAA; + fifo_values[0] = cpu_to_be16(0xAAAA); } ret = regmap_bulk_read(mpu3050->map, diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index c0acbb5d2ffb..57be68b291fa 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -14,7 +14,6 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/irq.h> #include <linux/delay.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index f3c7282321a8..60bb1029e759 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -40,6 +40,33 @@ config ADIS16480 source "drivers/iio/imu/bmi160/Kconfig" +config FXOS8700 + tristate + +config FXOS8700_I2C + tristate "NXP FXOS8700 I2C driver" + depends on I2C + select FXOS8700 + select REGMAP_I2C + help + Say yes here to build support for the NXP FXOS8700 m+g combo + sensor on I2C. + + This driver can also be built as a module. If so, the module will be + called fxos8700_i2c. + +config FXOS8700_SPI + tristate "NXP FXOS8700 SPI driver" + depends on SPI + select FXOS8700 + select REGMAP_SPI + help + Say yes here to build support for the NXP FXOS8700 m+g combo + sensor on SPI. + + This driver can also be built as a module. If so, the module will be + called fxos8700_spi. + config KMX61 tristate "Kionix KMX61 6-axis accelerometer and magnetometer" depends on I2C diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 4a6958865504..5237fd4bc384 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -14,6 +14,11 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o obj-y += bmi160/ + +obj-$(CONFIG_FXOS8700) += fxos8700_core.o +obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o +obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o + obj-y += inv_mpu6050/ obj-$(CONFIG_KMX61) += kmx61.o diff --git a/drivers/iio/imu/fxos8700.h b/drivers/iio/imu/fxos8700.h new file mode 100644 index 000000000000..6dfb8d7099e4 --- /dev/null +++ b/drivers/iio/imu/fxos8700.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef FXOS8700_H_ +#define FXOS8700_H_ + +extern const struct regmap_config fxos8700_regmap_config; + +int fxos8700_core_probe(struct device *dev, struct regmap *regmap, + const char *name, bool use_spi); + +#endif /* FXOS8700_H_ */ diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c new file mode 100644 index 000000000000..7b47be44ea59 --- /dev/null +++ b/drivers/iio/imu/fxos8700_core.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FXOS8700 - NXP IMU (accelerometer plus magnetometer) + * + * IIO core driver for FXOS8700, with support for I2C/SPI busses + * + * TODO: Buffer, trigger, and IRQ support + */ +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/acpi.h> +#include <linux/bitops.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include "fxos8700.h" + +/* Register Definitions */ +#define FXOS8700_STATUS 0x00 +#define FXOS8700_OUT_X_MSB 0x01 +#define FXOS8700_OUT_X_LSB 0x02 +#define FXOS8700_OUT_Y_MSB 0x03 +#define FXOS8700_OUT_Y_LSB 0x04 +#define FXOS8700_OUT_Z_MSB 0x05 +#define FXOS8700_OUT_Z_LSB 0x06 +#define FXOS8700_F_SETUP 0x09 +#define FXOS8700_TRIG_CFG 0x0a +#define FXOS8700_SYSMOD 0x0b +#define FXOS8700_INT_SOURCE 0x0c +#define FXOS8700_WHO_AM_I 0x0d +#define FXOS8700_XYZ_DATA_CFG 0x0e +#define FXOS8700_HP_FILTER_CUTOFF 0x0f +#define FXOS8700_PL_STATUS 0x10 +#define FXOS8700_PL_CFG 0x11 +#define FXOS8700_PL_COUNT 0x12 +#define FXOS8700_PL_BF_ZCOMP 0x13 +#define FXOS8700_PL_THS_REG 0x14 +#define FXOS8700_A_FFMT_CFG 0x15 +#define FXOS8700_A_FFMT_SRC 0x16 +#define FXOS8700_A_FFMT_THS 0x17 +#define FXOS8700_A_FFMT_COUNT 0x18 +#define FXOS8700_TRANSIENT_CFG 0x1d +#define FXOS8700_TRANSIENT_SRC 0x1e +#define FXOS8700_TRANSIENT_THS 0x1f +#define FXOS8700_TRANSIENT_COUNT 0x20 +#define FXOS8700_PULSE_CFG 0x21 +#define FXOS8700_PULSE_SRC 0x22 +#define FXOS8700_PULSE_THSX 0x23 +#define FXOS8700_PULSE_THSY 0x24 +#define FXOS8700_PULSE_THSZ 0x25 +#define FXOS8700_PULSE_TMLT 0x26 +#define FXOS8700_PULSE_LTCY 0x27 +#define FXOS8700_PULSE_WIND 0x28 +#define FXOS8700_ASLP_COUNT 0x29 +#define FXOS8700_CTRL_REG1 0x2a +#define FXOS8700_CTRL_REG2 0x2b +#define FXOS8700_CTRL_REG3 0x2c +#define FXOS8700_CTRL_REG4 0x2d +#define FXOS8700_CTRL_REG5 0x2e +#define FXOS8700_OFF_X 0x2f +#define FXOS8700_OFF_Y 0x30 +#define FXOS8700_OFF_Z 0x31 +#define FXOS8700_M_DR_STATUS 0x32 +#define FXOS8700_M_OUT_X_MSB 0x33 +#define FXOS8700_M_OUT_X_LSB 0x34 +#define FXOS8700_M_OUT_Y_MSB 0x35 +#define FXOS8700_M_OUT_Y_LSB 0x36 +#define FXOS8700_M_OUT_Z_MSB 0x37 +#define FXOS8700_M_OUT_Z_LSB 0x38 +#define FXOS8700_CMP_X_MSB 0x39 +#define FXOS8700_CMP_X_LSB 0x3a +#define FXOS8700_CMP_Y_MSB 0x3b +#define FXOS8700_CMP_Y_LSB 0x3c +#define FXOS8700_CMP_Z_MSB 0x3d +#define FXOS8700_CMP_Z_LSB 0x3e +#define FXOS8700_M_OFF_X_MSB 0x3f +#define FXOS8700_M_OFF_X_LSB 0x40 +#define FXOS8700_M_OFF_Y_MSB 0x41 +#define FXOS8700_M_OFF_Y_LSB 0x42 +#define FXOS8700_M_OFF_Z_MSB 0x43 +#define FXOS8700_M_OFF_Z_LSB 0x44 +#define FXOS8700_MAX_X_MSB 0x45 +#define FXOS8700_MAX_X_LSB 0x46 +#define FXOS8700_MAX_Y_MSB 0x47 +#define FXOS8700_MAX_Y_LSB 0x48 +#define FXOS8700_MAX_Z_MSB 0x49 +#define FXOS8700_MAX_Z_LSB 0x4a +#define FXOS8700_MIN_X_MSB 0x4b +#define FXOS8700_MIN_X_LSB 0x4c +#define FXOS8700_MIN_Y_MSB 0x4d +#define FXOS8700_MIN_Y_LSB 0x4e +#define FXOS8700_MIN_Z_MSB 0x4f +#define FXOS8700_MIN_Z_LSB 0x50 +#define FXOS8700_TEMP 0x51 +#define FXOS8700_M_THS_CFG 0x52 +#define FXOS8700_M_THS_SRC 0x53 +#define FXOS8700_M_THS_X_MSB 0x54 +#define FXOS8700_M_THS_X_LSB 0x55 +#define FXOS8700_M_THS_Y_MSB 0x56 +#define FXOS8700_M_THS_Y_LSB 0x57 +#define FXOS8700_M_THS_Z_MSB 0x58 +#define FXOS8700_M_THS_Z_LSB 0x59 +#define FXOS8700_M_THS_COUNT 0x5a +#define FXOS8700_M_CTRL_REG1 0x5b +#define FXOS8700_M_CTRL_REG2 0x5c +#define FXOS8700_M_CTRL_REG3 0x5d +#define FXOS8700_M_INT_SRC 0x5e +#define FXOS8700_A_VECM_CFG 0x5f +#define FXOS8700_A_VECM_THS_MSB 0x60 +#define FXOS8700_A_VECM_THS_LSB 0x61 +#define FXOS8700_A_VECM_CNT 0x62 +#define FXOS8700_A_VECM_INITX_MSB 0x63 +#define FXOS8700_A_VECM_INITX_LSB 0x64 +#define FXOS8700_A_VECM_INITY_MSB 0x65 +#define FXOS8700_A_VECM_INITY_LSB 0x66 +#define FXOS8700_A_VECM_INITZ_MSB 0x67 +#define FXOS8700_A_VECM_INITZ_LSB 0x68 +#define FXOS8700_M_VECM_CFG 0x69 +#define FXOS8700_M_VECM_THS_MSB 0x6a +#define FXOS8700_M_VECM_THS_LSB 0x6b +#define FXOS8700_M_VECM_CNT 0x6c +#define FXOS8700_M_VECM_INITX_MSB 0x6d +#define FXOS8700_M_VECM_INITX_LSB 0x6e +#define FXOS8700_M_VECM_INITY_MSB 0x6f +#define FXOS8700_M_VECM_INITY_LSB 0x70 +#define FXOS8700_M_VECM_INITZ_MSB 0x71 +#define FXOS8700_M_VECM_INITZ_LSB 0x72 +#define FXOS8700_A_FFMT_THS_X_MSB 0x73 +#define FXOS8700_A_FFMT_THS_X_LSB 0x74 +#define FXOS8700_A_FFMT_THS_Y_MSB 0x75 +#define FXOS8700_A_FFMT_THS_Y_LSB 0x76 +#define FXOS8700_A_FFMT_THS_Z_MSB 0x77 +#define FXOS8700_A_FFMT_THS_Z_LSB 0x78 +#define FXOS8700_A_TRAN_INIT_MSB 0x79 +#define FXOS8700_A_TRAN_INIT_LSB_X 0x7a +#define FXOS8700_A_TRAN_INIT_LSB_Y 0x7b +#define FXOS8700_A_TRAN_INIT_LSB_Z 0x7d +#define FXOS8700_TM_NVM_LOCK 0x7e +#define FXOS8700_NVM_DATA0_35 0x80 +#define FXOS8700_NVM_DATA_BNK3 0xa4 +#define FXOS8700_NVM_DATA_BNK2 0xa5 +#define FXOS8700_NVM_DATA_BNK1 0xa6 +#define FXOS8700_NVM_DATA_BNK0 0xa7 + +/* Bit definitions for FXOS8700_CTRL_REG1 */ +#define FXOS8700_CTRL_ODR_MSK 0x38 +#define FXOS8700_CTRL_ODR_MAX 0x00 +#define FXOS8700_CTRL_ODR_MIN GENMASK(4, 3) + +/* Bit definitions for FXOS8700_M_CTRL_REG1 */ +#define FXOS8700_HMS_MASK GENMASK(1, 0) +#define FXOS8700_OS_MASK GENMASK(4, 2) + +/* Bit definitions for FXOS8700_M_CTRL_REG2 */ +#define FXOS8700_MAXMIN_RST BIT(2) +#define FXOS8700_MAXMIN_DIS_THS BIT(3) +#define FXOS8700_MAXMIN_DIS BIT(4) + +#define FXOS8700_ACTIVE 0x01 +#define FXOS8700_ACTIVE_MIN_USLEEP 4000 /* from table 6 in datasheet */ + +#define FXOS8700_DEVICE_ID 0xC7 +#define FXOS8700_PRE_DEVICE_ID 0xC4 +#define FXOS8700_DATA_BUF_SIZE 3 + +struct fxos8700_data { + struct regmap *regmap; + struct iio_trigger *trig; + __be16 buf[FXOS8700_DATA_BUF_SIZE] ____cacheline_aligned; +}; + +/* Regmap info */ +static const struct regmap_range read_range[] = { + { + .range_min = FXOS8700_STATUS, + .range_max = FXOS8700_A_FFMT_COUNT, + }, { + .range_min = FXOS8700_TRANSIENT_CFG, + .range_max = FXOS8700_A_FFMT_THS_Z_LSB, + }, +}; + +static const struct regmap_range write_range[] = { + { + .range_min = FXOS8700_F_SETUP, + .range_max = FXOS8700_TRIG_CFG, + }, { + .range_min = FXOS8700_XYZ_DATA_CFG, + .range_max = FXOS8700_HP_FILTER_CUTOFF, + }, { + .range_min = FXOS8700_PL_CFG, + .range_max = FXOS8700_A_FFMT_CFG, + }, { + .range_min = FXOS8700_A_FFMT_THS, + .range_max = FXOS8700_TRANSIENT_CFG, + }, { + .range_min = FXOS8700_TRANSIENT_THS, + .range_max = FXOS8700_PULSE_CFG, + }, { + .range_min = FXOS8700_PULSE_THSX, + .range_max = FXOS8700_OFF_Z, + }, { + .range_min = FXOS8700_M_OFF_X_MSB, + .range_max = FXOS8700_M_OFF_Z_LSB, + }, { + .range_min = FXOS8700_M_THS_CFG, + .range_max = FXOS8700_M_THS_CFG, + }, { + .range_min = FXOS8700_M_THS_X_MSB, + .range_max = FXOS8700_M_CTRL_REG3, + }, { + .range_min = FXOS8700_A_VECM_CFG, + .range_max = FXOS8700_A_FFMT_THS_Z_LSB, + }, +}; + +static const struct regmap_access_table driver_read_table = { + .yes_ranges = read_range, + .n_yes_ranges = ARRAY_SIZE(read_range), +}; + +static const struct regmap_access_table driver_write_table = { + .yes_ranges = write_range, + .n_yes_ranges = ARRAY_SIZE(write_range), +}; + +const struct regmap_config fxos8700_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = FXOS8700_NVM_DATA_BNK0, + .rd_table = &driver_read_table, + .wr_table = &driver_write_table, +}; +EXPORT_SYMBOL(fxos8700_regmap_config); + +#define FXOS8700_CHANNEL(_type, _axis) { \ + .type = _type, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +enum fxos8700_accel_scale_bits { + MODE_2G = 0, + MODE_4G, + MODE_8G, +}; + +/* scan indexes follow DATA register order */ +enum fxos8700_scan_axis { + FXOS8700_SCAN_ACCEL_X = 0, + FXOS8700_SCAN_ACCEL_Y, + FXOS8700_SCAN_ACCEL_Z, + FXOS8700_SCAN_MAGN_X, + FXOS8700_SCAN_MAGN_Y, + FXOS8700_SCAN_MAGN_Z, + FXOS8700_SCAN_RHALL, + FXOS8700_SCAN_TIMESTAMP, +}; + +enum fxos8700_sensor { + FXOS8700_ACCEL = 0, + FXOS8700_MAGN, + FXOS8700_NUM_SENSORS /* must be last */ +}; + +enum fxos8700_int_pin { + FXOS8700_PIN_INT1, + FXOS8700_PIN_INT2 +}; + +struct fxos8700_scale { + u8 bits; + int uscale; +}; + +struct fxos8700_odr { + u8 bits; + int odr; + int uodr; +}; + +static const struct fxos8700_scale fxos8700_accel_scale[] = { + { MODE_2G, 244}, + { MODE_4G, 488}, + { MODE_8G, 976}, +}; + +/* + * Accellerometer and magnetometer have the same ODR options, set in the + * CTRL_REG1 register. ODR is halved when using both sensors at once in + * hybrid mode. + */ +static const struct fxos8700_odr fxos8700_odr[] = { + {0x00, 800, 0}, + {0x01, 400, 0}, + {0x02, 200, 0}, + {0x03, 100, 0}, + {0x04, 50, 0}, + {0x05, 12, 500000}, + {0x06, 6, 250000}, + {0x07, 1, 562500}, +}; + +static const struct iio_chan_spec fxos8700_channels[] = { + FXOS8700_CHANNEL(IIO_ACCEL, X), + FXOS8700_CHANNEL(IIO_ACCEL, Y), + FXOS8700_CHANNEL(IIO_ACCEL, Z), + FXOS8700_CHANNEL(IIO_MAGN, X), + FXOS8700_CHANNEL(IIO_MAGN, Y), + FXOS8700_CHANNEL(IIO_MAGN, Z), + IIO_CHAN_SOFT_TIMESTAMP(FXOS8700_SCAN_TIMESTAMP), +}; + +static enum fxos8700_sensor fxos8700_to_sensor(enum iio_chan_type iio_type) +{ + switch (iio_type) { + case IIO_ACCEL: + return FXOS8700_ACCEL; + case IIO_ANGL_VEL: + return FXOS8700_MAGN; + default: + return -EINVAL; + } +} + +static int fxos8700_set_active_mode(struct fxos8700_data *data, + enum fxos8700_sensor t, bool mode) +{ + int ret; + + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, mode); + if (ret) + return ret; + + usleep_range(FXOS8700_ACTIVE_MIN_USLEEP, + FXOS8700_ACTIVE_MIN_USLEEP + 1000); + + return 0; +} + +static int fxos8700_set_scale(struct fxos8700_data *data, + enum fxos8700_sensor t, int uscale) +{ + int i; + static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale); + struct device *dev = regmap_get_device(data->regmap); + + if (t == FXOS8700_MAGN) { + dev_err(dev, "Magnetometer scale is locked at 1200uT\n"); + return -EINVAL; + } + + for (i = 0; i < scale_num; i++) + if (fxos8700_accel_scale[i].uscale == uscale) + break; + + if (i == scale_num) + return -EINVAL; + + return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, + fxos8700_accel_scale[i].bits); +} + +static int fxos8700_get_scale(struct fxos8700_data *data, + enum fxos8700_sensor t, int *uscale) +{ + int i, ret, val; + static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale); + + if (t == FXOS8700_MAGN) { + *uscale = 1200; /* Magnetometer is locked at 1200uT */ + return 0; + } + + ret = regmap_read(data->regmap, FXOS8700_XYZ_DATA_CFG, &val); + if (ret) + return ret; + + for (i = 0; i < scale_num; i++) { + if (fxos8700_accel_scale[i].bits == (val & 0x3)) { + *uscale = fxos8700_accel_scale[i].uscale; + return 0; + } + } + + return -EINVAL; +} + +static int fxos8700_get_data(struct fxos8700_data *data, int chan_type, + int axis, int *val) +{ + u8 base, reg; + int ret; + enum fxos8700_sensor type = fxos8700_to_sensor(chan_type); + + base = type ? FXOS8700_OUT_X_MSB : FXOS8700_M_OUT_X_MSB; + + /* Block read 6 bytes of device output registers to avoid data loss */ + ret = regmap_bulk_read(data->regmap, base, data->buf, + FXOS8700_DATA_BUF_SIZE); + if (ret) + return ret; + + /* Convert axis to buffer index */ + reg = axis - IIO_MOD_X; + + /* Convert to native endianness */ + *val = sign_extend32(be16_to_cpu(data->buf[reg]), 15); + + return 0; +} + +static int fxos8700_set_odr(struct fxos8700_data *data, enum fxos8700_sensor t, + int odr, int uodr) +{ + int i, ret, val; + bool active_mode; + static const int odr_num = ARRAY_SIZE(fxos8700_odr); + + ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val); + if (ret) + return ret; + + active_mode = val & FXOS8700_ACTIVE; + + if (active_mode) { + /* + * The device must be in standby mode to change any of the + * other fields within CTRL_REG1 + */ + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, + val & ~FXOS8700_ACTIVE); + if (ret) + return ret; + } + + for (i = 0; i < odr_num; i++) + if (fxos8700_odr[i].odr == odr && fxos8700_odr[i].uodr == uodr) + break; + + if (i >= odr_num) + return -EINVAL; + + return regmap_update_bits(data->regmap, + FXOS8700_CTRL_REG1, + FXOS8700_CTRL_ODR_MSK + FXOS8700_ACTIVE, + fxos8700_odr[i].bits << 3 | active_mode); +} + +static int fxos8700_get_odr(struct fxos8700_data *data, enum fxos8700_sensor t, + int *odr, int *uodr) +{ + int i, val, ret; + static const int odr_num = ARRAY_SIZE(fxos8700_odr); + + ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val); + if (ret) + return ret; + + val &= FXOS8700_CTRL_ODR_MSK; + + for (i = 0; i < odr_num; i++) + if (val == fxos8700_odr[i].bits) + break; + + if (i >= odr_num) + return -EINVAL; + + *odr = fxos8700_odr[i].odr; + *uodr = fxos8700_odr[i].uodr; + + return 0; +} + +static int fxos8700_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct fxos8700_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = fxos8700_get_data(data, chan->type, chan->channel2, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + ret = fxos8700_get_scale(data, fxos8700_to_sensor(chan->type), + val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = fxos8700_get_odr(data, fxos8700_to_sensor(chan->type), + val, val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int fxos8700_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct fxos8700_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return fxos8700_set_scale(data, fxos8700_to_sensor(chan->type), + val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return fxos8700_set_odr(data, fxos8700_to_sensor(chan->type), + val, val2); + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR(in_accel_sampling_frequency_available, + "1.5625 6.25 12.5 50 100 200 400 800"); +static IIO_CONST_ATTR(in_magn_sampling_frequency_available, + "1.5625 6.25 12.5 50 100 200 400 800"); +static IIO_CONST_ATTR(in_accel_scale_available, "0.000244 0.000488 0.000976"); +static IIO_CONST_ATTR(in_magn_scale_available, "0.000001200"); + +static struct attribute *fxos8700_attrs[] = { + &iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_magn_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + &iio_const_attr_in_magn_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group fxos8700_attrs_group = { + .attrs = fxos8700_attrs, +}; + +static const struct iio_info fxos8700_info = { + .read_raw = fxos8700_read_raw, + .write_raw = fxos8700_write_raw, + .attrs = &fxos8700_attrs_group, +}; + +static int fxos8700_chip_init(struct fxos8700_data *data, bool use_spi) +{ + int ret; + unsigned int val; + struct device *dev = regmap_get_device(data->regmap); + + ret = regmap_read(data->regmap, FXOS8700_WHO_AM_I, &val); + if (ret) { + dev_err(dev, "Error reading chip id\n"); + return ret; + } + if (val != FXOS8700_DEVICE_ID && val != FXOS8700_PRE_DEVICE_ID) { + dev_err(dev, "Wrong chip id, got %x expected %x or %x\n", + val, FXOS8700_DEVICE_ID, FXOS8700_PRE_DEVICE_ID); + return -ENODEV; + } + + ret = fxos8700_set_active_mode(data, FXOS8700_ACCEL, true); + if (ret) + return ret; + + ret = fxos8700_set_active_mode(data, FXOS8700_MAGN, true); + if (ret) + return ret; + + /* + * The device must be in standby mode to change any of the other fields + * within CTRL_REG1 + */ + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, 0x00); + if (ret) + return ret; + + /* Set max oversample ratio (OSR) and both devices active */ + ret = regmap_write(data->regmap, FXOS8700_M_CTRL_REG1, + FXOS8700_HMS_MASK | FXOS8700_OS_MASK); + if (ret) + return ret; + + /* Disable and rst min/max measurements & threshold */ + ret = regmap_write(data->regmap, FXOS8700_M_CTRL_REG2, + FXOS8700_MAXMIN_RST | FXOS8700_MAXMIN_DIS_THS | + FXOS8700_MAXMIN_DIS); + if (ret) + return ret; + + /* Max ODR (800Hz individual or 400Hz hybrid), active mode */ + ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, + FXOS8700_CTRL_ODR_MAX | FXOS8700_ACTIVE); + if (ret) + return ret; + + /* Set for max full-scale range (+/-8G) */ + return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, MODE_8G); +} + +static void fxos8700_chip_uninit(void *data) +{ + struct fxos8700_data *fxos8700_data = data; + + fxos8700_set_active_mode(fxos8700_data, FXOS8700_ACCEL, false); + fxos8700_set_active_mode(fxos8700_data, FXOS8700_MAGN, false); +} + +int fxos8700_core_probe(struct device *dev, struct regmap *regmap, + const char *name, bool use_spi) +{ + struct iio_dev *indio_dev; + struct fxos8700_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + data->regmap = regmap; + + ret = fxos8700_chip_init(data, use_spi); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, fxos8700_chip_uninit, data); + if (ret) + return ret; + + indio_dev->dev.parent = dev; + indio_dev->channels = fxos8700_channels; + indio_dev->num_channels = ARRAY_SIZE(fxos8700_channels); + indio_dev->name = name ? name : "fxos8700"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &fxos8700_info; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(fxos8700_core_probe); + +MODULE_AUTHOR("Robert Jones <rjones@gateworks.com>"); +MODULE_DESCRIPTION("FXOS8700 6-Axis Acc and Mag Combo Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/fxos8700_i2c.c b/drivers/iio/imu/fxos8700_i2c.c new file mode 100644 index 000000000000..3ceb76366313 --- /dev/null +++ b/drivers/iio/imu/fxos8700_i2c.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FXOS8700 - NXP IMU, I2C bits + * + * 7-bit I2C slave address determined by SA1 and SA0 logic level + * inputs represented in the following table: + * SA1 | SA0 | Slave Address + * 0 | 0 | 0x1E + * 0 | 1 | 0x1D + * 1 | 0 | 0x1C + * 1 | 1 | 0x1F + */ +#include <linux/acpi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> + +#include "fxos8700.h" + +static int fxos8700_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + const char *name = NULL; + + regmap = devm_regmap_init_i2c(client, &fxos8700_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + if (id) + name = id->name; + + return fxos8700_core_probe(&client->dev, regmap, name, false); +} + +static const struct i2c_device_id fxos8700_i2c_id[] = { + {"fxos8700", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, fxos8700_i2c_id); + +static const struct acpi_device_id fxos8700_acpi_match[] = { + {"FXOS8700", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, fxos8700_acpi_match); + +static const struct of_device_id fxos8700_of_match[] = { + { .compatible = "nxp,fxos8700" }, + { } +}; +MODULE_DEVICE_TABLE(of, fxos8700_of_match); + +static struct i2c_driver fxos8700_i2c_driver = { + .driver = { + .name = "fxos8700_i2c", + .acpi_match_table = ACPI_PTR(fxos8700_acpi_match), + .of_match_table = fxos8700_of_match, + }, + .probe = fxos8700_i2c_probe, + .id_table = fxos8700_i2c_id, +}; +module_i2c_driver(fxos8700_i2c_driver); + +MODULE_AUTHOR("Robert Jones <rjones@gateworks.com>"); +MODULE_DESCRIPTION("FXOS8700 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c new file mode 100644 index 000000000000..57e7bb6444e7 --- /dev/null +++ b/drivers/iio/imu/fxos8700_spi.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FXOS8700 - NXP IMU, SPI bits + */ +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "fxos8700.h" + +static int fxos8700_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + const struct spi_device_id *id = spi_get_device_id(spi); + + regmap = devm_regmap_init_spi(spi, &fxos8700_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to register spi regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return fxos8700_core_probe(&spi->dev, regmap, id->name, true); +} + +static const struct spi_device_id fxos8700_spi_id[] = { + {"fxos8700", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, fxos8700_spi_id); + +static const struct acpi_device_id fxos8700_acpi_match[] = { + {"FXOS8700", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, fxos8700_acpi_match); + +static const struct of_device_id fxos8700_of_match[] = { + { .compatible = "nxp,fxos8700" }, + { } +}; +MODULE_DEVICE_TABLE(of, fxos8700_of_match); + +static struct spi_driver fxos8700_spi_driver = { + .probe = fxos8700_spi_probe, + .id_table = fxos8700_spi_id, + .driver = { + .acpi_match_table = ACPI_PTR(fxos8700_acpi_match), + .of_match_table = fxos8700_of_match, + .name = "fxos8700_spi", + }, +}; +module_spi_driver(fxos8700_spi_driver); + +MODULE_AUTHOR("Robert Jones <rjones@gateworks.com>"); +MODULE_DESCRIPTION("FXOS8700 SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index 77aa0e77212d..28f59d09208a 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -12,7 +12,8 @@ config IIO_ST_LSM6DSX Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr, lsm6ds3tr-c, - ism330dhcx and the accelerometer/gyroscope of lsm9ds1. + ism330dhcx, lsm6dsrx, lsm6ds0 and the accelerometer/gyroscope + of lsm9ds1. To compile this driver as a module, choose M here: the module will be called st_lsm6dsx. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index e5e782f756e4..37e499fe6bcf 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -26,6 +26,8 @@ #define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c" #define ST_ISM330DHCX_DEV_NAME "ism330dhcx" #define ST_LSM9DS1_DEV_NAME "lsm9ds1-imu" +#define ST_LSM6DS0_DEV_NAME "lsm6ds0" +#define ST_LSM6DSRX_DEV_NAME "lsm6dsrx" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, @@ -40,6 +42,8 @@ enum st_lsm6dsx_hw_id { ST_LSM6DS3TRC_ID, ST_ISM330DHCX_ID, ST_LSM9DS1_ID, + ST_LSM6DS0_ID, + ST_LSM6DSRX_ID, ST_LSM6DSX_MAX_ID, }; @@ -153,12 +157,14 @@ struct st_lsm6dsx_fifo_ops { * @hr_timer: Hw timer resolution register info (addr + mask). * @fifo_en: Hw timer FIFO enable register info (addr + mask). * @decimator: Hw timer FIFO decimator register info (addr + mask). + * @freq_fine: Difference in % of ODR with respect to the typical. */ struct st_lsm6dsx_hw_ts_settings { struct st_lsm6dsx_reg timer_en; struct st_lsm6dsx_reg hr_timer; struct st_lsm6dsx_reg fifo_en; struct st_lsm6dsx_reg decimator; + u8 freq_fine; }; /** @@ -238,30 +244,27 @@ struct st_lsm6dsx_ext_dev_settings { /** * struct st_lsm6dsx_settings - ST IMU sensor settings * @wai: Sensor WhoAmI default value. - * @int1_addr: Control Register address for INT1 - * @int2_addr: Control Register address for INT2 - * @reset_addr: register address for reset/reboot + * @reset: register address for reset. + * @boot: register address for boot. + * @bdu: register address for Block Data Update. * @max_fifo_size: Sensor max fifo length in FIFO words. * @id: List of hw id/device name supported by the driver configuration. * @channels: IIO channels supported by the device. + * @irq_config: interrupts related registers. + * @drdy_mask: register info for data-ready mask (addr + mask). * @odr_table: Hw sensors odr table (Hz + val). * @fs_table: Hw sensors gain table (gain + val). * @decimator: List of decimator register info (addr + mask). * @batch: List of FIFO batching register info (addr + mask). - * @lir: Latched interrupt register info (addr + mask). - * @clear_on_read: Clear on read register info (addr + mask). * @fifo_ops: Sensor hw FIFO parameters. * @ts_settings: Hw timer related settings. * @shub_settings: i2c controller related settings. */ struct st_lsm6dsx_settings { u8 wai; - u8 int1_addr; - u8 int2_addr; - u8 int1_func_addr; - u8 int2_func_addr; - u8 int_func_mask; - u8 reset_addr; + struct st_lsm6dsx_reg reset; + struct st_lsm6dsx_reg boot; + struct st_lsm6dsx_reg bdu; u16 max_fifo_size; struct { enum st_lsm6dsx_hw_id hw_id; @@ -271,12 +274,21 @@ struct st_lsm6dsx_settings { const struct iio_chan_spec *chan; int len; } channels[2]; + struct { + struct st_lsm6dsx_reg irq1; + struct st_lsm6dsx_reg irq2; + struct st_lsm6dsx_reg irq1_func; + struct st_lsm6dsx_reg irq2_func; + struct st_lsm6dsx_reg lir; + struct st_lsm6dsx_reg clear_on_read; + struct st_lsm6dsx_reg hla; + struct st_lsm6dsx_reg od; + } irq_config; + struct st_lsm6dsx_reg drdy_mask; struct st_lsm6dsx_odr_table_entry odr_table[2]; struct st_lsm6dsx_fs_table_entry fs_table[2]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID]; - struct st_lsm6dsx_reg lir; - struct st_lsm6dsx_reg clear_on_read; struct st_lsm6dsx_fifo_ops fifo_ops; struct st_lsm6dsx_hw_ts_settings ts_settings; struct st_lsm6dsx_shub_settings shub_settings; @@ -340,9 +352,13 @@ struct st_lsm6dsx_sensor { * @fifo_mode: FIFO operating mode supported by the device. * @suspend_mask: Suspended sensor bitmask. * @enable_mask: Enabled sensor bitmask. + * @ts_gain: Hw timestamp rate after internal calibration. * @ts_sip: Total number of timestamp samples in a given pattern. * @sip: Total number of samples (acc/gyro/ts) in a given pattern. * @buff: Device read buffer. + * @irq_routing: pointer to interrupt routing configuration. + * @event_threshold: wakeup event threshold. + * @enable_event: enabled event bitmask. * @iio_devs: Pointers to acc/gyro iio_dev instances. * @settings: Pointer to the specific sensor settings in use. */ @@ -358,12 +374,13 @@ struct st_lsm6dsx_hw { enum st_lsm6dsx_fifo_mode fifo_mode; u8 suspend_mask; u8 enable_mask; + s64 ts_gain; u8 ts_sip; u8 sip; + const struct st_lsm6dsx_reg *irq_routing; u8 event_threshold; u8 enable_event; - struct st_lsm6dsx_reg irq_routing; u8 *buff; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index ef579650fd52..31cd90d2c60e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -14,10 +14,10 @@ * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the * value of the decimation factor and ODR set for each FIFO data set. * - * LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX: The FIFO buffer can be - * configured to store data from gyroscope and accelerometer. Each sample - * is queued with a tag (1B) indicating data source (gyroscope, accelerometer, - * hw timer). + * LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/LSM6DSRX/ISM330DHCX: + * The FIFO buffer can be configured to store data from gyroscope and + * accelerometer. Each sample is queued with a tag (1B) indicating data + * source (gyroscope, accelerometer, hw timer). * * FIFO supported modes: * - BYPASS: FIFO disabled @@ -50,7 +50,6 @@ #define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 -#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */ #define ST_LSM6DSX_TS_RESET_VAL 0xaa struct st_lsm6dsx_decimator_entry { @@ -423,7 +422,7 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) */ if (!reset_ts && ts >= 0xff0000) reset_ts = true; - ts *= ST_LSM6DSX_TS_SENSITIVITY; + ts *= hw->ts_gain; offset += ST_LSM6DSX_SAMPLE_SIZE; } @@ -450,13 +449,19 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) return read_len; } +#define ST_LSM6DSX_INVALID_SAMPLE 0x7ffd static int st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, u8 *data, s64 ts) { + s16 val = le16_to_cpu(*(__le16 *)data); struct st_lsm6dsx_sensor *sensor; struct iio_dev *iio_dev; + /* invalid sample during bootstrap phase */ + if (val >= ST_LSM6DSX_INVALID_SAMPLE) + return -EINVAL; + /* * EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG * corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG @@ -566,7 +571,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) */ if (!reset_ts && ts >= 0xffff0000) reset_ts = true; - ts *= ST_LSM6DSX_TS_SENSITIVITY; + ts *= hw->ts_gain; } else { st_lsm6dsx_push_tagged_data(hw, tag, iio_buff, ts); @@ -586,6 +591,9 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) { int err; + if (!hw->settings->fifo_ops.read_fifo) + return -ENOTSUPP; + mutex_lock(&hw->fifo_lock); hw->settings->fifo_ops.read_fifo(hw); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index de0049c75eeb..1f28a7733fc0 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -32,7 +32,7 @@ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 3KB * - * - LSM9DS1: + * - LSM9DS1/LSM6DS0: * - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952 @@ -61,17 +61,9 @@ #include "st_lsm6dsx.h" -#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3) #define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f -#define ST_LSM6DSX_REG_RESET_MASK BIT(0) -#define ST_LSM6DSX_REG_BOOT_MASK BIT(7) -#define ST_LSM6DSX_REG_BDU_ADDR 0x12 -#define ST_LSM6DSX_REG_BDU_MASK BIT(6) -#define ST_LSM6DSX_REG_HLACTIVE_ADDR 0x12 -#define ST_LSM6DSX_REG_HLACTIVE_MASK BIT(5) -#define ST_LSM6DSX_REG_PP_OD_ADDR 0x12 -#define ST_LSM6DSX_REG_PP_OD_MASK BIT(4) +#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */ static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = { ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0), @@ -97,14 +89,26 @@ static const struct iio_chan_spec st_lsm6ds0_gyro_channels[] = { static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { { .wai = 0x68, - .int1_addr = 0x0c, - .int2_addr = 0x0d, - .reset_addr = 0x22, + .reset = { + .addr = 0x22, + .mask = BIT(0), + }, + .boot = { + .addr = 0x22, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x22, + .mask = BIT(6), + }, .max_fifo_size = 32, .id = { { .hw_id = ST_LSM9DS1_ID, .name = ST_LSM9DS1_DEV_NAME, + }, { + .hw_id = ST_LSM6DS0_ID, + .name = ST_LSM6DS0_DEV_NAME, }, }, .channels = { @@ -160,21 +164,46 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x10, .mask = GENMASK(4, 3), }, - .fs_avl[0] = { IIO_DEGREE_TO_RAD(245), 0x0 }, - .fs_avl[1] = { IIO_DEGREE_TO_RAD(500), 0x1 }, - .fs_avl[2] = { IIO_DEGREE_TO_RAD(2000), 0x3 }, + + .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 }, + .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 }, + .fs_avl[2] = { IIO_DEGREE_TO_RAD(70000), 0x3 }, .fs_len = 3, }, }, + .irq_config = { + .irq1 = { + .addr = 0x0c, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .hla = { + .addr = 0x22, + .mask = BIT(5), + }, + .od = { + .addr = 0x22, + .mask = BIT(4), + }, + }, }, { .wai = 0x69, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .int1_func_addr = 0x5e, - .int2_func_addr = 0x5f, - .int_func_mask = BIT(5), - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 1365, .id = { { @@ -242,6 +271,36 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, .decimator = { [ST_LSM6DSX_ID_ACC] = { .addr = 0x08, @@ -252,10 +311,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(5, 3), }, }, - .lir = { - .addr = 0x58, - .mask = BIT(0), - }, .fifo_ops = { .update_fifo = st_lsm6dsx_update_fifo, .read_fifo = st_lsm6dsx_read_fifo, @@ -301,12 +356,18 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, { .wai = 0x69, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .int1_func_addr = 0x5e, - .int2_func_addr = 0x5f, - .int_func_mask = BIT(5), - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 682, .id = { { @@ -374,6 +435,36 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, .decimator = { [ST_LSM6DSX_ID_ACC] = { .addr = 0x08, @@ -384,10 +475,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(5, 3), }, }, - .lir = { - .addr = 0x58, - .mask = BIT(0), - }, .fifo_ops = { .update_fifo = st_lsm6dsx_update_fifo, .read_fifo = st_lsm6dsx_read_fifo, @@ -433,12 +520,18 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, { .wai = 0x6a, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .int1_func_addr = 0x5e, - .int2_func_addr = 0x5f, - .int_func_mask = BIT(5), - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 682, .id = { { @@ -515,6 +608,36 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, .decimator = { [ST_LSM6DSX_ID_ACC] = { .addr = 0x08, @@ -525,10 +648,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(5, 3), }, }, - .lir = { - .addr = 0x58, - .mask = BIT(0), - }, .fifo_ops = { .update_fifo = st_lsm6dsx_update_fifo, .read_fifo = st_lsm6dsx_read_fifo, @@ -578,9 +697,18 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, { .wai = 0x6c, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 512, .id = { { @@ -601,6 +729,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), }, }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, .odr_table = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -651,6 +783,40 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, .batch = { [ST_LSM6DSX_ID_ACC] = { .addr = 0x09, @@ -661,14 +827,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(7, 4), }, }, - .lir = { - .addr = 0x56, - .mask = BIT(0), - }, - .clear_on_read = { - .addr = 0x56, - .mask = BIT(6), - }, .fifo_ops = { .update_fifo = st_lsm6dsx_update_fifo, .read_fifo = st_lsm6dsx_read_tagged_fifo, @@ -691,6 +849,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x0a, .mask = GENMASK(7, 6), }, + .freq_fine = 0x63, }, .shub_settings = { .page_mux = { @@ -717,16 +876,37 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .slv0_addr = 0x15, .dw_slv0_addr = 0x21, .batch_en = BIT(3), - } + }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5b, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, }, { .wai = 0x6b, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .int1_func_addr = 0x5e, - .int2_func_addr = 0x5f, - .int_func_mask = BIT(5), - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 512, .id = { { @@ -744,6 +924,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), }, }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, .odr_table = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -794,6 +978,40 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, .batch = { [ST_LSM6DSX_ID_ACC] = { .addr = 0x09, @@ -804,14 +1022,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(7, 4), }, }, - .lir = { - .addr = 0x56, - .mask = BIT(0), - }, - .clear_on_read = { - .addr = 0x56, - .mask = BIT(6), - }, .fifo_ops = { .update_fifo = st_lsm6dsx_update_fifo, .read_fifo = st_lsm6dsx_read_tagged_fifo, @@ -834,6 +1044,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x0a, .mask = GENMASK(7, 6), }, + .freq_fine = 0x63, }, .event_settings = { .enable_reg = { @@ -853,12 +1064,18 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, { .wai = 0x6b, - .int1_addr = 0x0d, - .int2_addr = 0x0e, - .int1_func_addr = 0x5e, - .int2_func_addr = 0x5f, - .int_func_mask = BIT(5), - .reset_addr = 0x12, + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, .max_fifo_size = 512, .id = { { @@ -867,6 +1084,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, { .hw_id = ST_ISM330DHCX_ID, .name = ST_ISM330DHCX_DEV_NAME, + }, { + .hw_id = ST_LSM6DSRX_ID, + .name = ST_LSM6DSRX_DEV_NAME, }, }, .channels = { @@ -879,6 +1099,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), }, }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, .odr_table = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -929,6 +1153,40 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fs_len = 4, }, }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, .batch = { [ST_LSM6DSX_ID_ACC] = { .addr = 0x09, @@ -939,14 +1197,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(7, 4), }, }, - .lir = { - .addr = 0x56, - .mask = BIT(0), - }, - .clear_on_read = { - .addr = 0x56, - .mask = BIT(6), - }, .fifo_ops = { .update_fifo = st_lsm6dsx_update_fifo, .read_fifo = st_lsm6dsx_read_tagged_fifo, @@ -969,6 +1219,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x0a, .mask = GENMASK(7, 6), }, + .freq_fine = 0x63, }, .shub_settings = { .page_mux = { @@ -1293,27 +1544,26 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, int state) { + const struct st_lsm6dsx_reg *reg; + unsigned int data; int err; - u8 enable = 0; - if (!hw->settings->int1_func_addr) + if (!hw->settings->irq_config.irq1_func.addr) return -ENOTSUPP; - enable = state ? hw->settings->event_settings.enable_reg.mask : 0; - - err = regmap_update_bits(hw->regmap, - hw->settings->event_settings.enable_reg.addr, - hw->settings->event_settings.enable_reg.mask, - enable); - if (err < 0) - return err; - - enable = state ? hw->irq_routing.mask : 0; + reg = &hw->settings->event_settings.enable_reg; + if (reg->addr) { + data = ST_LSM6DSX_SHIFT_VAL(state, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, + reg->mask, data); + if (err < 0) + return err; + } /* Enable wakeup interrupt */ - return regmap_update_bits(hw->regmap, hw->irq_routing.addr, - hw->irq_routing.mask, - enable); + data = ST_LSM6DSX_SHIFT_VAL(state, hw->irq_routing->mask); + return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing->addr, + hw->irq_routing->mask, data); } static int st_lsm6dsx_read_event(struct iio_dev *iio_dev, @@ -1335,15 +1585,18 @@ static int st_lsm6dsx_read_event(struct iio_dev *iio_dev, return IIO_VAL_INT; } -static int st_lsm6dsx_write_event(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) +static int +st_lsm6dsx_write_event(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *reg; + unsigned int data; int err; if (type != IIO_EV_TYPE_THRESH) @@ -1352,11 +1605,11 @@ static int st_lsm6dsx_write_event(struct iio_dev *iio_dev, if (val < 0 || val > 31) return -EINVAL; - err = regmap_update_bits(hw->regmap, - hw->settings->event_settings.wakeup_reg.addr, - hw->settings->event_settings.wakeup_reg.mask, - val); - if (err) + reg = &hw->settings->event_settings.wakeup_reg; + data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, + reg->mask, data); + if (err < 0) return -EINVAL; hw->event_threshold = val; @@ -1364,10 +1617,11 @@ static int st_lsm6dsx_write_event(struct iio_dev *iio_dev, return 0; } -static int st_lsm6dsx_read_event_config(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir) +static int +st_lsm6dsx_read_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); struct st_lsm6dsx_hw *hw = sensor->hw; @@ -1378,11 +1632,11 @@ static int st_lsm6dsx_read_event_config(struct iio_dev *iio_dev, return !!(hw->enable_event & BIT(chan->channel2)); } -static int st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - int state) +static int +st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); struct st_lsm6dsx_hw *hw = sensor->hw; @@ -1414,7 +1668,9 @@ static int st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, if (err < 0) return err; + mutex_lock(&hw->conf_lock); err = st_lsm6dsx_sensor_set_enable(sensor, state); + mutex_unlock(&hw->conf_lock); if (err < 0) return err; @@ -1537,7 +1793,9 @@ static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin) return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin); } -static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg) +static int +st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, + const struct st_lsm6dsx_reg **drdy_reg) { int err = 0, drdy_pin; @@ -1551,14 +1809,12 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg) switch (drdy_pin) { case 1: - *drdy_reg = hw->settings->int1_addr; - hw->irq_routing.addr = hw->settings->int1_func_addr; - hw->irq_routing.mask = hw->settings->int_func_mask; + hw->irq_routing = &hw->settings->irq_config.irq1_func; + *drdy_reg = &hw->settings->irq_config.irq1; break; case 2: - *drdy_reg = hw->settings->int2_addr; - hw->irq_routing.addr = hw->settings->int2_func_addr; - hw->irq_routing.mask = hw->settings->int_func_mask; + hw->irq_routing = &hw->settings->irq_config.irq2_func; + *drdy_reg = &hw->settings->irq_config.irq2; break; default: dev_err(hw->dev, "unsupported data ready pin\n"); @@ -1649,74 +1905,95 @@ static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw) if (err < 0) return err; } + + /* calibrate timestamp sensitivity */ + hw->ts_gain = ST_LSM6DSX_TS_SENSITIVITY; + if (ts_settings->freq_fine) { + err = regmap_read(hw->regmap, ts_settings->freq_fine, &val); + if (err < 0) + return err; + + /* + * linearize the AN5192 formula: + * 1 / (1 + x) ~= 1 - x (Taylor’s Series) + * ttrim[s] = 1 / (40000 * (1 + 0.0015 * val)) + * ttrim[ns] ~= 25000 - 37.5 * val + * ttrim[ns] ~= 25000 - (37500 * val) / 1000 + */ + hw->ts_gain -= ((s8)val * 37500) / 1000; + } + return 0; } static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) { - u8 drdy_int_reg; + const struct st_lsm6dsx_reg *reg; int err; /* device sw reset */ - err = regmap_update_bits(hw->regmap, hw->settings->reset_addr, - ST_LSM6DSX_REG_RESET_MASK, - FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1)); + reg = &hw->settings->reset; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; msleep(50); /* reload trimming parameter */ - err = regmap_update_bits(hw->regmap, hw->settings->reset_addr, - ST_LSM6DSX_REG_BOOT_MASK, - FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1)); + reg = &hw->settings->boot; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; msleep(50); /* enable Block Data Update */ - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR, - ST_LSM6DSX_REG_BDU_MASK, - FIELD_PREP(ST_LSM6DSX_REG_BDU_MASK, 1)); + reg = &hw->settings->bdu; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; /* enable FIFO watermak interrupt */ - err = st_lsm6dsx_get_drdy_reg(hw, &drdy_int_reg); + err = st_lsm6dsx_get_drdy_reg(hw, ®); if (err < 0) return err; - err = regmap_update_bits(hw->regmap, drdy_int_reg, - ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, - FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, - 1)); + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; /* enable Latched interrupts for device events */ - if (hw->settings->lir.addr) { - unsigned int data; - - data = ST_LSM6DSX_SHIFT_VAL(1, hw->settings->lir.mask); - err = regmap_update_bits(hw->regmap, hw->settings->lir.addr, - hw->settings->lir.mask, data); + if (hw->settings->irq_config.lir.addr) { + reg = &hw->settings->irq_config.lir; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; /* enable clear on read for latched interrupts */ - if (hw->settings->clear_on_read.addr) { - data = ST_LSM6DSX_SHIFT_VAL(1, - hw->settings->clear_on_read.mask); + if (hw->settings->irq_config.clear_on_read.addr) { + reg = &hw->settings->irq_config.clear_on_read; err = regmap_update_bits(hw->regmap, - hw->settings->clear_on_read.addr, - hw->settings->clear_on_read.mask, - data); + reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; } } + /* enable drdy-mas if available */ + if (hw->settings->drdy_mask.addr) { + reg = &hw->settings->drdy_mask; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + } + err = st_lsm6dsx_init_shub(hw); if (err < 0) return err; @@ -1767,10 +2044,23 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, return iio_dev; } -static void st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw, int data) +static bool +st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw) { - s64 timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + const struct st_lsm6dsx_event_settings *event_settings; + int err, data; + s64 timestamp; + + if (!hw->enable_event) + return false; + + event_settings = &hw->settings->event_settings; + err = st_lsm6dsx_read_locked(hw, event_settings->wakeup_src_reg, + &data, sizeof(data)); + if (err < 0) + return false; + timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]); if ((data & hw->settings->event_settings.wakeup_src_z_mask) && (hw->enable_event & BIT(IIO_MOD_Z))) iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], @@ -1800,36 +2090,33 @@ static void st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw, int data) IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), timestamp); + + return data & event_settings->wakeup_src_status_mask; } static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) { struct st_lsm6dsx_hw *hw = private; + bool event; int count; - int data, err; - if (hw->enable_event) { - err = regmap_read(hw->regmap, - hw->settings->event_settings.wakeup_src_reg, - &data); - if (err < 0) - return IRQ_NONE; + event = st_lsm6dsx_report_motion_event(hw); - if (data & hw->settings->event_settings.wakeup_src_status_mask) - st_lsm6dsx_report_motion_event(hw, data); - } + if (!hw->settings->fifo_ops.read_fifo) + return event ? IRQ_HANDLED : IRQ_NONE; mutex_lock(&hw->fifo_lock); count = hw->settings->fifo_ops.read_fifo(hw); mutex_unlock(&hw->fifo_lock); - return count ? IRQ_HANDLED : IRQ_NONE; + return count || event ? IRQ_HANDLED : IRQ_NONE; } static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) { - struct st_sensors_platform_data *pdata; struct device_node *np = hw->dev->of_node; + struct st_sensors_platform_data *pdata; + const struct st_lsm6dsx_reg *reg; unsigned long irq_type; bool irq_active_low; int err; @@ -1850,20 +2137,19 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) return -EINVAL; } - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_HLACTIVE_ADDR, - ST_LSM6DSX_REG_HLACTIVE_MASK, - FIELD_PREP(ST_LSM6DSX_REG_HLACTIVE_MASK, - irq_active_low)); + reg = &hw->settings->irq_config.hla; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(irq_active_low, + reg->mask)); if (err < 0) return err; pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; if ((np && of_property_read_bool(np, "drive-open-drain")) || (pdata && pdata->open_drain)) { - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_PP_OD_ADDR, - ST_LSM6DSX_REG_PP_OD_MASK, - FIELD_PREP(ST_LSM6DSX_REG_PP_OD_MASK, - 1)); + reg = &hw->settings->irq_config.od; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); if (err < 0) return err; @@ -1887,7 +2173,9 @@ static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap) { + struct st_sensors_platform_data *pdata = dev->platform_data; const struct st_lsm6dsx_shub_settings *hub_settings; + struct device_node *np = dev->of_node; struct st_lsm6dsx_hw *hw; const char *name = NULL; int i, err; @@ -1950,7 +2238,8 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - if (dev->of_node && of_property_read_bool(dev->of_node, "wakeup-source")) + if ((np && of_property_read_bool(np, "wakeup-source")) || + (pdata && pdata->wakeup_source)) device_init_wakeup(dev, true); return 0; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index f52511059545..cd47ec1fedcb 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -87,6 +87,14 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,lsm9ds1-imu", .data = (void *)ST_LSM9DS1_ID, }, + { + .compatible = "st,lsm6ds0", + .data = (void *)ST_LSM6DS0_ID, + }, + { + .compatible = "st,lsm6dsrx", + .data = (void *)ST_LSM6DSRX_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -104,6 +112,8 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, + { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, + { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 344b28dddebb..67ff36eac247 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -87,6 +87,14 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,lsm9ds1-imu", .data = (void *)ST_LSM9DS1_ID, }, + { + .compatible = "st,lsm6ds0", + .data = (void *)ST_LSM6DS0_ID, + }, + { + .compatible = "st,lsm6dsrx", + .data = (void *)ST_LSM6DSRX_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -104,6 +112,8 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, + { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, + { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 4a1a883dc061..d793c1541705 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -32,6 +32,17 @@ config ADJD_S311 This driver can also be built as a module. If so, the module will be called adjd_s311. +config ADUX1020 + tristate "ADUX1020 photometric sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here if you want to build a driver for the Analog Devices + ADUX1020 photometric sensor. + + To compile this driver as a module, choose M here: the + module will be called adux1020. + config AL3320A tristate "AL3320A ambient light sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 00d1f9b98f39..5d650ce46a40 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -6,6 +6,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ACPI_ALS) += acpi-als.o obj-$(CONFIG_ADJD_S311) += adjd_s311.o +obj-$(CONFIG_ADUX1020) += adux1020.o obj-$(CONFIG_AL3320A) += al3320a.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9960) += apds9960.o diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c new file mode 100644 index 000000000000..b07797ac10d7 --- /dev/null +++ b/drivers/iio/light/adux1020.c @@ -0,0 +1,849 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * adux1020.c - Support for Analog Devices ADUX1020 photometric sensor + * + * Copyright (C) 2019 Linaro Ltd. + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + * + * TODO: Triggered buffer support + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +#define ADUX1020_REGMAP_NAME "adux1020_regmap" +#define ADUX1020_DRV_NAME "adux1020" + +/* System registers */ +#define ADUX1020_REG_CHIP_ID 0x08 +#define ADUX1020_REG_SLAVE_ADDRESS 0x09 + +#define ADUX1020_REG_SW_RESET 0x0f +#define ADUX1020_REG_INT_ENABLE 0x1c +#define ADUX1020_REG_INT_POLARITY 0x1d +#define ADUX1020_REG_PROX_TH_ON1 0x2a +#define ADUX1020_REG_PROX_TH_OFF1 0x2b +#define ADUX1020_REG_PROX_TYPE 0x2f +#define ADUX1020_REG_TEST_MODES_3 0x32 +#define ADUX1020_REG_FORCE_MODE 0x33 +#define ADUX1020_REG_FREQUENCY 0x40 +#define ADUX1020_REG_LED_CURRENT 0x41 +#define ADUX1020_REG_OP_MODE 0x45 +#define ADUX1020_REG_INT_MASK 0x48 +#define ADUX1020_REG_INT_STATUS 0x49 +#define ADUX1020_REG_DATA_BUFFER 0x60 + +/* Chip ID bits */ +#define ADUX1020_CHIP_ID_MASK GENMASK(11, 0) +#define ADUX1020_CHIP_ID 0x03fc + +#define ADUX1020_SW_RESET BIT(1) +#define ADUX1020_FIFO_FLUSH BIT(15) +#define ADUX1020_OP_MODE_MASK GENMASK(3, 0) +#define ADUX1020_DATA_OUT_MODE_MASK GENMASK(7, 4) +#define ADUX1020_DATA_OUT_PROX_I FIELD_PREP(ADUX1020_DATA_OUT_MODE_MASK, 1) + +#define ADUX1020_MODE_INT_MASK GENMASK(7, 0) +#define ADUX1020_INT_ENABLE 0x2094 +#define ADUX1020_INT_DISABLE 0x2090 +#define ADUX1020_PROX_INT_ENABLE 0x00f0 +#define ADUX1020_PROX_ON1_INT BIT(0) +#define ADUX1020_PROX_OFF1_INT BIT(1) +#define ADUX1020_FIFO_INT_ENABLE 0x7f +#define ADUX1020_MODE_INT_DISABLE 0xff +#define ADUX1020_MODE_INT_STATUS_MASK GENMASK(7, 0) +#define ADUX1020_FIFO_STATUS_MASK GENMASK(15, 8) +#define ADUX1020_INT_CLEAR 0xff +#define ADUX1020_PROX_TYPE BIT(15) + +#define ADUX1020_INT_PROX_ON1 BIT(0) +#define ADUX1020_INT_PROX_OFF1 BIT(1) + +#define ADUX1020_FORCE_CLOCK_ON 0x0f4f +#define ADUX1020_FORCE_CLOCK_RESET 0x0040 +#define ADUX1020_ACTIVE_4_STATE 0x0008 + +#define ADUX1020_PROX_FREQ_MASK GENMASK(7, 4) +#define ADUX1020_PROX_FREQ(x) FIELD_PREP(ADUX1020_PROX_FREQ_MASK, x) + +#define ADUX1020_LED_CURRENT_MASK GENMASK(3, 0) +#define ADUX1020_LED_PIREF_EN BIT(12) + +/* Operating modes */ +enum adux1020_op_modes { + ADUX1020_MODE_STANDBY, + ADUX1020_MODE_PROX_I, + ADUX1020_MODE_PROX_XY, + ADUX1020_MODE_GEST, + ADUX1020_MODE_SAMPLE, + ADUX1020_MODE_FORCE = 0x0e, + ADUX1020_MODE_IDLE = 0x0f, +}; + +struct adux1020_data { + struct i2c_client *client; + struct iio_dev *indio_dev; + struct mutex lock; + struct regmap *regmap; +}; + +struct adux1020_mode_data { + u8 bytes; + u8 buf_len; + u16 int_en; +}; + +static const struct adux1020_mode_data adux1020_modes[] = { + [ADUX1020_MODE_PROX_I] = { + .bytes = 2, + .buf_len = 1, + .int_en = ADUX1020_PROX_INT_ENABLE, + }, +}; + +static const struct regmap_config adux1020_regmap_config = { + .name = ADUX1020_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x6F, + .cache_type = REGCACHE_NONE, +}; + +static const struct reg_sequence adux1020_def_conf[] = { + { 0x000c, 0x000f }, + { 0x0010, 0x1010 }, + { 0x0011, 0x004c }, + { 0x0012, 0x5f0c }, + { 0x0013, 0xada5 }, + { 0x0014, 0x0080 }, + { 0x0015, 0x0000 }, + { 0x0016, 0x0600 }, + { 0x0017, 0x0000 }, + { 0x0018, 0x2693 }, + { 0x0019, 0x0004 }, + { 0x001a, 0x4280 }, + { 0x001b, 0x0060 }, + { 0x001c, 0x2094 }, + { 0x001d, 0x0020 }, + { 0x001e, 0x0001 }, + { 0x001f, 0x0100 }, + { 0x0020, 0x0320 }, + { 0x0021, 0x0A13 }, + { 0x0022, 0x0320 }, + { 0x0023, 0x0113 }, + { 0x0024, 0x0000 }, + { 0x0025, 0x2412 }, + { 0x0026, 0x2412 }, + { 0x0027, 0x0022 }, + { 0x0028, 0x0000 }, + { 0x0029, 0x0300 }, + { 0x002a, 0x0700 }, + { 0x002b, 0x0600 }, + { 0x002c, 0x6000 }, + { 0x002d, 0x4000 }, + { 0x002e, 0x0000 }, + { 0x002f, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0031, 0x0000 }, + { 0x0032, 0x0040 }, + { 0x0033, 0x0008 }, + { 0x0034, 0xE400 }, + { 0x0038, 0x8080 }, + { 0x0039, 0x8080 }, + { 0x003a, 0x2000 }, + { 0x003b, 0x1f00 }, + { 0x003c, 0x2000 }, + { 0x003d, 0x2000 }, + { 0x003e, 0x0000 }, + { 0x0040, 0x8069 }, + { 0x0041, 0x1f2f }, + { 0x0042, 0x4000 }, + { 0x0043, 0x0000 }, + { 0x0044, 0x0008 }, + { 0x0046, 0x0000 }, + { 0x0048, 0x00ef }, + { 0x0049, 0x0000 }, + { 0x0045, 0x0000 }, +}; + +static const int adux1020_rates[][2] = { + { 0, 100000 }, + { 0, 200000 }, + { 0, 500000 }, + { 1, 0 }, + { 2, 0 }, + { 5, 0 }, + { 10, 0 }, + { 20, 0 }, + { 50, 0 }, + { 100, 0 }, + { 190, 0 }, + { 450, 0 }, + { 820, 0 }, + { 1400, 0 }, +}; + +static const int adux1020_led_currents[][2] = { + { 0, 25000 }, + { 0, 40000 }, + { 0, 55000 }, + { 0, 70000 }, + { 0, 85000 }, + { 0, 100000 }, + { 0, 115000 }, + { 0, 130000 }, + { 0, 145000 }, + { 0, 160000 }, + { 0, 175000 }, + { 0, 190000 }, + { 0, 205000 }, + { 0, 220000 }, + { 0, 235000 }, + { 0, 250000 }, +}; + +static int adux1020_flush_fifo(struct adux1020_data *data) +{ + int ret; + + /* Force Idle mode */ + ret = regmap_write(data->regmap, ADUX1020_REG_FORCE_MODE, + ADUX1020_ACTIVE_4_STATE); + if (ret < 0) + return ret; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_OP_MODE_MASK, ADUX1020_MODE_FORCE); + if (ret < 0) + return ret; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_OP_MODE_MASK, ADUX1020_MODE_IDLE); + if (ret < 0) + return ret; + + /* Flush FIFO */ + ret = regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_ON); + if (ret < 0) + return ret; + + ret = regmap_write(data->regmap, ADUX1020_REG_INT_STATUS, + ADUX1020_FIFO_FLUSH); + if (ret < 0) + return ret; + + return regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_RESET); +} + +static int adux1020_read_fifo(struct adux1020_data *data, u16 *buf, u8 buf_len) +{ + unsigned int regval; + int i, ret; + + /* Enable 32MHz clock */ + ret = regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_ON); + if (ret < 0) + return ret; + + for (i = 0; i < buf_len; i++) { + ret = regmap_read(data->regmap, ADUX1020_REG_DATA_BUFFER, + ®val); + if (ret < 0) + return ret; + + buf[i] = regval; + } + + /* Set 32MHz clock to be controlled by internal state machine */ + return regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3, + ADUX1020_FORCE_CLOCK_RESET); +} + +static int adux1020_set_mode(struct adux1020_data *data, + enum adux1020_op_modes mode) +{ + int ret; + + /* Switch to standby mode before changing the mode */ + ret = regmap_write(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_MODE_STANDBY); + if (ret < 0) + return ret; + + /* Set data out and switch to the desired mode */ + switch (mode) { + case ADUX1020_MODE_PROX_I: + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_DATA_OUT_MODE_MASK, + ADUX1020_DATA_OUT_PROX_I); + if (ret < 0) + return ret; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE, + ADUX1020_OP_MODE_MASK, + ADUX1020_MODE_PROX_I); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int adux1020_measure(struct adux1020_data *data, + enum adux1020_op_modes mode, + u16 *val) +{ + unsigned int status; + int ret, tries = 50; + + /* Disable INT pin as polling is going to be used */ + ret = regmap_write(data->regmap, ADUX1020_REG_INT_ENABLE, + ADUX1020_INT_DISABLE); + if (ret < 0) + return ret; + + /* Enable mode interrupt */ + ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + ADUX1020_MODE_INT_MASK, + adux1020_modes[mode].int_en); + if (ret < 0) + return ret; + + while (tries--) { + ret = regmap_read(data->regmap, ADUX1020_REG_INT_STATUS, + &status); + if (ret < 0) + return ret; + + status &= ADUX1020_FIFO_STATUS_MASK; + if (status >= adux1020_modes[mode].bytes) + break; + msleep(20); + } + + if (tries < 0) + return -EIO; + + ret = adux1020_read_fifo(data, val, adux1020_modes[mode].buf_len); + if (ret < 0) + return ret; + + /* Clear mode interrupt */ + ret = regmap_write(data->regmap, ADUX1020_REG_INT_STATUS, + (~adux1020_modes[mode].int_en)); + if (ret < 0) + return ret; + + /* Disable mode interrupts */ + return regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + ADUX1020_MODE_INT_MASK, + ADUX1020_MODE_INT_DISABLE); +} + +static int adux1020_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adux1020_data *data = iio_priv(indio_dev); + u16 buf[3]; + int ret = -EINVAL; + unsigned int regval; + + mutex_lock(&data->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_PROXIMITY: + ret = adux1020_set_mode(data, ADUX1020_MODE_PROX_I); + if (ret < 0) + goto fail; + + ret = adux1020_measure(data, ADUX1020_MODE_PROX_I, buf); + if (ret < 0) + goto fail; + + *val = buf[0]; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_CURRENT: + ret = regmap_read(data->regmap, + ADUX1020_REG_LED_CURRENT, ®val); + if (ret < 0) + goto fail; + + regval = regval & ADUX1020_LED_CURRENT_MASK; + + *val = adux1020_led_currents[regval][0]; + *val2 = adux1020_led_currents[regval][1]; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->type) { + case IIO_PROXIMITY: + ret = regmap_read(data->regmap, ADUX1020_REG_FREQUENCY, + ®val); + if (ret < 0) + goto fail; + + regval = FIELD_GET(ADUX1020_PROX_FREQ_MASK, regval); + + *val = adux1020_rates[regval][0]; + *val2 = adux1020_rates[regval][1]; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + break; + } + break; + default: + break; + } + +fail: + mutex_unlock(&data->lock); + + return ret; +}; + +static inline int adux1020_find_index(const int array[][2], int count, int val, + int val2) +{ + int i; + + for (i = 0; i < count; i++) + if (val == array[i][0] && val2 == array[i][1]) + return i; + + return -EINVAL; +} + +static int adux1020_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adux1020_data *data = iio_priv(indio_dev); + int i, ret = -EINVAL; + + mutex_lock(&data->lock); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (chan->type == IIO_PROXIMITY) { + i = adux1020_find_index(adux1020_rates, + ARRAY_SIZE(adux1020_rates), + val, val2); + if (i < 0) { + ret = i; + goto fail; + } + + ret = regmap_update_bits(data->regmap, + ADUX1020_REG_FREQUENCY, + ADUX1020_PROX_FREQ_MASK, + ADUX1020_PROX_FREQ(i)); + } + break; + case IIO_CHAN_INFO_PROCESSED: + if (chan->type == IIO_CURRENT) { + i = adux1020_find_index(adux1020_led_currents, + ARRAY_SIZE(adux1020_led_currents), + val, val2); + if (i < 0) { + ret = i; + goto fail; + } + + ret = regmap_update_bits(data->regmap, + ADUX1020_REG_LED_CURRENT, + ADUX1020_LED_CURRENT_MASK, i); + } + break; + default: + break; + } + +fail: + mutex_unlock(&data->lock); + + return ret; +} + +static int adux1020_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 adux1020_data *data = iio_priv(indio_dev); + int ret, mask; + + mutex_lock(&data->lock); + + ret = regmap_write(data->regmap, ADUX1020_REG_INT_ENABLE, + ADUX1020_INT_ENABLE); + if (ret < 0) + goto fail; + + ret = regmap_write(data->regmap, ADUX1020_REG_INT_POLARITY, 0); + if (ret < 0) + goto fail; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + mask = ADUX1020_PROX_ON1_INT; + else + mask = ADUX1020_PROX_OFF1_INT; + + if (state) + state = 0; + else + state = mask; + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + mask, state); + if (ret < 0) + goto fail; + + /* + * Trigger proximity interrupt when the intensity is above + * or below threshold + */ + ret = regmap_update_bits(data->regmap, ADUX1020_REG_PROX_TYPE, + ADUX1020_PROX_TYPE, + ADUX1020_PROX_TYPE); + if (ret < 0) + goto fail; + + /* Set proximity mode */ + ret = adux1020_set_mode(data, ADUX1020_MODE_PROX_I); + break; + default: + ret = -EINVAL; + break; + } + +fail: + mutex_unlock(&data->lock); + + return ret; +} + +static int adux1020_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 adux1020_data *data = iio_priv(indio_dev); + int ret, mask; + unsigned int regval; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + mask = ADUX1020_PROX_ON1_INT; + else + mask = ADUX1020_PROX_OFF1_INT; + break; + default: + return -EINVAL; + } + + ret = regmap_read(data->regmap, ADUX1020_REG_INT_MASK, ®val); + if (ret < 0) + return ret; + + return !(regval & mask); +} + +static int adux1020_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct adux1020_data *data = iio_priv(indio_dev); + u8 reg; + int ret; + unsigned int regval; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + reg = ADUX1020_REG_PROX_TH_ON1; + else + reg = ADUX1020_REG_PROX_TH_OFF1; + break; + default: + return -EINVAL; + } + + ret = regmap_read(data->regmap, reg, ®val); + if (ret < 0) + return ret; + + *val = regval; + + return IIO_VAL_INT; +} + +static int adux1020_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct adux1020_data *data = iio_priv(indio_dev); + u8 reg; + + switch (chan->type) { + case IIO_PROXIMITY: + if (dir == IIO_EV_DIR_RISING) + reg = ADUX1020_REG_PROX_TH_ON1; + else + reg = ADUX1020_REG_PROX_TH_OFF1; + break; + default: + return -EINVAL; + } + + /* Full scale threshold value is 0-65535 */ + if (val < 0 || val > 65535) + return -EINVAL; + + return regmap_write(data->regmap, reg, val); +} + +static const struct iio_event_spec adux1020_proximity_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec adux1020_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .event_spec = adux1020_proximity_event, + .num_event_specs = ARRAY_SIZE(adux1020_proximity_event), + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .extend_name = "led", + .output = 1, + }, +}; + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "0.1 0.2 0.5 1 2 5 10 20 50 100 190 450 820 1400"); + +static struct attribute *adux1020_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group adux1020_attribute_group = { + .attrs = adux1020_attributes, +}; + +static const struct iio_info adux1020_info = { + .attrs = &adux1020_attribute_group, + .read_raw = adux1020_read_raw, + .write_raw = adux1020_write_raw, + .read_event_config = adux1020_read_event_config, + .write_event_config = adux1020_write_event_config, + .read_event_value = adux1020_read_thresh, + .write_event_value = adux1020_write_thresh, +}; + +static irqreturn_t adux1020_interrupt_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct adux1020_data *data = iio_priv(indio_dev); + int ret, status; + + ret = regmap_read(data->regmap, ADUX1020_REG_INT_STATUS, &status); + if (ret < 0) + return IRQ_HANDLED; + + status &= ADUX1020_MODE_INT_STATUS_MASK; + + if (status & ADUX1020_INT_PROX_ON1) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + + if (status & ADUX1020_INT_PROX_OFF1) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + regmap_update_bits(data->regmap, ADUX1020_REG_INT_STATUS, + ADUX1020_MODE_INT_MASK, ADUX1020_INT_CLEAR); + + return IRQ_HANDLED; +} + +static int adux1020_chip_init(struct adux1020_data *data) +{ + struct i2c_client *client = data->client; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ADUX1020_REG_CHIP_ID, &val); + if (ret < 0) + return ret; + + if ((val & ADUX1020_CHIP_ID_MASK) != ADUX1020_CHIP_ID) { + dev_err(&client->dev, "invalid chip id 0x%04x\n", val); + return -ENODEV; + } + + dev_dbg(&client->dev, "Detected ADUX1020 with chip id: 0x%04x\n", val); + + ret = regmap_update_bits(data->regmap, ADUX1020_REG_SW_RESET, + ADUX1020_SW_RESET, ADUX1020_SW_RESET); + if (ret < 0) + return ret; + + /* Load default configuration */ + ret = regmap_multi_reg_write(data->regmap, adux1020_def_conf, + ARRAY_SIZE(adux1020_def_conf)); + if (ret < 0) + return ret; + + ret = adux1020_flush_fifo(data); + if (ret < 0) + return ret; + + /* Use LED_IREF for proximity mode */ + ret = regmap_update_bits(data->regmap, ADUX1020_REG_LED_CURRENT, + ADUX1020_LED_PIREF_EN, 0); + if (ret < 0) + return ret; + + /* Mask all interrupts */ + return regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK, + ADUX1020_MODE_INT_MASK, ADUX1020_MODE_INT_DISABLE); +} + +static int adux1020_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adux1020_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &adux1020_info; + indio_dev->name = ADUX1020_DRV_NAME; + indio_dev->channels = adux1020_channels; + indio_dev->num_channels = ARRAY_SIZE(adux1020_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + data = iio_priv(indio_dev); + + data->regmap = devm_regmap_init_i2c(client, &adux1020_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "regmap initialization failed.\n"); + return PTR_ERR(data->regmap); + } + + data->client = client; + data->indio_dev = indio_dev; + mutex_init(&data->lock); + + ret = adux1020_chip_init(data); + if (ret) + return ret; + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, adux1020_interrupt_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + ADUX1020_DRV_NAME, indio_dev); + if (ret) { + dev_err(&client->dev, "irq request error %d\n", -ret); + return ret; + } + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id adux1020_id[] = { + { "adux1020", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adux1020_id); + +static const struct of_device_id adux1020_of_match[] = { + { .compatible = "adi,adux1020" }, + { } +}; +MODULE_DEVICE_TABLE(of, adux1020_of_match); + +static struct i2c_driver adux1020_driver = { + .driver = { + .name = ADUX1020_DRV_NAME, + .of_match_table = adux1020_of_match, + }, + .probe = adux1020_probe, + .id_table = adux1020_id, +}; +module_i2c_driver(adux1020_driver); + +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_DESCRIPTION("ADUX1020 photometric sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index a3a268ee2896..e68184a93a6d 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -14,7 +14,6 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/irq.h> #include <linux/delay.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index c3b5c1f6614d..29c209cc1108 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -74,6 +74,12 @@ struct bmp280_calib { s8 H6; }; +static const char *const bmp280_supply_names[] = { + "vddd", "vdda" +}; + +#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) + struct bmp280_data { struct device *dev; struct mutex lock; @@ -85,8 +91,7 @@ struct bmp280_data { struct bmp180_calib bmp180; struct bmp280_calib bmp280; } calib; - struct regulator *vddd; - struct regulator *vdda; + struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; unsigned int start_up_time; /* in microseconds */ /* log of base 2 of oversampling rate */ @@ -148,6 +153,8 @@ static int bmp280_read_calib(struct bmp280_data *data, { int ret; unsigned int tmp; + __le16 l16; + __be16 b16; struct device *dev = data->dev; __le16 t_buf[BMP280_COMP_TEMP_REG_COUNT / 2]; __le16 p_buf[BMP280_COMP_PRESS_REG_COUNT / 2]; @@ -207,12 +214,12 @@ static int bmp280_read_calib(struct bmp280_data *data, } calib->H1 = tmp; - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &tmp, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &l16, 2); if (ret < 0) { dev_err(dev, "failed to read H2 comp value\n"); return ret; } - calib->H2 = sign_extend32(le16_to_cpu(tmp), 15); + calib->H2 = sign_extend32(le16_to_cpu(l16), 15); ret = regmap_read(data->regmap, BMP280_REG_COMP_H3, &tmp); if (ret < 0) { @@ -221,20 +228,20 @@ static int bmp280_read_calib(struct bmp280_data *data, } calib->H3 = tmp; - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &tmp, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &b16, 2); if (ret < 0) { dev_err(dev, "failed to read H4 comp value\n"); return ret; } - calib->H4 = sign_extend32(((be16_to_cpu(tmp) >> 4) & 0xff0) | - (be16_to_cpu(tmp) & 0xf), 11); + calib->H4 = sign_extend32(((be16_to_cpu(b16) >> 4) & 0xff0) | + (be16_to_cpu(b16) & 0xf), 11); - ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &tmp, 2); + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &l16, 2); if (ret < 0) { dev_err(dev, "failed to read H5 comp value\n"); return ret; } - calib->H5 = sign_extend32(((le16_to_cpu(tmp) >> 4) & 0xfff), 11); + calib->H5 = sign_extend32(((le16_to_cpu(l16) >> 4) & 0xfff), 11); ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp); if (ret < 0) { @@ -979,6 +986,22 @@ static int bmp085_fetch_eoc_irq(struct device *dev, return 0; } +static void bmp280_pm_disable(void *data) +{ + struct device *dev = data; + + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); +} + +static void bmp280_regulators_disable(void *data) +{ + struct regulator_bulk_data *supplies = data; + + regulator_bulk_disable(BMP280_NUM_SUPPLIES, supplies); +} + int bmp280_common_probe(struct device *dev, struct regmap *regmap, unsigned int chip, @@ -1033,27 +1056,28 @@ int bmp280_common_probe(struct device *dev, } /* Bring up regulators */ - data->vddd = devm_regulator_get(dev, "vddd"); - if (IS_ERR(data->vddd)) { - dev_err(dev, "failed to get VDDD regulator\n"); - return PTR_ERR(data->vddd); - } - ret = regulator_enable(data->vddd); + regulator_bulk_set_supply_names(data->supplies, + bmp280_supply_names, + BMP280_NUM_SUPPLIES); + + ret = devm_regulator_bulk_get(dev, + BMP280_NUM_SUPPLIES, data->supplies); if (ret) { - dev_err(dev, "failed to enable VDDD regulator\n"); + dev_err(dev, "failed to get regulators\n"); return ret; } - data->vdda = devm_regulator_get(dev, "vdda"); - if (IS_ERR(data->vdda)) { - dev_err(dev, "failed to get VDDA regulator\n"); - ret = PTR_ERR(data->vdda); - goto out_disable_vddd; - } - ret = regulator_enable(data->vdda); + + ret = regulator_bulk_enable(BMP280_NUM_SUPPLIES, data->supplies); if (ret) { - dev_err(dev, "failed to enable VDDA regulator\n"); - goto out_disable_vddd; + dev_err(dev, "failed to enable regulators\n"); + return ret; } + + ret = devm_add_action_or_reset(dev, bmp280_regulators_disable, + data->supplies); + if (ret) + return ret; + /* Wait to make sure we started up properly */ usleep_range(data->start_up_time, data->start_up_time + 100); @@ -1068,17 +1092,16 @@ int bmp280_common_probe(struct device *dev, data->regmap = regmap; ret = regmap_read(regmap, BMP280_REG_ID, &chip_id); if (ret < 0) - goto out_disable_vdda; + return ret; if (chip_id != chip) { dev_err(dev, "bad chip id: expected %x got %x\n", chip, chip_id); - ret = -EINVAL; - goto out_disable_vdda; + return -EINVAL; } ret = data->chip_info->chip_config(data); if (ret < 0) - goto out_disable_vdda; + return ret; dev_set_drvdata(dev, indio_dev); @@ -1092,14 +1115,14 @@ int bmp280_common_probe(struct device *dev, if (ret < 0) { dev_err(data->dev, "failed to read calibration coefficients\n"); - goto out_disable_vdda; + return ret; } } else if (chip_id == BMP280_CHIP_ID || chip_id == BME280_CHIP_ID) { ret = bmp280_read_calib(data, &data->calib.bmp280, chip_id); if (ret < 0) { dev_err(data->dev, "failed to read calibration coefficients\n"); - goto out_disable_vdda; + return ret; } } @@ -1111,7 +1134,7 @@ int bmp280_common_probe(struct device *dev, if (irq > 0 || (chip_id == BMP180_CHIP_ID)) { ret = bmp085_fetch_eoc_irq(dev, name, irq, data); if (ret) - goto out_disable_vdda; + return ret; } /* Enable runtime PM */ @@ -1126,50 +1149,21 @@ int bmp280_common_probe(struct device *dev, pm_runtime_use_autosuspend(dev); pm_runtime_put(dev); - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(dev, bmp280_pm_disable, dev); if (ret) - goto out_runtime_pm_disable; - - return 0; + return ret; -out_runtime_pm_disable: - pm_runtime_get_sync(data->dev); - pm_runtime_put_noidle(data->dev); - pm_runtime_disable(data->dev); -out_disable_vdda: - regulator_disable(data->vdda); -out_disable_vddd: - regulator_disable(data->vddd); - return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL(bmp280_common_probe); -int bmp280_common_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct bmp280_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - pm_runtime_get_sync(data->dev); - pm_runtime_put_noidle(data->dev); - pm_runtime_disable(data->dev); - regulator_disable(data->vdda); - regulator_disable(data->vddd); - return 0; -} -EXPORT_SYMBOL(bmp280_common_remove); - #ifdef CONFIG_PM static int bmp280_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmp280_data *data = iio_priv(indio_dev); - int ret; - ret = regulator_disable(data->vdda); - if (ret) - return ret; - return regulator_disable(data->vddd); + return regulator_bulk_disable(BMP280_NUM_SUPPLIES, data->supplies); } static int bmp280_runtime_resume(struct device *dev) @@ -1178,10 +1172,7 @@ static int bmp280_runtime_resume(struct device *dev) struct bmp280_data *data = iio_priv(indio_dev); int ret; - ret = regulator_enable(data->vddd); - if (ret) - return ret; - ret = regulator_enable(data->vdda); + ret = regulator_bulk_enable(BMP280_NUM_SUPPLIES, data->supplies); if (ret) return ret; usleep_range(data->start_up_time, data->start_up_time + 100); diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index acd9a3784fb4..3109c8e2cc11 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -38,11 +38,6 @@ static int bmp280_i2c_probe(struct i2c_client *client, client->irq); } -static int bmp280_i2c_remove(struct i2c_client *client) -{ - return bmp280_common_remove(&client->dev); -} - static const struct acpi_device_id bmp280_acpi_i2c_match[] = { {"BMP0280", BMP280_CHIP_ID }, {"BMP0180", BMP180_CHIP_ID }, @@ -82,7 +77,6 @@ static struct i2c_driver bmp280_i2c_driver = { .pm = &bmp280_dev_pm_ops, }, .probe = bmp280_i2c_probe, - .remove = bmp280_i2c_remove, .id_table = bmp280_i2c_id, }; module_i2c_driver(bmp280_i2c_driver); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 9d57b7a3b134..625b86878ad8 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -86,11 +86,6 @@ static int bmp280_spi_probe(struct spi_device *spi) spi->irq); } -static int bmp280_spi_remove(struct spi_device *spi) -{ - return bmp280_common_remove(&spi->dev); -} - static const struct of_device_id bmp280_of_spi_match[] = { { .compatible = "bosch,bmp085", }, { .compatible = "bosch,bmp180", }, @@ -118,7 +113,6 @@ static struct spi_driver bmp280_spi_driver = { }, .id_table = bmp280_spi_id, .probe = bmp280_spi_probe, - .remove = bmp280_spi_remove, }; module_spi_driver(bmp280_spi_driver); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index eda50ef65706..57ba0e85db91 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -112,7 +112,6 @@ int bmp280_common_probe(struct device *dev, unsigned int chip, const char *name, int irq); -int bmp280_common_remove(struct device *dev); /* PM ops */ extern const struct dev_pm_ops bmp280_dev_pm_ops; diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index ca6863b32a5f..bd972cec4830 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -14,7 +14,6 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/irq.h> #include <linux/delay.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 9d0d07930236..99dfe33ee402 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -1243,6 +1243,11 @@ static int zpa2326_postenable_buffer(struct iio_dev *indio_dev) const struct zpa2326_private *priv = iio_priv(indio_dev); int err; + /* Plug our own trigger event handler. */ + err = iio_triggered_buffer_postenable(indio_dev); + if (err) + goto err; + if (!priv->waken) { /* * We were already power supplied. Just clear hardware FIFO to @@ -1250,7 +1255,7 @@ static int zpa2326_postenable_buffer(struct iio_dev *indio_dev) */ err = zpa2326_clear_fifo(indio_dev, 0); if (err) - goto err; + goto err_buffer_predisable; } if (!iio_trigger_using_own(indio_dev) && priv->waken) { @@ -1260,16 +1265,13 @@ static int zpa2326_postenable_buffer(struct iio_dev *indio_dev) */ err = zpa2326_config_oneshot(indio_dev, priv->irq); if (err) - goto err; + goto err_buffer_predisable; } - /* Plug our own trigger event handler. */ - err = iio_triggered_buffer_postenable(indio_dev); - if (err) - goto err; - return 0; +err_buffer_predisable: + iio_triggered_buffer_predisable(indio_dev); err: zpa2326_err(indio_dev, "failed to enable buffering (%d)", err); diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 47af54f14756..5b369645ef49 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -136,12 +136,13 @@ static inline int lidar_write_power(struct lidar_data *data, int val) static int lidar_read_measurement(struct lidar_data *data, u16 *reg) { + __be16 value; int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE | (data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0), - (u8 *) reg, 2); + (u8 *) &value, 2); if (!ret) - *reg = be16_to_cpu(*reg); + *reg = be16_to_cpu(value); return ret; } diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index 737faa0901fe..e1ccb4003015 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -4,6 +4,17 @@ # menu "Temperature sensors" +config LTC2983 + tristate "Analog Devices Multi-Sensor Digital Temperature Measurement System" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for the LTC2983 Multi-Sensor + high accuracy digital temperature measurement system. + + To compile this driver as a module, choose M here: the module + will be called ltc2983. + config MAXIM_THERMOCOUPLE tristate "Maxim thermocouple sensors" depends on SPI diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index baca4776ca0d..d6b850b0cf63 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -3,6 +3,7 @@ # Makefile for industrial I/O temperature drivers # +obj-$(CONFIG_LTC2983) += ltc2983.o obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MAX31856) += max31856.o diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c new file mode 100644 index 000000000000..ddf47023364b --- /dev/null +++ b/drivers/iio/temperature/ltc2983.c @@ -0,0 +1,1557 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices LTC2983 Multi-Sensor Digital Temperature Measurement System + * driver + * + * Copyright 2019 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +/* register map */ +#define LTC2983_STATUS_REG 0x0000 +#define LTC2983_TEMP_RES_START_REG 0x0010 +#define LTC2983_TEMP_RES_END_REG 0x005F +#define LTC2983_GLOBAL_CONFIG_REG 0x00F0 +#define LTC2983_MULT_CHANNEL_START_REG 0x00F4 +#define LTC2983_MULT_CHANNEL_END_REG 0x00F7 +#define LTC2983_MUX_CONFIG_REG 0x00FF +#define LTC2983_CHAN_ASSIGN_START_REG 0x0200 +#define LTC2983_CHAN_ASSIGN_END_REG 0x024F +#define LTC2983_CUST_SENS_TBL_START_REG 0x0250 +#define LTC2983_CUST_SENS_TBL_END_REG 0x03CF + +#define LTC2983_DIFFERENTIAL_CHAN_MIN 2 +#define LTC2983_MAX_CHANNELS_NR 20 +#define LTC2983_MIN_CHANNELS_NR 1 +#define LTC2983_SLEEP 0x97 +#define LTC2983_CUSTOM_STEINHART_SIZE 24 +#define LTC2983_CUSTOM_SENSOR_ENTRY_SZ 6 +#define LTC2983_CUSTOM_STEINHART_ENTRY_SZ 4 + +#define LTC2983_CHAN_START_ADDR(chan) \ + (((chan - 1) * 4) + LTC2983_CHAN_ASSIGN_START_REG) +#define LTC2983_CHAN_RES_ADDR(chan) \ + (((chan - 1) * 4) + LTC2983_TEMP_RES_START_REG) +#define LTC2983_THERMOCOUPLE_DIFF_MASK BIT(3) +#define LTC2983_THERMOCOUPLE_SGL(x) \ + FIELD_PREP(LTC2983_THERMOCOUPLE_DIFF_MASK, x) +#define LTC2983_THERMOCOUPLE_OC_CURR_MASK GENMASK(1, 0) +#define LTC2983_THERMOCOUPLE_OC_CURR(x) \ + FIELD_PREP(LTC2983_THERMOCOUPLE_OC_CURR_MASK, x) +#define LTC2983_THERMOCOUPLE_OC_CHECK_MASK BIT(2) +#define LTC2983_THERMOCOUPLE_OC_CHECK(x) \ + FIELD_PREP(LTC2983_THERMOCOUPLE_OC_CHECK_MASK, x) + +#define LTC2983_THERMISTOR_DIFF_MASK BIT(2) +#define LTC2983_THERMISTOR_SGL(x) \ + FIELD_PREP(LTC2983_THERMISTOR_DIFF_MASK, x) +#define LTC2983_THERMISTOR_R_SHARE_MASK BIT(1) +#define LTC2983_THERMISTOR_R_SHARE(x) \ + FIELD_PREP(LTC2983_THERMISTOR_R_SHARE_MASK, x) +#define LTC2983_THERMISTOR_C_ROTATE_MASK BIT(0) +#define LTC2983_THERMISTOR_C_ROTATE(x) \ + FIELD_PREP(LTC2983_THERMISTOR_C_ROTATE_MASK, x) + +#define LTC2983_DIODE_DIFF_MASK BIT(2) +#define LTC2983_DIODE_SGL(x) \ + FIELD_PREP(LTC2983_DIODE_DIFF_MASK, x) +#define LTC2983_DIODE_3_CONV_CYCLE_MASK BIT(1) +#define LTC2983_DIODE_3_CONV_CYCLE(x) \ + FIELD_PREP(LTC2983_DIODE_3_CONV_CYCLE_MASK, x) +#define LTC2983_DIODE_AVERAGE_ON_MASK BIT(0) +#define LTC2983_DIODE_AVERAGE_ON(x) \ + FIELD_PREP(LTC2983_DIODE_AVERAGE_ON_MASK, x) + +#define LTC2983_RTD_4_WIRE_MASK BIT(3) +#define LTC2983_RTD_ROTATION_MASK BIT(1) +#define LTC2983_RTD_C_ROTATE(x) \ + FIELD_PREP(LTC2983_RTD_ROTATION_MASK, x) +#define LTC2983_RTD_KELVIN_R_SENSE_MASK GENMASK(3, 2) +#define LTC2983_RTD_N_WIRES_MASK GENMASK(3, 2) +#define LTC2983_RTD_N_WIRES(x) \ + FIELD_PREP(LTC2983_RTD_N_WIRES_MASK, x) +#define LTC2983_RTD_R_SHARE_MASK BIT(0) +#define LTC2983_RTD_R_SHARE(x) \ + FIELD_PREP(LTC2983_RTD_R_SHARE_MASK, 1) + +#define LTC2983_COMMON_HARD_FAULT_MASK GENMASK(31, 30) +#define LTC2983_COMMON_SOFT_FAULT_MASK GENMASK(27, 25) + +#define LTC2983_STATUS_START_MASK BIT(7) +#define LTC2983_STATUS_START(x) FIELD_PREP(LTC2983_STATUS_START_MASK, x) + +#define LTC2983_STATUS_CHAN_SEL_MASK GENMASK(4, 0) +#define LTC2983_STATUS_CHAN_SEL(x) \ + FIELD_PREP(LTC2983_STATUS_CHAN_SEL_MASK, x) + +#define LTC2983_TEMP_UNITS_MASK BIT(2) +#define LTC2983_TEMP_UNITS(x) FIELD_PREP(LTC2983_TEMP_UNITS_MASK, x) + +#define LTC2983_NOTCH_FREQ_MASK GENMASK(1, 0) +#define LTC2983_NOTCH_FREQ(x) FIELD_PREP(LTC2983_NOTCH_FREQ_MASK, x) + +#define LTC2983_RES_VALID_MASK BIT(24) +#define LTC2983_DATA_MASK GENMASK(23, 0) +#define LTC2983_DATA_SIGN_BIT 23 + +#define LTC2983_CHAN_TYPE_MASK GENMASK(31, 27) +#define LTC2983_CHAN_TYPE(x) FIELD_PREP(LTC2983_CHAN_TYPE_MASK, x) + +/* cold junction for thermocouples and rsense for rtd's and thermistor's */ +#define LTC2983_CHAN_ASSIGN_MASK GENMASK(26, 22) +#define LTC2983_CHAN_ASSIGN(x) FIELD_PREP(LTC2983_CHAN_ASSIGN_MASK, x) + +#define LTC2983_CUSTOM_LEN_MASK GENMASK(5, 0) +#define LTC2983_CUSTOM_LEN(x) FIELD_PREP(LTC2983_CUSTOM_LEN_MASK, x) + +#define LTC2983_CUSTOM_ADDR_MASK GENMASK(11, 6) +#define LTC2983_CUSTOM_ADDR(x) FIELD_PREP(LTC2983_CUSTOM_ADDR_MASK, x) + +#define LTC2983_THERMOCOUPLE_CFG_MASK GENMASK(21, 18) +#define LTC2983_THERMOCOUPLE_CFG(x) \ + FIELD_PREP(LTC2983_THERMOCOUPLE_CFG_MASK, x) +#define LTC2983_THERMOCOUPLE_HARD_FAULT_MASK GENMASK(31, 29) +#define LTC2983_THERMOCOUPLE_SOFT_FAULT_MASK GENMASK(28, 25) + +#define LTC2983_RTD_CFG_MASK GENMASK(21, 18) +#define LTC2983_RTD_CFG(x) FIELD_PREP(LTC2983_RTD_CFG_MASK, x) +#define LTC2983_RTD_EXC_CURRENT_MASK GENMASK(17, 14) +#define LTC2983_RTD_EXC_CURRENT(x) \ + FIELD_PREP(LTC2983_RTD_EXC_CURRENT_MASK, x) +#define LTC2983_RTD_CURVE_MASK GENMASK(13, 12) +#define LTC2983_RTD_CURVE(x) FIELD_PREP(LTC2983_RTD_CURVE_MASK, x) + +#define LTC2983_THERMISTOR_CFG_MASK GENMASK(21, 19) +#define LTC2983_THERMISTOR_CFG(x) \ + FIELD_PREP(LTC2983_THERMISTOR_CFG_MASK, x) +#define LTC2983_THERMISTOR_EXC_CURRENT_MASK GENMASK(18, 15) +#define LTC2983_THERMISTOR_EXC_CURRENT(x) \ + FIELD_PREP(LTC2983_THERMISTOR_EXC_CURRENT_MASK, x) + +#define LTC2983_DIODE_CFG_MASK GENMASK(26, 24) +#define LTC2983_DIODE_CFG(x) FIELD_PREP(LTC2983_DIODE_CFG_MASK, x) +#define LTC2983_DIODE_EXC_CURRENT_MASK GENMASK(23, 22) +#define LTC2983_DIODE_EXC_CURRENT(x) \ + FIELD_PREP(LTC2983_DIODE_EXC_CURRENT_MASK, x) +#define LTC2983_DIODE_IDEAL_FACTOR_MASK GENMASK(21, 0) +#define LTC2983_DIODE_IDEAL_FACTOR(x) \ + FIELD_PREP(LTC2983_DIODE_IDEAL_FACTOR_MASK, x) + +#define LTC2983_R_SENSE_VAL_MASK GENMASK(26, 0) +#define LTC2983_R_SENSE_VAL(x) FIELD_PREP(LTC2983_R_SENSE_VAL_MASK, x) + +#define LTC2983_ADC_SINGLE_ENDED_MASK BIT(26) +#define LTC2983_ADC_SINGLE_ENDED(x) \ + FIELD_PREP(LTC2983_ADC_SINGLE_ENDED_MASK, x) + +enum { + LTC2983_SENSOR_THERMOCOUPLE = 1, + LTC2983_SENSOR_THERMOCOUPLE_CUSTOM = 9, + LTC2983_SENSOR_RTD = 10, + LTC2983_SENSOR_RTD_CUSTOM = 18, + LTC2983_SENSOR_THERMISTOR = 19, + LTC2983_SENSOR_THERMISTOR_STEINHART = 26, + LTC2983_SENSOR_THERMISTOR_CUSTOM = 27, + LTC2983_SENSOR_DIODE = 28, + LTC2983_SENSOR_SENSE_RESISTOR = 29, + LTC2983_SENSOR_DIRECT_ADC = 30, +}; + +#define to_thermocouple(_sensor) \ + container_of(_sensor, struct ltc2983_thermocouple, sensor) + +#define to_rtd(_sensor) \ + container_of(_sensor, struct ltc2983_rtd, sensor) + +#define to_thermistor(_sensor) \ + container_of(_sensor, struct ltc2983_thermistor, sensor) + +#define to_diode(_sensor) \ + container_of(_sensor, struct ltc2983_diode, sensor) + +#define to_rsense(_sensor) \ + container_of(_sensor, struct ltc2983_rsense, sensor) + +#define to_adc(_sensor) \ + container_of(_sensor, struct ltc2983_adc, sensor) + +struct ltc2983_data { + struct regmap *regmap; + struct spi_device *spi; + struct mutex lock; + struct completion completion; + struct iio_chan_spec *iio_chan; + struct ltc2983_sensor **sensors; + u32 mux_delay_config; + u32 filter_notch_freq; + u16 custom_table_size; + u8 num_channels; + u8 iio_channels; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Holds the converted temperature + */ + __be32 temp ____cacheline_aligned; +}; + +struct ltc2983_sensor { + int (*fault_handler)(const struct ltc2983_data *st, const u32 result); + int (*assign_chan)(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor); + /* specifies the sensor channel */ + u32 chan; + /* sensor type */ + u32 type; +}; + +struct ltc2983_custom_sensor { + /* raw table sensor data */ + u8 *table; + size_t size; + /* address offset */ + s8 offset; + bool is_steinhart; +}; + +struct ltc2983_thermocouple { + struct ltc2983_sensor sensor; + struct ltc2983_custom_sensor *custom; + u32 sensor_config; + u32 cold_junction_chan; +}; + +struct ltc2983_rtd { + struct ltc2983_sensor sensor; + struct ltc2983_custom_sensor *custom; + u32 sensor_config; + u32 r_sense_chan; + u32 excitation_current; + u32 rtd_curve; +}; + +struct ltc2983_thermistor { + struct ltc2983_sensor sensor; + struct ltc2983_custom_sensor *custom; + u32 sensor_config; + u32 r_sense_chan; + u32 excitation_current; +}; + +struct ltc2983_diode { + struct ltc2983_sensor sensor; + u32 sensor_config; + u32 excitation_current; + u32 ideal_factor_value; +}; + +struct ltc2983_rsense { + struct ltc2983_sensor sensor; + u32 r_sense_val; +}; + +struct ltc2983_adc { + struct ltc2983_sensor sensor; + bool single_ended; +}; + +/* + * Convert to Q format numbers. These number's are integers where + * the number of integer and fractional bits are specified. The resolution + * is given by 1/@resolution and tell us the number of fractional bits. For + * instance a resolution of 2^-10 means we have 10 fractional bits. + */ +static u32 __convert_to_raw(const u64 val, const u32 resolution) +{ + u64 __res = val * resolution; + + /* all values are multiplied by 1000000 to remove the fraction */ + do_div(__res, 1000000); + + return __res; +} + +static u32 __convert_to_raw_sign(const u64 val, const u32 resolution) +{ + s64 __res = -(s32)val; + + __res = __convert_to_raw(__res, resolution); + + return (u32)-__res; +} + +static int __ltc2983_fault_handler(const struct ltc2983_data *st, + const u32 result, const u32 hard_mask, + const u32 soft_mask) +{ + const struct device *dev = &st->spi->dev; + + if (result & hard_mask) { + dev_err(dev, "Invalid conversion: Sensor HARD fault\n"); + return -EIO; + } else if (result & soft_mask) { + /* just print a warning */ + dev_warn(dev, "Suspicious conversion: Sensor SOFT fault\n"); + } + + return 0; +} + +static int __ltc2983_chan_assign_common(const struct ltc2983_data *st, + const struct ltc2983_sensor *sensor, + u32 chan_val) +{ + u32 reg = LTC2983_CHAN_START_ADDR(sensor->chan); + __be32 __chan_val; + + chan_val |= LTC2983_CHAN_TYPE(sensor->type); + dev_dbg(&st->spi->dev, "Assign reg:0x%04X, val:0x%08X\n", reg, + chan_val); + __chan_val = cpu_to_be32(chan_val); + return regmap_bulk_write(st->regmap, reg, &__chan_val, + sizeof(__chan_val)); +} + +static int __ltc2983_chan_custom_sensor_assign(struct ltc2983_data *st, + struct ltc2983_custom_sensor *custom, + u32 *chan_val) +{ + u32 reg; + u8 mult = custom->is_steinhart ? LTC2983_CUSTOM_STEINHART_ENTRY_SZ : + LTC2983_CUSTOM_SENSOR_ENTRY_SZ; + const struct device *dev = &st->spi->dev; + /* + * custom->size holds the raw size of the table. However, when + * configuring the sensor channel, we must write the number of + * entries of the table minus 1. For steinhart sensors 0 is written + * since the size is constant! + */ + const u8 len = custom->is_steinhart ? 0 : + (custom->size / LTC2983_CUSTOM_SENSOR_ENTRY_SZ) - 1; + /* + * Check if the offset was assigned already. It should be for steinhart + * sensors. When coming from sleep, it should be assigned for all. + */ + if (custom->offset < 0) { + /* + * This needs to be done again here because, from the moment + * when this test was done (successfully) for this custom + * sensor, a steinhart sensor might have been added changing + * custom_table_size... + */ + if (st->custom_table_size + custom->size > + (LTC2983_CUST_SENS_TBL_END_REG - + LTC2983_CUST_SENS_TBL_START_REG) + 1) { + dev_err(dev, + "Not space left(%d) for new custom sensor(%zu)", + st->custom_table_size, + custom->size); + return -EINVAL; + } + + custom->offset = st->custom_table_size / + LTC2983_CUSTOM_SENSOR_ENTRY_SZ; + st->custom_table_size += custom->size; + } + + reg = (custom->offset * mult) + LTC2983_CUST_SENS_TBL_START_REG; + + *chan_val |= LTC2983_CUSTOM_LEN(len); + *chan_val |= LTC2983_CUSTOM_ADDR(custom->offset); + dev_dbg(dev, "Assign custom sensor, reg:0x%04X, off:%d, sz:%zu", + reg, custom->offset, + custom->size); + /* write custom sensor table */ + return regmap_bulk_write(st->regmap, reg, custom->table, custom->size); +} + +static struct ltc2983_custom_sensor *__ltc2983_custom_sensor_new( + struct ltc2983_data *st, + const struct device_node *np, + const char *propname, + const bool is_steinhart, + const u32 resolution, + const bool has_signed) +{ + struct ltc2983_custom_sensor *new_custom; + u8 index, n_entries, tbl = 0; + struct device *dev = &st->spi->dev; + /* + * For custom steinhart, the full u32 is taken. For all the others + * the MSB is discarded. + */ + const u8 n_size = (is_steinhart == true) ? 4 : 3; + const u8 e_size = (is_steinhart == true) ? sizeof(u32) : sizeof(u64); + + n_entries = of_property_count_elems_of_size(np, propname, e_size); + /* n_entries must be an even number */ + if (!n_entries || (n_entries % 2) != 0) { + dev_err(dev, "Number of entries either 0 or not even\n"); + return ERR_PTR(-EINVAL); + } + + new_custom = devm_kzalloc(dev, sizeof(*new_custom), GFP_KERNEL); + if (!new_custom) + return ERR_PTR(-ENOMEM); + + new_custom->size = n_entries * n_size; + /* check Steinhart size */ + if (is_steinhart && new_custom->size != LTC2983_CUSTOM_STEINHART_SIZE) { + dev_err(dev, "Steinhart sensors size(%zu) must be 24", + new_custom->size); + return ERR_PTR(-EINVAL); + } + /* Check space on the table. */ + if (st->custom_table_size + new_custom->size > + (LTC2983_CUST_SENS_TBL_END_REG - + LTC2983_CUST_SENS_TBL_START_REG) + 1) { + dev_err(dev, "No space left(%d) for new custom sensor(%zu)", + st->custom_table_size, new_custom->size); + return ERR_PTR(-EINVAL); + } + + /* allocate the table */ + new_custom->table = devm_kzalloc(dev, new_custom->size, GFP_KERNEL); + if (!new_custom->table) + return ERR_PTR(-ENOMEM); + + for (index = 0; index < n_entries; index++) { + u64 temp = 0, j; + /* + * Steinhart sensors are configured with raw values in the + * devicetree. For the other sensors we must convert the + * value to raw. The odd index's correspond to temperarures + * and always have 1/1024 of resolution. Temperatures also + * come in kelvin, so signed values is not possible + */ + if (!is_steinhart) { + of_property_read_u64_index(np, propname, index, &temp); + + if ((index % 2) != 0) + temp = __convert_to_raw(temp, 1024); + else if (has_signed && (s64)temp < 0) + temp = __convert_to_raw_sign(temp, resolution); + else + temp = __convert_to_raw(temp, resolution); + } else { + of_property_read_u32_index(np, propname, index, + (u32 *)&temp); + } + + for (j = 0; j < n_size; j++) + new_custom->table[tbl++] = + temp >> (8 * (n_size - j - 1)); + } + + new_custom->is_steinhart = is_steinhart; + /* + * This is done to first add all the steinhart sensors to the table, + * in order to maximize the table usage. If we mix adding steinhart + * with the other sensors, we might have to do some roundup to make + * sure that sensor_addr - 0x250(start address) is a multiple of 4 + * (for steinhart), and a multiple of 6 for all the other sensors. + * Since we have const 24 bytes for steinhart sensors and 24 is + * also a multiple of 6, we guarantee that the first non-steinhart + * sensor will sit in a correct address without the need of filling + * addresses. + */ + if (is_steinhart) { + new_custom->offset = st->custom_table_size / + LTC2983_CUSTOM_STEINHART_ENTRY_SZ; + st->custom_table_size += new_custom->size; + } else { + /* mark as unset. This is checked later on the assign phase */ + new_custom->offset = -1; + } + + return new_custom; +} + +static int ltc2983_thermocouple_fault_handler(const struct ltc2983_data *st, + const u32 result) +{ + return __ltc2983_fault_handler(st, result, + LTC2983_THERMOCOUPLE_HARD_FAULT_MASK, + LTC2983_THERMOCOUPLE_SOFT_FAULT_MASK); +} + +static int ltc2983_common_fault_handler(const struct ltc2983_data *st, + const u32 result) +{ + return __ltc2983_fault_handler(st, result, + LTC2983_COMMON_HARD_FAULT_MASK, + LTC2983_COMMON_SOFT_FAULT_MASK); +} + +static int ltc2983_thermocouple_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_thermocouple *thermo = to_thermocouple(sensor); + u32 chan_val; + + chan_val = LTC2983_CHAN_ASSIGN(thermo->cold_junction_chan); + chan_val |= LTC2983_THERMOCOUPLE_CFG(thermo->sensor_config); + + if (thermo->custom) { + int ret; + + ret = __ltc2983_chan_custom_sensor_assign(st, thermo->custom, + &chan_val); + if (ret) + return ret; + } + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + +static int ltc2983_rtd_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_rtd *rtd = to_rtd(sensor); + u32 chan_val; + + chan_val = LTC2983_CHAN_ASSIGN(rtd->r_sense_chan); + chan_val |= LTC2983_RTD_CFG(rtd->sensor_config); + chan_val |= LTC2983_RTD_EXC_CURRENT(rtd->excitation_current); + chan_val |= LTC2983_RTD_CURVE(rtd->rtd_curve); + + if (rtd->custom) { + int ret; + + ret = __ltc2983_chan_custom_sensor_assign(st, rtd->custom, + &chan_val); + if (ret) + return ret; + } + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + +static int ltc2983_thermistor_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_thermistor *thermistor = to_thermistor(sensor); + u32 chan_val; + + chan_val = LTC2983_CHAN_ASSIGN(thermistor->r_sense_chan); + chan_val |= LTC2983_THERMISTOR_CFG(thermistor->sensor_config); + chan_val |= + LTC2983_THERMISTOR_EXC_CURRENT(thermistor->excitation_current); + + if (thermistor->custom) { + int ret; + + ret = __ltc2983_chan_custom_sensor_assign(st, + thermistor->custom, + &chan_val); + if (ret) + return ret; + } + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + +static int ltc2983_diode_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_diode *diode = to_diode(sensor); + u32 chan_val; + + chan_val = LTC2983_DIODE_CFG(diode->sensor_config); + chan_val |= LTC2983_DIODE_EXC_CURRENT(diode->excitation_current); + chan_val |= LTC2983_DIODE_IDEAL_FACTOR(diode->ideal_factor_value); + + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + +static int ltc2983_r_sense_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_rsense *rsense = to_rsense(sensor); + u32 chan_val; + + chan_val = LTC2983_R_SENSE_VAL(rsense->r_sense_val); + + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + +static int ltc2983_adc_assign_chan(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_adc *adc = to_adc(sensor); + u32 chan_val; + + chan_val = LTC2983_ADC_SINGLE_ENDED(adc->single_ended); + + return __ltc2983_chan_assign_common(st, sensor, chan_val); +} + +static struct ltc2983_sensor *ltc2983_thermocouple_new( + const struct device_node *child, + struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_thermocouple *thermo; + struct device_node *phandle; + u32 oc_current; + int ret; + + thermo = devm_kzalloc(&st->spi->dev, sizeof(*thermo), GFP_KERNEL); + if (!thermo) + return ERR_PTR(-ENOMEM); + + if (of_property_read_bool(child, "adi,single-ended")) + thermo->sensor_config = LTC2983_THERMOCOUPLE_SGL(1); + + ret = of_property_read_u32(child, "adi,sensor-oc-current-microamp", + &oc_current); + if (!ret) { + switch (oc_current) { + case 10: + thermo->sensor_config |= + LTC2983_THERMOCOUPLE_OC_CURR(0); + break; + case 100: + thermo->sensor_config |= + LTC2983_THERMOCOUPLE_OC_CURR(1); + break; + case 500: + thermo->sensor_config |= + LTC2983_THERMOCOUPLE_OC_CURR(2); + break; + case 1000: + thermo->sensor_config |= + LTC2983_THERMOCOUPLE_OC_CURR(3); + break; + default: + dev_err(&st->spi->dev, + "Invalid open circuit current:%u", oc_current); + return ERR_PTR(-EINVAL); + } + + thermo->sensor_config |= LTC2983_THERMOCOUPLE_OC_CHECK(1); + } + /* validate channel index */ + if (!(thermo->sensor_config & LTC2983_THERMOCOUPLE_DIFF_MASK) && + sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) { + dev_err(&st->spi->dev, + "Invalid chann:%d for differential thermocouple", + sensor->chan); + return ERR_PTR(-EINVAL); + } + + phandle = of_parse_phandle(child, "adi,cold-junction-handle", 0); + if (phandle) { + int ret; + + ret = of_property_read_u32(phandle, "reg", + &thermo->cold_junction_chan); + if (ret) { + /* + * This would be catched later but we can just return + * the error right away. + */ + dev_err(&st->spi->dev, "Property reg must be given\n"); + of_node_put(phandle); + return ERR_PTR(-EINVAL); + } + } + + /* check custom sensor */ + if (sensor->type == LTC2983_SENSOR_THERMOCOUPLE_CUSTOM) { + const char *propname = "adi,custom-thermocouple"; + + thermo->custom = __ltc2983_custom_sensor_new(st, child, + propname, false, + 16384, true); + if (IS_ERR(thermo->custom)) { + of_node_put(phandle); + return ERR_CAST(thermo->custom); + } + } + + /* set common parameters */ + thermo->sensor.fault_handler = ltc2983_thermocouple_fault_handler; + thermo->sensor.assign_chan = ltc2983_thermocouple_assign_chan; + + of_node_put(phandle); + return &thermo->sensor; +} + +static struct ltc2983_sensor *ltc2983_rtd_new(const struct device_node *child, + struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_rtd *rtd; + int ret = 0; + struct device *dev = &st->spi->dev; + struct device_node *phandle; + u32 excitation_current = 0, n_wires = 0; + + rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); + if (!rtd) + return ERR_PTR(-ENOMEM); + + phandle = of_parse_phandle(child, "adi,rsense-handle", 0); + if (!phandle) { + dev_err(dev, "Property adi,rsense-handle missing or invalid"); + return ERR_PTR(-EINVAL); + } + + ret = of_property_read_u32(phandle, "reg", &rtd->r_sense_chan); + if (ret) { + dev_err(dev, "Property reg must be given\n"); + goto fail; + } + + ret = of_property_read_u32(child, "adi,number-of-wires", &n_wires); + if (!ret) { + switch (n_wires) { + case 2: + rtd->sensor_config = LTC2983_RTD_N_WIRES(0); + break; + case 3: + rtd->sensor_config = LTC2983_RTD_N_WIRES(1); + break; + case 4: + rtd->sensor_config = LTC2983_RTD_N_WIRES(2); + break; + case 5: + /* 4 wires, Kelvin Rsense */ + rtd->sensor_config = LTC2983_RTD_N_WIRES(3); + break; + default: + dev_err(dev, "Invalid number of wires:%u\n", n_wires); + ret = -EINVAL; + goto fail; + } + } + + if (of_property_read_bool(child, "adi,rsense-share")) { + /* Current rotation is only available with rsense sharing */ + if (of_property_read_bool(child, "adi,current-rotate")) { + if (n_wires == 2 || n_wires == 3) { + dev_err(dev, + "Rotation not allowed for 2/3 Wire RTDs"); + ret = -EINVAL; + goto fail; + } + rtd->sensor_config |= LTC2983_RTD_C_ROTATE(1); + } else { + rtd->sensor_config |= LTC2983_RTD_R_SHARE(1); + } + } + /* + * rtd channel indexes are a bit more complicated to validate. + * For 4wire RTD with rotation, the channel selection cannot be + * >=19 since the chann + 1 is used in this configuration. + * For 4wire RTDs with kelvin rsense, the rsense channel cannot be + * <=1 since chanel - 1 and channel - 2 are used. + */ + if (rtd->sensor_config & LTC2983_RTD_4_WIRE_MASK) { + /* 4-wire */ + u8 min = LTC2983_DIFFERENTIAL_CHAN_MIN, + max = LTC2983_MAX_CHANNELS_NR; + + if (rtd->sensor_config & LTC2983_RTD_ROTATION_MASK) + max = LTC2983_MAX_CHANNELS_NR - 1; + + if (((rtd->sensor_config & LTC2983_RTD_KELVIN_R_SENSE_MASK) + == LTC2983_RTD_KELVIN_R_SENSE_MASK) && + (rtd->r_sense_chan <= min)) { + /* kelvin rsense*/ + dev_err(dev, + "Invalid rsense chann:%d to use in kelvin rsense", + rtd->r_sense_chan); + + ret = -EINVAL; + goto fail; + } + + if (sensor->chan < min || sensor->chan > max) { + dev_err(dev, "Invalid chann:%d for the rtd config", + sensor->chan); + + ret = -EINVAL; + goto fail; + } + } else { + /* same as differential case */ + if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) { + dev_err(&st->spi->dev, + "Invalid chann:%d for RTD", sensor->chan); + + ret = -EINVAL; + goto fail; + } + } + + /* check custom sensor */ + if (sensor->type == LTC2983_SENSOR_RTD_CUSTOM) { + rtd->custom = __ltc2983_custom_sensor_new(st, child, + "adi,custom-rtd", + false, 2048, false); + if (IS_ERR(rtd->custom)) { + of_node_put(phandle); + return ERR_CAST(rtd->custom); + } + } + + /* set common parameters */ + rtd->sensor.fault_handler = ltc2983_common_fault_handler; + rtd->sensor.assign_chan = ltc2983_rtd_assign_chan; + + ret = of_property_read_u32(child, "adi,excitation-current-microamp", + &excitation_current); + if (ret) { + /* default to 5uA */ + rtd->excitation_current = 1; + } else { + switch (excitation_current) { + case 5: + rtd->excitation_current = 0x01; + break; + case 10: + rtd->excitation_current = 0x02; + break; + case 25: + rtd->excitation_current = 0x03; + break; + case 50: + rtd->excitation_current = 0x04; + break; + case 100: + rtd->excitation_current = 0x05; + break; + case 250: + rtd->excitation_current = 0x06; + break; + case 500: + rtd->excitation_current = 0x07; + break; + case 1000: + rtd->excitation_current = 0x08; + break; + default: + dev_err(&st->spi->dev, + "Invalid value for excitation current(%u)", + excitation_current); + ret = -EINVAL; + goto fail; + } + } + + of_property_read_u32(child, "adi,rtd-curve", &rtd->rtd_curve); + + of_node_put(phandle); + return &rtd->sensor; +fail: + of_node_put(phandle); + return ERR_PTR(ret); +} + +static struct ltc2983_sensor *ltc2983_thermistor_new( + const struct device_node *child, + struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_thermistor *thermistor; + struct device *dev = &st->spi->dev; + struct device_node *phandle; + u32 excitation_current = 0; + int ret = 0; + + thermistor = devm_kzalloc(dev, sizeof(*thermistor), GFP_KERNEL); + if (!thermistor) + return ERR_PTR(-ENOMEM); + + phandle = of_parse_phandle(child, "adi,rsense-handle", 0); + if (!phandle) { + dev_err(dev, "Property adi,rsense-handle missing or invalid"); + return ERR_PTR(-EINVAL); + } + + ret = of_property_read_u32(phandle, "reg", &thermistor->r_sense_chan); + if (ret) { + dev_err(dev, "rsense channel must be configured...\n"); + goto fail; + } + + if (of_property_read_bool(child, "adi,single-ended")) { + thermistor->sensor_config = LTC2983_THERMISTOR_SGL(1); + } else if (of_property_read_bool(child, "adi,rsense-share")) { + /* rotation is only possible if sharing rsense */ + if (of_property_read_bool(child, "adi,current-rotate")) + thermistor->sensor_config = + LTC2983_THERMISTOR_C_ROTATE(1); + else + thermistor->sensor_config = + LTC2983_THERMISTOR_R_SHARE(1); + } + /* validate channel index */ + if (!(thermistor->sensor_config & LTC2983_THERMISTOR_DIFF_MASK) && + sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) { + dev_err(&st->spi->dev, + "Invalid chann:%d for differential thermistor", + sensor->chan); + ret = -EINVAL; + goto fail; + } + + /* check custom sensor */ + if (sensor->type >= LTC2983_SENSOR_THERMISTOR_STEINHART) { + bool steinhart = false; + const char *propname; + + if (sensor->type == LTC2983_SENSOR_THERMISTOR_STEINHART) { + steinhart = true; + propname = "adi,custom-steinhart"; + } else { + propname = "adi,custom-thermistor"; + } + + thermistor->custom = __ltc2983_custom_sensor_new(st, child, + propname, + steinhart, + 64, false); + if (IS_ERR(thermistor->custom)) { + of_node_put(phandle); + return ERR_CAST(thermistor->custom); + } + } + /* set common parameters */ + thermistor->sensor.fault_handler = ltc2983_common_fault_handler; + thermistor->sensor.assign_chan = ltc2983_thermistor_assign_chan; + + ret = of_property_read_u32(child, "adi,excitation-current-nanoamp", + &excitation_current); + if (ret) { + /* Auto range is not allowed for custom sensors */ + if (sensor->type >= LTC2983_SENSOR_THERMISTOR_STEINHART) + /* default to 1uA */ + thermistor->excitation_current = 0x03; + else + /* default to auto-range */ + thermistor->excitation_current = 0x0c; + } else { + switch (excitation_current) { + case 0: + /* auto range */ + if (sensor->type >= + LTC2983_SENSOR_THERMISTOR_STEINHART) { + dev_err(&st->spi->dev, + "Auto Range not allowed for custom sensors\n"); + ret = -EINVAL; + goto fail; + } + thermistor->excitation_current = 0x0c; + break; + case 250: + thermistor->excitation_current = 0x01; + break; + case 500: + thermistor->excitation_current = 0x02; + break; + case 1000: + thermistor->excitation_current = 0x03; + break; + case 5000: + thermistor->excitation_current = 0x04; + break; + case 10000: + thermistor->excitation_current = 0x05; + break; + case 25000: + thermistor->excitation_current = 0x06; + break; + case 50000: + thermistor->excitation_current = 0x07; + break; + case 100000: + thermistor->excitation_current = 0x08; + break; + case 250000: + thermistor->excitation_current = 0x09; + break; + case 500000: + thermistor->excitation_current = 0x0a; + break; + case 1000000: + thermistor->excitation_current = 0x0b; + break; + default: + dev_err(&st->spi->dev, + "Invalid value for excitation current(%u)", + excitation_current); + ret = -EINVAL; + goto fail; + } + } + + of_node_put(phandle); + return &thermistor->sensor; +fail: + of_node_put(phandle); + return ERR_PTR(ret); +} + +static struct ltc2983_sensor *ltc2983_diode_new( + const struct device_node *child, + const struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_diode *diode; + u32 temp = 0, excitation_current = 0; + int ret; + + diode = devm_kzalloc(&st->spi->dev, sizeof(*diode), GFP_KERNEL); + if (!diode) + return ERR_PTR(-ENOMEM); + + if (of_property_read_bool(child, "adi,single-ended")) + diode->sensor_config = LTC2983_DIODE_SGL(1); + + if (of_property_read_bool(child, "adi,three-conversion-cycles")) + diode->sensor_config |= LTC2983_DIODE_3_CONV_CYCLE(1); + + if (of_property_read_bool(child, "adi,average-on")) + diode->sensor_config |= LTC2983_DIODE_AVERAGE_ON(1); + + /* validate channel index */ + if (!(diode->sensor_config & LTC2983_DIODE_DIFF_MASK) && + sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) { + dev_err(&st->spi->dev, + "Invalid chann:%d for differential thermistor", + sensor->chan); + return ERR_PTR(-EINVAL); + } + /* set common parameters */ + diode->sensor.fault_handler = ltc2983_common_fault_handler; + diode->sensor.assign_chan = ltc2983_diode_assign_chan; + + ret = of_property_read_u32(child, "adi,excitation-current-microamp", + &excitation_current); + if (!ret) { + switch (excitation_current) { + case 10: + diode->excitation_current = 0x00; + break; + case 20: + diode->excitation_current = 0x01; + break; + case 40: + diode->excitation_current = 0x02; + break; + case 80: + diode->excitation_current = 0x03; + break; + default: + dev_err(&st->spi->dev, + "Invalid value for excitation current(%u)", + excitation_current); + return ERR_PTR(-EINVAL); + } + } + + of_property_read_u32(child, "adi,ideal-factor-value", &temp); + + /* 2^20 resolution */ + diode->ideal_factor_value = __convert_to_raw(temp, 1048576); + + return &diode->sensor; +} + +static struct ltc2983_sensor *ltc2983_r_sense_new(struct device_node *child, + struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_rsense *rsense; + int ret; + u32 temp; + + rsense = devm_kzalloc(&st->spi->dev, sizeof(*rsense), GFP_KERNEL); + if (!rsense) + return ERR_PTR(-ENOMEM); + + /* validate channel index */ + if (sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) { + dev_err(&st->spi->dev, "Invalid chann:%d for r_sense", + sensor->chan); + return ERR_PTR(-EINVAL); + } + + ret = of_property_read_u32(child, "adi,rsense-val-milli-ohms", &temp); + if (ret) { + dev_err(&st->spi->dev, "Property adi,rsense-val-milli-ohms missing\n"); + return ERR_PTR(-EINVAL); + } + /* + * Times 1000 because we have milli-ohms and __convert_to_raw + * expects scales of 1000000 which are used for all other + * properties. + * 2^10 resolution + */ + rsense->r_sense_val = __convert_to_raw((u64)temp * 1000, 1024); + + /* set common parameters */ + rsense->sensor.assign_chan = ltc2983_r_sense_assign_chan; + + return &rsense->sensor; +} + +static struct ltc2983_sensor *ltc2983_adc_new(struct device_node *child, + struct ltc2983_data *st, + const struct ltc2983_sensor *sensor) +{ + struct ltc2983_adc *adc; + + adc = devm_kzalloc(&st->spi->dev, sizeof(*adc), GFP_KERNEL); + if (!adc) + return ERR_PTR(-ENOMEM); + + if (of_property_read_bool(child, "adi,single-ended")) + adc->single_ended = true; + + if (!adc->single_ended && + sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) { + dev_err(&st->spi->dev, "Invalid chan:%d for differential adc\n", + sensor->chan); + return ERR_PTR(-EINVAL); + } + /* set common parameters */ + adc->sensor.assign_chan = ltc2983_adc_assign_chan; + adc->sensor.fault_handler = ltc2983_common_fault_handler; + + return &adc->sensor; +} + +static int ltc2983_chan_read(struct ltc2983_data *st, + const struct ltc2983_sensor *sensor, int *val) +{ + u32 start_conversion = 0; + int ret; + unsigned long time; + + start_conversion = LTC2983_STATUS_START(true); + start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan); + dev_dbg(&st->spi->dev, "Start conversion on chan:%d, status:%02X\n", + sensor->chan, start_conversion); + /* start conversion */ + ret = regmap_write(st->regmap, LTC2983_STATUS_REG, start_conversion); + if (ret) + return ret; + + reinit_completion(&st->completion); + /* + * wait for conversion to complete. + * 300 ms should be more than enough to complete the conversion. + * Depending on the sensor configuration, there are 2/3 conversions + * cycles of 82ms. + */ + time = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(300)); + if (!time) { + dev_warn(&st->spi->dev, "Conversion timed out\n"); + return -ETIMEDOUT; + } + + /* read the converted data */ + ret = regmap_bulk_read(st->regmap, LTC2983_CHAN_RES_ADDR(sensor->chan), + &st->temp, sizeof(st->temp)); + if (ret) + return ret; + + *val = __be32_to_cpu(st->temp); + + if (!(LTC2983_RES_VALID_MASK & *val)) { + dev_err(&st->spi->dev, "Invalid conversion detected\n"); + return -EIO; + } + + ret = sensor->fault_handler(st, *val); + if (ret) + return ret; + + *val = sign_extend32((*val) & LTC2983_DATA_MASK, LTC2983_DATA_SIGN_BIT); + return 0; +} + +static int ltc2983_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2983_data *st = iio_priv(indio_dev); + int ret; + + /* sanity check */ + if (chan->address >= st->num_channels) { + dev_err(&st->spi->dev, "Invalid chan address:%ld", + chan->address); + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + ret = ltc2983_chan_read(st, st->sensors[chan->address], val); + mutex_unlock(&st->lock); + return ret ?: IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* value in milli degrees */ + *val = 1000; + /* 2^10 */ + *val2 = 1024; + return IIO_VAL_FRACTIONAL; + case IIO_VOLTAGE: + /* value in millivolt */ + *val = 1000; + /* 2^21 */ + *val2 = 2097152; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static int ltc2983_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ltc2983_data *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static irqreturn_t ltc2983_irq_handler(int irq, void *data) +{ + struct ltc2983_data *st = data; + + complete(&st->completion); + return IRQ_HANDLED; +} + +#define LTC2983_CHAN(__type, index, __address) ({ \ + struct iio_chan_spec __chan = { \ + .type = __type, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = __address, \ + }; \ + __chan; \ +}) + +static int ltc2983_parse_dt(struct ltc2983_data *st) +{ + struct device_node *child; + struct device *dev = &st->spi->dev; + int ret = 0, chan = 0, channel_avail_mask = 0; + + of_property_read_u32(dev->of_node, "adi,mux-delay-config-us", + &st->mux_delay_config); + + of_property_read_u32(dev->of_node, "adi,filter-notch-freq", + &st->filter_notch_freq); + + st->num_channels = of_get_available_child_count(dev->of_node); + st->sensors = devm_kcalloc(dev, st->num_channels, sizeof(*st->sensors), + GFP_KERNEL); + if (!st->sensors) + return -ENOMEM; + + st->iio_channels = st->num_channels; + for_each_available_child_of_node(dev->of_node, child) { + struct ltc2983_sensor sensor; + + ret = of_property_read_u32(child, "reg", &sensor.chan); + if (ret) { + dev_err(dev, "reg property must given for child nodes\n"); + return ret; + } + + /* check if we have a valid channel */ + if (sensor.chan < LTC2983_MIN_CHANNELS_NR || + sensor.chan > LTC2983_MAX_CHANNELS_NR) { + dev_err(dev, + "chan:%d must be from 1 to 20\n", sensor.chan); + return -EINVAL; + } else if (channel_avail_mask & BIT(sensor.chan)) { + dev_err(dev, "chan:%d already in use\n", sensor.chan); + return -EINVAL; + } + + ret = of_property_read_u32(child, "adi,sensor-type", + &sensor.type); + if (ret) { + dev_err(dev, + "adi,sensor-type property must given for child nodes\n"); + return ret; + } + + dev_dbg(dev, "Create new sensor, type %u, chann %u", + sensor.type, + sensor.chan); + + if (sensor.type >= LTC2983_SENSOR_THERMOCOUPLE && + sensor.type <= LTC2983_SENSOR_THERMOCOUPLE_CUSTOM) { + st->sensors[chan] = ltc2983_thermocouple_new(child, st, + &sensor); + } else if (sensor.type >= LTC2983_SENSOR_RTD && + sensor.type <= LTC2983_SENSOR_RTD_CUSTOM) { + st->sensors[chan] = ltc2983_rtd_new(child, st, &sensor); + } else if (sensor.type >= LTC2983_SENSOR_THERMISTOR && + sensor.type <= LTC2983_SENSOR_THERMISTOR_CUSTOM) { + st->sensors[chan] = ltc2983_thermistor_new(child, st, + &sensor); + } else if (sensor.type == LTC2983_SENSOR_DIODE) { + st->sensors[chan] = ltc2983_diode_new(child, st, + &sensor); + } else if (sensor.type == LTC2983_SENSOR_SENSE_RESISTOR) { + st->sensors[chan] = ltc2983_r_sense_new(child, st, + &sensor); + /* don't add rsense to iio */ + st->iio_channels--; + } else if (sensor.type == LTC2983_SENSOR_DIRECT_ADC) { + st->sensors[chan] = ltc2983_adc_new(child, st, &sensor); + } else { + dev_err(dev, "Unknown sensor type %d\n", sensor.type); + return -EINVAL; + } + + if (IS_ERR(st->sensors[chan])) { + dev_err(dev, "Failed to create sensor %ld", + PTR_ERR(st->sensors[chan])); + return PTR_ERR(st->sensors[chan]); + } + /* set generic sensor parameters */ + st->sensors[chan]->chan = sensor.chan; + st->sensors[chan]->type = sensor.type; + + channel_avail_mask |= BIT(sensor.chan); + chan++; + } + + return 0; +} + +static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio) +{ + u32 iio_chan_t = 0, iio_chan_v = 0, chan, iio_idx = 0; + int ret; + unsigned long time; + + /* make sure the device is up */ + time = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(250)); + + if (!time) { + dev_err(&st->spi->dev, "Device startup timed out\n"); + return -ETIMEDOUT; + } + + st->iio_chan = devm_kzalloc(&st->spi->dev, + st->iio_channels * sizeof(*st->iio_chan), + GFP_KERNEL); + + if (!st->iio_chan) + return -ENOMEM; + + ret = regmap_update_bits(st->regmap, LTC2983_GLOBAL_CONFIG_REG, + LTC2983_NOTCH_FREQ_MASK, + LTC2983_NOTCH_FREQ(st->filter_notch_freq)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, LTC2983_MUX_CONFIG_REG, + st->mux_delay_config); + if (ret) + return ret; + + for (chan = 0; chan < st->num_channels; chan++) { + u32 chan_type = 0, *iio_chan; + + ret = st->sensors[chan]->assign_chan(st, st->sensors[chan]); + if (ret) + return ret; + /* + * The assign_iio flag is necessary for when the device is + * coming out of sleep. In that case, we just need to + * re-configure the device channels. + * We also don't assign iio channels for rsense. + */ + if (st->sensors[chan]->type == LTC2983_SENSOR_SENSE_RESISTOR || + !assign_iio) + continue; + + /* assign iio channel */ + if (st->sensors[chan]->type != LTC2983_SENSOR_DIRECT_ADC) { + chan_type = IIO_TEMP; + iio_chan = &iio_chan_t; + } else { + chan_type = IIO_VOLTAGE; + iio_chan = &iio_chan_v; + } + + /* + * add chan as the iio .address so that, we can directly + * reference the sensor given the iio_chan_spec + */ + st->iio_chan[iio_idx++] = LTC2983_CHAN(chan_type, (*iio_chan)++, + chan); + } + + return 0; +} + +static const struct regmap_range ltc2983_reg_ranges[] = { + regmap_reg_range(LTC2983_STATUS_REG, LTC2983_STATUS_REG), + regmap_reg_range(LTC2983_TEMP_RES_START_REG, LTC2983_TEMP_RES_END_REG), + regmap_reg_range(LTC2983_GLOBAL_CONFIG_REG, LTC2983_GLOBAL_CONFIG_REG), + regmap_reg_range(LTC2983_MULT_CHANNEL_START_REG, + LTC2983_MULT_CHANNEL_END_REG), + regmap_reg_range(LTC2983_MUX_CONFIG_REG, LTC2983_MUX_CONFIG_REG), + regmap_reg_range(LTC2983_CHAN_ASSIGN_START_REG, + LTC2983_CHAN_ASSIGN_END_REG), + regmap_reg_range(LTC2983_CUST_SENS_TBL_START_REG, + LTC2983_CUST_SENS_TBL_END_REG), +}; + +static const struct regmap_access_table ltc2983_reg_table = { + .yes_ranges = ltc2983_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(ltc2983_reg_ranges), +}; + +/* + * The reg_bits are actually 12 but the device needs the first *complete* + * byte for the command (R/W). + */ +static const struct regmap_config ltc2983_regmap_config = { + .reg_bits = 24, + .val_bits = 8, + .wr_table = <c2983_reg_table, + .rd_table = <c2983_reg_table, + .read_flag_mask = GENMASK(1, 0), + .write_flag_mask = BIT(1), +}; + +static const struct iio_info ltc2983_iio_info = { + .read_raw = ltc2983_read_raw, + .debugfs_reg_access = ltc2983_reg_access, +}; + +static int ltc2983_probe(struct spi_device *spi) +{ + struct ltc2983_data *st; + struct iio_dev *indio_dev; + const char *name = spi_get_device_id(spi)->name; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->regmap = devm_regmap_init_spi(spi, <c2983_regmap_config); + if (IS_ERR(st->regmap)) { + dev_err(&spi->dev, "Failed to initialize regmap\n"); + return PTR_ERR(st->regmap); + } + + mutex_init(&st->lock); + init_completion(&st->completion); + st->spi = spi; + spi_set_drvdata(spi, st); + + ret = ltc2983_parse_dt(st); + if (ret) + return ret; + /* + * let's request the irq now so it is used to sync the device + * startup in ltc2983_setup() + */ + ret = devm_request_irq(&spi->dev, spi->irq, ltc2983_irq_handler, + IRQF_TRIGGER_RISING, name, st); + if (ret) { + dev_err(&spi->dev, "failed to request an irq, %d", ret); + return ret; + } + + ret = ltc2983_setup(st, true); + if (ret) + return ret; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = name; + indio_dev->num_channels = st->iio_channels; + indio_dev->channels = st->iio_chan; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = <c2983_iio_info; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static int __maybe_unused ltc2983_resume(struct device *dev) +{ + struct ltc2983_data *st = spi_get_drvdata(to_spi_device(dev)); + int dummy; + + /* dummy read to bring the device out of sleep */ + regmap_read(st->regmap, LTC2983_STATUS_REG, &dummy); + /* we need to re-assign the channels */ + return ltc2983_setup(st, false); +} + +static int __maybe_unused ltc2983_suspend(struct device *dev) +{ + struct ltc2983_data *st = spi_get_drvdata(to_spi_device(dev)); + + return regmap_write(st->regmap, LTC2983_STATUS_REG, LTC2983_SLEEP); +} + +static SIMPLE_DEV_PM_OPS(ltc2983_pm_ops, ltc2983_suspend, ltc2983_resume); + +static const struct spi_device_id ltc2983_id_table[] = { + { "ltc2983" }, + {}, +}; +MODULE_DEVICE_TABLE(spi, ltc2983_id_table); + +static const struct of_device_id ltc2983_of_match[] = { + { .compatible = "adi,ltc2983" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltc2983_of_match); + +static struct spi_driver ltc2983_driver = { + .driver = { + .name = "ltc2983", + .of_match_table = ltc2983_of_match, + .pm = <c2983_pm_ops, + }, + .probe = ltc2983_probe, + .id_table = ltc2983_id_table, +}; + +module_spi_driver(ltc2983_driver); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices LTC2983 SPI Temperature sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index 2ab68282d0b6..d1360605209c 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -194,7 +194,7 @@ static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev, default: *val = 250; /* 1000 * 0.25 */ ret = IIO_VAL_INT; - }; + } break; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ae24d3ea68ea..420900852166 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1210,13 +1210,6 @@ config AB8500_DEBUG Select this option if you want debug information using the debug filesystem, debugfs. -config AB8500_GPADC - bool "ST-Ericsson AB8500 GPADC driver" - depends on AB8500_CORE && REGULATOR_AB8500 - default y - help - AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage - config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c1067ea46204..aed99f08739f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -177,7 +177,6 @@ obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o -obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o # ab8500-core need to come after db8500-prcmu (which provides the channel) obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index f4e26b6e5362..1a9a3414d4fa 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -84,7 +84,6 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> #ifdef CONFIG_DEBUG_FS #include <linux/string.h> @@ -103,11 +102,6 @@ static int num_irqs; static struct device_attribute **dev_attr; static char **event_name; -static u8 avg_sample = SAMPLE_16; -static u8 trig_edge = RISING_EDGE; -static u8 conv_type = ADC_SW; -static u8 trig_timer; - /** * struct ab8500_reg_range * @first: the first address of the range @@ -152,7 +146,6 @@ static struct hwreg_cfg hwreg_cfg = { }; #define AB8500_NAME_STRING "ab8500" -#define AB8500_ADC_NAME_STRING "gpadc" #define AB8500_NUM_BANKS AB8500_DEBUG_FIELD_LAST #define AB8500_REV_REG 0x80 @@ -1646,633 +1639,6 @@ report_write_failure: DEFINE_SHOW_ATTRIBUTE(ab8500_modem); -static int ab8500_gpadc_bat_ctrl_show(struct seq_file *s, void *p) -{ - int bat_ctrl_raw; - int bat_ctrl_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL, - avg_sample, trig_edge, trig_timer, conv_type); - bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, - BAT_CTRL, bat_ctrl_raw); - - seq_printf(s, "%d,0x%X\n", bat_ctrl_convert, bat_ctrl_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_bat_ctrl); - -static int ab8500_gpadc_btemp_ball_show(struct seq_file *s, void *p) -{ - int btemp_ball_raw; - int btemp_ball_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL, - avg_sample, trig_edge, trig_timer, conv_type); - btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, - btemp_ball_raw); - - seq_printf(s, "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_btemp_ball); - -static int ab8500_gpadc_main_charger_v_show(struct seq_file *s, void *p) -{ - int main_charger_v_raw; - int main_charger_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V, - avg_sample, trig_edge, trig_timer, conv_type); - main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, - MAIN_CHARGER_V, main_charger_v_raw); - - seq_printf(s, "%d,0x%X\n", main_charger_v_convert, main_charger_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_charger_v); - -static int ab8500_gpadc_acc_detect1_show(struct seq_file *s, void *p) -{ - int acc_detect1_raw; - int acc_detect1_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1, - avg_sample, trig_edge, trig_timer, conv_type); - acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1, - acc_detect1_raw); - - seq_printf(s, "%d,0x%X\n", acc_detect1_convert, acc_detect1_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_acc_detect1); - -static int ab8500_gpadc_acc_detect2_show(struct seq_file *s, void *p) -{ - int acc_detect2_raw; - int acc_detect2_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2, - avg_sample, trig_edge, trig_timer, conv_type); - acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc, - ACC_DETECT2, acc_detect2_raw); - - seq_printf(s, "%d,0x%X\n", acc_detect2_convert, acc_detect2_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_acc_detect2); - -static int ab8500_gpadc_aux1_show(struct seq_file *s, void *p) -{ - int aux1_raw; - int aux1_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1, - avg_sample, trig_edge, trig_timer, conv_type); - aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1, - aux1_raw); - - seq_printf(s, "%d,0x%X\n", aux1_convert, aux1_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_aux1); - -static int ab8500_gpadc_aux2_show(struct seq_file *s, void *p) -{ - int aux2_raw; - int aux2_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2, - avg_sample, trig_edge, trig_timer, conv_type); - aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2, - aux2_raw); - - seq_printf(s, "%d,0x%X\n", aux2_convert, aux2_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_aux2); - -static int ab8500_gpadc_main_bat_v_show(struct seq_file *s, void *p) -{ - int main_bat_v_raw; - int main_bat_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V, - avg_sample, trig_edge, trig_timer, conv_type); - main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, - main_bat_v_raw); - - seq_printf(s, "%d,0x%X\n", main_bat_v_convert, main_bat_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_bat_v); - -static int ab8500_gpadc_vbus_v_show(struct seq_file *s, void *p) -{ - int vbus_v_raw; - int vbus_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V, - avg_sample, trig_edge, trig_timer, conv_type); - vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V, - vbus_v_raw); - - seq_printf(s, "%d,0x%X\n", vbus_v_convert, vbus_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_vbus_v); - -static int ab8500_gpadc_main_charger_c_show(struct seq_file *s, void *p) -{ - int main_charger_c_raw; - int main_charger_c_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C, - avg_sample, trig_edge, trig_timer, conv_type); - main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, - MAIN_CHARGER_C, main_charger_c_raw); - - seq_printf(s, "%d,0x%X\n", main_charger_c_convert, main_charger_c_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_charger_c); - -static int ab8500_gpadc_usb_charger_c_show(struct seq_file *s, void *p) -{ - int usb_charger_c_raw; - int usb_charger_c_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C, - avg_sample, trig_edge, trig_timer, conv_type); - usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, - USB_CHARGER_C, usb_charger_c_raw); - - seq_printf(s, "%d,0x%X\n", usb_charger_c_convert, usb_charger_c_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_usb_charger_c); - -static int ab8500_gpadc_bk_bat_v_show(struct seq_file *s, void *p) -{ - int bk_bat_v_raw; - int bk_bat_v_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V, - avg_sample, trig_edge, trig_timer, conv_type); - bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, - BK_BAT_V, bk_bat_v_raw); - - seq_printf(s, "%d,0x%X\n", bk_bat_v_convert, bk_bat_v_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_bk_bat_v); - -static int ab8500_gpadc_die_temp_show(struct seq_file *s, void *p) -{ - int die_temp_raw; - int die_temp_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP, - avg_sample, trig_edge, trig_timer, conv_type); - die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP, - die_temp_raw); - - seq_printf(s, "%d,0x%X\n", die_temp_convert, die_temp_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_die_temp); - -static int ab8500_gpadc_usb_id_show(struct seq_file *s, void *p) -{ - int usb_id_raw; - int usb_id_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - usb_id_raw = ab8500_gpadc_read_raw(gpadc, USB_ID, - avg_sample, trig_edge, trig_timer, conv_type); - usb_id_convert = ab8500_gpadc_ad_to_voltage(gpadc, USB_ID, - usb_id_raw); - - seq_printf(s, "%d,0x%X\n", usb_id_convert, usb_id_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_usb_id); - -static int ab8540_gpadc_xtal_temp_show(struct seq_file *s, void *p) -{ - int xtal_temp_raw; - int xtal_temp_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - xtal_temp_raw = ab8500_gpadc_read_raw(gpadc, XTAL_TEMP, - avg_sample, trig_edge, trig_timer, conv_type); - xtal_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, XTAL_TEMP, - xtal_temp_raw); - - seq_printf(s, "%d,0x%X\n", xtal_temp_convert, xtal_temp_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_xtal_temp); - -static int ab8540_gpadc_vbat_true_meas_show(struct seq_file *s, void *p) -{ - int vbat_true_meas_raw; - int vbat_true_meas_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbat_true_meas_raw = ab8500_gpadc_read_raw(gpadc, VBAT_TRUE_MEAS, - avg_sample, trig_edge, trig_timer, conv_type); - vbat_true_meas_convert = - ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS, - vbat_true_meas_raw); - - seq_printf(s, "%d,0x%X\n", vbat_true_meas_convert, vbat_true_meas_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_true_meas); - -static int ab8540_gpadc_bat_ctrl_and_ibat_show(struct seq_file *s, void *p) -{ - int bat_ctrl_raw; - int bat_ctrl_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bat_ctrl_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_CTRL_AND_IBAT, - avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); - - bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, BAT_CTRL, - bat_ctrl_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - bat_ctrl_convert, bat_ctrl_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_bat_ctrl_and_ibat); - -static int ab8540_gpadc_vbat_meas_and_ibat_show(struct seq_file *s, void *p) -{ - int vbat_meas_raw; - int vbat_meas_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbat_meas_raw = ab8500_gpadc_double_read_raw(gpadc, VBAT_MEAS_AND_IBAT, - avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); - vbat_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, - vbat_meas_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - vbat_meas_convert, vbat_meas_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_meas_and_ibat); - -static int ab8540_gpadc_vbat_true_meas_and_ibat_show(struct seq_file *s, void *p) -{ - int vbat_true_meas_raw; - int vbat_true_meas_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbat_true_meas_raw = ab8500_gpadc_double_read_raw(gpadc, - VBAT_TRUE_MEAS_AND_IBAT, avg_sample, trig_edge, - trig_timer, conv_type, &ibat_raw); - vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, - VBAT_TRUE_MEAS, vbat_true_meas_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - vbat_true_meas_convert, vbat_true_meas_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_true_meas_and_ibat); - -static int ab8540_gpadc_bat_temp_and_ibat_show(struct seq_file *s, void *p) -{ - int bat_temp_raw; - int bat_temp_convert; - int ibat_raw; - int ibat_convert; - struct ab8500_gpadc *gpadc; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bat_temp_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_TEMP_AND_IBAT, - avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); - bat_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, - bat_temp_raw); - ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, - ibat_raw); - - seq_printf(s, - "%d,0x%X\n" - "%d,0x%X\n", - bat_temp_convert, bat_temp_raw, - ibat_convert, ibat_raw); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_bat_temp_and_ibat); - -static int ab8540_gpadc_otp_calib_show(struct seq_file *s, void *p) -{ - struct ab8500_gpadc *gpadc; - u16 vmain_l, vmain_h, btemp_l, btemp_h; - u16 vbat_l, vbat_h, ibat_l, ibat_h; - - gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - ab8540_gpadc_get_otp(gpadc, &vmain_l, &vmain_h, &btemp_l, &btemp_h, - &vbat_l, &vbat_h, &ibat_l, &ibat_h); - seq_printf(s, - "VMAIN_L:0x%X\n" - "VMAIN_H:0x%X\n" - "BTEMP_L:0x%X\n" - "BTEMP_H:0x%X\n" - "VBAT_L:0x%X\n" - "VBAT_H:0x%X\n" - "IBAT_L:0x%X\n" - "IBAT_H:0x%X\n", - vmain_l, vmain_h, btemp_l, btemp_h, - vbat_l, vbat_h, ibat_l, ibat_h); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_otp_calib); - -static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", avg_sample); - - return 0; -} - -static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_avg_sample_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_avg_sample_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_avg_sample; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_avg_sample); - if (err) - return err; - - if ((user_avg_sample == SAMPLE_1) || (user_avg_sample == SAMPLE_4) - || (user_avg_sample == SAMPLE_8) - || (user_avg_sample == SAMPLE_16)) { - avg_sample = (u8) user_avg_sample; - } else { - dev_err(dev, - "debugfs err input: should be egal to 1, 4, 8 or 16\n"); - return -EINVAL; - } - - return count; -} - -static const struct file_operations ab8500_gpadc_avg_sample_fops = { - .open = ab8500_gpadc_avg_sample_open, - .read = seq_read, - .write = ab8500_gpadc_avg_sample_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", trig_edge); - - return 0; -} - -static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_trig_edge_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_trig_edge_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_trig_edge; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_trig_edge); - if (err) - return err; - - if ((user_trig_edge == RISING_EDGE) - || (user_trig_edge == FALLING_EDGE)) { - trig_edge = (u8) user_trig_edge; - } else { - dev_err(dev, "Wrong input:\n" - "Enter 0. Rising edge\n" - "Enter 1. Falling edge\n"); - return -EINVAL; - } - - return count; -} - -static const struct file_operations ab8500_gpadc_trig_edge_fops = { - .open = ab8500_gpadc_trig_edge_open, - .read = seq_read, - .write = ab8500_gpadc_trig_edge_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", trig_timer); - - return 0; -} - -static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_trig_timer_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_trig_timer_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_trig_timer; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_trig_timer); - if (err) - return err; - - if (user_trig_timer & ~0xFF) { - dev_err(dev, - "debugfs error input: should be between 0 to 255\n"); - return -EINVAL; - } - - trig_timer = (u8) user_trig_timer; - - return count; -} - -static const struct file_operations ab8500_gpadc_trig_timer_fops = { - .open = ab8500_gpadc_trig_timer_open, - .read = seq_read, - .write = ab8500_gpadc_trig_timer_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p) -{ - seq_printf(s, "%d\n", conv_type); - - return 0; -} - -static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_conv_type_print, - inode->i_private); -} - -static ssize_t ab8500_gpadc_conv_type_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = ((struct seq_file *)(file->private_data))->private; - unsigned long user_conv_type; - int err; - - err = kstrtoul_from_user(user_buf, count, 0, &user_conv_type); - if (err) - return err; - - if ((user_conv_type == ADC_SW) - || (user_conv_type == ADC_HW)) { - conv_type = (u8) user_conv_type; - } else { - dev_err(dev, "Wrong input:\n" - "Enter 0. ADC SW conversion\n" - "Enter 1. ADC HW conversion\n"); - return -EINVAL; - } - - return count; -} - -static const struct file_operations ab8500_gpadc_conv_type_fops = { - .open = ab8500_gpadc_conv_type_open, - .read = seq_read, - .write = ab8500_gpadc_conv_type_write, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - /* * return length of an ASCII numerical value, 0 is string is not a * numerical value. @@ -2647,7 +2013,6 @@ static const struct file_operations ab8500_hwreg_fops = { static int ab8500_debug_probe(struct platform_device *plf) { struct dentry *ab8500_dir; - struct dentry *ab8500_gpadc_dir; struct ab8500 *ab8500; struct resource *res; @@ -2689,9 +2054,6 @@ static int ab8500_debug_probe(struct platform_device *plf) ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); - ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING, - ab8500_dir); - debugfs_create_file("all-bank-registers", S_IRUGO, ab8500_dir, &plf->dev, &ab8500_bank_registers_fops); debugfs_create_file("all-banks", S_IRUGO, ab8500_dir, @@ -2727,83 +2089,6 @@ static int ab8500_debug_probe(struct platform_device *plf) &plf->dev, &ab8500_hwreg_fops); debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP), ab8500_dir, &plf->dev, &ab8500_modem_fops); - debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_bat_ctrl_fops); - debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_btemp_ball_fops); - debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_main_charger_v_fops); - debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_acc_detect1_fops); - debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_acc_detect2_fops); - debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_aux1_fops); - debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_aux2_fops); - debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_main_bat_v_fops); - debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_vbus_v_fops); - debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_main_charger_c_fops); - debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_usb_charger_c_fops); - debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_bk_bat_v_fops); - debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_die_temp_fops); - debugfs_create_file("usb_id", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_usb_id_fops); - if (is_ab8540(ab8500)) { - debugfs_create_file("xtal_temp", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_xtal_temp_fops); - debugfs_create_file("vbattruemeas", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_vbat_true_meas_fops); - debugfs_create_file("batctrl_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_bat_ctrl_and_ibat_fops); - debugfs_create_file("vbatmeas_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_vbat_meas_and_ibat_fops); - debugfs_create_file("vbattruemeas_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_vbat_true_meas_and_ibat_fops); - debugfs_create_file("battemp_and_ibat", (S_IRUGO | S_IWUGO), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_bat_temp_and_ibat_fops); - debugfs_create_file("otp_calib", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_otp_calib_fops); - } - debugfs_create_file("avg_sample", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_avg_sample_fops); - debugfs_create_file("trig_edge", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_trig_edge_fops); - debugfs_create_file("trig_timer", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_trig_timer_fops); - debugfs_create_file("conv_type", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8500_gpadc_conv_type_fops); return 0; } diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c deleted file mode 100644 index 005f9ee34cd1..000000000000 --- a/drivers/mfd/ab8500-gpadc.c +++ /dev/null @@ -1,1075 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Arun R Murthy <arun.murthy@stericsson.com> - * Author: Daniel Willerud <daniel.willerud@stericsson.com> - * Author: Johan Palsson <johan.palsson@stericsson.com> - * Author: M'boumba Cedric Madianga - */ -#include <linux/init.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/delay.h> -#include <linux/pm_runtime.h> -#include <linux/platform_device.h> -#include <linux/completion.h> -#include <linux/regulator/consumer.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/mfd/abx500.h> -#include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> - -/* - * GPADC register offsets - * Bank : 0x0A - */ -#define AB8500_GPADC_CTRL1_REG 0x00 -#define AB8500_GPADC_CTRL2_REG 0x01 -#define AB8500_GPADC_CTRL3_REG 0x02 -#define AB8500_GPADC_AUTO_TIMER_REG 0x03 -#define AB8500_GPADC_STAT_REG 0x04 -#define AB8500_GPADC_MANDATAL_REG 0x05 -#define AB8500_GPADC_MANDATAH_REG 0x06 -#define AB8500_GPADC_AUTODATAL_REG 0x07 -#define AB8500_GPADC_AUTODATAH_REG 0x08 -#define AB8500_GPADC_MUX_CTRL_REG 0x09 -#define AB8540_GPADC_MANDATA2L_REG 0x09 -#define AB8540_GPADC_MANDATA2H_REG 0x0A -#define AB8540_GPADC_APEAAX_REG 0x10 -#define AB8540_GPADC_APEAAT_REG 0x11 -#define AB8540_GPADC_APEAAM_REG 0x12 -#define AB8540_GPADC_APEAAH_REG 0x13 -#define AB8540_GPADC_APEAAL_REG 0x14 - -/* - * OTP register offsets - * Bank : 0x15 - */ -#define AB8500_GPADC_CAL_1 0x0F -#define AB8500_GPADC_CAL_2 0x10 -#define AB8500_GPADC_CAL_3 0x11 -#define AB8500_GPADC_CAL_4 0x12 -#define AB8500_GPADC_CAL_5 0x13 -#define AB8500_GPADC_CAL_6 0x14 -#define AB8500_GPADC_CAL_7 0x15 -/* New calibration for 8540 */ -#define AB8540_GPADC_OTP4_REG_7 0x38 -#define AB8540_GPADC_OTP4_REG_6 0x39 -#define AB8540_GPADC_OTP4_REG_5 0x3A - -/* gpadc constants */ -#define EN_VINTCORE12 0x04 -#define EN_VTVOUT 0x02 -#define EN_GPADC 0x01 -#define DIS_GPADC 0x00 -#define AVG_1 0x00 -#define AVG_4 0x20 -#define AVG_8 0x40 -#define AVG_16 0x60 -#define ADC_SW_CONV 0x04 -#define EN_ICHAR 0x80 -#define BTEMP_PULL_UP 0x08 -#define EN_BUF 0x40 -#define DIS_ZERO 0x00 -#define GPADC_BUSY 0x01 -#define EN_FALLING 0x10 -#define EN_TRIG_EDGE 0x02 -#define EN_VBIAS_XTAL_TEMP 0x02 - -/* GPADC constants from AB8500 spec, UM0836 */ -#define ADC_RESOLUTION 1024 -#define ADC_CH_BTEMP_MIN 0 -#define ADC_CH_BTEMP_MAX 1350 -#define ADC_CH_DIETEMP_MIN 0 -#define ADC_CH_DIETEMP_MAX 1350 -#define ADC_CH_CHG_V_MIN 0 -#define ADC_CH_CHG_V_MAX 20030 -#define ADC_CH_ACCDET2_MIN 0 -#define ADC_CH_ACCDET2_MAX 2500 -#define ADC_CH_VBAT_MIN 2300 -#define ADC_CH_VBAT_MAX 4800 -#define ADC_CH_CHG_I_MIN 0 -#define ADC_CH_CHG_I_MAX 1500 -#define ADC_CH_BKBAT_MIN 0 -#define ADC_CH_BKBAT_MAX 3200 - -/* GPADC constants from AB8540 spec */ -#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ -#define ADC_CH_IBAT_MAX 6000 -#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ -#define ADC_CH_IBAT_MAX_V 60 -#define IBAT_VDROP_L (-56) /* mV */ -#define IBAT_VDROP_H 56 - -/* This is used to not lose precision when dividing to get gain and offset */ -#define CALIB_SCALE 1000 -/* - * Number of bits shift used to not lose precision - * when dividing to get ibat gain. - */ -#define CALIB_SHIFT_IBAT 20 - -/* Time in ms before disabling regulator */ -#define GPADC_AUDOSUSPEND_DELAY 1 - -#define CONVERSION_TIME 500 /* ms */ - -enum cal_channels { - ADC_INPUT_VMAIN = 0, - ADC_INPUT_BTEMP, - ADC_INPUT_VBAT, - ADC_INPUT_IBAT, - NBR_CAL_INPUTS, -}; - -/** - * struct adc_cal_data - Table for storing gain and offset for the calibrated - * ADC channels - * @gain: Gain of the ADC channel - * @offset: Offset of the ADC channel - */ -struct adc_cal_data { - s64 gain; - s64 offset; - u16 otp_calib_hi; - u16 otp_calib_lo; -}; - -/** - * struct ab8500_gpadc - AB8500 GPADC device information - * @dev: pointer to the struct device - * @node: a list of AB8500 GPADCs, hence prepared for - reentrance - * @parent: pointer to the struct ab8500 - * @ab8500_gpadc_complete: pointer to the struct completion, to indicate - * the completion of gpadc conversion - * @ab8500_gpadc_lock: structure of type mutex - * @regu: pointer to the struct regulator - * @irq_sw: interrupt number that is used by gpadc for Sw - * conversion - * @irq_hw: interrupt number that is used by gpadc for Hw - * conversion - * @cal_data array of ADC calibration data structs - */ -struct ab8500_gpadc { - struct device *dev; - struct list_head node; - struct ab8500 *parent; - struct completion ab8500_gpadc_complete; - struct mutex ab8500_gpadc_lock; - struct regulator *regu; - int irq_sw; - int irq_hw; - struct adc_cal_data cal_data[NBR_CAL_INPUTS]; -}; - -static LIST_HEAD(ab8500_gpadc_list); - -/** - * ab8500_gpadc_get() - returns a reference to the primary AB8500 GPADC - * (i.e. the first GPADC in the instance list) - */ -struct ab8500_gpadc *ab8500_gpadc_get(char *name) -{ - struct ab8500_gpadc *gpadc; - - list_for_each_entry(gpadc, &ab8500_gpadc_list, node) { - if (!strcmp(name, dev_name(gpadc->dev))) - return gpadc; - } - - return ERR_PTR(-ENOENT); -} -EXPORT_SYMBOL(ab8500_gpadc_get); - -/** - * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage - */ -int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, - int ad_value) -{ - int res; - - switch (channel) { - case MAIN_CHARGER_V: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) { - res = ADC_CH_CHG_V_MIN + (ADC_CH_CHG_V_MAX - - ADC_CH_CHG_V_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VMAIN].gain + - gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE; - break; - - case XTAL_TEMP: - case BAT_CTRL: - case BTEMP_BALL: - case ACC_DETECT1: - case ADC_AUX1: - case ADC_AUX2: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_BTEMP].gain) { - res = ADC_CH_BTEMP_MIN + (ADC_CH_BTEMP_MAX - - ADC_CH_BTEMP_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_BTEMP].gain + - gpadc->cal_data[ADC_INPUT_BTEMP].offset) / CALIB_SCALE; - break; - - case MAIN_BAT_V: - case VBAT_TRUE_MEAS: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) { - res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX - - ADC_CH_VBAT_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VBAT].gain + - gpadc->cal_data[ADC_INPUT_VBAT].offset) / CALIB_SCALE; - break; - - case DIE_TEMP: - res = ADC_CH_DIETEMP_MIN + - (ADC_CH_DIETEMP_MAX - ADC_CH_DIETEMP_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case ACC_DETECT2: - res = ADC_CH_ACCDET2_MIN + - (ADC_CH_ACCDET2_MAX - ADC_CH_ACCDET2_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case VBUS_V: - res = ADC_CH_CHG_V_MIN + - (ADC_CH_CHG_V_MAX - ADC_CH_CHG_V_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case MAIN_CHARGER_C: - case USB_CHARGER_C: - res = ADC_CH_CHG_I_MIN + - (ADC_CH_CHG_I_MAX - ADC_CH_CHG_I_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case BK_BAT_V: - res = ADC_CH_BKBAT_MIN + - (ADC_CH_BKBAT_MAX - ADC_CH_BKBAT_MIN) * ad_value / - ADC_RESOLUTION; - break; - - case IBAT_VIRTUAL_CHANNEL: - /* For some reason we don't have calibrated data */ - if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) { - res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX - - ADC_CH_IBAT_MIN) * ad_value / - ADC_RESOLUTION; - break; - } - /* Here we can use the calibrated data */ - res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain + - gpadc->cal_data[ADC_INPUT_IBAT].offset) - >> CALIB_SHIFT_IBAT; - break; - - default: - dev_err(gpadc->dev, - "unknown channel, not possible to convert\n"); - res = -EINVAL; - break; - - } - return res; -} -EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage); - -/** - * ab8500_gpadc_sw_hw_convert() - gpadc conversion - * @channel: analog channel to be converted to digital data - * @avg_sample: number of ADC sample to average - * @trig_egde: selected ADC trig edge - * @trig_timer: selected ADC trigger delay timer - * @conv_type: selected conversion type (HW or SW conversion) - * - * This function converts the selected analog i/p to digital - * data. - */ -int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type) -{ - int ad_value; - int voltage; - - ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample, - trig_edge, trig_timer, conv_type); - - /* On failure retry a second time */ - if (ad_value < 0) - ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample, - trig_edge, trig_timer, conv_type); - if (ad_value < 0) { - dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", - channel); - return ad_value; - } - - voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value); - if (voltage < 0) - dev_err(gpadc->dev, - "GPADC to voltage conversion failed ch: %d AD: 0x%x\n", - channel, ad_value); - - return voltage; -} -EXPORT_SYMBOL(ab8500_gpadc_sw_hw_convert); - -/** - * ab8500_gpadc_read_raw() - gpadc read - * @channel: analog channel to be read - * @avg_sample: number of ADC sample to average - * @trig_edge: selected trig edge - * @trig_timer: selected ADC trigger delay timer - * @conv_type: selected conversion type (HW or SW conversion) - * - * This function obtains the raw ADC value for an hardware conversion, - * this then needs to be converted by calling ab8500_gpadc_ad_to_voltage() - */ -int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type) -{ - return ab8500_gpadc_double_read_raw(gpadc, channel, avg_sample, - trig_edge, trig_timer, conv_type, - NULL); -} - -int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type, - int *ibat) -{ - int ret; - int looplimit = 0; - unsigned long completion_timeout; - u8 val, low_data, high_data, low_data2, high_data2; - u8 val_reg1 = 0; - unsigned int delay_min = 0; - unsigned int delay_max = 0; - u8 data_low_addr, data_high_addr; - - if (!gpadc) - return -ENODEV; - - /* check if convertion is supported */ - if ((gpadc->irq_sw < 0) && (conv_type == ADC_SW)) - return -ENOTSUPP; - if ((gpadc->irq_hw < 0) && (conv_type == ADC_HW)) - return -ENOTSUPP; - - mutex_lock(&gpadc->ab8500_gpadc_lock); - /* Enable VTVout LDO this is required for GPADC */ - pm_runtime_get_sync(gpadc->dev); - - /* Check if ADC is not busy, lock and proceed */ - do { - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); - if (ret < 0) - goto out; - if (!(val & GPADC_BUSY)) - break; - msleep(20); - } while (++looplimit < 10); - if (looplimit >= 10 && (val & GPADC_BUSY)) { - dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); - ret = -EINVAL; - goto out; - } - - /* Enable GPADC */ - val_reg1 |= EN_GPADC; - - /* Select the channel source and set average samples */ - switch (avg_sample) { - case SAMPLE_1: - val = channel | AVG_1; - break; - case SAMPLE_4: - val = channel | AVG_4; - break; - case SAMPLE_8: - val = channel | AVG_8; - break; - default: - val = channel | AVG_16; - break; - } - - if (conv_type == ADC_HW) { - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val); - val_reg1 |= EN_TRIG_EDGE; - if (trig_edge) - val_reg1 |= EN_FALLING; - } else - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: set avg samples failed\n"); - goto out; - } - - /* - * Enable ADC, buffering, select rising edge and enable ADC path - * charging current sense if it needed, ABB 3.0 needs some special - * treatment too. - */ - switch (channel) { - case MAIN_CHARGER_C: - case USB_CHARGER_C: - val_reg1 |= EN_BUF | EN_ICHAR; - break; - case BTEMP_BALL: - if (!is_ab8500_2p0_or_earlier(gpadc->parent)) { - val_reg1 |= EN_BUF | BTEMP_PULL_UP; - /* - * Delay might be needed for ABB8500 cut 3.0, if not, - * remove when hardware will be availible - */ - delay_min = 1000; /* Delay in micro seconds */ - delay_max = 10000; /* large range optimises sleepmode */ - break; - } - /* Intentional fallthrough */ - default: - val_reg1 |= EN_BUF; - break; - } - - /* Write configuration to register */ - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL1_REG, val_reg1); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: set Control register failed\n"); - goto out; - } - - if (delay_min != 0) - usleep_range(delay_min, delay_max); - - if (conv_type == ADC_HW) { - /* Set trigger delay timer */ - ret = abx500_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: trig timer failed\n"); - goto out; - } - completion_timeout = 2 * HZ; - data_low_addr = AB8500_GPADC_AUTODATAL_REG; - data_high_addr = AB8500_GPADC_AUTODATAH_REG; - } else { - /* Start SW conversion */ - ret = abx500_mask_and_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL1_REG, - ADC_SW_CONV, ADC_SW_CONV); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: start s/w conv failed\n"); - goto out; - } - completion_timeout = msecs_to_jiffies(CONVERSION_TIME); - data_low_addr = AB8500_GPADC_MANDATAL_REG; - data_high_addr = AB8500_GPADC_MANDATAH_REG; - } - - /* wait for completion of conversion */ - if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, - completion_timeout)) { - dev_err(gpadc->dev, - "timeout didn't receive GPADC conv interrupt\n"); - ret = -EINVAL; - goto out; - } - - /* Read the converted RAW data */ - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, data_low_addr, &low_data); - if (ret < 0) { - dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); - goto out; - } - - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, data_high_addr, &high_data); - if (ret < 0) { - dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n"); - goto out; - } - - /* Check if double convertion is required */ - if ((channel == BAT_CTRL_AND_IBAT) || - (channel == VBAT_MEAS_AND_IBAT) || - (channel == VBAT_TRUE_MEAS_AND_IBAT) || - (channel == BAT_TEMP_AND_IBAT)) { - - if (conv_type == ADC_HW) { - /* not supported */ - ret = -ENOTSUPP; - dev_err(gpadc->dev, - "gpadc_conversion: only SW double conversion supported\n"); - goto out; - } else { - /* Read the converted RAW data 2 */ - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG, - &low_data2); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: read sw low data 2 failed\n"); - goto out; - } - - ret = abx500_get_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG, - &high_data2); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: read sw high data 2 failed\n"); - goto out; - } - if (ibat != NULL) { - *ibat = (high_data2 << 8) | low_data2; - } else { - dev_warn(gpadc->dev, - "gpadc_conversion: ibat not stored\n"); - } - - } - } - - /* Disable GPADC */ - ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_CTRL1_REG, DIS_GPADC); - if (ret < 0) { - dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); - goto out; - } - - /* Disable VTVout LDO this is required for GPADC */ - pm_runtime_mark_last_busy(gpadc->dev); - pm_runtime_put_autosuspend(gpadc->dev); - - mutex_unlock(&gpadc->ab8500_gpadc_lock); - - return (high_data << 8) | low_data; - -out: - /* - * It has shown to be needed to turn off the GPADC if an error occurs, - * otherwise we might have problem when waiting for the busy bit in the - * GPADC status register to go low. In V1.1 there wait_for_completion - * seems to timeout when waiting for an interrupt.. Not seen in V2.0 - */ - (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_CTRL1_REG, DIS_GPADC); - pm_runtime_put(gpadc->dev); - mutex_unlock(&gpadc->ab8500_gpadc_lock); - dev_err(gpadc->dev, - "gpadc_conversion: Failed to AD convert channel %d\n", channel); - return ret; -} -EXPORT_SYMBOL(ab8500_gpadc_read_raw); - -/** - * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion - * @irq: irq number - * @data: pointer to the data passed during request irq - * - * This is a interrupt service routine for gpadc conversion completion. - * Notifies the gpadc completion is completed and the converted raw value - * can be read from the registers. - * Returns IRQ status(IRQ_HANDLED) - */ -static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc) -{ - struct ab8500_gpadc *gpadc = _gpadc; - - complete(&gpadc->ab8500_gpadc_complete); - - return IRQ_HANDLED; -} - -static int otp_cal_regs[] = { - AB8500_GPADC_CAL_1, - AB8500_GPADC_CAL_2, - AB8500_GPADC_CAL_3, - AB8500_GPADC_CAL_4, - AB8500_GPADC_CAL_5, - AB8500_GPADC_CAL_6, - AB8500_GPADC_CAL_7, -}; - -static int otp4_cal_regs[] = { - AB8540_GPADC_OTP4_REG_7, - AB8540_GPADC_OTP4_REG_6, - AB8540_GPADC_OTP4_REG_5, -}; - -static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) -{ - int i; - int ret[ARRAY_SIZE(otp_cal_regs)]; - u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; - int ret_otp4[ARRAY_SIZE(otp4_cal_regs)]; - u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; - int vmain_high, vmain_low; - int btemp_high, btemp_low; - int vbat_high, vbat_low; - int ibat_high, ibat_low; - s64 V_gain, V_offset, V2A_gain, V2A_offset; - struct ab8500 *ab8500; - - ab8500 = gpadc->parent; - - /* First we read all OTP registers and store the error code */ - for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { - ret[i] = abx500_get_register_interruptible(gpadc->dev, - AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); - if (ret[i] < 0) - dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", - __func__, otp_cal_regs[i]); - } - - /* - * The ADC calibration data is stored in OTP registers. - * The layout of the calibration data is outlined below and a more - * detailed description can be found in UM0836 - * - * vm_h/l = vmain_high/low - * bt_h/l = btemp_high/low - * vb_h/l = vbat_high/low - * - * Data bits 8500/9540: - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | | vm_h9 | vm_h8 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | - * |.......|.......|.......|.......|.......|.......|.......|....... - * - * Data bits 8540: - * OTP2 - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | - * |.......|.......|.......|.......|.......|.......|.......|....... - * - * Data bits 8540: - * OTP4 - * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | | ib_h9 | ib_h8 | ib_h7 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5 - * |.......|.......|.......|.......|.......|.......|.......|....... - * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 | - * - * - * Ideal output ADC codes corresponding to injected input voltages - * during manufacturing is: - * - * vmain_high: Vin = 19500mV / ADC ideal code = 997 - * vmain_low: Vin = 315mV / ADC ideal code = 16 - * btemp_high: Vin = 1300mV / ADC ideal code = 985 - * btemp_low: Vin = 21mV / ADC ideal code = 16 - * vbat_high: Vin = 4700mV / ADC ideal code = 982 - * vbat_low: Vin = 2380mV / ADC ideal code = 33 - */ - - if (is_ab8540(ab8500)) { - /* Calculate gain and offset for VMAIN if all reads succeeded*/ - if (!(ret[1] < 0 || ret[2] < 0)) { - vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | - ((gpadc_cal[2] & 0xC0) >> 6)); - vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); - - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi = - (u16)vmain_high; - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo = - (u16)vmain_low; - - gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * - (19500 - 315) / (vmain_high - vmain_low); - gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * - 19500 - (CALIB_SCALE * (19500 - 315) / - (vmain_high - vmain_low)) * vmain_high; - } else { - gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; - } - - /* Read IBAT calibration Data */ - for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) { - ret_otp4[i] = abx500_get_register_interruptible( - gpadc->dev, AB8500_OTP_EMUL, - otp4_cal_regs[i], &gpadc_otp4[i]); - if (ret_otp4[i] < 0) - dev_err(gpadc->dev, - "%s: read otp4 reg 0x%02x failed\n", - __func__, otp4_cal_regs[i]); - } - - /* Calculate gain and offset for IBAT if all reads succeeded */ - if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) { - ibat_high = (((gpadc_otp4[0] & 0x07) << 7) | - ((gpadc_otp4[1] & 0xFE) >> 1)); - ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | - ((gpadc_otp4[2] & 0xF8) >> 3)); - - gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi = - (u16)ibat_high; - gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo = - (u16)ibat_low; - - V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L) - << CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); - - V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) - - (((IBAT_VDROP_H - IBAT_VDROP_L) << - CALIB_SHIFT_IBAT) / (ibat_high - ibat_low)) - * ibat_high; - /* - * Result obtained is in mV (at a scale factor), - * we need to calculate gain and offset to get mA - */ - V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/ - (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V); - V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN - - ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V) - << CALIB_SHIFT_IBAT) - / (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V); - - gpadc->cal_data[ADC_INPUT_IBAT].gain = - V_gain * V2A_gain; - gpadc->cal_data[ADC_INPUT_IBAT].offset = - V_offset * V2A_gain + V2A_offset; - } else { - gpadc->cal_data[ADC_INPUT_IBAT].gain = 0; - } - - dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_IBAT].gain, - gpadc->cal_data[ADC_INPUT_IBAT].offset); - } else { - /* Calculate gain and offset for VMAIN if all reads succeeded */ - if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { - vmain_high = (((gpadc_cal[0] & 0x03) << 8) | - ((gpadc_cal[1] & 0x3F) << 2) | - ((gpadc_cal[2] & 0xC0) >> 6)); - vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); - - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi = - (u16)vmain_high; - gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo = - (u16)vmain_low; - - gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * - (19500 - 315) / (vmain_high - vmain_low); - - gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * - 19500 - (CALIB_SCALE * (19500 - 315) / - (vmain_high - vmain_low)) * vmain_high; - } else { - gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; - } - } - - /* Calculate gain and offset for BTEMP if all reads succeeded */ - if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { - btemp_high = (((gpadc_cal[2] & 0x01) << 9) | - (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); - btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); - - gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high; - gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low; - - gpadc->cal_data[ADC_INPUT_BTEMP].gain = - CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); - gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 - - (CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low)) - * btemp_high; - } else { - gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0; - } - - /* Calculate gain and offset for VBAT if all reads succeeded */ - if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { - vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); - vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); - - gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high; - gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low; - - gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE * - (4700 - 2380) / (vbat_high - vbat_low); - gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 - - (CALIB_SCALE * (4700 - 2380) / - (vbat_high - vbat_low)) * vbat_high; - } else { - gpadc->cal_data[ADC_INPUT_VBAT].gain = 0; - } - - dev_dbg(gpadc->dev, "VMAIN gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_VMAIN].gain, - gpadc->cal_data[ADC_INPUT_VMAIN].offset); - - dev_dbg(gpadc->dev, "BTEMP gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_BTEMP].gain, - gpadc->cal_data[ADC_INPUT_BTEMP].offset); - - dev_dbg(gpadc->dev, "VBAT gain %llu offset %llu\n", - gpadc->cal_data[ADC_INPUT_VBAT].gain, - gpadc->cal_data[ADC_INPUT_VBAT].offset); -} - -#ifdef CONFIG_PM -static int ab8500_gpadc_runtime_suspend(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - - regulator_disable(gpadc->regu); - return 0; -} - -static int ab8500_gpadc_runtime_resume(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - int ret; - - ret = regulator_enable(gpadc->regu); - if (ret) - dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret); - return ret; -} -#endif - -#ifdef CONFIG_PM_SLEEP -static int ab8500_gpadc_suspend(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - - mutex_lock(&gpadc->ab8500_gpadc_lock); - - pm_runtime_get_sync(dev); - - regulator_disable(gpadc->regu); - return 0; -} - -static int ab8500_gpadc_resume(struct device *dev) -{ - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - int ret; - - ret = regulator_enable(gpadc->regu); - if (ret) - dev_err(dev, "Failed to enable vtvout LDO: %d\n", ret); - - pm_runtime_mark_last_busy(gpadc->dev); - pm_runtime_put_autosuspend(gpadc->dev); - - mutex_unlock(&gpadc->ab8500_gpadc_lock); - return ret; -} -#endif - -static int ab8500_gpadc_probe(struct platform_device *pdev) -{ - int ret = 0; - struct ab8500_gpadc *gpadc; - - gpadc = devm_kzalloc(&pdev->dev, - sizeof(struct ab8500_gpadc), GFP_KERNEL); - if (!gpadc) - return -ENOMEM; - - gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); - if (gpadc->irq_sw < 0) - dev_err(gpadc->dev, "failed to get platform sw_conv_end irq\n"); - - gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); - if (gpadc->irq_hw < 0) - dev_err(gpadc->dev, "failed to get platform hw_conv_end irq\n"); - - gpadc->dev = &pdev->dev; - gpadc->parent = dev_get_drvdata(pdev->dev.parent); - mutex_init(&gpadc->ab8500_gpadc_lock); - - /* Initialize completion used to notify completion of conversion */ - init_completion(&gpadc->ab8500_gpadc_complete); - - /* Register interrupts */ - if (gpadc->irq_sw >= 0) { - ret = request_threaded_irq(gpadc->irq_sw, NULL, - ab8500_bm_gpadcconvend_handler, - IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, - "ab8500-gpadc-sw", - gpadc); - if (ret < 0) { - dev_err(gpadc->dev, - "Failed to register interrupt irq: %d\n", - gpadc->irq_sw); - goto fail; - } - } - - if (gpadc->irq_hw >= 0) { - ret = request_threaded_irq(gpadc->irq_hw, NULL, - ab8500_bm_gpadcconvend_handler, - IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT, - "ab8500-gpadc-hw", - gpadc); - if (ret < 0) { - dev_err(gpadc->dev, - "Failed to register interrupt irq: %d\n", - gpadc->irq_hw); - goto fail_irq; - } - } - - /* VTVout LDO used to power up ab8500-GPADC */ - gpadc->regu = devm_regulator_get(&pdev->dev, "vddadc"); - if (IS_ERR(gpadc->regu)) { - ret = PTR_ERR(gpadc->regu); - dev_err(gpadc->dev, "failed to get vtvout LDO\n"); - goto fail_irq; - } - - platform_set_drvdata(pdev, gpadc); - - ret = regulator_enable(gpadc->regu); - if (ret) { - dev_err(gpadc->dev, "Failed to enable vtvout LDO: %d\n", ret); - goto fail_enable; - } - - pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY); - pm_runtime_use_autosuspend(gpadc->dev); - pm_runtime_set_active(gpadc->dev); - pm_runtime_enable(gpadc->dev); - - ab8500_gpadc_read_calibration_data(gpadc); - list_add_tail(&gpadc->node, &ab8500_gpadc_list); - dev_dbg(gpadc->dev, "probe success\n"); - - return 0; - -fail_enable: -fail_irq: - free_irq(gpadc->irq_sw, gpadc); - free_irq(gpadc->irq_hw, gpadc); -fail: - return ret; -} - -static int ab8500_gpadc_remove(struct platform_device *pdev) -{ - struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev); - - /* remove this gpadc entry from the list */ - list_del(&gpadc->node); - /* remove interrupt - completion of Sw ADC conversion */ - if (gpadc->irq_sw >= 0) - free_irq(gpadc->irq_sw, gpadc); - if (gpadc->irq_hw >= 0) - free_irq(gpadc->irq_hw, gpadc); - - pm_runtime_get_sync(gpadc->dev); - pm_runtime_disable(gpadc->dev); - - regulator_disable(gpadc->regu); - - pm_runtime_set_suspended(gpadc->dev); - - pm_runtime_put_noidle(gpadc->dev); - - return 0; -} - -static const struct dev_pm_ops ab8500_gpadc_pm_ops = { - SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend, - ab8500_gpadc_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend, - ab8500_gpadc_resume) - -}; - -static struct platform_driver ab8500_gpadc_driver = { - .probe = ab8500_gpadc_probe, - .remove = ab8500_gpadc_remove, - .driver = { - .name = "ab8500-gpadc", - .pm = &ab8500_gpadc_pm_ops, - }, -}; - -static int __init ab8500_gpadc_init(void) -{ - return platform_driver_register(&ab8500_gpadc_driver); -} -subsys_initcall_sync(ab8500_gpadc_init); - -/** - * ab8540_gpadc_get_otp() - returns OTP values - * - */ -void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc, - u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h, - u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h) -{ - *vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo; - *vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi; - *btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo; - *btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi; - *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo; - *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi; - *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo; - *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi; -} diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index c84a7b1caeb6..27164a1d3c7c 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -629,7 +629,7 @@ config BATTERY_GAUGE_LTC2941 config AB8500_BM bool "AB8500 Battery Management Driver" - depends on AB8500_CORE && AB8500_GPADC + depends on AB8500_CORE && AB8500_GPADC && (IIO = y) help Say Y to include support for AB8500 battery management. diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 8fe81259bfd9..ad8c51ef8b8b 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -26,7 +26,7 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/iio/consumer.h> #define VTVOUT_V 1800 @@ -79,7 +79,8 @@ struct ab8500_btemp_ranges { * @bat_temp: Dispatched battery temperature in degree Celsius * @prev_bat_temp Last measured battery temperature in degree Celsius * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @adc_btemp_ball: ADC channel for the battery ball temperature + * @adc_bat_ctrl: ADC channel for the battery control * @fg: Pointer to the struct fg * @bm: Platform specific battery management information * @btemp_psy: Structure for BTEMP specific battery properties @@ -96,7 +97,8 @@ struct ab8500_btemp { int bat_temp; int prev_bat_temp; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *btemp_ball; + struct iio_channel *bat_ctrl; struct ab8500_fg *fg; struct abx500_bm_data *bm; struct power_supply *btemp_psy; @@ -177,13 +179,13 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, */ static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di) { - int vbtemp; + int vbtemp, ret; static int prev; - vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL); - if (vbtemp < 0) { + ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed, using previous value", + "%s ADC conversion failed, using previous value", __func__); return prev; } @@ -455,7 +457,7 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di, */ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) { - int temp; + int temp, ret; static int prev; int rbat, rntc, vntc; u8 id; @@ -480,10 +482,10 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) di->bm->bat_type[id].r_to_t_tbl, di->bm->bat_type[id].n_temp_tbl_elements, rbat); } else { - vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL); - if (vntc < 0) { + ret = iio_read_channel_processed(di->btemp_ball, &vntc); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed," + "%s ADC conversion failed," " using previous value\n", __func__); return prev; } @@ -1024,7 +1026,22 @@ static int ab8500_btemp_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + /* Get ADC channels */ + di->btemp_ball = devm_iio_channel_get(&pdev->dev, "btemp_ball"); + if (IS_ERR(di->btemp_ball)) { + if (PTR_ERR(di->btemp_ball) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get BTEMP BALL ADC channel\n"); + return PTR_ERR(di->btemp_ball); + } + di->bat_ctrl = devm_iio_channel_get(&pdev->dev, "bat_ctrl"); + if (IS_ERR(di->bat_ctrl)) { + if (PTR_ERR(di->bat_ctrl) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get BAT CTRL ADC channel\n"); + return PTR_ERR(di->bat_ctrl); + } di->initialized = false; diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index e51d0e72beea..c053ede47eb2 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -29,10 +29,10 @@ #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> #include <linux/mfd/abx500/ux500_chargalg.h> #include <linux/usb/otg.h> #include <linux/mutex.h> +#include <linux/iio/consumer.h> /* Charger constants */ #define NO_PW_CONN 0 @@ -233,7 +233,10 @@ struct ab8500_charger_max_usb_in_curr { * @current_stepping_sessions: * Counter for current stepping sessions * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @adc_main_charger_v ADC channel for main charger voltage + * @adc_main_charger_c ADC channel for main charger current + * @adc_vbus_v ADC channel for USB charger voltage + * @adc_usb_charger_c ADC channel for USB charger current * @bm: Platform specific battery management information * @flags: Structure for information about events triggered * @usb_state: Structure for usb stack information @@ -283,7 +286,10 @@ struct ab8500_charger { int is_aca_rid; atomic_t current_stepping_sessions; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *adc_main_charger_v; + struct iio_channel *adc_main_charger_c; + struct iio_channel *adc_vbus_v; + struct iio_channel *adc_usb_charger_c; struct abx500_bm_data *bm; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; @@ -459,13 +465,13 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di, */ static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di) { - int vch; + int vch, ret; /* Only measure voltage if the charger is connected */ if (di->ac.charger_connected) { - vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V); - if (vch < 0) - dev_err(di->dev, "%s gpadc conv failed,\n", __func__); + ret = iio_read_channel_processed(di->adc_main_charger_v, &vch); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { vch = 0; } @@ -510,13 +516,13 @@ static int ab8500_charger_ac_cv(struct ab8500_charger *di) */ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) { - int vch; + int vch, ret; /* Only measure voltage if the charger is connected */ if (di->usb.charger_connected) { - vch = ab8500_gpadc_convert(di->gpadc, VBUS_V); - if (vch < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_vbus_v, &vch); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { vch = 0; } @@ -532,13 +538,13 @@ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) */ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) { - int ich; + int ich, ret; /* Only measure current if the charger is online */ if (di->usb.charger_online) { - ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C); - if (ich < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_usb_charger_c, &ich); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { ich = 0; } @@ -554,13 +560,13 @@ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) */ static int ab8500_charger_get_ac_current(struct ab8500_charger *di) { - int ich; + int ich, ret; /* Only measure current if the charger is online */ if (di->ac.charger_online) { - ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C); - if (ich < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_main_charger_c, &ich); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { ich = 0; } @@ -3371,7 +3377,39 @@ static int ab8500_charger_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + /* Get ADC channels */ + di->adc_main_charger_v = devm_iio_channel_get(&pdev->dev, + "main_charger_v"); + if (IS_ERR(di->adc_main_charger_v)) { + if (PTR_ERR(di->adc_main_charger_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC main charger voltage\n"); + return PTR_ERR(di->adc_main_charger_v); + } + di->adc_main_charger_c = devm_iio_channel_get(&pdev->dev, + "main_charger_c"); + if (IS_ERR(di->adc_main_charger_c)) { + if (PTR_ERR(di->adc_main_charger_c) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC main charger current\n"); + return PTR_ERR(di->adc_main_charger_v); + } + di->adc_vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v"); + if (IS_ERR(di->adc_vbus_v)) { + if (PTR_ERR(di->adc_vbus_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC USB charger voltage\n"); + return PTR_ERR(di->adc_vbus_v); + } + di->adc_usb_charger_c = devm_iio_channel_get(&pdev->dev, + "usb_charger_c"); + if (IS_ERR(di->adc_usb_charger_c)) { + if (PTR_ERR(di->adc_usb_charger_c) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC USB charger current\n"); + return PTR_ERR(di->adc_usb_charger_c); + } /* initialize lock */ spin_lock_init(&di->usb_state.usb_lock); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 6fc4bc30644c..f7909dfd3b61 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -32,7 +32,7 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/iio/consumer.h> #include <linux/kernel.h> #define MILLI_TO_MICRO 1000 @@ -182,7 +182,7 @@ struct inst_curr_result_list { * @bat_cap: Structure for battery capacity specific parameters * @avg_cap: Average capacity filter * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @main_bat_v: ADC channel for the main battery voltage * @bm: Platform specific battery management information * @fg_psy: Structure that holds the FG specific battery properties * @fg_wq: Work queue for running the FG algorithm @@ -224,7 +224,7 @@ struct ab8500_fg { struct ab8500_fg_battery_capacity bat_cap; struct ab8500_fg_avg_cap avg_cap; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *main_bat_v; struct abx500_bm_data *bm; struct power_supply *fg_psy; struct workqueue_struct *fg_wq; @@ -829,13 +829,13 @@ exit: */ static int ab8500_fg_bat_voltage(struct ab8500_fg *di) { - int vbat; + int vbat, ret; static int prev; - vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V); - if (vbat < 0) { + ret = iio_read_channel_processed(di->main_bat_v, &vbat); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed, using previous value\n", + "%s ADC conversion failed, using previous value\n", __func__); return prev; } @@ -3066,7 +3066,14 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + di->main_bat_v = devm_iio_channel_get(&pdev->dev, "main_bat_v"); + if (IS_ERR(di->main_bat_v)) { + if (PTR_ERR(di->main_bat_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get main battery ADC channel\n"); + return PTR_ERR(di->main_bat_v); + } psy_cfg.supplied_to = supply_interface; psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e3a2518503ed..bd21655c37a6 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -508,15 +508,6 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. -config PWM_TIPWMSS - bool - default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM) - help - PWM Subsystem driver support for AM33xx SOC. - - PWM submodules require PWM config space access from submodule - drivers and require common parent driver support. - config PWM_TWL tristate "TWL4030/6030 PWM support" depends on TWL4030_CORE diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 26326adf71d7..9a475073dafc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -50,7 +50,6 @@ obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o -obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 038d6732c3fd..23026978a5a5 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -417,6 +417,10 @@ static int ad9834_probe(struct spi_device *spi) st = iio_priv(indio_dev); mutex_init(&st->lock); st->mclk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(st->mclk)) { + ret = PTR_ERR(st->mclk); + goto error_disable_reg; + } ret = clk_prepare_enable(st->mclk); if (ret) { diff --git a/include/linux/counter.h b/include/linux/counter.h index a061cdcdef7c..9dbd5df4cd34 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -290,53 +290,22 @@ struct counter_device_state { const struct attribute_group **groups; }; -/** - * struct counter_signal_read_value - Opaque Signal read value - * @buf: string representation of Signal read value - * @len: length of string in @buf - */ -struct counter_signal_read_value { - char *buf; - size_t len; -}; - -/** - * struct counter_count_read_value - Opaque Count read value - * @buf: string representation of Count read value - * @len: length of string in @buf - */ -struct counter_count_read_value { - char *buf; - size_t len; -}; - -/** - * struct counter_count_write_value - Opaque Count write value - * @buf: string representation of Count write value - */ -struct counter_count_write_value { - const char *buf; +enum counter_signal_value { + COUNTER_SIGNAL_LOW = 0, + COUNTER_SIGNAL_HIGH }; /** * struct counter_ops - Callbacks from driver * @signal_read: optional read callback for Signal attribute. The read * value of the respective Signal should be passed back via - * the val parameter. val points to an opaque type which - * should be set only by calling the - * counter_signal_read_value_set function from within the - * signal_read callback. + * the val parameter. * @count_read: optional read callback for Count attribute. The read * value of the respective Count should be passed back via - * the val parameter. val points to an opaque type which - * should be set only by calling the - * counter_count_read_value_set function from within the - * count_read callback. + * the val parameter. * @count_write: optional write callback for Count attribute. The write * value for the respective Count is passed in via the val - * parameter. val points to an opaque type which should be - * accessed only by calling the - * counter_count_write_value_get function. + * parameter. * @function_get: function to get the current count function mode. Returns * 0 on success and negative error code on error. The index * of the respective Count's returned function mode should @@ -346,7 +315,7 @@ struct counter_count_write_value { * Count's functions_list array. * @action_get: function to get the current action mode. Returns 0 on * success and negative error code on error. The index of - * the respective Signal's returned action mode should be + * the respective Synapse's returned action mode should be * passed back via the action parameter. * @action_set: function to set the action mode. action is the index of * the requested action mode from the respective Synapse's @@ -355,13 +324,11 @@ struct counter_count_write_value { struct counter_ops { int (*signal_read)(struct counter_device *counter, struct counter_signal *signal, - struct counter_signal_read_value *val); + enum counter_signal_value *val); int (*count_read)(struct counter_device *counter, - struct counter_count *count, - struct counter_count_read_value *val); + struct counter_count *count, unsigned long *val); int (*count_write)(struct counter_device *counter, - struct counter_count *count, - struct counter_count_write_value *val); + struct counter_count *count, unsigned long val); int (*function_get)(struct counter_device *counter, struct counter_count *count, size_t *function); int (*function_set)(struct counter_device *counter, @@ -477,29 +444,6 @@ struct counter_device { void *priv; }; -enum counter_signal_level { - COUNTER_SIGNAL_LEVEL_LOW = 0, - COUNTER_SIGNAL_LEVEL_HIGH -}; - -enum counter_signal_value_type { - COUNTER_SIGNAL_LEVEL = 0 -}; - -enum counter_count_value_type { - COUNTER_COUNT_POSITION = 0, -}; - -void counter_signal_read_value_set(struct counter_signal_read_value *const val, - const enum counter_signal_value_type type, - void *const data); -void counter_count_read_value_set(struct counter_count_read_value *const val, - const enum counter_count_value_type type, - void *const data); -int counter_count_write_value_get(void *const data, - const enum counter_count_value_type type, - const struct counter_count_write_value *const val); - int counter_register(struct counter_device *const counter); void counter_unregister(struct counter_device *const counter); int devm_counter_register(struct device *dev, diff --git a/include/linux/mfd/abx500/ab8500-gpadc.h b/include/linux/mfd/abx500/ab8500-gpadc.h deleted file mode 100644 index 836c944abe2e..000000000000 --- a/include/linux/mfd/abx500/ab8500-gpadc.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2010 ST-Ericsson SA - * - * Author: Arun R Murthy <arun.murthy@stericsson.com> - * Author: Daniel Willerud <daniel.willerud@stericsson.com> - * Author: M'boumba Cedric Madianga <cedric.madianga@stericsson.com> - */ - -#ifndef _AB8500_GPADC_H -#define _AB8500_GPADC_H - -/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2 - * and ADCHwSel[4:0] in GPADCCtrl3 ) */ -#define BAT_CTRL 0x01 -#define BTEMP_BALL 0x02 -#define MAIN_CHARGER_V 0x03 -#define ACC_DETECT1 0x04 -#define ACC_DETECT2 0x05 -#define ADC_AUX1 0x06 -#define ADC_AUX2 0x07 -#define MAIN_BAT_V 0x08 -#define VBUS_V 0x09 -#define MAIN_CHARGER_C 0x0A -#define USB_CHARGER_C 0x0B -#define BK_BAT_V 0x0C -#define DIE_TEMP 0x0D -#define USB_ID 0x0E -#define XTAL_TEMP 0x12 -#define VBAT_TRUE_MEAS 0x13 -#define BAT_CTRL_AND_IBAT 0x1C -#define VBAT_MEAS_AND_IBAT 0x1D -#define VBAT_TRUE_MEAS_AND_IBAT 0x1E -#define BAT_TEMP_AND_IBAT 0x1F - -/* Virtual channel used only for ibat convertion to ampere - * Battery current conversion (ibat) cannot be requested as a single conversion - * but it is always in combination with other input requests - */ -#define IBAT_VIRTUAL_CHANNEL 0xFF - -#define SAMPLE_1 1 -#define SAMPLE_4 4 -#define SAMPLE_8 8 -#define SAMPLE_16 16 -#define RISING_EDGE 0 -#define FALLING_EDGE 1 - -/* Arbitrary ADC conversion type constants */ -#define ADC_SW 0 -#define ADC_HW 1 - -struct ab8500_gpadc; - -struct ab8500_gpadc *ab8500_gpadc_get(char *name); -int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type); -static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) -{ - return ab8500_gpadc_sw_hw_convert(gpadc, channel, - SAMPLE_16, 0, 0, ADC_SW); -} - -int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type); -int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel, - u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type, - int *ibat); -int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, - u8 channel, int ad_value); -void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc, - u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h, - u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h); - -#endif /* _AB8500_GPADC_H */ diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h index 30929c22227d..e40b28ca892e 100644 --- a/include/linux/platform_data/st_sensors_pdata.h +++ b/include/linux/platform_data/st_sensors_pdata.h @@ -18,12 +18,14 @@ * @open_drain: set the interrupt line to be open drain if possible. * @spi_3wire: enable spi-3wire mode. * @pullups: enable/disable i2c controller pullup resistors. + * @wakeup_source: enable/disable device as wakeup generator. */ struct st_sensors_platform_data { u8 drdy_int_pin; bool open_drain; bool spi_3wire; bool pullups; + bool wakeup_source; }; #endif /* ST_SENSORS_PDATA_H */ |