Pull char/misc and other driver subsystem updates from Greg KH: "Here is the big set of char/misc and a number of other driver subsystem updates for 6.9-rc1. Included in here are: - IIO driver updates, loads of new ones and evolution of existing ones - coresight driver updates - const cleanups for many driver subsystems - speakup driver additions - platform remove callback void cleanups - mei driver updates - mhi driver updates - cdx driver updates for MSI interrupt handling - nvmem driver updates - other smaller driver updates and cleanups, full details in the shortlog All of these have been in linux-next for a long time with no reported issue, other than a build warning for the speakup driver" The build warning hits clang and is a gcc (and C23) extension, and is fixed up in the merge. Link: https://lore.kernel.org/all/20240321134831.GA2762840@dev-arch.thelio-3990X/ * tag 'char-misc-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (279 commits) binder: remove redundant variable page_addr uio_dmem_genirq: UIO_MEM_DMA_COHERENT conversion uio_pruss: UIO_MEM_DMA_COHERENT conversion cnic,bnx2,bnx2x: use UIO_MEM_DMA_COHERENT uio: introduce UIO_MEM_DMA_COHERENT type cdx: add MSI support for CDX bus pps: use cflags-y instead of EXTRA_CFLAGS speakup: Add /dev/synthu device speakup: Fix 8bit characters from direct synth parport: sunbpp: Convert to platform remove callback returning void parport: amiga: Convert to platform remove callback returning void char: xillybus: Convert to platform remove callback returning void vmw_balloon: change maintainership MAINTAINERS: change the maintainer for hpilo driver char: xilinx_hwicap: Fix NULL vs IS_ERR() bug hpet: remove hpets::hp_clocksource platform: goldfish: move the separate 'default' propery for CONFIG_GOLDFISH char: xilinx_hwicap: drop casting to void in dev_set_drvdata greybus: move is_gb_* functions out of greybus.h greybus: Remove usage of the deprecated ida_simple_xx() API ...
Unified · Split
| @@ -439,6 +439,8 @@ Mukesh Ojha <quic_mojha@quicinc.com> <mojha@codeaurora.org> · 438 unmodified lines | ||
| 439 | 439 | Muna Sinada <quic_msinada@quicinc.com> <msinada@codeaurora.org> |
| 440 | 440 | Murali Nalajala <quic_mnalajal@quicinc.com> <mnalajal@codeaurora.org> |
| 441 | 441 | Mythri P K <mythripk@ti.com> |
| 442 | +Nadav Amit <nadav.amit@gmail.com> <namit@vmware.com> | |
| 443 | +Nadav Amit <nadav.amit@gmail.com> <namit@cs.technion.ac.il> | |
| 442 | 444 | Nadia Yvette Chambers <nyc@holomorphy.com> William Lee Irwin III <wli@holomorphy.com> |
| 443 | 445 | Naoya Horiguchi <naoya.horiguchi@nec.com> <n-horiguchi@ah.jp.nec.com> |
| 444 | 446 | Nathan Chancellor <nathan@kernel.org> <natechancellor@gmail.com> |
| @@ -170,3 +170,90 @@ Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_t · 169 unmodified lines | ||
| 170 | 170 | Description: |
| 171 | 171 | (RW) Set/Get the MSR(mux select register) for the DSB subunit |
| 172 | 172 | TPDM. |
| 173 | + | |
| 174 | +What: /sys/bus/coresight/devices/<tpdm-name>/cmb_mode | |
| 175 | +Date: January 2024 | |
| 176 | +KernelVersion 6.9 | |
| 177 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 178 | +Description: (Write) Set the data collection mode of CMB tpdm. Continuous | |
| 179 | + change creates CMB data set elements on every CMBCLK edge. | |
| 180 | + Trace-on-change creates CMB data set elements only when a new | |
| 181 | + data set element differs in value from the previous element | |
| 182 | + in a CMB data set. | |
| 183 | + | |
| 184 | + Accepts only one of the 2 values - 0 or 1. | |
| 185 | + 0 : Continuous CMB collection mode. | |
| 186 | + 1 : Trace-on-change CMB collection mode. | |
| 187 | + | |
| 188 | +What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_patt/xpr[0:1] | |
| 189 | +Date: January 2024 | |
| 190 | +KernelVersion 6.9 | |
| 191 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 192 | +Description: | |
| 193 | + (RW) Set/Get the value of the trigger pattern for the CMB | |
| 194 | + subunit TPDM. | |
| 195 | + | |
| 196 | +What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_patt/xpmr[0:1] | |
| 197 | +Date: January 2024 | |
| 198 | +KernelVersion 6.9 | |
| 199 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 200 | +Description: | |
| 201 | + (RW) Set/Get the mask of the trigger pattern for the CMB | |
| 202 | + subunit TPDM. | |
| 203 | + | |
| 204 | +What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpr[0:1] | |
| 205 | +Date: January 2024 | |
| 206 | +KernelVersion 6.9 | |
| 207 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 208 | +Description: | |
| 209 | + (RW) Set/Get the value of the pattern for the CMB subunit TPDM. | |
| 210 | + | |
| 211 | +What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpmr[0:1] | |
| 212 | +Date: January 2024 | |
| 213 | +KernelVersion 6.9 | |
| 214 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 215 | +Description: | |
| 216 | + (RW) Set/Get the mask of the pattern for the CMB subunit TPDM. | |
| 217 | + | |
| 218 | +What: /sys/bus/coresight/devices/<tpdm-name>/cmb_patt/enable_ts | |
| 219 | +Date: January 2024 | |
| 220 | +KernelVersion 6.9 | |
| 221 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 222 | +Description: | |
| 223 | + (Write) Set the pattern timestamp of CMB tpdm. Read | |
| 224 | + the pattern timestamp of CMB tpdm. | |
| 225 | + | |
| 226 | + Accepts only one of the 2 values - 0 or 1. | |
| 227 | + 0 : Disable CMB pattern timestamp. | |
| 228 | + 1 : Enable CMB pattern timestamp. | |
| 229 | + | |
| 230 | +What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_ts | |
| 231 | +Date: January 2024 | |
| 232 | +KernelVersion 6.9 | |
| 233 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 234 | +Description: | |
| 235 | + (RW) Set/Get the trigger timestamp of the CMB for tpdm. | |
| 236 | + | |
| 237 | + Accepts only one of the 2 values - 0 or 1. | |
| 238 | + 0 : Set the CMB trigger type to false | |
| 239 | + 1 : Set the CMB trigger type to true | |
| 240 | + | |
| 241 | +What: /sys/bus/coresight/devices/<tpdm-name>/cmb_ts_all | |
| 242 | +Date: January 2024 | |
| 243 | +KernelVersion 6.9 | |
| 244 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 245 | +Description: | |
| 246 | + (RW) Read or write the status of timestamp upon all interface. | |
| 247 | + Only value 0 and 1 can be written to this node. Set this node to 1 to requeset | |
| 248 | + timestamp to all trace packet. | |
| 249 | + Accepts only one of the 2 values - 0 or 1. | |
| 250 | + 0 : Disable the timestamp of all trace packets. | |
| 251 | + 1 : Enable the timestamp of all trace packets. | |
| 252 | + | |
| 253 | +What: /sys/bus/coresight/devices/<tpdm-name>/cmb_msr/msr[0:31] | |
| 254 | +Date: January 2024 | |
| 255 | +KernelVersion 6.9 | |
| 256 | +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> | |
| 257 | +Description: | |
| 258 | + (RW) Set/Get the MSR(mux select register) for the CMB subunit | |
| 259 | + TPDM. | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistorY | |
| 2 | +KernelVersion: 6.7 | |
| 3 | +Contact: linux-iio@vger.kernel.org | |
| 4 | +Description: | |
| 5 | + The value of the shunt resistor may be known only at runtime | |
| 6 | + and set by a client application. This attribute allows to | |
| 7 | + set its value in micro-ohms. X is the IIO index of the device. | |
| 8 | + Y is the channel number. The value is used to calculate | |
| 9 | + current, power and accumulated energy. | |
| @@ -44,14 +44,21 @@ properties: · 43 unmodified lines | ||
| 44 | 44 | minItems: 1 |
| 45 | 45 | maxItems: 2 |
| 46 | 46 | |
| 47 | − qcom,dsb-element-size: | |
| 47 | + qcom,dsb-element-bits: | |
| 48 | 48 | description: |
| 49 | 49 | Specifies the DSB(Discrete Single Bit) element size supported by |
| 50 | 50 | the monitor. The associated aggregator will read this size before it |
| 51 | 51 | is enabled. DSB element size currently only supports 32-bit and 64-bit. |
| 52 | − $ref: /schemas/types.yaml#/definitions/uint8 | |
| 53 | 52 | enum: [32, 64] |
| 54 | 53 | |
| 54 | + qcom,cmb-element-bits: | |
| 55 | + description: | |
| 56 | + Specifies the CMB(Continuous Multi-Bit) element size supported by | |
| 57 | + the monitor. The associated aggregator will read this size before it | |
| 58 | + is enabled. CMB element size currently only supports 8-bit, 32-bit | |
| 59 | + and 64-bit. | |
| 60 | + enum: [8, 32, 64] | |
| 61 | + | |
| 55 | 62 | qcom,dsb-msrs-num: |
| 56 | 63 | description: |
| 57 | 64 | Specifies the number of DSB(Discrete Single Bit) MSR(mux select register) |
| @@ -61,6 +68,15 @@ properties: · 3 unmodified lines | ||
| 61 | 68 | minimum: 0 |
| 62 | 69 | maximum: 32 |
| 63 | 70 | |
| 71 | + qcom,cmb-msrs-num: | |
| 72 | + description: | |
| 73 | + Specifies the number of CMB MSR(mux select register) registers supported | |
| 74 | + by the monitor. If this property is not configured or set to 0, it means | |
| 75 | + this TPDM doesn't support CMB MSR. | |
| 76 | + $ref: /schemas/types.yaml#/definitions/uint32 | |
| 77 | + minimum: 0 | |
| 78 | + maximum: 32 | |
| 79 | + | |
| 64 | 80 | clocks: |
| 65 | 81 | maxItems: 1 |
| 66 | 82 | |
| @@ -94,7 +110,7 @@ examples: · 27 unmodified lines | ||
| 94 | 110 | compatible = "qcom,coresight-tpdm", "arm,primecell"; |
| 95 | 111 | reg = <0x0684c000 0x1000>; |
| 96 | 112 | |
| 97 | − qcom,dsb-element-size = /bits/ 8 <32>; | |
| 113 | + qcom,dsb-element-bits = <32>; | |
| 98 | 114 | qcom,dsb-msrs-num = <16>; |
| 99 | 115 | |
| 100 | 116 | clocks = <&aoss_qmp>; |
| @@ -110,4 +126,22 @@ examples: · 9 unmodified lines | ||
| 110 | 126 | }; |
| 111 | 127 | }; |
| 112 | 128 | |
| 129 | + tpdm@6c29000 { | |
| 130 | + compatible = "qcom,coresight-tpdm", "arm,primecell"; | |
| 131 | + reg = <0x06c29000 0x1000>; | |
| 132 | + | |
| 133 | + qcom,cmb-element-bits = <64>; | |
| 134 | + qcom,cmb-msrs-num = <32>; | |
| 135 | + | |
| 136 | + clocks = <&aoss_qmp>; | |
| 137 | + clock-names = "apb_pclk"; | |
| 138 | + | |
| 139 | + out-ports { | |
| 140 | + port { | |
| 141 | + tpdm_ipcc_out_funnel_center: endpoint { | |
| 142 | + remote-endpoint = <&funnel_center_in_tpdm_ipcc>; | |
| 143 | + }; | |
| 144 | + }; | |
| 145 | + }; | |
| 146 | + }; | |
| 113 | 147 | ... |
| @@ -22,7 +22,6 @@ properties: · 21 unmodified lines | ||
| 22 | 22 | maxItems: 1 |
| 23 | 23 | |
| 24 | 24 | label: |
| 25 | − $ref: /schemas/types.yaml#/definitions/string | |
| 26 | 25 | description: Unique name to identify which channel this is. |
| 27 | 26 | |
| 28 | 27 | bipolar: |
| @@ -44,6 +44,9 @@ properties: · 43 unmodified lines | ||
| 44 | 44 | Pin that controls the powerdown mode of the device. |
| 45 | 45 | maxItems: 1 |
| 46 | 46 | |
| 47 | + io-backends: | |
| 48 | + maxItems: 1 | |
| 49 | + | |
| 47 | 50 | reset-gpios: |
| 48 | 51 | description: |
| 49 | 52 | Reset pin for the device. |
| @@ -68,6 +71,7 @@ examples: · 18 unmodified lines | ||
| 68 | 71 | reg = <0>; |
| 69 | 72 | clocks = <&adc_clk>; |
| 70 | 73 | clock-names = "adc-clk"; |
| 74 | + io-backends = <&iio_backend>; | |
| 71 | 75 | }; |
| 72 | 76 | }; |
| 73 | 77 | ... |
| @@ -39,12 +39,15 @@ properties: · 38 unmodified lines | ||
| 39 | 39 | $ref: /schemas/types.yaml#/definitions/phandle |
| 40 | 40 | description: |
| 41 | 41 | A reference to a the actual ADC to which this FPGA ADC interfaces to. |
| 42 | + deprecated: true | |
| 43 | + | |
| 44 | + '#io-backend-cells': | |
| 45 | + const: 0 | |
| 42 | 46 | |
| 43 | 47 | required: |
| 44 | 48 | - compatible |
| 45 | 49 | - dmas |
| 46 | 50 | - reg |
| 47 | − - adi,adc-dev | |
| 48 | 51 | |
| 49 | 52 | additionalProperties: false |
| 50 | 53 | |
| @@ -55,7 +58,6 @@ examples: · 4 unmodified lines | ||
| 55 | 58 | reg = <0x44a00000 0x10000>; |
| 56 | 59 | dmas = <&rx_dma 0>; |
| 57 | 60 | dma-names = "rx"; |
| 58 | − | |
| 59 | − adi,adc-dev = <&spi_adc>; | |
| 61 | + #io-backend-cells = <0>; | |
| 60 | 62 | }; |
| 61 | 63 | ... |
| @@ -0,0 +1,120 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/iio/adc/microchip,pac1934.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Microchip PAC1934 Power Monitors with Accumulator | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Marius Cristea <marius.cristea@microchip.com> | |
| 11 | + | |
| 12 | +description: | | |
| 13 | + This device is part of the Microchip family of Power Monitors with | |
| 14 | + Accumulator. | |
| 15 | + The datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here: | |
| 16 | + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf | |
| 17 | + | |
| 18 | +properties: | |
| 19 | + compatible: | |
| 20 | + enum: | |
| 21 | + - microchip,pac1931 | |
| 22 | + - microchip,pac1932 | |
| 23 | + - microchip,pac1933 | |
| 24 | + - microchip,pac1934 | |
| 25 | + | |
| 26 | + reg: | |
| 27 | + maxItems: 1 | |
| 28 | + | |
| 29 | + "#address-cells": | |
| 30 | + const: 1 | |
| 31 | + | |
| 32 | + "#size-cells": | |
| 33 | + const: 0 | |
| 34 | + | |
| 35 | + interrupts: | |
| 36 | + maxItems: 1 | |
| 37 | + | |
| 38 | + slow-io-gpios: | |
| 39 | + description: | |
| 40 | + A GPIO used to trigger a change is sampling rate (lowering the chip power | |
| 41 | + consumption). If configured in SLOW mode, if this pin is forced high, | |
| 42 | + sampling rate is forced to eight samples/second. When it is forced low, | |
| 43 | + the sampling rate is 1024 samples/second unless a different sample rate | |
| 44 | + has been programmed. | |
| 45 | + | |
| 46 | +patternProperties: | |
| 47 | + "^channel@[1-4]+$": | |
| 48 | + type: object | |
| 49 | + $ref: adc.yaml | |
| 50 | + description: | |
| 51 | + Represents the external channels which are connected to the ADC. | |
| 52 | + | |
| 53 | + properties: | |
| 54 | + reg: | |
| 55 | + items: | |
| 56 | + minimum: 1 | |
| 57 | + maximum: 4 | |
| 58 | + | |
| 59 | + shunt-resistor-micro-ohms: | |
| 60 | + description: | |
| 61 | + Value in micro Ohms of the shunt resistor connected between | |
| 62 | + the SENSE+ and SENSE- inputs, across which the current is measured. | |
| 63 | + Value is needed to compute the scaling of the measured current. | |
| 64 | + | |
| 65 | + required: | |
| 66 | + - reg | |
| 67 | + - shunt-resistor-micro-ohms | |
| 68 | + | |
| 69 | + unevaluatedProperties: false | |
| 70 | + | |
| 71 | +required: | |
| 72 | + - compatible | |
| 73 | + - reg | |
| 74 | + - "#address-cells" | |
| 75 | + - "#size-cells" | |
| 76 | + | |
| 77 | +additionalProperties: false | |
| 78 | + | |
| 79 | +examples: | |
| 80 | + - | | |
| 81 | + i2c { | |
| 82 | + #address-cells = <1>; | |
| 83 | + #size-cells = <0>; | |
| 84 | + | |
| 85 | + power-monitor@10 { | |
| 86 | + compatible = "microchip,pac1934"; | |
| 87 | + reg = <0x10>; | |
| 88 | + | |
| 89 | + #address-cells = <1>; | |
| 90 | + #size-cells = <0>; | |
| 91 | + | |
| 92 | + channel@1 { | |
| 93 | + reg = <0x1>; | |
| 94 | + shunt-resistor-micro-ohms = <24900000>; | |
| 95 | + label = "CPU"; | |
| 96 | + }; | |
| 97 | + | |
| 98 | + channel@2 { | |
| 99 | + reg = <0x2>; | |
| 100 | + shunt-resistor-micro-ohms = <49900000>; | |
| 101 | + label = "GPU"; | |
| 102 | + }; | |
| 103 | + | |
| 104 | + channel@3 { | |
| 105 | + reg = <0x3>; | |
| 106 | + shunt-resistor-micro-ohms = <75000000>; | |
| 107 | + label = "MEM"; | |
| 108 | + bipolar; | |
| 109 | + }; | |
| 110 | + | |
| 111 | + channel@4 { | |
| 112 | + reg = <0x4>; | |
| 113 | + shunt-resistor-micro-ohms = <100000000>; | |
| 114 | + label = "NET"; | |
| 115 | + bipolar; | |
| 116 | + }; | |
| 117 | + }; | |
| 118 | + }; | |
| 119 | + | |
| 120 | +... | |
| @@ -31,7 +31,6 @@ properties: · 30 unmodified lines | ||
| 31 | 31 | - description: normal conversion, include EOC (End of Conversion), |
| 32 | 32 | ECH (End of Chain), JEOC (End of Injected Conversion) and |
| 33 | 33 | JECH (End of injected Chain). |
| 34 | − - description: Self-testing Interrupts. | |
| 35 | 34 | |
| 36 | 35 | clocks: |
| 37 | 36 | maxItems: 1 |
| @@ -70,8 +69,7 @@ examples: · 32 unmodified lines | ||
| 70 | 69 | reg = <0x44530000 0x10000>; |
| 71 | 70 | interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>, |
| 72 | 71 | <GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>, |
| 73 | − <GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>, | |
| 74 | − <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>; | |
| 72 | + <GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>; | |
| 75 | 73 | clocks = <&clk IMX93_CLK_ADC1_GATE>; |
| 76 | 74 | clock-names = "ipg"; |
| 77 | 75 | vref-supply = <®_vref_1v8>; |
| @@ -75,7 +75,6 @@ patternProperties: · 74 unmodified lines | ||
| 75 | 75 | in the PMIC-specific files in include/dt-bindings/iio/. |
| 76 | 76 | |
| 77 | 77 | label: |
| 78 | − $ref: /schemas/types.yaml#/definitions/string | |
| 79 | 78 | description: | |
| 80 | 79 | ADC input of the platform as seen in the schematics. |
| 81 | 80 | For thermistor inputs connected to generic AMUX or GPIO inputs |
| @@ -25,7 +25,14 @@ description: | · 24 unmodified lines | ||
| 25 | 25 | |
| 26 | 26 | properties: |
| 27 | 27 | compatible: |
| 28 | − const: richtek,rtq6056 | |
| 28 | + oneOf: | |
| 29 | + - enum: | |
| 30 | + - richtek,rtq6056 | |
| 31 | + - richtek,rtq6059 | |
| 32 | + - items: | |
| 33 | + - enum: | |
| 34 | + - richtek,rtq6053 | |
| 35 | + - const: richtek,rtq6056 | |
| 29 | 36 | |
| 30 | 37 | reg: |
| 31 | 38 | maxItems: 1 |
| @@ -0,0 +1,80 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/iio/adc/ti,ads1298.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Texas Instruments' ads1298 medical ADC chips | |
| 8 | + | |
| 9 | +description: | | |
| 10 | + Datasheet at: https://www.ti.com/product/ADS1298 | |
| 11 | + Bindings for this chip aren't complete. | |
| 12 | + | |
| 13 | +maintainers: | |
| 14 | + - Mike Looijmans <mike.looijmans@topic.nl> | |
| 15 | + | |
| 16 | +properties: | |
| 17 | + compatible: | |
| 18 | + enum: | |
| 19 | + - ti,ads1298 | |
| 20 | + | |
| 21 | + reg: | |
| 22 | + maxItems: 1 | |
| 23 | + | |
| 24 | + spi-cpha: true | |
| 25 | + | |
| 26 | + reset-gpios: | |
| 27 | + maxItems: 1 | |
| 28 | + | |
| 29 | + avdd-supply: | |
| 30 | + description: | |
| 31 | + Analog power supply, voltage between AVDD and AVSS. When providing a | |
| 32 | + symmetric +/- 2.5V, the regulator should report 5V. | |
| 33 | + | |
| 34 | + vref-supply: | |
| 35 | + description: | |
| 36 | + Optional reference voltage. If omitted, internal reference is used, | |
| 37 | + which is 2.4V when analog supply is below 4.4V, 4V otherwise. | |
| 38 | + | |
| 39 | + clocks: | |
| 40 | + description: Optional 2.048 MHz external source clock on CLK pin | |
| 41 | + maxItems: 1 | |
| 42 | + | |
| 43 | + interrupts: | |
| 44 | + description: Interrupt on DRDY pin, triggers on falling edge | |
| 45 | + maxItems: 1 | |
| 46 | + | |
| 47 | + label: true | |
| 48 | + | |
| 49 | +required: | |
| 50 | + - compatible | |
| 51 | + - reg | |
| 52 | + - avdd-supply | |
| 53 | + - interrupts | |
| 54 | + | |
| 55 | +allOf: | |
| 56 | + - $ref: /schemas/spi/spi-peripheral-props.yaml# | |
| 57 | + | |
| 58 | +unevaluatedProperties: false | |
| 59 | + | |
| 60 | +examples: | |
| 61 | + - | | |
| 62 | + #include <dt-bindings/gpio/gpio.h> | |
| 63 | + #include <dt-bindings/interrupt-controller/irq.h> | |
| 64 | + spi { | |
| 65 | + #address-cells = <1>; | |
| 66 | + #size-cells = <0>; | |
| 67 | + | |
| 68 | + adc@1 { | |
| 69 | + reg = <1>; | |
| 70 | + compatible = "ti,ads1298"; | |
| 71 | + label = "ads1298-1-ecg"; | |
| 72 | + avdd-supply = <®_iso_5v_a>; | |
| 73 | + clocks = <&clk_ads1298>; | |
| 74 | + interrupt-parent = <&gpio0>; | |
| 75 | + interrupts = <78 IRQ_TYPE_EDGE_FALLING>; | |
| 76 | + spi-max-frequency = <20000000>; | |
| 77 | + spi-cpha; | |
| 78 | + }; | |
| 79 | + }; | |
| 80 | +... | |
| @@ -39,6 +39,17 @@ properties: · 38 unmodified lines | ||
| 39 | 39 | description: | |
| 40 | 40 | Channel node of a voltage io-channel. |
| 41 | 41 | |
| 42 | + '#io-channel-cells': | |
| 43 | + description: | |
| 44 | + In addition to consuming the measurement services of a voltage | |
| 45 | + output channel, the voltage divider can act as a provider of | |
| 46 | + measurement services to other devices. This is particularly | |
| 47 | + useful in scenarios wherein an ADC has an analog frontend, | |
| 48 | + such as a voltage divider, and then consuming its raw value | |
| 49 | + isn't interesting. In this case, the voltage before the divider | |
| 50 | + is desired. | |
| 51 | + const: 1 | |
| 52 | + | |
| 42 | 53 | output-ohms: |
| 43 | 54 | description: |
| 44 | 55 | Resistance Rout over which the output voltage is measured. See full-ohms. |
| @@ -21,6 +21,8 @@ description: | · 20 unmodified lines | ||
| 21 | 21 | HMC540S 1 dB LSB Silicon MMIC 4-Bit Digital Positive Control Attenuator, 0.1 - 8 GHz |
| 22 | 22 | https://www.analog.com/media/en/technical-documentation/data-sheets/hmc540s.pdf |
| 23 | 23 | |
| 24 | + LTC6373 is a 3-Bit precision instrumentation amplifier with fully differential outputs | |
| 25 | + https://www.analog.com/media/en/technical-documentation/data-sheets/ltc6373.pdf | |
| 24 | 26 | |
| 25 | 27 | properties: |
| 26 | 28 | compatible: |
| @@ -28,16 +30,55 @@ properties: · 1 unmodified line | ||
| 28 | 30 | - adi,adrf5740 |
| 29 | 31 | - adi,hmc425a |
| 30 | 32 | - adi,hmc540s |
| 33 | + - adi,ltc6373 | |
| 31 | 34 | |
| 32 | 35 | vcc-supply: true |
| 33 | 36 | |
| 34 | 37 | ctrl-gpios: |
| 35 | 38 | description: |
| 36 | − Must contain an array of 6 GPIO specifiers, referring to the GPIO pins | |
| 37 | − connected to the control pins V1-V6. | |
| 38 | − minItems: 6 | |
| 39 | + Must contain an array of GPIO specifiers, referring to the GPIO pins | |
| 40 | + connected to the control pins. | |
| 41 | + ADRF5740 - 4 GPIO connected to D2-D5 | |
| 42 | + HMC540S - 4 GPIO connected to V1-V4 | |
| 43 | + HMC425A - 6 GPIO connected to V1-V6 | |
| 44 | + LTC6373 - 3 GPIO connected to A0-A2 | |
| 45 | + minItems: 1 | |
| 39 | 46 | maxItems: 6 |
| 40 | 47 | |
| 48 | +allOf: | |
| 49 | + - if: | |
| 50 | + properties: | |
| 51 | + compatible: | |
| 52 | + contains: | |
| 53 | + const: adi,hmc425a | |
| 54 | + then: | |
| 55 | + properties: | |
| 56 | + ctrl-gpios: | |
| 57 | + minItems: 6 | |
| 58 | + maxItems: 6 | |
| 59 | + - if: | |
| 60 | + properties: | |
| 61 | + compatible: | |
| 62 | + contains: | |
| 63 | + anyOf: | |
| 64 | + - const: adi,adrf5740 | |
| 65 | + - const: adi,hmc540s | |
| 66 | + then: | |
| 67 | + properties: | |
| 68 | + ctrl-gpios: | |
| 69 | + minItems: 4 | |
| 70 | + maxItems: 4 | |
| 71 | + - if: | |
| 72 | + properties: | |
| 73 | + compatible: | |
| 74 | + contains: | |
| 75 | + const: adi,ltc6373 | |
| 76 | + then: | |
| 77 | + properties: | |
| 78 | + ctrl-gpios: | |
| 79 | + minItems: 3 | |
| 80 | + maxItems: 3 | |
| 81 | + | |
| 41 | 82 | required: |
| 42 | 83 | - compatible |
| 43 | 84 | - ctrl-gpios |
| @@ -0,0 +1,127 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +# Copyright 2024 Analog Devices Inc. | |
| 3 | +%YAML 1.2 | |
| 4 | +--- | |
| 5 | +$id: http://devicetree.org/schemas/iio/frequency/adi,admfm2000.yaml# | |
| 6 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 7 | + | |
| 8 | +title: ADMFM2000 Dual Microwave Down Converter | |
| 9 | + | |
| 10 | +maintainers: | |
| 11 | + - Kim Seer Paller <kimseer.paller@analog.com> | |
| 12 | + | |
| 13 | +description: | |
| 14 | + Dual microwave down converter module with input RF and LO frequency ranges | |
| 15 | + from 0.5 to 32 GHz and an output IF frequency range from 0.1 to 8 GHz. | |
| 16 | + It consists of a LNA, mixer, IF filter, DSA, and IF amplifier for each down | |
| 17 | + conversion path. | |
| 18 | + | |
| 19 | +properties: | |
| 20 | + compatible: | |
| 21 | + enum: | |
| 22 | + - adi,admfm2000 | |
| 23 | + | |
| 24 | + '#address-cells': | |
| 25 | + const: 1 | |
| 26 | + | |
| 27 | + '#size-cells': | |
| 28 | + const: 0 | |
| 29 | + | |
| 30 | +patternProperties: | |
| 31 | + "^channel@[0-1]$": | |
| 32 | + type: object | |
| 33 | + description: Represents a channel of the device. | |
| 34 | + | |
| 35 | + additionalProperties: false | |
| 36 | + | |
| 37 | + properties: | |
| 38 | + reg: | |
| 39 | + description: | |
| 40 | + The channel number. | |
| 41 | + minimum: 0 | |
| 42 | + maximum: 1 | |
| 43 | + | |
| 44 | + adi,mixer-mode: | |
| 45 | + description: | |
| 46 | + Enable mixer mode for the channel. It downconverts RF between 5 GHz | |
| 47 | + and 32 GHz to IF between 0.5 GHz and 8 GHz. If not present, the channel | |
| 48 | + is in direct IF mode which bypasses the mixer and downconverts RF | |
| 49 | + between 2 GHz and 8 GHz to IF between 0.5 GHz and 8 GHz. | |
| 50 | + type: boolean | |
| 51 | + | |
| 52 | + switch-gpios: | |
| 53 | + description: | | |
| 54 | + GPIOs to select the RF path for the channel. The same state of CTRL-A | |
| 55 | + and CTRL-B GPIOs is not permitted. | |
| 56 | + CTRL-A CTRL-B CH1 Status CH2 Status | |
| 57 | + 1 0 Direct IF mode Mixer mode | |
| 58 | + 0 1 Mixer mode Direct IF mode | |
| 59 | + | |
| 60 | + items: | |
| 61 | + - description: CTRL-A GPIO | |
| 62 | + - description: CTRL-B GPIO | |
| 63 | + | |
| 64 | + attenuation-gpios: | |
| 65 | + description: | | |
| 66 | + Choice of attenuation: | |
| 67 | + DSA-V4 DSA-V3 DSA-V2 DSA-V1 DSA-V0 | |
| 68 | + 1 1 1 1 1 0 dB | |
| 69 | + 1 1 1 1 0 -1 dB | |
| 70 | + 1 1 1 0 1 -2 dB | |
| 71 | + 1 1 0 1 1 -4 dB | |
| 72 | + 1 0 1 1 1 -8 dB | |
| 73 | + 0 1 1 1 1 -16 dB | |
| 74 | + 0 0 0 0 0 -31 dB | |
| 75 | + | |
| 76 | + items: | |
| 77 | + - description: DSA-V0 GPIO | |
| 78 | + - description: DSA-V1 GPIO | |
| 79 | + - description: DSA-V2 GPIO | |
| 80 | + - description: DSA-V3 GPIO | |
| 81 | + - description: DSA-V4 GPIO | |
| 82 | + | |
| 83 | + required: | |
| 84 | + - reg | |
| 85 | + - switch-gpios | |
| 86 | + - attenuation-gpios | |
| 87 | + | |
| 88 | +required: | |
| 89 | + - compatible | |
| 90 | + | |
| 91 | +additionalProperties: false | |
| 92 | + | |
| 93 | +examples: | |
| 94 | + - | | |
| 95 | + #include <dt-bindings/gpio/gpio.h> | |
| 96 | + converter { | |
| 97 | + compatible = "adi,admfm2000"; | |
| 98 | + | |
| 99 | + #address-cells = <1>; | |
| 100 | + #size-cells = <0>; | |
| 101 | + | |
| 102 | + channel@0 { | |
| 103 | + reg = <0>; | |
| 104 | + switch-gpios = <&gpio 1 GPIO_ACTIVE_LOW>, | |
| 105 | + <&gpio 2 GPIO_ACTIVE_HIGH>; | |
| 106 | + | |
| 107 | + attenuation-gpios = <&gpio 17 GPIO_ACTIVE_LOW>, | |
| 108 | + <&gpio 22 GPIO_ACTIVE_LOW>, | |
| 109 | + <&gpio 23 GPIO_ACTIVE_LOW>, | |
| 110 | + <&gpio 24 GPIO_ACTIVE_LOW>, | |
| 111 | + <&gpio 25 GPIO_ACTIVE_LOW>; | |
| 112 | + }; | |
| 113 | + | |
| 114 | + channel@1 { | |
| 115 | + reg = <1>; | |
| 116 | + adi,mixer-mode; | |
| 117 | + switch-gpios = <&gpio 3 GPIO_ACTIVE_LOW>, | |
| 118 | + <&gpio 4 GPIO_ACTIVE_HIGH>; | |
| 119 | + | |
| 120 | + attenuation-gpios = <&gpio 0 GPIO_ACTIVE_LOW>, | |
| 121 | + <&gpio 5 GPIO_ACTIVE_LOW>, | |
| 122 | + <&gpio 6 GPIO_ACTIVE_LOW>, | |
| 123 | + <&gpio 16 GPIO_ACTIVE_LOW>, | |
| 124 | + <&gpio 26 GPIO_ACTIVE_LOW>; | |
| 125 | + }; | |
| 126 | + }; | |
| 127 | +... | |
| @@ -22,6 +22,9 @@ properties: · 21 unmodified lines | ||
| 22 | 22 | vdd-supply: true |
| 23 | 23 | vddio-supply: true |
| 24 | 24 | |
| 25 | + spi-max-frequency: | |
| 26 | + maximum: 10000000 | |
| 27 | + | |
| 25 | 28 | interrupts: |
| 26 | 29 | minItems: 1 |
| 27 | 30 | maxItems: 2 |
| @@ -33,7 +36,10 @@ required: · 5 unmodified lines | ||
| 33 | 36 | - compatible |
| 34 | 37 | - reg |
| 35 | 38 | |
| 36 | −additionalProperties: false | |
| 39 | +allOf: | |
| 40 | + - $ref: /schemas/spi/spi-peripheral-props.yaml# | |
| 41 | + | |
| 42 | +unevaluatedProperties: false | |
| 37 | 43 | |
| 38 | 44 | examples: |
| 39 | 45 | - | |
| @@ -27,6 +27,9 @@ properties: · 26 unmodified lines | ||
| 27 | 27 | reg: |
| 28 | 28 | maxItems: 1 |
| 29 | 29 | |
| 30 | + interrupts: | |
| 31 | + maxItems: 1 | |
| 32 | + | |
| 30 | 33 | required: |
| 31 | 34 | - compatible |
| 32 | 35 | - reg |
| @@ -43,6 +43,7 @@ additionalProperties: false · 42 unmodified lines | ||
| 43 | 43 | |
| 44 | 44 | examples: |
| 45 | 45 | - | |
| 46 | + #include <dt-bindings/interrupt-controller/irq.h> | |
| 46 | 47 | i2c { |
| 47 | 48 | #address-cells = <1>; |
| 48 | 49 | #size-cells = <0>; |
| @@ -51,5 +52,7 @@ examples: · 2 unmodified lines | ||
| 51 | 52 | compatible = "ti,hdc3021", "ti,hdc3020"; |
| 52 | 53 | reg = <0x47>; |
| 53 | 54 | vdd-supply = <&vcc_3v3>; |
| 55 | + interrupt-parent = <&gpio3>; | |
| 56 | + interrupts = <23 IRQ_TYPE_EDGE_RISING>; | |
| 54 | 57 | }; |
| 55 | 58 | }; |
| @@ -35,7 +35,9 @@ properties: · 34 unmodified lines | ||
| 35 | 35 | - st,lsm6dsv |
| 36 | 36 | - st,lsm6dso16is |
| 37 | 37 | - items: |
| 38 | − - const: st,asm330lhhx | |
| 38 | + - enum: | |
| 39 | + - st,asm330lhhx | |
| 40 | + - st,asm330lhhxg1 | |
| 39 | 41 | - const: st,lsm6dsr |
| 40 | 42 | - items: |
| 41 | 43 | - const: st,lsm6dstx |
| @@ -4,19 +4,22 @@ · 3 unmodified lines | ||
| 4 | 4 | $id: http://devicetree.org/schemas/iio/light/ams,as73211.yaml# |
| 5 | 5 | $schema: http://devicetree.org/meta-schemas/core.yaml# |
| 6 | 6 | |
| 7 | −title: AMS AS73211 JENCOLOR(R) Digital XYZ Sensor | |
| 7 | +title: AMS AS73211 JENCOLOR(R) Digital XYZ Sensor and AMS AS7331 UV Sensor | |
| 8 | 8 | |
| 9 | 9 | maintainers: |
| 10 | 10 | - Christian Eggers <ceggers@arri.de> |
| 11 | 11 | |
| 12 | 12 | description: | |
| 13 | − XYZ True Color Sensor with I2C Interface | |
| 13 | + AMS AS73211 XYZ True Color Sensor with I2C Interface | |
| 14 | 14 | https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf/a65474c0-b302-c2fd-e30a-c98df87616df |
| 15 | + AMS AS7331 UVA, UVB and UVC Sensor with I2C Interface | |
| 16 | + https://ams.com/documents/20143/9106314/AS7331_DS001047_4-00.pdf | |
| 15 | 17 | |
| 16 | 18 | properties: |
| 17 | 19 | compatible: |
| 18 | 20 | enum: |
| 19 | 21 | - ams,as73211 |
| 22 | + - ams,as7331 | |
| 20 | 23 | |
| 21 | 24 | reg: |
| 22 | 25 | description: |
| @@ -21,6 +21,7 @@ properties: · 20 unmodified lines | ||
| 21 | 21 | required: |
| 22 | 22 | - compatible |
| 23 | 23 | - reg |
| 24 | + - vdd-supply | |
| 24 | 25 | |
| 25 | 26 | additionalProperties: false |
| 26 | 27 | |
| @@ -0,0 +1,60 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/iio/magnetometer/voltafield,af8133j.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Voltafield AF8133J magnetometer sensor | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Ondřej Jirman <megi@xff.cz> | |
| 11 | + | |
| 12 | +properties: | |
| 13 | + compatible: | |
| 14 | + const: voltafield,af8133j | |
| 15 | + | |
| 16 | + reg: | |
| 17 | + maxItems: 1 | |
| 18 | + | |
| 19 | + reset-gpios: | |
| 20 | + description: | |
| 21 | + A signal for active low reset input of the sensor. (optional; if not | |
| 22 | + used, software reset over I2C will be used instead) | |
| 23 | + | |
| 24 | + avdd-supply: | |
| 25 | + description: | |
| 26 | + A regulator that provides AVDD power (Working power, usually 3.3V) to | |
| 27 | + the sensor. | |
| 28 | + | |
| 29 | + dvdd-supply: | |
| 30 | + description: | |
| 31 | + A regulator that provides DVDD power (Digital IO power, 1.8V - AVDD) | |
| 32 | + to the sensor. | |
| 33 | + | |
| 34 | + mount-matrix: | |
| 35 | + description: An optional 3x3 mounting rotation matrix. | |
| 36 | + | |
| 37 | +required: | |
| 38 | + - compatible | |
| 39 | + - reg | |
| 40 | + - avdd-supply | |
| 41 | + - dvdd-supply | |
| 42 | + | |
| 43 | +additionalProperties: false | |
| 44 | + | |
| 45 | +examples: | |
| 46 | + - | | |
| 47 | + #include <dt-bindings/interrupt-controller/irq.h> | |
| 48 | + #include <dt-bindings/gpio/gpio.h> | |
| 49 | + i2c { | |
| 50 | + #address-cells = <1>; | |
| 51 | + #size-cells = <0>; | |
| 52 | + | |
| 53 | + magnetometer@1c { | |
| 54 | + compatible = "voltafield,af8133j"; | |
| 55 | + reg = <0x1c>; | |
| 56 | + avdd-supply = <®_dldo1>; | |
| 57 | + dvdd-supply = <®_dldo1>; | |
| 58 | + reset-gpios = <&pio 1 1 GPIO_ACTIVE_LOW>; | |
| 59 | + }; | |
| 60 | + }; | |
| @@ -99,6 +99,9 @@ required: · 98 unmodified lines | ||
| 99 | 99 | - honeywell,transfer-function |
| 100 | 100 | - honeywell,pressure-triplet |
| 101 | 101 | |
| 102 | +allOf: | |
| 103 | + - $ref: /schemas/spi/spi-peripheral-props.yaml | |
| 104 | + | |
| 102 | 105 | additionalProperties: false |
| 103 | 106 | |
| 104 | 107 | dependentSchemas: |
| @@ -8,25 +8,28 @@ title: Honeywell mprls0025pa pressure sensor · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | maintainers: |
| 10 | 10 | - Andreas Klinger <ak@it-klinger.de> |
| 11 | + - Petre Rodan <petre.rodan@subdimension.ro> | |
| 11 | 12 | |
| 12 | 13 | description: | |
| 13 | 14 | Honeywell pressure sensor of model mprls0025pa. |
| 14 | 15 | |
| 15 | − This sensor has an I2C and SPI interface. Only the I2C interface is | |
| 16 | − implemented. | |
| 16 | + This sensor has an I2C and SPI interface. | |
| 17 | 17 | |
| 18 | 18 | There are many models with different pressure ranges available. The vendor |
| 19 | 19 | calls them "mpr series". All of them have the identical programming model and |
| 20 | 20 | differ in the pressure range, unit and transfer function. |
| 21 | 21 | |
| 22 | − To support different models one need to specify the pressure range as well as | |
| 23 | − the transfer function. Pressure range needs to be converted from its unit to | |
| 24 | − pascal. | |
| 22 | + To support different models one need to specify its pressure triplet as well | |
| 23 | + as the transfer function. | |
| 24 | + | |
| 25 | + For custom silicon chips not covered by the Honeywell MPR series datasheet, | |
| 26 | + the pressure values can be specified manually via honeywell,pmin-pascal and | |
| 27 | + honeywell,pmax-pascal. | |
| 28 | + The minimal range value stands for the minimum pressure and the maximum value | |
| 29 | + also for the maximum pressure with linear relation inside the range. | |
| 25 | 30 | |
| 26 | 31 | The transfer function defines the ranges of numerical values delivered by the |
| 27 | − sensor. The minimal range value stands for the minimum pressure and the | |
| 28 | − maximum value also for the maximum pressure with linear relation inside the | |
| 29 | − range. | |
| 32 | + sensor. | |
| 30 | 33 | |
| 31 | 34 | Specifications about the devices can be found at: |
| 32 | 35 | https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/ |
| @@ -42,6 +45,10 @@ properties: · 9 unmodified lines | ||
| 42 | 45 | maxItems: 1 |
| 43 | 46 | |
| 44 | 47 | interrupts: |
| 48 | + description: | |
| 49 | + Optional interrupt for indicating End-of-conversion. | |
| 50 | + If not present, the driver loops for a while until the received status | |
| 51 | + byte indicates correct measurement. | |
| 45 | 52 | maxItems: 1 |
| 46 | 53 | |
| 47 | 54 | reset-gpios: |
| @@ -50,14 +57,6 @@ properties: · 2 unmodified lines | ||
| 50 | 57 | If not present the device is not reset during the probe. |
| 51 | 58 | maxItems: 1 |
| 52 | 59 | |
| 53 | − honeywell,pmin-pascal: | |
| 54 | − description: | |
| 55 | − Minimum pressure value the sensor can measure in pascal. | |
| 56 | − | |
| 57 | − honeywell,pmax-pascal: | |
| 58 | − description: | |
| 59 | − Maximum pressure value the sensor can measure in pascal. | |
| 60 | − | |
| 61 | 60 | honeywell,transfer-function: |
| 62 | 61 | description: | |
| 63 | 62 | Transfer function which defines the range of valid values delivered by the |
| @@ -65,19 +64,57 @@ properties: · 1 unmodified line | ||
| 65 | 64 | 1 - A, 10% to 90% of 2^24 (1677722 .. 15099494) |
| 66 | 65 | 2 - B, 2.5% to 22.5% of 2^24 (419430 .. 3774874) |
| 67 | 66 | 3 - C, 20% to 80% of 2^24 (3355443 .. 13421773) |
| 67 | + enum: [1, 2, 3] | |
| 68 | 68 | $ref: /schemas/types.yaml#/definitions/uint32 |
| 69 | 69 | |
| 70 | + honeywell,pressure-triplet: | |
| 71 | + description: | | |
| 72 | + Case-sensitive five character string that defines pressure range, unit | |
| 73 | + and type as part of the device nomenclature. In the unlikely case of a | |
| 74 | + custom chip, unset and provide pmin-pascal and pmax-pascal instead. | |
| 75 | + enum: [0001BA, 01.6BA, 02.5BA, 0060MG, 0100MG, 0160MG, 0250MG, 0400MG, | |
| 76 | + 0600MG, 0001BG, 01.6BG, 02.5BG, 0100KA, 0160KA, 0250KA, 0006KG, | |
| 77 | + 0010KG, 0016KG, 0025KG, 0040KG, 0060KG, 0100KG, 0160KG, 0250KG, | |
| 78 | + 0015PA, 0025PA, 0030PA, 0001PG, 0005PG, 0015PG, 0030PG, 0300YG] | |
| 79 | + $ref: /schemas/types.yaml#/definitions/string | |
| 80 | + | |
| 81 | + honeywell,pmin-pascal: | |
| 82 | + description: | |
| 83 | + Minimum pressure value the sensor can measure in pascal. | |
| 84 | + | |
| 85 | + honeywell,pmax-pascal: | |
| 86 | + description: | |
| 87 | + Maximum pressure value the sensor can measure in pascal. | |
| 88 | + | |
| 89 | + spi-max-frequency: | |
| 90 | + maximum: 800000 | |
| 91 | + | |
| 70 | 92 | vdd-supply: |
| 71 | 93 | description: provide VDD power to the sensor. |
| 72 | 94 | |
| 73 | 95 | required: |
| 74 | 96 | - compatible |
| 75 | 97 | - reg |
| 76 | − - honeywell,pmin-pascal | |
| 77 | − - honeywell,pmax-pascal | |
| 78 | 98 | - honeywell,transfer-function |
| 79 | 99 | - vdd-supply |
| 80 | 100 | |
| 101 | +oneOf: | |
| 102 | + - required: | |
| 103 | + - honeywell,pressure-triplet | |
| 104 | + - required: | |
| 105 | + - honeywell,pmin-pascal | |
| 106 | + - honeywell,pmax-pascal | |
| 107 | + | |
| 108 | +allOf: | |
| 109 | + - $ref: /schemas/spi/spi-peripheral-props.yaml | |
| 110 | + - if: | |
| 111 | + required: | |
| 112 | + - honeywell,pressure-triplet | |
| 113 | + then: | |
| 114 | + properties: | |
| 115 | + honeywell,pmin-pascal: false | |
| 116 | + honeywell,pmax-pascal: false | |
| 117 | + | |
| 81 | 118 | additionalProperties: false |
| 82 | 119 | |
| 83 | 120 | examples: |
| @@ -93,10 +130,29 @@ examples: · 9 unmodified lines | ||
| 93 | 130 | reg = <0x18>; |
| 94 | 131 | reset-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>; |
| 95 | 132 | interrupt-parent = <&gpio3>; |
| 96 | − interrupts = <21 IRQ_TYPE_EDGE_FALLING>; | |
| 97 | − honeywell,pmin-pascal = <0>; | |
| 98 | − honeywell,pmax-pascal = <172369>; | |
| 133 | + interrupts = <21 IRQ_TYPE_EDGE_RISING>; | |
| 134 | + | |
| 135 | + honeywell,pressure-triplet = "0025PA"; | |
| 136 | + honeywell,transfer-function = <1>; | |
| 137 | + vdd-supply = <&vcc_3v3>; | |
| 138 | + }; | |
| 139 | + }; | |
| 140 | + - | | |
| 141 | + spi { | |
| 142 | + #address-cells = <1>; | |
| 143 | + #size-cells = <0>; | |
| 144 | + | |
| 145 | + pressure@0 { | |
| 146 | + compatible = "honeywell,mprls0025pa"; | |
| 147 | + reg = <0>; | |
| 148 | + spi-max-frequency = <800000>; | |
| 149 | + reset-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; | |
| 150 | + interrupt-parent = <&gpio0>; | |
| 151 | + interrupts = <30 IRQ_TYPE_EDGE_RISING>; | |
| 152 | + | |
| 153 | + honeywell,pressure-triplet = "0015PA"; | |
| 99 | 154 | honeywell,transfer-function = <1>; |
| 100 | 155 | vdd-supply = <&vcc_3v3>; |
| 101 | 156 | }; |
| 102 | 157 | }; |
| 158 | +... | |
| @@ -24,9 +24,16 @@ properties: · 23 unmodified lines | ||
| 24 | 24 | reg: |
| 25 | 25 | maxItems: 1 |
| 26 | 26 | |
| 27 | + vcc-supply: | |
| 28 | + description: provide VCC power to the sensor. | |
| 29 | + | |
| 30 | + label: | |
| 31 | + description: Unique name to identify which device this is. | |
| 32 | + | |
| 27 | 33 | required: |
| 28 | 34 | - compatible |
| 29 | 35 | - reg |
| 36 | + - vcc-supply | |
| 30 | 37 | |
| 31 | 38 | additionalProperties: false |
| 32 | 39 | |
| @@ -39,5 +46,6 @@ examples: · 6 unmodified lines | ||
| 39 | 46 | tmp117@48 { |
| 40 | 47 | compatible = "ti,tmp117"; |
| 41 | 48 | reg = <0x48>; |
| 49 | + vcc-supply = <&pmic_reg_3v3>; | |
| 42 | 50 | }; |
| 43 | 51 | }; |
| @@ -23,6 +23,9 @@ properties: · 22 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | compatible: |
| 25 | 25 | enum: |
| 26 | + - qcom,msm8909-bimc | |
| 27 | + - qcom,msm8909-pcnoc | |
| 28 | + - qcom,msm8909-snoc | |
| 26 | 29 | - qcom,msm8916-bimc |
| 27 | 30 | - qcom,msm8916-pcnoc |
| 28 | 31 | - qcom,msm8916-snoc |
| @@ -8,7 +8,7 @@ title: Qualcomm RPMh Network-On-Chip Interconnect · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | maintainers: |
| 10 | 10 | - Georgi Djakov <georgi.djakov@linaro.org> |
| 11 | − - Odelu Kukatla <okukatla@codeaurora.org> | |
| 11 | + - Odelu Kukatla <quic_okukatla@quicinc.com> | |
| 12 | 12 | |
| 13 | 13 | description: | |
| 14 | 14 | RPMh interconnect providers support system bandwidth requirements through |
| @@ -0,0 +1,84 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/interconnect/qcom,sm7150-rpmh.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Qualcomm RPMh Network-On-Chip Interconnect on SM7150 | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Danila Tikhonov <danila@jiaxyga.com> | |
| 11 | + | |
| 12 | +description: | | |
| 13 | + RPMh interconnect providers support system bandwidth requirements through | |
| 14 | + RPMh hardware accelerators known as Bus Clock Manager (BCM). | |
| 15 | + | |
| 16 | + See also:: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h | |
| 17 | + | |
| 18 | +allOf: | |
| 19 | + - $ref: qcom,rpmh-common.yaml# | |
| 20 | + | |
| 21 | +properties: | |
| 22 | + compatible: | |
| 23 | + enum: | |
| 24 | + - qcom,sm7150-aggre1-noc | |
| 25 | + - qcom,sm7150-aggre2-noc | |
| 26 | + - qcom,sm7150-compute-noc | |
| 27 | + - qcom,sm7150-config-noc | |
| 28 | + - qcom,sm7150-dc-noc | |
| 29 | + - qcom,sm7150-gem-noc | |
| 30 | + - qcom,sm7150-mc-virt | |
| 31 | + - qcom,sm7150-mmss-noc | |
| 32 | + - qcom,sm7150-system-noc | |
| 33 | + | |
| 34 | + reg: | |
| 35 | + maxItems: 1 | |
| 36 | + | |
| 37 | +# Child node's properties | |
| 38 | +patternProperties: | |
| 39 | + '^interconnect-[0-9]+$': | |
| 40 | + type: object | |
| 41 | + description: | |
| 42 | + The interconnect providers do not have a separate QoS register space, | |
| 43 | + but share parent's space. | |
| 44 | + | |
| 45 | + allOf: | |
| 46 | + - $ref: qcom,rpmh-common.yaml# | |
| 47 | + | |
| 48 | + properties: | |
| 49 | + compatible: | |
| 50 | + enum: | |
| 51 | + - qcom,sm7150-camnoc-virt | |
| 52 | + | |
| 53 | + required: | |
| 54 | + - compatible | |
| 55 | + | |
| 56 | + unevaluatedProperties: false | |
| 57 | + | |
| 58 | +required: | |
| 59 | + - compatible | |
| 60 | + - reg | |
| 61 | + | |
| 62 | +unevaluatedProperties: false | |
| 63 | + | |
| 64 | +examples: | |
| 65 | + - | | |
| 66 | + mc_virt: interconnect@1380000 { | |
| 67 | + compatible = "qcom,sm7150-mc-virt"; | |
| 68 | + reg = <0x01380000 0x40000>; | |
| 69 | + #interconnect-cells = <2>; | |
| 70 | + qcom,bcm-voters = <&apps_bcm_voter>; | |
| 71 | + }; | |
| 72 | + | |
| 73 | + system_noc: interconnect@1620000 { | |
| 74 | + compatible = "qcom,sm7150-system-noc"; | |
| 75 | + reg = <0x01620000 0x40000>; | |
| 76 | + #interconnect-cells = <2>; | |
| 77 | + qcom,bcm-voters = <&apps_bcm_voter>; | |
| 78 | + | |
| 79 | + camnoc_virt: interconnect-0 { | |
| 80 | + compatible = "qcom,sm7150-camnoc-virt"; | |
| 81 | + #interconnect-cells = <2>; | |
| 82 | + qcom,bcm-voters = <&apps_bcm_voter>; | |
| 83 | + }; | |
| 84 | + }; | |
| @@ -36,20 +36,18 @@ properties: · 35 unmodified lines | ||
| 36 | 36 | |
| 37 | 37 | allOf: |
| 38 | 38 | - if: |
| 39 | + properties: | |
| 40 | + compatible: | |
| 41 | + contains: | |
| 42 | + const: mac-base | |
| 39 | 43 | required: [ compatible ] |
| 40 | 44 | then: |
| 41 | − if: | |
| 42 | − properties: | |
| 43 | − compatible: | |
| 44 | − contains: | |
| 45 | − const: mac-base | |
| 46 | − then: | |
| 47 | − properties: | |
| 48 | − "#nvmem-cell-cells": | |
| 49 | − description: The first argument is a MAC address offset. | |
| 50 | − const: 1 | |
| 51 | − required: | |
| 52 | − - "#nvmem-cell-cells" | |
| 45 | + properties: | |
| 46 | + "#nvmem-cell-cells": | |
| 47 | + description: The first argument is a MAC address offset. | |
| 48 | + const: 1 | |
| 49 | + required: | |
| 50 | + - "#nvmem-cell-cells" | |
| 53 | 51 | |
| 54 | 52 | required: |
| 55 | 53 | - reg |
| @@ -0,0 +1,18 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/nvmem/nvmem-provider.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/base.yaml# | |
| 6 | + | |
| 7 | +title: NVMEM (Non Volatile Memory) Provider | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | |
| 11 | + | |
| 12 | +select: true | |
| 13 | + | |
| 14 | +properties: | |
| 15 | + '#nvmem-cell-cells': | |
| 16 | + enum: [0, 1] | |
| 17 | + | |
| 18 | +additionalProperties: true | |
| @@ -1,46 +0,0 @@ | ||
| 1 | −-------------------------------------------------------------------------- | |
| 2 | −= Zynq UltraScale+ MPSoC nvmem firmware driver binding = | |
| 3 | −-------------------------------------------------------------------------- | |
| 4 | −The nvmem_firmware node provides access to the hardware related data | |
| 5 | −like soc revision, IDCODE... etc, By using the firmware interface. | |
| 6 | − | |
| 7 | −Required properties: | |
| 8 | −- compatible: should be "xlnx,zynqmp-nvmem-fw" | |
| 9 | − | |
| 10 | −= Data cells = | |
| 11 | −Are child nodes of silicon id, bindings of which as described in | |
| 12 | −bindings/nvmem/nvmem.txt | |
| 13 | − | |
| 14 | −------- | |
| 15 | − Example | |
| 16 | −------- | |
| 17 | −firmware { | |
| 18 | − zynqmp_firmware: zynqmp-firmware { | |
| 19 | − compatible = "xlnx,zynqmp-firmware"; | |
| 20 | − method = "smc"; | |
| 21 | − | |
| 22 | − nvmem_firmware { | |
| 23 | − compatible = "xlnx,zynqmp-nvmem-fw"; | |
| 24 | − #address-cells = <1>; | |
| 25 | − #size-cells = <1>; | |
| 26 | − | |
| 27 | − /* Data cells */ | |
| 28 | − soc_revision: soc_revision { | |
| 29 | − reg = <0x0 0x4>; | |
| 30 | − }; | |
| 31 | − }; | |
| 32 | − }; | |
| 33 | −}; | |
| 34 | − | |
| 35 | −= Data consumers = | |
| 36 | −Are device nodes which consume nvmem data cells. | |
| 37 | − | |
| 38 | −For example: | |
| 39 | − pcap { | |
| 40 | − ... | |
| 41 | − | |
| 42 | − nvmem-cells = <&soc_revision>; | |
| 43 | − nvmem-cell-names = "soc_revision"; | |
| 44 | − | |
| 45 | − ... | |
| 46 | − }; | |
| @@ -0,0 +1,42 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/nvmem/xlnx,zynqmp-nvmem.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Zynq UltraScale+ MPSoC Non Volatile Memory interface | |
| 8 | + | |
| 9 | +description: | | |
| 10 | + The ZynqMP MPSoC provides access to the hardware related data | |
| 11 | + like SOC revision, IDCODE and specific purpose efuses. | |
| 12 | + | |
| 13 | +maintainers: | |
| 14 | + - Kalyani Akula <kalyani.akula@amd.com> | |
| 15 | + - Praveen Teja Kundanala <praveen.teja.kundanala@amd.com> | |
| 16 | + | |
| 17 | +allOf: | |
| 18 | + - $ref: nvmem.yaml# | |
| 19 | + | |
| 20 | +properties: | |
| 21 | + compatible: | |
| 22 | + const: xlnx,zynqmp-nvmem-fw | |
| 23 | + | |
| 24 | +required: | |
| 25 | + - compatible | |
| 26 | + | |
| 27 | +unevaluatedProperties: false | |
| 28 | + | |
| 29 | +examples: | |
| 30 | + - | | |
| 31 | + nvmem { | |
| 32 | + compatible = "xlnx,zynqmp-nvmem-fw"; | |
| 33 | + nvmem-layout { | |
| 34 | + compatible = "fixed-layout"; | |
| 35 | + #address-cells = <1>; | |
| 36 | + #size-cells = <1>; | |
| 37 | + | |
| 38 | + soc_revision: soc-revision@0 { | |
| 39 | + reg = <0x0 0x4>; | |
| 40 | + }; | |
| 41 | + }; | |
| 42 | + }; | |
| @@ -1573,6 +1573,8 @@ patternProperties: · 1572 unmodified lines | ||
| 1573 | 1573 | description: VoCore Studio |
| 1574 | 1574 | "^voipac,.*": |
| 1575 | 1575 | description: Voipac Technologies s.r.o. |
| 1576 | + "^voltafield,.*": | |
| 1577 | + description: Voltafield Technology Corp. | |
| 1576 | 1578 | "^vot,.*": |
| 1577 | 1579 | description: Vision Optical Technology Co., Ltd. |
| 1578 | 1580 | "^vscom,.*": |
| @@ -0,0 +1,407 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +================ | |
| 4 | +ADIS16475 driver | |
| 5 | +================ | |
| 6 | + | |
| 7 | +This driver supports Analog Device's IMUs on SPI bus. | |
| 8 | + | |
| 9 | +1. Supported devices | |
| 10 | +==================== | |
| 11 | + | |
| 12 | +* `ADIS16465 <https://www.analog.com/ADIS16465>`_ | |
| 13 | +* `ADIS16467 <https://www.analog.com/ADIS16467>`_ | |
| 14 | +* `ADIS16470 <https://www.analog.com/ADIS16470>`_ | |
| 15 | +* `ADIS16475 <https://www.analog.com/ADIS16475>`_ | |
| 16 | +* `ADIS16477 <https://www.analog.com/ADIS16477>`_ | |
| 17 | +* `ADIS16500 <https://www.analog.com/ADIS16500>`_ | |
| 18 | +* `ADIS16505 <https://www.analog.com/ADIS16505>`_ | |
| 19 | +* `ADIS16507 <https://www.analog.com/ADIS16507>`_ | |
| 20 | + | |
| 21 | +Each supported device is a precision, miniature microelectromechanical system | |
| 22 | +(MEMS) inertial measurement unit (IMU) that includes a triaxial gyroscope and a | |
| 23 | +triaxial accelerometer. Each inertial sensor in the IMU device combines with | |
| 24 | +signal conditioning that optimizes dynamic performance. The factory calibration | |
| 25 | +characterizes each sensor for sensitivity, bias, alignment, linear acceleration | |
| 26 | +(gyroscope bias), and point of percussion (accelerometer location). As a result, | |
| 27 | +each sensor has dynamic compensation formulas that provide accurate sensor | |
| 28 | +measurements over a broad set of conditions. | |
| 29 | + | |
| 30 | +2. Device attributes | |
| 31 | +==================== | |
| 32 | + | |
| 33 | +Accelerometer, gyroscope measurements are always provided. Furthermore, the | |
| 34 | +driver offers the capability to retrieve the delta angle and the delta velocity | |
| 35 | +measurements computed by the device. | |
| 36 | + | |
| 37 | +The delta angle measurements represent a calculation of angular displacement | |
| 38 | +between each sample update, while the delta velocity measurements represent a | |
| 39 | +calculation of linear velocity change between each sample update. | |
| 40 | + | |
| 41 | +Finally, temperature data are provided which show a coarse measurement of | |
| 42 | +the temperature inside of the IMU device. This data is most useful for | |
| 43 | +monitoring relative changes in the thermal environment. | |
| 44 | + | |
| 45 | +The signal chain of each inertial sensor (accelerometers and gyroscopes) | |
| 46 | +includes the application of unique correction formulas, which are derived from | |
| 47 | +extensive characterization of bias, sensitivity, alignment, response to linear | |
| 48 | +acceleration (gyroscopes), and point of percussion (accelerometer location) | |
| 49 | +over a temperature range of −40°C to +85°C, for each ADIS device. These | |
| 50 | +correction formulas are not accessible, but users do have the opportunity to | |
| 51 | +adjust the bias for each sensor individually through the calibbias attribute. | |
| 52 | + | |
| 53 | +Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``, | |
| 54 | +where X is the IIO index of the device. Under these folders reside a set of | |
| 55 | +device files, depending on the characteristics and features of the hardware | |
| 56 | +device in questions. These files are consistently generalized and documented in | |
| 57 | +the IIO ABI documentation. | |
| 58 | + | |
| 59 | +The following tables show the adis16475 related device files, found in the | |
| 60 | +specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. | |
| 61 | + | |
| 62 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 63 | +| 3-Axis Accelerometer related device files | Description | | |
| 64 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 65 | +| in_accel_scale | Scale for the accelerometer channels. | | |
| 66 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 67 | +| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. | | |
| 68 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 69 | +| in_accel_calibbias_x | x-axis acceleration offset correction | | |
| 70 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 71 | +| in_accel_x_raw | Raw X-axis accelerometer channel value. | | |
| 72 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 73 | +| in_accel_calibbias_y | y-axis acceleration offset correction | | |
| 74 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 75 | +| in_accel_y_raw | Raw Y-axis accelerometer channel value. | | |
| 76 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 77 | +| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. | | |
| 78 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 79 | +| in_accel_z_raw | Raw Z-axis accelerometer channel value. | | |
| 80 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 81 | +| in_deltavelocity_scale | Scale for delta velocity channels. | | |
| 82 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 83 | +| in_deltavelocity_x_raw | Raw X-axis delta velocity channel value. | | |
| 84 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 85 | +| in_deltavelocity_y_raw | Raw Y-axis delta velocity channel value. | | |
| 86 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 87 | +| in_deltavelocity_z_raw | Raw Z-axis delta velocity channel value. | | |
| 88 | ++-------------------------------------------+----------------------------------------------------------+ | |
| 89 | + | |
| 90 | ++---------------------------------------+------------------------------------------------------+ | |
| 91 | +| 3-Axis Gyroscope related device files | Description | | |
| 92 | ++---------------------------------------+------------------------------------------------------+ | |
| 93 | +| in_anglvel_scale | Scale for the gyroscope channels. | | |
| 94 | ++---------------------------------------+------------------------------------------------------+ | |
| 95 | +| in_anglvel_x_calibbias | Calibration offset for the X-axis gyroscope channel. | | |
| 96 | ++---------------------------------------+------------------------------------------------------+ | |
| 97 | +| in_anglvel_calibbias_x | x-axis gyroscope offset correction | | |
| 98 | ++---------------------------------------+------------------------------------------------------+ | |
| 99 | +| in_anglvel_x_raw | Raw X-axis gyroscope channel value. | | |
| 100 | ++---------------------------------------+------------------------------------------------------+ | |
| 101 | +| in_anglvel_calibbias_y | y-axis gyroscope offset correction | | |
| 102 | ++---------------------------------------+------------------------------------------------------+ | |
| 103 | +| in_anglvel_y_raw | Raw Y-axis gyroscope channel value. | | |
| 104 | ++---------------------------------------+------------------------------------------------------+ | |
| 105 | +| in_anglvel_z_calibbias | Calibration offset for the Z-axis gyroscope channel. | | |
| 106 | ++---------------------------------------+------------------------------------------------------+ | |
| 107 | +| in_anglvel_z_raw | Raw Z-axis gyroscope channel value. | | |
| 108 | ++---------------------------------------+------------------------------------------------------+ | |
| 109 | +| in_deltaangl_scale | Scale for delta angle channels. | | |
| 110 | ++---------------------------------------+------------------------------------------------------+ | |
| 111 | +| in_deltaangl_x_raw | Raw X-axis delta angle channel value. | | |
| 112 | ++---------------------------------------+------------------------------------------------------+ | |
| 113 | +| in_deltaangl_y_raw | Raw Y-axis delta angle channel value. | | |
| 114 | ++---------------------------------------+------------------------------------------------------+ | |
| 115 | +| in_deltaangl_z_raw | Raw Z-axis delta angle channel value. | | |
| 116 | ++---------------------------------------+------------------------------------------------------+ | |
| 117 | + | |
| 118 | ++----------------------------------+-------------------------------------------+ | |
| 119 | +| Temperature sensor related files | Description | | |
| 120 | ++----------------------------------+-------------------------------------------+ | |
| 121 | +| in_temp0_raw | Raw temperature channel value. | | |
| 122 | ++----------------------------------+-------------------------------------------+ | |
| 123 | +| in_temp0_scale | Scale for the temperature sensor channel. | | |
| 124 | ++----------------------------------+-------------------------------------------+ | |
| 125 | + | |
| 126 | ++-------------------------------+---------------------------------------------------------+ | |
| 127 | +| Miscellaneous device files | Description | | |
| 128 | ++-------------------------------+---------------------------------------------------------+ | |
| 129 | +| name | Name of the IIO device. | | |
| 130 | ++-------------------------------+---------------------------------------------------------+ | |
| 131 | +| sampling_frequency | Currently selected sample rate. | | |
| 132 | ++-------------------------------+---------------------------------------------------------+ | |
| 133 | +| filter_low_pass_3db_frequency | Bandwidth for the accelerometer and gyroscope channels. | | |
| 134 | ++-------------------------------+---------------------------------------------------------+ | |
| 135 | + | |
| 136 | +The following table shows the adis16475 related device debug files, found in the | |
| 137 | +specific device debug folder path ``/sys/kernel/debug/iio/iio:deviceX``. | |
| 138 | + | |
| 139 | ++----------------------+-------------------------------------------------------------------------+ | |
| 140 | +| Debugfs device files | Description | | |
| 141 | ++----------------------+-------------------------------------------------------------------------+ | |
| 142 | +| serial_number | The serial number of the chip in hexadecimal format. | | |
| 143 | ++----------------------+-------------------------------------------------------------------------+ | |
| 144 | +| product_id | Chip specific product id (e.g. 16475, 16500, 16505, etc.). | | |
| 145 | ++----------------------+-------------------------------------------------------------------------+ | |
| 146 | +| flash_count | The number of flash writes performed on the device. | | |
| 147 | ++----------------------+-------------------------------------------------------------------------+ | |
| 148 | +| firmware_revision | String containing the firmware revision in the following format ##.##. | | |
| 149 | ++----------------------+-------------------------------------------------------------------------+ | |
| 150 | +| firmware_date | String containing the firmware date in the following format mm-dd-yyyy. | | |
| 151 | ++----------------------+-------------------------------------------------------------------------+ | |
| 152 | + | |
| 153 | +Channels processed values | |
| 154 | +------------------------- | |
| 155 | + | |
| 156 | +A channel value can be read from its _raw attribute. The value returned is the | |
| 157 | +raw value as reported by the devices. To get the processed value of the channel, | |
| 158 | +apply the following formula: | |
| 159 | + | |
| 160 | +.. code-block:: bash | |
| 161 | + | |
| 162 | + processed value = (_raw + _offset) * _scale | |
| 163 | + | |
| 164 | +Where _offset and _scale are device attributes. If no _offset attribute is | |
| 165 | +present, simply assume its value is 0. | |
| 166 | + | |
| 167 | +The adis16475 driver offers data for 5 types of channels, the table below shows | |
| 168 | +the measurement units for the processed value, which are defined by the IIO | |
| 169 | +framework: | |
| 170 | + | |
| 171 | ++-------------------------------------+---------------------------+ | |
| 172 | +| Channel type | Measurement unit | | |
| 173 | ++-------------------------------------+---------------------------+ | |
| 174 | +| Acceleration on X, Y, and Z axis | Meters per Second squared | | |
| 175 | ++-------------------------------------+---------------------------+ | |
| 176 | +| Angular velocity on X, Y and Z axis | Radians per second | | |
| 177 | ++-------------------------------------+---------------------------+ | |
| 178 | +| Delta velocity on X. Y, and Z axis | Meters per Second | | |
| 179 | ++-------------------------------------+---------------------------+ | |
| 180 | +| Delta angle on X, Y, and Z axis | Radians | | |
| 181 | ++-------------------------------------+---------------------------+ | |
| 182 | +| Temperature | Millidegrees Celsius | | |
| 183 | ++-------------------------------------+---------------------------+ | |
| 184 | + | |
| 185 | +Usage examples | |
| 186 | +-------------- | |
| 187 | + | |
| 188 | +Show device name: | |
| 189 | + | |
| 190 | +.. code-block:: bash | |
| 191 | + | |
| 192 | + root:/sys/bus/iio/devices/iio:device0> cat name | |
| 193 | + adis16505-2 | |
| 194 | + | |
| 195 | +Show accelerometer channels value: | |
| 196 | + | |
| 197 | +.. code-block:: bash | |
| 198 | + | |
| 199 | + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw | |
| 200 | + -275924 | |
| 201 | + root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw | |
| 202 | + -30142222 | |
| 203 | + root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw | |
| 204 | + 261265769 | |
| 205 | + root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale | |
| 206 | + 0.000000037 | |
| 207 | + | |
| 208 | +- X-axis acceleration = in_accel_x_raw * in_accel_scale = −0.010209188 m/s^2 | |
| 209 | +- Y-axis acceleration = in_accel_y_raw * in_accel_scale = −1.115262214 m/s^2 | |
| 210 | +- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 9.666833453 m/s^2 | |
| 211 | + | |
| 212 | +Show gyroscope channels value: | |
| 213 | + | |
| 214 | +.. code-block:: bash | |
| 215 | + | |
| 216 | + root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_x_raw | |
| 217 | + -3324626 | |
| 218 | + root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_raw | |
| 219 | + 1336980 | |
| 220 | + root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_z_raw | |
| 221 | + -602983 | |
| 222 | + root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_scale | |
| 223 | + 0.000000006 | |
| 224 | + | |
| 225 | +- X-axis angular velocity = in_anglvel_x_raw * in_anglvel_scale = −0.019947756 rad/s | |
| 226 | +- Y-axis angular velocity = in_anglvel_y_raw * in_anglvel_scale = 0.00802188 rad/s | |
| 227 | +- Z-axis angular velocity = in_anglvel_z_raw * in_anglvel_scale = −0.003617898 rad/s | |
| 228 | + | |
| 229 | +Set calibration offset for accelerometer channels: | |
| 230 | + | |
| 231 | +.. code-block:: bash | |
| 232 | + | |
| 233 | + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias | |
| 234 | + 0 | |
| 235 | + | |
| 236 | + root:/sys/bus/iio/devices/iio:device0> echo 5000 > in_accel_x_calibbias | |
| 237 | + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias | |
| 238 | + 5000 | |
| 239 | + | |
| 240 | +Set calibration offset for gyroscope channels: | |
| 241 | + | |
| 242 | +.. code-block:: bash | |
| 243 | + | |
| 244 | + root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias | |
| 245 | + 0 | |
| 246 | + | |
| 247 | + root:/sys/bus/iio/devices/iio:device0> echo -5000 > in_anglvel_y_calibbias | |
| 248 | + root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias | |
| 249 | + -5000 | |
| 250 | + | |
| 251 | +Set sampling frequency: | |
| 252 | + | |
| 253 | +.. code-block:: bash | |
| 254 | + | |
| 255 | + root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency | |
| 256 | + 2000.000000 | |
| 257 | + | |
| 258 | + root:/sys/bus/iio/devices/iio:device0> echo 1000 > sampling_frequency | |
| 259 | + 1000.000000 | |
| 260 | + | |
| 261 | +Set bandwidth for accelerometer and gyroscope: | |
| 262 | + | |
| 263 | +.. code-block:: bash | |
| 264 | + | |
| 265 | + root:/sys/bus/iio/devices/iio:device0> cat filter_low_pass_3db_frequency | |
| 266 | + 720 | |
| 267 | + | |
| 268 | + root:/sys/bus/iio/devices/iio:device0> echo 360 > filter_low_pass_3db_frequency | |
| 269 | + root:/sys/bus/iio/devices/iio:device0> cat filter_low_pass_3db_frequency | |
| 270 | + 360 | |
| 271 | + | |
| 272 | +Show serial number: | |
| 273 | + | |
| 274 | +.. code-block:: bash | |
| 275 | + | |
| 276 | + root:/sys/kernel/debug/iio/iio:device0> cat serial_number | |
| 277 | + 0x04f9 | |
| 278 | + | |
| 279 | +Show product id: | |
| 280 | + | |
| 281 | +.. code-block:: bash | |
| 282 | + | |
| 283 | + root:/sys/kernel/debug/iio/iio:device0> cat product_id | |
| 284 | + 16505 | |
| 285 | + | |
| 286 | +Show flash count: | |
| 287 | + | |
| 288 | +.. code-block:: bash | |
| 289 | + | |
| 290 | + root:/sys/kernel/debug/iio/iio:device0> cat flash_count | |
| 291 | + 150 | |
| 292 | + | |
| 293 | +Show firmware revision: | |
| 294 | + | |
| 295 | +.. code-block:: bash | |
| 296 | + | |
| 297 | + root:/sys/kernel/debug/iio/iio:device0> cat firmware_revision | |
| 298 | + 1.6 | |
| 299 | + | |
| 300 | +Show firmware date: | |
| 301 | + | |
| 302 | +.. code-block:: bash | |
| 303 | + | |
| 304 | + root:/sys/kernel/debug/iio/iio:device0> cat firmware_date | |
| 305 | + 06-27-2019 | |
| 306 | + | |
| 307 | +3. Device buffers | |
| 308 | +================= | |
| 309 | + | |
| 310 | +This driver supports IIO buffers. | |
| 311 | + | |
| 312 | +All devices support retrieving the raw acceleration, gyroscope and temperature | |
| 313 | +measurements using buffers. | |
| 314 | + | |
| 315 | +The following device families also support retrieving the delta velocity, delta | |
| 316 | +angle and temperature measurements using buffers: | |
| 317 | + | |
| 318 | +- ADIS16477 | |
| 319 | +- ADIS16500 | |
| 320 | +- ADIS16505 | |
| 321 | +- ADIS16507 | |
| 322 | + | |
| 323 | +However, when retrieving acceleration or gyroscope data using buffers, delta | |
| 324 | +readings will not be available and vice versa. | |
| 325 | + | |
| 326 | +Usage examples | |
| 327 | +-------------- | |
| 328 | + | |
| 329 | +Set device trigger in current_trigger, if not already set: | |
| 330 | + | |
| 331 | +.. code-block:: bash | |
| 332 | + | |
| 333 | + root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger | |
| 334 | + | |
| 335 | + root:/sys/bus/iio/devices/iio:device0> echo adis16505-2-dev0 > trigger/current_trigger | |
| 336 | + root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger | |
| 337 | + adis16505-2-dev0 | |
| 338 | + | |
| 339 | +Select channels for buffer read: | |
| 340 | + | |
| 341 | +.. code-block:: bash | |
| 342 | + | |
| 343 | + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_x_en | |
| 344 | + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_y_en | |
| 345 | + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_z_en | |
| 346 | + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_temp0_en | |
| 347 | + | |
| 348 | +Set the number of samples to be stored in the buffer: | |
| 349 | + | |
| 350 | +.. code-block:: bash | |
| 351 | + | |
| 352 | + root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length | |
| 353 | + | |
| 354 | +Enable buffer readings: | |
| 355 | + | |
| 356 | +.. code-block:: bash | |
| 357 | + | |
| 358 | + root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable | |
| 359 | + | |
| 360 | +Obtain buffered data: | |
| 361 | + | |
| 362 | +.. code-block:: bash | |
| 363 | + | |
| 364 | + root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0 | |
| 365 | + ... | |
| 366 | + 00001680 01 1f 00 00 ff ff fe ef 00 00 47 bf 00 03 35 55 |..........G...5U| | |
| 367 | + 00001690 01 1f 00 00 ff ff ff d9 00 00 46 f1 00 03 35 35 |..........F...55| | |
| 368 | + 000016a0 01 1f 00 00 ff ff fe fc 00 00 46 cb 00 03 35 7b |..........F...5{| | |
| 369 | + 000016b0 01 1f 00 00 ff ff fe 41 00 00 47 0d 00 03 35 8b |.......A..G...5.| | |
| 370 | + 000016c0 01 1f 00 00 ff ff fe 37 00 00 46 b4 00 03 35 90 |.......7..F...5.| | |
| 371 | + 000016d0 01 1d 00 00 ff ff fe 5a 00 00 45 d7 00 03 36 08 |.......Z..E...6.| | |
| 372 | + 000016e0 01 1b 00 00 ff ff fe fb 00 00 45 e7 00 03 36 60 |..........E...6`| | |
| 373 | + 000016f0 01 1a 00 00 ff ff ff 17 00 00 46 bc 00 03 36 de |..........F...6.| | |
| 374 | + 00001700 01 1a 00 00 ff ff fe 59 00 00 46 d7 00 03 37 b8 |.......Y..F...7.| | |
| 375 | + 00001710 01 1a 00 00 ff ff fe ae 00 00 46 95 00 03 37 ba |..........F...7.| | |
| 376 | + 00001720 01 1a 00 00 ff ff fe c5 00 00 46 63 00 03 37 9f |..........Fc..7.| | |
| 377 | + 00001730 01 1a 00 00 ff ff fe 55 00 00 46 89 00 03 37 c1 |.......U..F...7.| | |
| 378 | + 00001740 01 1a 00 00 ff ff fe 31 00 00 46 aa 00 03 37 f7 |.......1..F...7.| | |
| 379 | + ... | |
| 380 | + | |
| 381 | +See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered | |
| 382 | +data is structured. | |
| 383 | + | |
| 384 | +4. IIO Interfacing Tools | |
| 385 | +======================== | |
| 386 | + | |
| 387 | +Linux Kernel Tools | |
| 388 | +------------------ | |
| 389 | + | |
| 390 | +Linux Kernel provides some userspace tools that can be used to retrieve data | |
| 391 | +from IIO sysfs: | |
| 392 | + | |
| 393 | +* lsiio: example application that provides a list of IIO devices and triggers | |
| 394 | +* iio_event_monitor: example application that reads events from an IIO device | |
| 395 | + and prints them | |
| 396 | +* iio_generic_buffer: example application that reads data from buffer | |
| 397 | +* iio_utils: set of APIs, typically used to access sysfs files. | |
| 398 | + | |
| 399 | +LibIIO | |
| 400 | +------ | |
| 401 | + | |
| 402 | +LibIIO is a C/C++ library that provides generic access to IIO devices. The | |
| 403 | +library abstracts the low-level details of the hardware, and provides a simple | |
| 404 | +yet complete programming interface that can be used for advanced projects. | |
| 405 | + | |
| 406 | +For more information about LibIIO, please see: | |
| 407 | +https://github.com/analogdevicesinc/libiio | |
| @@ -0,0 +1,152 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +============================= | |
| 4 | +Industrial IIO device buffers | |
| 5 | +============================= | |
| 6 | + | |
| 7 | +1. Overview | |
| 8 | +=========== | |
| 9 | + | |
| 10 | +The Industrial I/O core offers a way for continuous data capture based on a | |
| 11 | +trigger source. Multiple data channels can be read at once from | |
| 12 | +``/dev/iio:deviceX`` character device node, thus reducing the CPU load. | |
| 13 | + | |
| 14 | +Devices with buffer support feature an additional sub-directory in the | |
| 15 | +``/sys/bus/iio/devices/iio:deviceX/`` directory hierarchy, called bufferY, where | |
| 16 | +Y defaults to 0, for devices with a single buffer. | |
| 17 | + | |
| 18 | +2. Buffer attributes | |
| 19 | +==================== | |
| 20 | + | |
| 21 | +An IIO buffer has an associated attributes directory under | |
| 22 | +``/sys/bus/iio/iio:deviceX/bufferY/``. The attributes are described below. | |
| 23 | + | |
| 24 | +``length`` | |
| 25 | +---------- | |
| 26 | + | |
| 27 | +Read / Write attribute which states the total number of data samples (capacity) | |
| 28 | +that can be stored by the buffer. | |
| 29 | + | |
| 30 | +``enable`` | |
| 31 | +---------- | |
| 32 | + | |
| 33 | +Read / Write attribute which starts / stops the buffer capture. This file should | |
| 34 | +be written last, after length and selection of scan elements. Writing a non-zero | |
| 35 | +value may result in an error, such as EINVAL, if, for example, an unsupported | |
| 36 | +combination of channels is given. | |
| 37 | + | |
| 38 | +``watermark`` | |
| 39 | +------------- | |
| 40 | + | |
| 41 | +Read / Write positive integer attribute specifying the maximum number of scan | |
| 42 | +elements to wait for. | |
| 43 | + | |
| 44 | +Poll will block until the watermark is reached. | |
| 45 | + | |
| 46 | +Blocking read will wait until the minimum between the requested read amount or | |
| 47 | +the low watermark is available. | |
| 48 | + | |
| 49 | +Non-blocking read will retrieve the available samples from the buffer even if | |
| 50 | +there are less samples than the watermark level. This allows the application to | |
| 51 | +block on poll with a timeout and read the available samples after the timeout | |
| 52 | +expires and thus have a maximum delay guarantee. | |
| 53 | + | |
| 54 | +Data available | |
| 55 | +-------------- | |
| 56 | + | |
| 57 | +Read-only attribute indicating the bytes of data available in the buffer. In the | |
| 58 | +case of an output buffer, this indicates the amount of empty space available to | |
| 59 | +write data to. In the case of an input buffer, this indicates the amount of data | |
| 60 | +available for reading. | |
| 61 | + | |
| 62 | +Scan elements | |
| 63 | +------------- | |
| 64 | + | |
| 65 | +The meta information associated with a channel data placed in a buffer is called | |
| 66 | +a scan element. The scan elements attributes are presented below. | |
| 67 | + | |
| 68 | +**_en** | |
| 69 | + | |
| 70 | +Read / Write attribute used for enabling a channel. If and only if its value | |
| 71 | +is non-zero, then a triggered capture will contain data samples for this | |
| 72 | +channel. | |
| 73 | + | |
| 74 | +**_index** | |
| 75 | + | |
| 76 | +Read-only unsigned integer attribute specifying the position of the channel in | |
| 77 | +the buffer. Note these are not dependent on what is enabled and may not be | |
| 78 | +contiguous. Thus for userspace to establish the full layout these must be used | |
| 79 | +in conjunction with all _en attributes to establish which channels are present, | |
| 80 | +and the relevant _type attributes to establish the data storage format. | |
| 81 | + | |
| 82 | +**_type** | |
| 83 | + | |
| 84 | +Read-only attribute containing the description of the scan element data storage | |
| 85 | +within the buffer and hence the form in which it is read from userspace. Format | |
| 86 | +is [be|le]:[s|u]bits/storagebits[Xrepeat][>>shift], where: | |
| 87 | + | |
| 88 | +- **be** or **le** specifies big or little-endian. | |
| 89 | +- **s** or **u** specifies if signed (2's complement) or unsigned. | |
| 90 | +- **bits** is the number of valid data bits. | |
| 91 | +- **storagebits** is the number of bits (after padding) that it occupies in the | |
| 92 | + buffer. | |
| 93 | +- **repeat** specifies the number of bits/storagebits repetitions. When the | |
| 94 | + repeat element is 0 or 1, then the repeat value is omitted. | |
| 95 | +- **shift** if specified, is the shift that needs to be applied prior to | |
| 96 | + masking out unused bits. | |
| 97 | + | |
| 98 | +For example, a driver for a 3-axis accelerometer with 12-bit resolution where | |
| 99 | +data is stored in two 8-bit registers is as follows:: | |
| 100 | + | |
| 101 | + 7 6 5 4 3 2 1 0 | |
| 102 | + +---+---+---+---+---+---+---+---+ | |
| 103 | + |D3 |D2 |D1 |D0 | X | X | X | X | (LOW byte, address 0x06) | |
| 104 | + +---+---+---+---+---+---+---+---+ | |
| 105 | + | |
| 106 | + 7 6 5 4 3 2 1 0 | |
| 107 | + +---+---+---+---+---+---+---+---+ | |
| 108 | + |D11|D10|D9 |D8 |D7 |D6 |D5 |D4 | (HIGH byte, address 0x07) | |
| 109 | + +---+---+---+---+---+---+---+---+ | |
| 110 | + | |
| 111 | +will have the following scan element type for each axis: | |
| 112 | + | |
| 113 | +.. code-block:: bash | |
| 114 | + | |
| 115 | + $ cat /sys/bus/iio/devices/iio:device0/buffer0/in_accel_y_type | |
| 116 | + le:s12/16>>4 | |
| 117 | + | |
| 118 | +A userspace application will interpret data samples read from the buffer as | |
| 119 | +two-byte little-endian signed data, that needs a 4 bits right shift before | |
| 120 | +masking out the 12 valid bits of data. | |
| 121 | + | |
| 122 | +It is also worth mentioning that the data in the buffer will be naturally | |
| 123 | +aligned, so the userspace application has to handle the buffers accordingly. | |
| 124 | + | |
| 125 | +Take for example, a driver with four channels with the following description: | |
| 126 | +- channel0: index: 0, type: be:u16/16>>0 | |
| 127 | +- channel1: index: 1, type: be:u32/32>>0 | |
| 128 | +- channel2: index: 2, type: be:u32/32>>0 | |
| 129 | +- channel3: index: 3, type: be:u64/64>>0 | |
| 130 | + | |
| 131 | +If all channels are enabled, the data will be aligned in the buffer as follows:: | |
| 132 | + | |
| 133 | + 0-1 2 3 4-7 8-11 12 13 14 15 16-23 -> buffer byte number | |
| 134 | + +-----+---+---+-----+-----+---+---+---+---+-----+ | |
| 135 | + |CHN_0|PAD|PAD|CHN_1|CHN_2|PAD|PAD|PAD|PAD|CHN_3| -> buffer content | |
| 136 | + +-----+---+---+-----+-----+---+---+---+---+-----+ | |
| 137 | + | |
| 138 | +If only channel0 and channel3 are enabled, the data will be aligned in the | |
| 139 | +buffer as follows:: | |
| 140 | + | |
| 141 | + 0-1 2 3 4 5 6 7 8-15 -> buffer byte number | |
| 142 | + +-----+---+---+---+---+---+---+-----+ | |
| 143 | + |CHN_0|PAD|PAD|PAD|PAD|PAD|PAD|CHN_3| -> buffer content | |
| 144 | + +-----+---+---+---+---+---+---+-----+ | |
| 145 | + | |
| 146 | +Typically the buffered data is found in raw format (unscaled with no offset | |
| 147 | +applied), however there are corner cases in which the buffered data may be found | |
| 148 | +in a processed form. Please note that these corner cases are not addressed by | |
| 149 | +this documentation. | |
| 150 | + | |
| 151 | +Please see ``Documentation/ABI/testing/sysfs-bus-iio`` for a complete | |
| 152 | +description of the attributes. | |
| @@ -8,7 +8,14 @@ Industrial I/O · 7 unmodified lines | ||
| 8 | 8 | :maxdepth: 1 |
| 9 | 9 | |
| 10 | 10 | iio_configfs |
| 11 | + iio_devbuf | |
| 11 | 12 | |
| 12 | − ep93xx_adc | |
| 13 | +Industrial I/O Kernel Drivers | |
| 14 | +============================= | |
| 13 | 15 | |
| 16 | +.. toctree:: | |
| 17 | + :maxdepth: 1 | |
| 18 | + | |
| 19 | + adis16475 | |
| 14 | 20 | bno055 |
| 21 | + ep93xx_adc | |
| @@ -579,6 +579,12 @@ F: drivers/iio/accel/adxl372.c · 578 unmodified lines | ||
| 579 | 579 | F: drivers/iio/accel/adxl372_i2c.c |
| 580 | 580 | F: drivers/iio/accel/adxl372_spi.c |
| 581 | 581 | |
| 582 | +AF8133J THREE-AXIS MAGNETOMETER DRIVER | |
| 583 | +M: Ondřej Jirman <megi@xff.cz> | |
| 584 | +S: Maintained | |
| 585 | +F: Documentation/devicetree/bindings/iio/magnetometer/voltafield,af8133j.yaml | |
| 586 | +F: drivers/iio/magnetometer/af8133j.c | |
| 587 | + | |
| 582 | 588 | AF9013 MEDIA DRIVER |
| 583 | 589 | L: linux-media@vger.kernel.org |
| 584 | 590 | S: Orphan |
| @@ -1158,7 +1164,7 @@ L: linux-iio@vger.kernel.org · 573 unmodified lines | ||
| 1158 | 1164 | S: Supported |
| 1159 | 1165 | W: http://ez.analog.com/community/linux-device-drivers |
| 1160 | 1166 | F: Documentation/devicetree/bindings/iio/adc/adi,ad7091r* |
| 1161 | −F: drivers/iio/adc/drivers/iio/adc/ad7091r* | |
| 1167 | +F: drivers/iio/adc/ad7091r* | |
| 1162 | 1168 | |
| 1163 | 1169 | ANALOG DEVICES INC AD7192 DRIVER |
| 1164 | 1170 | M: Alexandru Tachici <alexandru.tachici@analog.com> |
| @@ -1281,6 +1287,14 @@ W: https://ez.analog.com/linux-software-drivers · 116 unmodified lines | ||
| 1281 | 1287 | F: Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml |
| 1282 | 1288 | F: drivers/hwmon/adm1177.c |
| 1283 | 1289 | |
| 1290 | +ANALOG DEVICES INC ADMFM2000 DRIVER | |
| 1291 | +M: Kim Seer Paller <kimseer.paller@analog.com> | |
| 1292 | +L: linux-iio@vger.kernel.org | |
| 1293 | +S: Supported | |
| 1294 | +W: https://ez.analog.com/linux-software-drivers | |
| 1295 | +F: Documentation/devicetree/bindings/iio/frequency/adi,admfm2000.yaml | |
| 1296 | +F: drivers/iio/frequency/admfm2000.c | |
| 1297 | + | |
| 1284 | 1298 | ANALOG DEVICES INC ADMV1013 DRIVER |
| 1285 | 1299 | M: Antoniu Miclaus <antoniu.miclaus@analog.com> |
| 1286 | 1300 | L: linux-iio@vger.kernel.org |
| @@ -9509,7 +9523,7 @@ T: git git://linuxtv.org/media_tree.git · 8222 unmodified lines | ||
| 9509 | 9523 | F: drivers/media/usb/hdpvr/ |
| 9510 | 9524 | |
| 9511 | 9525 | HEWLETT PACKARD ENTERPRISE ILO CHIF DRIVER |
| 9512 | −M: Matt Hsiao <matt.hsiao@hpe.com> | |
| 9526 | +M: Keng-Yu Lin <keng-yu.lin@hpe.com> | |
| 9513 | 9527 | S: Supported |
| 9514 | 9528 | F: drivers/misc/hpilo.[ch] |
| 9515 | 9529 | |
| @@ -9879,10 +9893,11 @@ F: drivers/iio/pressure/hsc030pa* · 363 unmodified lines | ||
| 9879 | 9893 | |
| 9880 | 9894 | HONEYWELL MPRLS0025PA PRESSURE SENSOR SERIES IIO DRIVER |
| 9881 | 9895 | M: Andreas Klinger <ak@it-klinger.de> |
| 9896 | +M: Petre Rodan <petre.rodan@subdimension.ro> | |
| 9882 | 9897 | L: linux-iio@vger.kernel.org |
| 9883 | 9898 | S: Maintained |
| 9884 | 9899 | F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml |
| 9885 | −F: drivers/iio/pressure/mprls0025pa.c | |
| 9900 | +F: drivers/iio/pressure/mprls0025pa* | |
| 9886 | 9901 | |
| 9887 | 9902 | HP BIOSCFG DRIVER |
| 9888 | 9903 | M: Jorge Lopez <jorge.lopez2@hp.com> |
| @@ -10475,6 +10490,14 @@ L: linux-media@vger.kernel.org · 586 unmodified lines | ||
| 10475 | 10490 | S: Maintained |
| 10476 | 10491 | F: drivers/media/rc/iguanair.c |
| 10477 | 10492 | |
| 10493 | +IIO BACKEND FRAMEWORK | |
| 10494 | +M: Nuno Sa <nuno.sa@analog.com> | |
| 10495 | +R: Olivier Moysan <olivier.moysan@foss.st.com> | |
| 10496 | +L: linux-iio@vger.kernel.org | |
| 10497 | +S: Maintained | |
| 10498 | +F: drivers/iio/industrialio-backend.c | |
| 10499 | +F: include/linux/iio/backend.h | |
| 10500 | + | |
| 10478 | 10501 | IIO DIGITAL POTENTIOMETER DAC |
| 10479 | 10502 | M: Peter Rosin <peda@axentia.se> |
| 10480 | 10503 | L: linux-iio@vger.kernel.org |
| @@ -10497,6 +10520,7 @@ L: linux-iio@vger.kernel.org · 16 unmodified lines | ||
| 10497 | 10520 | S: Maintained |
| 10498 | 10521 | F: drivers/iio/industrialio-gts-helper.c |
| 10499 | 10522 | F: include/linux/iio/iio-gts-helper.h |
| 10523 | +F: drivers/iio/test/iio-test-gts.c | |
| 10500 | 10524 | |
| 10501 | 10525 | IIO MULTIPLEXER |
| 10502 | 10526 | M: Peter Rosin <peda@axentia.se> |
| @@ -14487,6 +14511,13 @@ F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml · 3984 unmodified lines | ||
| 14487 | 14511 | F: drivers/nvmem/microchip-otpc.c |
| 14488 | 14512 | F: include/dt-bindings/nvmem/microchip,sama7g5-otpc.h |
| 14489 | 14513 | |
| 14514 | +MICROCHIP PAC1934 POWER/ENERGY MONITOR DRIVER | |
| 14515 | +M: Marius Cristea <marius.cristea@microchip.com> | |
| 14516 | +L: linux-iio@vger.kernel.org | |
| 14517 | +S: Supported | |
| 14518 | +F: Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml | |
| 14519 | +F: drivers/iio/adc/pac1934.c | |
| 14520 | + | |
| 14490 | 14521 | MICROCHIP PCI1XXXX GP DRIVER |
| 14491 | 14522 | M: Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com> |
| 14492 | 14523 | M: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> |
| @@ -23566,8 +23597,8 @@ F: Documentation/driver-api/vme.rst · 9073 unmodified lines | ||
| 23566 | 23597 | F: drivers/staging/vme_user/ |
| 23567 | 23598 | |
| 23568 | 23599 | VMWARE BALLOON DRIVER |
| 23569 | −M: Nadav Amit <namit@vmware.com> | |
| 23570 | −R: VMware PV-Drivers Reviewers <pv-drivers@vmware.com> | |
| 23600 | +M: Jerrin Shaji George <jerrin.shaji-george@broadcom.com> | |
| 23601 | +R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com> | |
| 23571 | 23602 | L: linux-kernel@vger.kernel.org |
| 23572 | 23603 | S: Supported |
| 23573 | 23604 | F: drivers/misc/vmw_balloon.c |
| @@ -24344,6 +24375,14 @@ M: Harsha <harsha.harsha@amd.com> · 770 unmodified lines | ||
| 24344 | 24375 | S: Maintained |
| 24345 | 24376 | F: drivers/crypto/xilinx/zynqmp-sha.c |
| 24346 | 24377 | |
| 24378 | +XILINX ZYNQMP NVMEM DRIVER | |
| 24379 | +M: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com> | |
| 24380 | +M: Kalyani Akula <kalyani.akula@amd.com> | |
| 24381 | +R: Michal Simek <michal.simek@amd.com> | |
| 24382 | +S: Maintained | |
| 24383 | +F: Documentation/devicetree/bindings/nvmem/xlnx,zynqmp-nvmem.yaml | |
| 24384 | +F: drivers/nvmem/zynqmp_nvmem.c | |
| 24385 | + | |
| 24347 | 24386 | XILLYBUS DRIVER |
| 24348 | 24387 | M: Eli Billauer <eli.billauer@gmail.com> |
| 24349 | 24388 | L: linux-kernel@vger.kernel.org |
| @@ -472,10 +472,6 @@ config X86_MPPARSE · 471 unmodified lines | ||
| 472 | 472 | For old smp systems that do not have proper acpi support. Newer systems |
| 473 | 473 | (esp with 64bit cpus) with acpi support, MADT and DSDT will override it |
| 474 | 474 | |
| 475 | −config GOLDFISH | |
| 476 | − def_bool y | |
| 477 | − depends on X86_GOLDFISH | |
| 478 | − | |
| 479 | 475 | config X86_CPU_RESCTRL |
| 480 | 476 | bool "x86 CPU resource control support" |
| 481 | 477 | depends on X86 && (CPU_SUP_INTEL || CPU_SUP_AMD) |
| @@ -7,9 +7,10 @@ · 6 unmodified lines | ||
| 7 | 7 | #include "speakup.h" |
| 8 | 8 | #include "spk_priv.h" |
| 9 | 9 | |
| 10 | −static int misc_registered; | |
| 10 | +static int synth_registered, synthu_registered; | |
| 11 | 11 | static int dev_opened; |
| 12 | 12 | |
| 13 | +/* Latin1 version */ | |
| 13 | 14 | static ssize_t speakup_file_write(struct file *fp, const char __user *buffer, |
| 14 | 15 | size_t nbytes, loff_t *ppos) |
| 15 | 16 | { |
| @@ -34,6 +35,98 @@ static ssize_t speakup_file_write(struct file *fp, const char __user *buffer, · 18 unmodified lines | ||
| 34 | 35 | return (ssize_t)nbytes; |
| 35 | 36 | } |
| 36 | 37 | |
| 38 | +/* UTF-8 version */ | |
| 39 | +static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer, | |
| 40 | + size_t nbytes, loff_t *ppos) | |
| 41 | +{ | |
| 42 | + size_t count = nbytes, want; | |
| 43 | + const char __user *ptr = buffer; | |
| 44 | + size_t bytes; | |
| 45 | + unsigned long flags; | |
| 46 | + unsigned char buf[256]; | |
| 47 | + u16 ubuf[256]; | |
| 48 | + size_t in, in2, out; | |
| 49 | + | |
| 50 | + if (!synth) | |
| 51 | + return -ENODEV; | |
| 52 | + | |
| 53 | + want = 1; | |
| 54 | + while (count >= want) { | |
| 55 | + /* Copy some UTF-8 piece from userland */ | |
| 56 | + bytes = min(count, sizeof(buf)); | |
| 57 | + if (copy_from_user(buf, ptr, bytes)) | |
| 58 | + return -EFAULT; | |
| 59 | + | |
| 60 | + /* Convert to u16 */ | |
| 61 | + for (in = 0, out = 0; in < bytes; in++) { | |
| 62 | + unsigned char c = buf[in]; | |
| 63 | + int nbytes = 8 - fls(c ^ 0xff); | |
| 64 | + u32 value; | |
| 65 | + | |
| 66 | + switch (nbytes) { | |
| 67 | + case 8: /* 0xff */ | |
| 68 | + case 7: /* 0xfe */ | |
| 69 | + case 1: /* 0x80 */ | |
| 70 | + /* Invalid, drop */ | |
| 71 | + goto drop; | |
| 72 | + | |
| 73 | + case 0: | |
| 74 | + /* ASCII, copy */ | |
| 75 | + ubuf[out++] = c; | |
| 76 | + continue; | |
| 77 | + | |
| 78 | + default: | |
| 79 | + /* 2..6-byte UTF-8 */ | |
| 80 | + | |
| 81 | + if (bytes - in < nbytes) { | |
| 82 | + /* We don't have it all yet, stop here | |
| 83 | + * and wait for the rest | |
| 84 | + */ | |
| 85 | + bytes = in; | |
| 86 | + want = nbytes; | |
| 87 | + continue; | |
| 88 | + } | |
| 89 | + | |
| 90 | + /* First byte */ | |
| 91 | + value = c & ((1u << (7 - nbytes)) - 1); | |
| 92 | + | |
| 93 | + /* Other bytes */ | |
| 94 | + for (in2 = 2; in2 <= nbytes; in2++) { | |
| 95 | + c = buf[in + 1]; | |
| 96 | + if ((c & 0xc0) != 0x80) { | |
| 97 | + /* Invalid, drop the head */ | |
| 98 | + want = 1; | |
| 99 | + goto drop; | |
| 100 | + } | |
| 101 | + value = (value << 6) | (c & 0x3f); | |
| 102 | + in++; | |
| 103 | + } | |
| 104 | + | |
| 105 | + if (value < 0x10000) | |
| 106 | + ubuf[out++] = value; | |
| 107 | + want = 1; | |
| 108 | + break; | |
| 109 | + } | |
| 110 | +drop: | |
| 111 | + /* empty statement */; | |
| 112 | + } | |
| 113 | + | |
| 114 | + count -= bytes; | |
| 115 | + ptr += bytes; | |
| 116 | + | |
| 117 | + /* And speak this up */ | |
| 118 | + if (out) { | |
| 119 | + spin_lock_irqsave(&speakup_info.spinlock, flags); | |
| 120 | + for (in = 0; in < out; in++) | |
| 121 | + synth_buffer_add(ubuf[in]); | |
| 122 | + synth_start(); | |
| 123 | + spin_unlock_irqrestore(&speakup_info.spinlock, flags); | |
| 124 | + } | |
| 125 | + } | |
| 126 | + | |
| 127 | + return (ssize_t)(nbytes - count); | |
| 128 | +} | |
| 129 | + | |
| 37 | 130 | static ssize_t speakup_file_read(struct file *fp, char __user *buf, |
| 38 | 131 | size_t nbytes, loff_t *ppos) |
| 39 | 132 | { |
| @@ -62,31 +155,57 @@ static const struct file_operations synth_fops = { · 22 unmodified lines | ||
| 62 | 155 | .release = speakup_file_release, |
| 63 | 156 | }; |
| 64 | 157 | |
| 158 | +static const struct file_operations synthu_fops = { | |
| 159 | + .read = speakup_file_read, | |
| 160 | + .write = speakup_file_writeu, | |
| 161 | + .open = speakup_file_open, | |
| 162 | + .release = speakup_file_release, | |
| 163 | +}; | |
| 164 | + | |
| 65 | 165 | static struct miscdevice synth_device = { |
| 66 | 166 | .minor = MISC_DYNAMIC_MINOR, |
| 67 | 167 | .name = "synth", |
| 68 | 168 | .fops = &synth_fops, |
| 69 | 169 | }; |
| 70 | 170 | |
| 171 | +static struct miscdevice synthu_device = { | |
| 172 | + .minor = MISC_DYNAMIC_MINOR, | |
| 173 | + .name = "synthu", | |
| 174 | + .fops = &synthu_fops, | |
| 175 | +}; | |
| 176 | + | |
| 71 | 177 | void speakup_register_devsynth(void) |
| 72 | 178 | { |
| 73 | − if (misc_registered != 0) | |
| 74 | − return; | |
| 75 | −/* zero it so if register fails, deregister will not ref invalid ptrs */ | |
| 76 | − if (misc_register(&synth_device)) { | |
| 77 | − pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); | |
| 78 | − } else { | |
| 79 | − pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", | |
| 80 | − MISC_MAJOR, synth_device.minor); | |
| 81 | − misc_registered = 1; | |
| 179 | + if (!synth_registered) { | |
| 180 | + if (misc_register(&synth_device)) { | |
| 181 | + pr_warn("Couldn't initialize miscdevice /dev/synth.\n"); | |
| 182 | + } else { | |
| 183 | + pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n", | |
| 184 | + MISC_MAJOR, synth_device.minor); | |
| 185 | + synth_registered = 1; | |
| 186 | + } | |
| 187 | + } | |
| 188 | + if (!synthu_registered) { | |
| 189 | + if (misc_register(&synthu_device)) { | |
| 190 | + pr_warn("Couldn't initialize miscdevice /dev/synthu.\n"); | |
| 191 | + } else { | |
| 192 | + pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n", | |
| 193 | + MISC_MAJOR, synthu_device.minor); | |
| 194 | + synthu_registered = 1; | |
| 195 | + } | |
| 82 | 196 | } |
| 83 | 197 | } |
| 84 | 198 | |
| 85 | 199 | void speakup_unregister_devsynth(void) |
| 86 | 200 | { |
| 87 | − if (!misc_registered) | |
| 88 | − return; | |
| 89 | − pr_info("speakup: unregistering synth device /dev/synth\n"); | |
| 90 | − misc_deregister(&synth_device); | |
| 91 | − misc_registered = 0; | |
| 201 | + if (synth_registered) { | |
| 202 | + pr_info("speakup: unregistering synth device /dev/synth\n"); | |
| 203 | + misc_deregister(&synth_device); | |
| 204 | + synth_registered = 0; | |
| 205 | + } | |
| 206 | + if (synthu_registered) { | |
| 207 | + pr_info("speakup: unregistering synth device /dev/synthu\n"); | |
| 208 | + misc_deregister(&synthu_device); | |
| 209 | + synthu_registered = 0; | |
| 210 | + } | |
| 92 | 211 | } |
| @@ -208,8 +208,10 @@ void spk_do_flush(void) · 207 unmodified lines | ||
| 208 | 208 | wake_up_process(speakup_task); |
| 209 | 209 | } |
| 210 | 210 | |
| 211 | −void synth_write(const char *buf, size_t count) | |
| 211 | +void synth_write(const char *_buf, size_t count) | |
| 212 | 212 | { |
| 213 | + const unsigned char *buf = (const unsigned char *) _buf; | |
| 214 | + | |
| 213 | 215 | while (count--) |
| 214 | 216 | synth_buffer_add(*buf++); |
| 215 | 217 | synth_start(); |
| @@ -925,7 +925,6 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) · 924 unmodified lines | ||
| 925 | 925 | int i; |
| 926 | 926 | |
| 927 | 927 | for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { |
| 928 | − unsigned long page_addr; | |
| 929 | 928 | bool on_lru; |
| 930 | 929 | |
| 931 | 930 | if (!alloc->pages[i].page_ptr) |
| @@ -933,7 +932,6 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) · 1 unmodified line | ||
| 933 | 932 | |
| 934 | 933 | on_lru = list_lru_del_obj(&binder_freelist, |
| 935 | 934 | &alloc->pages[i].lru); |
| 936 | − page_addr = alloc->buffer + i * PAGE_SIZE; | |
| 937 | 935 | binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, |
| 938 | 936 | "%s: %d: page %d %s\n", |
| 939 | 937 | __func__, alloc->pid, i, |
| @@ -297,30 +297,30 @@ struct mhi_ring_element { · 296 unmodified lines | ||
| 297 | 297 | __le32 dword[2]; |
| 298 | 298 | }; |
| 299 | 299 | |
| 300 | +#define MHI_STATE_LIST \ | |
| 301 | + mhi_state(RESET, "RESET") \ | |
| 302 | + mhi_state(READY, "READY") \ | |
| 303 | + mhi_state(M0, "M0") \ | |
| 304 | + mhi_state(M1, "M1") \ | |
| 305 | + mhi_state(M2, "M2") \ | |
| 306 | + mhi_state(M3, "M3") \ | |
| 307 | + mhi_state(M3_FAST, "M3_FAST") \ | |
| 308 | + mhi_state(BHI, "BHI") \ | |
| 309 | + mhi_state_end(SYS_ERR, "SYS ERROR") | |
| 310 | + | |
| 311 | +#undef mhi_state | |
| 312 | +#undef mhi_state_end | |
| 313 | + | |
| 314 | +#define mhi_state(a, b) case MHI_STATE_##a: return b; | |
| 315 | +#define mhi_state_end(a, b) case MHI_STATE_##a: return b; | |
| 316 | + | |
| 300 | 317 | static inline const char *mhi_state_str(enum mhi_state state) |
| 301 | 318 | { |
| 302 | 319 | switch (state) { |
| 303 | − case MHI_STATE_RESET: | |
| 304 | − return "RESET"; | |
| 305 | − case MHI_STATE_READY: | |
| 306 | − return "READY"; | |
| 307 | − case MHI_STATE_M0: | |
| 308 | − return "M0"; | |
| 309 | − case MHI_STATE_M1: | |
| 310 | − return "M1"; | |
| 311 | − case MHI_STATE_M2: | |
| 312 | − return "M2"; | |
| 313 | − case MHI_STATE_M3: | |
| 314 | − return "M3"; | |
| 315 | − case MHI_STATE_M3_FAST: | |
| 316 | − return "M3 FAST"; | |
| 317 | − case MHI_STATE_BHI: | |
| 318 | − return "BHI"; | |
| 319 | − case MHI_STATE_SYS_ERR: | |
| 320 | − return "SYS ERROR"; | |
| 320 | + MHI_STATE_LIST | |
| 321 | 321 | default: |
| 322 | 322 | return "Unknown state"; |
| 323 | 323 | } |
| 324 | −}; | |
| 324 | +} | |
| 325 | 325 | |
| 326 | 326 | #endif /* _MHI_COMMON_H */ |
| @@ -1149,8 +1149,9 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl) · 1148 unmodified lines | ||
| 1149 | 1149 | mhi_ep_mmio_mask_interrupts(mhi_cntrl); |
| 1150 | 1150 | mhi_ep_mmio_init(mhi_cntrl); |
| 1151 | 1151 | |
| 1152 | − mhi_cntrl->mhi_event = kzalloc(mhi_cntrl->event_rings * (sizeof(*mhi_cntrl->mhi_event)), | |
| 1153 | − GFP_KERNEL); | |
| 1152 | + mhi_cntrl->mhi_event = kcalloc(mhi_cntrl->event_rings, | |
| 1153 | + sizeof(*mhi_cntrl->mhi_event), | |
| 1154 | + GFP_KERNEL); | |
| 1154 | 1155 | if (!mhi_cntrl->mhi_event) |
| 1155 | 1156 | return -ENOMEM; |
| 1156 | 1157 | |
| @@ -1496,7 +1497,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, · 339 unmodified lines | ||
| 1496 | 1497 | mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item", |
| 1497 | 1498 | sizeof(struct mhi_ep_ring_item), 0, |
| 1498 | 1499 | 0, NULL); |
| 1499 | − if (!mhi_cntrl->ev_ring_el_cache) { | |
| 1500 | + if (!mhi_cntrl->ring_item_cache) { | |
| 1500 | 1501 | ret = -ENOMEM; |
| 1501 | 1502 | goto err_destroy_tre_buf_cache; |
| 1502 | 1503 | } |
| @@ -395,7 +395,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) · 394 unmodified lines | ||
| 395 | 395 | void *buf; |
| 396 | 396 | dma_addr_t dma_addr; |
| 397 | 397 | size_t size, fw_sz; |
| 398 | − int i, ret; | |
| 398 | + int ret; | |
| 399 | 399 | |
| 400 | 400 | if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { |
| 401 | 401 | dev_err(dev, "Device MHI is not in valid state\n"); |
| @@ -408,15 +408,6 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) · 6 unmodified lines | ||
| 408 | 408 | if (ret) |
| 409 | 409 | dev_err(dev, "Could not capture serial number via BHI\n"); |
| 410 | 410 | |
| 411 | − for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) { | |
| 412 | − ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), | |
| 413 | − &mhi_cntrl->oem_pk_hash[i]); | |
| 414 | − if (ret) { | |
| 415 | − dev_err(dev, "Could not capture OEM PK HASH via BHI\n"); | |
| 416 | − break; | |
| 417 | − } | |
| 418 | − } | |
| 419 | − | |
| 420 | 411 | /* wait for ready on pass through or any other execution environment */ |
| 421 | 412 | if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee)) |
| 422 | 413 | goto fw_load_ready_state; |
| @@ -20,50 +20,49 @@ · 19 unmodified lines | ||
| 20 | 20 | #include <linux/wait.h> |
| 21 | 21 | #include "internal.h" |
| 22 | 22 | |
| 23 | +#define CREATE_TRACE_POINTS | |
| 24 | +#include "trace.h" | |
| 25 | + | |
| 23 | 26 | static DEFINE_IDA(mhi_controller_ida); |
| 24 | 27 | |
| 28 | +#undef mhi_ee | |
| 29 | +#undef mhi_ee_end | |
| 30 | + | |
| 31 | +#define mhi_ee(a, b) [MHI_EE_##a] = b, | |
| 32 | +#define mhi_ee_end(a, b) [MHI_EE_##a] = b, | |
| 33 | + | |
| 25 | 34 | const char * const mhi_ee_str[MHI_EE_MAX] = { |
| 26 | − [MHI_EE_PBL] = "PRIMARY BOOTLOADER", | |
| 27 | − [MHI_EE_SBL] = "SECONDARY BOOTLOADER", | |
| 28 | − [MHI_EE_AMSS] = "MISSION MODE", | |
| 29 | − [MHI_EE_RDDM] = "RAMDUMP DOWNLOAD MODE", | |
| 30 | − [MHI_EE_WFW] = "WLAN FIRMWARE", | |
| 31 | − [MHI_EE_PTHRU] = "PASS THROUGH", | |
| 32 | − [MHI_EE_EDL] = "EMERGENCY DOWNLOAD", | |
| 33 | − [MHI_EE_FP] = "FLASH PROGRAMMER", | |
| 34 | − [MHI_EE_DISABLE_TRANSITION] = "DISABLE", | |
| 35 | − [MHI_EE_NOT_SUPPORTED] = "NOT SUPPORTED", | |
| 35 | + MHI_EE_LIST | |
| 36 | 36 | }; |
| 37 | 37 | |
| 38 | +#undef dev_st_trans | |
| 39 | +#undef dev_st_trans_end | |
| 40 | + | |
| 41 | +#define dev_st_trans(a, b) [DEV_ST_TRANSITION_##a] = b, | |
| 42 | +#define dev_st_trans_end(a, b) [DEV_ST_TRANSITION_##a] = b, | |
| 43 | + | |
| 38 | 44 | const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = { |
| 39 | − [DEV_ST_TRANSITION_PBL] = "PBL", | |
| 40 | − [DEV_ST_TRANSITION_READY] = "READY", | |
| 41 | − [DEV_ST_TRANSITION_SBL] = "SBL", | |
| 42 | − [DEV_ST_TRANSITION_MISSION_MODE] = "MISSION MODE", | |
| 43 | − [DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER", | |
| 44 | − [DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR", | |
| 45 | − [DEV_ST_TRANSITION_DISABLE] = "DISABLE", | |
| 45 | + DEV_ST_TRANSITION_LIST | |
| 46 | 46 | }; |
| 47 | 47 | |
| 48 | +#undef ch_state_type | |
| 49 | +#undef ch_state_type_end | |
| 50 | + | |
| 51 | +#define ch_state_type(a, b) [MHI_CH_STATE_TYPE_##a] = b, | |
| 52 | +#define ch_state_type_end(a, b) [MHI_CH_STATE_TYPE_##a] = b, | |
| 53 | + | |
| 48 | 54 | const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = { |
| 49 | − [MHI_CH_STATE_TYPE_RESET] = "RESET", | |
| 50 | − [MHI_CH_STATE_TYPE_STOP] = "STOP", | |
| 51 | − [MHI_CH_STATE_TYPE_START] = "START", | |
| 55 | + MHI_CH_STATE_TYPE_LIST | |
| 52 | 56 | }; |
| 53 | 57 | |
| 58 | +#undef mhi_pm_state | |
| 59 | +#undef mhi_pm_state_end | |
| 60 | + | |
| 61 | +#define mhi_pm_state(a, b) [MHI_PM_STATE_##a] = b, | |
| 62 | +#define mhi_pm_state_end(a, b) [MHI_PM_STATE_##a] = b, | |
| 63 | + | |
| 54 | 64 | static const char * const mhi_pm_state_str[] = { |
| 55 | − [MHI_PM_STATE_DISABLE] = "DISABLE", | |
| 56 | − [MHI_PM_STATE_POR] = "POWER ON RESET", | |
| 57 | − [MHI_PM_STATE_M0] = "M0", | |
| 58 | − [MHI_PM_STATE_M2] = "M2", | |
| 59 | − [MHI_PM_STATE_M3_ENTER] = "M?->M3", | |
| 60 | − [MHI_PM_STATE_M3] = "M3", | |
| 61 | − [MHI_PM_STATE_M3_EXIT] = "M3->M0", | |
| 62 | − [MHI_PM_STATE_FW_DL_ERR] = "Firmware Download Error", | |
| 63 | − [MHI_PM_STATE_SYS_ERR_DETECT] = "SYS ERROR Detect", | |
| 64 | − [MHI_PM_STATE_SYS_ERR_PROCESS] = "SYS ERROR Process", | |
| 65 | − [MHI_PM_STATE_SHUTDOWN_PROCESS] = "SHUTDOWN Process", | |
| 66 | − [MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect", | |
| 65 | + MHI_PM_STATE_LIST | |
| 67 | 66 | }; |
| 68 | 67 | |
| 69 | 68 | const char *to_mhi_pm_state_str(u32 state) |
| @@ -97,11 +96,19 @@ static ssize_t oem_pk_hash_show(struct device *dev, · 27 unmodified lines | ||
| 97 | 96 | { |
| 98 | 97 | struct mhi_device *mhi_dev = to_mhi_device(dev); |
| 99 | 98 | struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; |
| 100 | − int i, cnt = 0; | |
| 99 | + u32 hash_segment[MHI_MAX_OEM_PK_HASH_SEGMENTS]; | |
| 100 | + int i, cnt = 0, ret; | |
| 101 | 101 | |
| 102 | − for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) | |
| 103 | − cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", | |
| 104 | − i, mhi_cntrl->oem_pk_hash[i]); | |
| 102 | + for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) { | |
| 103 | + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), &hash_segment[i]); | |
| 104 | + if (ret) { | |
| 105 | + dev_err(dev, "Could not capture OEM PK HASH\n"); | |
| 106 | + return ret; | |
| 107 | + } | |
| 108 | + } | |
| 109 | + | |
| 110 | + for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) | |
| 111 | + cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", i, hash_segment[i]); | |
| 105 | 112 | |
| 106 | 113 | return cnt; |
| 107 | 114 | } |
| @@ -907,7 +914,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, · 799 unmodified lines | ||
| 907 | 914 | struct mhi_chan *mhi_chan; |
| 908 | 915 | struct mhi_cmd *mhi_cmd; |
| 909 | 916 | struct mhi_device *mhi_dev; |
| 910 | − u32 soc_info; | |
| 911 | 917 | int ret, i; |
| 912 | 918 | |
| 913 | 919 | if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->regs || |
| @@ -982,17 +988,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, · 68 unmodified lines | ||
| 982 | 988 | mhi_cntrl->unmap_single = mhi_unmap_single_no_bb; |
| 983 | 989 | } |
| 984 | 990 | |
| 985 | − /* Read the MHI device info */ | |
| 986 | − ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, | |
| 987 | − SOC_HW_VERSION_OFFS, &soc_info); | |
| 988 | − if (ret) | |
| 989 | − goto err_destroy_wq; | |
| 990 | − | |
| 991 | − mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info); | |
| 992 | − mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info); | |
| 993 | − mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info); | |
| 994 | − mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info); | |
| 995 | − | |
| 996 | 991 | mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL); |
| 997 | 992 | if (mhi_cntrl->index < 0) { |
| 998 | 993 | ret = mhi_cntrl->index; |
| @@ -15,12 +15,6 @@ extern struct bus_type mhi_bus_type; · 14 unmodified lines | ||
| 15 | 15 | #define MHI_SOC_RESET_REQ_OFFSET 0xb0 |
| 16 | 16 | #define MHI_SOC_RESET_REQ BIT(0) |
| 17 | 17 | |
| 18 | −#define SOC_HW_VERSION_OFFS 0x224 | |
| 19 | −#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28) | |
| 20 | −#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16) | |
| 21 | −#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8) | |
| 22 | −#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0) | |
| 23 | − | |
| 24 | 18 | struct mhi_ctxt { |
| 25 | 19 | struct mhi_event_ctxt *er_ctxt; |
| 26 | 20 | struct mhi_chan_ctxt *chan_ctxt; |
| @@ -42,6 +36,11 @@ enum mhi_ch_state_type { · 15 unmodified lines | ||
| 42 | 36 | MHI_CH_STATE_TYPE_MAX, |
| 43 | 37 | }; |
| 44 | 38 | |
| 39 | +#define MHI_CH_STATE_TYPE_LIST \ | |
| 40 | + ch_state_type(RESET, "RESET") \ | |
| 41 | + ch_state_type(STOP, "STOP") \ | |
| 42 | + ch_state_type_end(START, "START") | |
| 43 | + | |
| 45 | 44 | extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX]; |
| 46 | 45 | #define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \ |
| 47 | 46 | "INVALID_STATE" : \ |
| @@ -50,6 +49,18 @@ extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX]; · 2 unmodified lines | ||
| 50 | 49 | #define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \ |
| 51 | 50 | mode != MHI_DB_BRST_ENABLE) |
| 52 | 51 | |
| 52 | +#define MHI_EE_LIST \ | |
| 53 | + mhi_ee(PBL, "PRIMARY BOOTLOADER") \ | |
| 54 | + mhi_ee(SBL, "SECONDARY BOOTLOADER") \ | |
| 55 | + mhi_ee(AMSS, "MISSION MODE") \ | |
| 56 | + mhi_ee(RDDM, "RAMDUMP DOWNLOAD MODE")\ | |
| 57 | + mhi_ee(WFW, "WLAN FIRMWARE") \ | |
| 58 | + mhi_ee(PTHRU, "PASS THROUGH") \ | |
| 59 | + mhi_ee(EDL, "EMERGENCY DOWNLOAD") \ | |
| 60 | + mhi_ee(FP, "FLASH PROGRAMMER") \ | |
| 61 | + mhi_ee(DISABLE_TRANSITION, "DISABLE") \ | |
| 62 | + mhi_ee_end(NOT_SUPPORTED, "NOT SUPPORTED") | |
| 63 | + | |
| 53 | 64 | extern const char * const mhi_ee_str[MHI_EE_MAX]; |
| 54 | 65 | #define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \ |
| 55 | 66 | "INVALID_EE" : mhi_ee_str[ee]) |
| @@ -72,6 +83,15 @@ enum dev_st_transition { · 16 unmodified lines | ||
| 72 | 83 | DEV_ST_TRANSITION_MAX, |
| 73 | 84 | }; |
| 74 | 85 | |
| 86 | +#define DEV_ST_TRANSITION_LIST \ | |
| 87 | + dev_st_trans(PBL, "PBL") \ | |
| 88 | + dev_st_trans(READY, "READY") \ | |
| 89 | + dev_st_trans(SBL, "SBL") \ | |
| 90 | + dev_st_trans(MISSION_MODE, "MISSION MODE") \ | |
| 91 | + dev_st_trans(FP, "FLASH PROGRAMMER") \ | |
| 92 | + dev_st_trans(SYS_ERR, "SYS ERROR") \ | |
| 93 | + dev_st_trans_end(DISABLE, "DISABLE") | |
| 94 | + | |
| 75 | 95 | extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX]; |
| 76 | 96 | #define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \ |
| 77 | 97 | "INVALID_STATE" : dev_state_tran_str[state]) |
| @@ -88,11 +108,27 @@ enum mhi_pm_state { · 10 unmodified lines | ||
| 88 | 108 | MHI_PM_STATE_FW_DL_ERR, |
| 89 | 109 | MHI_PM_STATE_SYS_ERR_DETECT, |
| 90 | 110 | MHI_PM_STATE_SYS_ERR_PROCESS, |
| 111 | + MHI_PM_STATE_SYS_ERR_FAIL, | |
| 91 | 112 | MHI_PM_STATE_SHUTDOWN_PROCESS, |
| 92 | 113 | MHI_PM_STATE_LD_ERR_FATAL_DETECT, |
| 93 | 114 | MHI_PM_STATE_MAX |
| 94 | 115 | }; |
| 95 | 116 | |
| 117 | +#define MHI_PM_STATE_LIST \ | |
| 118 | + mhi_pm_state(DISABLE, "DISABLE") \ | |
| 119 | + mhi_pm_state(POR, "POWER ON RESET") \ | |
| 120 | + mhi_pm_state(M0, "M0") \ | |
| 121 | + mhi_pm_state(M2, "M2") \ | |
| 122 | + mhi_pm_state(M3_ENTER, "M?->M3") \ | |
| 123 | + mhi_pm_state(M3, "M3") \ | |
| 124 | + mhi_pm_state(M3_EXIT, "M3->M0") \ | |
| 125 | + mhi_pm_state(FW_DL_ERR, "Firmware Download Error") \ | |
| 126 | + mhi_pm_state(SYS_ERR_DETECT, "SYS ERROR Detect") \ | |
| 127 | + mhi_pm_state(SYS_ERR_PROCESS, "SYS ERROR Process") \ | |
| 128 | + mhi_pm_state(SYS_ERR_FAIL, "SYS ERROR Failure") \ | |
| 129 | + mhi_pm_state(SHUTDOWN_PROCESS, "SHUTDOWN Process") \ | |
| 130 | + mhi_pm_state_end(LD_ERR_FATAL_DETECT, "Linkdown or Error Fatal Detect") | |
| 131 | + | |
| 96 | 132 | #define MHI_PM_DISABLE BIT(0) |
| 97 | 133 | #define MHI_PM_POR BIT(1) |
| 98 | 134 | #define MHI_PM_M0 BIT(2) |
| @@ -104,14 +140,16 @@ enum mhi_pm_state { · 5 unmodified lines | ||
| 104 | 140 | #define MHI_PM_FW_DL_ERR BIT(7) |
| 105 | 141 | #define MHI_PM_SYS_ERR_DETECT BIT(8) |
| 106 | 142 | #define MHI_PM_SYS_ERR_PROCESS BIT(9) |
| 107 | −#define MHI_PM_SHUTDOWN_PROCESS BIT(10) | |
| 143 | +#define MHI_PM_SYS_ERR_FAIL BIT(10) | |
| 144 | +#define MHI_PM_SHUTDOWN_PROCESS BIT(11) | |
| 108 | 145 | /* link not accessible */ |
| 109 | −#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11) | |
| 146 | +#define MHI_PM_LD_ERR_FATAL_DETECT BIT(12) | |
| 110 | 147 | |
| 111 | 148 | #define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \ |
| 112 | 149 | MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \ |
| 113 | 150 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \ |
| 114 | − MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR))) | |
| 151 | + MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | \ | |
| 152 | + MHI_PM_FW_DL_ERR))) | |
| 115 | 153 | #define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR) |
| 116 | 154 | #define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT) |
| 117 | 155 | #define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access) |
| @@ -15,6 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/skbuff.h> |
| 16 | 16 | #include <linux/slab.h> |
| 17 | 17 | #include "internal.h" |
| 18 | +#include "trace.h" | |
| 18 | 19 | |
| 19 | 20 | int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, |
| 20 | 21 | void __iomem *base, u32 offset, u32 *out) |
| @@ -493,11 +494,8 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv) · 472 unmodified lines | ||
| 493 | 494 | |
| 494 | 495 | state = mhi_get_mhi_state(mhi_cntrl); |
| 495 | 496 | ee = mhi_get_exec_env(mhi_cntrl); |
| 496 | − dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n", | |
| 497 | − TO_MHI_EXEC_STR(mhi_cntrl->ee), | |
| 498 | − mhi_state_str(mhi_cntrl->dev_state), | |
| 499 | − TO_MHI_EXEC_STR(ee), mhi_state_str(state)); | |
| 500 | 497 | |
| 498 | + trace_mhi_intvec_states(mhi_cntrl, ee, state); | |
| 501 | 499 | if (state == MHI_STATE_SYS_ERR) { |
| 502 | 500 | dev_dbg(dev, "System error detected\n"); |
| 503 | 501 | pm_state = mhi_tryset_pm_state(mhi_cntrl, |
| @@ -838,6 +836,8 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, · 334 unmodified lines | ||
| 838 | 836 | while (dev_rp != local_rp) { |
| 839 | 837 | enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); |
| 840 | 838 | |
| 839 | + trace_mhi_ctrl_event(mhi_cntrl, local_rp); | |
| 840 | + | |
| 841 | 841 | switch (type) { |
| 842 | 842 | case MHI_PKT_TYPE_BW_REQ_EVENT: |
| 843 | 843 | { |
| @@ -1003,6 +1003,8 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, · 159 unmodified lines | ||
| 1003 | 1003 | while (dev_rp != local_rp && event_quota > 0) { |
| 1004 | 1004 | enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); |
| 1005 | 1005 | |
| 1006 | + trace_mhi_data_event(mhi_cntrl, local_rp); | |
| 1007 | + | |
| 1006 | 1008 | chan = MHI_TRE_GET_EV_CHID(local_rp); |
| 1007 | 1009 | |
| 1008 | 1010 | WARN_ON(chan >= mhi_cntrl->max_chan); |
| @@ -1243,6 +1245,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, · 234 unmodified lines | ||
| 1243 | 1245 | mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len); |
| 1244 | 1246 | mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain); |
| 1245 | 1247 | |
| 1248 | + trace_mhi_gen_tre(mhi_cntrl, mhi_chan, mhi_tre); | |
| 1246 | 1249 | /* increment WP */ |
| 1247 | 1250 | mhi_add_ring_element(mhi_cntrl, tre_ring); |
| 1248 | 1251 | mhi_add_ring_element(mhi_cntrl, buf_ring); |
| @@ -1337,9 +1340,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, · 88 unmodified lines | ||
| 1337 | 1340 | enum mhi_cmd_type cmd = MHI_CMD_NOP; |
| 1338 | 1341 | int ret; |
| 1339 | 1342 | |
| 1340 | − dev_dbg(dev, "%d: Updating channel state to: %s\n", mhi_chan->chan, | |
| 1341 | − TO_CH_STATE_TYPE_STR(to_state)); | |
| 1342 | − | |
| 1343 | + trace_mhi_channel_command_start(mhi_cntrl, mhi_chan, to_state, TPS("Updating")); | |
| 1343 | 1344 | switch (to_state) { |
| 1344 | 1345 | case MHI_CH_STATE_TYPE_RESET: |
| 1345 | 1346 | write_lock_irq(&mhi_chan->lock); |
| @@ -1406,9 +1407,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, · 60 unmodified lines | ||
| 1406 | 1407 | write_unlock_irq(&mhi_chan->lock); |
| 1407 | 1408 | } |
| 1408 | 1409 | |
| 1409 | − dev_dbg(dev, "%d: Channel state change to %s successful\n", | |
| 1410 | − mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state)); | |
| 1411 | − | |
| 1410 | + trace_mhi_channel_command_end(mhi_cntrl, mhi_chan, to_state, TPS("Updated")); | |
| 1412 | 1411 | exit_channel_update: |
| 1413 | 1412 | mhi_cntrl->runtime_put(mhi_cntrl); |
| 1414 | 1413 | mhi_device_put(mhi_cntrl->mhi_dev); |
| @@ -538,7 +538,7 @@ static struct mhi_event_config mhi_telit_fn980_hw_v1_events[] = { · 537 unmodified lines | ||
| 538 | 538 | MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101) |
| 539 | 539 | }; |
| 540 | 540 | |
| 541 | −static struct mhi_controller_config modem_telit_fn980_hw_v1_config = { | |
| 541 | +static const struct mhi_controller_config modem_telit_fn980_hw_v1_config = { | |
| 542 | 542 | .max_channels = 128, |
| 543 | 543 | .timeout_ms = 20000, |
| 544 | 544 | .num_channels = ARRAY_SIZE(mhi_telit_fn980_hw_v1_channels), |
| @@ -15,6 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/slab.h> |
| 16 | 16 | #include <linux/wait.h> |
| 17 | 17 | #include "internal.h" |
| 18 | +#include "trace.h" | |
| 18 | 19 | |
| 19 | 20 | /* |
| 20 | 21 | * Not all MHI state transitions are synchronous. Transitions like Linkdown, |
| @@ -36,7 +37,10 @@ · 15 unmodified lines | ||
| 36 | 37 | * M0 <--> M0 |
| 37 | 38 | * M0 -> FW_DL_ERR |
| 38 | 39 | * M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0 |
| 39 | − * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR | |
| 40 | + * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS | |
| 41 | + * SYS_ERR_PROCESS -> SYS_ERR_FAIL | |
| 42 | + * SYS_ERR_FAIL -> SYS_ERR_DETECT | |
| 43 | + * SYS_ERR_PROCESS --> POR | |
| 40 | 44 | * L2: SHUTDOWN_PROCESS -> LD_ERR_FATAL_DETECT |
| 41 | 45 | * SHUTDOWN_PROCESS -> DISABLE |
| 42 | 46 | * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT |
| @@ -93,7 +97,12 @@ static const struct mhi_pm_transitions dev_state_transitions[] = { · 50 unmodified lines | ||
| 93 | 97 | }, |
| 94 | 98 | { |
| 95 | 99 | MHI_PM_SYS_ERR_PROCESS, |
| 96 | − MHI_PM_POR | MHI_PM_SHUTDOWN_PROCESS | | |
| 100 | + MHI_PM_POR | MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | | |
| 101 | + MHI_PM_LD_ERR_FATAL_DETECT | |
| 102 | + }, | |
| 103 | + { | |
| 104 | + MHI_PM_SYS_ERR_FAIL, | |
| 105 | + MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS | | |
| 97 | 106 | MHI_PM_LD_ERR_FATAL_DETECT |
| 98 | 107 | }, |
| 99 | 108 | /* L2 States */ |
| @@ -123,6 +132,7 @@ enum mhi_pm_state __must_check mhi_tryset_pm_state(struct mhi_controller *mhi_cn · 23 unmodified lines | ||
| 123 | 132 | if (unlikely(!(dev_state_transitions[index].to_states & state))) |
| 124 | 133 | return cur_state; |
| 125 | 134 | |
| 135 | + trace_mhi_tryset_pm_state(mhi_cntrl, state); | |
| 126 | 136 | mhi_cntrl->pm_state = state; |
| 127 | 137 | return mhi_cntrl->pm_state; |
| 128 | 138 | } |
| @@ -629,7 +639,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) · 500 unmodified lines | ||
| 629 | 639 | !in_reset, timeout); |
| 630 | 640 | if (!ret || in_reset) { |
| 631 | 641 | dev_err(dev, "Device failed to exit MHI Reset state\n"); |
| 632 | − goto exit_sys_error_transition; | |
| 642 | + write_lock_irq(&mhi_cntrl->pm_lock); | |
| 643 | + cur_state = mhi_tryset_pm_state(mhi_cntrl, | |
| 644 | + MHI_PM_SYS_ERR_FAIL); | |
| 645 | + write_unlock_irq(&mhi_cntrl->pm_lock); | |
| 646 | + /* Shutdown may have occurred, otherwise cleanup now */ | |
| 647 | + if (cur_state != MHI_PM_SYS_ERR_FAIL) | |
| 648 | + goto exit_sys_error_transition; | |
| 633 | 649 | } |
| 634 | 650 | |
| 635 | 651 | /* |
| @@ -758,7 +774,6 @@ void mhi_pm_st_worker(struct work_struct *work) · 122 unmodified lines | ||
| 758 | 774 | struct mhi_controller *mhi_cntrl = container_of(work, |
| 759 | 775 | struct mhi_controller, |
| 760 | 776 | st_worker); |
| 761 | − struct device *dev = &mhi_cntrl->mhi_dev->dev; | |
| 762 | 777 | |
| 763 | 778 | spin_lock_irq(&mhi_cntrl->transition_lock); |
| 764 | 779 | list_splice_tail_init(&mhi_cntrl->transition_list, &head); |
| @@ -766,8 +781,8 @@ void mhi_pm_st_worker(struct work_struct *work) · 1 unmodified line | ||
| 766 | 781 | |
| 767 | 782 | list_for_each_entry_safe(itr, tmp, &head, node) { |
| 768 | 783 | list_del(&itr->node); |
| 769 | − dev_dbg(dev, "Handling state transition: %s\n", | |
| 770 | − TO_DEV_STATE_TRANS_STR(itr->state)); | |
| 784 | + | |
| 785 | + trace_mhi_pm_st_transition(mhi_cntrl, itr->state); | |
| 771 | 786 | |
| 772 | 787 | switch (itr->state) { |
| 773 | 788 | case DEV_ST_TRANSITION_PBL: |
| @@ -0,0 +1,282 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* | |
| 3 | + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#undef TRACE_SYSTEM | |
| 7 | +#define TRACE_SYSTEM mhi_host | |
| 8 | + | |
| 9 | +#if !defined(_TRACE_EVENT_MHI_HOST_H) || defined(TRACE_HEADER_MULTI_READ) | |
| 10 | +#define _TRACE_EVENT_MHI_HOST_H | |
| 11 | + | |
| 12 | +#include <linux/tracepoint.h> | |
| 13 | +#include <linux/trace_seq.h> | |
| 14 | +#include "../common.h" | |
| 15 | +#include "internal.h" | |
| 16 | + | |
| 17 | +#undef mhi_state | |
| 18 | +#undef mhi_state_end | |
| 19 | + | |
| 20 | +#define mhi_state(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a); | |
| 21 | +#define mhi_state_end(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a); | |
| 22 | + | |
| 23 | +MHI_STATE_LIST | |
| 24 | + | |
| 25 | +#undef mhi_state | |
| 26 | +#undef mhi_state_end | |
| 27 | + | |
| 28 | +#define mhi_state(a, b) { MHI_STATE_##a, b }, | |
| 29 | +#define mhi_state_end(a, b) { MHI_STATE_##a, b } | |
| 30 | + | |
| 31 | +#undef mhi_pm_state | |
| 32 | +#undef mhi_pm_state_end | |
| 33 | + | |
| 34 | +#define mhi_pm_state(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a); | |
| 35 | +#define mhi_pm_state_end(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a); | |
| 36 | + | |
| 37 | +MHI_PM_STATE_LIST | |
| 38 | + | |
| 39 | +#undef mhi_pm_state | |
| 40 | +#undef mhi_pm_state_end | |
| 41 | + | |
| 42 | +#define mhi_pm_state(a, b) { MHI_PM_STATE_##a, b }, | |
| 43 | +#define mhi_pm_state_end(a, b) { MHI_PM_STATE_##a, b } | |
| 44 | + | |
| 45 | +#undef mhi_ee | |
| 46 | +#undef mhi_ee_end | |
| 47 | + | |
| 48 | +#define mhi_ee(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a); | |
| 49 | +#define mhi_ee_end(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a); | |
| 50 | + | |
| 51 | +MHI_EE_LIST | |
| 52 | + | |
| 53 | +#undef mhi_ee | |
| 54 | +#undef mhi_ee_end | |
| 55 | + | |
| 56 | +#define mhi_ee(a, b) { MHI_EE_##a, b }, | |
| 57 | +#define mhi_ee_end(a, b) { MHI_EE_##a, b } | |
| 58 | + | |
| 59 | +#undef ch_state_type | |
| 60 | +#undef ch_state_type_end | |
| 61 | + | |
| 62 | +#define ch_state_type(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a); | |
| 63 | +#define ch_state_type_end(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a); | |
| 64 | + | |
| 65 | +MHI_CH_STATE_TYPE_LIST | |
| 66 | + | |
| 67 | +#undef ch_state_type | |
| 68 | +#undef ch_state_type_end | |
| 69 | + | |
| 70 | +#define ch_state_type(a, b) { MHI_CH_STATE_TYPE_##a, b }, | |
| 71 | +#define ch_state_type_end(a, b) { MHI_CH_STATE_TYPE_##a, b } | |
| 72 | + | |
| 73 | +#undef dev_st_trans | |
| 74 | +#undef dev_st_trans_end | |
| 75 | + | |
| 76 | +#define dev_st_trans(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a); | |
| 77 | +#define dev_st_trans_end(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a); | |
| 78 | + | |
| 79 | +DEV_ST_TRANSITION_LIST | |
| 80 | + | |
| 81 | +#undef dev_st_trans | |
| 82 | +#undef dev_st_trans_end | |
| 83 | + | |
| 84 | +#define dev_st_trans(a, b) { DEV_ST_TRANSITION_##a, b }, | |
| 85 | +#define dev_st_trans_end(a, b) { DEV_ST_TRANSITION_##a, b } | |
| 86 | + | |
| 87 | +#define TPS(x) tracepoint_string(x) | |
| 88 | + | |
| 89 | +TRACE_EVENT(mhi_gen_tre, | |
| 90 | + | |
| 91 | + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, | |
| 92 | + struct mhi_ring_element *mhi_tre), | |
| 93 | + | |
| 94 | + TP_ARGS(mhi_cntrl, mhi_chan, mhi_tre), | |
| 95 | + | |
| 96 | + TP_STRUCT__entry( | |
| 97 | + __string(name, mhi_cntrl->mhi_dev->name) | |
| 98 | + __field(int, ch_num) | |
| 99 | + __field(void *, wp) | |
| 100 | + __field(__le64, tre_ptr) | |
| 101 | + __field(__le32, dword0) | |
| 102 | + __field(__le32, dword1) | |
| 103 | + ), | |
| 104 | + | |
| 105 | + TP_fast_assign( | |
| 106 | + __assign_str(name, mhi_cntrl->mhi_dev->name); | |
| 107 | + __entry->ch_num = mhi_chan->chan; | |
| 108 | + __entry->wp = mhi_tre; | |
| 109 | + __entry->tre_ptr = mhi_tre->ptr; | |
| 110 | + __entry->dword0 = mhi_tre->dword[0]; | |
| 111 | + __entry->dword1 = mhi_tre->dword[1]; | |
| 112 | + ), | |
| 113 | + | |
| 114 | + TP_printk("%s: Chan: %d TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x\n", | |
| 115 | + __get_str(name), __entry->ch_num, __entry->wp, __entry->tre_ptr, | |
| 116 | + __entry->dword0, __entry->dword1) | |
| 117 | +); | |
| 118 | + | |
| 119 | +TRACE_EVENT(mhi_intvec_states, | |
| 120 | + | |
| 121 | + TP_PROTO(struct mhi_controller *mhi_cntrl, int dev_ee, int dev_state), | |
| 122 | + | |
| 123 | + TP_ARGS(mhi_cntrl, dev_ee, dev_state), | |
| 124 | + | |
| 125 | + TP_STRUCT__entry( | |
| 126 | + __string(name, mhi_cntrl->mhi_dev->name) | |
| 127 | + __field(int, local_ee) | |
| 128 | + __field(int, state) | |
| 129 | + __field(int, dev_ee) | |
| 130 | + __field(int, dev_state) | |
| 131 | + ), | |
| 132 | + | |
| 133 | + TP_fast_assign( | |
| 134 | + __assign_str(name, mhi_cntrl->mhi_dev->name); | |
| 135 | + __entry->local_ee = mhi_cntrl->ee; | |
| 136 | + __entry->state = mhi_cntrl->dev_state; | |
| 137 | + __entry->dev_ee = dev_ee; | |
| 138 | + __entry->dev_state = dev_state; | |
| 139 | + ), | |
| 140 | + | |
| 141 | + TP_printk("%s: Local EE: %s State: %s Device EE: %s Dev State: %s\n", | |
| 142 | + __get_str(name), | |
| 143 | + __print_symbolic(__entry->local_ee, MHI_EE_LIST), | |
| 144 | + __print_symbolic(__entry->state, MHI_STATE_LIST), | |
| 145 | + __print_symbolic(__entry->dev_ee, MHI_EE_LIST), | |
| 146 | + __print_symbolic(__entry->dev_state, MHI_STATE_LIST)) | |
| 147 | +); | |
| 148 | + | |
| 149 | +TRACE_EVENT(mhi_tryset_pm_state, | |
| 150 | + | |
| 151 | + TP_PROTO(struct mhi_controller *mhi_cntrl, int pm_state), | |
| 152 | + | |
| 153 | + TP_ARGS(mhi_cntrl, pm_state), | |
| 154 | + | |
| 155 | + TP_STRUCT__entry( | |
| 156 | + __string(name, mhi_cntrl->mhi_dev->name) | |
| 157 | + __field(int, pm_state) | |
| 158 | + ), | |
| 159 | + | |
| 160 | + TP_fast_assign( | |
| 161 | + __assign_str(name, mhi_cntrl->mhi_dev->name); | |
| 162 | + if (pm_state) | |
| 163 | + pm_state = __fls(pm_state); | |
| 164 | + __entry->pm_state = pm_state; | |
| 165 | + ), | |
| 166 | + | |
| 167 | + TP_printk("%s: PM state: %s\n", __get_str(name), | |
| 168 | + __print_symbolic(__entry->pm_state, MHI_PM_STATE_LIST)) | |
| 169 | +); | |
| 170 | + | |
| 171 | +DECLARE_EVENT_CLASS(mhi_process_event_ring, | |
| 172 | + | |
| 173 | + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp), | |
| 174 | + | |
| 175 | + TP_ARGS(mhi_cntrl, rp), | |
| 176 | + | |
| 177 | + TP_STRUCT__entry( | |
| 178 | + __string(name, mhi_cntrl->mhi_dev->name) | |
| 179 | + __field(__le32, dword0) | |
| 180 | + __field(__le32, dword1) | |
| 181 | + __field(int, state) | |
| 182 | + __field(__le64, ptr) | |
| 183 | + __field(void *, rp) | |
| 184 | + ), | |
| 185 | + | |
| 186 | + TP_fast_assign( | |
| 187 | + __assign_str(name, mhi_cntrl->mhi_dev->name); | |
| 188 | + __entry->rp = rp; | |
| 189 | + __entry->ptr = rp->ptr; | |
| 190 | + __entry->dword0 = rp->dword[0]; | |
| 191 | + __entry->dword1 = rp->dword[1]; | |
| 192 | + __entry->state = MHI_TRE_GET_EV_STATE(rp); | |
| 193 | + ), | |
| 194 | + | |
| 195 | + TP_printk("%s: TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x State: %s\n", | |
| 196 | + __get_str(name), __entry->rp, __entry->ptr, __entry->dword0, | |
| 197 | + __entry->dword1, __print_symbolic(__entry->state, MHI_STATE_LIST)) | |
| 198 | +); | |
| 199 | + | |
| 200 | +DEFINE_EVENT(mhi_process_event_ring, mhi_data_event, | |
| 201 | + | |
| 202 | + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp), | |
| 203 | + | |
| 204 | + TP_ARGS(mhi_cntrl, rp) | |
| 205 | +); | |
| 206 | + | |
| 207 | +DEFINE_EVENT(mhi_process_event_ring, mhi_ctrl_event, | |
| 208 | + | |
| 209 | + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp), | |
| 210 | + | |
| 211 | + TP_ARGS(mhi_cntrl, rp) | |
| 212 | +); | |
| 213 | + | |
| 214 | +DECLARE_EVENT_CLASS(mhi_update_channel_state, | |
| 215 | + | |
| 216 | + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state, | |
| 217 | + const char *reason), | |
| 218 | + | |
| 219 | + TP_ARGS(mhi_cntrl, mhi_chan, state, reason), | |
| 220 | + | |
| 221 | + TP_STRUCT__entry( | |
| 222 | + __string(name, mhi_cntrl->mhi_dev->name) | |
| 223 | + __field(int, ch_num) | |
| 224 | + __field(int, state) | |
| 225 | + __field(const char *, reason) | |
| 226 | + ), | |
| 227 | + | |
| 228 | + TP_fast_assign( | |
| 229 | + __assign_str(name, mhi_cntrl->mhi_dev->name); | |
| 230 | + __entry->ch_num = mhi_chan->chan; | |
| 231 | + __entry->state = state; | |
| 232 | + __entry->reason = reason; | |
| 233 | + ), | |
| 234 | + | |
| 235 | + TP_printk("%s: chan%d: %s state to: %s\n", | |
| 236 | + __get_str(name), __entry->ch_num, __entry->reason, | |
| 237 | + __print_symbolic(__entry->state, MHI_CH_STATE_TYPE_LIST)) | |
| 238 | +); | |
| 239 | + | |
| 240 | +DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_start, | |
| 241 | + | |
| 242 | + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state, | |
| 243 | + const char *reason), | |
| 244 | + | |
| 245 | + TP_ARGS(mhi_cntrl, mhi_chan, state, reason) | |
| 246 | +); | |
| 247 | + | |
| 248 | +DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_end, | |
| 249 | + | |
| 250 | + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state, | |
| 251 | + const char *reason), | |
| 252 | + | |
| 253 | + TP_ARGS(mhi_cntrl, mhi_chan, state, reason) | |
| 254 | +); | |
| 255 | + | |
| 256 | +TRACE_EVENT(mhi_pm_st_transition, | |
| 257 | + | |
| 258 | + TP_PROTO(struct mhi_controller *mhi_cntrl, int state), | |
| 259 | + | |
| 260 | + TP_ARGS(mhi_cntrl, state), | |
| 261 | + | |
| 262 | + TP_STRUCT__entry( | |
| 263 | + __string(name, mhi_cntrl->mhi_dev->name) | |
| 264 | + __field(int, state) | |
| 265 | + ), | |
| 266 | + | |
| 267 | + TP_fast_assign( | |
| 268 | + __assign_str(name, mhi_cntrl->mhi_dev->name); | |
| 269 | + __entry->state = state; | |
| 270 | + ), | |
| 271 | + | |
| 272 | + TP_printk("%s: Handling state transition: %s\n", __get_str(name), | |
| 273 | + __print_symbolic(__entry->state, DEV_ST_TRANSITION_LIST)) | |
| 274 | +); | |
| 275 | + | |
| 276 | +#endif | |
| 277 | +#undef TRACE_INCLUDE_PATH | |
| 278 | +#define TRACE_INCLUDE_PATH ../../drivers/bus/mhi/host | |
| 279 | +#undef TRACE_INCLUDE_FILE | |
| 280 | +#define TRACE_INCLUDE_FILE trace | |
| 281 | + | |
| 282 | +#include <trace/define_trace.h> | |
| @@ -8,3 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=CDX_BUS |
| 9 | 9 | |
| 10 | 10 | obj-$(CONFIG_CDX_BUS) += cdx.o controller/ |
| 11 | + | |
| 12 | +ifdef CONFIG_GENERIC_MSI_IRQ | |
| 13 | +obj-$(CONFIG_CDX_BUS) += cdx_msi.o | |
| 14 | +endif | |
| @@ -56,6 +56,7 @@ · 55 unmodified lines | ||
| 56 | 56 | */ |
| 57 | 57 | |
| 58 | 58 | #include <linux/init.h> |
| 59 | +#include <linux/irqdomain.h> | |
| 59 | 60 | #include <linux/kernel.h> |
| 60 | 61 | #include <linux/of.h> |
| 61 | 62 | #include <linux/of_device.h> |
| @@ -302,8 +303,19 @@ static int cdx_probe(struct device *dev) · 240 unmodified lines | ||
| 302 | 303 | { |
| 303 | 304 | struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver); |
| 304 | 305 | struct cdx_device *cdx_dev = to_cdx_device(dev); |
| 306 | + struct cdx_controller *cdx = cdx_dev->cdx; | |
| 305 | 307 | int error; |
| 306 | 308 | |
| 309 | + /* | |
| 310 | + * Setup MSI device data so that generic MSI alloc/free can | |
| 311 | + * be used by the device driver. | |
| 312 | + */ | |
| 313 | + if (cdx->msi_domain) { | |
| 314 | + error = msi_setup_device_data(&cdx_dev->dev); | |
| 315 | + if (error) | |
| 316 | + return error; | |
| 317 | + } | |
| 318 | + | |
| 307 | 319 | error = cdx_drv->probe(cdx_dev); |
| 308 | 320 | if (error) { |
| 309 | 321 | dev_err_probe(dev, error, "%s failed\n", __func__); |
| @@ -787,6 +799,7 @@ int cdx_device_add(struct cdx_dev_params *dev_params) · 477 unmodified lines | ||
| 787 | 799 | |
| 788 | 800 | /* Populate CDX dev params */ |
| 789 | 801 | cdx_dev->req_id = dev_params->req_id; |
| 802 | + cdx_dev->msi_dev_id = dev_params->msi_dev_id; | |
| 790 | 803 | cdx_dev->vendor = dev_params->vendor; |
| 791 | 804 | cdx_dev->device = dev_params->device; |
| 792 | 805 | cdx_dev->subsystem_vendor = dev_params->subsys_vendor; |
| @@ -804,12 +817,19 @@ int cdx_device_add(struct cdx_dev_params *dev_params) · 11 unmodified lines | ||
| 804 | 817 | cdx_dev->dev.bus = &cdx_bus_type; |
| 805 | 818 | cdx_dev->dev.dma_mask = &cdx_dev->dma_mask; |
| 806 | 819 | cdx_dev->dev.release = cdx_device_release; |
| 820 | + cdx_dev->msi_write_pending = false; | |
| 821 | + mutex_init(&cdx_dev->irqchip_lock); | |
| 807 | 822 | |
| 808 | 823 | /* Set Name */ |
| 809 | 824 | dev_set_name(&cdx_dev->dev, "cdx-%02x:%02x", |
| 810 | 825 | ((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (cdx_dev->bus_num & CDX_BUS_NUM_MASK)), |
| 811 | 826 | cdx_dev->dev_num); |
| 812 | 827 | |
| 828 | + if (cdx->msi_domain) { | |
| 829 | + cdx_dev->num_msi = dev_params->num_msi; | |
| 830 | + dev_set_msi_domain(&cdx_dev->dev, cdx->msi_domain); | |
| 831 | + } | |
| 832 | + | |
| 813 | 833 | ret = device_add(&cdx_dev->dev); |
| 814 | 834 | if (ret) { |
| 815 | 835 | dev_err(&cdx_dev->dev, |
| @@ -25,6 +25,8 @@ · 24 unmodified lines | ||
| 25 | 25 | * @req_id: Requestor ID associated with CDX device |
| 26 | 26 | * @class: Class of the CDX Device |
| 27 | 27 | * @revision: Revision of the CDX device |
| 28 | + * @msi_dev_id: MSI device ID associated with CDX device | |
| 29 | + * @num_msi: Number of MSI's supported by the device | |
| 28 | 30 | */ |
| 29 | 31 | struct cdx_dev_params { |
| 30 | 32 | struct cdx_controller *cdx; |
| @@ -40,6 +42,8 @@ struct cdx_dev_params { · 9 unmodified lines | ||
| 40 | 42 | u32 req_id; |
| 41 | 43 | u32 class; |
| 42 | 44 | u8 revision; |
| 45 | + u32 msi_dev_id; | |
| 46 | + u32 num_msi; | |
| 43 | 47 | }; |
| 44 | 48 | |
| 45 | 49 | /** |
| @@ -79,4 +83,12 @@ int cdx_device_add(struct cdx_dev_params *dev_params); · 33 unmodified lines | ||
| 79 | 83 | */ |
| 80 | 84 | struct device *cdx_bus_add(struct cdx_controller *cdx, u8 bus_num); |
| 81 | 85 | |
| 86 | +/** | |
| 87 | + * cdx_msi_domain_init - Init the CDX bus MSI domain. | |
| 88 | + * @dev: Device of the CDX bus controller | |
| 89 | + * | |
| 90 | + * Return: CDX MSI domain, NULL on failure | |
| 91 | + */ | |
| 92 | +struct irq_domain *cdx_msi_domain_init(struct device *dev); | |
| 93 | + | |
| 82 | 94 | #endif /* _CDX_H_ */ |
| @@ -0,0 +1,192 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * AMD CDX bus driver MSI support | |
| 4 | + * | |
| 5 | + * Copyright (C) 2022-2023, Advanced Micro Devices, Inc. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#include <linux/of.h> | |
| 9 | +#include <linux/of_device.h> | |
| 10 | +#include <linux/of_address.h> | |
| 11 | +#include <linux/of_irq.h> | |
| 12 | +#include <linux/irq.h> | |
| 13 | +#include <linux/irqdomain.h> | |
| 14 | +#include <linux/msi.h> | |
| 15 | +#include <linux/cdx/cdx_bus.h> | |
| 16 | + | |
| 17 | +#include "cdx.h" | |
| 18 | + | |
| 19 | +static void cdx_msi_write_msg(struct irq_data *irq_data, struct msi_msg *msg) | |
| 20 | +{ | |
| 21 | + struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); | |
| 22 | + struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); | |
| 23 | + | |
| 24 | + /* We would not operate on msg here rather we wait for irq_bus_sync_unlock() | |
| 25 | + * to be called from preemptible task context. | |
| 26 | + */ | |
| 27 | + msi_desc->msg = *msg; | |
| 28 | + cdx_dev->msi_write_pending = true; | |
| 29 | +} | |
| 30 | + | |
| 31 | +static void cdx_msi_write_irq_lock(struct irq_data *irq_data) | |
| 32 | +{ | |
| 33 | + struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); | |
| 34 | + struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); | |
| 35 | + | |
| 36 | + mutex_lock(&cdx_dev->irqchip_lock); | |
| 37 | +} | |
| 38 | + | |
| 39 | +static void cdx_msi_write_irq_unlock(struct irq_data *irq_data) | |
| 40 | +{ | |
| 41 | + struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); | |
| 42 | + struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev); | |
| 43 | + struct cdx_controller *cdx = cdx_dev->cdx; | |
| 44 | + struct cdx_device_config dev_config; | |
| 45 | + | |
| 46 | + if (!cdx_dev->msi_write_pending) { | |
| 47 | + mutex_unlock(&cdx_dev->irqchip_lock); | |
| 48 | + return; | |
| 49 | + } | |
| 50 | + | |
| 51 | + cdx_dev->msi_write_pending = false; | |
| 52 | + mutex_unlock(&cdx_dev->irqchip_lock); | |
| 53 | + | |
| 54 | + dev_config.msi.msi_index = msi_desc->msi_index; | |
| 55 | + dev_config.msi.data = msi_desc->msg.data; | |
| 56 | + dev_config.msi.addr = ((u64)(msi_desc->msg.address_hi) << 32) | msi_desc->msg.address_lo; | |
| 57 | + | |
| 58 | + /* | |
| 59 | + * dev_configure() is a controller callback which can interact with | |
| 60 | + * Firmware or other entities, and can sleep, so invoke this function | |
| 61 | + * outside of the mutex held region. | |
| 62 | + */ | |
| 63 | + dev_config.type = CDX_DEV_MSI_CONF; | |
| 64 | + if (cdx->ops->dev_configure) | |
| 65 | + cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config); | |
| 66 | +} | |
| 67 | + | |
| 68 | +int cdx_enable_msi(struct cdx_device *cdx_dev) | |
| 69 | +{ | |
| 70 | + struct cdx_controller *cdx = cdx_dev->cdx; | |
| 71 | + struct cdx_device_config dev_config; | |
| 72 | + | |
| 73 | + dev_config.type = CDX_DEV_MSI_ENABLE; | |
| 74 | + dev_config.msi_enable = true; | |
| 75 | + if (cdx->ops->dev_configure) { | |
| 76 | + return cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, | |
| 77 | + &dev_config); | |
| 78 | + } | |
| 79 | + | |
| 80 | + return -EOPNOTSUPP; | |
| 81 | +} | |
| 82 | +EXPORT_SYMBOL_GPL(cdx_enable_msi); | |
| 83 | + | |
| 84 | +void cdx_disable_msi(struct cdx_device *cdx_dev) | |
| 85 | +{ | |
| 86 | + struct cdx_controller *cdx = cdx_dev->cdx; | |
| 87 | + struct cdx_device_config dev_config; | |
| 88 | + | |
| 89 | + dev_config.type = CDX_DEV_MSI_ENABLE; | |
| 90 | + dev_config.msi_enable = false; | |
| 91 | + if (cdx->ops->dev_configure) | |
| 92 | + cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config); | |
| 93 | +} | |
| 94 | +EXPORT_SYMBOL_GPL(cdx_disable_msi); | |
| 95 | + | |
| 96 | +static struct irq_chip cdx_msi_irq_chip = { | |
| 97 | + .name = "CDX-MSI", | |
| 98 | + .irq_mask = irq_chip_mask_parent, | |
| 99 | + .irq_unmask = irq_chip_unmask_parent, | |
| 100 | + .irq_eoi = irq_chip_eoi_parent, | |
| 101 | + .irq_set_affinity = msi_domain_set_affinity, | |
| 102 | + .irq_write_msi_msg = cdx_msi_write_msg, | |
| 103 | + .irq_bus_lock = cdx_msi_write_irq_lock, | |
| 104 | + .irq_bus_sync_unlock = cdx_msi_write_irq_unlock | |
| 105 | +}; | |
| 106 | + | |
| 107 | +/* Convert an msi_desc to a unique identifier within the domain. */ | |
| 108 | +static irq_hw_number_t cdx_domain_calc_hwirq(struct cdx_device *dev, | |
| 109 | + struct msi_desc *desc) | |
| 110 | +{ | |
| 111 | + return ((irq_hw_number_t)dev->msi_dev_id << 10) | desc->msi_index; | |
| 112 | +} | |
| 113 | + | |
| 114 | +static void cdx_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) | |
| 115 | +{ | |
| 116 | + arg->desc = desc; | |
| 117 | + arg->hwirq = cdx_domain_calc_hwirq(to_cdx_device(desc->dev), desc); | |
| 118 | +} | |
| 119 | + | |
| 120 | +static int cdx_msi_prepare(struct irq_domain *msi_domain, | |
| 121 | + struct device *dev, | |
| 122 | + int nvec, msi_alloc_info_t *info) | |
| 123 | +{ | |
| 124 | + struct cdx_device *cdx_dev = to_cdx_device(dev); | |
| 125 | + struct device *parent = cdx_dev->cdx->dev; | |
| 126 | + struct msi_domain_info *msi_info; | |
| 127 | + u32 dev_id; | |
| 128 | + int ret; | |
| 129 | + | |
| 130 | + /* Retrieve device ID from requestor ID using parent device */ | |
| 131 | + ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask", | |
| 132 | + NULL, &dev_id); | |
| 133 | + if (ret) { | |
| 134 | + dev_err(dev, "of_map_id failed for MSI: %d\n", ret); | |
| 135 | + return ret; | |
| 136 | + } | |
| 137 | + | |
| 138 | +#ifdef GENERIC_MSI_DOMAIN_OPS | |
| 139 | + /* Set the device Id to be passed to the GIC-ITS */ | |
| 140 | + info->scratchpad[0].ul = dev_id; | |
| 141 | +#endif | |
| 142 | + | |
| 143 | + msi_info = msi_get_domain_info(msi_domain->parent); | |
| 144 | + | |
| 145 | + return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info); | |
| 146 | +} | |
| 147 | + | |
| 148 | +static struct msi_domain_ops cdx_msi_ops = { | |
| 149 | + .msi_prepare = cdx_msi_prepare, | |
| 150 | + .set_desc = cdx_msi_set_desc | |
| 151 | +}; | |
| 152 | + | |
| 153 | +static struct msi_domain_info cdx_msi_domain_info = { | |
| 154 | + .ops = &cdx_msi_ops, | |
| 155 | + .chip = &cdx_msi_irq_chip, | |
| 156 | + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | |
| 157 | + MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS | |
| 158 | +}; | |
| 159 | + | |
| 160 | +struct irq_domain *cdx_msi_domain_init(struct device *dev) | |
| 161 | +{ | |
| 162 | + struct device_node *np = dev->of_node; | |
| 163 | + struct fwnode_handle *fwnode_handle; | |
| 164 | + struct irq_domain *cdx_msi_domain; | |
| 165 | + struct device_node *parent_node; | |
| 166 | + struct irq_domain *parent; | |
| 167 | + | |
| 168 | + fwnode_handle = of_node_to_fwnode(np); | |
| 169 | + | |
| 170 | + parent_node = of_parse_phandle(np, "msi-map", 1); | |
| 171 | + if (!parent_node) { | |
| 172 | + dev_err(dev, "msi-map not present on cdx controller\n"); | |
| 173 | + return NULL; | |
| 174 | + } | |
| 175 | + | |
| 176 | + parent = irq_find_matching_fwnode(of_node_to_fwnode(parent_node), DOMAIN_BUS_NEXUS); | |
| 177 | + if (!parent || !msi_get_domain_info(parent)) { | |
| 178 | + dev_err(dev, "unable to locate ITS domain\n"); | |
| 179 | + return NULL; | |
| 180 | + } | |
| 181 | + | |
| 182 | + cdx_msi_domain = msi_create_irq_domain(fwnode_handle, &cdx_msi_domain_info, parent); | |
| 183 | + if (!cdx_msi_domain) { | |
| 184 | + dev_err(dev, "unable to create CDX-MSI domain\n"); | |
| 185 | + return NULL; | |
| 186 | + } | |
| 187 | + | |
| 188 | + dev_dbg(dev, "CDX-MSI domain created\n"); | |
| 189 | + | |
| 190 | + return cdx_msi_domain; | |
| 191 | +} | |
| 192 | +EXPORT_SYMBOL_NS_GPL(cdx_msi_domain_init, CDX_BUS_CONTROLLER); | |
| @@ -9,6 +9,7 @@ if CDX_BUS · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | config CDX_CONTROLLER |
| 11 | 11 | tristate "CDX bus controller" |
| 12 | + select GENERIC_MSI_IRQ | |
| 12 | 13 | select REMOTEPROC |
| 13 | 14 | select RPMSG |
| 14 | 15 | help |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | #include <linux/platform_device.h> |
| 10 | 10 | #include <linux/slab.h> |
| 11 | 11 | #include <linux/cdx/cdx_bus.h> |
| 12 | +#include <linux/irqdomain.h> | |
| 12 | 13 | |
| 13 | 14 | #include "cdx_controller.h" |
| 14 | 15 | #include "../cdx.h" |
| @@ -60,9 +61,19 @@ static int cdx_configure_device(struct cdx_controller *cdx, · 45 unmodified lines | ||
| 60 | 61 | u8 bus_num, u8 dev_num, |
| 61 | 62 | struct cdx_device_config *dev_config) |
| 62 | 63 | { |
| 64 | + u16 msi_index; | |
| 63 | 65 | int ret = 0; |
| 66 | + u32 data; | |
| 67 | + u64 addr; | |
| 64 | 68 | |
| 65 | 69 | switch (dev_config->type) { |
| 70 | + case CDX_DEV_MSI_CONF: | |
| 71 | + msi_index = dev_config->msi.msi_index; | |
| 72 | + data = dev_config->msi.data; | |
| 73 | + addr = dev_config->msi.addr; | |
| 74 | + | |
| 75 | + ret = cdx_mcdi_write_msi(cdx->priv, bus_num, dev_num, msi_index, addr, data); | |
| 76 | + break; | |
| 66 | 77 | case CDX_DEV_RESET_CONF: |
| 67 | 78 | ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num); |
| 68 | 79 | break; |
| @@ -70,6 +81,9 @@ static int cdx_configure_device(struct cdx_controller *cdx, · 1 unmodified line | ||
| 70 | 81 | ret = cdx_mcdi_bus_master_enable(cdx->priv, bus_num, dev_num, |
| 71 | 82 | dev_config->bus_master_enable); |
| 72 | 83 | break; |
| 84 | + case CDX_DEV_MSI_ENABLE: | |
| 85 | + ret = cdx_mcdi_msi_enable(cdx->priv, bus_num, dev_num, dev_config->msi_enable); | |
| 86 | + break; | |
| 73 | 87 | default: |
| 74 | 88 | ret = -EINVAL; |
| 75 | 89 | } |
| @@ -178,6 +192,14 @@ static int xlnx_cdx_probe(struct platform_device *pdev) · 102 unmodified lines | ||
| 178 | 192 | cdx->priv = cdx_mcdi; |
| 179 | 193 | cdx->ops = &cdx_ops; |
| 180 | 194 | |
| 195 | + /* Create MSI domain */ | |
| 196 | + cdx->msi_domain = cdx_msi_domain_init(&pdev->dev); | |
| 197 | + if (!cdx->msi_domain) { | |
| 198 | + dev_err(&pdev->dev, "cdx_msi_domain_init() failed"); | |
| 199 | + ret = -ENODEV; | |
| 200 | + goto cdx_msi_fail; | |
| 201 | + } | |
| 202 | + | |
| 181 | 203 | ret = cdx_setup_rpmsg(pdev); |
| 182 | 204 | if (ret) { |
| 183 | 205 | if (ret != -EPROBE_DEFER) |
| @@ -189,6 +211,8 @@ static int xlnx_cdx_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 189 | 211 | return 0; |
| 190 | 212 | |
| 191 | 213 | cdx_rpmsg_fail: |
| 214 | + irq_domain_remove(cdx->msi_domain); | |
| 215 | +cdx_msi_fail: | |
| 192 | 216 | kfree(cdx); |
| 193 | 217 | cdx_alloc_fail: |
| 194 | 218 | cdx_mcdi_finish(cdx_mcdi); |
| @@ -205,6 +229,7 @@ static int xlnx_cdx_remove(struct platform_device *pdev) · 10 unmodified lines | ||
| 205 | 229 | |
| 206 | 230 | cdx_destroy_rpmsg(pdev); |
| 207 | 231 | |
| 232 | + irq_domain_remove(cdx->msi_domain); | |
| 208 | 233 | kfree(cdx); |
| 209 | 234 | |
| 210 | 235 | cdx_mcdi_finish(cdx_mcdi); |
| @@ -455,6 +455,12 @@ · 454 unmodified lines | ||
| 455 | 455 | #define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_OFST 84 |
| 456 | 456 | #define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_LEN 4 |
| 457 | 457 | |
| 458 | +/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2 msgresponse */ | |
| 459 | +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN 92 | |
| 460 | +/* Requester ID used by device for GIC ITS DeviceID */ | |
| 461 | +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_OFST 88 | |
| 462 | +#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_LEN 4 | |
| 463 | + | |
| 458 | 464 | /***********************************/ |
| 459 | 465 | /* |
| 460 | 466 | * MC_CMD_CDX_BUS_DOWN |
| @@ -616,6 +622,64 @@ · 155 unmodified lines | ||
| 616 | 622 | #define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_LBN 2 |
| 617 | 623 | #define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_WIDTH 1 |
| 618 | 624 | |
| 625 | +/***********************************/ | |
| 626 | +/* | |
| 627 | + * MC_CMD_CDX_DEVICE_WRITE_MSI_MSG | |
| 628 | + * Populates the MSI message to be used by the hardware to raise the specified | |
| 629 | + * interrupt vector. Versal-net implementation specific limitations are that | |
| 630 | + * only 4 CDX devices with MSI interrupt capability are supported and all | |
| 631 | + * vectors within a device must use the same write address. The command will | |
| 632 | + * return EINVAL if any of these limitations is violated. | |
| 633 | + */ | |
| 634 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG 0x9 | |
| 635 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_MSGSET 0x9 | |
| 636 | +#undef MC_CMD_0x9_PRIVILEGE_CTG | |
| 637 | + | |
| 638 | +#define MC_CMD_0x9_PRIVILEGE_CTG SRIOV_CTG_ADMIN | |
| 639 | + | |
| 640 | +/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN msgrequest */ | |
| 641 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN 28 | |
| 642 | +/* Device bus number, in range 0 to BUS_COUNT-1 */ | |
| 643 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_OFST 0 | |
| 644 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_LEN 4 | |
| 645 | +/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */ | |
| 646 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_OFST 4 | |
| 647 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_LEN 4 | |
| 648 | +/* | |
| 649 | + * Device-relative MSI vector number. Must be < MSI_COUNT reported for the | |
| 650 | + * device. | |
| 651 | + */ | |
| 652 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_OFST 8 | |
| 653 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_LEN 4 | |
| 654 | +/* Reserved (alignment) */ | |
| 655 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_OFST 12 | |
| 656 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_LEN 4 | |
| 657 | +/* | |
| 658 | + * MSI address to be used by the hardware. Typically, on ARM systems this | |
| 659 | + * address is translated by the IOMMU (if enabled) and it is the responsibility | |
| 660 | + * of the entity managing the IOMMU (APU kernel) to supply the correct IOVA | |
| 661 | + * here. | |
| 662 | + */ | |
| 663 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_OFST 16 | |
| 664 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LEN 8 | |
| 665 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_OFST 16 | |
| 666 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LEN 4 | |
| 667 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LBN 128 | |
| 668 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_WIDTH 32 | |
| 669 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_OFST 20 | |
| 670 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LEN 4 | |
| 671 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LBN 160 | |
| 672 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_WIDTH 32 | |
| 673 | +/* | |
| 674 | + * MSI data to be used by the hardware. On versal-net, only the lower 16-bits | |
| 675 | + * are used, the remaining bits are ignored and should be set to zero. | |
| 676 | + */ | |
| 677 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_OFST 24 | |
| 678 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_LEN 4 | |
| 679 | + | |
| 680 | +/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT msgresponse */ | |
| 681 | +#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT_LEN 0 | |
| 682 | + | |
| 619 | 683 | /***********************************/ |
| 620 | 684 | /* MC_CMD_V2_EXTN - Encapsulation for a v2 extended command */ |
| 621 | 685 | #define MC_CMD_V2_EXTN 0x7f |
| @@ -49,7 +49,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, · 48 unmodified lines | ||
| 49 | 49 | u8 bus_num, u8 dev_num, |
| 50 | 50 | struct cdx_dev_params *dev_params) |
| 51 | 51 | { |
| 52 | − MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN); | |
| 52 | + MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN); | |
| 53 | 53 | MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN); |
| 54 | 54 | struct resource *res = &dev_params->res[0]; |
| 55 | 55 | size_t outlen; |
| @@ -64,7 +64,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, · 8 unmodified lines | ||
| 64 | 64 | if (ret) |
| 65 | 65 | return ret; |
| 66 | 66 | |
| 67 | − if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN) | |
| 67 | + if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN) | |
| 68 | 68 | return -EIO; |
| 69 | 69 | |
| 70 | 70 | dev_params->bus_num = bus_num; |
| @@ -73,6 +73,9 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, · 2 unmodified lines | ||
| 73 | 73 | req_id = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID); |
| 74 | 74 | dev_params->req_id = req_id; |
| 75 | 75 | |
| 76 | + dev_params->msi_dev_id = MCDI_DWORD(outbuf, | |
| 77 | + CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID); | |
| 78 | + | |
| 76 | 79 | dev_params->res_count = 0; |
| 77 | 80 | if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) != 0) { |
| 78 | 81 | res[dev_params->res_count].start = |
| @@ -127,6 +130,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx, · 48 unmodified lines | ||
| 127 | 130 | dev_params->class = MCDI_DWORD(outbuf, |
| 128 | 131 | CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS) & 0xFFFFFF; |
| 129 | 132 | dev_params->revision = MCDI_BYTE(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION); |
| 133 | + dev_params->num_msi = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT); | |
| 130 | 134 | |
| 131 | 135 | return 0; |
| 132 | 136 | } |
| @@ -155,6 +159,24 @@ int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num) · 22 unmodified lines | ||
| 155 | 159 | return ret; |
| 156 | 160 | } |
| 157 | 161 | |
| 162 | +int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, | |
| 163 | + u32 msi_vector, u64 msi_address, u32 msi_data) | |
| 164 | +{ | |
| 165 | + MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN); | |
| 166 | + int ret; | |
| 167 | + | |
| 168 | + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_BUS, bus_num); | |
| 169 | + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE, dev_num); | |
| 170 | + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR, msi_vector); | |
| 171 | + MCDI_SET_QWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS, msi_address); | |
| 172 | + MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA, msi_data); | |
| 173 | + | |
| 174 | + ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG, inbuf, sizeof(inbuf), | |
| 175 | + NULL, 0, NULL); | |
| 176 | + | |
| 177 | + return ret; | |
| 178 | +} | |
| 179 | + | |
| 158 | 180 | int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num) |
| 159 | 181 | { |
| 160 | 182 | MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN); |
| @@ -226,3 +248,10 @@ int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num, · 65 unmodified lines | ||
| 226 | 248 | return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable, |
| 227 | 249 | MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_LBN); |
| 228 | 250 | } |
| 251 | + | |
| 252 | +int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num, | |
| 253 | + u8 dev_num, bool enable) | |
| 254 | +{ | |
| 255 | + return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable, | |
| 256 | + MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_LBN); | |
| 257 | +} | |
| @@ -65,6 +65,26 @@ int cdx_mcdi_bus_enable(struct cdx_mcdi *cdx, u8 bus_num); · 64 unmodified lines | ||
| 65 | 65 | */ |
| 66 | 66 | int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num); |
| 67 | 67 | |
| 68 | +/** | |
| 69 | + * cdx_mcdi_write_msi - Write MSI configuration for CDX device | |
| 70 | + * @cdx: pointer to MCDI interface. | |
| 71 | + * @bus_num: Bus number. | |
| 72 | + * @dev_num: Device number. | |
| 73 | + * @msi_vector: Device-relative MSI vector number. | |
| 74 | + * Must be < MSI_COUNT reported for the device. | |
| 75 | + * @msi_address: MSI address to be used by the hardware. Typically, on ARM | |
| 76 | + * systems this address is translated by the IOMMU (if enabled) and | |
| 77 | + * it is the responsibility of the entity managing the IOMMU (APU kernel) | |
| 78 | + * to supply the correct IOVA here. | |
| 79 | + * @msi_data: MSI data to be used by the hardware. On versal-net, only the | |
| 80 | + * lower 16-bits are used, the remaining bits are ignored and should be | |
| 81 | + * set to zero. | |
| 82 | + * | |
| 83 | + * Return: 0 on success, <0 on failure | |
| 84 | + */ | |
| 85 | +int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num, | |
| 86 | + u32 msi_vector, u64 msi_address, u32 msi_data); | |
| 87 | + | |
| 68 | 88 | /** |
| 69 | 89 | * cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num |
| 70 | 90 | * @cdx: pointer to MCDI interface. |
| @@ -89,4 +109,17 @@ int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, · 18 unmodified lines | ||
| 89 | 109 | int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num, |
| 90 | 110 | u8 dev_num, bool enable); |
| 91 | 111 | |
| 112 | +/** | |
| 113 | + * cdx_mcdi_msi_enable - Enable/Disable MSIs for cdx device represented | |
| 114 | + * by bus_num:dev_num | |
| 115 | + * @cdx: pointer to MCDI interface. | |
| 116 | + * @bus_num: Bus number. | |
| 117 | + * @dev_num: Device number. | |
| 118 | + * @enable: Enable msi's if set, disable otherwise. | |
| 119 | + * | |
| 120 | + * Return: 0 on success, <0 on failure | |
| 121 | + */ | |
| 122 | +int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num, | |
| 123 | + u8 dev_num, bool enable); | |
| 124 | + | |
| 92 | 125 | #endif /* CDX_MCDI_FUNCTIONS_H */ |
| @@ -87,7 +87,6 @@ struct hpets { · 86 unmodified lines | ||
| 87 | 87 | struct hpets *hp_next; |
| 88 | 88 | struct hpet __iomem *hp_hpet; |
| 89 | 89 | unsigned long hp_hpet_phys; |
| 90 | − struct clocksource *hp_clocksource; | |
| 91 | 90 | unsigned long long hp_tick_freq; |
| 92 | 91 | unsigned long hp_delta; |
| 93 | 92 | unsigned int hp_ntimer; |
| @@ -636,11 +636,11 @@ static int hwicap_setup(struct platform_device *pdev, int id, · 635 unmodified lines | ||
| 636 | 636 | retval = -ENOMEM; |
| 637 | 637 | goto failed; |
| 638 | 638 | } |
| 639 | − dev_set_drvdata(dev, (void *)drvdata); | |
| 639 | + dev_set_drvdata(dev, drvdata); | |
| 640 | 640 | |
| 641 | 641 | drvdata->base_address = devm_platform_ioremap_resource(pdev, 0); |
| 642 | − if (!drvdata->base_address) { | |
| 643 | − retval = -ENODEV; | |
| 642 | + if (IS_ERR(drvdata->base_address)) { | |
| 643 | + retval = PTR_ERR(drvdata->base_address); | |
| 644 | 644 | goto failed; |
| 645 | 645 | } |
| 646 | 646 | |
| @@ -64,19 +64,17 @@ static int xilly_drv_probe(struct platform_device *op) · 63 unmodified lines | ||
| 64 | 64 | return xillybus_endpoint_discovery(endpoint); |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | −static int xilly_drv_remove(struct platform_device *op) | |
| 67 | +static void xilly_drv_remove(struct platform_device *op) | |
| 68 | 68 | { |
| 69 | 69 | struct device *dev = &op->dev; |
| 70 | 70 | struct xilly_endpoint *endpoint = dev_get_drvdata(dev); |
| 71 | 71 | |
| 72 | 72 | xillybus_endpoint_remove(endpoint); |
| 73 | − | |
| 74 | − return 0; | |
| 75 | 73 | } |
| 76 | 74 | |
| 77 | 75 | static struct platform_driver xillybus_platform_driver = { |
| 78 | 76 | .probe = xilly_drv_probe, |
| 79 | − .remove = xilly_drv_remove, | |
| 77 | + .remove_new = xilly_drv_remove, | |
| 80 | 78 | .driver = { |
| 81 | 79 | .name = xillyname, |
| 82 | 80 | .of_match_table = xillybus_of_match, |
| @@ -177,7 +177,6 @@ static int das08_ai_insn_read(struct comedi_device *dev, · 176 unmodified lines | ||
| 177 | 177 | int ret; |
| 178 | 178 | |
| 179 | 179 | chan = CR_CHAN(insn->chanspec); |
| 180 | − range = CR_RANGE(insn->chanspec); | |
| 181 | 180 | |
| 182 | 181 | /* clear crap */ |
| 183 | 182 | inb(dev->iobase + DAS08_AI_LSB_REG); |
| @@ -123,7 +123,7 @@ static int dio_bus_match(struct device *dev, struct device_driver *drv) · 122 unmodified lines | ||
| 123 | 123 | } |
| 124 | 124 | |
| 125 | 125 | |
| 126 | −struct bus_type dio_bus_type = { | |
| 126 | +const struct bus_type dio_bus_type = { | |
| 127 | 127 | .name = "dio", |
| 128 | 128 | .match = dio_bus_match, |
| 129 | 129 | .probe = dio_device_probe, |
| @@ -3,6 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | * Xilinx Zynq MPSoC Firmware layer |
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2014-2022 Xilinx, Inc. |
| 6 | + * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. | |
| 6 | 7 | * |
| 7 | 8 | * Michal Simek <michal.simek@amd.com> |
| 8 | 9 | * Davorin Mista <davorin.mista@aggios.com> |
| @@ -1384,6 +1385,30 @@ int zynqmp_pm_aes_engine(const u64 address, u32 *out) · 1375 unmodified lines | ||
| 1384 | 1385 | } |
| 1385 | 1386 | EXPORT_SYMBOL_GPL(zynqmp_pm_aes_engine); |
| 1386 | 1387 | |
| 1388 | +/** | |
| 1389 | + * zynqmp_pm_efuse_access - Provides access to efuse memory. | |
| 1390 | + * @address: Address of the efuse params structure | |
| 1391 | + * @out: Returned output value | |
| 1392 | + * | |
| 1393 | + * Return: Returns status, either success or error code. | |
| 1394 | + */ | |
| 1395 | +int zynqmp_pm_efuse_access(const u64 address, u32 *out) | |
| 1396 | +{ | |
| 1397 | + u32 ret_payload[PAYLOAD_ARG_CNT]; | |
| 1398 | + int ret; | |
| 1399 | + | |
| 1400 | + if (!out) | |
| 1401 | + return -EINVAL; | |
| 1402 | + | |
| 1403 | + ret = zynqmp_pm_invoke_fn(PM_EFUSE_ACCESS, ret_payload, 2, | |
| 1404 | + upper_32_bits(address), | |
| 1405 | + lower_32_bits(address)); | |
| 1406 | + *out = ret_payload[1]; | |
| 1407 | + | |
| 1408 | + return ret; | |
| 1409 | +} | |
| 1410 | +EXPORT_SYMBOL_GPL(zynqmp_pm_efuse_access); | |
| 1411 | + | |
| 1387 | 1412 | /** |
| 1388 | 1413 | * zynqmp_pm_sha_hash - Access the SHA engine to calculate the hash |
| 1389 | 1414 | * @address: Address of the data/ Address of output buffer where |
| @@ -327,7 +327,7 @@ static struct attribute *dfl_dev_attrs[] = { · 326 unmodified lines | ||
| 327 | 327 | }; |
| 328 | 328 | ATTRIBUTE_GROUPS(dfl_dev); |
| 329 | 329 | |
| 330 | −static struct bus_type dfl_bus_type = { | |
| 330 | +static const struct bus_type dfl_bus_type = { | |
| 331 | 331 | .name = "dfl", |
| 332 | 332 | .match = dfl_bus_match, |
| 333 | 333 | .probe = dfl_bus_probe, |
| @@ -30,7 +30,7 @@ int fpga_bridge_enable(struct fpga_bridge *bridge) · 29 unmodified lines | ||
| 30 | 30 | { |
| 31 | 31 | dev_dbg(&bridge->dev, "enable\n"); |
| 32 | 32 | |
| 33 | − if (bridge->br_ops && bridge->br_ops->enable_set) | |
| 33 | + if (bridge->br_ops->enable_set) | |
| 34 | 34 | return bridge->br_ops->enable_set(bridge, 1); |
| 35 | 35 | |
| 36 | 36 | return 0; |
| @@ -48,7 +48,7 @@ int fpga_bridge_disable(struct fpga_bridge *bridge) · 11 unmodified lines | ||
| 48 | 48 | { |
| 49 | 49 | dev_dbg(&bridge->dev, "disable\n"); |
| 50 | 50 | |
| 51 | − if (bridge->br_ops && bridge->br_ops->enable_set) | |
| 51 | + if (bridge->br_ops->enable_set) | |
| 52 | 52 | return bridge->br_ops->enable_set(bridge, 0); |
| 53 | 53 | |
| 54 | 54 | return 0; |
| @@ -296,7 +296,7 @@ static ssize_t state_show(struct device *dev, · 241 unmodified lines | ||
| 296 | 296 | struct fpga_bridge *bridge = to_fpga_bridge(dev); |
| 297 | 297 | int state = 1; |
| 298 | 298 | |
| 299 | − if (bridge->br_ops && bridge->br_ops->enable_show) { | |
| 299 | + if (bridge->br_ops->enable_show) { | |
| 300 | 300 | state = bridge->br_ops->enable_show(bridge); |
| 301 | 301 | if (state < 0) |
| 302 | 302 | return state; |
| @@ -401,7 +401,7 @@ void fpga_bridge_unregister(struct fpga_bridge *bridge) · 98 unmodified lines | ||
| 401 | 401 | * If the low level driver provides a method for putting bridge into |
| 402 | 402 | * a desired state upon unregister, do it. |
| 403 | 403 | */ |
| 404 | − if (bridge->br_ops && bridge->br_ops->fpga_bridge_remove) | |
| 404 | + if (bridge->br_ops->fpga_bridge_remove) | |
| 405 | 405 | bridge->br_ops->fpga_bridge_remove(bridge); |
| 406 | 406 | |
| 407 | 407 | device_unregister(&bridge->dev); |
| @@ -166,7 +166,7 @@ static const struct dev_pm_ops gb_bundle_pm_ops = { · 165 unmodified lines | ||
| 166 | 166 | SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle) |
| 167 | 167 | }; |
| 168 | 168 | |
| 169 | −struct device_type greybus_bundle_type = { | |
| 169 | +const struct device_type greybus_bundle_type = { | |
| 170 | 170 | .name = "greybus_bundle", |
| 171 | 171 | .release = gb_bundle_release, |
| 172 | 172 | .pm = &gb_bundle_pm_ops, |
| @@ -436,7 +436,7 @@ static void gb_control_release(struct device *dev) · 435 unmodified lines | ||
| 436 | 436 | kfree(control); |
| 437 | 437 | } |
| 438 | 438 | |
| 439 | −struct device_type greybus_control_type = { | |
| 439 | +const struct device_type greybus_control_type = { | |
| 440 | 440 | .name = "greybus_control", |
| 441 | 441 | .release = gb_control_release, |
| 442 | 442 | }; |
| @@ -27,6 +27,36 @@ int greybus_disabled(void) · 26 unmodified lines | ||
| 27 | 27 | } |
| 28 | 28 | EXPORT_SYMBOL_GPL(greybus_disabled); |
| 29 | 29 | |
| 30 | +static int is_gb_host_device(const struct device *dev) | |
| 31 | +{ | |
| 32 | + return dev->type == &greybus_hd_type; | |
| 33 | +} | |
| 34 | + | |
| 35 | +static int is_gb_module(const struct device *dev) | |
| 36 | +{ | |
| 37 | + return dev->type == &greybus_module_type; | |
| 38 | +} | |
| 39 | + | |
| 40 | +static int is_gb_interface(const struct device *dev) | |
| 41 | +{ | |
| 42 | + return dev->type == &greybus_interface_type; | |
| 43 | +} | |
| 44 | + | |
| 45 | +static int is_gb_control(const struct device *dev) | |
| 46 | +{ | |
| 47 | + return dev->type == &greybus_control_type; | |
| 48 | +} | |
| 49 | + | |
| 50 | +static int is_gb_bundle(const struct device *dev) | |
| 51 | +{ | |
| 52 | + return dev->type == &greybus_bundle_type; | |
| 53 | +} | |
| 54 | + | |
| 55 | +static int is_gb_svc(const struct device *dev) | |
| 56 | +{ | |
| 57 | + return dev->type == &greybus_svc_type; | |
| 58 | +} | |
| 59 | + | |
| 30 | 60 | static bool greybus_match_one_id(struct gb_bundle *bundle, |
| 31 | 61 | const struct greybus_bundle_id *id) |
| 32 | 62 | { |
| @@ -155,7 +185,7 @@ static void greybus_shutdown(struct device *dev) · 122 unmodified lines | ||
| 155 | 185 | } |
| 156 | 186 | } |
| 157 | 187 | |
| 158 | −struct bus_type greybus_bus_type = { | |
| 188 | +const struct bus_type greybus_bus_type = { | |
| 159 | 189 | .name = "greybus", |
| 160 | 190 | .match = greybus_match_device, |
| 161 | 191 | .uevent = greybus_uevent, |
| @@ -513,16 +513,16 @@ static int es2_cport_allocate(struct gb_host_device *hd, int cport_id, · 512 unmodified lines | ||
| 513 | 513 | |
| 514 | 514 | if (cport_id < 0) { |
| 515 | 515 | ida_start = 0; |
| 516 | − ida_end = hd->num_cports; | |
| 516 | + ida_end = hd->num_cports - 1; | |
| 517 | 517 | } else if (cport_id < hd->num_cports) { |
| 518 | 518 | ida_start = cport_id; |
| 519 | − ida_end = cport_id + 1; | |
| 519 | + ida_end = cport_id; | |
| 520 | 520 | } else { |
| 521 | 521 | dev_err(&hd->dev, "cport %d not available\n", cport_id); |
| 522 | 522 | return -EINVAL; |
| 523 | 523 | } |
| 524 | 524 | |
| 525 | − return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); | |
| 525 | + return ida_alloc_range(id_map, ida_start, ida_end, GFP_KERNEL); | |
| 526 | 526 | } |
| 527 | 527 | |
| 528 | 528 | static void es2_cport_release(struct gb_host_device *hd, u16 cport_id) |
| @@ -535,7 +535,7 @@ static void es2_cport_release(struct gb_host_device *hd, u16 cport_id) · 6 unmodified lines | ||
| 535 | 535 | return; |
| 536 | 536 | } |
| 537 | 537 | |
| 538 | − ida_simple_remove(&hd->cport_id_map, cport_id); | |
| 538 | + ida_free(&hd->cport_id_map, cport_id); | |
| 539 | 539 | } |
| 540 | 540 | |
| 541 | 541 | static int cport_enable(struct gb_host_device *hd, u16 cport_id, |
| @@ -50,7 +50,7 @@ int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id) · 49 unmodified lines | ||
| 50 | 50 | struct ida *id_map = &hd->cport_id_map; |
| 51 | 51 | int ret; |
| 52 | 52 | |
| 53 | − ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL); | |
| 53 | + ret = ida_alloc_range(id_map, cport_id, cport_id, GFP_KERNEL); | |
| 54 | 54 | if (ret < 0) { |
| 55 | 55 | dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id); |
| 56 | 56 | return ret; |
| @@ -64,7 +64,7 @@ void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id) · 7 unmodified lines | ||
| 64 | 64 | { |
| 65 | 65 | struct ida *id_map = &hd->cport_id_map; |
| 66 | 66 | |
| 67 | − ida_simple_remove(id_map, cport_id); | |
| 67 | + ida_free(id_map, cport_id); | |
| 68 | 68 | } |
| 69 | 69 | EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved); |
| 70 | 70 | |
| @@ -80,16 +80,16 @@ int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, · 9 unmodified lines | ||
| 80 | 80 | |
| 81 | 81 | if (cport_id < 0) { |
| 82 | 82 | ida_start = 0; |
| 83 | − ida_end = hd->num_cports; | |
| 83 | + ida_end = hd->num_cports - 1; | |
| 84 | 84 | } else if (cport_id < hd->num_cports) { |
| 85 | 85 | ida_start = cport_id; |
| 86 | − ida_end = cport_id + 1; | |
| 86 | + ida_end = cport_id; | |
| 87 | 87 | } else { |
| 88 | 88 | dev_err(&hd->dev, "cport %d not available\n", cport_id); |
| 89 | 89 | return -EINVAL; |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | − return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); | |
| 92 | + return ida_alloc_range(id_map, ida_start, ida_end, GFP_KERNEL); | |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | 95 | /* Locking: Caller guarantees serialisation */ |
| @@ -100,7 +100,7 @@ void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id) · 4 unmodified lines | ||
| 100 | 100 | return; |
| 101 | 101 | } |
| 102 | 102 | |
| 103 | − ida_simple_remove(&hd->cport_id_map, cport_id); | |
| 103 | + ida_free(&hd->cport_id_map, cport_id); | |
| 104 | 104 | } |
| 105 | 105 | |
| 106 | 106 | static void gb_hd_release(struct device *dev) |
| @@ -111,12 +111,12 @@ static void gb_hd_release(struct device *dev) · 4 unmodified lines | ||
| 111 | 111 | |
| 112 | 112 | if (hd->svc) |
| 113 | 113 | gb_svc_put(hd->svc); |
| 114 | − ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id); | |
| 114 | + ida_free(&gb_hd_bus_id_map, hd->bus_id); | |
| 115 | 115 | ida_destroy(&hd->cport_id_map); |
| 116 | 116 | kfree(hd); |
| 117 | 117 | } |
| 118 | 118 | |
| 119 | −struct device_type greybus_hd_type = { | |
| 119 | +const struct device_type greybus_hd_type = { | |
| 120 | 120 | .name = "greybus_host_device", |
| 121 | 121 | .release = gb_hd_release, |
| 122 | 122 | }; |
| @@ -162,7 +162,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, · 39 unmodified lines | ||
| 162 | 162 | if (!hd) |
| 163 | 163 | return ERR_PTR(-ENOMEM); |
| 164 | 164 | |
| 165 | − ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL); | |
| 165 | + ret = ida_alloc_min(&gb_hd_bus_id_map, 1, GFP_KERNEL); | |
| 166 | 166 | if (ret < 0) { |
| 167 | 167 | kfree(hd); |
| 168 | 168 | return ERR_PTR(ret); |
| @@ -131,9 +131,8 @@ static int gb_interface_route_create(struct gb_interface *intf) · 130 unmodified lines | ||
| 131 | 131 | int ret; |
| 132 | 132 | |
| 133 | 133 | /* Allocate an interface device id. */ |
| 134 | − ret = ida_simple_get(&svc->device_id_map, | |
| 135 | − GB_SVC_DEVICE_ID_MIN, GB_SVC_DEVICE_ID_MAX + 1, | |
| 136 | − GFP_KERNEL); | |
| 134 | + ret = ida_alloc_range(&svc->device_id_map, GB_SVC_DEVICE_ID_MIN, | |
| 135 | + GB_SVC_DEVICE_ID_MAX, GFP_KERNEL); | |
| 137 | 136 | if (ret < 0) { |
| 138 | 137 | dev_err(&intf->dev, "failed to allocate device id: %d\n", ret); |
| 139 | 138 | return ret; |
| @@ -165,7 +164,7 @@ err_svc_id_free: · 25 unmodified lines | ||
| 165 | 164 | * XXX anymore. |
| 166 | 165 | */ |
| 167 | 166 | err_ida_remove: |
| 168 | − ida_simple_remove(&svc->device_id_map, device_id); | |
| 167 | + ida_free(&svc->device_id_map, device_id); | |
| 169 | 168 | |
| 170 | 169 | return ret; |
| 171 | 170 | } |
| @@ -178,7 +177,7 @@ static void gb_interface_route_destroy(struct gb_interface *intf) · 6 unmodified lines | ||
| 178 | 177 | return; |
| 179 | 178 | |
| 180 | 179 | gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id); |
| 181 | − ida_simple_remove(&svc->device_id_map, intf->device_id); | |
| 180 | + ida_free(&svc->device_id_map, intf->device_id); | |
| 182 | 181 | intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; |
| 183 | 182 | } |
| 184 | 183 | |
| @@ -765,7 +764,7 @@ static const struct dev_pm_ops gb_interface_pm_ops = { · 580 unmodified lines | ||
| 765 | 764 | gb_interface_runtime_idle) |
| 766 | 765 | }; |
| 767 | 766 | |
| 768 | −struct device_type greybus_interface_type = { | |
| 767 | +const struct device_type greybus_interface_type = { | |
| 769 | 768 | .name = "greybus_interface", |
| 770 | 769 | .release = gb_interface_release, |
| 771 | 770 | .pm = &gb_interface_pm_ops, |
| @@ -81,7 +81,7 @@ static void gb_module_release(struct device *dev) · 80 unmodified lines | ||
| 81 | 81 | kfree(module); |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | −struct device_type greybus_module_type = { | |
| 84 | +const struct device_type greybus_module_type = { | |
| 85 | 85 | .name = "greybus_module", |
| 86 | 86 | .release = gb_module_release, |
| 87 | 87 | }; |
| @@ -1305,7 +1305,7 @@ static void gb_svc_release(struct device *dev) · 1304 unmodified lines | ||
| 1305 | 1305 | kfree(svc); |
| 1306 | 1306 | } |
| 1307 | 1307 | |
| 1308 | −struct device_type greybus_svc_type = { | |
| 1308 | +const struct device_type greybus_svc_type = { | |
| 1309 | 1309 | .name = "greybus_svc", |
| 1310 | 1310 | .release = gb_svc_release, |
| 1311 | 1311 | }; |
| @@ -2,6 +2,26 @@ · 1 unmodified line | ||
| 2 | 2 | # |
| 3 | 3 | # Makefile for CoreSight drivers. |
| 4 | 4 | # |
| 5 | + | |
| 6 | +# Current W=1 warnings | |
| 7 | +subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter | |
| 8 | +subdir-ccflags-y += -Wmissing-declarations | |
| 9 | +subdir-ccflags-y += -Wmissing-format-attribute | |
| 10 | +subdir-ccflags-y += -Wmissing-prototypes | |
| 11 | +subdir-ccflags-y += -Wold-style-definition | |
| 12 | +subdir-ccflags-y += -Wmissing-include-dirs | |
| 13 | +subdir-ccflags-y += -Wno-sign-compare | |
| 14 | +condflags := \ | |
| 15 | + $(call cc-option, -Wrestrict) \ | |
| 16 | + $(call cc-option, -Wunused-but-set-variable) \ | |
| 17 | + $(call cc-option, -Wunused-const-variable) \ | |
| 18 | + $(call cc-option, -Wpacked-not-aligned) \ | |
| 19 | + $(call cc-option, -Wformat-overflow) \ | |
| 20 | + $(call cc-option, -Wformat-truncation) \ | |
| 21 | + $(call cc-option, -Wstringop-overflow) \ | |
| 22 | + $(call cc-option, -Wstringop-truncation) | |
| 23 | +subdir-ccflags-y += $(condflags) | |
| 24 | + | |
| 5 | 25 | obj-$(CONFIG_CORESIGHT) += coresight.o |
| 6 | 26 | coresight-y := coresight-core.o coresight-etm-perf.o coresight-platform.o \ |
| 7 | 27 | coresight-sysfs.o coresight-syscfg.o coresight-config.o \ |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | /* ETMv4 includes and features */ |
| 10 | 10 | #if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X) |
| 11 | 11 | #include "coresight-etm4x-cfg.h" |
| 12 | +#include "coresight-cfg-preload.h" | |
| 12 | 13 | |
| 13 | 14 | /* preload configurations and features */ |
| 14 | 15 | |
| @@ -9,7 +9,6 @@ · 8 unmodified lines | ||
| 9 | 9 | #include <linux/types.h> |
| 10 | 10 | #include <linux/device.h> |
| 11 | 11 | #include <linux/io.h> |
| 12 | −#include <linux/idr.h> | |
| 13 | 12 | #include <linux/err.h> |
| 14 | 13 | #include <linux/export.h> |
| 15 | 14 | #include <linux/slab.h> |
| @@ -25,15 +24,12 @@ · 9 unmodified lines | ||
| 25 | 24 | #include "coresight-priv.h" |
| 26 | 25 | #include "coresight-syscfg.h" |
| 27 | 26 | |
| 28 | −static DEFINE_MUTEX(coresight_mutex); | |
| 29 | −static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); | |
| 30 | − | |
| 31 | 27 | /* |
| 32 | − * Use IDR to map the hash of the source's device name | |
| 33 | − * to the pointer of path for the source. The idr is for | |
| 34 | − * the sources which aren't associated with CPU. | |
| 28 | + * Mutex used to lock all sysfs enable and disable actions and loading and | |
| 29 | + * unloading devices by the Coresight core. | |
| 35 | 30 | */ |
| 36 | −static DEFINE_IDR(path_idr); | |
| 31 | +DEFINE_MUTEX(coresight_mutex); | |
| 32 | +static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); | |
| 37 | 33 | |
| 38 | 34 | /** |
| 39 | 35 | * struct coresight_node - elements of a path, from source to sink |
| @@ -45,12 +41,6 @@ struct coresight_node { · 5 unmodified lines | ||
| 45 | 41 | struct list_head link; |
| 46 | 42 | }; |
| 47 | 43 | |
| 48 | −/* | |
| 49 | − * When operating Coresight drivers from the sysFS interface, only a single | |
| 50 | − * path can exist from a tracer (associated to a CPU) to a sink. | |
| 51 | − */ | |
| 52 | −static DEFINE_PER_CPU(struct list_head *, tracer_path); | |
| 53 | − | |
| 54 | 44 | /* |
| 55 | 45 | * When losing synchronisation a new barrier packet needs to be inserted at the |
| 56 | 46 | * beginning of the data collected in a buffer. That way the decoder knows that |
| @@ -61,34 +51,6 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt); · 4 unmodified lines | ||
| 61 | 51 | |
| 62 | 52 | static const struct cti_assoc_op *cti_assoc_ops; |
| 63 | 53 | |
| 64 | −ssize_t coresight_simple_show_pair(struct device *_dev, | |
| 65 | − struct device_attribute *attr, char *buf) | |
| 66 | −{ | |
| 67 | − struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); | |
| 68 | − struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr); | |
| 69 | − u64 val; | |
| 70 | − | |
| 71 | − pm_runtime_get_sync(_dev->parent); | |
| 72 | − val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off); | |
| 73 | − pm_runtime_put_sync(_dev->parent); | |
| 74 | − return sysfs_emit(buf, "0x%llx\n", val); | |
| 75 | −} | |
| 76 | −EXPORT_SYMBOL_GPL(coresight_simple_show_pair); | |
| 77 | − | |
| 78 | −ssize_t coresight_simple_show32(struct device *_dev, | |
| 79 | − struct device_attribute *attr, char *buf) | |
| 80 | −{ | |
| 81 | − struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); | |
| 82 | − struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr); | |
| 83 | − u64 val; | |
| 84 | − | |
| 85 | − pm_runtime_get_sync(_dev->parent); | |
| 86 | − val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off); | |
| 87 | − pm_runtime_put_sync(_dev->parent); | |
| 88 | − return sysfs_emit(buf, "0x%llx\n", val); | |
| 89 | −} | |
| 90 | −EXPORT_SYMBOL_GPL(coresight_simple_show32); | |
| 91 | − | |
| 92 | 54 | void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) |
| 93 | 55 | { |
| 94 | 56 | cti_assoc_ops = cti_op; |
| @@ -279,42 +241,18 @@ EXPORT_SYMBOL_GPL(coresight_add_helper); · 184 unmodified lines | ||
| 279 | 241 | static int coresight_enable_sink(struct coresight_device *csdev, |
| 280 | 242 | enum cs_mode mode, void *data) |
| 281 | 243 | { |
| 282 | − int ret; | |
| 283 | − | |
| 284 | − /* | |
| 285 | − * We need to make sure the "new" session is compatible with the | |
| 286 | − * existing "mode" of operation. | |
| 287 | − */ | |
| 288 | − if (!sink_ops(csdev)->enable) | |
| 289 | − return -EINVAL; | |
| 290 | − | |
| 291 | − ret = sink_ops(csdev)->enable(csdev, mode, data); | |
| 292 | − if (ret) | |
| 293 | − return ret; | |
| 294 | − | |
| 295 | − csdev->enable = true; | |
| 296 | − | |
| 297 | − return 0; | |
| 244 | + return sink_ops(csdev)->enable(csdev, mode, data); | |
| 298 | 245 | } |
| 299 | 246 | |
| 300 | 247 | static void coresight_disable_sink(struct coresight_device *csdev) |
| 301 | 248 | { |
| 302 | − int ret; | |
| 303 | − | |
| 304 | − if (!sink_ops(csdev)->disable) | |
| 305 | − return; | |
| 306 | − | |
| 307 | − ret = sink_ops(csdev)->disable(csdev); | |
| 308 | − if (ret) | |
| 309 | − return; | |
| 310 | − csdev->enable = false; | |
| 249 | + sink_ops(csdev)->disable(csdev); | |
| 311 | 250 | } |
| 312 | 251 | |
| 313 | 252 | static int coresight_enable_link(struct coresight_device *csdev, |
| 314 | 253 | struct coresight_device *parent, |
| 315 | 254 | struct coresight_device *child) |
| 316 | 255 | { |
| 317 | − int ret = 0; | |
| 318 | 256 | int link_subtype; |
| 319 | 257 | struct coresight_connection *inconn, *outconn; |
| 320 | 258 | |
| @@ -330,21 +268,13 @@ static int coresight_enable_link(struct coresight_device *csdev, · 9 unmodified lines | ||
| 330 | 268 | if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && IS_ERR(outconn)) |
| 331 | 269 | return PTR_ERR(outconn); |
| 332 | 270 | |
| 333 | − if (link_ops(csdev)->enable) { | |
| 334 | − ret = link_ops(csdev)->enable(csdev, inconn, outconn); | |
| 335 | − if (!ret) | |
| 336 | − csdev->enable = true; | |
| 337 | − } | |
| 338 | − | |
| 339 | − return ret; | |
| 271 | + return link_ops(csdev)->enable(csdev, inconn, outconn); | |
| 340 | 272 | } |
| 341 | 273 | |
| 342 | 274 | static void coresight_disable_link(struct coresight_device *csdev, |
| 343 | 275 | struct coresight_device *parent, |
| 344 | 276 | struct coresight_device *child) |
| 345 | 277 | { |
| 346 | − int i; | |
| 347 | − int link_subtype; | |
| 348 | 278 | struct coresight_connection *inconn, *outconn; |
| 349 | 279 | |
| 350 | 280 | if (!parent || !child) |
| @@ -352,49 +282,9 @@ static void coresight_disable_link(struct coresight_device *csdev, · 1 unmodified line | ||
| 352 | 282 | |
| 353 | 283 | inconn = coresight_find_out_connection(parent, csdev); |
| 354 | 284 | outconn = coresight_find_out_connection(csdev, child); |
| 355 | − link_subtype = csdev->subtype.link_subtype; | |
| 356 | 285 | |
| 357 | − if (link_ops(csdev)->disable) { | |
| 358 | − link_ops(csdev)->disable(csdev, inconn, outconn); | |
| 359 | − } | |
| 360 | − | |
| 361 | − if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { | |
| 362 | − for (i = 0; i < csdev->pdata->nr_inconns; i++) | |
| 363 | − if (atomic_read(&csdev->pdata->in_conns[i]->dest_refcnt) != | |
| 364 | − 0) | |
| 365 | − return; | |
| 366 | − } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) { | |
| 367 | − for (i = 0; i < csdev->pdata->nr_outconns; i++) | |
| 368 | − if (atomic_read(&csdev->pdata->out_conns[i]->src_refcnt) != | |
| 369 | − 0) | |
| 370 | − return; | |
| 371 | − } else { | |
| 372 | − if (atomic_read(&csdev->refcnt) != 0) | |
| 373 | − return; | |
| 374 | − } | |
| 375 | − | |
| 376 | − csdev->enable = false; | |
| 377 | −} | |
| 378 | − | |
| 379 | −int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode, | |
| 380 | − void *data) | |
| 381 | −{ | |
| 382 | − int ret; | |
| 383 | − | |
| 384 | − if (!csdev->enable) { | |
| 385 | − if (source_ops(csdev)->enable) { | |
| 386 | − ret = source_ops(csdev)->enable(csdev, data, mode); | |
| 387 | − if (ret) | |
| 388 | − return ret; | |
| 389 | − } | |
| 390 | − csdev->enable = true; | |
| 391 | − } | |
| 392 | − | |
| 393 | − atomic_inc(&csdev->refcnt); | |
| 394 | − | |
| 395 | − return 0; | |
| 286 | + link_ops(csdev)->disable(csdev, inconn, outconn); | |
| 396 | 287 | } |
| 397 | −EXPORT_SYMBOL_GPL(coresight_enable_source); | |
| 398 | 288 | |
| 399 | 289 | static bool coresight_is_helper(struct coresight_device *csdev) |
| 400 | 290 | { |
| @@ -404,29 +294,12 @@ static bool coresight_is_helper(struct coresight_device *csdev) · 3 unmodified lines | ||
| 404 | 294 | static int coresight_enable_helper(struct coresight_device *csdev, |
| 405 | 295 | enum cs_mode mode, void *data) |
| 406 | 296 | { |
| 407 | − int ret; | |
| 408 | − | |
| 409 | − if (!helper_ops(csdev)->enable) | |
| 410 | − return 0; | |
| 411 | − ret = helper_ops(csdev)->enable(csdev, mode, data); | |
| 412 | − if (ret) | |
| 413 | − return ret; | |
| 414 | − | |
| 415 | − csdev->enable = true; | |
| 416 | − return 0; | |
| 297 | + return helper_ops(csdev)->enable(csdev, mode, data); | |
| 417 | 298 | } |
| 418 | 299 | |
| 419 | 300 | static void coresight_disable_helper(struct coresight_device *csdev) |
| 420 | 301 | { |
| 421 | − int ret; | |
| 422 | − | |
| 423 | − if (!helper_ops(csdev)->disable) | |
| 424 | − return; | |
| 425 | − | |
| 426 | − ret = helper_ops(csdev)->disable(csdev, NULL); | |
| 427 | − if (ret) | |
| 428 | − return; | |
| 429 | − csdev->enable = false; | |
| 302 | + helper_ops(csdev)->disable(csdev, NULL); | |
| 430 | 303 | } |
| 431 | 304 | |
| 432 | 305 | static void coresight_disable_helpers(struct coresight_device *csdev) |
| @@ -441,25 +314,20 @@ static void coresight_disable_helpers(struct coresight_device *csdev) · 8 unmodified lines | ||
| 441 | 314 | } |
| 442 | 315 | } |
| 443 | 316 | |
| 444 | −/** | |
| 445 | − * coresight_disable_source - Drop the reference count by 1 and disable | |
| 446 | − * the device if there are no users left. | |
| 447 | − * | |
| 448 | − * @csdev: The coresight device to disable | |
| 449 | − * @data: Opaque data to pass on to the disable function of the source device. | |
| 450 | − * For example in perf mode this is a pointer to the struct perf_event. | |
| 317 | +/* | |
| 318 | + * Helper function to call source_ops(csdev)->disable and also disable the | |
| 319 | + * helpers. | |
| 451 | 320 | * |
| 452 | − * Returns true if the device has been disabled. | |
| 321 | + * There is an imbalance between coresight_enable_path() and | |
| 322 | + * coresight_disable_path(). Enabling also enables the source's helpers as part | |
| 323 | + * of the path, but disabling always skips the first item in the path (which is | |
| 324 | + * the source), so sources and their helpers don't get disabled as part of that | |
| 325 | + * function and we need the extra step here. | |
| 453 | 326 | */ |
| 454 | −bool coresight_disable_source(struct coresight_device *csdev, void *data) | |
| 327 | +void coresight_disable_source(struct coresight_device *csdev, void *data) | |
| 455 | 328 | { |
| 456 | − if (atomic_dec_return(&csdev->refcnt) == 0) { | |
| 457 | − if (source_ops(csdev)->disable) | |
| 458 | − source_ops(csdev)->disable(csdev, data); | |
| 459 | − coresight_disable_helpers(csdev); | |
| 460 | − csdev->enable = false; | |
| 461 | − } | |
| 462 | − return !csdev->enable; | |
| 329 | + source_ops(csdev)->disable(csdev, data); | |
| 330 | + coresight_disable_helpers(csdev); | |
| 463 | 331 | } |
| 464 | 332 | EXPORT_SYMBOL_GPL(coresight_disable_source); |
| 465 | 333 | |
| @@ -484,7 +352,7 @@ static void coresight_disable_path_from(struct list_head *path, · 18 unmodified lines | ||
| 484 | 352 | /* |
| 485 | 353 | * ETF devices are tricky... They can be a link or a sink, |
| 486 | 354 | * depending on how they are configured. If an ETF has been |
| 487 | − * "activated" it will be configured as a sink, otherwise | |
| 355 | + * selected as a sink it will be configured as a sink, otherwise | |
| 488 | 356 | * go ahead with the link configuration. |
| 489 | 357 | */ |
| 490 | 358 | if (type == CORESIGHT_DEV_TYPE_LINKSINK) |
| @@ -562,7 +430,7 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, · 71 unmodified lines | ||
| 562 | 430 | /* |
| 563 | 431 | * ETF devices are tricky... They can be a link or a sink, |
| 564 | 432 | * depending on how they are configured. If an ETF has been |
| 565 | − * "activated" it will be configured as a sink, otherwise | |
| 433 | + * selected as a sink it will be configured as a sink, otherwise | |
| 566 | 434 | * go ahead with the link configuration. |
| 567 | 435 | */ |
| 568 | 436 | if (type == CORESIGHT_DEV_TYPE_LINKSINK) |
| @@ -619,48 +487,6 @@ struct coresight_device *coresight_get_sink(struct list_head *path) · 50 unmodified lines | ||
| 619 | 487 | return csdev; |
| 620 | 488 | } |
| 621 | 489 | |
| 622 | −static struct coresight_device * | |
| 623 | −coresight_find_enabled_sink(struct coresight_device *csdev) | |
| 624 | −{ | |
| 625 | − int i; | |
| 626 | − struct coresight_device *sink = NULL; | |
| 627 | − | |
| 628 | − if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || | |
| 629 | − csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && | |
| 630 | − csdev->activated) | |
| 631 | − return csdev; | |
| 632 | − | |
| 633 | − /* | |
| 634 | − * Recursively explore each port found on this element. | |
| 635 | − */ | |
| 636 | − for (i = 0; i < csdev->pdata->nr_outconns; i++) { | |
| 637 | − struct coresight_device *child_dev; | |
| 638 | − | |
| 639 | − child_dev = csdev->pdata->out_conns[i]->dest_dev; | |
| 640 | − if (child_dev) | |
| 641 | − sink = coresight_find_enabled_sink(child_dev); | |
| 642 | − if (sink) | |
| 643 | − return sink; | |
| 644 | − } | |
| 645 | − | |
| 646 | − return NULL; | |
| 647 | −} | |
| 648 | − | |
| 649 | −/** | |
| 650 | − * coresight_get_enabled_sink - returns the first enabled sink using | |
| 651 | − * connection based search starting from the source reference | |
| 652 | − * | |
| 653 | − * @source: Coresight source device reference | |
| 654 | − */ | |
| 655 | −struct coresight_device * | |
| 656 | −coresight_get_enabled_sink(struct coresight_device *source) | |
| 657 | −{ | |
| 658 | − if (!source) | |
| 659 | − return NULL; | |
| 660 | − | |
| 661 | − return coresight_find_enabled_sink(source); | |
| 662 | −} | |
| 663 | − | |
| 664 | 490 | static int coresight_sink_by_id(struct device *dev, const void *data) |
| 665 | 491 | { |
| 666 | 492 | struct coresight_device *csdev = to_coresight_device(dev); |
| @@ -794,11 +620,10 @@ static void coresight_drop_device(struct coresight_device *csdev) · 127 unmodified lines | ||
| 794 | 620 | * @sink: The final sink we want in this path. |
| 795 | 621 | * @path: The list to add devices to. |
| 796 | 622 | * |
| 797 | − * The tree of Coresight device is traversed until an activated sink is | |
| 798 | − * found. From there the sink is added to the list along with all the | |
| 799 | − * devices that led to that point - the end result is a list from source | |
| 800 | − * to sink. In that list the source is the first device and the sink the | |
| 801 | − * last one. | |
| 623 | + * The tree of Coresight device is traversed until @sink is found. | |
| 624 | + * From there the sink is added to the list along with all the devices that led | |
| 625 | + * to that point - the end result is a list from source to sink. In that list | |
| 626 | + * the source is the first device and the sink the last one. | |
| 802 | 627 | */ |
| 803 | 628 | static int _coresight_build_path(struct coresight_device *csdev, |
| 804 | 629 | struct coresight_device *sink, |
| @@ -808,7 +633,7 @@ static int _coresight_build_path(struct coresight_device *csdev, · 3 unmodified lines | ||
| 808 | 633 | bool found = false; |
| 809 | 634 | struct coresight_node *node; |
| 810 | 635 | |
| 811 | − /* An activated sink has been found. Enqueue the element */ | |
| 636 | + /* The sink has been found. Enqueue the element */ | |
| 812 | 637 | if (csdev == sink) |
| 813 | 638 | goto out; |
| 814 | 639 | |
| @@ -1072,269 +897,6 @@ static void coresight_clear_default_sink(struct coresight_device *csdev) · 257 unmodified lines | ||
| 1072 | 897 | } |
| 1073 | 898 | } |
| 1074 | 899 | |
| 1075 | −/** coresight_validate_source - make sure a source has the right credentials | |
| 1076 | − * @csdev: the device structure for a source. | |
| 1077 | − * @function: the function this was called from. | |
| 1078 | − * | |
| 1079 | − * Assumes the coresight_mutex is held. | |
| 1080 | − */ | |
| 1081 | −static int coresight_validate_source(struct coresight_device *csdev, | |
| 1082 | − const char *function) | |
| 1083 | −{ | |
| 1084 | − u32 type, subtype; | |
| 1085 | − | |
| 1086 | − type = csdev->type; | |
| 1087 | − subtype = csdev->subtype.source_subtype; | |
| 1088 | − | |
| 1089 | − if (type != CORESIGHT_DEV_TYPE_SOURCE) { | |
| 1090 | − dev_err(&csdev->dev, "wrong device type in %s\n", function); | |
| 1091 | − return -EINVAL; | |
| 1092 | − } | |
| 1093 | − | |
| 1094 | − if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && | |
| 1095 | − subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE && | |
| 1096 | − subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM && | |
| 1097 | − subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) { | |
| 1098 | − dev_err(&csdev->dev, "wrong device subtype in %s\n", function); | |
| 1099 | − return -EINVAL; | |
| 1100 | − } | |
| 1101 | − | |
| 1102 | − return 0; | |
| 1103 | −} | |
| 1104 | − | |
| 1105 | −int coresight_enable(struct coresight_device *csdev) | |
| 1106 | −{ | |
| 1107 | − int cpu, ret = 0; | |
| 1108 | − struct coresight_device *sink; | |
| 1109 | − struct list_head *path; | |
| 1110 | − enum coresight_dev_subtype_source subtype; | |
| 1111 | − u32 hash; | |
| 1112 | − | |
| 1113 | − subtype = csdev->subtype.source_subtype; | |
| 1114 | − | |
| 1115 | − mutex_lock(&coresight_mutex); | |
| 1116 | − | |
| 1117 | − ret = coresight_validate_source(csdev, __func__); | |
| 1118 | − if (ret) | |
| 1119 | − goto out; | |
| 1120 | − | |
| 1121 | − if (csdev->enable) { | |
| 1122 | − /* | |
| 1123 | − * There could be multiple applications driving the software | |
| 1124 | − * source. So keep the refcount for each such user when the | |
| 1125 | − * source is already enabled. | |
| 1126 | − */ | |
| 1127 | − if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) | |
| 1128 | − atomic_inc(&csdev->refcnt); | |
| 1129 | − goto out; | |
| 1130 | − } | |
| 1131 | − | |
| 1132 | − sink = coresight_get_enabled_sink(csdev); | |
| 1133 | − if (!sink) { | |
| 1134 | − ret = -EINVAL; | |
| 1135 | − goto out; | |
| 1136 | − } | |
| 1137 | − | |
| 1138 | − path = coresight_build_path(csdev, sink); | |
| 1139 | − if (IS_ERR(path)) { | |
| 1140 | − pr_err("building path(s) failed\n"); | |
| 1141 | − ret = PTR_ERR(path); | |
| 1142 | − goto out; | |
| 1143 | − } | |
| 1144 | − | |
| 1145 | − ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); | |
| 1146 | − if (ret) | |
| 1147 | − goto err_path; | |
| 1148 | − | |
| 1149 | − ret = coresight_enable_source(csdev, CS_MODE_SYSFS, NULL); | |
| 1150 | − if (ret) | |
| 1151 | − goto err_source; | |
| 1152 | − | |
| 1153 | − switch (subtype) { | |
| 1154 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: | |
| 1155 | − /* | |
| 1156 | − * When working from sysFS it is important to keep track | |
| 1157 | − * of the paths that were created so that they can be | |
| 1158 | − * undone in 'coresight_disable()'. Since there can only | |
| 1159 | − * be a single session per tracer (when working from sysFS) | |
| 1160 | − * a per-cpu variable will do just fine. | |
| 1161 | − */ | |
| 1162 | − cpu = source_ops(csdev)->cpu_id(csdev); | |
| 1163 | − per_cpu(tracer_path, cpu) = path; | |
| 1164 | − break; | |
| 1165 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: | |
| 1166 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: | |
| 1167 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: | |
| 1168 | − /* | |
| 1169 | − * Use the hash of source's device name as ID | |
| 1170 | − * and map the ID to the pointer of the path. | |
| 1171 | − */ | |
| 1172 | − hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); | |
| 1173 | − ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); | |
| 1174 | − if (ret) | |
| 1175 | − goto err_source; | |
| 1176 | − break; | |
| 1177 | − default: | |
| 1178 | − /* We can't be here */ | |
| 1179 | − break; | |
| 1180 | − } | |
| 1181 | − | |
| 1182 | −out: | |
| 1183 | − mutex_unlock(&coresight_mutex); | |
| 1184 | − return ret; | |
| 1185 | − | |
| 1186 | −err_source: | |
| 1187 | − coresight_disable_path(path); | |
| 1188 | − | |
| 1189 | −err_path: | |
| 1190 | − coresight_release_path(path); | |
| 1191 | − goto out; | |
| 1192 | −} | |
| 1193 | −EXPORT_SYMBOL_GPL(coresight_enable); | |
| 1194 | − | |
| 1195 | −void coresight_disable(struct coresight_device *csdev) | |
| 1196 | −{ | |
| 1197 | − int cpu, ret; | |
| 1198 | − struct list_head *path = NULL; | |
| 1199 | − u32 hash; | |
| 1200 | − | |
| 1201 | − mutex_lock(&coresight_mutex); | |
| 1202 | − | |
| 1203 | − ret = coresight_validate_source(csdev, __func__); | |
| 1204 | − if (ret) | |
| 1205 | − goto out; | |
| 1206 | − | |
| 1207 | − if (!csdev->enable || !coresight_disable_source(csdev, NULL)) | |
| 1208 | − goto out; | |
| 1209 | − | |
| 1210 | − switch (csdev->subtype.source_subtype) { | |
| 1211 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: | |
| 1212 | − cpu = source_ops(csdev)->cpu_id(csdev); | |
| 1213 | − path = per_cpu(tracer_path, cpu); | |
| 1214 | − per_cpu(tracer_path, cpu) = NULL; | |
| 1215 | − break; | |
| 1216 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: | |
| 1217 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: | |
| 1218 | − case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: | |
| 1219 | − hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); | |
| 1220 | − /* Find the path by the hash. */ | |
| 1221 | − path = idr_find(&path_idr, hash); | |
| 1222 | − if (path == NULL) { | |
| 1223 | − pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); | |
| 1224 | − goto out; | |
| 1225 | − } | |
| 1226 | − idr_remove(&path_idr, hash); | |
| 1227 | − break; | |
| 1228 | − default: | |
| 1229 | − /* We can't be here */ | |
| 1230 | − break; | |
| 1231 | − } | |
| 1232 | − | |
| 1233 | − coresight_disable_path(path); | |
| 1234 | − coresight_release_path(path); | |
| 1235 | − | |
| 1236 | −out: | |
| 1237 | − mutex_unlock(&coresight_mutex); | |
| 1238 | −} | |
| 1239 | −EXPORT_SYMBOL_GPL(coresight_disable); | |
| 1240 | − | |
| 1241 | −static ssize_t enable_sink_show(struct device *dev, | |
| 1242 | − struct device_attribute *attr, char *buf) | |
| 1243 | −{ | |
| 1244 | − struct coresight_device *csdev = to_coresight_device(dev); | |
| 1245 | − | |
| 1246 | − return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->activated); | |
| 1247 | −} | |
| 1248 | − | |
| 1249 | −static ssize_t enable_sink_store(struct device *dev, | |
| 1250 | − struct device_attribute *attr, | |
| 1251 | − const char *buf, size_t size) | |
| 1252 | −{ | |
| 1253 | − int ret; | |
| 1254 | − unsigned long val; | |
| 1255 | − struct coresight_device *csdev = to_coresight_device(dev); | |
| 1256 | − | |
| 1257 | − ret = kstrtoul(buf, 10, &val); | |
| 1258 | − if (ret) | |
| 1259 | − return ret; | |
| 1260 | − | |
| 1261 | − if (val) | |
| 1262 | − csdev->activated = true; | |
| 1263 | − else | |
| 1264 | − csdev->activated = false; | |
| 1265 | − | |
| 1266 | − return size; | |
| 1267 | − | |
| 1268 | −} | |
| 1269 | −static DEVICE_ATTR_RW(enable_sink); | |
| 1270 | − | |
| 1271 | −static ssize_t enable_source_show(struct device *dev, | |
| 1272 | − struct device_attribute *attr, char *buf) | |
| 1273 | −{ | |
| 1274 | − struct coresight_device *csdev = to_coresight_device(dev); | |
| 1275 | − | |
| 1276 | − return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->enable); | |
| 1277 | −} | |
| 1278 | − | |
| 1279 | −static ssize_t enable_source_store(struct device *dev, | |
| 1280 | − struct device_attribute *attr, | |
| 1281 | − const char *buf, size_t size) | |
| 1282 | −{ | |
| 1283 | − int ret = 0; | |
| 1284 | − unsigned long val; | |
| 1285 | − struct coresight_device *csdev = to_coresight_device(dev); | |
| 1286 | − | |
| 1287 | − ret = kstrtoul(buf, 10, &val); | |
| 1288 | − if (ret) | |
| 1289 | − return ret; | |
| 1290 | − | |
| 1291 | − if (val) { | |
| 1292 | − ret = coresight_enable(csdev); | |
| 1293 | − if (ret) | |
| 1294 | − return ret; | |
| 1295 | − } else { | |
| 1296 | − coresight_disable(csdev); | |
| 1297 | − } | |
| 1298 | − | |
| 1299 | − return size; | |
| 1300 | −} | |
| 1301 | −static DEVICE_ATTR_RW(enable_source); | |
| 1302 | − | |
| 1303 | −static struct attribute *coresight_sink_attrs[] = { | |
| 1304 | − &dev_attr_enable_sink.attr, | |
| 1305 | − NULL, | |
| 1306 | −}; | |
| 1307 | −ATTRIBUTE_GROUPS(coresight_sink); | |
| 1308 | − | |
| 1309 | −static struct attribute *coresight_source_attrs[] = { | |
| 1310 | − &dev_attr_enable_source.attr, | |
| 1311 | − NULL, | |
| 1312 | −}; | |
| 1313 | −ATTRIBUTE_GROUPS(coresight_source); | |
| 1314 | − | |
| 1315 | −static struct device_type coresight_dev_type[] = { | |
| 1316 | − { | |
| 1317 | − .name = "sink", | |
| 1318 | − .groups = coresight_sink_groups, | |
| 1319 | − }, | |
| 1320 | − { | |
| 1321 | − .name = "link", | |
| 1322 | − }, | |
| 1323 | − { | |
| 1324 | − .name = "linksink", | |
| 1325 | − .groups = coresight_sink_groups, | |
| 1326 | − }, | |
| 1327 | − { | |
| 1328 | − .name = "source", | |
| 1329 | − .groups = coresight_source_groups, | |
| 1330 | − }, | |
| 1331 | − { | |
| 1332 | − .name = "helper", | |
| 1333 | − } | |
| 1334 | −}; | |
| 1335 | −/* Ensure the enum matches the names and groups */ | |
| 1336 | −static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX); | |
| 1337 | − | |
| 1338 | 900 | static void coresight_device_release(struct device *dev) |
| 1339 | 901 | { |
| 1340 | 902 | struct coresight_device *csdev = to_coresight_device(dev); |
| @@ -1799,7 +1361,7 @@ done: · 458 unmodified lines | ||
| 1799 | 1361 | } |
| 1800 | 1362 | EXPORT_SYMBOL_GPL(coresight_alloc_device_name); |
| 1801 | 1363 | |
| 1802 | −struct bus_type coresight_bustype = { | |
| 1364 | +const struct bus_type coresight_bustype = { | |
| 1803 | 1365 | .name = "coresight", |
| 1804 | 1366 | }; |
| 1805 | 1367 | |
| @@ -974,7 +974,7 @@ static const struct amba_id cti_ids[] = { · 973 unmodified lines | ||
| 974 | 974 | CS_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */ |
| 975 | 975 | CS_AMBA_UCI_ID(0x000bb9da, uci_id_cti), /* CTI - C-A35 */ |
| 976 | 976 | CS_AMBA_UCI_ID(0x000bb9ed, uci_id_cti), /* Coresight CTI (SoC 600) */ |
| 977 | − { 0, 0}, | |
| 977 | + { 0, 0, NULL }, | |
| 978 | 978 | }; |
| 979 | 979 | |
| 980 | 980 | MODULE_DEVICE_TABLE(amba, cti_ids); |
| @@ -76,7 +76,6 @@ DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb"); · 75 unmodified lines | ||
| 76 | 76 | * @pid: Process ID of the process being monitored by the session |
| 77 | 77 | * that is using this component. |
| 78 | 78 | * @buf: area of memory where ETB buffer content gets sent. |
| 79 | − * @mode: this ETB is being used. | |
| 80 | 79 | * @buffer_depth: size of @buf. |
| 81 | 80 | * @trigger_cntr: amount of words to store after a trigger. |
| 82 | 81 | */ |
| @@ -89,7 +88,6 @@ struct etb_drvdata { · 6 unmodified lines | ||
| 89 | 88 | local_t reading; |
| 90 | 89 | pid_t pid; |
| 91 | 90 | u8 *buf; |
| 92 | − u32 mode; | |
| 93 | 91 | u32 buffer_depth; |
| 94 | 92 | u32 trigger_cntr; |
| 95 | 93 | }; |
| @@ -150,20 +148,20 @@ static int etb_enable_sysfs(struct coresight_device *csdev) · 54 unmodified lines | ||
| 150 | 148 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 151 | 149 | |
| 152 | 150 | /* Don't messup with perf sessions. */ |
| 153 | − if (drvdata->mode == CS_MODE_PERF) { | |
| 151 | + if (coresight_get_mode(csdev) == CS_MODE_PERF) { | |
| 154 | 152 | ret = -EBUSY; |
| 155 | 153 | goto out; |
| 156 | 154 | } |
| 157 | 155 | |
| 158 | − if (drvdata->mode == CS_MODE_DISABLED) { | |
| 156 | + if (coresight_get_mode(csdev) == CS_MODE_DISABLED) { | |
| 159 | 157 | ret = etb_enable_hw(drvdata); |
| 160 | 158 | if (ret) |
| 161 | 159 | goto out; |
| 162 | 160 | |
| 163 | − drvdata->mode = CS_MODE_SYSFS; | |
| 161 | + coresight_set_mode(csdev, CS_MODE_SYSFS); | |
| 164 | 162 | } |
| 165 | 163 | |
| 166 | − atomic_inc(&csdev->refcnt); | |
| 164 | + csdev->refcnt++; | |
| 167 | 165 | out: |
| 168 | 166 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 169 | 167 | return ret; |
| @@ -181,7 +179,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) · 11 unmodified lines | ||
| 181 | 179 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 182 | 180 | |
| 183 | 181 | /* No need to continue if the component is already in used by sysFS. */ |
| 184 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 182 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { | |
| 185 | 183 | ret = -EBUSY; |
| 186 | 184 | goto out; |
| 187 | 185 | } |
| @@ -199,7 +197,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) · 11 unmodified lines | ||
| 199 | 197 | * use for this session. |
| 200 | 198 | */ |
| 201 | 199 | if (drvdata->pid == pid) { |
| 202 | − atomic_inc(&csdev->refcnt); | |
| 200 | + csdev->refcnt++; | |
| 203 | 201 | goto out; |
| 204 | 202 | } |
| 205 | 203 | |
| @@ -216,8 +214,8 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) · 10 unmodified lines | ||
| 216 | 214 | if (!ret) { |
| 217 | 215 | /* Associate with monitored process. */ |
| 218 | 216 | drvdata->pid = pid; |
| 219 | − drvdata->mode = CS_MODE_PERF; | |
| 220 | − atomic_inc(&csdev->refcnt); | |
| 217 | + coresight_set_mode(drvdata->csdev, CS_MODE_PERF); | |
| 218 | + csdev->refcnt++; | |
| 221 | 219 | } |
| 222 | 220 | |
| 223 | 221 | out: |
| @@ -356,17 +354,18 @@ static int etb_disable(struct coresight_device *csdev) · 132 unmodified lines | ||
| 356 | 354 | |
| 357 | 355 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 358 | 356 | |
| 359 | − if (atomic_dec_return(&csdev->refcnt)) { | |
| 357 | + csdev->refcnt--; | |
| 358 | + if (csdev->refcnt) { | |
| 360 | 359 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 361 | 360 | return -EBUSY; |
| 362 | 361 | } |
| 363 | 362 | |
| 364 | 363 | /* Complain if we (somehow) got out of sync */ |
| 365 | − WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); | |
| 364 | + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); | |
| 366 | 365 | etb_disable_hw(drvdata); |
| 367 | 366 | /* Dissociate from monitored process. */ |
| 368 | 367 | drvdata->pid = -1; |
| 369 | − drvdata->mode = CS_MODE_DISABLED; | |
| 368 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 370 | 369 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 371 | 370 | |
| 372 | 371 | dev_dbg(&csdev->dev, "ETB disabled\n"); |
| @@ -447,7 +446,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev, · 74 unmodified lines | ||
| 447 | 446 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 448 | 447 | |
| 449 | 448 | /* Don't do anything if another tracer is using this sink */ |
| 450 | − if (atomic_read(&csdev->refcnt) != 1) | |
| 449 | + if (csdev->refcnt != 1) | |
| 451 | 450 | goto out; |
| 452 | 451 | |
| 453 | 452 | __etb_disable_hw(drvdata); |
| @@ -589,7 +588,7 @@ static void etb_dump(struct etb_drvdata *drvdata) · 135 unmodified lines | ||
| 589 | 588 | unsigned long flags; |
| 590 | 589 | |
| 591 | 590 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 592 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 591 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { | |
| 593 | 592 | __etb_disable_hw(drvdata); |
| 594 | 593 | etb_dump_hw(drvdata); |
| 595 | 594 | __etb_enable_hw(drvdata); |
| @@ -837,7 +836,7 @@ static const struct amba_id etb_ids[] = { · 241 unmodified lines | ||
| 837 | 836 | .id = 0x000bb907, |
| 838 | 837 | .mask = 0x000fffff, |
| 839 | 838 | }, |
| 840 | − { 0, 0}, | |
| 839 | + { 0, 0, NULL }, | |
| 841 | 840 | }; |
| 842 | 841 | |
| 843 | 842 | MODULE_DEVICE_TABLE(amba, etb_ids); |
| @@ -589,7 +589,7 @@ static void etm_event_stop(struct perf_event *event, int mode) · 588 unmodified lines | ||
| 589 | 589 | return; |
| 590 | 590 | |
| 591 | 591 | /* stop tracer */ |
| 592 | − source_ops(csdev)->disable(csdev, event); | |
| 592 | + coresight_disable_source(csdev, event); | |
| 593 | 593 | |
| 594 | 594 | /* tell the core */ |
| 595 | 595 | event->hw.state = PERF_HES_STOPPED; |
| @@ -215,7 +215,6 @@ struct etm_config { · 214 unmodified lines | ||
| 215 | 215 | * @port_size: port size as reported by ETMCR bit 4-6 and 21. |
| 216 | 216 | * @arch: ETM/PTM version number. |
| 217 | 217 | * @use_cpu14: true if management registers need to be accessed via CP14. |
| 218 | − * @mode: this tracer's mode, i.e sysFS, Perf or disabled. | |
| 219 | 218 | * @sticky_enable: true if ETM base configuration has been done. |
| 220 | 219 | * @boot_enable:true if we should start tracing at boot time. |
| 221 | 220 | * @os_unlock: true if access to management registers is allowed. |
| @@ -238,7 +237,6 @@ struct etm_drvdata { · 16 unmodified lines | ||
| 238 | 237 | int port_size; |
| 239 | 238 | u8 arch; |
| 240 | 239 | bool use_cp14; |
| 241 | − local_t mode; | |
| 242 | 240 | bool sticky_enable; |
| 243 | 241 | bool boot_enable; |
| 244 | 242 | bool os_unlock; |
| @@ -115,7 +115,7 @@ static void etm_clr_pwrup(struct etm_drvdata *drvdata) · 114 unmodified lines | ||
| 115 | 115 | * |
| 116 | 116 | * Basically the same as @coresight_timeout except for the register access |
| 117 | 117 | * method where we have to account for CP14 configurations. |
| 118 | − | |
| 118 | + * | |
| 119 | 119 | * Return: 0 as soon as the bit has taken the desired state or -EAGAIN if |
| 120 | 120 | * TIMEOUT_US has elapsed, which ever happens first. |
| 121 | 121 | */ |
| @@ -556,14 +556,12 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event, · 434 unmodified lines | ||
| 556 | 556 | enum cs_mode mode) |
| 557 | 557 | { |
| 558 | 558 | int ret; |
| 559 | − u32 val; | |
| 560 | 559 | struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
| 561 | 560 | |
| 562 | − val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); | |
| 563 | − | |
| 564 | − /* Someone is already using the tracer */ | |
| 565 | − if (val) | |
| 561 | + if (!coresight_take_mode(csdev, mode)) { | |
| 562 | + /* Someone is already using the tracer */ | |
| 566 | 563 | return -EBUSY; |
| 564 | + } | |
| 567 | 565 | |
| 568 | 566 | switch (mode) { |
| 569 | 567 | case CS_MODE_SYSFS: |
| @@ -578,7 +576,7 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event, · 8 unmodified lines | ||
| 578 | 576 | |
| 579 | 577 | /* The tracer didn't start */ |
| 580 | 578 | if (ret) |
| 581 | − local_set(&drvdata->mode, CS_MODE_DISABLED); | |
| 579 | + coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); | |
| 582 | 580 | |
| 583 | 581 | return ret; |
| 584 | 582 | } |
| @@ -672,14 +670,13 @@ static void etm_disable(struct coresight_device *csdev, · 87 unmodified lines | ||
| 672 | 670 | struct perf_event *event) |
| 673 | 671 | { |
| 674 | 672 | enum cs_mode mode; |
| 675 | − struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
| 676 | 673 | |
| 677 | 674 | /* |
| 678 | 675 | * For as long as the tracer isn't disabled another entity can't |
| 679 | 676 | * change its status. As such we can read the status here without |
| 680 | 677 | * fearing it will change under us. |
| 681 | 678 | */ |
| 682 | − mode = local_read(&drvdata->mode); | |
| 679 | + mode = coresight_get_mode(csdev); | |
| 683 | 680 | |
| 684 | 681 | switch (mode) { |
| 685 | 682 | case CS_MODE_DISABLED: |
| @@ -696,7 +693,7 @@ static void etm_disable(struct coresight_device *csdev, · 10 unmodified lines | ||
| 696 | 693 | } |
| 697 | 694 | |
| 698 | 695 | if (mode) |
| 699 | − local_set(&drvdata->mode, CS_MODE_DISABLED); | |
| 696 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 700 | 697 | } |
| 701 | 698 | |
| 702 | 699 | static const struct coresight_ops_source etm_source_ops = { |
| @@ -715,7 +712,7 @@ static int etm_online_cpu(unsigned int cpu) · 12 unmodified lines | ||
| 715 | 712 | return 0; |
| 716 | 713 | |
| 717 | 714 | if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable) |
| 718 | − coresight_enable(etmdrvdata[cpu]->csdev); | |
| 715 | + coresight_enable_sysfs(etmdrvdata[cpu]->csdev); | |
| 719 | 716 | return 0; |
| 720 | 717 | } |
| 721 | 718 | |
| @@ -730,7 +727,7 @@ static int etm_starting_cpu(unsigned int cpu) · 8 unmodified lines | ||
| 730 | 727 | etmdrvdata[cpu]->os_unlock = true; |
| 731 | 728 | } |
| 732 | 729 | |
| 733 | − if (local_read(&etmdrvdata[cpu]->mode)) | |
| 730 | + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) | |
| 734 | 731 | etm_enable_hw(etmdrvdata[cpu]); |
| 735 | 732 | spin_unlock(&etmdrvdata[cpu]->spinlock); |
| 736 | 733 | return 0; |
| @@ -742,7 +739,7 @@ static int etm_dying_cpu(unsigned int cpu) · 5 unmodified lines | ||
| 742 | 739 | return 0; |
| 743 | 740 | |
| 744 | 741 | spin_lock(&etmdrvdata[cpu]->spinlock); |
| 745 | − if (local_read(&etmdrvdata[cpu]->mode)) | |
| 742 | + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) | |
| 746 | 743 | etm_disable_hw(etmdrvdata[cpu]); |
| 747 | 744 | spin_unlock(&etmdrvdata[cpu]->spinlock); |
| 748 | 745 | return 0; |
| @@ -925,7 +922,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) · 176 unmodified lines | ||
| 925 | 922 | dev_info(&drvdata->csdev->dev, |
| 926 | 923 | "%s initialized\n", (char *)coresight_get_uci_data(id)); |
| 927 | 924 | if (boot_enable) { |
| 928 | − coresight_enable(drvdata->csdev); | |
| 925 | + coresight_enable_sysfs(drvdata->csdev); | |
| 929 | 926 | drvdata->boot_enable = true; |
| 930 | 927 | } |
| 931 | 928 | |
| @@ -1003,7 +1000,7 @@ static const struct amba_id etm_ids[] = { · 71 unmodified lines | ||
| 1003 | 1000 | CS_AMBA_ID_DATA(0x000bb95f, "PTM 1.1"), |
| 1004 | 1001 | /* PTM 1.1 Qualcomm */ |
| 1005 | 1002 | CS_AMBA_ID_DATA(0x000b006f, "PTM 1.1"), |
| 1006 | − { 0, 0}, | |
| 1003 | + { 0, 0, NULL}, | |
| 1007 | 1004 | }; |
| 1008 | 1005 | |
| 1009 | 1006 | MODULE_DEVICE_TABLE(amba, etm_ids); |
| @@ -722,7 +722,7 @@ static ssize_t cntr_val_show(struct device *dev, · 721 unmodified lines | ||
| 722 | 722 | struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 723 | 723 | struct etm_config *config = &drvdata->config; |
| 724 | 724 | |
| 725 | − if (!local_read(&drvdata->mode)) { | |
| 725 | + if (!coresight_get_mode(drvdata->csdev)) { | |
| 726 | 726 | spin_lock(&drvdata->spinlock); |
| 727 | 727 | for (i = 0; i < drvdata->nr_cntr; i++) |
| 728 | 728 | ret += sprintf(buf, "counter %d: %x\n", |
| @@ -941,7 +941,7 @@ static ssize_t seq_curr_state_show(struct device *dev, · 212 unmodified lines | ||
| 941 | 941 | struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 942 | 942 | struct etm_config *config = &drvdata->config; |
| 943 | 943 | |
| 944 | − if (!local_read(&drvdata->mode)) { | |
| 944 | + if (!coresight_get_mode(drvdata->csdev)) { | |
| 945 | 945 | val = config->seq_curr_state; |
| 946 | 946 | goto out; |
| 947 | 947 | } |
| @@ -840,14 +840,11 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, · 839 unmodified lines | ||
| 840 | 840 | enum cs_mode mode) |
| 841 | 841 | { |
| 842 | 842 | int ret; |
| 843 | − u32 val; | |
| 844 | − struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
| 845 | − | |
| 846 | − val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); | |
| 847 | 843 | |
| 848 | − /* Someone is already using the tracer */ | |
| 849 | − if (val) | |
| 844 | + if (!coresight_take_mode(csdev, mode)) { | |
| 845 | + /* Someone is already using the tracer */ | |
| 850 | 846 | return -EBUSY; |
| 847 | + } | |
| 851 | 848 | |
| 852 | 849 | switch (mode) { |
| 853 | 850 | case CS_MODE_SYSFS: |
| @@ -862,7 +859,7 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event, · 8 unmodified lines | ||
| 862 | 859 | |
| 863 | 860 | /* The tracer didn't start */ |
| 864 | 861 | if (ret) |
| 865 | − local_set(&drvdata->mode, CS_MODE_DISABLED); | |
| 862 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 866 | 863 | |
| 867 | 864 | return ret; |
| 868 | 865 | } |
| @@ -1004,14 +1001,13 @@ static void etm4_disable(struct coresight_device *csdev, · 135 unmodified lines | ||
| 1004 | 1001 | struct perf_event *event) |
| 1005 | 1002 | { |
| 1006 | 1003 | enum cs_mode mode; |
| 1007 | − struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
| 1008 | 1004 | |
| 1009 | 1005 | /* |
| 1010 | 1006 | * For as long as the tracer isn't disabled another entity can't |
| 1011 | 1007 | * change its status. As such we can read the status here without |
| 1012 | 1008 | * fearing it will change under us. |
| 1013 | 1009 | */ |
| 1014 | − mode = local_read(&drvdata->mode); | |
| 1010 | + mode = coresight_get_mode(csdev); | |
| 1015 | 1011 | |
| 1016 | 1012 | switch (mode) { |
| 1017 | 1013 | case CS_MODE_DISABLED: |
| @@ -1025,7 +1021,7 @@ static void etm4_disable(struct coresight_device *csdev, · 7 unmodified lines | ||
| 1025 | 1021 | } |
| 1026 | 1022 | |
| 1027 | 1023 | if (mode) |
| 1028 | − local_set(&drvdata->mode, CS_MODE_DISABLED); | |
| 1024 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 1029 | 1025 | } |
| 1030 | 1026 | |
| 1031 | 1027 | static const struct coresight_ops_source etm4_source_ops = { |
| @@ -1200,6 +1196,7 @@ static void etm4_init_arch_data(void *info) · 168 unmodified lines | ||
| 1200 | 1196 | struct etm4_init_arg *init_arg = info; |
| 1201 | 1197 | struct etmv4_drvdata *drvdata; |
| 1202 | 1198 | struct csdev_access *csa; |
| 1199 | + struct device *dev = init_arg->dev; | |
| 1203 | 1200 | int i; |
| 1204 | 1201 | |
| 1205 | 1202 | drvdata = dev_get_drvdata(init_arg->dev); |
| @@ -1213,6 +1210,10 @@ static void etm4_init_arch_data(void *info) · 7 unmodified lines | ||
| 1213 | 1210 | if (!etm4_init_csdev_access(drvdata, csa)) |
| 1214 | 1211 | return; |
| 1215 | 1212 | |
| 1213 | + if (!csa->io_mem || | |
| 1214 | + fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up")) | |
| 1215 | + drvdata->skip_power_up = true; | |
| 1216 | + | |
| 1216 | 1217 | /* Detect the support for OS Lock before we actually use it */ |
| 1217 | 1218 | etm_detect_os_lock(drvdata, csa); |
| 1218 | 1219 | |
| @@ -1650,7 +1651,7 @@ static int etm4_online_cpu(unsigned int cpu) · 431 unmodified lines | ||
| 1650 | 1651 | return etm4_probe_cpu(cpu); |
| 1651 | 1652 | |
| 1652 | 1653 | if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable) |
| 1653 | − coresight_enable(etmdrvdata[cpu]->csdev); | |
| 1654 | + coresight_enable_sysfs(etmdrvdata[cpu]->csdev); | |
| 1654 | 1655 | return 0; |
| 1655 | 1656 | } |
| 1656 | 1657 | |
| @@ -1663,7 +1664,7 @@ static int etm4_starting_cpu(unsigned int cpu) · 6 unmodified lines | ||
| 1663 | 1664 | if (!etmdrvdata[cpu]->os_unlock) |
| 1664 | 1665 | etm4_os_unlock(etmdrvdata[cpu]); |
| 1665 | 1666 | |
| 1666 | − if (local_read(&etmdrvdata[cpu]->mode)) | |
| 1667 | + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) | |
| 1667 | 1668 | etm4_enable_hw(etmdrvdata[cpu]); |
| 1668 | 1669 | spin_unlock(&etmdrvdata[cpu]->spinlock); |
| 1669 | 1670 | return 0; |
| @@ -1675,7 +1676,7 @@ static int etm4_dying_cpu(unsigned int cpu) · 5 unmodified lines | ||
| 1675 | 1676 | return 0; |
| 1676 | 1677 | |
| 1677 | 1678 | spin_lock(&etmdrvdata[cpu]->spinlock); |
| 1678 | − if (local_read(&etmdrvdata[cpu]->mode)) | |
| 1679 | + if (coresight_get_mode(etmdrvdata[cpu]->csdev)) | |
| 1679 | 1680 | etm4_disable_hw(etmdrvdata[cpu]); |
| 1680 | 1681 | spin_unlock(&etmdrvdata[cpu]->spinlock); |
| 1681 | 1682 | return 0; |
| @@ -1833,7 +1834,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) · 151 unmodified lines | ||
| 1833 | 1834 | * Save and restore the ETM Trace registers only if |
| 1834 | 1835 | * the ETM is active. |
| 1835 | 1836 | */ |
| 1836 | − if (local_read(&drvdata->mode) && drvdata->save_state) | |
| 1837 | + if (coresight_get_mode(drvdata->csdev) && drvdata->save_state) | |
| 1837 | 1838 | ret = __etm4_cpu_save(drvdata); |
| 1838 | 1839 | return ret; |
| 1839 | 1840 | } |
| @@ -2040,11 +2041,6 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) · 200 unmodified lines | ||
| 2040 | 2041 | if (!drvdata->arch) |
| 2041 | 2042 | return -EINVAL; |
| 2042 | 2043 | |
| 2043 | − /* TRCPDCR is not accessible with system instructions. */ | |
| 2044 | − if (!desc.access.io_mem || | |
| 2045 | − fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up")) | |
| 2046 | − drvdata->skip_power_up = true; | |
| 2047 | − | |
| 2048 | 2044 | major = ETM_ARCH_MAJOR_VERSION(drvdata->arch); |
| 2049 | 2045 | minor = ETM_ARCH_MINOR_VERSION(drvdata->arch); |
| 2050 | 2046 | |
| @@ -2098,7 +2094,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) · 47 unmodified lines | ||
| 2098 | 2094 | drvdata->cpu, type_name, major, minor); |
| 2099 | 2095 | |
| 2100 | 2096 | if (boot_enable) { |
| 2101 | − coresight_enable(drvdata->csdev); | |
| 2097 | + coresight_enable_sysfs(drvdata->csdev); | |
| 2102 | 2098 | drvdata->boot_enable = true; |
| 2103 | 2099 | } |
| 2104 | 2100 | |
| @@ -2390,7 +2386,7 @@ static const struct of_device_id etm4_sysreg_match[] = { · 285 unmodified lines | ||
| 2390 | 2386 | |
| 2391 | 2387 | #ifdef CONFIG_ACPI |
| 2392 | 2388 | static const struct acpi_device_id etm4x_acpi_ids[] = { |
| 2393 | − {"ARMHC500", 0}, /* ARM CoreSight ETM4x */ | |
| 2389 | + {"ARMHC500", 0, 0, 0}, /* ARM CoreSight ETM4x */ | |
| 2394 | 2390 | {} |
| 2395 | 2391 | }; |
| 2396 | 2392 | MODULE_DEVICE_TABLE(acpi, etm4x_acpi_ids); |
| @@ -1016,7 +1016,6 @@ struct etmv4_drvdata { · 1015 unmodified lines | ||
| 1016 | 1016 | void __iomem *base; |
| 1017 | 1017 | struct coresight_device *csdev; |
| 1018 | 1018 | spinlock_t spinlock; |
| 1019 | − local_t mode; | |
| 1020 | 1019 | int cpu; |
| 1021 | 1020 | u8 arch; |
| 1022 | 1021 | u8 nr_pe; |
| @@ -350,7 +350,7 @@ MODULE_DEVICE_TABLE(of, static_funnel_match); · 349 unmodified lines | ||
| 350 | 350 | |
| 351 | 351 | #ifdef CONFIG_ACPI |
| 352 | 352 | static const struct acpi_device_id static_funnel_ids[] = { |
| 353 | − {"ARMHC9FE", 0}, | |
| 353 | + {"ARMHC9FE", 0, 0, 0}, | |
| 354 | 354 | {}, |
| 355 | 355 | }; |
| 356 | 356 | |
| @@ -391,7 +391,7 @@ static const struct amba_id dynamic_funnel_ids[] = { · 34 unmodified lines | ||
| 391 | 391 | .id = 0x000bb9eb, |
| 392 | 392 | .mask = 0x000fffff, |
| 393 | 393 | }, |
| 394 | − { 0, 0}, | |
| 394 | + { 0, 0, NULL }, | |
| 395 | 395 | }; |
| 396 | 396 | |
| 397 | 397 | MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids); |
| @@ -12,6 +12,9 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <linux/coresight.h> |
| 13 | 13 | #include <linux/pm_runtime.h> |
| 14 | 14 | |
| 15 | +extern struct mutex coresight_mutex; | |
| 16 | +extern struct device_type coresight_dev_type[]; | |
| 17 | + | |
| 15 | 18 | /* |
| 16 | 19 | * Coresight management registers (0xf00-0xfcc) |
| 17 | 20 | * 0xfa0 - 0xfa4: Management registers in PFTv1.0 |
| @@ -130,8 +133,6 @@ void coresight_disable_path(struct list_head *path); · 112 unmodified lines | ||
| 130 | 133 | int coresight_enable_path(struct list_head *path, enum cs_mode mode, |
| 131 | 134 | void *sink_data); |
| 132 | 135 | struct coresight_device *coresight_get_sink(struct list_head *path); |
| 133 | −struct coresight_device * | |
| 134 | −coresight_get_enabled_sink(struct coresight_device *source); | |
| 135 | 136 | struct coresight_device *coresight_get_sink_by_id(u32 id); |
| 136 | 137 | struct coresight_device * |
| 137 | 138 | coresight_find_default_sink(struct coresight_device *csdev); |
| @@ -231,8 +232,6 @@ void coresight_add_helper(struct coresight_device *csdev, · 93 unmodified lines | ||
| 231 | 232 | |
| 232 | 233 | void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev); |
| 233 | 234 | struct coresight_device *coresight_get_percpu_sink(int cpu); |
| 234 | −int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode, | |
| 235 | − void *data); | |
| 236 | −bool coresight_disable_source(struct coresight_device *csdev, void *data); | |
| 235 | +void coresight_disable_source(struct coresight_device *csdev, void *data); | |
| 237 | 236 | |
| 238 | 237 | #endif |
| @@ -363,7 +363,7 @@ MODULE_DEVICE_TABLE(of, static_replicator_match); · 362 unmodified lines | ||
| 363 | 363 | |
| 364 | 364 | #ifdef CONFIG_ACPI |
| 365 | 365 | static const struct acpi_device_id static_replicator_acpi_ids[] = { |
| 366 | − {"ARMHC985", 0}, /* ARM CoreSight Static Replicator */ | |
| 366 | + {"ARMHC985", 0, 0, 0}, /* ARM CoreSight Static Replicator */ | |
| 367 | 367 | {} |
| 368 | 368 | }; |
| 369 | 369 | |
| @@ -119,7 +119,6 @@ DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm"); · 118 unmodified lines | ||
| 119 | 119 | * @spinlock: only one at a time pls. |
| 120 | 120 | * @chs: the channels accociated to this STM. |
| 121 | 121 | * @stm: structure associated to the generic STM interface. |
| 122 | − * @mode: this tracer's mode (enum cs_mode), i.e sysFS, or disabled. | |
| 123 | 122 | * @traceid: value of the current ID for this component. |
| 124 | 123 | * @write_bytes: Maximus bytes this STM can write at a time. |
| 125 | 124 | * @stmsper: settings for register STMSPER. |
| @@ -136,7 +135,6 @@ struct stm_drvdata { · 10 unmodified lines | ||
| 136 | 135 | spinlock_t spinlock; |
| 137 | 136 | struct channel_space chs; |
| 138 | 137 | struct stm_data stm; |
| 139 | − local_t mode; | |
| 140 | 138 | u8 traceid; |
| 141 | 139 | u32 write_bytes; |
| 142 | 140 | u32 stmsper; |
| @@ -195,17 +193,15 @@ static void stm_enable_hw(struct stm_drvdata *drvdata) · 52 unmodified lines | ||
| 195 | 193 | static int stm_enable(struct coresight_device *csdev, struct perf_event *event, |
| 196 | 194 | enum cs_mode mode) |
| 197 | 195 | { |
| 198 | − u32 val; | |
| 199 | 196 | struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
| 200 | 197 | |
| 201 | 198 | if (mode != CS_MODE_SYSFS) |
| 202 | 199 | return -EINVAL; |
| 203 | 200 | |
| 204 | − val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); | |
| 205 | − | |
| 206 | − /* Someone is already using the tracer */ | |
| 207 | − if (val) | |
| 201 | + if (!coresight_take_mode(csdev, mode)) { | |
| 202 | + /* Someone is already using the tracer */ | |
| 208 | 203 | return -EBUSY; |
| 204 | + } | |
| 209 | 205 | |
| 210 | 206 | pm_runtime_get_sync(csdev->dev.parent); |
| 211 | 207 | |
| @@ -266,7 +262,7 @@ static void stm_disable(struct coresight_device *csdev, · 54 unmodified lines | ||
| 266 | 262 | * change its status. As such we can read the status here without |
| 267 | 263 | * fearing it will change under us. |
| 268 | 264 | */ |
| 269 | − if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { | |
| 265 | + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { | |
| 270 | 266 | spin_lock(&drvdata->spinlock); |
| 271 | 267 | stm_disable_hw(drvdata); |
| 272 | 268 | spin_unlock(&drvdata->spinlock); |
| @@ -276,7 +272,7 @@ static void stm_disable(struct coresight_device *csdev, · 3 unmodified lines | ||
| 276 | 272 | |
| 277 | 273 | pm_runtime_put(csdev->dev.parent); |
| 278 | 274 | |
| 279 | − local_set(&drvdata->mode, CS_MODE_DISABLED); | |
| 275 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 280 | 276 | dev_dbg(&csdev->dev, "STM tracing disabled\n"); |
| 281 | 277 | } |
| 282 | 278 | } |
| @@ -334,7 +330,7 @@ static int stm_generic_link(struct stm_data *stm_data, · 51 unmodified lines | ||
| 334 | 330 | if (!drvdata || !drvdata->csdev) |
| 335 | 331 | return -EINVAL; |
| 336 | 332 | |
| 337 | − return coresight_enable(drvdata->csdev); | |
| 333 | + return coresight_enable_sysfs(drvdata->csdev); | |
| 338 | 334 | } |
| 339 | 335 | |
| 340 | 336 | static void stm_generic_unlink(struct stm_data *stm_data, |
| @@ -345,7 +341,7 @@ static void stm_generic_unlink(struct stm_data *stm_data, · 4 unmodified lines | ||
| 345 | 341 | if (!drvdata || !drvdata->csdev) |
| 346 | 342 | return; |
| 347 | 343 | |
| 348 | − coresight_disable(drvdata->csdev); | |
| 344 | + coresight_disable_sysfs(drvdata->csdev); | |
| 349 | 345 | } |
| 350 | 346 | |
| 351 | 347 | static phys_addr_t |
| @@ -373,7 +369,7 @@ static long stm_generic_set_options(struct stm_data *stm_data, · 21 unmodified lines | ||
| 373 | 369 | { |
| 374 | 370 | struct stm_drvdata *drvdata = container_of(stm_data, |
| 375 | 371 | struct stm_drvdata, stm); |
| 376 | − if (!(drvdata && local_read(&drvdata->mode))) | |
| 372 | + if (!(drvdata && coresight_get_mode(drvdata->csdev))) | |
| 377 | 373 | return -EINVAL; |
| 378 | 374 | |
| 379 | 375 | if (channel >= drvdata->numsp) |
| @@ -408,7 +404,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, · 28 unmodified lines | ||
| 408 | 404 | struct stm_drvdata, stm); |
| 409 | 405 | unsigned int stm_flags; |
| 410 | 406 | |
| 411 | − if (!(drvdata && local_read(&drvdata->mode))) | |
| 407 | + if (!(drvdata && coresight_get_mode(drvdata->csdev))) | |
| 412 | 408 | return -EACCES; |
| 413 | 409 | |
| 414 | 410 | if (channel >= drvdata->numsp) |
| @@ -515,7 +511,7 @@ static ssize_t port_select_show(struct device *dev, · 100 unmodified lines | ||
| 515 | 511 | struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 516 | 512 | unsigned long val; |
| 517 | 513 | |
| 518 | − if (!local_read(&drvdata->mode)) { | |
| 514 | + if (!coresight_get_mode(drvdata->csdev)) { | |
| 519 | 515 | val = drvdata->stmspscr; |
| 520 | 516 | } else { |
| 521 | 517 | spin_lock(&drvdata->spinlock); |
| @@ -541,7 +537,7 @@ static ssize_t port_select_store(struct device *dev, · 19 unmodified lines | ||
| 541 | 537 | spin_lock(&drvdata->spinlock); |
| 542 | 538 | drvdata->stmspscr = val; |
| 543 | 539 | |
| 544 | − if (local_read(&drvdata->mode)) { | |
| 540 | + if (coresight_get_mode(drvdata->csdev)) { | |
| 545 | 541 | CS_UNLOCK(drvdata->base); |
| 546 | 542 | /* Process as per ARM's TRM recommendation */ |
| 547 | 543 | stmsper = readl_relaxed(drvdata->base + STMSPER); |
| @@ -562,7 +558,7 @@ static ssize_t port_enable_show(struct device *dev, · 14 unmodified lines | ||
| 562 | 558 | struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 563 | 559 | unsigned long val; |
| 564 | 560 | |
| 565 | − if (!local_read(&drvdata->mode)) { | |
| 561 | + if (!coresight_get_mode(drvdata->csdev)) { | |
| 566 | 562 | val = drvdata->stmsper; |
| 567 | 563 | } else { |
| 568 | 564 | spin_lock(&drvdata->spinlock); |
| @@ -588,7 +584,7 @@ static ssize_t port_enable_store(struct device *dev, · 19 unmodified lines | ||
| 588 | 584 | spin_lock(&drvdata->spinlock); |
| 589 | 585 | drvdata->stmsper = val; |
| 590 | 586 | |
| 591 | − if (local_read(&drvdata->mode)) { | |
| 587 | + if (coresight_get_mode(drvdata->csdev)) { | |
| 592 | 588 | CS_UNLOCK(drvdata->base); |
| 593 | 589 | writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER); |
| 594 | 590 | CS_LOCK(drvdata->base); |
| @@ -950,7 +946,7 @@ static const struct dev_pm_ops stm_dev_pm_ops = { · 355 unmodified lines | ||
| 950 | 946 | static const struct amba_id stm_ids[] = { |
| 951 | 947 | CS_AMBA_ID_DATA(0x000bb962, "STM32"), |
| 952 | 948 | CS_AMBA_ID_DATA(0x000bb963, "STM500"), |
| 953 | − { 0, 0}, | |
| 949 | + { 0, 0, NULL }, | |
| 954 | 950 | }; |
| 955 | 951 | |
| 956 | 952 | MODULE_DEVICE_TABLE(amba, stm_ids); |
| @@ -5,10 +5,401 @@ · 4 unmodified lines | ||
| 5 | 5 | */ |
| 6 | 6 | |
| 7 | 7 | #include <linux/device.h> |
| 8 | +#include <linux/idr.h> | |
| 8 | 9 | #include <linux/kernel.h> |
| 9 | 10 | |
| 10 | 11 | #include "coresight-priv.h" |
| 11 | 12 | |
| 13 | +/* | |
| 14 | + * Use IDR to map the hash of the source's device name | |
| 15 | + * to the pointer of path for the source. The idr is for | |
| 16 | + * the sources which aren't associated with CPU. | |
| 17 | + */ | |
| 18 | +static DEFINE_IDR(path_idr); | |
| 19 | + | |
| 20 | +/* | |
| 21 | + * When operating Coresight drivers from the sysFS interface, only a single | |
| 22 | + * path can exist from a tracer (associated to a CPU) to a sink. | |
| 23 | + */ | |
| 24 | +static DEFINE_PER_CPU(struct list_head *, tracer_path); | |
| 25 | + | |
| 26 | +ssize_t coresight_simple_show_pair(struct device *_dev, | |
| 27 | + struct device_attribute *attr, char *buf) | |
| 28 | +{ | |
| 29 | + struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); | |
| 30 | + struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr); | |
| 31 | + u64 val; | |
| 32 | + | |
| 33 | + pm_runtime_get_sync(_dev->parent); | |
| 34 | + val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off); | |
| 35 | + pm_runtime_put_sync(_dev->parent); | |
| 36 | + return sysfs_emit(buf, "0x%llx\n", val); | |
| 37 | +} | |
| 38 | +EXPORT_SYMBOL_GPL(coresight_simple_show_pair); | |
| 39 | + | |
| 40 | +ssize_t coresight_simple_show32(struct device *_dev, | |
| 41 | + struct device_attribute *attr, char *buf) | |
| 42 | +{ | |
| 43 | + struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev); | |
| 44 | + struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr); | |
| 45 | + u64 val; | |
| 46 | + | |
| 47 | + pm_runtime_get_sync(_dev->parent); | |
| 48 | + val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off); | |
| 49 | + pm_runtime_put_sync(_dev->parent); | |
| 50 | + return sysfs_emit(buf, "0x%llx\n", val); | |
| 51 | +} | |
| 52 | +EXPORT_SYMBOL_GPL(coresight_simple_show32); | |
| 53 | + | |
| 54 | +static int coresight_enable_source_sysfs(struct coresight_device *csdev, | |
| 55 | + enum cs_mode mode, void *data) | |
| 56 | +{ | |
| 57 | + int ret; | |
| 58 | + | |
| 59 | + /* | |
| 60 | + * Comparison with CS_MODE_SYSFS works without taking any device | |
| 61 | + * specific spinlock because the truthyness of that comparison can only | |
| 62 | + * change with coresight_mutex held, which we already have here. | |
| 63 | + */ | |
| 64 | + lockdep_assert_held(&coresight_mutex); | |
| 65 | + if (coresight_get_mode(csdev) != CS_MODE_SYSFS) { | |
| 66 | + ret = source_ops(csdev)->enable(csdev, data, mode); | |
| 67 | + if (ret) | |
| 68 | + return ret; | |
| 69 | + } | |
| 70 | + | |
| 71 | + csdev->refcnt++; | |
| 72 | + | |
| 73 | + return 0; | |
| 74 | +} | |
| 75 | + | |
| 76 | +/** | |
| 77 | + * coresight_disable_source_sysfs - Drop the reference count by 1 and disable | |
| 78 | + * the device if there are no users left. | |
| 79 | + * | |
| 80 | + * @csdev: The coresight device to disable | |
| 81 | + * @data: Opaque data to pass on to the disable function of the source device. | |
| 82 | + * For example in perf mode this is a pointer to the struct perf_event. | |
| 83 | + * | |
| 84 | + * Returns true if the device has been disabled. | |
| 85 | + */ | |
| 86 | +static bool coresight_disable_source_sysfs(struct coresight_device *csdev, | |
| 87 | + void *data) | |
| 88 | +{ | |
| 89 | + lockdep_assert_held(&coresight_mutex); | |
| 90 | + if (coresight_get_mode(csdev) != CS_MODE_SYSFS) | |
| 91 | + return false; | |
| 92 | + | |
| 93 | + csdev->refcnt--; | |
| 94 | + if (csdev->refcnt == 0) { | |
| 95 | + coresight_disable_source(csdev, data); | |
| 96 | + return true; | |
| 97 | + } | |
| 98 | + return false; | |
| 99 | +} | |
| 100 | + | |
| 101 | +/** | |
| 102 | + * coresight_find_activated_sysfs_sink - returns the first sink activated via | |
| 103 | + * sysfs using connection based search starting from the source reference. | |
| 104 | + * | |
| 105 | + * @csdev: Coresight source device reference | |
| 106 | + */ | |
| 107 | +static struct coresight_device * | |
| 108 | +coresight_find_activated_sysfs_sink(struct coresight_device *csdev) | |
| 109 | +{ | |
| 110 | + int i; | |
| 111 | + struct coresight_device *sink = NULL; | |
| 112 | + | |
| 113 | + if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || | |
| 114 | + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && | |
| 115 | + csdev->sysfs_sink_activated) | |
| 116 | + return csdev; | |
| 117 | + | |
| 118 | + /* | |
| 119 | + * Recursively explore each port found on this element. | |
| 120 | + */ | |
| 121 | + for (i = 0; i < csdev->pdata->nr_outconns; i++) { | |
| 122 | + struct coresight_device *child_dev; | |
| 123 | + | |
| 124 | + child_dev = csdev->pdata->out_conns[i]->dest_dev; | |
| 125 | + if (child_dev) | |
| 126 | + sink = coresight_find_activated_sysfs_sink(child_dev); | |
| 127 | + if (sink) | |
| 128 | + return sink; | |
| 129 | + } | |
| 130 | + | |
| 131 | + return NULL; | |
| 132 | +} | |
| 133 | + | |
| 134 | +/** coresight_validate_source - make sure a source has the right credentials to | |
| 135 | + * be used via sysfs. | |
| 136 | + * @csdev: the device structure for a source. | |
| 137 | + * @function: the function this was called from. | |
| 138 | + * | |
| 139 | + * Assumes the coresight_mutex is held. | |
| 140 | + */ | |
| 141 | +static int coresight_validate_source_sysfs(struct coresight_device *csdev, | |
| 142 | + const char *function) | |
| 143 | +{ | |
| 144 | + u32 type, subtype; | |
| 145 | + | |
| 146 | + type = csdev->type; | |
| 147 | + subtype = csdev->subtype.source_subtype; | |
| 148 | + | |
| 149 | + if (type != CORESIGHT_DEV_TYPE_SOURCE) { | |
| 150 | + dev_err(&csdev->dev, "wrong device type in %s\n", function); | |
| 151 | + return -EINVAL; | |
| 152 | + } | |
| 153 | + | |
| 154 | + if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && | |
| 155 | + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE && | |
| 156 | + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM && | |
| 157 | + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) { | |
| 158 | + dev_err(&csdev->dev, "wrong device subtype in %s\n", function); | |
| 159 | + return -EINVAL; | |
| 160 | + } | |
| 161 | + | |
| 162 | + return 0; | |
| 163 | +} | |
| 164 | + | |
| 165 | +int coresight_enable_sysfs(struct coresight_device *csdev) | |
| 166 | +{ | |
| 167 | + int cpu, ret = 0; | |
| 168 | + struct coresight_device *sink; | |
| 169 | + struct list_head *path; | |
| 170 | + enum coresight_dev_subtype_source subtype; | |
| 171 | + u32 hash; | |
| 172 | + | |
| 173 | + subtype = csdev->subtype.source_subtype; | |
| 174 | + | |
| 175 | + mutex_lock(&coresight_mutex); | |
| 176 | + | |
| 177 | + ret = coresight_validate_source_sysfs(csdev, __func__); | |
| 178 | + if (ret) | |
| 179 | + goto out; | |
| 180 | + | |
| 181 | + /* | |
| 182 | + * mode == SYSFS implies that it's already enabled. Don't look at the | |
| 183 | + * refcount to determine this because we don't claim the source until | |
| 184 | + * coresight_enable_source() so can still race with Perf mode which | |
| 185 | + * doesn't hold coresight_mutex. | |
| 186 | + */ | |
| 187 | + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { | |
| 188 | + /* | |
| 189 | + * There could be multiple applications driving the software | |
| 190 | + * source. So keep the refcount for each such user when the | |
| 191 | + * source is already enabled. | |
| 192 | + */ | |
| 193 | + if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) | |
| 194 | + csdev->refcnt++; | |
| 195 | + goto out; | |
| 196 | + } | |
| 197 | + | |
| 198 | + sink = coresight_find_activated_sysfs_sink(csdev); | |
| 199 | + if (!sink) { | |
| 200 | + ret = -EINVAL; | |
| 201 | + goto out; | |
| 202 | + } | |
| 203 | + | |
| 204 | + path = coresight_build_path(csdev, sink); | |
| 205 | + if (IS_ERR(path)) { | |
| 206 | + pr_err("building path(s) failed\n"); | |
| 207 | + ret = PTR_ERR(path); | |
| 208 | + goto out; | |
| 209 | + } | |
| 210 | + | |
| 211 | + ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); | |
| 212 | + if (ret) | |
| 213 | + goto err_path; | |
| 214 | + | |
| 215 | + ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, NULL); | |
| 216 | + if (ret) | |
| 217 | + goto err_source; | |
| 218 | + | |
| 219 | + switch (subtype) { | |
| 220 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: | |
| 221 | + /* | |
| 222 | + * When working from sysFS it is important to keep track | |
| 223 | + * of the paths that were created so that they can be | |
| 224 | + * undone in 'coresight_disable()'. Since there can only | |
| 225 | + * be a single session per tracer (when working from sysFS) | |
| 226 | + * a per-cpu variable will do just fine. | |
| 227 | + */ | |
| 228 | + cpu = source_ops(csdev)->cpu_id(csdev); | |
| 229 | + per_cpu(tracer_path, cpu) = path; | |
| 230 | + break; | |
| 231 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: | |
| 232 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: | |
| 233 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: | |
| 234 | + /* | |
| 235 | + * Use the hash of source's device name as ID | |
| 236 | + * and map the ID to the pointer of the path. | |
| 237 | + */ | |
| 238 | + hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); | |
| 239 | + ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); | |
| 240 | + if (ret) | |
| 241 | + goto err_source; | |
| 242 | + break; | |
| 243 | + default: | |
| 244 | + /* We can't be here */ | |
| 245 | + break; | |
| 246 | + } | |
| 247 | + | |
| 248 | +out: | |
| 249 | + mutex_unlock(&coresight_mutex); | |
| 250 | + return ret; | |
| 251 | + | |
| 252 | +err_source: | |
| 253 | + coresight_disable_path(path); | |
| 254 | + | |
| 255 | +err_path: | |
| 256 | + coresight_release_path(path); | |
| 257 | + goto out; | |
| 258 | +} | |
| 259 | +EXPORT_SYMBOL_GPL(coresight_enable_sysfs); | |
| 260 | + | |
| 261 | +void coresight_disable_sysfs(struct coresight_device *csdev) | |
| 262 | +{ | |
| 263 | + int cpu, ret; | |
| 264 | + struct list_head *path = NULL; | |
| 265 | + u32 hash; | |
| 266 | + | |
| 267 | + mutex_lock(&coresight_mutex); | |
| 268 | + | |
| 269 | + ret = coresight_validate_source_sysfs(csdev, __func__); | |
| 270 | + if (ret) | |
| 271 | + goto out; | |
| 272 | + | |
| 273 | + if (!coresight_disable_source_sysfs(csdev, NULL)) | |
| 274 | + goto out; | |
| 275 | + | |
| 276 | + switch (csdev->subtype.source_subtype) { | |
| 277 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: | |
| 278 | + cpu = source_ops(csdev)->cpu_id(csdev); | |
| 279 | + path = per_cpu(tracer_path, cpu); | |
| 280 | + per_cpu(tracer_path, cpu) = NULL; | |
| 281 | + break; | |
| 282 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: | |
| 283 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: | |
| 284 | + case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: | |
| 285 | + hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); | |
| 286 | + /* Find the path by the hash. */ | |
| 287 | + path = idr_find(&path_idr, hash); | |
| 288 | + if (path == NULL) { | |
| 289 | + pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); | |
| 290 | + goto out; | |
| 291 | + } | |
| 292 | + idr_remove(&path_idr, hash); | |
| 293 | + break; | |
| 294 | + default: | |
| 295 | + /* We can't be here */ | |
| 296 | + break; | |
| 297 | + } | |
| 298 | + | |
| 299 | + coresight_disable_path(path); | |
| 300 | + coresight_release_path(path); | |
| 301 | + | |
| 302 | +out: | |
| 303 | + mutex_unlock(&coresight_mutex); | |
| 304 | +} | |
| 305 | +EXPORT_SYMBOL_GPL(coresight_disable_sysfs); | |
| 306 | + | |
| 307 | +static ssize_t enable_sink_show(struct device *dev, | |
| 308 | + struct device_attribute *attr, char *buf) | |
| 309 | +{ | |
| 310 | + struct coresight_device *csdev = to_coresight_device(dev); | |
| 311 | + | |
| 312 | + return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated); | |
| 313 | +} | |
| 314 | + | |
| 315 | +static ssize_t enable_sink_store(struct device *dev, | |
| 316 | + struct device_attribute *attr, | |
| 317 | + const char *buf, size_t size) | |
| 318 | +{ | |
| 319 | + int ret; | |
| 320 | + unsigned long val; | |
| 321 | + struct coresight_device *csdev = to_coresight_device(dev); | |
| 322 | + | |
| 323 | + ret = kstrtoul(buf, 10, &val); | |
| 324 | + if (ret) | |
| 325 | + return ret; | |
| 326 | + | |
| 327 | + csdev->sysfs_sink_activated = !!val; | |
| 328 | + | |
| 329 | + return size; | |
| 330 | + | |
| 331 | +} | |
| 332 | +static DEVICE_ATTR_RW(enable_sink); | |
| 333 | + | |
| 334 | +static ssize_t enable_source_show(struct device *dev, | |
| 335 | + struct device_attribute *attr, char *buf) | |
| 336 | +{ | |
| 337 | + struct coresight_device *csdev = to_coresight_device(dev); | |
| 338 | + | |
| 339 | + guard(mutex)(&coresight_mutex); | |
| 340 | + return scnprintf(buf, PAGE_SIZE, "%u\n", | |
| 341 | + coresight_get_mode(csdev) == CS_MODE_SYSFS); | |
| 342 | +} | |
| 343 | + | |
| 344 | +static ssize_t enable_source_store(struct device *dev, | |
| 345 | + struct device_attribute *attr, | |
| 346 | + const char *buf, size_t size) | |
| 347 | +{ | |
| 348 | + int ret = 0; | |
| 349 | + unsigned long val; | |
| 350 | + struct coresight_device *csdev = to_coresight_device(dev); | |
| 351 | + | |
| 352 | + ret = kstrtoul(buf, 10, &val); | |
| 353 | + if (ret) | |
| 354 | + return ret; | |
| 355 | + | |
| 356 | + if (val) { | |
| 357 | + ret = coresight_enable_sysfs(csdev); | |
| 358 | + if (ret) | |
| 359 | + return ret; | |
| 360 | + } else { | |
| 361 | + coresight_disable_sysfs(csdev); | |
| 362 | + } | |
| 363 | + | |
| 364 | + return size; | |
| 365 | +} | |
| 366 | +static DEVICE_ATTR_RW(enable_source); | |
| 367 | + | |
| 368 | +static struct attribute *coresight_sink_attrs[] = { | |
| 369 | + &dev_attr_enable_sink.attr, | |
| 370 | + NULL, | |
| 371 | +}; | |
| 372 | +ATTRIBUTE_GROUPS(coresight_sink); | |
| 373 | + | |
| 374 | +static struct attribute *coresight_source_attrs[] = { | |
| 375 | + &dev_attr_enable_source.attr, | |
| 376 | + NULL, | |
| 377 | +}; | |
| 378 | +ATTRIBUTE_GROUPS(coresight_source); | |
| 379 | + | |
| 380 | +struct device_type coresight_dev_type[] = { | |
| 381 | + [CORESIGHT_DEV_TYPE_SINK] = { | |
| 382 | + .name = "sink", | |
| 383 | + .groups = coresight_sink_groups, | |
| 384 | + }, | |
| 385 | + [CORESIGHT_DEV_TYPE_LINK] = { | |
| 386 | + .name = "link", | |
| 387 | + }, | |
| 388 | + [CORESIGHT_DEV_TYPE_LINKSINK] = { | |
| 389 | + .name = "linksink", | |
| 390 | + .groups = coresight_sink_groups, | |
| 391 | + }, | |
| 392 | + [CORESIGHT_DEV_TYPE_SOURCE] = { | |
| 393 | + .name = "source", | |
| 394 | + .groups = coresight_source_groups, | |
| 395 | + }, | |
| 396 | + [CORESIGHT_DEV_TYPE_HELPER] = { | |
| 397 | + .name = "helper", | |
| 398 | + } | |
| 399 | +}; | |
| 400 | +/* Ensure the enum matches the names and groups */ | |
| 401 | +static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX); | |
| 402 | + | |
| 12 | 403 | /* |
| 13 | 404 | * Connections group - links attribute. |
| 14 | 405 | * Count of created links between coresight components in the group. |
| @@ -558,7 +558,7 @@ static void tmc_shutdown(struct amba_device *adev) · 557 unmodified lines | ||
| 558 | 558 | |
| 559 | 559 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 560 | 560 | |
| 561 | − if (drvdata->mode == CS_MODE_DISABLED) | |
| 561 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED) | |
| 562 | 562 | goto out; |
| 563 | 563 | |
| 564 | 564 | if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) |
| @@ -594,7 +594,7 @@ static const struct amba_id tmc_ids[] = { · 29 unmodified lines | ||
| 594 | 594 | CS_AMBA_ID(0x000bb9e9), |
| 595 | 595 | /* Coresight SoC 600 TMC-ETF */ |
| 596 | 596 | CS_AMBA_ID(0x000bb9ea), |
| 597 | − { 0, 0}, | |
| 597 | + { 0, 0, NULL }, | |
| 598 | 598 | }; |
| 599 | 599 | |
| 600 | 600 | MODULE_DEVICE_TABLE(amba, tmc_ids); |
| @@ -89,7 +89,7 @@ static void __tmc_etb_disable_hw(struct tmc_drvdata *drvdata) · 88 unmodified lines | ||
| 89 | 89 | * When operating in sysFS mode the content of the buffer needs to be |
| 90 | 90 | * read before the TMC is disabled. |
| 91 | 91 | */ |
| 92 | − if (drvdata->mode == CS_MODE_SYSFS) | |
| 92 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) | |
| 93 | 93 | tmc_etb_dump_hw(drvdata); |
| 94 | 94 | tmc_disable_hw(drvdata); |
| 95 | 95 | |
| @@ -205,8 +205,8 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) · 109 unmodified lines | ||
| 205 | 205 | * sink is already enabled no memory is needed and the HW need not be |
| 206 | 206 | * touched. |
| 207 | 207 | */ |
| 208 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 209 | − atomic_inc(&csdev->refcnt); | |
| 208 | + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { | |
| 209 | + csdev->refcnt++; | |
| 210 | 210 | goto out; |
| 211 | 211 | } |
| 212 | 212 | |
| @@ -228,8 +228,8 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) · 15 unmodified lines | ||
| 228 | 228 | |
| 229 | 229 | ret = tmc_etb_enable_hw(drvdata); |
| 230 | 230 | if (!ret) { |
| 231 | − drvdata->mode = CS_MODE_SYSFS; | |
| 232 | − atomic_inc(&csdev->refcnt); | |
| 231 | + coresight_set_mode(csdev, CS_MODE_SYSFS); | |
| 232 | + csdev->refcnt++; | |
| 233 | 233 | } else { |
| 234 | 234 | /* Free up the buffer if we failed to enable */ |
| 235 | 235 | used = false; |
| @@ -262,7 +262,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) · 26 unmodified lines | ||
| 262 | 262 | * No need to continue if the ETB/ETF is already operated |
| 263 | 263 | * from sysFS. |
| 264 | 264 | */ |
| 265 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 265 | + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { | |
| 266 | 266 | ret = -EBUSY; |
| 267 | 267 | break; |
| 268 | 268 | } |
| @@ -284,7 +284,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) · 15 unmodified lines | ||
| 284 | 284 | * use for this session. |
| 285 | 285 | */ |
| 286 | 286 | if (drvdata->pid == pid) { |
| 287 | − atomic_inc(&csdev->refcnt); | |
| 287 | + csdev->refcnt++; | |
| 288 | 288 | break; |
| 289 | 289 | } |
| 290 | 290 | |
| @@ -292,8 +292,8 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) · 1 unmodified line | ||
| 292 | 292 | if (!ret) { |
| 293 | 293 | /* Associate with monitored process. */ |
| 294 | 294 | drvdata->pid = pid; |
| 295 | − drvdata->mode = CS_MODE_PERF; | |
| 296 | − atomic_inc(&csdev->refcnt); | |
| 295 | + coresight_set_mode(csdev, CS_MODE_PERF); | |
| 296 | + csdev->refcnt++; | |
| 297 | 297 | } |
| 298 | 298 | } while (0); |
| 299 | 299 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| @@ -338,17 +338,18 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev) · 38 unmodified lines | ||
| 338 | 338 | return -EBUSY; |
| 339 | 339 | } |
| 340 | 340 | |
| 341 | − if (atomic_dec_return(&csdev->refcnt)) { | |
| 341 | + csdev->refcnt--; | |
| 342 | + if (csdev->refcnt) { | |
| 342 | 343 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 343 | 344 | return -EBUSY; |
| 344 | 345 | } |
| 345 | 346 | |
| 346 | 347 | /* Complain if we (somehow) got out of sync */ |
| 347 | − WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); | |
| 348 | + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); | |
| 348 | 349 | tmc_etb_disable_hw(drvdata); |
| 349 | 350 | /* Dissociate from monitored process. */ |
| 350 | 351 | drvdata->pid = -1; |
| 351 | − drvdata->mode = CS_MODE_DISABLED; | |
| 352 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 352 | 353 | |
| 353 | 354 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 354 | 355 | |
| @@ -371,15 +372,15 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, · 16 unmodified lines | ||
| 371 | 372 | return -EBUSY; |
| 372 | 373 | } |
| 373 | 374 | |
| 374 | − if (atomic_read(&csdev->refcnt) == 0) { | |
| 375 | + if (csdev->refcnt == 0) { | |
| 375 | 376 | ret = tmc_etf_enable_hw(drvdata); |
| 376 | 377 | if (!ret) { |
| 377 | − drvdata->mode = CS_MODE_SYSFS; | |
| 378 | + coresight_set_mode(csdev, CS_MODE_SYSFS); | |
| 378 | 379 | first_enable = true; |
| 379 | 380 | } |
| 380 | 381 | } |
| 381 | 382 | if (!ret) |
| 382 | − atomic_inc(&csdev->refcnt); | |
| 383 | + csdev->refcnt++; | |
| 383 | 384 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 384 | 385 | |
| 385 | 386 | if (first_enable) |
| @@ -401,9 +402,10 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, · 15 unmodified lines | ||
| 401 | 402 | return; |
| 402 | 403 | } |
| 403 | 404 | |
| 404 | − if (atomic_dec_return(&csdev->refcnt) == 0) { | |
| 405 | + csdev->refcnt--; | |
| 406 | + if (csdev->refcnt == 0) { | |
| 405 | 407 | tmc_etf_disable_hw(drvdata); |
| 406 | − drvdata->mode = CS_MODE_DISABLED; | |
| 408 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 407 | 409 | last_disable = true; |
| 408 | 410 | } |
| 409 | 411 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| @@ -483,13 +485,13 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, · 73 unmodified lines | ||
| 483 | 485 | return 0; |
| 484 | 486 | |
| 485 | 487 | /* This shouldn't happen */ |
| 486 | − if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF)) | |
| 488 | + if (WARN_ON_ONCE(coresight_get_mode(csdev) != CS_MODE_PERF)) | |
| 487 | 489 | return 0; |
| 488 | 490 | |
| 489 | 491 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 490 | 492 | |
| 491 | 493 | /* Don't do anything if another tracer is using this sink */ |
| 492 | − if (atomic_read(&csdev->refcnt) != 1) | |
| 494 | + if (csdev->refcnt != 1) | |
| 493 | 495 | goto out; |
| 494 | 496 | |
| 495 | 497 | CS_UNLOCK(drvdata->base); |
| @@ -629,7 +631,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) · 133 unmodified lines | ||
| 629 | 631 | } |
| 630 | 632 | |
| 631 | 633 | /* Don't interfere if operated from Perf */ |
| 632 | − if (drvdata->mode == CS_MODE_PERF) { | |
| 634 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_PERF) { | |
| 633 | 635 | ret = -EINVAL; |
| 634 | 636 | goto out; |
| 635 | 637 | } |
| @@ -641,7 +643,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) · 5 unmodified lines | ||
| 641 | 643 | } |
| 642 | 644 | |
| 643 | 645 | /* Disable the TMC if need be */ |
| 644 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 646 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { | |
| 645 | 647 | /* There is no point in reading a TMC in HW FIFO mode */ |
| 646 | 648 | mode = readl_relaxed(drvdata->base + TMC_MODE); |
| 647 | 649 | if (mode != TMC_MODE_CIRCULAR_BUFFER) { |
| @@ -673,7 +675,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) · 25 unmodified lines | ||
| 673 | 675 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 674 | 676 | |
| 675 | 677 | /* Re-enable the TMC if need be */ |
| 676 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 678 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { | |
| 677 | 679 | /* There is no point in reading a TMC in HW FIFO mode */ |
| 678 | 680 | mode = readl_relaxed(drvdata->base + TMC_MODE); |
| 679 | 681 | if (mode != TMC_MODE_CIRCULAR_BUFFER) { |
| @@ -1143,7 +1143,7 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata) · 1142 unmodified lines | ||
| 1143 | 1143 | * When operating in sysFS mode the content of the buffer needs to be |
| 1144 | 1144 | * read before the TMC is disabled. |
| 1145 | 1145 | */ |
| 1146 | − if (drvdata->mode == CS_MODE_SYSFS) | |
| 1146 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) | |
| 1147 | 1147 | tmc_etr_sync_sysfs_buf(drvdata); |
| 1148 | 1148 | |
| 1149 | 1149 | tmc_disable_hw(drvdata); |
| @@ -1189,7 +1189,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev) · 39 unmodified lines | ||
| 1189 | 1189 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 1190 | 1190 | } |
| 1191 | 1191 | |
| 1192 | − if (drvdata->reading || drvdata->mode == CS_MODE_PERF) { | |
| 1192 | + if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) { | |
| 1193 | 1193 | ret = -EBUSY; |
| 1194 | 1194 | goto out; |
| 1195 | 1195 | } |
| @@ -1230,15 +1230,15 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) · 34 unmodified lines | ||
| 1230 | 1230 | * sink is already enabled no memory is needed and the HW need not be |
| 1231 | 1231 | * touched, even if the buffer size has changed. |
| 1232 | 1232 | */ |
| 1233 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 1234 | − atomic_inc(&csdev->refcnt); | |
| 1233 | + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { | |
| 1234 | + csdev->refcnt++; | |
| 1235 | 1235 | goto out; |
| 1236 | 1236 | } |
| 1237 | 1237 | |
| 1238 | 1238 | ret = tmc_etr_enable_hw(drvdata, sysfs_buf); |
| 1239 | 1239 | if (!ret) { |
| 1240 | − drvdata->mode = CS_MODE_SYSFS; | |
| 1241 | − atomic_inc(&csdev->refcnt); | |
| 1240 | + coresight_set_mode(csdev, CS_MODE_SYSFS); | |
| 1241 | + csdev->refcnt++; | |
| 1242 | 1242 | } |
| 1243 | 1243 | |
| 1244 | 1244 | out: |
| @@ -1564,7 +1564,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev, · 319 unmodified lines | ||
| 1564 | 1564 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 1565 | 1565 | |
| 1566 | 1566 | /* Don't do anything if another tracer is using this sink */ |
| 1567 | − if (atomic_read(&csdev->refcnt) != 1) { | |
| 1567 | + if (csdev->refcnt != 1) { | |
| 1568 | 1568 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 1569 | 1569 | goto out; |
| 1570 | 1570 | } |
| @@ -1652,7 +1652,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) · 81 unmodified lines | ||
| 1652 | 1652 | |
| 1653 | 1653 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 1654 | 1654 | /* Don't use this sink if it is already claimed by sysFS */ |
| 1655 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 1655 | + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { | |
| 1656 | 1656 | rc = -EBUSY; |
| 1657 | 1657 | goto unlock_out; |
| 1658 | 1658 | } |
| @@ -1676,7 +1676,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) · 17 unmodified lines | ||
| 1676 | 1676 | * use for this session. |
| 1677 | 1677 | */ |
| 1678 | 1678 | if (drvdata->pid == pid) { |
| 1679 | − atomic_inc(&csdev->refcnt); | |
| 1679 | + csdev->refcnt++; | |
| 1680 | 1680 | goto unlock_out; |
| 1681 | 1681 | } |
| 1682 | 1682 | |
| @@ -1684,9 +1684,9 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) · 1 unmodified line | ||
| 1684 | 1684 | if (!rc) { |
| 1685 | 1685 | /* Associate with monitored process. */ |
| 1686 | 1686 | drvdata->pid = pid; |
| 1687 | − drvdata->mode = CS_MODE_PERF; | |
| 1687 | + coresight_set_mode(csdev, CS_MODE_PERF); | |
| 1688 | 1688 | drvdata->perf_buf = etr_perf->etr_buf; |
| 1689 | − atomic_inc(&csdev->refcnt); | |
| 1689 | + csdev->refcnt++; | |
| 1690 | 1690 | } |
| 1691 | 1691 | |
| 1692 | 1692 | unlock_out: |
| @@ -1719,17 +1719,18 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) · 26 unmodified lines | ||
| 1719 | 1719 | return -EBUSY; |
| 1720 | 1720 | } |
| 1721 | 1721 | |
| 1722 | − if (atomic_dec_return(&csdev->refcnt)) { | |
| 1722 | + csdev->refcnt--; | |
| 1723 | + if (csdev->refcnt) { | |
| 1723 | 1724 | spin_unlock_irqrestore(&drvdata->spinlock, flags); |
| 1724 | 1725 | return -EBUSY; |
| 1725 | 1726 | } |
| 1726 | 1727 | |
| 1727 | 1728 | /* Complain if we (somehow) got out of sync */ |
| 1728 | − WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); | |
| 1729 | + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); | |
| 1729 | 1730 | tmc_etr_disable_hw(drvdata); |
| 1730 | 1731 | /* Dissociate from monitored process. */ |
| 1731 | 1732 | drvdata->pid = -1; |
| 1732 | − drvdata->mode = CS_MODE_DISABLED; | |
| 1733 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 1733 | 1734 | /* Reset perf specific data */ |
| 1734 | 1735 | drvdata->perf_buf = NULL; |
| 1735 | 1736 | |
| @@ -1777,7 +1778,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) · 41 unmodified lines | ||
| 1777 | 1778 | } |
| 1778 | 1779 | |
| 1779 | 1780 | /* Disable the TMC if we are trying to read from a running session. */ |
| 1780 | − if (drvdata->mode == CS_MODE_SYSFS) | |
| 1781 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) | |
| 1781 | 1782 | __tmc_etr_disable_hw(drvdata); |
| 1782 | 1783 | |
| 1783 | 1784 | drvdata->reading = true; |
| @@ -1799,7 +1800,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) · 15 unmodified lines | ||
| 1799 | 1800 | spin_lock_irqsave(&drvdata->spinlock, flags); |
| 1800 | 1801 | |
| 1801 | 1802 | /* RE-enable the TMC if need be */ |
| 1802 | − if (drvdata->mode == CS_MODE_SYSFS) { | |
| 1803 | + if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) { | |
| 1803 | 1804 | /* |
| 1804 | 1805 | * The trace run will continue with the same allocated trace |
| 1805 | 1806 | * buffer. Since the tracer is still enabled drvdata::buf can't |
| @@ -178,7 +178,6 @@ struct etr_buf { · 177 unmodified lines | ||
| 178 | 178 | * @size: trace buffer size for this TMC (common for all modes). |
| 179 | 179 | * @max_burst_size: The maximum burst size that can be initiated by |
| 180 | 180 | * TMC-ETR on AXI bus. |
| 181 | − * @mode: how this TMC is being used. | |
| 182 | 181 | * @config_type: TMC variant, must be of type @tmc_config_type. |
| 183 | 182 | * @memwidth: width of the memory interface databus, in bytes. |
| 184 | 183 | * @trigger_cntr: amount of words to store after a trigger. |
| @@ -203,7 +202,6 @@ struct tmc_drvdata { · 18 unmodified lines | ||
| 203 | 202 | u32 len; |
| 204 | 203 | u32 size; |
| 205 | 204 | u32 max_burst_size; |
| 206 | − u32 mode; | |
| 207 | 205 | enum tmc_config_type config_type; |
| 208 | 206 | enum tmc_mem_intf_width memwidth; |
| 209 | 207 | u32 trigger_cntr; |
| @@ -18,6 +18,7 @@ · 17 unmodified lines | ||
| 18 | 18 | #include "coresight-priv.h" |
| 19 | 19 | #include "coresight-tpda.h" |
| 20 | 20 | #include "coresight-trace-id.h" |
| 21 | +#include "coresight-tpdm.h" | |
| 21 | 22 | |
| 22 | 23 | DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); |
| 23 | 24 | |
| @@ -28,24 +29,59 @@ static bool coresight_device_is_tpdm(struct coresight_device *csdev) · 4 unmodified lines | ||
| 28 | 29 | CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM); |
| 29 | 30 | } |
| 30 | 31 | |
| 32 | +static void tpda_clear_element_size(struct coresight_device *csdev) | |
| 33 | +{ | |
| 34 | + struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
| 35 | + | |
| 36 | + drvdata->dsb_esize = 0; | |
| 37 | + drvdata->cmb_esize = 0; | |
| 38 | +} | |
| 39 | + | |
| 40 | +static void tpda_set_element_size(struct tpda_drvdata *drvdata, u32 *val) | |
| 41 | +{ | |
| 42 | + /* Clear all relevant fields */ | |
| 43 | + *val &= ~(TPDA_Pn_CR_DSBSIZE | TPDA_Pn_CR_CMBSIZE); | |
| 44 | + | |
| 45 | + if (drvdata->dsb_esize == 64) | |
| 46 | + *val |= TPDA_Pn_CR_DSBSIZE; | |
| 47 | + else if (drvdata->dsb_esize == 32) | |
| 48 | + *val &= ~TPDA_Pn_CR_DSBSIZE; | |
| 49 | + | |
| 50 | + if (drvdata->cmb_esize == 64) | |
| 51 | + *val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x2); | |
| 52 | + else if (drvdata->cmb_esize == 32) | |
| 53 | + *val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x1); | |
| 54 | + else if (drvdata->cmb_esize == 8) | |
| 55 | + *val &= ~TPDA_Pn_CR_CMBSIZE; | |
| 56 | +} | |
| 57 | + | |
| 31 | 58 | /* |
| 32 | − * Read the DSB element size from the TPDM device | |
| 59 | + * Read the element size from the TPDM device. One TPDM must have at least one of the | |
| 60 | + * element size property. | |
| 33 | 61 | * Returns |
| 34 | − * The dsb element size read from the devicetree if available. | |
| 35 | − * 0 - Otherwise, with a warning once. | |
| 62 | + * 0 - The element size property is read | |
| 63 | + * Others - Cannot read the property of the element size | |
| 36 | 64 | */ |
| 37 | −static int tpdm_read_dsb_element_size(struct coresight_device *csdev) | |
| 65 | +static int tpdm_read_element_size(struct tpda_drvdata *drvdata, | |
| 66 | + struct coresight_device *csdev) | |
| 38 | 67 | { |
| 39 | − int rc = 0; | |
| 40 | − u8 size = 0; | |
| 68 | + int rc = -EINVAL; | |
| 69 | + struct tpdm_drvdata *tpdm_data = dev_get_drvdata(csdev->dev.parent); | |
| 70 | + | |
| 71 | + if (tpdm_has_dsb_dataset(tpdm_data)) { | |
| 72 | + rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent), | |
| 73 | + "qcom,dsb-element-bits", &drvdata->dsb_esize); | |
| 74 | + } | |
| 75 | + if (tpdm_has_cmb_dataset(tpdm_data)) { | |
| 76 | + rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent), | |
| 77 | + "qcom,cmb-element-bits", &drvdata->cmb_esize); | |
| 78 | + } | |
| 41 | 79 | |
| 42 | − rc = fwnode_property_read_u8(dev_fwnode(csdev->dev.parent), | |
| 43 | − "qcom,dsb-element-size", &size); | |
| 44 | 80 | if (rc) |
| 45 | 81 | dev_warn_once(&csdev->dev, |
| 46 | − "Failed to read TPDM DSB Element size: %d\n", rc); | |
| 82 | + "Failed to read TPDM Element size: %d\n", rc); | |
| 47 | 83 | |
| 48 | − return size; | |
| 84 | + return rc; | |
| 49 | 85 | } |
| 50 | 86 | |
| 51 | 87 | /* |
| @@ -56,11 +92,12 @@ static int tpdm_read_dsb_element_size(struct coresight_device *csdev) · 4 unmodified lines | ||
| 56 | 92 | * Parameter "inport" is used to pass in the input port number |
| 57 | 93 | * of TPDA, and it is set to -1 in the recursize call. |
| 58 | 94 | */ |
| 59 | −static int tpda_get_element_size(struct coresight_device *csdev, | |
| 95 | +static int tpda_get_element_size(struct tpda_drvdata *drvdata, | |
| 96 | + struct coresight_device *csdev, | |
| 60 | 97 | int inport) |
| 61 | 98 | { |
| 62 | − int dsb_size = -ENOENT; | |
| 63 | − int i, size; | |
| 99 | + int rc = 0; | |
| 100 | + int i; | |
| 64 | 101 | struct coresight_device *in; |
| 65 | 102 | |
| 66 | 103 | for (i = 0; i < csdev->pdata->nr_inconns; i++) { |
| @@ -69,30 +106,26 @@ static int tpda_get_element_size(struct coresight_device *csdev, · 2 unmodified lines | ||
| 69 | 106 | continue; |
| 70 | 107 | |
| 71 | 108 | /* Ignore the paths that do not match port */ |
| 72 | − if (inport > 0 && | |
| 109 | + if (inport >= 0 && | |
| 73 | 110 | csdev->pdata->in_conns[i]->dest_port != inport) |
| 74 | 111 | continue; |
| 75 | 112 | |
| 76 | 113 | if (coresight_device_is_tpdm(in)) { |
| 77 | − size = tpdm_read_dsb_element_size(in); | |
| 114 | + if (drvdata->dsb_esize || drvdata->cmb_esize) | |
| 115 | + return -EEXIST; | |
| 116 | + rc = tpdm_read_element_size(drvdata, in); | |
| 117 | + if (rc) | |
| 118 | + return rc; | |
| 78 | 119 | } else { |
| 79 | 120 | /* Recurse down the path */ |
| 80 | − size = tpda_get_element_size(in, -1); | |
| 81 | − } | |
| 82 | − | |
| 83 | − if (size < 0) | |
| 84 | − return size; | |
| 85 | − | |
| 86 | − if (dsb_size < 0) { | |
| 87 | − /* Found a size, save it. */ | |
| 88 | − dsb_size = size; | |
| 89 | − } else { | |
| 90 | − /* Found duplicate TPDMs */ | |
| 91 | − return -EEXIST; | |
| 121 | + rc = tpda_get_element_size(drvdata, in, -1); | |
| 122 | + if (rc) | |
| 123 | + return rc; | |
| 92 | 124 | } |
| 93 | 125 | } |
| 94 | 126 | |
| 95 | − return dsb_size; | |
| 127 | + | |
| 128 | + return rc; | |
| 96 | 129 | } |
| 97 | 130 | |
| 98 | 131 | /* Settings pre enabling port control register */ |
| @@ -109,37 +142,24 @@ static void tpda_enable_pre_port(struct tpda_drvdata *drvdata) · 10 unmodified lines | ||
| 109 | 142 | static int tpda_enable_port(struct tpda_drvdata *drvdata, int port) |
| 110 | 143 | { |
| 111 | 144 | u32 val; |
| 112 | − int size; | |
| 145 | + int rc; | |
| 113 | 146 | |
| 114 | 147 | val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port)); |
| 115 | − /* | |
| 116 | − * Configure aggregator port n DSB data set element size | |
| 117 | − * Set the bit to 0 if the size is 32 | |
| 118 | − * Set the bit to 1 if the size is 64 | |
| 119 | − */ | |
| 120 | − size = tpda_get_element_size(drvdata->csdev, port); | |
| 121 | − switch (size) { | |
| 122 | − case 32: | |
| 123 | − val &= ~TPDA_Pn_CR_DSBSIZE; | |
| 124 | − break; | |
| 125 | − case 64: | |
| 126 | − val |= TPDA_Pn_CR_DSBSIZE; | |
| 127 | − break; | |
| 128 | − case 0: | |
| 129 | − return -EEXIST; | |
| 130 | − case -EEXIST: | |
| 148 | + tpda_clear_element_size(drvdata->csdev); | |
| 149 | + rc = tpda_get_element_size(drvdata, drvdata->csdev, port); | |
| 150 | + if (!rc && (drvdata->dsb_esize || drvdata->cmb_esize)) { | |
| 151 | + tpda_set_element_size(drvdata, &val); | |
| 152 | + /* Enable the port */ | |
| 153 | + val |= TPDA_Pn_CR_ENA; | |
| 154 | + writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port)); | |
| 155 | + } else if (rc == -EEXIST) | |
| 131 | 156 | dev_warn_once(&drvdata->csdev->dev, |
| 132 | − "Detected multiple TPDMs on port %d", -EEXIST); | |
| 133 | − return -EEXIST; | |
| 134 | − default: | |
| 135 | − return -EINVAL; | |
| 136 | − } | |
| 137 | − | |
| 138 | − /* Enable the port */ | |
| 139 | − val |= TPDA_Pn_CR_ENA; | |
| 140 | − writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port)); | |
| 157 | + "Detected multiple TPDMs on port %d", port); | |
| 158 | + else | |
| 159 | + dev_warn_once(&drvdata->csdev->dev, | |
| 160 | + "Didn't find TPDM element size"); | |
| 141 | 161 | |
| 142 | − return 0; | |
| 162 | + return rc; | |
| 143 | 163 | } |
| 144 | 164 | |
| 145 | 165 | static int __tpda_enable(struct tpda_drvdata *drvdata, int port) |
| @@ -148,7 +168,12 @@ static int __tpda_enable(struct tpda_drvdata *drvdata, int port) · 2 unmodified lines | ||
| 148 | 168 | |
| 149 | 169 | CS_UNLOCK(drvdata->base); |
| 150 | 170 | |
| 151 | − if (!drvdata->csdev->enable) | |
| 171 | + /* | |
| 172 | + * Only do pre-port enable for first port that calls enable when the | |
| 173 | + * device's main refcount is still 0 | |
| 174 | + */ | |
| 175 | + lockdep_assert_held(&drvdata->spinlock); | |
| 176 | + if (!drvdata->csdev->refcnt) | |
| 152 | 177 | tpda_enable_pre_port(drvdata); |
| 153 | 178 | |
| 154 | 179 | ret = tpda_enable_port(drvdata, port); |
| @@ -169,6 +194,7 @@ static int tpda_enable(struct coresight_device *csdev, · 14 unmodified lines | ||
| 169 | 194 | ret = __tpda_enable(drvdata, in->dest_port); |
| 170 | 195 | if (!ret) { |
| 171 | 196 | atomic_inc(&in->dest_refcnt); |
| 197 | + csdev->refcnt++; | |
| 172 | 198 | dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); |
| 173 | 199 | } |
| 174 | 200 | } |
| @@ -197,9 +223,10 @@ static void tpda_disable(struct coresight_device *csdev, · 22 unmodified lines | ||
| 197 | 223 | struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
| 198 | 224 | |
| 199 | 225 | spin_lock(&drvdata->spinlock); |
| 200 | − if (atomic_dec_return(&in->dest_refcnt) == 0) | |
| 226 | + if (atomic_dec_return(&in->dest_refcnt) == 0) { | |
| 201 | 227 | __tpda_disable(drvdata, in->dest_port); |
| 202 | − | |
| 228 | + csdev->refcnt--; | |
| 229 | + } | |
| 203 | 230 | spin_unlock(&drvdata->spinlock); |
| 204 | 231 | |
| 205 | 232 | dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port); |
| @@ -300,7 +327,7 @@ static struct amba_id tpda_ids[] = { · 94 unmodified lines | ||
| 300 | 327 | .id = 0x000f0f00, |
| 301 | 328 | .mask = 0x000fff00, |
| 302 | 329 | }, |
| 303 | − { 0, 0}, | |
| 330 | + { 0, 0, NULL }, | |
| 304 | 331 | }; |
| 305 | 332 | |
| 306 | 333 | static struct amba_driver tpda_driver = { |
| @@ -10,6 +10,8 @@ · 9 unmodified lines | ||
| 10 | 10 | #define TPDA_Pn_CR(n) (0x004 + (n * 4)) |
| 11 | 11 | /* Aggregator port enable bit */ |
| 12 | 12 | #define TPDA_Pn_CR_ENA BIT(0) |
| 13 | +/* Aggregator port CMB data set element size bit */ | |
| 14 | +#define TPDA_Pn_CR_CMBSIZE GENMASK(7, 6) | |
| 13 | 15 | /* Aggregator port DSB data set element size bit */ |
| 14 | 16 | #define TPDA_Pn_CR_DSBSIZE BIT(8) |
| 15 | 17 | |
| @@ -25,6 +27,8 @@ · 9 unmodified lines | ||
| 25 | 27 | * @csdev: component vitals needed by the framework. |
| 26 | 28 | * @spinlock: lock for the drvdata value. |
| 27 | 29 | * @enable: enable status of the component. |
| 30 | + * @dsb_esize Record the DSB element size. | |
| 31 | + * @cmb_esize Record the CMB element size. | |
| 28 | 32 | */ |
| 29 | 33 | struct tpda_drvdata { |
| 30 | 34 | void __iomem *base; |
| @@ -32,6 +36,8 @@ struct tpda_drvdata { · 1 unmodified line | ||
| 32 | 36 | struct coresight_device *csdev; |
| 33 | 37 | spinlock_t spinlock; |
| 34 | 38 | u8 atid; |
| 39 | + u32 dsb_esize; | |
| 40 | + u32 cmb_esize; | |
| 35 | 41 | }; |
| 36 | 42 | |
| 37 | 43 | #endif /* _CORESIGHT_CORESIGHT_TPDA_H */ |
| @@ -66,6 +66,31 @@ static ssize_t tpdm_simple_dataset_show(struct device *dev, · 65 unmodified lines | ||
| 66 | 66 | return -EINVAL; |
| 67 | 67 | return sysfs_emit(buf, "0x%x\n", |
| 68 | 68 | drvdata->dsb->msr[tpdm_attr->idx]); |
| 69 | + case CMB_TRIG_PATT: | |
| 70 | + if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT) | |
| 71 | + return -EINVAL; | |
| 72 | + return sysfs_emit(buf, "0x%x\n", | |
| 73 | + drvdata->cmb->trig_patt[tpdm_attr->idx]); | |
| 74 | + case CMB_TRIG_PATT_MASK: | |
| 75 | + if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT) | |
| 76 | + return -EINVAL; | |
| 77 | + return sysfs_emit(buf, "0x%x\n", | |
| 78 | + drvdata->cmb->trig_patt_mask[tpdm_attr->idx]); | |
| 79 | + case CMB_PATT: | |
| 80 | + if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT) | |
| 81 | + return -EINVAL; | |
| 82 | + return sysfs_emit(buf, "0x%x\n", | |
| 83 | + drvdata->cmb->patt_val[tpdm_attr->idx]); | |
| 84 | + case CMB_PATT_MASK: | |
| 85 | + if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT) | |
| 86 | + return -EINVAL; | |
| 87 | + return sysfs_emit(buf, "0x%x\n", | |
| 88 | + drvdata->cmb->patt_mask[tpdm_attr->idx]); | |
| 89 | + case CMB_MSR: | |
| 90 | + if (tpdm_attr->idx >= drvdata->cmb_msr_num) | |
| 91 | + return -EINVAL; | |
| 92 | + return sysfs_emit(buf, "0x%x\n", | |
| 93 | + drvdata->cmb->msr[tpdm_attr->idx]); | |
| 69 | 94 | } |
| 70 | 95 | return -EINVAL; |
| 71 | 96 | } |
| @@ -77,67 +102,103 @@ static ssize_t tpdm_simple_dataset_store(struct device *dev, · 5 unmodified lines | ||
| 77 | 102 | size_t size) |
| 78 | 103 | { |
| 79 | 104 | unsigned long val; |
| 80 | − ssize_t ret = size; | |
| 105 | + ssize_t ret = -EINVAL; | |
| 81 | 106 | |
| 82 | 107 | struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 83 | 108 | struct tpdm_dataset_attribute *tpdm_attr = |
| 84 | 109 | container_of(attr, struct tpdm_dataset_attribute, attr); |
| 85 | 110 | |
| 86 | 111 | if (kstrtoul(buf, 0, &val)) |
| 87 | − return -EINVAL; | |
| 112 | + return ret; | |
| 88 | 113 | |
| 89 | − spin_lock(&drvdata->spinlock); | |
| 114 | + guard(spinlock)(&drvdata->spinlock); | |
| 90 | 115 | switch (tpdm_attr->mem) { |
| 91 | 116 | case DSB_TRIG_PATT: |
| 92 | − if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) | |
| 117 | + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) { | |
| 93 | 118 | drvdata->dsb->trig_patt[tpdm_attr->idx] = val; |
| 94 | − else | |
| 95 | − ret = -EINVAL; | |
| 119 | + ret = size; | |
| 120 | + } | |
| 96 | 121 | break; |
| 97 | 122 | case DSB_TRIG_PATT_MASK: |
| 98 | − if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) | |
| 123 | + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) { | |
| 99 | 124 | drvdata->dsb->trig_patt_mask[tpdm_attr->idx] = val; |
| 100 | − else | |
| 101 | − ret = -EINVAL; | |
| 125 | + ret = size; | |
| 126 | + } | |
| 102 | 127 | break; |
| 103 | 128 | case DSB_PATT: |
| 104 | − if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) | |
| 129 | + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) { | |
| 105 | 130 | drvdata->dsb->patt_val[tpdm_attr->idx] = val; |
| 106 | − else | |
| 107 | − ret = -EINVAL; | |
| 131 | + ret = size; | |
| 132 | + } | |
| 108 | 133 | break; |
| 109 | 134 | case DSB_PATT_MASK: |
| 110 | − if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) | |
| 135 | + if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) { | |
| 111 | 136 | drvdata->dsb->patt_mask[tpdm_attr->idx] = val; |
| 112 | − else | |
| 113 | − ret = -EINVAL; | |
| 137 | + ret = size; | |
| 138 | + } | |
| 114 | 139 | break; |
| 115 | 140 | case DSB_MSR: |
| 116 | − if (tpdm_attr->idx < drvdata->dsb_msr_num) | |
| 141 | + if (tpdm_attr->idx < drvdata->dsb_msr_num) { | |
| 117 | 142 | drvdata->dsb->msr[tpdm_attr->idx] = val; |
| 118 | − else | |
| 119 | − ret = -EINVAL; | |
| 143 | + ret = size; | |
| 144 | + } | |
| 145 | + break; | |
| 146 | + case CMB_TRIG_PATT: | |
| 147 | + if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) { | |
| 148 | + drvdata->cmb->trig_patt[tpdm_attr->idx] = val; | |
| 149 | + ret = size; | |
| 150 | + } | |
| 151 | + break; | |
| 152 | + case CMB_TRIG_PATT_MASK: | |
| 153 | + if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) { | |
| 154 | + drvdata->cmb->trig_patt_mask[tpdm_attr->idx] = val; | |
| 155 | + ret = size; | |
| 156 | + } | |
| 157 | + break; | |
| 158 | + case CMB_PATT: | |
| 159 | + if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) { | |
| 160 | + drvdata->cmb->patt_val[tpdm_attr->idx] = val; | |
| 161 | + ret = size; | |
| 162 | + } | |
| 163 | + break; | |
| 164 | + case CMB_PATT_MASK: | |
| 165 | + if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) { | |
| 166 | + drvdata->cmb->patt_mask[tpdm_attr->idx] = val; | |
| 167 | + ret = size; | |
| 168 | + } | |
| 169 | + break; | |
| 170 | + case CMB_MSR: | |
| 171 | + if (tpdm_attr->idx < drvdata->cmb_msr_num) { | |
| 172 | + drvdata->cmb->msr[tpdm_attr->idx] = val; | |
| 173 | + ret = size; | |
| 174 | + } | |
| 120 | 175 | break; |
| 121 | 176 | default: |
| 122 | − ret = -EINVAL; | |
| 177 | + break; | |
| 123 | 178 | } |
| 124 | − spin_unlock(&drvdata->spinlock); | |
| 125 | 179 | |
| 126 | 180 | return ret; |
| 127 | 181 | } |
| 128 | 182 | |
| 129 | −static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata) | |
| 183 | +static umode_t tpdm_dsb_is_visible(struct kobject *kobj, | |
| 184 | + struct attribute *attr, int n) | |
| 130 | 185 | { |
| 131 | − return (drvdata->datasets & TPDM_PIDR0_DS_DSB); | |
| 186 | + struct device *dev = kobj_to_dev(kobj); | |
| 187 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 188 | + | |
| 189 | + if (drvdata && tpdm_has_dsb_dataset(drvdata)) | |
| 190 | + return attr->mode; | |
| 191 | + | |
| 192 | + return 0; | |
| 132 | 193 | } |
| 133 | 194 | |
| 134 | −static umode_t tpdm_dsb_is_visible(struct kobject *kobj, | |
| 195 | +static umode_t tpdm_cmb_is_visible(struct kobject *kobj, | |
| 135 | 196 | struct attribute *attr, int n) |
| 136 | 197 | { |
| 137 | 198 | struct device *dev = kobj_to_dev(kobj); |
| 138 | 199 | struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 139 | 200 | |
| 140 | − if (drvdata && tpdm_has_dsb_dataset(drvdata)) | |
| 201 | + if (drvdata && tpdm_has_cmb_dataset(drvdata)) | |
| 141 | 202 | return attr->mode; |
| 142 | 203 | |
| 143 | 204 | return 0; |
| @@ -159,6 +220,23 @@ static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj, · 15 unmodified lines | ||
| 159 | 220 | return 0; |
| 160 | 221 | } |
| 161 | 222 | |
| 223 | +static umode_t tpdm_cmb_msr_is_visible(struct kobject *kobj, | |
| 224 | + struct attribute *attr, int n) | |
| 225 | +{ | |
| 226 | + struct device *dev = kobj_to_dev(kobj); | |
| 227 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 228 | + | |
| 229 | + struct device_attribute *dev_attr = | |
| 230 | + container_of(attr, struct device_attribute, attr); | |
| 231 | + struct tpdm_dataset_attribute *tpdm_attr = | |
| 232 | + container_of(dev_attr, struct tpdm_dataset_attribute, attr); | |
| 233 | + | |
| 234 | + if (tpdm_attr->idx < drvdata->cmb_msr_num) | |
| 235 | + return attr->mode; | |
| 236 | + | |
| 237 | + return 0; | |
| 238 | +} | |
| 239 | + | |
| 162 | 240 | static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata) |
| 163 | 241 | { |
| 164 | 242 | if (tpdm_has_dsb_dataset(drvdata)) { |
| @@ -167,6 +245,9 @@ static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata) · 2 unmodified lines | ||
| 167 | 245 | drvdata->dsb->trig_ts = true; |
| 168 | 246 | drvdata->dsb->trig_type = false; |
| 169 | 247 | } |
| 248 | + | |
| 249 | + if (drvdata->cmb) | |
| 250 | + memset(drvdata->cmb, 0, sizeof(struct cmb_dataset)); | |
| 170 | 251 | } |
| 171 | 252 | |
| 172 | 253 | static void set_dsb_mode(struct tpdm_drvdata *drvdata, u32 *val) |
| @@ -233,25 +314,27 @@ static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata) · 60 unmodified lines | ||
| 233 | 314 | { |
| 234 | 315 | u32 val, i; |
| 235 | 316 | |
| 317 | + if (!tpdm_has_dsb_dataset(drvdata)) | |
| 318 | + return; | |
| 319 | + | |
| 236 | 320 | for (i = 0; i < TPDM_DSB_MAX_EDCR; i++) |
| 237 | 321 | writel_relaxed(drvdata->dsb->edge_ctrl[i], |
| 238 | − drvdata->base + TPDM_DSB_EDCR(i)); | |
| 322 | + drvdata->base + TPDM_DSB_EDCR(i)); | |
| 239 | 323 | for (i = 0; i < TPDM_DSB_MAX_EDCMR; i++) |
| 240 | 324 | writel_relaxed(drvdata->dsb->edge_ctrl_mask[i], |
| 241 | − drvdata->base + TPDM_DSB_EDCMR(i)); | |
| 325 | + drvdata->base + TPDM_DSB_EDCMR(i)); | |
| 242 | 326 | for (i = 0; i < TPDM_DSB_MAX_PATT; i++) { |
| 243 | 327 | writel_relaxed(drvdata->dsb->patt_val[i], |
| 244 | − drvdata->base + TPDM_DSB_TPR(i)); | |
| 328 | + drvdata->base + TPDM_DSB_TPR(i)); | |
| 245 | 329 | writel_relaxed(drvdata->dsb->patt_mask[i], |
| 246 | − drvdata->base + TPDM_DSB_TPMR(i)); | |
| 330 | + drvdata->base + TPDM_DSB_TPMR(i)); | |
| 247 | 331 | writel_relaxed(drvdata->dsb->trig_patt[i], |
| 248 | − drvdata->base + TPDM_DSB_XPR(i)); | |
| 332 | + drvdata->base + TPDM_DSB_XPR(i)); | |
| 249 | 333 | writel_relaxed(drvdata->dsb->trig_patt_mask[i], |
| 250 | − drvdata->base + TPDM_DSB_XPMR(i)); | |
| 334 | + drvdata->base + TPDM_DSB_XPMR(i)); | |
| 251 | 335 | } |
| 252 | 336 | |
| 253 | 337 | set_dsb_tier(drvdata); |
| 254 | − | |
| 255 | 338 | set_dsb_msr(drvdata); |
| 256 | 339 | |
| 257 | 340 | val = readl_relaxed(drvdata->base + TPDM_DSB_CR); |
| @@ -267,6 +350,76 @@ static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata) · 9 unmodified lines | ||
| 267 | 350 | writel_relaxed(val, drvdata->base + TPDM_DSB_CR); |
| 268 | 351 | } |
| 269 | 352 | |
| 353 | +static void set_cmb_tier(struct tpdm_drvdata *drvdata) | |
| 354 | +{ | |
| 355 | + u32 val; | |
| 356 | + | |
| 357 | + val = readl_relaxed(drvdata->base + TPDM_CMB_TIER); | |
| 358 | + | |
| 359 | + /* Clear all relevant fields */ | |
| 360 | + val &= ~(TPDM_CMB_TIER_PATT_TSENAB | TPDM_CMB_TIER_TS_ALL | | |
| 361 | + TPDM_CMB_TIER_XTRIG_TSENAB); | |
| 362 | + | |
| 363 | + /* Set pattern timestamp type and enablement */ | |
| 364 | + if (drvdata->cmb->patt_ts) | |
| 365 | + val |= TPDM_CMB_TIER_PATT_TSENAB; | |
| 366 | + | |
| 367 | + /* Set trigger timestamp */ | |
| 368 | + if (drvdata->cmb->trig_ts) | |
| 369 | + val |= TPDM_CMB_TIER_XTRIG_TSENAB; | |
| 370 | + | |
| 371 | + /* Set all timestamp enablement*/ | |
| 372 | + if (drvdata->cmb->ts_all) | |
| 373 | + val |= TPDM_CMB_TIER_TS_ALL; | |
| 374 | + | |
| 375 | + writel_relaxed(val, drvdata->base + TPDM_CMB_TIER); | |
| 376 | +} | |
| 377 | + | |
| 378 | +static void set_cmb_msr(struct tpdm_drvdata *drvdata) | |
| 379 | +{ | |
| 380 | + int i; | |
| 381 | + | |
| 382 | + for (i = 0; i < drvdata->cmb_msr_num; i++) | |
| 383 | + writel_relaxed(drvdata->cmb->msr[i], | |
| 384 | + drvdata->base + TPDM_CMB_MSR(i)); | |
| 385 | +} | |
| 386 | + | |
| 387 | +static void tpdm_enable_cmb(struct tpdm_drvdata *drvdata) | |
| 388 | +{ | |
| 389 | + u32 val, i; | |
| 390 | + | |
| 391 | + if (!tpdm_has_cmb_dataset(drvdata)) | |
| 392 | + return; | |
| 393 | + | |
| 394 | + /* Configure pattern registers */ | |
| 395 | + for (i = 0; i < TPDM_CMB_MAX_PATT; i++) { | |
| 396 | + writel_relaxed(drvdata->cmb->patt_val[i], | |
| 397 | + drvdata->base + TPDM_CMB_TPR(i)); | |
| 398 | + writel_relaxed(drvdata->cmb->patt_mask[i], | |
| 399 | + drvdata->base + TPDM_CMB_TPMR(i)); | |
| 400 | + writel_relaxed(drvdata->cmb->trig_patt[i], | |
| 401 | + drvdata->base + TPDM_CMB_XPR(i)); | |
| 402 | + writel_relaxed(drvdata->cmb->trig_patt_mask[i], | |
| 403 | + drvdata->base + TPDM_CMB_XPMR(i)); | |
| 404 | + } | |
| 405 | + | |
| 406 | + set_cmb_tier(drvdata); | |
| 407 | + set_cmb_msr(drvdata); | |
| 408 | + | |
| 409 | + val = readl_relaxed(drvdata->base + TPDM_CMB_CR); | |
| 410 | + /* | |
| 411 | + * Set to 0 for continuous CMB collection mode, | |
| 412 | + * 1 for trace-on-change CMB collection mode. | |
| 413 | + */ | |
| 414 | + if (drvdata->cmb->trace_mode) | |
| 415 | + val |= TPDM_CMB_CR_MODE; | |
| 416 | + else | |
| 417 | + val &= ~TPDM_CMB_CR_MODE; | |
| 418 | + /* Set the enable bit of CMB control register to 1 */ | |
| 419 | + val |= TPDM_CMB_CR_ENA; | |
| 420 | + writel_relaxed(val, drvdata->base + TPDM_CMB_CR); | |
| 421 | +} | |
| 422 | + | |
| 270 | 423 | /* |
| 271 | 424 | * TPDM enable operations |
| 272 | 425 | * The TPDM or Monitor serves as data collection component for various |
| @@ -279,8 +432,8 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata) · 6 unmodified lines | ||
| 279 | 432 | { |
| 280 | 433 | CS_UNLOCK(drvdata->base); |
| 281 | 434 | |
| 282 | − if (tpdm_has_dsb_dataset(drvdata)) | |
| 283 | − tpdm_enable_dsb(drvdata); | |
| 435 | + tpdm_enable_dsb(drvdata); | |
| 436 | + tpdm_enable_cmb(drvdata); | |
| 284 | 437 | |
| 285 | 438 | CS_LOCK(drvdata->base); |
| 286 | 439 | } |
| @@ -308,19 +461,35 @@ static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata) · 21 unmodified lines | ||
| 308 | 461 | { |
| 309 | 462 | u32 val; |
| 310 | 463 | |
| 464 | + if (!tpdm_has_dsb_dataset(drvdata)) | |
| 465 | + return; | |
| 466 | + | |
| 311 | 467 | /* Set the enable bit of DSB control register to 0 */ |
| 312 | 468 | val = readl_relaxed(drvdata->base + TPDM_DSB_CR); |
| 313 | 469 | val &= ~TPDM_DSB_CR_ENA; |
| 314 | 470 | writel_relaxed(val, drvdata->base + TPDM_DSB_CR); |
| 315 | 471 | } |
| 316 | 472 | |
| 473 | +static void tpdm_disable_cmb(struct tpdm_drvdata *drvdata) | |
| 474 | +{ | |
| 475 | + u32 val; | |
| 476 | + | |
| 477 | + if (!tpdm_has_cmb_dataset(drvdata)) | |
| 478 | + return; | |
| 479 | + | |
| 480 | + val = readl_relaxed(drvdata->base + TPDM_CMB_CR); | |
| 481 | + /* Set the enable bit of CMB control register to 0 */ | |
| 482 | + val &= ~TPDM_CMB_CR_ENA; | |
| 483 | + writel_relaxed(val, drvdata->base + TPDM_CMB_CR); | |
| 484 | +} | |
| 485 | + | |
| 317 | 486 | /* TPDM disable operations */ |
| 318 | 487 | static void __tpdm_disable(struct tpdm_drvdata *drvdata) |
| 319 | 488 | { |
| 320 | 489 | CS_UNLOCK(drvdata->base); |
| 321 | 490 | |
| 322 | − if (tpdm_has_dsb_dataset(drvdata)) | |
| 323 | − tpdm_disable_dsb(drvdata); | |
| 491 | + tpdm_disable_dsb(drvdata); | |
| 492 | + tpdm_disable_cmb(drvdata); | |
| 324 | 493 | |
| 325 | 494 | CS_LOCK(drvdata->base); |
| 326 | 495 | } |
| @@ -366,6 +535,12 @@ static int tpdm_datasets_setup(struct tpdm_drvdata *drvdata) · 39 unmodified lines | ||
| 366 | 535 | if (!drvdata->dsb) |
| 367 | 536 | return -ENOMEM; |
| 368 | 537 | } |
| 538 | + if (tpdm_has_cmb_dataset(drvdata) && (!drvdata->cmb)) { | |
| 539 | + drvdata->cmb = devm_kzalloc(drvdata->dev, | |
| 540 | + sizeof(*drvdata->cmb), GFP_KERNEL); | |
| 541 | + if (!drvdata->cmb) | |
| 542 | + return -ENOMEM; | |
| 543 | + } | |
| 369 | 544 | tpdm_reset_datasets(drvdata); |
| 370 | 545 | |
| 371 | 546 | return 0; |
| @@ -577,9 +752,18 @@ static ssize_t enable_ts_show(struct device *dev, · 205 unmodified lines | ||
| 577 | 752 | char *buf) |
| 578 | 753 | { |
| 579 | 754 | struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 755 | + struct tpdm_dataset_attribute *tpdm_attr = | |
| 756 | + container_of(attr, struct tpdm_dataset_attribute, attr); | |
| 757 | + ssize_t size = -EINVAL; | |
| 580 | 758 | |
| 581 | − return sysfs_emit(buf, "%u\n", | |
| 582 | − (unsigned int)drvdata->dsb->patt_ts); | |
| 759 | + if (tpdm_attr->mem == DSB_PATT) | |
| 760 | + size = sysfs_emit(buf, "%u\n", | |
| 761 | + (unsigned int)drvdata->dsb->patt_ts); | |
| 762 | + else if (tpdm_attr->mem == CMB_PATT) | |
| 763 | + size = sysfs_emit(buf, "%u\n", | |
| 764 | + (unsigned int)drvdata->cmb->patt_ts); | |
| 765 | + | |
| 766 | + return size; | |
| 583 | 767 | } |
| 584 | 768 | |
| 585 | 769 | /* |
| @@ -591,17 +775,23 @@ static ssize_t enable_ts_store(struct device *dev, · 5 unmodified lines | ||
| 591 | 775 | size_t size) |
| 592 | 776 | { |
| 593 | 777 | struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); |
| 778 | + struct tpdm_dataset_attribute *tpdm_attr = | |
| 779 | + container_of(attr, struct tpdm_dataset_attribute, attr); | |
| 594 | 780 | unsigned long val; |
| 595 | 781 | |
| 596 | 782 | if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) |
| 597 | 783 | return -EINVAL; |
| 598 | 784 | |
| 599 | − spin_lock(&drvdata->spinlock); | |
| 600 | − drvdata->dsb->patt_ts = !!val; | |
| 601 | − spin_unlock(&drvdata->spinlock); | |
| 785 | + guard(spinlock)(&drvdata->spinlock); | |
| 786 | + if (tpdm_attr->mem == DSB_PATT) | |
| 787 | + drvdata->dsb->patt_ts = !!val; | |
| 788 | + else if (tpdm_attr->mem == CMB_PATT) | |
| 789 | + drvdata->cmb->patt_ts = !!val; | |
| 790 | + else | |
| 791 | + return -EINVAL; | |
| 792 | + | |
| 602 | 793 | return size; |
| 603 | 794 | } |
| 604 | −static DEVICE_ATTR_RW(enable_ts); | |
| 605 | 795 | |
| 606 | 796 | static ssize_t set_type_show(struct device *dev, |
| 607 | 797 | struct device_attribute *attr, |
| @@ -704,6 +894,96 @@ static ssize_t dsb_trig_ts_store(struct device *dev, · 96 unmodified lines | ||
| 704 | 894 | } |
| 705 | 895 | static DEVICE_ATTR_RW(dsb_trig_ts); |
| 706 | 896 | |
| 897 | +static ssize_t cmb_mode_show(struct device *dev, | |
| 898 | + struct device_attribute *attr, | |
| 899 | + char *buf) | |
| 900 | +{ | |
| 901 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 902 | + | |
| 903 | + return sysfs_emit(buf, "%x\n", drvdata->cmb->trace_mode); | |
| 904 | + | |
| 905 | +} | |
| 906 | + | |
| 907 | +static ssize_t cmb_mode_store(struct device *dev, | |
| 908 | + struct device_attribute *attr, | |
| 909 | + const char *buf, | |
| 910 | + size_t size) | |
| 911 | +{ | |
| 912 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 913 | + unsigned long trace_mode; | |
| 914 | + | |
| 915 | + if (kstrtoul(buf, 0, &trace_mode) || (trace_mode & ~1UL)) | |
| 916 | + return -EINVAL; | |
| 917 | + | |
| 918 | + spin_lock(&drvdata->spinlock); | |
| 919 | + drvdata->cmb->trace_mode = trace_mode; | |
| 920 | + spin_unlock(&drvdata->spinlock); | |
| 921 | + return size; | |
| 922 | +} | |
| 923 | +static DEVICE_ATTR_RW(cmb_mode); | |
| 924 | + | |
| 925 | +static ssize_t cmb_ts_all_show(struct device *dev, | |
| 926 | + struct device_attribute *attr, | |
| 927 | + char *buf) | |
| 928 | +{ | |
| 929 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 930 | + | |
| 931 | + return sysfs_emit(buf, "%u\n", | |
| 932 | + (unsigned int)drvdata->cmb->ts_all); | |
| 933 | +} | |
| 934 | + | |
| 935 | +static ssize_t cmb_ts_all_store(struct device *dev, | |
| 936 | + struct device_attribute *attr, | |
| 937 | + const char *buf, | |
| 938 | + size_t size) | |
| 939 | +{ | |
| 940 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 941 | + unsigned long val; | |
| 942 | + | |
| 943 | + if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) | |
| 944 | + return -EINVAL; | |
| 945 | + | |
| 946 | + guard(spinlock)(&drvdata->spinlock); | |
| 947 | + if (val) | |
| 948 | + drvdata->cmb->ts_all = true; | |
| 949 | + else | |
| 950 | + drvdata->cmb->ts_all = false; | |
| 951 | + | |
| 952 | + return size; | |
| 953 | +} | |
| 954 | +static DEVICE_ATTR_RW(cmb_ts_all); | |
| 955 | + | |
| 956 | +static ssize_t cmb_trig_ts_show(struct device *dev, | |
| 957 | + struct device_attribute *attr, | |
| 958 | + char *buf) | |
| 959 | +{ | |
| 960 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 961 | + | |
| 962 | + return sysfs_emit(buf, "%u\n", | |
| 963 | + (unsigned int)drvdata->cmb->trig_ts); | |
| 964 | +} | |
| 965 | + | |
| 966 | +static ssize_t cmb_trig_ts_store(struct device *dev, | |
| 967 | + struct device_attribute *attr, | |
| 968 | + const char *buf, | |
| 969 | + size_t size) | |
| 970 | +{ | |
| 971 | + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); | |
| 972 | + unsigned long val; | |
| 973 | + | |
| 974 | + if ((kstrtoul(buf, 0, &val)) || (val & ~1UL)) | |
| 975 | + return -EINVAL; | |
| 976 | + | |
| 977 | + guard(spinlock)(&drvdata->spinlock); | |
| 978 | + if (val) | |
| 979 | + drvdata->cmb->trig_ts = true; | |
| 980 | + else | |
| 981 | + drvdata->cmb->trig_ts = false; | |
| 982 | + | |
| 983 | + return size; | |
| 984 | +} | |
| 985 | +static DEVICE_ATTR_RW(cmb_trig_ts); | |
| 986 | + | |
| 707 | 987 | static struct attribute *tpdm_dsb_edge_attrs[] = { |
| 708 | 988 | &dev_attr_ctrl_idx.attr, |
| 709 | 989 | &dev_attr_ctrl_val.attr, |
| @@ -772,7 +1052,7 @@ static struct attribute *tpdm_dsb_patt_attrs[] = { · 62 unmodified lines | ||
| 772 | 1052 | DSB_PATT_MASK_ATTR(5), |
| 773 | 1053 | DSB_PATT_MASK_ATTR(6), |
| 774 | 1054 | DSB_PATT_MASK_ATTR(7), |
| 775 | − &dev_attr_enable_ts.attr, | |
| 1055 | + DSB_PATT_ENABLE_TS, | |
| 776 | 1056 | &dev_attr_set_type.attr, |
| 777 | 1057 | NULL, |
| 778 | 1058 | }; |
| @@ -813,6 +1093,59 @@ static struct attribute *tpdm_dsb_msr_attrs[] = { · 34 unmodified lines | ||
| 813 | 1093 | NULL, |
| 814 | 1094 | }; |
| 815 | 1095 | |
| 1096 | +static struct attribute *tpdm_cmb_trig_patt_attrs[] = { | |
| 1097 | + CMB_TRIG_PATT_ATTR(0), | |
| 1098 | + CMB_TRIG_PATT_ATTR(1), | |
| 1099 | + CMB_TRIG_PATT_MASK_ATTR(0), | |
| 1100 | + CMB_TRIG_PATT_MASK_ATTR(1), | |
| 1101 | + NULL, | |
| 1102 | +}; | |
| 1103 | + | |
| 1104 | +static struct attribute *tpdm_cmb_patt_attrs[] = { | |
| 1105 | + CMB_PATT_ATTR(0), | |
| 1106 | + CMB_PATT_ATTR(1), | |
| 1107 | + CMB_PATT_MASK_ATTR(0), | |
| 1108 | + CMB_PATT_MASK_ATTR(1), | |
| 1109 | + CMB_PATT_ENABLE_TS, | |
| 1110 | + NULL, | |
| 1111 | +}; | |
| 1112 | + | |
| 1113 | +static struct attribute *tpdm_cmb_msr_attrs[] = { | |
| 1114 | + CMB_MSR_ATTR(0), | |
| 1115 | + CMB_MSR_ATTR(1), | |
| 1116 | + CMB_MSR_ATTR(2), | |
| 1117 | + CMB_MSR_ATTR(3), | |
| 1118 | + CMB_MSR_ATTR(4), | |
| 1119 | + CMB_MSR_ATTR(5), | |
| 1120 | + CMB_MSR_ATTR(6), | |
| 1121 | + CMB_MSR_ATTR(7), | |
| 1122 | + CMB_MSR_ATTR(8), | |
| 1123 | + CMB_MSR_ATTR(9), | |
| 1124 | + CMB_MSR_ATTR(10), | |
| 1125 | + CMB_MSR_ATTR(11), | |
| 1126 | + CMB_MSR_ATTR(12), | |
| 1127 | + CMB_MSR_ATTR(13), | |
| 1128 | + CMB_MSR_ATTR(14), | |
| 1129 | + CMB_MSR_ATTR(15), | |
| 1130 | + CMB_MSR_ATTR(16), | |
| 1131 | + CMB_MSR_ATTR(17), | |
| 1132 | + CMB_MSR_ATTR(18), | |
| 1133 | + CMB_MSR_ATTR(19), | |
| 1134 | + CMB_MSR_ATTR(20), | |
| 1135 | + CMB_MSR_ATTR(21), | |
| 1136 | + CMB_MSR_ATTR(22), | |
| 1137 | + CMB_MSR_ATTR(23), | |
| 1138 | + CMB_MSR_ATTR(24), | |
| 1139 | + CMB_MSR_ATTR(25), | |
| 1140 | + CMB_MSR_ATTR(26), | |
| 1141 | + CMB_MSR_ATTR(27), | |
| 1142 | + CMB_MSR_ATTR(28), | |
| 1143 | + CMB_MSR_ATTR(29), | |
| 1144 | + CMB_MSR_ATTR(30), | |
| 1145 | + CMB_MSR_ATTR(31), | |
| 1146 | + NULL, | |
| 1147 | +}; | |
| 1148 | + | |
| 816 | 1149 | static struct attribute *tpdm_dsb_attrs[] = { |
| 817 | 1150 | &dev_attr_dsb_mode.attr, |
| 818 | 1151 | &dev_attr_dsb_trig_ts.attr, |
| @@ -820,6 +1153,13 @@ static struct attribute *tpdm_dsb_attrs[] = { · 1 unmodified line | ||
| 820 | 1153 | NULL, |
| 821 | 1154 | }; |
| 822 | 1155 | |
| 1156 | +static struct attribute *tpdm_cmb_attrs[] = { | |
| 1157 | + &dev_attr_cmb_mode.attr, | |
| 1158 | + &dev_attr_cmb_ts_all.attr, | |
| 1159 | + &dev_attr_cmb_trig_ts.attr, | |
| 1160 | + NULL, | |
| 1161 | +}; | |
| 1162 | + | |
| 823 | 1163 | static struct attribute_group tpdm_dsb_attr_grp = { |
| 824 | 1164 | .attrs = tpdm_dsb_attrs, |
| 825 | 1165 | .is_visible = tpdm_dsb_is_visible, |
| @@ -849,6 +1189,29 @@ static struct attribute_group tpdm_dsb_msr_grp = { · 23 unmodified lines | ||
| 849 | 1189 | .name = "dsb_msr", |
| 850 | 1190 | }; |
| 851 | 1191 | |
| 1192 | +static struct attribute_group tpdm_cmb_attr_grp = { | |
| 1193 | + .attrs = tpdm_cmb_attrs, | |
| 1194 | + .is_visible = tpdm_cmb_is_visible, | |
| 1195 | +}; | |
| 1196 | + | |
| 1197 | +static struct attribute_group tpdm_cmb_trig_patt_grp = { | |
| 1198 | + .attrs = tpdm_cmb_trig_patt_attrs, | |
| 1199 | + .is_visible = tpdm_cmb_is_visible, | |
| 1200 | + .name = "cmb_trig_patt", | |
| 1201 | +}; | |
| 1202 | + | |
| 1203 | +static struct attribute_group tpdm_cmb_patt_grp = { | |
| 1204 | + .attrs = tpdm_cmb_patt_attrs, | |
| 1205 | + .is_visible = tpdm_cmb_is_visible, | |
| 1206 | + .name = "cmb_patt", | |
| 1207 | +}; | |
| 1208 | + | |
| 1209 | +static struct attribute_group tpdm_cmb_msr_grp = { | |
| 1210 | + .attrs = tpdm_cmb_msr_attrs, | |
| 1211 | + .is_visible = tpdm_cmb_msr_is_visible, | |
| 1212 | + .name = "cmb_msr", | |
| 1213 | +}; | |
| 1214 | + | |
| 852 | 1215 | static const struct attribute_group *tpdm_attr_grps[] = { |
| 853 | 1216 | &tpdm_attr_grp, |
| 854 | 1217 | &tpdm_dsb_attr_grp, |
| @@ -856,6 +1219,10 @@ static const struct attribute_group *tpdm_attr_grps[] = { · 1 unmodified line | ||
| 856 | 1219 | &tpdm_dsb_trig_patt_grp, |
| 857 | 1220 | &tpdm_dsb_patt_grp, |
| 858 | 1221 | &tpdm_dsb_msr_grp, |
| 1222 | + &tpdm_cmb_attr_grp, | |
| 1223 | + &tpdm_cmb_trig_patt_grp, | |
| 1224 | + &tpdm_cmb_patt_grp, | |
| 1225 | + &tpdm_cmb_msr_grp, | |
| 859 | 1226 | NULL, |
| 860 | 1227 | }; |
| 861 | 1228 | |
| @@ -894,6 +1261,10 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) · 32 unmodified lines | ||
| 894 | 1261 | of_property_read_u32(drvdata->dev->of_node, |
| 895 | 1262 | "qcom,dsb-msrs-num", &drvdata->dsb_msr_num); |
| 896 | 1263 | |
| 1264 | + if (drvdata && tpdm_has_cmb_dataset(drvdata)) | |
| 1265 | + of_property_read_u32(drvdata->dev->of_node, | |
| 1266 | + "qcom,cmb-msrs-num", &drvdata->cmb_msr_num); | |
| 1267 | + | |
| 897 | 1268 | /* Set up coresight component description */ |
| 898 | 1269 | desc.name = coresight_alloc_device_name(&tpdm_devs, dev); |
| 899 | 1270 | if (!desc.name) |
| @@ -933,7 +1304,7 @@ static struct amba_id tpdm_ids[] = { · 33 unmodified lines | ||
| 933 | 1304 | .id = 0x000f0e00, |
| 934 | 1305 | .mask = 0x000fff00, |
| 935 | 1306 | }, |
| 936 | − { 0, 0}, | |
| 1307 | + { 0, 0, NULL }, | |
| 937 | 1308 | }; |
| 938 | 1309 | |
| 939 | 1310 | static struct amba_driver tpdm_driver = { |
| @@ -9,6 +9,38 @@ · 8 unmodified lines | ||
| 9 | 9 | /* The max number of the datasets that TPDM supports */ |
| 10 | 10 | #define TPDM_DATASETS 7 |
| 11 | 11 | |
| 12 | +/* CMB Subunit Registers */ | |
| 13 | +#define TPDM_CMB_CR (0xA00) | |
| 14 | +/* CMB subunit timestamp insertion enable register */ | |
| 15 | +#define TPDM_CMB_TIER (0xA04) | |
| 16 | +/* CMB subunit timestamp pattern registers */ | |
| 17 | +#define TPDM_CMB_TPR(n) (0xA08 + (n * 4)) | |
| 18 | +/* CMB subunit timestamp pattern mask registers */ | |
| 19 | +#define TPDM_CMB_TPMR(n) (0xA10 + (n * 4)) | |
| 20 | +/* CMB subunit trigger pattern registers */ | |
| 21 | +#define TPDM_CMB_XPR(n) (0xA18 + (n * 4)) | |
| 22 | +/* CMB subunit trigger pattern mask registers */ | |
| 23 | +#define TPDM_CMB_XPMR(n) (0xA20 + (n * 4)) | |
| 24 | +/* CMB MSR register */ | |
| 25 | +#define TPDM_CMB_MSR(n) (0xA80 + (n * 4)) | |
| 26 | + | |
| 27 | +/* Enable bit for CMB subunit */ | |
| 28 | +#define TPDM_CMB_CR_ENA BIT(0) | |
| 29 | +/* Trace collection mode for CMB subunit */ | |
| 30 | +#define TPDM_CMB_CR_MODE BIT(1) | |
| 31 | +/* Timestamp control for pattern match */ | |
| 32 | +#define TPDM_CMB_TIER_PATT_TSENAB BIT(0) | |
| 33 | +/* CMB CTI timestamp request */ | |
| 34 | +#define TPDM_CMB_TIER_XTRIG_TSENAB BIT(1) | |
| 35 | +/* For timestamp fo all trace */ | |
| 36 | +#define TPDM_CMB_TIER_TS_ALL BIT(2) | |
| 37 | + | |
| 38 | +/* Patten register number */ | |
| 39 | +#define TPDM_CMB_MAX_PATT 2 | |
| 40 | + | |
| 41 | +/* MAX number of DSB MSR */ | |
| 42 | +#define TPDM_CMB_MAX_MSR 32 | |
| 43 | + | |
| 12 | 44 | /* DSB Subunit Registers */ |
| 13 | 45 | #define TPDM_DSB_CR (0x780) |
| 14 | 46 | #define TPDM_DSB_TIER (0x784) |
| @@ -79,10 +111,12 @@ · 64 unmodified lines | ||
| 79 | 111 | * |
| 80 | 112 | * PERIPHIDR0[0] : Fix to 1 if ImplDef subunit present, else 0 |
| 81 | 113 | * PERIPHIDR0[1] : Fix to 1 if DSB subunit present, else 0 |
| 114 | + * PERIPHIDR0[2] : Fix to 1 if CMB subunit present, else 0 | |
| 82 | 115 | */ |
| 83 | 116 | |
| 84 | 117 | #define TPDM_PIDR0_DS_IMPDEF BIT(0) |
| 85 | 118 | #define TPDM_PIDR0_DS_DSB BIT(1) |
| 119 | +#define TPDM_PIDR0_DS_CMB BIT(2) | |
| 86 | 120 | |
| 87 | 121 | #define TPDM_DSB_MAX_LINES 256 |
| 88 | 122 | /* MAX number of EDCR registers */ |
| @@ -113,6 +147,16 @@ · 24 unmodified lines | ||
| 113 | 147 | } \ |
| 114 | 148 | })[0].attr.attr) |
| 115 | 149 | |
| 150 | +#define tpdm_patt_enable_ts(name, mem) \ | |
| 151 | + (&((struct tpdm_dataset_attribute[]) { \ | |
| 152 | + { \ | |
| 153 | + __ATTR(name, 0644, enable_ts_show, \ | |
| 154 | + enable_ts_store), \ | |
| 155 | + mem, \ | |
| 156 | + 0, \ | |
| 157 | + } \ | |
| 158 | + })[0].attr.attr) | |
| 159 | + | |
| 116 | 160 | #define DSB_EDGE_CTRL_ATTR(nr) \ |
| 117 | 161 | tpdm_simple_dataset_ro(edcr##nr, \ |
| 118 | 162 | DSB_EDGE_CTRL, nr) |
| @@ -137,10 +181,38 @@ · 18 unmodified lines | ||
| 137 | 181 | tpdm_simple_dataset_rw(tpmr##nr, \ |
| 138 | 182 | DSB_PATT_MASK, nr) |
| 139 | 183 | |
| 184 | +#define DSB_PATT_ENABLE_TS \ | |
| 185 | + tpdm_patt_enable_ts(enable_ts, \ | |
| 186 | + DSB_PATT) | |
| 187 | + | |
| 140 | 188 | #define DSB_MSR_ATTR(nr) \ |
| 141 | 189 | tpdm_simple_dataset_rw(msr##nr, \ |
| 142 | 190 | DSB_MSR, nr) |
| 143 | 191 | |
| 192 | +#define CMB_TRIG_PATT_ATTR(nr) \ | |
| 193 | + tpdm_simple_dataset_rw(xpr##nr, \ | |
| 194 | + CMB_TRIG_PATT, nr) | |
| 195 | + | |
| 196 | +#define CMB_TRIG_PATT_MASK_ATTR(nr) \ | |
| 197 | + tpdm_simple_dataset_rw(xpmr##nr, \ | |
| 198 | + CMB_TRIG_PATT_MASK, nr) | |
| 199 | + | |
| 200 | +#define CMB_PATT_ATTR(nr) \ | |
| 201 | + tpdm_simple_dataset_rw(tpr##nr, \ | |
| 202 | + CMB_PATT, nr) | |
| 203 | + | |
| 204 | +#define CMB_PATT_MASK_ATTR(nr) \ | |
| 205 | + tpdm_simple_dataset_rw(tpmr##nr, \ | |
| 206 | + CMB_PATT_MASK, nr) | |
| 207 | + | |
| 208 | +#define CMB_PATT_ENABLE_TS \ | |
| 209 | + tpdm_patt_enable_ts(enable_ts, \ | |
| 210 | + CMB_PATT) | |
| 211 | + | |
| 212 | +#define CMB_MSR_ATTR(nr) \ | |
| 213 | + tpdm_simple_dataset_rw(msr##nr, \ | |
| 214 | + CMB_MSR, nr) | |
| 215 | + | |
| 144 | 216 | /** |
| 145 | 217 | * struct dsb_dataset - specifics associated to dsb dataset |
| 146 | 218 | * @mode: DSB programming mode |
| @@ -173,6 +245,30 @@ struct dsb_dataset { · 26 unmodified lines | ||
| 173 | 245 | bool trig_type; |
| 174 | 246 | }; |
| 175 | 247 | |
| 248 | +/** | |
| 249 | + * struct cmb_dataset | |
| 250 | + * @trace_mode: Dataset collection mode | |
| 251 | + * @patt_val: Save value for pattern | |
| 252 | + * @patt_mask: Save value for pattern mask | |
| 253 | + * @trig_patt: Save value for trigger pattern | |
| 254 | + * @trig_patt_mask: Save value for trigger pattern mask | |
| 255 | + * @msr Save value for MSR | |
| 256 | + * @patt_ts: Indicates if pattern match for timestamp is enabled. | |
| 257 | + * @trig_ts: Indicates if CTI trigger for timestamp is enabled. | |
| 258 | + * @ts_all: Indicates if timestamp is enabled for all packets. | |
| 259 | + */ | |
| 260 | +struct cmb_dataset { | |
| 261 | + u32 trace_mode; | |
| 262 | + u32 patt_val[TPDM_CMB_MAX_PATT]; | |
| 263 | + u32 patt_mask[TPDM_CMB_MAX_PATT]; | |
| 264 | + u32 trig_patt[TPDM_CMB_MAX_PATT]; | |
| 265 | + u32 trig_patt_mask[TPDM_CMB_MAX_PATT]; | |
| 266 | + u32 msr[TPDM_CMB_MAX_MSR]; | |
| 267 | + bool patt_ts; | |
| 268 | + bool trig_ts; | |
| 269 | + bool ts_all; | |
| 270 | +}; | |
| 271 | + | |
| 176 | 272 | /** |
| 177 | 273 | * struct tpdm_drvdata - specifics associated to an TPDM component |
| 178 | 274 | * @base: memory mapped base address for this component. |
| @@ -182,7 +278,9 @@ struct dsb_dataset { · 3 unmodified lines | ||
| 182 | 278 | * @enable: enable status of the component. |
| 183 | 279 | * @datasets: The datasets types present of the TPDM. |
| 184 | 280 | * @dsb Specifics associated to TPDM DSB. |
| 281 | + * @cmb Specifics associated to TPDM CMB. | |
| 185 | 282 | * @dsb_msr_num Number of MSR supported by DSB TPDM |
| 283 | + * @cmb_msr_num Number of MSR supported by CMB TPDM | |
| 186 | 284 | */ |
| 187 | 285 | |
| 188 | 286 | struct tpdm_drvdata { |
| @@ -193,7 +291,9 @@ struct tpdm_drvdata { · 4 unmodified lines | ||
| 193 | 291 | bool enable; |
| 194 | 292 | unsigned long datasets; |
| 195 | 293 | struct dsb_dataset *dsb; |
| 294 | + struct cmb_dataset *cmb; | |
| 196 | 295 | u32 dsb_msr_num; |
| 296 | + u32 cmb_msr_num; | |
| 197 | 297 | }; |
| 198 | 298 | |
| 199 | 299 | /* Enumerate members of various datasets */ |
| @@ -205,6 +305,11 @@ enum dataset_mem { · 5 unmodified lines | ||
| 205 | 305 | DSB_PATT, |
| 206 | 306 | DSB_PATT_MASK, |
| 207 | 307 | DSB_MSR, |
| 308 | + CMB_TRIG_PATT, | |
| 309 | + CMB_TRIG_PATT_MASK, | |
| 310 | + CMB_PATT, | |
| 311 | + CMB_PATT_MASK, | |
| 312 | + CMB_MSR | |
| 208 | 313 | }; |
| 209 | 314 | |
| 210 | 315 | /** |
| @@ -220,4 +325,13 @@ struct tpdm_dataset_attribute { · 9 unmodified lines | ||
| 220 | 325 | u32 idx; |
| 221 | 326 | }; |
| 222 | 327 | |
| 328 | +static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata) | |
| 329 | +{ | |
| 330 | + return (drvdata->datasets & TPDM_PIDR0_DS_DSB); | |
| 331 | +} | |
| 332 | + | |
| 333 | +static bool tpdm_has_cmb_dataset(struct tpdm_drvdata *drvdata) | |
| 334 | +{ | |
| 335 | + return (drvdata->datasets & TPDM_PIDR0_DS_CMB); | |
| 336 | +} | |
| 223 | 337 | #endif /* _CORESIGHT_CORESIGHT_TPDM_H */ |
| @@ -58,6 +58,7 @@ struct tpiu_drvdata { · 57 unmodified lines | ||
| 58 | 58 | void __iomem *base; |
| 59 | 59 | struct clk *atclk; |
| 60 | 60 | struct coresight_device *csdev; |
| 61 | + spinlock_t spinlock; | |
| 61 | 62 | }; |
| 62 | 63 | |
| 63 | 64 | static void tpiu_enable_hw(struct csdev_access *csa) |
| @@ -72,8 +73,11 @@ static void tpiu_enable_hw(struct csdev_access *csa) · 8 unmodified lines | ||
| 72 | 73 | static int tpiu_enable(struct coresight_device *csdev, enum cs_mode mode, |
| 73 | 74 | void *__unused) |
| 74 | 75 | { |
| 76 | + struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
| 77 | + | |
| 78 | + guard(spinlock)(&drvdata->spinlock); | |
| 75 | 79 | tpiu_enable_hw(&csdev->access); |
| 76 | − atomic_inc(&csdev->refcnt); | |
| 80 | + csdev->refcnt++; | |
| 77 | 81 | dev_dbg(&csdev->dev, "TPIU enabled\n"); |
| 78 | 82 | return 0; |
| 79 | 83 | } |
| @@ -96,7 +100,11 @@ static void tpiu_disable_hw(struct csdev_access *csa) · 16 unmodified lines | ||
| 96 | 100 | |
| 97 | 101 | static int tpiu_disable(struct coresight_device *csdev) |
| 98 | 102 | { |
| 99 | − if (atomic_dec_return(&csdev->refcnt)) | |
| 103 | + struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
| 104 | + | |
| 105 | + guard(spinlock)(&drvdata->spinlock); | |
| 106 | + csdev->refcnt--; | |
| 107 | + if (csdev->refcnt) | |
| 100 | 108 | return -EBUSY; |
| 101 | 109 | |
| 102 | 110 | tpiu_disable_hw(&csdev->access); |
| @@ -132,6 +140,8 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) · 29 unmodified lines | ||
| 132 | 140 | if (!drvdata) |
| 133 | 141 | return -ENOMEM; |
| 134 | 142 | |
| 143 | + spin_lock_init(&drvdata->spinlock); | |
| 144 | + | |
| 135 | 145 | drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ |
| 136 | 146 | if (!IS_ERR(drvdata->atclk)) { |
| 137 | 147 | ret = clk_prepare_enable(drvdata->atclk); |
| @@ -218,7 +228,7 @@ static const struct amba_id tpiu_ids[] = { · 80 unmodified lines | ||
| 218 | 228 | .id = 0x000bb9e7, |
| 219 | 229 | .mask = 0x000fffff, |
| 220 | 230 | }, |
| 221 | − { 0, 0}, | |
| 231 | + { 0, 0, NULL }, | |
| 222 | 232 | }; |
| 223 | 233 | |
| 224 | 234 | MODULE_DEVICE_TABLE(amba, tpiu_ids); |
| @@ -103,7 +103,7 @@ static int smb_open(struct inode *inode, struct file *file) · 102 unmodified lines | ||
| 103 | 103 | if (drvdata->reading) |
| 104 | 104 | return -EBUSY; |
| 105 | 105 | |
| 106 | − if (atomic_read(&drvdata->csdev->refcnt)) | |
| 106 | + if (drvdata->csdev->refcnt) | |
| 107 | 107 | return -EBUSY; |
| 108 | 108 | |
| 109 | 109 | smb_update_data_size(drvdata); |
| @@ -207,11 +207,11 @@ static void smb_enable_sysfs(struct coresight_device *csdev) · 97 unmodified lines | ||
| 207 | 207 | { |
| 208 | 208 | struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); |
| 209 | 209 | |
| 210 | − if (drvdata->mode != CS_MODE_DISABLED) | |
| 210 | + if (coresight_get_mode(csdev) != CS_MODE_DISABLED) | |
| 211 | 211 | return; |
| 212 | 212 | |
| 213 | 213 | smb_enable_hw(drvdata); |
| 214 | − drvdata->mode = CS_MODE_SYSFS; | |
| 214 | + coresight_set_mode(csdev, CS_MODE_SYSFS); | |
| 215 | 215 | } |
| 216 | 216 | |
| 217 | 217 | static int smb_enable_perf(struct coresight_device *csdev, void *data) |
| @@ -234,7 +234,7 @@ static int smb_enable_perf(struct coresight_device *csdev, void *data) · 16 unmodified lines | ||
| 234 | 234 | if (drvdata->pid == -1) { |
| 235 | 235 | smb_enable_hw(drvdata); |
| 236 | 236 | drvdata->pid = pid; |
| 237 | − drvdata->mode = CS_MODE_PERF; | |
| 237 | + coresight_set_mode(csdev, CS_MODE_PERF); | |
| 238 | 238 | } |
| 239 | 239 | |
| 240 | 240 | return 0; |
| @@ -253,7 +253,8 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, · 12 unmodified lines | ||
| 253 | 253 | return -EBUSY; |
| 254 | 254 | |
| 255 | 255 | /* Do nothing, the SMB is already enabled as other mode */ |
| 256 | − if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode) | |
| 256 | + if (coresight_get_mode(csdev) != CS_MODE_DISABLED && | |
| 257 | + coresight_get_mode(csdev) != mode) | |
| 257 | 258 | return -EBUSY; |
| 258 | 259 | |
| 259 | 260 | switch (mode) { |
| @@ -270,7 +271,7 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode, · 10 unmodified lines | ||
| 270 | 271 | if (ret) |
| 271 | 272 | return ret; |
| 272 | 273 | |
| 273 | − atomic_inc(&csdev->refcnt); | |
| 274 | + csdev->refcnt++; | |
| 274 | 275 | dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n"); |
| 275 | 276 | |
| 276 | 277 | return ret; |
| @@ -285,17 +286,18 @@ static int smb_disable(struct coresight_device *csdev) · 8 unmodified lines | ||
| 285 | 286 | if (drvdata->reading) |
| 286 | 287 | return -EBUSY; |
| 287 | 288 | |
| 288 | − if (atomic_dec_return(&csdev->refcnt)) | |
| 289 | + csdev->refcnt--; | |
| 290 | + if (csdev->refcnt) | |
| 289 | 291 | return -EBUSY; |
| 290 | 292 | |
| 291 | 293 | /* Complain if we (somehow) got out of sync */ |
| 292 | − WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); | |
| 294 | + WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED); | |
| 293 | 295 | |
| 294 | 296 | smb_disable_hw(drvdata); |
| 295 | 297 | |
| 296 | 298 | /* Dissociate from the target process. */ |
| 297 | 299 | drvdata->pid = -1; |
| 298 | − drvdata->mode = CS_MODE_DISABLED; | |
| 300 | + coresight_set_mode(csdev, CS_MODE_DISABLED); | |
| 299 | 301 | dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n"); |
| 300 | 302 | |
| 301 | 303 | return 0; |
| @@ -380,7 +382,7 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev, · 78 unmodified lines | ||
| 380 | 382 | guard(spinlock)(&drvdata->spinlock); |
| 381 | 383 | |
| 382 | 384 | /* Don't do anything if another tracer is using this sink. */ |
| 383 | − if (atomic_read(&csdev->refcnt) != 1) | |
| 385 | + if (csdev->refcnt != 1) | |
| 384 | 386 | return 0; |
| 385 | 387 | |
| 386 | 388 | smb_disable_hw(drvdata); |
| @@ -586,7 +588,7 @@ static void smb_remove(struct platform_device *pdev) · 199 unmodified lines | ||
| 586 | 588 | |
| 587 | 589 | #ifdef CONFIG_ACPI |
| 588 | 590 | static const struct acpi_device_id ultrasoc_smb_acpi_match[] = { |
| 589 | − {"HISI03A1", 0}, | |
| 591 | + {"HISI03A1", 0, 0, 0}, | |
| 590 | 592 | {} |
| 591 | 593 | }; |
| 592 | 594 | MODULE_DEVICE_TABLE(acpi, ultrasoc_smb_acpi_match); |
| @@ -109,7 +109,6 @@ struct smb_data_buffer { · 108 unmodified lines | ||
| 109 | 109 | * @reading: Synchronise user space access to SMB buffer. |
| 110 | 110 | * @pid: Process ID of the process being monitored by the |
| 111 | 111 | * session that is using this component. |
| 112 | − * @mode: How this SMB is being used, perf mode or sysfs mode. | |
| 113 | 112 | */ |
| 114 | 113 | struct smb_drv_data { |
| 115 | 114 | void __iomem *base; |
| @@ -119,7 +118,6 @@ struct smb_drv_data { · 3 unmodified lines | ||
| 119 | 118 | spinlock_t spinlock; |
| 120 | 119 | bool reading; |
| 121 | 120 | pid_t pid; |
| 122 | − enum cs_mode mode; | |
| 123 | 121 | }; |
| 124 | 122 | |
| 125 | 123 | #endif |
| @@ -998,6 +998,9 @@ static int hisi_ptt_pmu_event_init(struct perf_event *event) · 997 unmodified lines | ||
| 998 | 998 | int ret; |
| 999 | 999 | u32 val; |
| 1000 | 1000 | |
| 1001 | + if (event->attr.type != hisi_ptt->hisi_ptt_pmu.type) | |
| 1002 | + return -ENOENT; | |
| 1003 | + | |
| 1001 | 1004 | if (event->cpu < 0) { |
| 1002 | 1005 | dev_dbg(event->pmu->dev, "Per-task mode not supported\n"); |
| 1003 | 1006 | return -EOPNOTSUPP; |
| @@ -1006,9 +1009,6 @@ static int hisi_ptt_pmu_event_init(struct perf_event *event) · 2 unmodified lines | ||
| 1006 | 1009 | if (event->attach_state & PERF_ATTACH_TASK) |
| 1007 | 1010 | return -EOPNOTSUPP; |
| 1008 | 1011 | |
| 1009 | − if (event->attr.type != hisi_ptt->hisi_ptt_pmu.type) | |
| 1010 | − return -ENOENT; | |
| 1011 | − | |
| 1012 | 1012 | ret = hisi_ptt_trace_valid_filter(hisi_ptt, event->attr.config); |
| 1013 | 1013 | if (ret < 0) |
| 1014 | 1014 | return ret; |
| @@ -71,6 +71,15 @@ config IIO_TRIGGERED_EVENT · 70 unmodified lines | ||
| 71 | 71 | help |
| 72 | 72 | Provides helper functions for setting up triggered events. |
| 73 | 73 | |
| 74 | +config IIO_BACKEND | |
| 75 | + tristate | |
| 76 | + help | |
| 77 | + Framework to handle complex IIO aggregate devices. The typical | |
| 78 | + architecture that can make use of this framework is to have one | |
| 79 | + device as the frontend device which can be "linked" against one or | |
| 80 | + multiple backend devices. The framework then makes it easy to get | |
| 81 | + and control such backend devices. | |
| 82 | + | |
| 74 | 83 | source "drivers/iio/accel/Kconfig" |
| 75 | 84 | source "drivers/iio/adc/Kconfig" |
| 76 | 85 | source "drivers/iio/addac/Kconfig" |
| @@ -13,6 +13,7 @@ obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o · 12 unmodified lines | ||
| 13 | 13 | obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o |
| 14 | 14 | obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o |
| 15 | 15 | obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o |
| 16 | +obj-$(CONFIG_IIO_BACKEND) += industrialio-backend.o | |
| 16 | 17 | |
| 17 | 18 | obj-y += accel/ |
| 18 | 19 | obj-y += adc/ |
| @@ -256,11 +256,11 @@ config BMC150_ACCEL_SPI · 255 unmodified lines | ||
| 256 | 256 | |
| 257 | 257 | config BMI088_ACCEL |
| 258 | 258 | tristate "Bosch BMI088 Accelerometer Driver" |
| 259 | − depends on SPI | |
| 260 | 259 | select IIO_BUFFER |
| 261 | 260 | select IIO_TRIGGERED_BUFFER |
| 262 | 261 | select REGMAP |
| 263 | − select BMI088_ACCEL_SPI | |
| 262 | + select BMI088_ACCEL_SPI if SPI | |
| 263 | + select BMI088_ACCEL_I2C if I2C | |
| 264 | 264 | help |
| 265 | 265 | Say yes here to build support for the following Bosch accelerometers: |
| 266 | 266 | BMI088, BMI085, BMI090L. Note that all of these are combo module that |
| @@ -269,6 +269,10 @@ config BMI088_ACCEL · 2 unmodified lines | ||
| 269 | 269 | This driver only implements the accelerometer part, which has its own |
| 270 | 270 | address and register map. BMG160 provides the gyroscope driver. |
| 271 | 271 | |
| 272 | +config BMI088_ACCEL_I2C | |
| 273 | + tristate | |
| 274 | + select REGMAP_I2C | |
| 275 | + | |
| 272 | 276 | config BMI088_ACCEL_SPI |
| 273 | 277 | tristate |
| 274 | 278 | select REGMAP_SPI |
| @@ -30,6 +30,7 @@ obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o · 29 unmodified lines | ||
| 30 | 30 | obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o |
| 31 | 31 | obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o |
| 32 | 32 | obj-$(CONFIG_BMI088_ACCEL) += bmi088-accel-core.o |
| 33 | +obj-$(CONFIG_BMI088_ACCEL_I2C) += bmi088-accel-i2c.o | |
| 33 | 34 | obj-$(CONFIG_BMI088_ACCEL_SPI) += bmi088-accel-spi.o |
| 34 | 35 | obj-$(CONFIG_DA280) += da280.o |
| 35 | 36 | obj-$(CONFIG_DA311) += da311.o |
| @@ -339,22 +339,17 @@ static int adxl367_set_act_threshold(struct adxl367_state *st, · 338 unmodified lines | ||
| 339 | 339 | { |
| 340 | 340 | int ret; |
| 341 | 341 | |
| 342 | − mutex_lock(&st->lock); | |
| 342 | + guard(mutex)(&st->lock); | |
| 343 | 343 | |
| 344 | 344 | ret = adxl367_set_measure_en(st, false); |
| 345 | 345 | if (ret) |
| 346 | − goto out; | |
| 346 | + return ret; | |
| 347 | 347 | |
| 348 | 348 | ret = _adxl367_set_act_threshold(st, act, threshold); |
| 349 | 349 | if (ret) |
| 350 | − goto out; | |
| 351 | − | |
| 352 | − ret = adxl367_set_measure_en(st, true); | |
| 353 | − | |
| 354 | −out: | |
| 355 | − mutex_unlock(&st->lock); | |
| 350 | + return ret; | |
| 356 | 351 | |
| 357 | − return ret; | |
| 352 | + return adxl367_set_measure_en(st, true); | |
| 358 | 353 | } |
| 359 | 354 | |
| 360 | 355 | static int adxl367_set_act_proc_mode(struct adxl367_state *st, |
| @@ -482,51 +477,45 @@ static int adxl367_set_fifo_watermark(struct adxl367_state *st, · 121 unmodified lines | ||
| 482 | 477 | static int adxl367_set_range(struct iio_dev *indio_dev, |
| 483 | 478 | enum adxl367_range range) |
| 484 | 479 | { |
| 485 | − struct adxl367_state *st = iio_priv(indio_dev); | |
| 486 | − int ret; | |
| 480 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 481 | + struct adxl367_state *st = iio_priv(indio_dev); | |
| 482 | + int ret; | |
| 487 | 483 | |
| 488 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 489 | − if (ret) | |
| 490 | − return ret; | |
| 491 | − | |
| 492 | − mutex_lock(&st->lock); | |
| 493 | − | |
| 494 | − ret = adxl367_set_measure_en(st, false); | |
| 495 | − if (ret) | |
| 496 | − goto out; | |
| 484 | + guard(mutex)(&st->lock); | |
| 497 | 485 | |
| 498 | − ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, | |
| 499 | − ADXL367_FILTER_CTL_RANGE_MASK, | |
| 500 | − FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK, | |
| 501 | − range)); | |
| 502 | − if (ret) | |
| 503 | − goto out; | |
| 486 | + ret = adxl367_set_measure_en(st, false); | |
| 487 | + if (ret) | |
| 488 | + return ret; | |
| 504 | 489 | |
| 505 | − adxl367_scale_act_thresholds(st, st->range, range); | |
| 490 | + ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, | |
| 491 | + ADXL367_FILTER_CTL_RANGE_MASK, | |
| 492 | + FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK, | |
| 493 | + range)); | |
| 494 | + if (ret) | |
| 495 | + return ret; | |
| 506 | 496 | |
| 507 | − /* Activity thresholds depend on range */ | |
| 508 | − ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, | |
| 509 | − st->act_threshold); | |
| 510 | − if (ret) | |
| 511 | − goto out; | |
| 497 | + adxl367_scale_act_thresholds(st, st->range, range); | |
| 512 | 498 | |
| 513 | − ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, | |
| 514 | − st->inact_threshold); | |
| 515 | − if (ret) | |
| 516 | − goto out; | |
| 517 | − | |
| 518 | − ret = adxl367_set_measure_en(st, true); | |
| 519 | − if (ret) | |
| 520 | − goto out; | |
| 499 | + /* Activity thresholds depend on range */ | |
| 500 | + ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, | |
| 501 | + st->act_threshold); | |
| 502 | + if (ret) | |
| 503 | + return ret; | |
| 521 | 504 | |
| 522 | − st->range = range; | |
| 505 | + ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, | |
| 506 | + st->inact_threshold); | |
| 507 | + if (ret) | |
| 508 | + return ret; | |
| 523 | 509 | |
| 524 | −out: | |
| 525 | − mutex_unlock(&st->lock); | |
| 510 | + ret = adxl367_set_measure_en(st, true); | |
| 511 | + if (ret) | |
| 512 | + return ret; | |
| 526 | 513 | |
| 527 | − iio_device_release_direct_mode(indio_dev); | |
| 514 | + st->range = range; | |
| 528 | 515 | |
| 529 | − return ret; | |
| 516 | + return 0; | |
| 517 | + } | |
| 518 | + unreachable(); | |
| 530 | 519 | } |
| 531 | 520 | |
| 532 | 521 | static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms) |
| @@ -587,11 +576,11 @@ static int adxl367_set_act_time_ms(struct adxl367_state *st, · 54 unmodified lines | ||
| 587 | 576 | { |
| 588 | 577 | int ret; |
| 589 | 578 | |
| 590 | − mutex_lock(&st->lock); | |
| 579 | + guard(mutex)(&st->lock); | |
| 591 | 580 | |
| 592 | 581 | ret = adxl367_set_measure_en(st, false); |
| 593 | 582 | if (ret) |
| 594 | − goto out; | |
| 583 | + return ret; | |
| 595 | 584 | |
| 596 | 585 | if (act == ADXL367_ACTIVITY) |
| 597 | 586 | ret = _adxl367_set_act_time_ms(st, ms); |
| @@ -599,14 +588,9 @@ static int adxl367_set_act_time_ms(struct adxl367_state *st, · 1 unmodified line | ||
| 599 | 588 | ret = _adxl367_set_inact_time_ms(st, ms); |
| 600 | 589 | |
| 601 | 590 | if (ret) |
| 602 | − goto out; | |
| 603 | − | |
| 604 | − ret = adxl367_set_measure_en(st, true); | |
| 605 | − | |
| 606 | −out: | |
| 607 | − mutex_unlock(&st->lock); | |
| 591 | + return ret; | |
| 608 | 592 | |
| 609 | − return ret; | |
| 593 | + return adxl367_set_measure_en(st, true); | |
| 610 | 594 | } |
| 611 | 595 | |
| 612 | 596 | static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) |
| @@ -636,31 +620,23 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) · 23 unmodified lines | ||
| 636 | 620 | |
| 637 | 621 | static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr) |
| 638 | 622 | { |
| 639 | − struct adxl367_state *st = iio_priv(indio_dev); | |
| 640 | − int ret; | |
| 623 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 624 | + struct adxl367_state *st = iio_priv(indio_dev);; | |
| 625 | + int ret; | |
| 641 | 626 | |
| 642 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 643 | − if (ret) | |
| 644 | − return ret; | |
| 627 | + guard(mutex)(&st->lock); | |
| 645 | 628 | |
| 646 | − mutex_lock(&st->lock); | |
| 647 | − | |
| 648 | − ret = adxl367_set_measure_en(st, false); | |
| 649 | − if (ret) | |
| 650 | − goto out; | |
| 651 | − | |
| 652 | − ret = _adxl367_set_odr(st, odr); | |
| 653 | − if (ret) | |
| 654 | − goto out; | |
| 655 | − | |
| 656 | − ret = adxl367_set_measure_en(st, true); | |
| 657 | − | |
| 658 | −out: | |
| 659 | − mutex_unlock(&st->lock); | |
| 629 | + ret = adxl367_set_measure_en(st, false); | |
| 630 | + if (ret) | |
| 631 | + return ret; | |
| 660 | 632 | |
| 661 | − iio_device_release_direct_mode(indio_dev); | |
| 633 | + ret = _adxl367_set_odr(st, odr); | |
| 634 | + if (ret) | |
| 635 | + return ret; | |
| 662 | 636 | |
| 663 | − return ret; | |
| 637 | + return adxl367_set_measure_en(st, true); | |
| 638 | + } | |
| 639 | + unreachable(); | |
| 664 | 640 | } |
| 665 | 641 | |
| 666 | 642 | static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg, |
| @@ -749,36 +725,32 @@ static int adxl367_read_sample(struct iio_dev *indio_dev, · 82 unmodified lines | ||
| 749 | 725 | struct iio_chan_spec const *chan, |
| 750 | 726 | int *val) |
| 751 | 727 | { |
| 752 | − struct adxl367_state *st = iio_priv(indio_dev); | |
| 753 | − u16 sample; | |
| 754 | − int ret; | |
| 728 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 729 | + struct adxl367_state *st = iio_priv(indio_dev); | |
| 730 | + u16 sample; | |
| 731 | + int ret; | |
| 755 | 732 | |
| 756 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 757 | − if (ret) | |
| 758 | − return ret; | |
| 733 | + guard(mutex)(&st->lock); | |
| 759 | 734 | |
| 760 | − mutex_lock(&st->lock); | |
| 761 | − | |
| 762 | − ret = adxl367_set_temp_adc_reg_en(st, chan->address, true); | |
| 763 | − if (ret) | |
| 764 | − goto out; | |
| 765 | − | |
| 766 | − ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf, | |
| 767 | − sizeof(st->sample_buf)); | |
| 768 | − if (ret) | |
| 769 | − goto out; | |
| 770 | − | |
| 771 | − sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf)); | |
| 772 | − *val = sign_extend32(sample, chan->scan_type.realbits - 1); | |
| 735 | + ret = adxl367_set_temp_adc_reg_en(st, chan->address, true); | |
| 736 | + if (ret) | |
| 737 | + return ret; | |
| 773 | 738 | |
| 774 | − ret = adxl367_set_temp_adc_reg_en(st, chan->address, false); | |
| 739 | + ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf, | |
| 740 | + sizeof(st->sample_buf)); | |
| 741 | + if (ret) | |
| 742 | + return ret; | |
| 775 | 743 | |
| 776 | −out: | |
| 777 | − mutex_unlock(&st->lock); | |
| 744 | + sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf)); | |
| 745 | + *val = sign_extend32(sample, chan->scan_type.realbits - 1); | |
| 778 | 746 | |
| 779 | − iio_device_release_direct_mode(indio_dev); | |
| 747 | + ret = adxl367_set_temp_adc_reg_en(st, chan->address, false); | |
| 748 | + if (ret) | |
| 749 | + return ret; | |
| 780 | 750 | |
| 781 | − return ret ?: IIO_VAL_INT; | |
| 751 | + return IIO_VAL_INT; | |
| 752 | + } | |
| 753 | + unreachable(); | |
| 782 | 754 | } |
| 783 | 755 | |
| 784 | 756 | static int adxl367_get_status(struct adxl367_state *st, u8 *status, |
| @@ -886,12 +858,12 @@ static int adxl367_read_raw(struct iio_dev *indio_dev, · 101 unmodified lines | ||
| 886 | 858 | return adxl367_read_sample(indio_dev, chan, val); |
| 887 | 859 | case IIO_CHAN_INFO_SCALE: |
| 888 | 860 | switch (chan->type) { |
| 889 | − case IIO_ACCEL: | |
| 890 | − mutex_lock(&st->lock); | |
| 861 | + case IIO_ACCEL: { | |
| 862 | + guard(mutex)(&st->lock); | |
| 891 | 863 | *val = adxl367_range_scale_tbl[st->range][0]; |
| 892 | 864 | *val2 = adxl367_range_scale_tbl[st->range][1]; |
| 893 | − mutex_unlock(&st->lock); | |
| 894 | 865 | return IIO_VAL_INT_PLUS_NANO; |
| 866 | + } | |
| 895 | 867 | case IIO_TEMP: |
| 896 | 868 | *val = 1000; |
| 897 | 869 | *val2 = ADXL367_TEMP_PER_C; |
| @@ -914,12 +886,12 @@ static int adxl367_read_raw(struct iio_dev *indio_dev, · 16 unmodified lines | ||
| 914 | 886 | default: |
| 915 | 887 | return -EINVAL; |
| 916 | 888 | } |
| 917 | − case IIO_CHAN_INFO_SAMP_FREQ: | |
| 918 | − mutex_lock(&st->lock); | |
| 889 | + case IIO_CHAN_INFO_SAMP_FREQ: { | |
| 890 | + guard(mutex)(&st->lock); | |
| 919 | 891 | *val = adxl367_samp_freq_tbl[st->odr][0]; |
| 920 | 892 | *val2 = adxl367_samp_freq_tbl[st->odr][1]; |
| 921 | − mutex_unlock(&st->lock); | |
| 922 | 893 | return IIO_VAL_INT_PLUS_MICRO; |
| 894 | + } | |
| 923 | 895 | default: |
| 924 | 896 | return -EINVAL; |
| 925 | 897 | } |
| @@ -1004,18 +976,15 @@ static int adxl367_read_event_value(struct iio_dev *indio_dev, · 78 unmodified lines | ||
| 1004 | 976 | { |
| 1005 | 977 | struct adxl367_state *st = iio_priv(indio_dev); |
| 1006 | 978 | |
| 979 | + guard(mutex)(&st->lock); | |
| 1007 | 980 | switch (info) { |
| 1008 | 981 | case IIO_EV_INFO_VALUE: { |
| 1009 | 982 | switch (dir) { |
| 1010 | 983 | case IIO_EV_DIR_RISING: |
| 1011 | − mutex_lock(&st->lock); | |
| 1012 | 984 | *val = st->act_threshold; |
| 1013 | − mutex_unlock(&st->lock); | |
| 1014 | 985 | return IIO_VAL_INT; |
| 1015 | 986 | case IIO_EV_DIR_FALLING: |
| 1016 | − mutex_lock(&st->lock); | |
| 1017 | 987 | *val = st->inact_threshold; |
| 1018 | − mutex_unlock(&st->lock); | |
| 1019 | 988 | return IIO_VAL_INT; |
| 1020 | 989 | default: |
| 1021 | 990 | return -EINVAL; |
| @@ -1024,15 +993,11 @@ static int adxl367_read_event_value(struct iio_dev *indio_dev, · 2 unmodified lines | ||
| 1024 | 993 | case IIO_EV_INFO_PERIOD: |
| 1025 | 994 | switch (dir) { |
| 1026 | 995 | case IIO_EV_DIR_RISING: |
| 1027 | − mutex_lock(&st->lock); | |
| 1028 | 996 | *val = st->act_time_ms; |
| 1029 | − mutex_unlock(&st->lock); | |
| 1030 | 997 | *val2 = 1000; |
| 1031 | 998 | return IIO_VAL_FRACTIONAL; |
| 1032 | 999 | case IIO_EV_DIR_FALLING: |
| 1033 | − mutex_lock(&st->lock); | |
| 1034 | 1000 | *val = st->inact_time_ms; |
| 1035 | − mutex_unlock(&st->lock); | |
| 1036 | 1001 | *val2 = 1000; |
| 1037 | 1002 | return IIO_VAL_FRACTIONAL; |
| 1038 | 1003 | default: |
| @@ -1110,9 +1075,7 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev, · 71 unmodified lines | ||
| 1110 | 1075 | enum iio_event_direction dir, |
| 1111 | 1076 | int state) |
| 1112 | 1077 | { |
| 1113 | − struct adxl367_state *st = iio_priv(indio_dev); | |
| 1114 | 1078 | enum adxl367_activity_type act; |
| 1115 | − int ret; | |
| 1116 | 1079 | |
| 1117 | 1080 | switch (dir) { |
| 1118 | 1081 | case IIO_EV_DIR_RISING: |
| @@ -1125,33 +1088,28 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev, · 6 unmodified lines | ||
| 1125 | 1088 | return -EINVAL; |
| 1126 | 1089 | } |
| 1127 | 1090 | |
| 1128 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 1129 | − if (ret) | |
| 1130 | − return ret; | |
| 1131 | − | |
| 1132 | − mutex_lock(&st->lock); | |
| 1091 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 1092 | + struct adxl367_state *st = iio_priv(indio_dev); | |
| 1093 | + int ret; | |
| 1133 | 1094 | |
| 1134 | − ret = adxl367_set_measure_en(st, false); | |
| 1135 | − if (ret) | |
| 1136 | − goto out; | |
| 1095 | + guard(mutex)(&st->lock); | |
| 1137 | 1096 | |
| 1138 | − ret = adxl367_set_act_interrupt_en(st, act, state); | |
| 1139 | − if (ret) | |
| 1140 | − goto out; | |
| 1141 | − | |
| 1142 | − ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED | |
| 1143 | − : ADXL367_ACT_DISABLED); | |
| 1144 | − if (ret) | |
| 1145 | − goto out; | |
| 1146 | − | |
| 1147 | − ret = adxl367_set_measure_en(st, true); | |
| 1097 | + ret = adxl367_set_measure_en(st, false); | |
| 1098 | + if (ret) | |
| 1099 | + return ret; | |
| 1148 | 1100 | |
| 1149 | −out: | |
| 1150 | − mutex_unlock(&st->lock); | |
| 1101 | + ret = adxl367_set_act_interrupt_en(st, act, state); | |
| 1102 | + if (ret) | |
| 1103 | + return ret; | |
| 1151 | 1104 | |
| 1152 | − iio_device_release_direct_mode(indio_dev); | |
| 1105 | + ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED | |
| 1106 | + : ADXL367_ACT_DISABLED); | |
| 1107 | + if (ret) | |
| 1108 | + return ret; | |
| 1153 | 1109 | |
| 1154 | − return ret; | |
| 1110 | + return adxl367_set_measure_en(st, true); | |
| 1111 | + } | |
| 1112 | + unreachable(); | |
| 1155 | 1113 | } |
| 1156 | 1114 | |
| 1157 | 1115 | static ssize_t adxl367_get_fifo_enabled(struct device *dev, |
| @@ -1176,9 +1134,8 @@ static ssize_t adxl367_get_fifo_watermark(struct device *dev, · 18 unmodified lines | ||
| 1176 | 1134 | struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev)); |
| 1177 | 1135 | unsigned int fifo_watermark; |
| 1178 | 1136 | |
| 1179 | − mutex_lock(&st->lock); | |
| 1137 | + guard(mutex)(&st->lock); | |
| 1180 | 1138 | fifo_watermark = st->fifo_watermark; |
| 1181 | − mutex_unlock(&st->lock); | |
| 1182 | 1139 | |
| 1183 | 1140 | return sysfs_emit(buf, "%d\n", fifo_watermark); |
| 1184 | 1141 | } |
| @@ -1207,22 +1164,17 @@ static int adxl367_set_watermark(struct iio_dev *indio_dev, unsigned int val) · 22 unmodified lines | ||
| 1207 | 1164 | if (val > ADXL367_FIFO_MAX_WATERMARK) |
| 1208 | 1165 | return -EINVAL; |
| 1209 | 1166 | |
| 1210 | − mutex_lock(&st->lock); | |
| 1167 | + guard(mutex)(&st->lock); | |
| 1211 | 1168 | |
| 1212 | 1169 | ret = adxl367_set_measure_en(st, false); |
| 1213 | 1170 | if (ret) |
| 1214 | − goto out; | |
| 1171 | + return ret; | |
| 1215 | 1172 | |
| 1216 | 1173 | ret = adxl367_set_fifo_watermark(st, val); |
| 1217 | 1174 | if (ret) |
| 1218 | − goto out; | |
| 1219 | − | |
| 1220 | − ret = adxl367_set_measure_en(st, true); | |
| 1221 | − | |
| 1222 | −out: | |
| 1223 | − mutex_unlock(&st->lock); | |
| 1175 | + return ret; | |
| 1224 | 1176 | |
| 1225 | − return ret; | |
| 1177 | + return adxl367_set_measure_en(st, true); | |
| 1226 | 1178 | } |
| 1227 | 1179 | |
| 1228 | 1180 | static bool adxl367_find_mask_fifo_format(const unsigned long *scan_mask, |
| @@ -1253,27 +1205,24 @@ static int adxl367_update_scan_mode(struct iio_dev *indio_dev, · 24 unmodified lines | ||
| 1253 | 1205 | if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format)) |
| 1254 | 1206 | return -EINVAL; |
| 1255 | 1207 | |
| 1256 | − mutex_lock(&st->lock); | |
| 1208 | + guard(mutex)(&st->lock); | |
| 1257 | 1209 | |
| 1258 | 1210 | ret = adxl367_set_measure_en(st, false); |
| 1259 | 1211 | if (ret) |
| 1260 | − goto out; | |
| 1212 | + return ret; | |
| 1261 | 1213 | |
| 1262 | 1214 | ret = adxl367_set_fifo_format(st, fifo_format); |
| 1263 | 1215 | if (ret) |
| 1264 | − goto out; | |
| 1216 | + return ret; | |
| 1265 | 1217 | |
| 1266 | 1218 | ret = adxl367_set_measure_en(st, true); |
| 1267 | 1219 | if (ret) |
| 1268 | − goto out; | |
| 1220 | + return ret; | |
| 1269 | 1221 | |
| 1270 | 1222 | st->fifo_set_size = bitmap_weight(active_scan_mask, |
| 1271 | 1223 | indio_dev->masklength); |
| 1272 | 1224 | |
| 1273 | −out: | |
| 1274 | − mutex_unlock(&st->lock); | |
| 1275 | − | |
| 1276 | − return ret; | |
| 1225 | + return 0; | |
| 1277 | 1226 | } |
| 1278 | 1227 | |
| 1279 | 1228 | static int adxl367_buffer_postenable(struct iio_dev *indio_dev) |
| @@ -1281,31 +1230,26 @@ static int adxl367_buffer_postenable(struct iio_dev *indio_dev) · 1 unmodified line | ||
| 1281 | 1230 | struct adxl367_state *st = iio_priv(indio_dev); |
| 1282 | 1231 | int ret; |
| 1283 | 1232 | |
| 1284 | − mutex_lock(&st->lock); | |
| 1233 | + guard(mutex)(&st->lock); | |
| 1285 | 1234 | |
| 1286 | 1235 | ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, |
| 1287 | 1236 | true); |
| 1288 | 1237 | if (ret) |
| 1289 | − goto out; | |
| 1238 | + return ret; | |
| 1290 | 1239 | |
| 1291 | 1240 | ret = adxl367_set_measure_en(st, false); |
| 1292 | 1241 | if (ret) |
| 1293 | − goto out; | |
| 1242 | + return ret; | |
| 1294 | 1243 | |
| 1295 | 1244 | ret = adxl367_set_fifo_watermark_interrupt_en(st, true); |
| 1296 | 1245 | if (ret) |
| 1297 | − goto out; | |
| 1246 | + return ret; | |
| 1298 | 1247 | |
| 1299 | 1248 | ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM); |
| 1300 | 1249 | if (ret) |
| 1301 | − goto out; | |
| 1302 | − | |
| 1303 | − ret = adxl367_set_measure_en(st, true); | |
| 1304 | − | |
| 1305 | −out: | |
| 1306 | − mutex_unlock(&st->lock); | |
| 1250 | + return ret; | |
| 1307 | 1251 | |
| 1308 | − return ret; | |
| 1252 | + return adxl367_set_measure_en(st, true); | |
| 1309 | 1253 | } |
| 1310 | 1254 | |
| 1311 | 1255 | static int adxl367_buffer_predisable(struct iio_dev *indio_dev) |
| @@ -1313,31 +1257,26 @@ static int adxl367_buffer_predisable(struct iio_dev *indio_dev) · 1 unmodified line | ||
| 1313 | 1257 | struct adxl367_state *st = iio_priv(indio_dev); |
| 1314 | 1258 | int ret; |
| 1315 | 1259 | |
| 1316 | − mutex_lock(&st->lock); | |
| 1260 | + guard(mutex)(&st->lock); | |
| 1317 | 1261 | |
| 1318 | 1262 | ret = adxl367_set_measure_en(st, false); |
| 1319 | 1263 | if (ret) |
| 1320 | − goto out; | |
| 1264 | + return ret; | |
| 1321 | 1265 | |
| 1322 | 1266 | ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED); |
| 1323 | 1267 | if (ret) |
| 1324 | − goto out; | |
| 1268 | + return ret; | |
| 1325 | 1269 | |
| 1326 | 1270 | ret = adxl367_set_fifo_watermark_interrupt_en(st, false); |
| 1327 | 1271 | if (ret) |
| 1328 | − goto out; | |
| 1272 | + return ret; | |
| 1329 | 1273 | |
| 1330 | 1274 | ret = adxl367_set_measure_en(st, true); |
| 1331 | 1275 | if (ret) |
| 1332 | − goto out; | |
| 1333 | − | |
| 1334 | − ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, | |
| 1335 | − false); | |
| 1336 | − | |
| 1337 | −out: | |
| 1338 | − mutex_unlock(&st->lock); | |
| 1276 | + return ret; | |
| 1339 | 1277 | |
| 1340 | − return ret; | |
| 1278 | + return adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, | |
| 1279 | + false); | |
| 1341 | 1280 | } |
| 1342 | 1281 | |
| 1343 | 1282 | static const struct iio_buffer_setup_ops adxl367_buffer_ops = { |
| @@ -6,8 +6,8 @@ · 5 unmodified lines | ||
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | #include <linux/module.h> |
| 9 | +#include <linux/mod_devicetable.h> | |
| 9 | 10 | #include <linux/regmap.h> |
| 10 | −#include <linux/of.h> | |
| 11 | 11 | #include <linux/spi/spi.h> |
| 12 | 12 | |
| 13 | 13 | #include "adxl372.h" |
| @@ -13,10 +13,10 @@ · 12 unmodified lines | ||
| 13 | 13 | */ |
| 14 | 14 | |
| 15 | 15 | #include <linux/module.h> |
| 16 | +#include <linux/mod_devicetable.h> | |
| 16 | 17 | #include <linux/i2c.h> |
| 17 | 18 | #include <linux/interrupt.h> |
| 18 | 19 | #include <linux/delay.h> |
| 19 | −#include <linux/of.h> | |
| 20 | 20 | #include <linux/bitops.h> |
| 21 | 21 | #include <linux/regulator/consumer.h> |
| 22 | 22 | #include <linux/slab.h> |
| @@ -224,6 +224,19 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { · 223 unmodified lines | ||
| 224 | 224 | {"BMA250E"}, |
| 225 | 225 | {"BMC150A"}, |
| 226 | 226 | {"BMI055A"}, |
| 227 | + /* | |
| 228 | + * The "BOSC0200" identifier used here is not unique to devices using | |
| 229 | + * bmc150. The same "BOSC0200" identifier is found in the ACPI tables | |
| 230 | + * of the ASUS ROG ALLY and Ayaneo AIR Plus which both use a Bosch | |
| 231 | + * BMI323 chip. This creates a conflict with duplicate ACPI identifiers | |
| 232 | + * which multiple drivers want to use. Fortunately, when the bmc150 | |
| 233 | + * driver starts to load on the ASUS ROG ALLY, the chip ID check | |
| 234 | + * portion fails (correctly) because the chip IDs received (via i2c) | |
| 235 | + * are unique between bmc150 and bmi323 and a dmesg output similar to | |
| 236 | + * this: "bmc150_accel_i2c i2c-BOSC0200:00: Invalid chip 0" can be | |
| 237 | + * seen. This allows the bmi323 driver to take over for ASUS ROG ALLY, | |
| 238 | + * and other devices using the bmi323 chip. | |
| 239 | + */ | |
| 227 | 240 | {"BOSC0200"}, |
| 228 | 241 | {"BSBA0150"}, |
| 229 | 242 | {"DUAL250E"}, |
| @@ -266,7 +279,7 @@ static struct i2c_driver bmc150_accel_driver = { · 36 unmodified lines | ||
| 266 | 279 | .driver = { |
| 267 | 280 | .name = "bmc150_accel_i2c", |
| 268 | 281 | .of_match_table = bmc150_accel_of_match, |
| 269 | − .acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match), | |
| 282 | + .acpi_match_table = bmc150_accel_acpi_match, | |
| 270 | 283 | .pm = &bmc150_accel_pm_ops, |
| 271 | 284 | }, |
| 272 | 285 | .probe = bmc150_accel_probe, |
| @@ -7,7 +7,6 @@ · 6 unmodified lines | ||
| 7 | 7 | #include <linux/device.h> |
| 8 | 8 | #include <linux/mod_devicetable.h> |
| 9 | 9 | #include <linux/module.h> |
| 10 | −#include <linux/acpi.h> | |
| 11 | 10 | #include <linux/regmap.h> |
| 12 | 11 | #include <linux/spi/spi.h> |
| 13 | 12 | |
| @@ -70,7 +69,7 @@ MODULE_DEVICE_TABLE(spi, bmc150_accel_id); · 56 unmodified lines | ||
| 70 | 69 | static struct spi_driver bmc150_accel_driver = { |
| 71 | 70 | .driver = { |
| 72 | 71 | .name = "bmc150_accel_spi", |
| 73 | − .acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match), | |
| 72 | + .acpi_match_table = bmc150_accel_acpi_match, | |
| 74 | 73 | .pm = &bmc150_accel_pm_ops, |
| 75 | 74 | }, |
| 76 | 75 | .probe = bmc150_accel_probe, |
| @@ -0,0 +1,70 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: | |
| 4 | + * - BMI088 | |
| 5 | + * - BMI085 | |
| 6 | + * - BMI090L | |
| 7 | + * | |
| 8 | + * Copyright 2023 Jun Yan <jerrysteve1101@gmail.com> | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#include <linux/i2c.h> | |
| 12 | +#include <linux/mod_devicetable.h> | |
| 13 | +#include <linux/module.h> | |
| 14 | +#include <linux/regmap.h> | |
| 15 | +#include <linux/slab.h> | |
| 16 | + | |
| 17 | +#include "bmi088-accel.h" | |
| 18 | + | |
| 19 | +static int bmi088_accel_probe(struct i2c_client *i2c) | |
| 20 | +{ | |
| 21 | + struct regmap *regmap; | |
| 22 | + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); | |
| 23 | + | |
| 24 | + regmap = devm_regmap_init_i2c(i2c, &bmi088_regmap_conf); | |
| 25 | + if (IS_ERR(regmap)) { | |
| 26 | + dev_err(&i2c->dev, "Failed to initialize i2c regmap\n"); | |
| 27 | + return PTR_ERR(regmap); | |
| 28 | + } | |
| 29 | + | |
| 30 | + return bmi088_accel_core_probe(&i2c->dev, regmap, i2c->irq, | |
| 31 | + id->driver_data); | |
| 32 | +} | |
| 33 | + | |
| 34 | +static void bmi088_accel_remove(struct i2c_client *i2c) | |
| 35 | +{ | |
| 36 | + bmi088_accel_core_remove(&i2c->dev); | |
| 37 | +} | |
| 38 | + | |
| 39 | +static const struct of_device_id bmi088_of_match[] = { | |
| 40 | + { .compatible = "bosch,bmi085-accel" }, | |
| 41 | + { .compatible = "bosch,bmi088-accel" }, | |
| 42 | + { .compatible = "bosch,bmi090l-accel" }, | |
| 43 | + {} | |
| 44 | +}; | |
| 45 | +MODULE_DEVICE_TABLE(of, bmi088_of_match); | |
| 46 | + | |
| 47 | +static const struct i2c_device_id bmi088_accel_id[] = { | |
| 48 | + { "bmi085-accel", BOSCH_BMI085 }, | |
| 49 | + { "bmi088-accel", BOSCH_BMI088 }, | |
| 50 | + { "bmi090l-accel", BOSCH_BMI090L }, | |
| 51 | + {} | |
| 52 | +}; | |
| 53 | +MODULE_DEVICE_TABLE(i2c, bmi088_accel_id); | |
| 54 | + | |
| 55 | +static struct i2c_driver bmi088_accel_driver = { | |
| 56 | + .driver = { | |
| 57 | + .name = "bmi088_accel_i2c", | |
| 58 | + .pm = pm_ptr(&bmi088_accel_pm_ops), | |
| 59 | + .of_match_table = bmi088_of_match, | |
| 60 | + }, | |
| 61 | + .probe = bmi088_accel_probe, | |
| 62 | + .remove = bmi088_accel_remove, | |
| 63 | + .id_table = bmi088_accel_id, | |
| 64 | +}; | |
| 65 | +module_i2c_driver(bmi088_accel_driver); | |
| 66 | + | |
| 67 | +MODULE_AUTHOR("Jun Yan <jerrysteve1101@gmail.com>"); | |
| 68 | +MODULE_LICENSE("GPL"); | |
| 69 | +MODULE_DESCRIPTION("BMI088 accelerometer driver (I2C)"); | |
| 70 | +MODULE_IMPORT_NS(IIO_BMI088); | |
| @@ -23,8 +23,6 @@ · 22 unmodified lines | ||
| 23 | 23 | #define DA280_MODE_ENABLE 0x1e |
| 24 | 24 | #define DA280_MODE_DISABLE 0x9e |
| 25 | 25 | |
| 26 | −enum da280_chipset { da217, da226, da280 }; | |
| 27 | − | |
| 28 | 26 | /* |
| 29 | 27 | * a value of + or -4096 corresponds to + or - 1G |
| 30 | 28 | * scale = 9.81 / 4096 = 0.002395019 |
| @@ -47,6 +45,11 @@ static const struct iio_chan_spec da280_channels[] = { · 16 unmodified lines | ||
| 47 | 45 | DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z), |
| 48 | 46 | }; |
| 49 | 47 | |
| 48 | +struct da280_match_data { | |
| 49 | + const char *name; | |
| 50 | + int num_channels; | |
| 51 | +}; | |
| 52 | + | |
| 50 | 53 | struct da280_data { |
| 51 | 54 | struct i2c_client *client; |
| 52 | 55 | }; |
| @@ -89,17 +92,6 @@ static const struct iio_info da280_info = { · 36 unmodified lines | ||
| 89 | 92 | .read_raw = da280_read_raw, |
| 90 | 93 | }; |
| 91 | 94 | |
| 92 | −static enum da280_chipset da280_match_acpi_device(struct device *dev) | |
| 93 | −{ | |
| 94 | − const struct acpi_device_id *id; | |
| 95 | − | |
| 96 | − id = acpi_match_device(dev->driver->acpi_match_table, dev); | |
| 97 | − if (!id) | |
| 98 | − return -EINVAL; | |
| 99 | − | |
| 100 | − return (enum da280_chipset) id->driver_data; | |
| 101 | −} | |
| 102 | − | |
| 103 | 95 | static void da280_disable(void *client) |
| 104 | 96 | { |
| 105 | 97 | da280_enable(client, false); |
| @@ -107,16 +99,21 @@ static void da280_disable(void *client) · 1 unmodified line | ||
| 107 | 99 | |
| 108 | 100 | static int da280_probe(struct i2c_client *client) |
| 109 | 101 | { |
| 110 | − const struct i2c_device_id *id = i2c_client_get_device_id(client); | |
| 111 | − int ret; | |
| 102 | + const struct da280_match_data *match_data; | |
| 112 | 103 | struct iio_dev *indio_dev; |
| 113 | 104 | struct da280_data *data; |
| 114 | − enum da280_chipset chip; | |
| 105 | + int ret; | |
| 115 | 106 | |
| 116 | 107 | ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID); |
| 117 | 108 | if (ret != DA280_CHIP_ID) |
| 118 | 109 | return (ret < 0) ? ret : -ENODEV; |
| 119 | 110 | |
| 111 | + match_data = i2c_get_match_data(client); | |
| 112 | + if (!match_data) { | |
| 113 | + dev_err(&client->dev, "Error match-data not set\n"); | |
| 114 | + return -EINVAL; | |
| 115 | + } | |
| 116 | + | |
| 120 | 117 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); |
| 121 | 118 | if (!indio_dev) |
| 122 | 119 | return -ENOMEM; |
| @@ -127,23 +124,8 @@ static int da280_probe(struct i2c_client *client) · 4 unmodified lines | ||
| 127 | 124 | indio_dev->info = &da280_info; |
| 128 | 125 | indio_dev->modes = INDIO_DIRECT_MODE; |
| 129 | 126 | indio_dev->channels = da280_channels; |
| 130 | − | |
| 131 | − if (ACPI_HANDLE(&client->dev)) { | |
| 132 | − chip = da280_match_acpi_device(&client->dev); | |
| 133 | − } else { | |
| 134 | − chip = id->driver_data; | |
| 135 | − } | |
| 136 | − | |
| 137 | − if (chip == da217) { | |
| 138 | − indio_dev->name = "da217"; | |
| 139 | − indio_dev->num_channels = 3; | |
| 140 | − } else if (chip == da226) { | |
| 141 | − indio_dev->name = "da226"; | |
| 142 | − indio_dev->num_channels = 2; | |
| 143 | − } else { | |
| 144 | − indio_dev->name = "da280"; | |
| 145 | − indio_dev->num_channels = 3; | |
| 146 | − } | |
| 127 | + indio_dev->num_channels = match_data->num_channels; | |
| 128 | + indio_dev->name = match_data->name; | |
| 147 | 129 | |
| 148 | 130 | ret = da280_enable(client, true); |
| 149 | 131 | if (ret < 0) |
| @@ -168,17 +150,21 @@ static int da280_resume(struct device *dev) · 18 unmodified lines | ||
| 168 | 150 | |
| 169 | 151 | static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); |
| 170 | 152 | |
| 153 | +static const struct da280_match_data da217_match_data = { "da217", 3 }; | |
| 154 | +static const struct da280_match_data da226_match_data = { "da226", 2 }; | |
| 155 | +static const struct da280_match_data da280_match_data = { "da280", 3 }; | |
| 156 | + | |
| 171 | 157 | static const struct acpi_device_id da280_acpi_match[] = { |
| 172 | − {"NSA2513", da217}, | |
| 173 | − {"MIRAACC", da280}, | |
| 174 | − {}, | |
| 158 | + { "NSA2513", (kernel_ulong_t)&da217_match_data }, | |
| 159 | + { "MIRAACC", (kernel_ulong_t)&da280_match_data }, | |
| 160 | + {} | |
| 175 | 161 | }; |
| 176 | 162 | MODULE_DEVICE_TABLE(acpi, da280_acpi_match); |
| 177 | 163 | |
| 178 | 164 | static const struct i2c_device_id da280_i2c_id[] = { |
| 179 | − { "da217", da217 }, | |
| 180 | − { "da226", da226 }, | |
| 181 | − { "da280", da280 }, | |
| 165 | + { "da217", (kernel_ulong_t)&da217_match_data }, | |
| 166 | + { "da226", (kernel_ulong_t)&da226_match_data }, | |
| 167 | + { "da280", (kernel_ulong_t)&da280_match_data }, | |
| 182 | 168 | {} |
| 183 | 169 | }; |
| 184 | 170 | MODULE_DEVICE_TABLE(i2c, da280_i2c_id); |
| @@ -186,7 +172,7 @@ MODULE_DEVICE_TABLE(i2c, da280_i2c_id); · 1 unmodified line | ||
| 186 | 172 | static struct i2c_driver da280_driver = { |
| 187 | 173 | .driver = { |
| 188 | 174 | .name = "da280", |
| 189 | − .acpi_match_table = ACPI_PTR(da280_acpi_match), | |
| 175 | + .acpi_match_table = da280_acpi_match, | |
| 190 | 176 | .pm = pm_sleep_ptr(&da280_pm_ops), |
| 191 | 177 | }, |
| 192 | 178 | .probe = da280_probe, |
| @@ -422,6 +422,23 @@ static int kiox010a_dsm(struct device *dev, int fn_index) · 421 unmodified lines | ||
| 422 | 422 | ACPI_FREE(obj); |
| 423 | 423 | return 0; |
| 424 | 424 | } |
| 425 | + | |
| 426 | +static const struct acpi_device_id kx_acpi_match[] = { | |
| 427 | + {"KXCJ1013", KXCJK1013}, | |
| 428 | + {"KXCJ1008", KXCJ91008}, | |
| 429 | + {"KXCJ9000", KXCJ91008}, | |
| 430 | + {"KIOX0008", KXCJ91008}, | |
| 431 | + {"KIOX0009", KXTJ21009}, | |
| 432 | + {"KIOX000A", KXCJ91008}, | |
| 433 | + {"KIOX010A", KXCJ91008}, /* KXCJ91008 in the display of a yoga 2-in-1 */ | |
| 434 | + {"KIOX020A", KXCJ91008}, /* KXCJ91008 in the base of a yoga 2-in-1 */ | |
| 435 | + {"KXTJ1009", KXTJ21009}, | |
| 436 | + {"KXJ2109", KXTJ21009}, | |
| 437 | + {"SMO8500", KXCJ91008}, | |
| 438 | + { } | |
| 439 | +}; | |
| 440 | +MODULE_DEVICE_TABLE(acpi, kx_acpi_match); | |
| 441 | + | |
| 425 | 442 | #endif |
| 426 | 443 | |
| 427 | 444 | static int kxcjk1013_set_mode(struct kxcjk1013_data *data, |
| @@ -619,6 +636,84 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) · 191 unmodified lines | ||
| 619 | 636 | return 0; |
| 620 | 637 | } |
| 621 | 638 | |
| 639 | +#ifdef CONFIG_ACPI | |
| 640 | +static bool kxj_acpi_orientation(struct device *dev, | |
| 641 | + struct iio_mount_matrix *orientation) | |
| 642 | +{ | |
| 643 | + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | |
| 644 | + struct acpi_device *adev = ACPI_COMPANION(dev); | |
| 645 | + char *str; | |
| 646 | + union acpi_object *obj, *elements; | |
| 647 | + acpi_status status; | |
| 648 | + int i, j, val[3]; | |
| 649 | + bool ret = false; | |
| 650 | + | |
| 651 | + if (!acpi_has_method(adev->handle, "ROTM")) | |
| 652 | + return false; | |
| 653 | + | |
| 654 | + status = acpi_evaluate_object(adev->handle, "ROTM", NULL, &buffer); | |
| 655 | + if (ACPI_FAILURE(status)) { | |
| 656 | + dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status); | |
| 657 | + return false; | |
| 658 | + } | |
| 659 | + | |
| 660 | + obj = buffer.pointer; | |
| 661 | + if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) { | |
| 662 | + dev_err(dev, "Unknown ACPI mount matrix package format\n"); | |
| 663 | + goto out_free_buffer; | |
| 664 | + } | |
| 665 | + | |
| 666 | + elements = obj->package.elements; | |
| 667 | + for (i = 0; i < 3; i++) { | |
| 668 | + if (elements[i].type != ACPI_TYPE_STRING) { | |
| 669 | + dev_err(dev, "Unknown ACPI mount matrix element format\n"); | |
| 670 | + goto out_free_buffer; | |
| 671 | + } | |
| 672 | + | |
| 673 | + str = elements[i].string.pointer; | |
| 674 | + if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) { | |
| 675 | + dev_err(dev, "Incorrect ACPI mount matrix string format\n"); | |
| 676 | + goto out_free_buffer; | |
| 677 | + } | |
| 678 | + | |
| 679 | + for (j = 0; j < 3; j++) { | |
| 680 | + switch (val[j]) { | |
| 681 | + case -1: str = "-1"; break; | |
| 682 | + case 0: str = "0"; break; | |
| 683 | + case 1: str = "1"; break; | |
| 684 | + default: | |
| 685 | + dev_err(dev, "Invalid value in ACPI mount matrix: %d\n", val[j]); | |
| 686 | + goto out_free_buffer; | |
| 687 | + } | |
| 688 | + orientation->rotation[i * 3 + j] = str; | |
| 689 | + } | |
| 690 | + } | |
| 691 | + | |
| 692 | + ret = true; | |
| 693 | + | |
| 694 | +out_free_buffer: | |
| 695 | + kfree(buffer.pointer); | |
| 696 | + return ret; | |
| 697 | +} | |
| 698 | + | |
| 699 | +static bool kxj1009_apply_acpi_orientation(struct device *dev, | |
| 700 | + struct iio_mount_matrix *orientation) | |
| 701 | +{ | |
| 702 | + struct acpi_device *adev = ACPI_COMPANION(dev); | |
| 703 | + | |
| 704 | + if (adev && acpi_dev_hid_uid_match(adev, "KIOX000A", NULL)) | |
| 705 | + return kxj_acpi_orientation(dev, orientation); | |
| 706 | + | |
| 707 | + return false; | |
| 708 | +} | |
| 709 | +#else | |
| 710 | +static bool kxj1009_apply_acpi_orientation(struct device *dev, | |
| 711 | + struct iio_mount_matrix *orientation) | |
| 712 | +{ | |
| 713 | + return false; | |
| 714 | +} | |
| 715 | +#endif | |
| 716 | + | |
| 622 | 717 | static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) |
| 623 | 718 | { |
| 624 | 719 | int ret; |
| @@ -1449,9 +1544,12 @@ static int kxcjk1013_probe(struct i2c_client *client) · 824 unmodified lines | ||
| 1449 | 1544 | } else { |
| 1450 | 1545 | data->active_high_intr = true; /* default polarity */ |
| 1451 | 1546 | |
| 1452 | − ret = iio_read_mount_matrix(&client->dev, &data->orientation); | |
| 1453 | − if (ret) | |
| 1454 | − return ret; | |
| 1547 | + if (!kxj1009_apply_acpi_orientation(&client->dev, &data->orientation)) { | |
| 1548 | + ret = iio_read_mount_matrix(&client->dev, &data->orientation); | |
| 1549 | + if (ret) | |
| 1550 | + return ret; | |
| 1551 | + } | |
| 1552 | + | |
| 1455 | 1553 | } |
| 1456 | 1554 | |
| 1457 | 1555 | ret = devm_regulator_bulk_get_enable(&client->dev, |
| @@ -1687,22 +1785,6 @@ static const struct dev_pm_ops kxcjk1013_pm_ops = { · 229 unmodified lines | ||
| 1687 | 1785 | kxcjk1013_runtime_resume, NULL) |
| 1688 | 1786 | }; |
| 1689 | 1787 | |
| 1690 | −static const struct acpi_device_id kx_acpi_match[] = { | |
| 1691 | − {"KXCJ1013", KXCJK1013}, | |
| 1692 | − {"KXCJ1008", KXCJ91008}, | |
| 1693 | − {"KXCJ9000", KXCJ91008}, | |
| 1694 | − {"KIOX0008", KXCJ91008}, | |
| 1695 | − {"KIOX0009", KXTJ21009}, | |
| 1696 | − {"KIOX000A", KXCJ91008}, | |
| 1697 | − {"KIOX010A", KXCJ91008}, /* KXCJ91008 in the display of a yoga 2-in-1 */ | |
| 1698 | − {"KIOX020A", KXCJ91008}, /* KXCJ91008 in the base of a yoga 2-in-1 */ | |
| 1699 | − {"KXTJ1009", KXTJ21009}, | |
| 1700 | − {"KXJ2109", KXTJ21009}, | |
| 1701 | − {"SMO8500", KXCJ91008}, | |
| 1702 | − { }, | |
| 1703 | −}; | |
| 1704 | −MODULE_DEVICE_TABLE(acpi, kx_acpi_match); | |
| 1705 | − | |
| 1706 | 1788 | static const struct i2c_device_id kxcjk1013_id[] = { |
| 1707 | 1789 | {"kxcjk1013", KXCJK1013}, |
| 1708 | 1790 | {"kxcj91008", KXCJ91008}, |
| @@ -1,9 +1,9 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | #include <linux/device.h> |
| 3 | 3 | #include <linux/kernel.h> |
| 4 | −#include <linux/of.h> | |
| 5 | 4 | #include <linux/spi/spi.h> |
| 6 | 5 | #include <linux/module.h> |
| 6 | +#include <linux/mod_devicetable.h> | |
| 7 | 7 | #include <linux/slab.h> |
| 8 | 8 | #include <linux/regmap.h> |
| 9 | 9 | |
| @@ -604,9 +604,9 @@ MODULE_DEVICE_TABLE(i2c, mma9551_id); · 603 unmodified lines | ||
| 604 | 604 | static struct i2c_driver mma9551_driver = { |
| 605 | 605 | .driver = { |
| 606 | 606 | .name = MMA9551_DRV_NAME, |
| 607 | − .acpi_match_table = ACPI_PTR(mma9551_acpi_match), | |
| 607 | + .acpi_match_table = mma9551_acpi_match, | |
| 608 | 608 | .pm = pm_ptr(&mma9551_pm_ops), |
| 609 | − }, | |
| 609 | + }, | |
| 610 | 610 | .probe = mma9551_probe, |
| 611 | 611 | .remove = mma9551_remove, |
| 612 | 612 | .id_table = mma9551_id, |
| @@ -1243,9 +1243,9 @@ MODULE_DEVICE_TABLE(i2c, mma9553_id); · 1242 unmodified lines | ||
| 1243 | 1243 | static struct i2c_driver mma9553_driver = { |
| 1244 | 1244 | .driver = { |
| 1245 | 1245 | .name = MMA9553_DRV_NAME, |
| 1246 | − .acpi_match_table = ACPI_PTR(mma9553_acpi_match), | |
| 1246 | + .acpi_match_table = mma9553_acpi_match, | |
| 1247 | 1247 | .pm = pm_ptr(&mma9553_pm_ops), |
| 1248 | − }, | |
| 1248 | + }, | |
| 1249 | 1249 | .probe = mma9553_probe, |
| 1250 | 1250 | .remove = mma9553_remove, |
| 1251 | 1251 | .id_table = mma9553_id, |
| @@ -8,7 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | #include <linux/module.h> |
| 9 | 9 | #include <linux/i2c.h> |
| 10 | 10 | #include <linux/iio/iio.h> |
| 11 | −#include <linux/acpi.h> | |
| 11 | +#include <linux/mod_devicetable.h> | |
| 12 | 12 | #include <linux/regmap.h> |
| 13 | 13 | #include <linux/iio/sysfs.h> |
| 14 | 14 | #include <linux/iio/trigger.h> |
| @@ -472,6 +472,7 @@ static int mxc4005_probe(struct i2c_client *client) · 457 unmodified lines | ||
| 472 | 472 | static const struct acpi_device_id mxc4005_acpi_match[] = { |
| 473 | 473 | {"MXC4005", 0}, |
| 474 | 474 | {"MXC6655", 0}, |
| 475 | + {"MDA6655", 0}, | |
| 475 | 476 | { }, |
| 476 | 477 | }; |
| 477 | 478 | MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match); |
| @@ -493,7 +494,7 @@ MODULE_DEVICE_TABLE(i2c, mxc4005_id); · 15 unmodified lines | ||
| 493 | 494 | static struct i2c_driver mxc4005_driver = { |
| 494 | 495 | .driver = { |
| 495 | 496 | .name = MXC4005_DRV_NAME, |
| 496 | − .acpi_match_table = ACPI_PTR(mxc4005_acpi_match), | |
| 497 | + .acpi_match_table = mxc4005_acpi_match, | |
| 497 | 498 | .of_match_table = mxc4005_of_match, |
| 498 | 499 | }, |
| 499 | 500 | .probe = mxc4005_probe, |
| @@ -12,7 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <linux/init.h> |
| 13 | 13 | #include <linux/iio/iio.h> |
| 14 | 14 | #include <linux/delay.h> |
| 15 | −#include <linux/acpi.h> | |
| 15 | +#include <linux/mod_devicetable.h> | |
| 16 | 16 | #include <linux/regmap.h> |
| 17 | 17 | #include <linux/iio/sysfs.h> |
| 18 | 18 | |
| @@ -181,7 +181,7 @@ MODULE_DEVICE_TABLE(i2c, mxc6255_id); · 162 unmodified lines | ||
| 181 | 181 | static struct i2c_driver mxc6255_driver = { |
| 182 | 182 | .driver = { |
| 183 | 183 | .name = MXC6255_DRV_NAME, |
| 184 | − .acpi_match_table = ACPI_PTR(mxc6255_acpi_match), | |
| 184 | + .acpi_match_table = mxc6255_acpi_match, | |
| 185 | 185 | }, |
| 186 | 186 | .probe = mxc6255_probe, |
| 187 | 187 | .id_table = mxc6255_id, |
| @@ -10,7 +10,6 @@ · 9 unmodified lines | ||
| 10 | 10 | #include <linux/kernel.h> |
| 11 | 11 | #include <linux/module.h> |
| 12 | 12 | #include <linux/mod_devicetable.h> |
| 13 | −#include <linux/acpi.h> | |
| 14 | 13 | #include <linux/i2c.h> |
| 15 | 14 | #include <linux/iio/iio.h> |
| 16 | 15 | |
| @@ -127,14 +126,12 @@ static const struct of_device_id st_accel_of_match[] = { · 110 unmodified lines | ||
| 127 | 126 | }; |
| 128 | 127 | MODULE_DEVICE_TABLE(of, st_accel_of_match); |
| 129 | 128 | |
| 130 | −#ifdef CONFIG_ACPI | |
| 131 | 129 | static const struct acpi_device_id st_accel_acpi_match[] = { |
| 132 | 130 | {"SMO8840", (kernel_ulong_t)LIS2DH12_ACCEL_DEV_NAME}, |
| 133 | 131 | {"SMO8A90", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME}, |
| 134 | 132 | { }, |
| 135 | 133 | }; |
| 136 | 134 | MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); |
| 137 | −#endif | |
| 138 | 135 | |
| 139 | 136 | static const struct i2c_device_id st_accel_id_table[] = { |
| 140 | 137 | { LSM303DLH_ACCEL_DEV_NAME }, |
| @@ -204,7 +201,7 @@ static struct i2c_driver st_accel_driver = { · 63 unmodified lines | ||
| 204 | 201 | .driver = { |
| 205 | 202 | .name = "st-accel-i2c", |
| 206 | 203 | .of_match_table = st_accel_of_match, |
| 207 | − .acpi_match_table = ACPI_PTR(st_accel_acpi_match), | |
| 204 | + .acpi_match_table = st_accel_acpi_match, | |
| 208 | 205 | }, |
| 209 | 206 | .probe = st_accel_i2c_probe, |
| 210 | 207 | .id_table = st_accel_id_table, |
| @@ -7,11 +7,11 @@ · 6 unmodified lines | ||
| 7 | 7 | * STK8BA50 7-bit I2C address: 0x18. |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | −#include <linux/acpi.h> | |
| 11 | 10 | #include <linux/i2c.h> |
| 12 | 11 | #include <linux/interrupt.h> |
| 13 | 12 | #include <linux/kernel.h> |
| 14 | 13 | #include <linux/module.h> |
| 14 | +#include <linux/mod_devicetable.h> | |
| 15 | 15 | #include <linux/iio/buffer.h> |
| 16 | 16 | #include <linux/iio/iio.h> |
| 17 | 17 | #include <linux/iio/sysfs.h> |
| @@ -541,7 +541,7 @@ static struct i2c_driver stk8ba50_driver = { · 523 unmodified lines | ||
| 541 | 541 | .driver = { |
| 542 | 542 | .name = "stk8ba50", |
| 543 | 543 | .pm = pm_sleep_ptr(&stk8ba50_pm_ops), |
| 544 | − .acpi_match_table = ACPI_PTR(stk8ba50_acpi_id), | |
| 544 | + .acpi_match_table = stk8ba50_acpi_id, | |
| 545 | 545 | }, |
| 546 | 546 | .probe = stk8ba50_probe, |
| 547 | 547 | .remove = stk8ba50_remove, |
| @@ -291,7 +291,7 @@ config AD799X · 290 unmodified lines | ||
| 291 | 291 | config AD9467 |
| 292 | 292 | tristate "Analog Devices AD9467 High Speed ADC driver" |
| 293 | 293 | depends on SPI |
| 294 | − depends on ADI_AXI_ADC | |
| 294 | + select IIO_BACKEND | |
| 295 | 295 | help |
| 296 | 296 | Say yes here to build support for Analog Devices: |
| 297 | 297 | * AD9467 16-Bit, 200 MSPS/250 MSPS Analog-to-Digital Converter |
| @@ -309,7 +309,7 @@ config ADI_AXI_ADC · 11 unmodified lines | ||
| 309 | 309 | select IIO_BUFFER_HW_CONSUMER |
| 310 | 310 | select IIO_BUFFER_DMAENGINE |
| 311 | 311 | select REGMAP_MMIO |
| 312 | − depends on OF | |
| 312 | + select IIO_BACKEND | |
| 313 | 313 | help |
| 314 | 314 | Say yes here to build support for Analog Devices Generic |
| 315 | 315 | AXI ADC IP core. The IP core is used for interfacing with |
| @@ -930,6 +930,17 @@ config NPCM_ADC · 614 unmodified lines | ||
| 930 | 930 | This driver can also be built as a module. If so, the module |
| 931 | 931 | will be called npcm_adc. |
| 932 | 932 | |
| 933 | +config PAC1934 | |
| 934 | + tristate "Microchip Technology PAC1934 driver" | |
| 935 | + depends on I2C | |
| 936 | + help | |
| 937 | + Say yes here to build support for Microchip Technology's PAC1931, | |
| 938 | + PAC1932, PAC1933, PAC1934 Single/Multi-Channel Power Monitor with | |
| 939 | + Accumulator. | |
| 940 | + | |
| 941 | + This driver can also be built as a module. If so, the module | |
| 942 | + will be called pac1934. | |
| 943 | + | |
| 933 | 944 | config PALMAS_GPADC |
| 934 | 945 | tristate "TI Palmas General Purpose ADC" |
| 935 | 946 | depends on MFD_PALMAS |
| @@ -1312,6 +1323,17 @@ config TI_ADS1100 · 376 unmodified lines | ||
| 1312 | 1323 | This driver can also be built as a module. If so, the module will be |
| 1313 | 1324 | called ti-ads1100. |
| 1314 | 1325 | |
| 1326 | +config TI_ADS1298 | |
| 1327 | + tristate "Texas Instruments ADS1298" | |
| 1328 | + depends on SPI | |
| 1329 | + select IIO_BUFFER | |
| 1330 | + help | |
| 1331 | + If you say yes here you get support for Texas Instruments ADS1298 | |
| 1332 | + medical ADC chips | |
| 1333 | + | |
| 1334 | + This driver can also be built as a module. If so, the module will be | |
| 1335 | + called ti-ads1298. | |
| 1336 | + | |
| 1315 | 1337 | config TI_ADS7950 |
| 1316 | 1338 | tristate "Texas Instruments ADS7950 ADC driver" |
| 1317 | 1339 | depends on SPI && GPIOLIB |
| @@ -86,6 +86,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o · 85 unmodified lines | ||
| 86 | 86 | obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o |
| 87 | 87 | obj-$(CONFIG_NAU7802) += nau7802.o |
| 88 | 88 | obj-$(CONFIG_NPCM_ADC) += npcm_adc.o |
| 89 | +obj-$(CONFIG_PAC1934) += pac1934.o | |
| 89 | 90 | obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o |
| 90 | 91 | obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o |
| 91 | 92 | obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o |
| @@ -116,6 +117,7 @@ obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o · 24 unmodified lines | ||
| 116 | 117 | obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o |
| 117 | 118 | obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o |
| 118 | 119 | obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o |
| 120 | +obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o | |
| 119 | 121 | obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o |
| 120 | 122 | obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o |
| 121 | 123 | obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o |
| @@ -887,9 +887,9 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev, · 886 unmodified lines | ||
| 887 | 887 | unsigned int old_fs; |
| 888 | 888 | int ret = 0; |
| 889 | 889 | |
| 890 | − mutex_lock(&st->lock); | |
| 890 | + guard(mutex)(&st->lock); | |
| 891 | 891 | if (setup_info->filter_mode == val) |
| 892 | − goto out; | |
| 892 | + return 0; | |
| 893 | 893 | |
| 894 | 894 | old_fs = setup_info->fs; |
| 895 | 895 | old_filter_mode = setup_info->filter_mode; |
| @@ -911,12 +911,10 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev, · 15 unmodified lines | ||
| 911 | 911 | if (ret) { |
| 912 | 912 | setup_info->fs = old_fs; |
| 913 | 913 | setup_info->filter_mode = old_filter_mode; |
| 914 | + return ret; | |
| 914 | 915 | } |
| 915 | 916 | |
| 916 | − out: | |
| 917 | − mutex_unlock(&st->lock); | |
| 918 | − | |
| 919 | − return ret; | |
| 917 | + return 0; | |
| 920 | 918 | } |
| 921 | 919 | |
| 922 | 920 | static int ad4130_get_filter_mode(struct iio_dev *indio_dev, |
| @@ -927,9 +925,8 @@ static int ad4130_get_filter_mode(struct iio_dev *indio_dev, · 4 unmodified lines | ||
| 927 | 925 | struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup; |
| 928 | 926 | enum ad4130_filter_mode filter_mode; |
| 929 | 927 | |
| 930 | − mutex_lock(&st->lock); | |
| 928 | + guard(mutex)(&st->lock); | |
| 931 | 929 | filter_mode = setup_info->filter_mode; |
| 932 | − mutex_unlock(&st->lock); | |
| 933 | 930 | |
| 934 | 931 | return filter_mode; |
| 935 | 932 | } |
| @@ -971,7 +968,7 @@ static int ad4130_set_channel_pga(struct ad4130_state *st, unsigned int channel, · 35 unmodified lines | ||
| 971 | 968 | struct ad4130_chan_info *chan_info = &st->chans_info[channel]; |
| 972 | 969 | struct ad4130_setup_info *setup_info = &chan_info->setup; |
| 973 | 970 | unsigned int pga, old_pga; |
| 974 | − int ret = 0; | |
| 971 | + int ret; | |
| 975 | 972 | |
| 976 | 973 | for (pga = 0; pga < AD4130_MAX_PGA; pga++) |
| 977 | 974 | if (val == st->scale_tbls[setup_info->ref_sel][pga][0] && |
| @@ -981,21 +978,20 @@ static int ad4130_set_channel_pga(struct ad4130_state *st, unsigned int channel, · 3 unmodified lines | ||
| 981 | 978 | if (pga == AD4130_MAX_PGA) |
| 982 | 979 | return -EINVAL; |
| 983 | 980 | |
| 984 | − mutex_lock(&st->lock); | |
| 981 | + guard(mutex)(&st->lock); | |
| 985 | 982 | if (pga == setup_info->pga) |
| 986 | − goto out; | |
| 983 | + return 0; | |
| 987 | 984 | |
| 988 | 985 | old_pga = setup_info->pga; |
| 989 | 986 | setup_info->pga = pga; |
| 990 | 987 | |
| 991 | 988 | ret = ad4130_write_channel_setup(st, channel, false); |
| 992 | − if (ret) | |
| 989 | + if (ret) { | |
| 993 | 990 | setup_info->pga = old_pga; |
| 991 | + return ret; | |
| 992 | + } | |
| 994 | 993 | |
| 995 | −out: | |
| 996 | − mutex_unlock(&st->lock); | |
| 997 | − | |
| 998 | − return ret; | |
| 994 | + return 0; | |
| 999 | 995 | } |
| 1000 | 996 | |
| 1001 | 997 | static int ad4130_set_channel_freq(struct ad4130_state *st, |
| @@ -1004,26 +1000,25 @@ static int ad4130_set_channel_freq(struct ad4130_state *st, · 2 unmodified lines | ||
| 1004 | 1000 | struct ad4130_chan_info *chan_info = &st->chans_info[channel]; |
| 1005 | 1001 | struct ad4130_setup_info *setup_info = &chan_info->setup; |
| 1006 | 1002 | unsigned int fs, old_fs; |
| 1007 | − int ret = 0; | |
| 1003 | + int ret; | |
| 1008 | 1004 | |
| 1009 | − mutex_lock(&st->lock); | |
| 1005 | + guard(mutex)(&st->lock); | |
| 1010 | 1006 | old_fs = setup_info->fs; |
| 1011 | 1007 | |
| 1012 | 1008 | ad4130_freq_to_fs(setup_info->filter_mode, val, val2, &fs); |
| 1013 | 1009 | |
| 1014 | 1010 | if (fs == setup_info->fs) |
| 1015 | − goto out; | |
| 1011 | + return 0; | |
| 1016 | 1012 | |
| 1017 | 1013 | setup_info->fs = fs; |
| 1018 | 1014 | |
| 1019 | 1015 | ret = ad4130_write_channel_setup(st, channel, false); |
| 1020 | − if (ret) | |
| 1016 | + if (ret) { | |
| 1021 | 1017 | setup_info->fs = old_fs; |
| 1018 | + return ret; | |
| 1019 | + } | |
| 1022 | 1020 | |
| 1023 | −out: | |
| 1024 | − mutex_unlock(&st->lock); | |
| 1025 | − | |
| 1026 | − return ret; | |
| 1021 | + return 0; | |
| 1027 | 1022 | } |
| 1028 | 1023 | |
| 1029 | 1024 | static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel, |
| @@ -1065,20 +1060,13 @@ static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel, · 35 unmodified lines | ||
| 1065 | 1060 | static int ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel, |
| 1066 | 1061 | int *val) |
| 1067 | 1062 | { |
| 1068 | − struct ad4130_state *st = iio_priv(indio_dev); | |
| 1069 | − int ret; | |
| 1070 | − | |
| 1071 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 1072 | − if (ret) | |
| 1073 | − return ret; | |
| 1063 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 1064 | + struct ad4130_state *st = iio_priv(indio_dev); | |
| 1074 | 1065 | |
| 1075 | − mutex_lock(&st->lock); | |
| 1076 | − ret = _ad4130_read_sample(indio_dev, channel, val); | |
| 1077 | − mutex_unlock(&st->lock); | |
| 1078 | − | |
| 1079 | − iio_device_release_direct_mode(indio_dev); | |
| 1080 | − | |
| 1081 | − return ret; | |
| 1066 | + guard(mutex)(&st->lock); | |
| 1067 | + return _ad4130_read_sample(indio_dev, channel, val); | |
| 1068 | + } | |
| 1069 | + unreachable(); | |
| 1082 | 1070 | } |
| 1083 | 1071 | |
| 1084 | 1072 | static int ad4130_read_raw(struct iio_dev *indio_dev, |
| @@ -1092,24 +1080,24 @@ static int ad4130_read_raw(struct iio_dev *indio_dev, · 7 unmodified lines | ||
| 1092 | 1080 | switch (info) { |
| 1093 | 1081 | case IIO_CHAN_INFO_RAW: |
| 1094 | 1082 | return ad4130_read_sample(indio_dev, channel, val); |
| 1095 | − case IIO_CHAN_INFO_SCALE: | |
| 1096 | − mutex_lock(&st->lock); | |
| 1083 | + case IIO_CHAN_INFO_SCALE: { | |
| 1084 | + guard(mutex)(&st->lock); | |
| 1097 | 1085 | *val = st->scale_tbls[setup_info->ref_sel][setup_info->pga][0]; |
| 1098 | 1086 | *val2 = st->scale_tbls[setup_info->ref_sel][setup_info->pga][1]; |
| 1099 | − mutex_unlock(&st->lock); | |
| 1100 | 1087 | |
| 1101 | 1088 | return IIO_VAL_INT_PLUS_NANO; |
| 1089 | + } | |
| 1102 | 1090 | case IIO_CHAN_INFO_OFFSET: |
| 1103 | 1091 | *val = st->bipolar ? -BIT(chan->scan_type.realbits - 1) : 0; |
| 1104 | 1092 | |
| 1105 | 1093 | return IIO_VAL_INT; |
| 1106 | − case IIO_CHAN_INFO_SAMP_FREQ: | |
| 1107 | − mutex_lock(&st->lock); | |
| 1094 | + case IIO_CHAN_INFO_SAMP_FREQ: { | |
| 1095 | + guard(mutex)(&st->lock); | |
| 1108 | 1096 | ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs, |
| 1109 | 1097 | val, val2); |
| 1110 | − mutex_unlock(&st->lock); | |
| 1111 | 1098 | |
| 1112 | 1099 | return IIO_VAL_INT_PLUS_NANO; |
| 1100 | + } | |
| 1113 | 1101 | default: |
| 1114 | 1102 | return -EINVAL; |
| 1115 | 1103 | } |
| @@ -1134,9 +1122,9 @@ static int ad4130_read_avail(struct iio_dev *indio_dev, · 18 unmodified lines | ||
| 1134 | 1122 | |
| 1135 | 1123 | return IIO_AVAIL_LIST; |
| 1136 | 1124 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 1137 | − mutex_lock(&st->lock); | |
| 1138 | − filter_config = &ad4130_filter_configs[setup_info->filter_mode]; | |
| 1139 | − mutex_unlock(&st->lock); | |
| 1125 | + scoped_guard(mutex, &st->lock) { | |
| 1126 | + filter_config = &ad4130_filter_configs[setup_info->filter_mode]; | |
| 1127 | + } | |
| 1140 | 1128 | |
| 1141 | 1129 | *vals = (int *)filter_config->samp_freq_avail; |
| 1142 | 1130 | *length = filter_config->samp_freq_avail_len * 2; |
| @@ -1197,21 +1185,18 @@ static int ad4130_update_scan_mode(struct iio_dev *indio_dev, · 54 unmodified lines | ||
| 1197 | 1185 | unsigned int val = 0; |
| 1198 | 1186 | int ret; |
| 1199 | 1187 | |
| 1200 | − mutex_lock(&st->lock); | |
| 1188 | + guard(mutex)(&st->lock); | |
| 1201 | 1189 | |
| 1202 | 1190 | for_each_set_bit(channel, scan_mask, indio_dev->num_channels) { |
| 1203 | 1191 | ret = ad4130_set_channel_enable(st, channel, true); |
| 1204 | 1192 | if (ret) |
| 1205 | − goto out; | |
| 1193 | + return ret; | |
| 1206 | 1194 | |
| 1207 | 1195 | val++; |
| 1208 | 1196 | } |
| 1209 | 1197 | |
| 1210 | 1198 | st->num_enabled_channels = val; |
| 1211 | 1199 | |
| 1212 | −out: | |
| 1213 | − mutex_unlock(&st->lock); | |
| 1214 | − | |
| 1215 | 1200 | return 0; |
| 1216 | 1201 | } |
| 1217 | 1202 | |
| @@ -1232,22 +1217,19 @@ static int ad4130_set_fifo_watermark(struct iio_dev *indio_dev, unsigned int val · 14 unmodified lines | ||
| 1232 | 1217 | */ |
| 1233 | 1218 | eff = rounddown(AD4130_FIFO_SIZE, st->num_enabled_channels); |
| 1234 | 1219 | |
| 1235 | − mutex_lock(&st->lock); | |
| 1220 | + guard(mutex)(&st->lock); | |
| 1236 | 1221 | |
| 1237 | 1222 | ret = regmap_update_bits(st->regmap, AD4130_FIFO_CONTROL_REG, |
| 1238 | 1223 | AD4130_FIFO_CONTROL_WM_MASK, |
| 1239 | 1224 | FIELD_PREP(AD4130_FIFO_CONTROL_WM_MASK, |
| 1240 | 1225 | ad4130_watermark_reg_val(eff))); |
| 1241 | 1226 | if (ret) |
| 1242 | − goto out; | |
| 1227 | + return ret; | |
| 1243 | 1228 | |
| 1244 | 1229 | st->effective_watermark = eff; |
| 1245 | 1230 | st->watermark = val; |
| 1246 | 1231 | |
| 1247 | −out: | |
| 1248 | − mutex_unlock(&st->lock); | |
| 1249 | − | |
| 1250 | − return ret; | |
| 1232 | + return 0; | |
| 1251 | 1233 | } |
| 1252 | 1234 | |
| 1253 | 1235 | static const struct iio_info ad4130_info = { |
| @@ -1265,26 +1247,21 @@ static int ad4130_buffer_postenable(struct iio_dev *indio_dev) · 11 unmodified lines | ||
| 1265 | 1247 | struct ad4130_state *st = iio_priv(indio_dev); |
| 1266 | 1248 | int ret; |
| 1267 | 1249 | |
| 1268 | − mutex_lock(&st->lock); | |
| 1250 | + guard(mutex)(&st->lock); | |
| 1269 | 1251 | |
| 1270 | 1252 | ret = ad4130_set_watermark_interrupt_en(st, true); |
| 1271 | 1253 | if (ret) |
| 1272 | − goto out; | |
| 1254 | + return ret; | |
| 1273 | 1255 | |
| 1274 | 1256 | ret = irq_set_irq_type(st->spi->irq, st->inv_irq_trigger); |
| 1275 | 1257 | if (ret) |
| 1276 | − goto out; | |
| 1258 | + return ret; | |
| 1277 | 1259 | |
| 1278 | 1260 | ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_WM); |
| 1279 | 1261 | if (ret) |
| 1280 | − goto out; | |
| 1281 | − | |
| 1282 | − ret = ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); | |
| 1283 | − | |
| 1284 | −out: | |
| 1285 | − mutex_unlock(&st->lock); | |
| 1262 | + return ret; | |
| 1286 | 1263 | |
| 1287 | − return ret; | |
| 1264 | + return ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); | |
| 1288 | 1265 | } |
| 1289 | 1266 | |
| 1290 | 1267 | static int ad4130_buffer_predisable(struct iio_dev *indio_dev) |
| @@ -1293,23 +1270,23 @@ static int ad4130_buffer_predisable(struct iio_dev *indio_dev) · 2 unmodified lines | ||
| 1293 | 1270 | unsigned int i; |
| 1294 | 1271 | int ret; |
| 1295 | 1272 | |
| 1296 | − mutex_lock(&st->lock); | |
| 1273 | + guard(mutex)(&st->lock); | |
| 1297 | 1274 | |
| 1298 | 1275 | ret = ad4130_set_mode(st, AD4130_MODE_IDLE); |
| 1299 | 1276 | if (ret) |
| 1300 | − goto out; | |
| 1277 | + return ret; | |
| 1301 | 1278 | |
| 1302 | 1279 | ret = irq_set_irq_type(st->spi->irq, st->irq_trigger); |
| 1303 | 1280 | if (ret) |
| 1304 | − goto out; | |
| 1281 | + return ret; | |
| 1305 | 1282 | |
| 1306 | 1283 | ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_DISABLED); |
| 1307 | 1284 | if (ret) |
| 1308 | − goto out; | |
| 1285 | + return ret; | |
| 1309 | 1286 | |
| 1310 | 1287 | ret = ad4130_set_watermark_interrupt_en(st, false); |
| 1311 | 1288 | if (ret) |
| 1312 | − goto out; | |
| 1289 | + return ret; | |
| 1313 | 1290 | |
| 1314 | 1291 | /* |
| 1315 | 1292 | * update_scan_mode() is not called in the disable path, disable all |
| @@ -1318,13 +1295,10 @@ static int ad4130_buffer_predisable(struct iio_dev *indio_dev) · 2 unmodified lines | ||
| 1318 | 1295 | for (i = 0; i < indio_dev->num_channels; i++) { |
| 1319 | 1296 | ret = ad4130_set_channel_enable(st, i, false); |
| 1320 | 1297 | if (ret) |
| 1321 | − goto out; | |
| 1298 | + return ret; | |
| 1322 | 1299 | } |
| 1323 | 1300 | |
| 1324 | −out: | |
| 1325 | − mutex_unlock(&st->lock); | |
| 1326 | − | |
| 1327 | − return ret; | |
| 1301 | + return 0; | |
| 1328 | 1302 | } |
| 1329 | 1303 | |
| 1330 | 1304 | static const struct iio_buffer_setup_ops ad4130_buffer_ops = { |
| @@ -1338,9 +1312,8 @@ static ssize_t hwfifo_watermark_show(struct device *dev, · 7 unmodified lines | ||
| 1338 | 1312 | struct ad4130_state *st = iio_priv(dev_to_iio_dev(dev)); |
| 1339 | 1313 | unsigned int val; |
| 1340 | 1314 | |
| 1341 | − mutex_lock(&st->lock); | |
| 1315 | + guard(mutex)(&st->lock); | |
| 1342 | 1316 | val = st->watermark; |
| 1343 | − mutex_unlock(&st->lock); | |
| 1344 | 1317 | |
| 1345 | 1318 | return sysfs_emit(buf, "%d\n", val); |
| 1346 | 1319 | } |
| @@ -86,28 +86,25 @@ static int ad7091r_read_raw(struct iio_dev *iio_dev, · 85 unmodified lines | ||
| 86 | 86 | unsigned int read_val; |
| 87 | 87 | int ret; |
| 88 | 88 | |
| 89 | − mutex_lock(&st->lock); | |
| 89 | + guard(mutex)(&st->lock); | |
| 90 | 90 | |
| 91 | 91 | switch (m) { |
| 92 | 92 | case IIO_CHAN_INFO_RAW: |
| 93 | − if (st->mode != AD7091R_MODE_COMMAND) { | |
| 94 | − ret = -EBUSY; | |
| 95 | − goto unlock; | |
| 96 | − } | |
| 93 | + if (st->mode != AD7091R_MODE_COMMAND) | |
| 94 | + return -EBUSY; | |
| 97 | 95 | |
| 98 | 96 | ret = ad7091r_read_one(iio_dev, chan->channel, &read_val); |
| 99 | 97 | if (ret) |
| 100 | − goto unlock; | |
| 98 | + return ret; | |
| 101 | 99 | |
| 102 | 100 | *val = read_val; |
| 103 | − ret = IIO_VAL_INT; | |
| 104 | − break; | |
| 101 | + return IIO_VAL_INT; | |
| 105 | 102 | |
| 106 | 103 | case IIO_CHAN_INFO_SCALE: |
| 107 | 104 | if (st->vref) { |
| 108 | 105 | ret = regulator_get_voltage(st->vref); |
| 109 | 106 | if (ret < 0) |
| 110 | − goto unlock; | |
| 107 | + return ret; | |
| 111 | 108 | |
| 112 | 109 | *val = ret / 1000; |
| 113 | 110 | } else { |
| @@ -115,17 +112,11 @@ static int ad7091r_read_raw(struct iio_dev *iio_dev, · 1 unmodified line | ||
| 115 | 112 | } |
| 116 | 113 | |
| 117 | 114 | *val2 = chan->scan_type.realbits; |
| 118 | − ret = IIO_VAL_FRACTIONAL_LOG2; | |
| 119 | − break; | |
| 115 | + return IIO_VAL_FRACTIONAL_LOG2; | |
| 120 | 116 | |
| 121 | 117 | default: |
| 122 | − ret = -EINVAL; | |
| 123 | − break; | |
| 118 | + return -EINVAL; | |
| 124 | 119 | } |
| 125 | − | |
| 126 | −unlock: | |
| 127 | − mutex_unlock(&st->lock); | |
| 128 | − return ret; | |
| 129 | 120 | } |
| 130 | 121 | |
| 131 | 122 | static int ad7091r_read_event_config(struct iio_dev *indio_dev, |
| @@ -17,13 +17,12 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <linux/of.h> |
| 18 | 18 | |
| 19 | 19 | |
| 20 | +#include <linux/iio/backend.h> | |
| 20 | 21 | #include <linux/iio/iio.h> |
| 21 | 22 | #include <linux/iio/sysfs.h> |
| 22 | 23 | |
| 23 | 24 | #include <linux/clk.h> |
| 24 | 25 | |
| 25 | −#include <linux/iio/adc/adi-axi-adc.h> | |
| 26 | − | |
| 27 | 26 | /* |
| 28 | 27 | * ADI High-Speed ADC common spi interface registers |
| 29 | 28 | * See Application-Note AN-877: |
| @@ -102,15 +101,20 @@ · 72 unmodified lines | ||
| 102 | 101 | #define AD9467_REG_VREF_MASK 0x0F |
| 103 | 102 | |
| 104 | 103 | struct ad9467_chip_info { |
| 105 | − struct adi_axi_adc_chip_info axi_adc_info; | |
| 106 | − unsigned int default_output_mode; | |
| 107 | − unsigned int vref_mask; | |
| 104 | + const char *name; | |
| 105 | + unsigned int id; | |
| 106 | + const struct iio_chan_spec *channels; | |
| 107 | + unsigned int num_channels; | |
| 108 | + const unsigned int (*scale_table)[2]; | |
| 109 | + int num_scales; | |
| 110 | + unsigned long max_rate; | |
| 111 | + unsigned int default_output_mode; | |
| 112 | + unsigned int vref_mask; | |
| 108 | 113 | }; |
| 109 | 114 | |
| 110 | −#define to_ad9467_chip_info(_info) \ | |
| 111 | − container_of(_info, struct ad9467_chip_info, axi_adc_info) | |
| 112 | − | |
| 113 | 115 | struct ad9467_state { |
| 116 | + const struct ad9467_chip_info *info; | |
| 117 | + struct iio_backend *back; | |
| 114 | 118 | struct spi_device *spi; |
| 115 | 119 | struct clk *clk; |
| 116 | 120 | unsigned int output_mode; |
| @@ -151,10 +155,10 @@ static int ad9467_spi_write(struct spi_device *spi, unsigned int reg, · 34 unmodified lines | ||
| 151 | 155 | return spi_write(spi, buf, ARRAY_SIZE(buf)); |
| 152 | 156 | } |
| 153 | 157 | |
| 154 | −static int ad9467_reg_access(struct adi_axi_adc_conv *conv, unsigned int reg, | |
| 158 | +static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg, | |
| 155 | 159 | unsigned int writeval, unsigned int *readval) |
| 156 | 160 | { |
| 157 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 161 | + struct ad9467_state *st = iio_priv(indio_dev); | |
| 158 | 162 | struct spi_device *spi = st->spi; |
| 159 | 163 | int ret; |
| 160 | 164 | |
| @@ -191,10 +195,10 @@ static const unsigned int ad9467_scale_table[][2] = { · 30 unmodified lines | ||
| 191 | 195 | {2300, 8}, {2400, 9}, {2500, 10}, |
| 192 | 196 | }; |
| 193 | 197 | |
| 194 | −static void __ad9467_get_scale(struct adi_axi_adc_conv *conv, int index, | |
| 198 | +static void __ad9467_get_scale(struct ad9467_state *st, int index, | |
| 195 | 199 | unsigned int *val, unsigned int *val2) |
| 196 | 200 | { |
| 197 | − const struct adi_axi_adc_chip_info *info = conv->chip_info; | |
| 201 | + const struct ad9467_chip_info *info = st->info; | |
| 198 | 202 | const struct iio_chan_spec *chan = &info->channels[0]; |
| 199 | 203 | unsigned int tmp; |
| 200 | 204 | |
| @@ -229,52 +233,44 @@ static const struct iio_chan_spec ad9467_channels[] = { · 28 unmodified lines | ||
| 229 | 233 | }; |
| 230 | 234 | |
| 231 | 235 | static const struct ad9467_chip_info ad9467_chip_tbl = { |
| 232 | − .axi_adc_info = { | |
| 233 | − .name = "ad9467", | |
| 234 | − .id = CHIPID_AD9467, | |
| 235 | − .max_rate = 250000000UL, | |
| 236 | − .scale_table = ad9467_scale_table, | |
| 237 | − .num_scales = ARRAY_SIZE(ad9467_scale_table), | |
| 238 | − .channels = ad9467_channels, | |
| 239 | − .num_channels = ARRAY_SIZE(ad9467_channels), | |
| 240 | − }, | |
| 236 | + .name = "ad9467", | |
| 237 | + .id = CHIPID_AD9467, | |
| 238 | + .max_rate = 250000000UL, | |
| 239 | + .scale_table = ad9467_scale_table, | |
| 240 | + .num_scales = ARRAY_SIZE(ad9467_scale_table), | |
| 241 | + .channels = ad9467_channels, | |
| 242 | + .num_channels = ARRAY_SIZE(ad9467_channels), | |
| 241 | 243 | .default_output_mode = AD9467_DEF_OUTPUT_MODE, |
| 242 | 244 | .vref_mask = AD9467_REG_VREF_MASK, |
| 243 | 245 | }; |
| 244 | 246 | |
| 245 | 247 | static const struct ad9467_chip_info ad9434_chip_tbl = { |
| 246 | − .axi_adc_info = { | |
| 247 | − .name = "ad9434", | |
| 248 | − .id = CHIPID_AD9434, | |
| 249 | − .max_rate = 500000000UL, | |
| 250 | − .scale_table = ad9434_scale_table, | |
| 251 | − .num_scales = ARRAY_SIZE(ad9434_scale_table), | |
| 252 | − .channels = ad9434_channels, | |
| 253 | − .num_channels = ARRAY_SIZE(ad9434_channels), | |
| 254 | − }, | |
| 248 | + .name = "ad9434", | |
| 249 | + .id = CHIPID_AD9434, | |
| 250 | + .max_rate = 500000000UL, | |
| 251 | + .scale_table = ad9434_scale_table, | |
| 252 | + .num_scales = ARRAY_SIZE(ad9434_scale_table), | |
| 253 | + .channels = ad9434_channels, | |
| 254 | + .num_channels = ARRAY_SIZE(ad9434_channels), | |
| 255 | 255 | .default_output_mode = AD9434_DEF_OUTPUT_MODE, |
| 256 | 256 | .vref_mask = AD9434_REG_VREF_MASK, |
| 257 | 257 | }; |
| 258 | 258 | |
| 259 | 259 | static const struct ad9467_chip_info ad9265_chip_tbl = { |
| 260 | − .axi_adc_info = { | |
| 261 | − .name = "ad9265", | |
| 262 | − .id = CHIPID_AD9265, | |
| 263 | − .max_rate = 125000000UL, | |
| 264 | − .scale_table = ad9265_scale_table, | |
| 265 | − .num_scales = ARRAY_SIZE(ad9265_scale_table), | |
| 266 | − .channels = ad9467_channels, | |
| 267 | − .num_channels = ARRAY_SIZE(ad9467_channels), | |
| 268 | − }, | |
| 260 | + .name = "ad9265", | |
| 261 | + .id = CHIPID_AD9265, | |
| 262 | + .max_rate = 125000000UL, | |
| 263 | + .scale_table = ad9265_scale_table, | |
| 264 | + .num_scales = ARRAY_SIZE(ad9265_scale_table), | |
| 265 | + .channels = ad9467_channels, | |
| 266 | + .num_channels = ARRAY_SIZE(ad9467_channels), | |
| 269 | 267 | .default_output_mode = AD9265_DEF_OUTPUT_MODE, |
| 270 | 268 | .vref_mask = AD9265_REG_VREF_MASK, |
| 271 | 269 | }; |
| 272 | 270 | |
| 273 | −static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2) | |
| 271 | +static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2) | |
| 274 | 272 | { |
| 275 | − const struct adi_axi_adc_chip_info *info = conv->chip_info; | |
| 276 | − const struct ad9467_chip_info *info1 = to_ad9467_chip_info(info); | |
| 277 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 273 | + const struct ad9467_chip_info *info = st->info; | |
| 278 | 274 | unsigned int i, vref_val; |
| 279 | 275 | int ret; |
| 280 | 276 | |
| @@ -282,7 +278,7 @@ static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2) · 1 unmodified line | ||
| 282 | 278 | if (ret < 0) |
| 283 | 279 | return ret; |
| 284 | 280 | |
| 285 | − vref_val = ret & info1->vref_mask; | |
| 281 | + vref_val = ret & info->vref_mask; | |
| 286 | 282 | |
| 287 | 283 | for (i = 0; i < info->num_scales; i++) { |
| 288 | 284 | if (vref_val == info->scale_table[i][1]) |
| @@ -292,15 +288,14 @@ static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2) · 3 unmodified lines | ||
| 292 | 288 | if (i == info->num_scales) |
| 293 | 289 | return -ERANGE; |
| 294 | 290 | |
| 295 | − __ad9467_get_scale(conv, i, val, val2); | |
| 291 | + __ad9467_get_scale(st, i, val, val2); | |
| 296 | 292 | |
| 297 | 293 | return IIO_VAL_INT_PLUS_MICRO; |
| 298 | 294 | } |
| 299 | 295 | |
| 300 | −static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2) | |
| 296 | +static int ad9467_set_scale(struct ad9467_state *st, int val, int val2) | |
| 301 | 297 | { |
| 302 | − const struct adi_axi_adc_chip_info *info = conv->chip_info; | |
| 303 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 298 | + const struct ad9467_chip_info *info = st->info; | |
| 304 | 299 | unsigned int scale_val[2]; |
| 305 | 300 | unsigned int i; |
| 306 | 301 | int ret; |
| @@ -309,7 +304,7 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2) · 2 unmodified lines | ||
| 309 | 304 | return -EINVAL; |
| 310 | 305 | |
| 311 | 306 | for (i = 0; i < info->num_scales; i++) { |
| 312 | − __ad9467_get_scale(conv, i, &scale_val[0], &scale_val[1]); | |
| 307 | + __ad9467_get_scale(st, i, &scale_val[0], &scale_val[1]); | |
| 313 | 308 | if (scale_val[0] != val || scale_val[1] != val2) |
| 314 | 309 | continue; |
| 315 | 310 | |
| @@ -326,15 +321,15 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2) · 10 unmodified lines | ||
| 326 | 321 | return -EINVAL; |
| 327 | 322 | } |
| 328 | 323 | |
| 329 | −static int ad9467_read_raw(struct adi_axi_adc_conv *conv, | |
| 324 | +static int ad9467_read_raw(struct iio_dev *indio_dev, | |
| 330 | 325 | struct iio_chan_spec const *chan, |
| 331 | 326 | int *val, int *val2, long m) |
| 332 | 327 | { |
| 333 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 328 | + struct ad9467_state *st = iio_priv(indio_dev); | |
| 334 | 329 | |
| 335 | 330 | switch (m) { |
| 336 | 331 | case IIO_CHAN_INFO_SCALE: |
| 337 | − return ad9467_get_scale(conv, val, val2); | |
| 332 | + return ad9467_get_scale(st, val, val2); | |
| 338 | 333 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 339 | 334 | *val = clk_get_rate(st->clk); |
| 340 | 335 | |
| @@ -344,17 +339,17 @@ static int ad9467_read_raw(struct adi_axi_adc_conv *conv, · 3 unmodified lines | ||
| 344 | 339 | } |
| 345 | 340 | } |
| 346 | 341 | |
| 347 | −static int ad9467_write_raw(struct adi_axi_adc_conv *conv, | |
| 342 | +static int ad9467_write_raw(struct iio_dev *indio_dev, | |
| 348 | 343 | struct iio_chan_spec const *chan, |
| 349 | 344 | int val, int val2, long mask) |
| 350 | 345 | { |
| 351 | − const struct adi_axi_adc_chip_info *info = conv->chip_info; | |
| 352 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 346 | + struct ad9467_state *st = iio_priv(indio_dev); | |
| 347 | + const struct ad9467_chip_info *info = st->info; | |
| 353 | 348 | long r_clk; |
| 354 | 349 | |
| 355 | 350 | switch (mask) { |
| 356 | 351 | case IIO_CHAN_INFO_SCALE: |
| 357 | − return ad9467_set_scale(conv, val, val2); | |
| 352 | + return ad9467_set_scale(st, val, val2); | |
| 358 | 353 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 359 | 354 | r_clk = clk_round_rate(st->clk, val); |
| 360 | 355 | if (r_clk < 0 || r_clk > info->max_rate) { |
| @@ -369,13 +364,13 @@ static int ad9467_write_raw(struct adi_axi_adc_conv *conv, · 8 unmodified lines | ||
| 369 | 364 | } |
| 370 | 365 | } |
| 371 | 366 | |
| 372 | −static int ad9467_read_avail(struct adi_axi_adc_conv *conv, | |
| 367 | +static int ad9467_read_avail(struct iio_dev *indio_dev, | |
| 373 | 368 | struct iio_chan_spec const *chan, |
| 374 | 369 | const int **vals, int *type, int *length, |
| 375 | 370 | long mask) |
| 376 | 371 | { |
| 377 | − const struct adi_axi_adc_chip_info *info = conv->chip_info; | |
| 378 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 372 | + struct ad9467_state *st = iio_priv(indio_dev); | |
| 373 | + const struct ad9467_chip_info *info = st->info; | |
| 379 | 374 | |
| 380 | 375 | switch (mask) { |
| 381 | 376 | case IIO_CHAN_INFO_SCALE: |
| @@ -389,6 +384,33 @@ static int ad9467_read_avail(struct adi_axi_adc_conv *conv, · 7 unmodified lines | ||
| 389 | 384 | } |
| 390 | 385 | } |
| 391 | 386 | |
| 387 | +static int ad9467_update_scan_mode(struct iio_dev *indio_dev, | |
| 388 | + const unsigned long *scan_mask) | |
| 389 | +{ | |
| 390 | + struct ad9467_state *st = iio_priv(indio_dev); | |
| 391 | + unsigned int c; | |
| 392 | + int ret; | |
| 393 | + | |
| 394 | + for (c = 0; c < st->info->num_channels; c++) { | |
| 395 | + if (test_bit(c, scan_mask)) | |
| 396 | + ret = iio_backend_chan_enable(st->back, c); | |
| 397 | + else | |
| 398 | + ret = iio_backend_chan_disable(st->back, c); | |
| 399 | + if (ret) | |
| 400 | + return ret; | |
| 401 | + } | |
| 402 | + | |
| 403 | + return 0; | |
| 404 | +} | |
| 405 | + | |
| 406 | +static const struct iio_info ad9467_info = { | |
| 407 | + .read_raw = ad9467_read_raw, | |
| 408 | + .write_raw = ad9467_write_raw, | |
| 409 | + .update_scan_mode = ad9467_update_scan_mode, | |
| 410 | + .debugfs_reg_access = ad9467_reg_access, | |
| 411 | + .read_avail = ad9467_read_avail, | |
| 412 | +}; | |
| 413 | + | |
| 392 | 414 | static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode) |
| 393 | 415 | { |
| 394 | 416 | int ret; |
| @@ -401,10 +423,9 @@ static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode) · 6 unmodified lines | ||
| 401 | 423 | AN877_ADC_TRANSFER_SYNC); |
| 402 | 424 | } |
| 403 | 425 | |
| 404 | −static int ad9467_scale_fill(struct adi_axi_adc_conv *conv) | |
| 426 | +static int ad9467_scale_fill(struct ad9467_state *st) | |
| 405 | 427 | { |
| 406 | − const struct adi_axi_adc_chip_info *info = conv->chip_info; | |
| 407 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 428 | + const struct ad9467_chip_info *info = st->info; | |
| 408 | 429 | unsigned int i, val1, val2; |
| 409 | 430 | |
| 410 | 431 | st->scales = devm_kmalloc_array(&st->spi->dev, info->num_scales, |
| @@ -413,7 +434,7 @@ static int ad9467_scale_fill(struct adi_axi_adc_conv *conv) · 2 unmodified lines | ||
| 413 | 434 | return -ENOMEM; |
| 414 | 435 | |
| 415 | 436 | for (i = 0; i < info->num_scales; i++) { |
| 416 | − __ad9467_get_scale(conv, i, &val1, &val2); | |
| 437 | + __ad9467_get_scale(st, i, &val1, &val2); | |
| 417 | 438 | st->scales[i][0] = val1; |
| 418 | 439 | st->scales[i][1] = val2; |
| 419 | 440 | } |
| @@ -421,11 +442,27 @@ static int ad9467_scale_fill(struct adi_axi_adc_conv *conv) · 1 unmodified line | ||
| 421 | 442 | return 0; |
| 422 | 443 | } |
| 423 | 444 | |
| 424 | −static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv) | |
| 445 | +static int ad9467_setup(struct ad9467_state *st) | |
| 425 | 446 | { |
| 426 | − struct ad9467_state *st = adi_axi_adc_conv_priv(conv); | |
| 447 | + struct iio_backend_data_fmt data = { | |
| 448 | + .sign_extend = true, | |
| 449 | + .enable = true, | |
| 450 | + }; | |
| 451 | + unsigned int c, mode; | |
| 452 | + int ret; | |
| 453 | + | |
| 454 | + mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; | |
| 455 | + ret = ad9467_outputmode_set(st->spi, mode); | |
| 456 | + if (ret) | |
| 457 | + return ret; | |
| 427 | 458 | |
| 428 | − return ad9467_outputmode_set(st->spi, st->output_mode); | |
| 459 | + for (c = 0; c < st->info->num_channels; c++) { | |
| 460 | + ret = iio_backend_data_format_set(st->back, c, &data); | |
| 461 | + if (ret) | |
| 462 | + return ret; | |
| 463 | + } | |
| 464 | + | |
| 465 | + return 0; | |
| 429 | 466 | } |
| 430 | 467 | |
| 431 | 468 | static int ad9467_reset(struct device *dev) |
| @@ -443,25 +480,65 @@ static int ad9467_reset(struct device *dev) · 11 unmodified lines | ||
| 443 | 480 | return 0; |
| 444 | 481 | } |
| 445 | 482 | |
| 483 | +static int ad9467_iio_backend_get(struct ad9467_state *st) | |
| 484 | +{ | |
| 485 | + struct device *dev = &st->spi->dev; | |
| 486 | + struct device_node *__back; | |
| 487 | + | |
| 488 | + st->back = devm_iio_backend_get(dev, NULL); | |
| 489 | + if (!IS_ERR(st->back)) | |
| 490 | + return 0; | |
| 491 | + /* If not found, don't error out as we might have legacy DT property */ | |
| 492 | + if (PTR_ERR(st->back) != -ENOENT) | |
| 493 | + return PTR_ERR(st->back); | |
| 494 | + | |
| 495 | + /* | |
| 496 | + * if we don't get the backend using the normal API's, use the legacy | |
| 497 | + * 'adi,adc-dev' property. So we get all nodes with that property, and | |
| 498 | + * look for the one pointing at us. Then we directly lookup that fwnode | |
| 499 | + * on the backend list of registered devices. This is done so we don't | |
| 500 | + * make io-backends mandatory which would break DT ABI. | |
| 501 | + */ | |
| 502 | + for_each_node_with_property(__back, "adi,adc-dev") { | |
| 503 | + struct device_node *__me; | |
| 504 | + | |
| 505 | + __me = of_parse_phandle(__back, "adi,adc-dev", 0); | |
| 506 | + if (!__me) | |
| 507 | + continue; | |
| 508 | + | |
| 509 | + if (!device_match_of_node(dev, __me)) { | |
| 510 | + of_node_put(__me); | |
| 511 | + continue; | |
| 512 | + } | |
| 513 | + | |
| 514 | + of_node_put(__me); | |
| 515 | + st->back = __devm_iio_backend_get_from_fwnode_lookup(dev, | |
| 516 | + of_fwnode_handle(__back)); | |
| 517 | + of_node_put(__back); | |
| 518 | + return PTR_ERR_OR_ZERO(st->back); | |
| 519 | + } | |
| 520 | + | |
| 521 | + return -ENODEV; | |
| 522 | +} | |
| 523 | + | |
| 446 | 524 | static int ad9467_probe(struct spi_device *spi) |
| 447 | 525 | { |
| 448 | − const struct ad9467_chip_info *info; | |
| 449 | − struct adi_axi_adc_conv *conv; | |
| 526 | + struct iio_dev *indio_dev; | |
| 450 | 527 | struct ad9467_state *st; |
| 451 | 528 | unsigned int id; |
| 452 | 529 | int ret; |
| 453 | 530 | |
| 454 | − info = spi_get_device_match_data(spi); | |
| 455 | − if (!info) | |
| 456 | − return -ENODEV; | |
| 457 | − | |
| 458 | − conv = devm_adi_axi_adc_conv_register(&spi->dev, sizeof(*st)); | |
| 459 | − if (IS_ERR(conv)) | |
| 460 | − return PTR_ERR(conv); | |
| 531 | + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | |
| 532 | + if (!indio_dev) | |
| 533 | + return -ENOMEM; | |
| 461 | 534 | |
| 462 | − st = adi_axi_adc_conv_priv(conv); | |
| 535 | + st = iio_priv(indio_dev); | |
| 463 | 536 | st->spi = spi; |
| 464 | 537 | |
| 538 | + st->info = spi_get_device_match_data(spi); | |
| 539 | + if (!st->info) | |
| 540 | + return -ENODEV; | |
| 541 | + | |
| 465 | 542 | st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); |
| 466 | 543 | if (IS_ERR(st->clk)) |
| 467 | 544 | return PTR_ERR(st->clk); |
| @@ -475,29 +552,39 @@ static int ad9467_probe(struct spi_device *spi) · 7 unmodified lines | ||
| 475 | 552 | if (ret) |
| 476 | 553 | return ret; |
| 477 | 554 | |
| 478 | − conv->chip_info = &info->axi_adc_info; | |
| 479 | − | |
| 480 | − ret = ad9467_scale_fill(conv); | |
| 555 | + ret = ad9467_scale_fill(st); | |
| 481 | 556 | if (ret) |
| 482 | 557 | return ret; |
| 483 | 558 | |
| 484 | 559 | id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID); |
| 485 | − if (id != conv->chip_info->id) { | |
| 560 | + if (id != st->info->id) { | |
| 486 | 561 | dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", |
| 487 | − id, conv->chip_info->id); | |
| 562 | + id, st->info->id); | |
| 488 | 563 | return -ENODEV; |
| 489 | 564 | } |
| 490 | 565 | |
| 491 | − conv->reg_access = ad9467_reg_access; | |
| 492 | − conv->write_raw = ad9467_write_raw; | |
| 493 | − conv->read_raw = ad9467_read_raw; | |
| 494 | − conv->read_avail = ad9467_read_avail; | |
| 495 | − conv->preenable_setup = ad9467_preenable_setup; | |
| 566 | + indio_dev->name = st->info->name; | |
| 567 | + indio_dev->channels = st->info->channels; | |
| 568 | + indio_dev->num_channels = st->info->num_channels; | |
| 569 | + indio_dev->info = &ad9467_info; | |
| 496 | 570 | |
| 497 | − st->output_mode = info->default_output_mode | | |
| 498 | − AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; | |
| 571 | + ret = ad9467_iio_backend_get(st); | |
| 572 | + if (ret) | |
| 573 | + return ret; | |
| 499 | 574 | |
| 500 | − return 0; | |
| 575 | + ret = devm_iio_backend_request_buffer(&spi->dev, st->back, indio_dev); | |
| 576 | + if (ret) | |
| 577 | + return ret; | |
| 578 | + | |
| 579 | + ret = devm_iio_backend_enable(&spi->dev, st->back); | |
| 580 | + if (ret) | |
| 581 | + return ret; | |
| 582 | + | |
| 583 | + ret = ad9467_setup(st); | |
| 584 | + if (ret) | |
| 585 | + return ret; | |
| 586 | + | |
| 587 | + return devm_iio_device_register(&spi->dev, indio_dev); | |
| 501 | 588 | } |
| 502 | 589 | |
| 503 | 590 | static const struct of_device_id ad9467_of_match[] = { |
| @@ -529,4 +616,4 @@ module_spi_driver(ad9467_driver); · 25 unmodified lines | ||
| 529 | 616 | MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); |
| 530 | 617 | MODULE_DESCRIPTION("Analog Devices AD9467 ADC driver"); |
| 531 | 618 | MODULE_LICENSE("GPL v2"); |
| 532 | −MODULE_IMPORT_NS(IIO_ADI_AXI); | |
| 619 | +MODULE_IMPORT_NS(IIO_BACKEND); | |
| @@ -568,6 +568,7 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, IIO_AD_SIGMA_DELTA); · 567 unmodified lines | ||
| 568 | 568 | static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) |
| 569 | 569 | { |
| 570 | 570 | struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); |
| 571 | + unsigned long irq_flags = irq_get_trigger_type(sigma_delta->spi->irq); | |
| 571 | 572 | int ret; |
| 572 | 573 | |
| 573 | 574 | if (dev != &sigma_delta->spi->dev) { |
| @@ -588,9 +589,13 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de · 14 unmodified lines | ||
| 588 | 589 | /* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */ |
| 589 | 590 | irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY); |
| 590 | 591 | |
| 592 | + /* Allow overwriting the flags from firmware */ | |
| 593 | + if (!irq_flags) | |
| 594 | + irq_flags = sigma_delta->info->irq_flags; | |
| 595 | + | |
| 591 | 596 | ret = devm_request_irq(dev, sigma_delta->spi->irq, |
| 592 | 597 | ad_sd_data_rdy_trig_poll, |
| 593 | − sigma_delta->info->irq_flags | IRQF_NO_AUTOEN, | |
| 598 | + irq_flags | IRQF_NO_AUTOEN, | |
| 594 | 599 | indio_dev->name, |
| 595 | 600 | sigma_delta); |
| 596 | 601 | if (ret) |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | #include <linux/bitfield.h> |
| 10 | 10 | #include <linux/clk.h> |
| 11 | +#include <linux/err.h> | |
| 11 | 12 | #include <linux/io.h> |
| 12 | 13 | #include <linux/delay.h> |
| 13 | 14 | #include <linux/module.h> |
| @@ -17,13 +18,12 @@ · 3 unmodified lines | ||
| 17 | 18 | #include <linux/regmap.h> |
| 18 | 19 | #include <linux/slab.h> |
| 19 | 20 | |
| 20 | −#include <linux/iio/iio.h> | |
| 21 | −#include <linux/iio/sysfs.h> | |
| 22 | −#include <linux/iio/buffer.h> | |
| 23 | −#include <linux/iio/buffer-dmaengine.h> | |
| 24 | − | |
| 25 | 21 | #include <linux/fpga/adi-axi-common.h> |
| 26 | −#include <linux/iio/adc/adi-axi-adc.h> | |
| 22 | + | |
| 23 | +#include <linux/iio/backend.h> | |
| 24 | +#include <linux/iio/buffer-dmaengine.h> | |
| 25 | +#include <linux/iio/buffer.h> | |
| 26 | +#include <linux/iio/iio.h> | |
| 27 | 27 | |
| 28 | 28 | /* |
| 29 | 29 | * Register definitions: |
| @@ -44,6 +44,7 @@ · 14 unmodified lines | ||
| 44 | 44 | #define ADI_AXI_REG_CHAN_CTRL_PN_SEL_OWR BIT(10) |
| 45 | 45 | #define ADI_AXI_REG_CHAN_CTRL_IQCOR_EN BIT(9) |
| 46 | 46 | #define ADI_AXI_REG_CHAN_CTRL_DCFILT_EN BIT(8) |
| 47 | +#define ADI_AXI_REG_CHAN_CTRL_FMT_MASK GENMASK(6, 4) | |
| 47 | 48 | #define ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT BIT(6) |
| 48 | 49 | #define ADI_AXI_REG_CHAN_CTRL_FMT_TYPE BIT(5) |
| 49 | 50 | #define ADI_AXI_REG_CHAN_CTRL_FMT_EN BIT(4) |
| @@ -55,286 +56,100 @@ · 5 unmodified lines | ||
| 55 | 56 | ADI_AXI_REG_CHAN_CTRL_FMT_EN | \ |
| 56 | 57 | ADI_AXI_REG_CHAN_CTRL_ENABLE) |
| 57 | 58 | |
| 58 | −struct adi_axi_adc_core_info { | |
| 59 | − unsigned int version; | |
| 60 | −}; | |
| 61 | − | |
| 62 | 59 | struct adi_axi_adc_state { |
| 63 | − struct mutex lock; | |
| 64 | − | |
| 65 | − struct adi_axi_adc_client *client; | |
| 66 | 60 | struct regmap *regmap; |
| 67 | −}; | |
| 68 | − | |
| 69 | −struct adi_axi_adc_client { | |
| 70 | − struct list_head entry; | |
| 71 | − struct adi_axi_adc_conv conv; | |
| 72 | − struct adi_axi_adc_state *state; | |
| 73 | 61 | struct device *dev; |
| 74 | − const struct adi_axi_adc_core_info *info; | |
| 75 | 62 | }; |
| 76 | 63 | |
| 77 | −static LIST_HEAD(registered_clients); | |
| 78 | −static DEFINE_MUTEX(registered_clients_lock); | |
| 79 | − | |
| 80 | −static struct adi_axi_adc_client *conv_to_client(struct adi_axi_adc_conv *conv) | |
| 81 | −{ | |
| 82 | − return container_of(conv, struct adi_axi_adc_client, conv); | |
| 83 | −} | |
| 84 | − | |
| 85 | −void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv) | |
| 86 | −{ | |
| 87 | − struct adi_axi_adc_client *cl = conv_to_client(conv); | |
| 88 | − | |
| 89 | − return (char *)cl + ALIGN(sizeof(struct adi_axi_adc_client), | |
| 90 | − IIO_DMA_MINALIGN); | |
| 91 | −} | |
| 92 | −EXPORT_SYMBOL_NS_GPL(adi_axi_adc_conv_priv, IIO_ADI_AXI); | |
| 93 | − | |
| 94 | −static int adi_axi_adc_config_dma_buffer(struct device *dev, | |
| 95 | − struct iio_dev *indio_dev) | |
| 96 | −{ | |
| 97 | − const char *dma_name; | |
| 98 | − | |
| 99 | − if (!device_property_present(dev, "dmas")) | |
| 100 | − return 0; | |
| 101 | − | |
| 102 | − if (device_property_read_string(dev, "dma-names", &dma_name)) | |
| 103 | − dma_name = "rx"; | |
| 104 | − | |
| 105 | − return devm_iio_dmaengine_buffer_setup(indio_dev->dev.parent, | |
| 106 | − indio_dev, dma_name); | |
| 107 | −} | |
| 108 | − | |
| 109 | −static int adi_axi_adc_read_raw(struct iio_dev *indio_dev, | |
| 110 | − struct iio_chan_spec const *chan, | |
| 111 | − int *val, int *val2, long mask) | |
| 112 | −{ | |
| 113 | − struct adi_axi_adc_state *st = iio_priv(indio_dev); | |
| 114 | − struct adi_axi_adc_conv *conv = &st->client->conv; | |
| 115 | − | |
| 116 | − if (!conv->read_raw) | |
| 117 | − return -EOPNOTSUPP; | |
| 118 | − | |
| 119 | − return conv->read_raw(conv, chan, val, val2, mask); | |
| 120 | −} | |
| 121 | − | |
| 122 | −static int adi_axi_adc_write_raw(struct iio_dev *indio_dev, | |
| 123 | − struct iio_chan_spec const *chan, | |
| 124 | − int val, int val2, long mask) | |
| 125 | −{ | |
| 126 | − struct adi_axi_adc_state *st = iio_priv(indio_dev); | |
| 127 | − struct adi_axi_adc_conv *conv = &st->client->conv; | |
| 128 | − | |
| 129 | − if (!conv->write_raw) | |
| 130 | − return -EOPNOTSUPP; | |
| 131 | − | |
| 132 | − return conv->write_raw(conv, chan, val, val2, mask); | |
| 133 | −} | |
| 134 | − | |
| 135 | −static int adi_axi_adc_read_avail(struct iio_dev *indio_dev, | |
| 136 | − struct iio_chan_spec const *chan, | |
| 137 | − const int **vals, int *type, int *length, | |
| 138 | − long mask) | |
| 139 | −{ | |
| 140 | − struct adi_axi_adc_state *st = iio_priv(indio_dev); | |
| 141 | − struct adi_axi_adc_conv *conv = &st->client->conv; | |
| 142 | − | |
| 143 | − if (!conv->read_avail) | |
| 144 | − return -EOPNOTSUPP; | |
| 145 | − | |
| 146 | − return conv->read_avail(conv, chan, vals, type, length, mask); | |
| 147 | −} | |
| 148 | − | |
| 149 | −static int adi_axi_adc_update_scan_mode(struct iio_dev *indio_dev, | |
| 150 | − const unsigned long *scan_mask) | |
| 64 | +static int axi_adc_enable(struct iio_backend *back) | |
| 151 | 65 | { |
| 152 | − struct adi_axi_adc_state *st = iio_priv(indio_dev); | |
| 153 | − struct adi_axi_adc_conv *conv = &st->client->conv; | |
| 154 | − unsigned int i; | |
| 66 | + struct adi_axi_adc_state *st = iio_backend_get_priv(back); | |
| 155 | 67 | int ret; |
| 156 | 68 | |
| 157 | − for (i = 0; i < conv->chip_info->num_channels; i++) { | |
| 158 | − if (test_bit(i, scan_mask)) | |
| 159 | − ret = regmap_set_bits(st->regmap, | |
| 160 | − ADI_AXI_REG_CHAN_CTRL(i), | |
| 161 | − ADI_AXI_REG_CHAN_CTRL_ENABLE); | |
| 162 | − else | |
| 163 | − ret = regmap_clear_bits(st->regmap, | |
| 164 | − ADI_AXI_REG_CHAN_CTRL(i), | |
| 165 | − ADI_AXI_REG_CHAN_CTRL_ENABLE); | |
| 166 | − if (ret) | |
| 167 | − return ret; | |
| 168 | − } | |
| 169 | − | |
| 170 | − return 0; | |
| 171 | −} | |
| 172 | − | |
| 173 | −static struct adi_axi_adc_conv *adi_axi_adc_conv_register(struct device *dev, | |
| 174 | − size_t sizeof_priv) | |
| 175 | −{ | |
| 176 | − struct adi_axi_adc_client *cl; | |
| 177 | − size_t alloc_size; | |
| 178 | − | |
| 179 | − alloc_size = ALIGN(sizeof(struct adi_axi_adc_client), IIO_DMA_MINALIGN); | |
| 180 | − if (sizeof_priv) | |
| 181 | − alloc_size += ALIGN(sizeof_priv, IIO_DMA_MINALIGN); | |
| 182 | − | |
| 183 | − cl = kzalloc(alloc_size, GFP_KERNEL); | |
| 184 | − if (!cl) | |
| 185 | − return ERR_PTR(-ENOMEM); | |
| 186 | − | |
| 187 | − mutex_lock(®istered_clients_lock); | |
| 188 | − | |
| 189 | − cl->dev = get_device(dev); | |
| 190 | − | |
| 191 | − list_add_tail(&cl->entry, ®istered_clients); | |
| 192 | − | |
| 193 | − mutex_unlock(®istered_clients_lock); | |
| 69 | + ret = regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN, | |
| 70 | + ADI_AXI_REG_RSTN_MMCM_RSTN); | |
| 71 | + if (ret) | |
| 72 | + return ret; | |
| 194 | 73 | |
| 195 | − return &cl->conv; | |
| 74 | + fsleep(10000); | |
| 75 | + return regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN, | |
| 76 | + ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); | |
| 196 | 77 | } |
| 197 | 78 | |
| 198 | −static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv) | |
| 79 | +static void axi_adc_disable(struct iio_backend *back) | |
| 199 | 80 | { |
| 200 | − struct adi_axi_adc_client *cl = conv_to_client(conv); | |
| 201 | − | |
| 202 | − mutex_lock(®istered_clients_lock); | |
| 203 | − | |
| 204 | − list_del(&cl->entry); | |
| 205 | − put_device(cl->dev); | |
| 81 | + struct adi_axi_adc_state *st = iio_backend_get_priv(back); | |
| 206 | 82 | |
| 207 | − mutex_unlock(®istered_clients_lock); | |
| 208 | − | |
| 209 | − kfree(cl); | |
| 83 | + regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); | |
| 210 | 84 | } |
| 211 | 85 | |
| 212 | −static void devm_adi_axi_adc_conv_release(void *conv) | |
| 86 | +static int axi_adc_data_format_set(struct iio_backend *back, unsigned int chan, | |
| 87 | + const struct iio_backend_data_fmt *data) | |
| 213 | 88 | { |
| 214 | − adi_axi_adc_conv_unregister(conv); | |
| 89 | + struct adi_axi_adc_state *st = iio_backend_get_priv(back); | |
| 90 | + u32 val; | |
| 91 | + | |
| 92 | + if (!data->enable) | |
| 93 | + return regmap_clear_bits(st->regmap, | |
| 94 | + ADI_AXI_REG_CHAN_CTRL(chan), | |
| 95 | + ADI_AXI_REG_CHAN_CTRL_FMT_EN); | |
| 96 | + | |
| 97 | + val = FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_EN, true); | |
| 98 | + if (data->sign_extend) | |
| 99 | + val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT, true); | |
| 100 | + if (data->type == IIO_BACKEND_OFFSET_BINARY) | |
| 101 | + val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_TYPE, true); | |
| 102 | + | |
| 103 | + return regmap_update_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), | |
| 104 | + ADI_AXI_REG_CHAN_CTRL_FMT_MASK, val); | |
| 215 | 105 | } |
| 216 | 106 | |
| 217 | −struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, | |
| 218 | − size_t sizeof_priv) | |
| 107 | +static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan) | |
| 219 | 108 | { |
| 220 | − struct adi_axi_adc_conv *conv; | |
| 221 | − int ret; | |
| 222 | − | |
| 223 | − conv = adi_axi_adc_conv_register(dev, sizeof_priv); | |
| 224 | − if (IS_ERR(conv)) | |
| 225 | − return conv; | |
| 109 | + struct adi_axi_adc_state *st = iio_backend_get_priv(back); | |
| 226 | 110 | |
| 227 | − ret = devm_add_action_or_reset(dev, devm_adi_axi_adc_conv_release, | |
| 228 | − conv); | |
| 229 | − if (ret) | |
| 230 | − return ERR_PTR(ret); | |
| 231 | − | |
| 232 | − return conv; | |
| 111 | + return regmap_set_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), | |
| 112 | + ADI_AXI_REG_CHAN_CTRL_ENABLE); | |
| 233 | 113 | } |
| 234 | −EXPORT_SYMBOL_NS_GPL(devm_adi_axi_adc_conv_register, IIO_ADI_AXI); | |
| 235 | − | |
| 236 | −static const struct iio_info adi_axi_adc_info = { | |
| 237 | − .read_raw = &adi_axi_adc_read_raw, | |
| 238 | − .write_raw = &adi_axi_adc_write_raw, | |
| 239 | − .update_scan_mode = &adi_axi_adc_update_scan_mode, | |
| 240 | − .read_avail = &adi_axi_adc_read_avail, | |
| 241 | −}; | |
| 242 | − | |
| 243 | −static const struct adi_axi_adc_core_info adi_axi_adc_10_0_a_info = { | |
| 244 | − .version = ADI_AXI_PCORE_VER(10, 0, 'a'), | |
| 245 | −}; | |
| 246 | 114 | |
| 247 | −static struct adi_axi_adc_client *adi_axi_adc_attach_client(struct device *dev) | |
| 115 | +static int axi_adc_chan_disable(struct iio_backend *back, unsigned int chan) | |
| 248 | 116 | { |
| 249 | − const struct adi_axi_adc_core_info *info; | |
| 250 | − struct adi_axi_adc_client *cl; | |
| 251 | − struct device_node *cln; | |
| 252 | − | |
| 253 | − info = of_device_get_match_data(dev); | |
| 254 | − if (!info) | |
| 255 | − return ERR_PTR(-ENODEV); | |
| 256 | − | |
| 257 | − cln = of_parse_phandle(dev->of_node, "adi,adc-dev", 0); | |
| 258 | − if (!cln) { | |
| 259 | − dev_err(dev, "No 'adi,adc-dev' node defined\n"); | |
| 260 | − return ERR_PTR(-ENODEV); | |
| 261 | − } | |
| 262 | − | |
| 263 | − mutex_lock(®istered_clients_lock); | |
| 264 | − | |
| 265 | − list_for_each_entry(cl, ®istered_clients, entry) { | |
| 266 | − if (!cl->dev) | |
| 267 | − continue; | |
| 268 | − | |
| 269 | − if (cl->dev->of_node != cln) | |
| 270 | − continue; | |
| 271 | − | |
| 272 | − if (!try_module_get(cl->dev->driver->owner)) { | |
| 273 | − mutex_unlock(®istered_clients_lock); | |
| 274 | − of_node_put(cln); | |
| 275 | − return ERR_PTR(-ENODEV); | |
| 276 | − } | |
| 277 | − | |
| 278 | − get_device(cl->dev); | |
| 279 | − cl->info = info; | |
| 280 | − mutex_unlock(®istered_clients_lock); | |
| 281 | − of_node_put(cln); | |
| 282 | − return cl; | |
| 283 | − } | |
| 117 | + struct adi_axi_adc_state *st = iio_backend_get_priv(back); | |
| 284 | 118 | |
| 285 | − mutex_unlock(®istered_clients_lock); | |
| 286 | − of_node_put(cln); | |
| 287 | − | |
| 288 | − return ERR_PTR(-EPROBE_DEFER); | |
| 119 | + return regmap_clear_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), | |
| 120 | + ADI_AXI_REG_CHAN_CTRL_ENABLE); | |
| 289 | 121 | } |
| 290 | 122 | |
| 291 | −static int adi_axi_adc_setup_channels(struct device *dev, | |
| 292 | − struct adi_axi_adc_state *st) | |
| 123 | +static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, | |
| 124 | + struct iio_dev *indio_dev) | |
| 293 | 125 | { |
| 294 | − struct adi_axi_adc_conv *conv = &st->client->conv; | |
| 295 | − int i, ret; | |
| 126 | + struct adi_axi_adc_state *st = iio_backend_get_priv(back); | |
| 127 | + struct iio_buffer *buffer; | |
| 128 | + const char *dma_name; | |
| 129 | + int ret; | |
| 296 | 130 | |
| 297 | − if (conv->preenable_setup) { | |
| 298 | − ret = conv->preenable_setup(conv); | |
| 299 | − if (ret) | |
| 300 | − return ret; | |
| 301 | − } | |
| 131 | + if (device_property_read_string(st->dev, "dma-names", &dma_name)) | |
| 132 | + dma_name = "rx"; | |
| 302 | 133 | |
| 303 | − for (i = 0; i < conv->chip_info->num_channels; i++) { | |
| 304 | − ret = regmap_write(st->regmap, ADI_AXI_REG_CHAN_CTRL(i), | |
| 305 | − ADI_AXI_REG_CHAN_CTRL_DEFAULTS); | |
| 306 | − if (ret) | |
| 307 | − return ret; | |
| 134 | + buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name); | |
| 135 | + if (IS_ERR(buffer)) { | |
| 136 | + dev_err(st->dev, "Could not get DMA buffer, %ld\n", | |
| 137 | + PTR_ERR(buffer)); | |
| 138 | + return ERR_CAST(buffer); | |
| 308 | 139 | } |
| 309 | 140 | |
| 310 | − return 0; | |
| 311 | −} | |
| 312 | − | |
| 313 | −static int axi_adc_reset(struct adi_axi_adc_state *st) | |
| 314 | −{ | |
| 315 | − int ret; | |
| 316 | − | |
| 317 | − ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); | |
| 318 | − if (ret) | |
| 319 | − return ret; | |
| 320 | − | |
| 321 | − mdelay(10); | |
| 322 | − ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, | |
| 323 | − ADI_AXI_REG_RSTN_MMCM_RSTN); | |
| 141 | + indio_dev->modes |= INDIO_BUFFER_HARDWARE; | |
| 142 | + ret = iio_device_attach_buffer(indio_dev, buffer); | |
| 324 | 143 | if (ret) |
| 325 | − return ret; | |
| 144 | + return ERR_PTR(ret); | |
| 326 | 145 | |
| 327 | − mdelay(10); | |
| 328 | − return regmap_write(st->regmap, ADI_AXI_REG_RSTN, | |
| 329 | − ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); | |
| 146 | + return buffer; | |
| 330 | 147 | } |
| 331 | 148 | |
| 332 | −static void adi_axi_adc_cleanup(void *data) | |
| 149 | +static void axi_adc_free_buffer(struct iio_backend *back, | |
| 150 | + struct iio_buffer *buffer) | |
| 333 | 151 | { |
| 334 | − struct adi_axi_adc_client *cl = data; | |
| 335 | − | |
| 336 | − put_device(cl->dev); | |
| 337 | − module_put(cl->dev->driver->owner); | |
| 152 | + iio_dmaengine_buffer_free(buffer); | |
| 338 | 153 | } |
| 339 | 154 | |
| 340 | 155 | static const struct regmap_config axi_adc_regmap_config = { |
| @@ -344,45 +159,47 @@ static const struct regmap_config axi_adc_regmap_config = { · 3 unmodified lines | ||
| 344 | 159 | .max_register = 0x0800, |
| 345 | 160 | }; |
| 346 | 161 | |
| 162 | +static const struct iio_backend_ops adi_axi_adc_generic = { | |
| 163 | + .enable = axi_adc_enable, | |
| 164 | + .disable = axi_adc_disable, | |
| 165 | + .data_format_set = axi_adc_data_format_set, | |
| 166 | + .chan_enable = axi_adc_chan_enable, | |
| 167 | + .chan_disable = axi_adc_chan_disable, | |
| 168 | + .request_buffer = axi_adc_request_buffer, | |
| 169 | + .free_buffer = axi_adc_free_buffer, | |
| 170 | +}; | |
| 171 | + | |
| 347 | 172 | static int adi_axi_adc_probe(struct platform_device *pdev) |
| 348 | 173 | { |
| 349 | − struct adi_axi_adc_conv *conv; | |
| 350 | − struct iio_dev *indio_dev; | |
| 351 | − struct adi_axi_adc_client *cl; | |
| 174 | + const unsigned int *expected_ver; | |
| 352 | 175 | struct adi_axi_adc_state *st; |
| 353 | 176 | void __iomem *base; |
| 354 | 177 | unsigned int ver; |
| 355 | 178 | int ret; |
| 356 | 179 | |
| 357 | − cl = adi_axi_adc_attach_client(&pdev->dev); | |
| 358 | − if (IS_ERR(cl)) | |
| 359 | − return PTR_ERR(cl); | |
| 360 | − | |
| 361 | − ret = devm_add_action_or_reset(&pdev->dev, adi_axi_adc_cleanup, cl); | |
| 362 | − if (ret) | |
| 363 | − return ret; | |
| 364 | − | |
| 365 | − indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); | |
| 366 | − if (indio_dev == NULL) | |
| 180 | + st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL); | |
| 181 | + if (!st) | |
| 367 | 182 | return -ENOMEM; |
| 368 | 183 | |
| 369 | − st = iio_priv(indio_dev); | |
| 370 | − st->client = cl; | |
| 371 | − cl->state = st; | |
| 372 | − mutex_init(&st->lock); | |
| 373 | − | |
| 374 | 184 | base = devm_platform_ioremap_resource(pdev, 0); |
| 375 | 185 | if (IS_ERR(base)) |
| 376 | 186 | return PTR_ERR(base); |
| 377 | 187 | |
| 188 | + st->dev = &pdev->dev; | |
| 378 | 189 | st->regmap = devm_regmap_init_mmio(&pdev->dev, base, |
| 379 | 190 | &axi_adc_regmap_config); |
| 380 | 191 | if (IS_ERR(st->regmap)) |
| 381 | 192 | return PTR_ERR(st->regmap); |
| 382 | 193 | |
| 383 | − conv = &st->client->conv; | |
| 194 | + expected_ver = device_get_match_data(&pdev->dev); | |
| 195 | + if (!expected_ver) | |
| 196 | + return -ENODEV; | |
| 384 | 197 | |
| 385 | − ret = axi_adc_reset(st); | |
| 198 | + /* | |
| 199 | + * Force disable the core. Up to the frontend to enable us. And we can | |
| 200 | + * still read/write registers... | |
| 201 | + */ | |
| 202 | + ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); | |
| 386 | 203 | if (ret) |
| 387 | 204 | return ret; |
| 388 | 205 | |
| @@ -390,33 +207,19 @@ static int adi_axi_adc_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 390 | 207 | if (ret) |
| 391 | 208 | return ret; |
| 392 | 209 | |
| 393 | − if (cl->info->version > ver) { | |
| 210 | + if (*expected_ver > ver) { | |
| 394 | 211 | dev_err(&pdev->dev, |
| 395 | 212 | "IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", |
| 396 | − ADI_AXI_PCORE_VER_MAJOR(cl->info->version), | |
| 397 | − ADI_AXI_PCORE_VER_MINOR(cl->info->version), | |
| 398 | − ADI_AXI_PCORE_VER_PATCH(cl->info->version), | |
| 213 | + ADI_AXI_PCORE_VER_MAJOR(*expected_ver), | |
| 214 | + ADI_AXI_PCORE_VER_MINOR(*expected_ver), | |
| 215 | + ADI_AXI_PCORE_VER_PATCH(*expected_ver), | |
| 399 | 216 | ADI_AXI_PCORE_VER_MAJOR(ver), |
| 400 | 217 | ADI_AXI_PCORE_VER_MINOR(ver), |
| 401 | 218 | ADI_AXI_PCORE_VER_PATCH(ver)); |
| 402 | 219 | return -ENODEV; |
| 403 | 220 | } |
| 404 | 221 | |
| 405 | − indio_dev->info = &adi_axi_adc_info; | |
| 406 | − indio_dev->name = "adi-axi-adc"; | |
| 407 | − indio_dev->modes = INDIO_DIRECT_MODE; | |
| 408 | − indio_dev->num_channels = conv->chip_info->num_channels; | |
| 409 | − indio_dev->channels = conv->chip_info->channels; | |
| 410 | − | |
| 411 | − ret = adi_axi_adc_config_dma_buffer(&pdev->dev, indio_dev); | |
| 412 | − if (ret) | |
| 413 | − return ret; | |
| 414 | − | |
| 415 | − ret = adi_axi_adc_setup_channels(&pdev->dev, st); | |
| 416 | − if (ret) | |
| 417 | − return ret; | |
| 418 | − | |
| 419 | − ret = devm_iio_device_register(&pdev->dev, indio_dev); | |
| 222 | + ret = devm_iio_backend_register(&pdev->dev, &adi_axi_adc_generic, st); | |
| 420 | 223 | if (ret) |
| 421 | 224 | return ret; |
| 422 | 225 | |
| @@ -428,6 +231,8 @@ static int adi_axi_adc_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 428 | 231 | return 0; |
| 429 | 232 | } |
| 430 | 233 | |
| 234 | +static unsigned int adi_axi_adc_10_0_a_info = ADI_AXI_PCORE_VER(10, 0, 'a'); | |
| 235 | + | |
| 431 | 236 | /* Match table for of_platform binding */ |
| 432 | 237 | static const struct of_device_id adi_axi_adc_of_match[] = { |
| 433 | 238 | { .compatible = "adi,axi-adc-10.0.a", .data = &adi_axi_adc_10_0_a_info }, |
| @@ -447,3 +252,5 @@ module_platform_driver(adi_axi_adc_driver); · 13 unmodified lines | ||
| 447 | 252 | MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); |
| 448 | 253 | MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver"); |
| 449 | 254 | MODULE_LICENSE("GPL v2"); |
| 255 | +MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER); | |
| 256 | +MODULE_IMPORT_NS(IIO_BACKEND); | |
| @@ -357,62 +357,55 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, · 356 unmodified lines | ||
| 357 | 357 | int *val, |
| 358 | 358 | long m) |
| 359 | 359 | { |
| 360 | − int ret = 0; | |
| 361 | − s32 data; | |
| 362 | − u8 rxbuf[2]; | |
| 363 | − struct max1363_state *st = iio_priv(indio_dev); | |
| 364 | − struct i2c_client *client = st->client; | |
| 365 | − | |
| 366 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 367 | − if (ret) | |
| 368 | − return ret; | |
| 369 | − mutex_lock(&st->lock); | |
| 370 | − | |
| 371 | − /* | |
| 372 | − * If monitor mode is enabled, the method for reading a single | |
| 373 | − * channel will have to be rather different and has not yet | |
| 374 | − * been implemented. | |
| 375 | − * | |
| 376 | − * Also, cannot read directly if buffered capture enabled. | |
| 377 | − */ | |
| 378 | − if (st->monitor_on) { | |
| 379 | − ret = -EBUSY; | |
| 380 | − goto error_ret; | |
| 381 | − } | |
| 382 | − | |
| 383 | − /* Check to see if current scan mode is correct */ | |
| 384 | − if (st->current_mode != &max1363_mode_table[chan->address]) { | |
| 385 | − /* Update scan mode if needed */ | |
| 386 | − st->current_mode = &max1363_mode_table[chan->address]; | |
| 387 | − ret = max1363_set_scan_mode(st); | |
| 388 | − if (ret < 0) | |
| 389 | − goto error_ret; | |
| 390 | − } | |
| 391 | − if (st->chip_info->bits != 8) { | |
| 392 | − /* Get reading */ | |
| 393 | − data = st->recv(client, rxbuf, 2); | |
| 394 | − if (data < 0) { | |
| 395 | − ret = data; | |
| 396 | − goto error_ret; | |
| 360 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 361 | + s32 data; | |
| 362 | + u8 rxbuf[2]; | |
| 363 | + struct max1363_state *st = iio_priv(indio_dev); | |
| 364 | + struct i2c_client *client = st->client; | |
| 365 | + | |
| 366 | + guard(mutex)(&st->lock); | |
| 367 | + | |
| 368 | + /* | |
| 369 | + * If monitor mode is enabled, the method for reading a single | |
| 370 | + * channel will have to be rather different and has not yet | |
| 371 | + * been implemented. | |
| 372 | + * | |
| 373 | + * Also, cannot read directly if buffered capture enabled. | |
| 374 | + */ | |
| 375 | + if (st->monitor_on) | |
| 376 | + return -EBUSY; | |
| 377 | + | |
| 378 | + /* Check to see if current scan mode is correct */ | |
| 379 | + if (st->current_mode != &max1363_mode_table[chan->address]) { | |
| 380 | + int ret; | |
| 381 | + | |
| 382 | + /* Update scan mode if needed */ | |
| 383 | + st->current_mode = &max1363_mode_table[chan->address]; | |
| 384 | + ret = max1363_set_scan_mode(st); | |
| 385 | + if (ret < 0) | |
| 386 | + return ret; | |
| 397 | 387 | } |
| 398 | − data = (rxbuf[1] | rxbuf[0] << 8) & | |
| 399 | − ((1 << st->chip_info->bits) - 1); | |
| 400 | − } else { | |
| 401 | − /* Get reading */ | |
| 402 | − data = st->recv(client, rxbuf, 1); | |
| 403 | − if (data < 0) { | |
| 404 | − ret = data; | |
| 405 | − goto error_ret; | |
| 388 | + if (st->chip_info->bits != 8) { | |
| 389 | + /* Get reading */ | |
| 390 | + data = st->recv(client, rxbuf, 2); | |
| 391 | + if (data < 0) | |
| 392 | + return data; | |
| 393 | + | |
| 394 | + data = (rxbuf[1] | rxbuf[0] << 8) & | |
| 395 | + ((1 << st->chip_info->bits) - 1); | |
| 396 | + } else { | |
| 397 | + /* Get reading */ | |
| 398 | + data = st->recv(client, rxbuf, 1); | |
| 399 | + if (data < 0) | |
| 400 | + return data; | |
| 401 | + | |
| 402 | + data = rxbuf[0]; | |
| 406 | 403 | } |
| 407 | − data = rxbuf[0]; | |
| 408 | − } | |
| 409 | − *val = data; | |
| 410 | − | |
| 411 | −error_ret: | |
| 412 | − mutex_unlock(&st->lock); | |
| 413 | − iio_device_release_direct_mode(indio_dev); | |
| 414 | − return ret; | |
| 404 | + *val = data; | |
| 415 | 405 | |
| 406 | + return 0; | |
| 407 | + } | |
| 408 | + unreachable(); | |
| 416 | 409 | } |
| 417 | 410 | |
| 418 | 411 | static int max1363_read_raw(struct iio_dev *indio_dev, |
| @@ -710,9 +703,8 @@ static ssize_t max1363_monitor_store_freq(struct device *dev, · 291 unmodified lines | ||
| 710 | 703 | if (!found) |
| 711 | 704 | return -EINVAL; |
| 712 | 705 | |
| 713 | − mutex_lock(&st->lock); | |
| 714 | − st->monitor_speed = i; | |
| 715 | − mutex_unlock(&st->lock); | |
| 706 | + scoped_guard(mutex, &st->lock) | |
| 707 | + st->monitor_speed = i; | |
| 716 | 708 | |
| 717 | 709 | return 0; |
| 718 | 710 | } |
| @@ -815,12 +807,11 @@ static int max1363_read_event_config(struct iio_dev *indio_dev, · 96 unmodified lines | ||
| 815 | 807 | int val; |
| 816 | 808 | int number = chan->channel; |
| 817 | 809 | |
| 818 | − mutex_lock(&st->lock); | |
| 810 | + guard(mutex)(&st->lock); | |
| 819 | 811 | if (dir == IIO_EV_DIR_FALLING) |
| 820 | 812 | val = (1 << number) & st->mask_low; |
| 821 | 813 | else |
| 822 | 814 | val = (1 << number) & st->mask_high; |
| 823 | − mutex_unlock(&st->lock); | |
| 824 | 815 | |
| 825 | 816 | return val; |
| 826 | 817 | } |
| @@ -962,46 +953,42 @@ static int max1363_write_event_config(struct iio_dev *indio_dev, · 135 unmodified lines | ||
| 962 | 953 | const struct iio_chan_spec *chan, enum iio_event_type type, |
| 963 | 954 | enum iio_event_direction dir, int state) |
| 964 | 955 | { |
| 965 | − int ret = 0; | |
| 966 | 956 | struct max1363_state *st = iio_priv(indio_dev); |
| 967 | − u16 unifiedmask; | |
| 968 | − int number = chan->channel; | |
| 969 | 957 | |
| 970 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 971 | − if (ret) | |
| 972 | − return ret; | |
| 973 | − mutex_lock(&st->lock); | |
| 974 | − | |
| 975 | − unifiedmask = st->mask_low | st->mask_high; | |
| 976 | − if (dir == IIO_EV_DIR_FALLING) { | |
| 977 | − | |
| 978 | − if (state == 0) | |
| 979 | − st->mask_low &= ~(1 << number); | |
| 980 | − else { | |
| 981 | − ret = __max1363_check_event_mask((1 << number), | |
| 982 | − unifiedmask); | |
| 983 | − if (ret) | |
| 984 | − goto error_ret; | |
| 985 | − st->mask_low |= (1 << number); | |
| 986 | − } | |
| 987 | − } else { | |
| 988 | − if (state == 0) | |
| 989 | − st->mask_high &= ~(1 << number); | |
| 990 | − else { | |
| 991 | − ret = __max1363_check_event_mask((1 << number), | |
| 992 | − unifiedmask); | |
| 993 | − if (ret) | |
| 994 | − goto error_ret; | |
| 995 | − st->mask_high |= (1 << number); | |
| 958 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 959 | + int number = chan->channel; | |
| 960 | + u16 unifiedmask; | |
| 961 | + int ret; | |
| 962 | + | |
| 963 | + guard(mutex)(&st->lock); | |
| 964 | + | |
| 965 | + unifiedmask = st->mask_low | st->mask_high; | |
| 966 | + if (dir == IIO_EV_DIR_FALLING) { | |
| 967 | + | |
| 968 | + if (state == 0) | |
| 969 | + st->mask_low &= ~(1 << number); | |
| 970 | + else { | |
| 971 | + ret = __max1363_check_event_mask((1 << number), | |
| 972 | + unifiedmask); | |
| 973 | + if (ret) | |
| 974 | + return ret; | |
| 975 | + st->mask_low |= (1 << number); | |
| 976 | + } | |
| 977 | + } else { | |
| 978 | + if (state == 0) | |
| 979 | + st->mask_high &= ~(1 << number); | |
| 980 | + else { | |
| 981 | + ret = __max1363_check_event_mask((1 << number), | |
| 982 | + unifiedmask); | |
| 983 | + if (ret) | |
| 984 | + return ret; | |
| 985 | + st->mask_high |= (1 << number); | |
| 986 | + } | |
| 996 | 987 | } |
| 997 | 988 | } |
| 998 | − | |
| 999 | 989 | max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); |
| 1000 | −error_ret: | |
| 1001 | − mutex_unlock(&st->lock); | |
| 1002 | − iio_device_release_direct_mode(indio_dev); | |
| 1003 | 990 | |
| 1004 | − return ret; | |
| 991 | + return 0; | |
| 1005 | 992 | } |
| 1006 | 993 | |
| 1007 | 994 | /* |
| @@ -371,6 +371,11 @@ static const struct mcp320x_chip_info mcp320x_chip_infos[] = { · 370 unmodified lines | ||
| 371 | 371 | }, |
| 372 | 372 | }; |
| 373 | 373 | |
| 374 | +static void mcp320x_regulator_disable(void *reg) | |
| 375 | +{ | |
| 376 | + regulator_disable(reg); | |
| 377 | +} | |
| 378 | + | |
| 374 | 379 | static int mcp320x_probe(struct spi_device *spi) |
| 375 | 380 | { |
| 376 | 381 | struct iio_dev *indio_dev; |
| @@ -388,7 +393,6 @@ static int mcp320x_probe(struct spi_device *spi) · 11 unmodified lines | ||
| 388 | 393 | indio_dev->name = spi_get_device_id(spi)->name; |
| 389 | 394 | indio_dev->modes = INDIO_DIRECT_MODE; |
| 390 | 395 | indio_dev->info = &mcp320x_info; |
| 391 | − spi_set_drvdata(spi, indio_dev); | |
| 392 | 396 | |
| 393 | 397 | device_index = spi_get_device_id(spi)->driver_data; |
| 394 | 398 | chip_info = &mcp320x_chip_infos[device_index]; |
| @@ -445,27 +449,13 @@ static int mcp320x_probe(struct spi_device *spi) · 50 unmodified lines | ||
| 445 | 449 | if (ret < 0) |
| 446 | 450 | return ret; |
| 447 | 451 | |
| 448 | − mutex_init(&adc->lock); | |
| 449 | − | |
| 450 | − ret = iio_device_register(indio_dev); | |
| 452 | + ret = devm_add_action_or_reset(&spi->dev, mcp320x_regulator_disable, adc->reg); | |
| 451 | 453 | if (ret < 0) |
| 452 | − goto reg_disable; | |
| 453 | − | |
| 454 | − return 0; | |
| 455 | − | |
| 456 | −reg_disable: | |
| 457 | − regulator_disable(adc->reg); | |
| 458 | − | |
| 459 | − return ret; | |
| 460 | −} | |
| 454 | + return ret; | |
| 461 | 455 | |
| 462 | −static void mcp320x_remove(struct spi_device *spi) | |
| 463 | −{ | |
| 464 | − struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
| 465 | − struct mcp320x *adc = iio_priv(indio_dev); | |
| 456 | + mutex_init(&adc->lock); | |
| 466 | 457 | |
| 467 | − iio_device_unregister(indio_dev); | |
| 468 | − regulator_disable(adc->reg); | |
| 458 | + return devm_iio_device_register(&spi->dev, indio_dev); | |
| 469 | 459 | } |
| 470 | 460 | |
| 471 | 461 | static const struct of_device_id mcp320x_dt_ids[] = { |
| @@ -520,7 +510,6 @@ static struct spi_driver mcp320x_driver = { · 48 unmodified lines | ||
| 520 | 510 | .of_match_table = mcp320x_dt_ids, |
| 521 | 511 | }, |
| 522 | 512 | .probe = mcp320x_probe, |
| 523 | − .remove = mcp320x_remove, | |
| 524 | 513 | .id_table = mcp320x_id, |
| 525 | 514 | }; |
| 526 | 515 | module_spi_driver(mcp320x_driver); |
| @@ -0,0 +1,1636 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | +/* | |
| 3 | + * IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor | |
| 4 | + * | |
| 5 | + * Copyright (C) 2017-2024 Microchip Technology Inc. and its subsidiaries | |
| 6 | + * | |
| 7 | + * Author: Bogdan Bolocan <bogdan.bolocan@microchip.com> | |
| 8 | + * Author: Victor Tudose | |
| 9 | + * Author: Marius Cristea <marius.cristea@microchip.com> | |
| 10 | + * | |
| 11 | + * Datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here: | |
| 12 | + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf | |
| 13 | + */ | |
| 14 | + | |
| 15 | +#include <linux/acpi.h> | |
| 16 | +#include <linux/bitfield.h> | |
| 17 | +#include <linux/delay.h> | |
| 18 | +#include <linux/device.h> | |
| 19 | +#include <linux/i2c.h> | |
| 20 | +#include <linux/iio/iio.h> | |
| 21 | +#include <linux/iio/sysfs.h> | |
| 22 | +#include <asm/unaligned.h> | |
| 23 | + | |
| 24 | +/* | |
| 25 | + * maximum accumulation time should be (17 * 60 * 1000) around 17 minutes@1024 sps | |
| 26 | + * till PAC1934 accumulation registers starts to saturate | |
| 27 | + */ | |
| 28 | +#define PAC1934_MAX_RFSH_LIMIT_MS 60000 | |
| 29 | +/* 50msec is the timeout for validity of the cached registers */ | |
| 30 | +#define PAC1934_MIN_POLLING_TIME_MS 50 | |
| 31 | +/* | |
| 32 | + * 1000usec is the minimum wait time for normal conversions when sample | |
| 33 | + * rate doesn't change | |
| 34 | + */ | |
| 35 | +#define PAC1934_MIN_UPDATE_WAIT_TIME_US 1000 | |
| 36 | + | |
| 37 | +/* 32000mV */ | |
| 38 | +#define PAC1934_VOLTAGE_MILLIVOLTS_MAX 32000 | |
| 39 | +/* voltage bits resolution when set for unsigned values */ | |
| 40 | +#define PAC1934_VOLTAGE_U_RES 16 | |
| 41 | +/* voltage bits resolution when set for signed values */ | |
| 42 | +#define PAC1934_VOLTAGE_S_RES 15 | |
| 43 | + | |
| 44 | +/* | |
| 45 | + * max signed value that can be stored on 32 bits and 8 digits fractional value | |
| 46 | + * (2^31 - 1) * 10^8 + 99999999 | |
| 47 | + */ | |
| 48 | +#define PAC_193X_MAX_POWER_ACC 214748364799999999LL | |
| 49 | +/* | |
| 50 | + * min signed value that can be stored on 32 bits and 8 digits fractional value | |
| 51 | + * -(2^31) * 10^8 - 99999999 | |
| 52 | + */ | |
| 53 | +#define PAC_193X_MIN_POWER_ACC -214748364899999999LL | |
| 54 | + | |
| 55 | +#define PAC1934_MAX_NUM_CHANNELS 4 | |
| 56 | + | |
| 57 | +#define PAC1934_MEAS_REG_LEN 76 | |
| 58 | +#define PAC1934_CTRL_REG_LEN 12 | |
| 59 | + | |
| 60 | +#define PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ 1024 | |
| 61 | + | |
| 62 | +/* I2C address map */ | |
| 63 | +#define PAC1934_REFRESH_REG_ADDR 0x00 | |
| 64 | +#define PAC1934_CTRL_REG_ADDR 0x01 | |
| 65 | +#define PAC1934_ACC_COUNT_REG_ADDR 0x02 | |
| 66 | +#define PAC1934_VPOWER_ACC_1_ADDR 0x03 | |
| 67 | +#define PAC1934_VPOWER_ACC_2_ADDR 0x04 | |
| 68 | +#define PAC1934_VPOWER_ACC_3_ADDR 0x05 | |
| 69 | +#define PAC1934_VPOWER_ACC_4_ADDR 0x06 | |
| 70 | +#define PAC1934_VBUS_1_ADDR 0x07 | |
| 71 | +#define PAC1934_VBUS_2_ADDR 0x08 | |
| 72 | +#define PAC1934_VBUS_3_ADDR 0x09 | |
| 73 | +#define PAC1934_VBUS_4_ADDR 0x0A | |
| 74 | +#define PAC1934_VSENSE_1_ADDR 0x0B | |
| 75 | +#define PAC1934_VSENSE_2_ADDR 0x0C | |
| 76 | +#define PAC1934_VSENSE_3_ADDR 0x0D | |
| 77 | +#define PAC1934_VSENSE_4_ADDR 0x0E | |
| 78 | +#define PAC1934_VBUS_AVG_1_ADDR 0x0F | |
| 79 | +#define PAC1934_VBUS_AVG_2_ADDR 0x10 | |
| 80 | +#define PAC1934_VBUS_AVG_3_ADDR 0x11 | |
| 81 | +#define PAC1934_VBUS_AVG_4_ADDR 0x12 | |
| 82 | +#define PAC1934_VSENSE_AVG_1_ADDR 0x13 | |
| 83 | +#define PAC1934_VSENSE_AVG_2_ADDR 0x14 | |
| 84 | +#define PAC1934_VSENSE_AVG_3_ADDR 0x15 | |
| 85 | +#define PAC1934_VSENSE_AVG_4_ADDR 0x16 | |
| 86 | +#define PAC1934_VPOWER_1_ADDR 0x17 | |
| 87 | +#define PAC1934_VPOWER_2_ADDR 0x18 | |
| 88 | +#define PAC1934_VPOWER_3_ADDR 0x19 | |
| 89 | +#define PAC1934_VPOWER_4_ADDR 0x1A | |
| 90 | +#define PAC1934_REFRESH_V_REG_ADDR 0x1F | |
| 91 | +#define PAC1934_CTRL_STAT_REGS_ADDR 0x1C | |
| 92 | +#define PAC1934_PID_REG_ADDR 0xFD | |
| 93 | +#define PAC1934_MID_REG_ADDR 0xFE | |
| 94 | +#define PAC1934_RID_REG_ADDR 0xFF | |
| 95 | + | |
| 96 | +/* PRODUCT ID REGISTER + MANUFACTURER ID REGISTER + REVISION ID REGISTER */ | |
| 97 | +#define PAC1934_ID_REG_LEN 3 | |
| 98 | +#define PAC1934_PID_IDX 0 | |
| 99 | +#define PAC1934_MID_IDX 1 | |
| 100 | +#define PAC1934_RID_IDX 2 | |
| 101 | + | |
| 102 | +#define PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS 1 | |
| 103 | +#define PAC1934_ACPI_GET_UOHMS_VALS 2 | |
| 104 | +#define PAC1934_ACPI_GET_BIPOLAR_SETTINGS 4 | |
| 105 | +#define PAC1934_ACPI_GET_SAMP 5 | |
| 106 | + | |
| 107 | +#define PAC1934_SAMPLE_RATE_SHIFT 6 | |
| 108 | + | |
| 109 | +#define PAC1934_VBUS_SENSE_REG_LEN 2 | |
| 110 | +#define PAC1934_ACC_REG_LEN 3 | |
| 111 | +#define PAC1934_VPOWER_REG_LEN 4 | |
| 112 | +#define PAC1934_VPOWER_ACC_REG_LEN 6 | |
| 113 | +#define PAC1934_MAX_REGISTER_LENGTH 6 | |
| 114 | + | |
| 115 | +#define PAC1934_CUSTOM_ATTR_FOR_CHANNEL 1 | |
| 116 | + | |
| 117 | +/* | |
| 118 | + * relative offsets when using multi-byte reads/writes even though these | |
| 119 | + * bytes are read one after the other, they are not at adjacent memory | |
| 120 | + * locations within the I2C memory map. The chip can skip some addresses | |
| 121 | + */ | |
| 122 | +#define PAC1934_CHANNEL_DIS_REG_OFF 0 | |
| 123 | +#define PAC1934_NEG_PWR_REG_OFF 1 | |
| 124 | + | |
| 125 | +/* | |
| 126 | + * when reading/writing multiple bytes from offset PAC1934_CHANNEL_DIS_REG_OFF, | |
| 127 | + * the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets | |
| 128 | + */ | |
| 129 | +#define PAC1934_SLOW_REG_OFF 2 | |
| 130 | +#define PAC1934_CTRL_ACT_REG_OFF 3 | |
| 131 | +#define PAC1934_CHANNEL_DIS_ACT_REG_OFF 4 | |
| 132 | +#define PAC1934_NEG_PWR_ACT_REG_OFF 5 | |
| 133 | +#define PAC1934_CTRL_LAT_REG_OFF 6 | |
| 134 | +#define PAC1934_CHANNEL_DIS_LAT_REG_OFF 7 | |
| 135 | +#define PAC1934_NEG_PWR_LAT_REG_OFF 8 | |
| 136 | +#define PAC1934_PID_REG_OFF 9 | |
| 137 | +#define PAC1934_MID_REG_OFF 10 | |
| 138 | +#define PAC1934_REV_REG_OFF 11 | |
| 139 | +#define PAC1934_CTRL_STATUS_INFO_LEN 12 | |
| 140 | + | |
| 141 | +#define PAC1934_MID 0x5D | |
| 142 | +#define PAC1931_PID 0x58 | |
| 143 | +#define PAC1932_PID 0x59 | |
| 144 | +#define PAC1933_PID 0x5A | |
| 145 | +#define PAC1934_PID 0x5B | |
| 146 | + | |
| 147 | +/* Scale constant = (10^3 * 3.2 * 10^9 / 2^28) for mili Watt-second */ | |
| 148 | +#define PAC1934_SCALE_CONSTANT 11921 | |
| 149 | + | |
| 150 | +#define PAC1934_MAX_VPOWER_RSHIFTED_BY_28B 11921 | |
| 151 | +#define PAC1934_MAX_VSENSE_RSHIFTED_BY_16B 1525 | |
| 152 | + | |
| 153 | +#define PAC1934_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) | |
| 154 | + | |
| 155 | +#define PAC1934_CRTL_SAMPLE_RATE_MASK GENMASK(7, 6) | |
| 156 | +#define PAC1934_CHAN_SLEEP_MASK BIT(5) | |
| 157 | +#define PAC1934_CHAN_SLEEP_SET BIT(5) | |
| 158 | +#define PAC1934_CHAN_SINGLE_MASK BIT(4) | |
| 159 | +#define PAC1934_CHAN_SINGLE_SHOT_SET BIT(4) | |
| 160 | +#define PAC1934_CHAN_ALERT_MASK BIT(3) | |
| 161 | +#define PAC1934_CHAN_ALERT_EN BIT(3) | |
| 162 | +#define PAC1934_CHAN_ALERT_CC_MASK BIT(2) | |
| 163 | +#define PAC1934_CHAN_ALERT_CC_EN BIT(2) | |
| 164 | +#define PAC1934_CHAN_OVF_ALERT_MASK BIT(1) | |
| 165 | +#define PAC1934_CHAN_OVF_ALERT_EN BIT(1) | |
| 166 | +#define PAC1934_CHAN_OVF_MASK BIT(0) | |
| 167 | + | |
| 168 | +#define PAC1934_CHAN_DIS_CH1_OFF_MASK BIT(7) | |
| 169 | +#define PAC1934_CHAN_DIS_CH2_OFF_MASK BIT(6) | |
| 170 | +#define PAC1934_CHAN_DIS_CH3_OFF_MASK BIT(5) | |
| 171 | +#define PAC1934_CHAN_DIS_CH4_OFF_MASK BIT(4) | |
| 172 | +#define PAC1934_SMBUS_TIMEOUT_MASK BIT(3) | |
| 173 | +#define PAC1934_SMBUS_BYTECOUNT_MASK BIT(2) | |
| 174 | +#define PAC1934_SMBUS_NO_SKIP_MASK BIT(1) | |
| 175 | + | |
| 176 | +#define PAC1934_NEG_PWR_CH1_BIDI_MASK BIT(7) | |
| 177 | +#define PAC1934_NEG_PWR_CH2_BIDI_MASK BIT(6) | |
| 178 | +#define PAC1934_NEG_PWR_CH3_BIDI_MASK BIT(5) | |
| 179 | +#define PAC1934_NEG_PWR_CH4_BIDI_MASK BIT(4) | |
| 180 | +#define PAC1934_NEG_PWR_CH1_BIDV_MASK BIT(3) | |
| 181 | +#define PAC1934_NEG_PWR_CH2_BIDV_MASK BIT(2) | |
| 182 | +#define PAC1934_NEG_PWR_CH3_BIDV_MASK BIT(1) | |
| 183 | +#define PAC1934_NEG_PWR_CH4_BIDV_MASK BIT(0) | |
| 184 | + | |
| 185 | +/* | |
| 186 | + * Universal Unique Identifier (UUID), | |
| 187 | + * 033771E0-1705-47B4-9535-D1BBE14D9A09, | |
| 188 | + * is reserved to Microchip for the PAC1934. | |
| 189 | + */ | |
| 190 | +#define PAC1934_DSM_UUID "033771E0-1705-47B4-9535-D1BBE14D9A09" | |
| 191 | + | |
| 192 | +enum pac1934_ids { | |
| 193 | + PAC1931, | |
| 194 | + PAC1932, | |
| 195 | + PAC1933, | |
| 196 | + PAC1934 | |
| 197 | +}; | |
| 198 | + | |
| 199 | +enum pac1934_samps { | |
| 200 | + PAC1934_SAMP_1024SPS, | |
| 201 | + PAC1934_SAMP_256SPS, | |
| 202 | + PAC1934_SAMP_64SPS, | |
| 203 | + PAC1934_SAMP_8SPS | |
| 204 | +}; | |
| 205 | + | |
| 206 | +/* | |
| 207 | + * these indexes are exactly describing the element order within a single | |
| 208 | + * PAC1934 phys channel IIO channel descriptor; see the static const struct | |
| 209 | + * iio_chan_spec pac1934_single_channel[] declaration | |
| 210 | + */ | |
| 211 | +enum pac1934_ch_idx { | |
| 212 | + PAC1934_CH_ENERGY, | |
| 213 | + PAC1934_CH_POWER, | |
| 214 | + PAC1934_CH_VOLTAGE, | |
| 215 | + PAC1934_CH_CURRENT, | |
| 216 | + PAC1934_CH_VOLTAGE_AVERAGE, | |
| 217 | + PAC1934_CH_CURRENT_AVERAGE | |
| 218 | +}; | |
| 219 | + | |
| 220 | +/** | |
| 221 | + * struct pac1934_features - features of a pac1934 instance | |
| 222 | + * @phys_channels: number of physical channels supported by the chip | |
| 223 | + * @name: chip's name | |
| 224 | + */ | |
| 225 | +struct pac1934_features { | |
| 226 | + u8 phys_channels; | |
| 227 | + const char *name; | |
| 228 | +}; | |
| 229 | + | |
| 230 | +struct samp_rate_mapping { | |
| 231 | + u16 samp_rate; | |
| 232 | + u8 shift2value; | |
| 233 | +}; | |
| 234 | + | |
| 235 | +static const unsigned int samp_rate_map_tbl[] = { | |
| 236 | + [PAC1934_SAMP_1024SPS] = 1024, | |
| 237 | + [PAC1934_SAMP_256SPS] = 256, | |
| 238 | + [PAC1934_SAMP_64SPS] = 64, | |
| 239 | + [PAC1934_SAMP_8SPS] = 8, | |
| 240 | +}; | |
| 241 | + | |
| 242 | +static const struct pac1934_features pac1934_chip_config[] = { | |
| 243 | + [PAC1931] = { | |
| 244 | + .phys_channels = 1, | |
| 245 | + .name = "pac1931", | |
| 246 | + }, | |
| 247 | + [PAC1932] = { | |
| 248 | + .phys_channels = 2, | |
| 249 | + .name = "pac1932", | |
| 250 | + }, | |
| 251 | + [PAC1933] = { | |
| 252 | + .phys_channels = 3, | |
| 253 | + .name = "pac1933", | |
| 254 | + }, | |
| 255 | + [PAC1934] = { | |
| 256 | + .phys_channels = 4, | |
| 257 | + .name = "pac1934", | |
| 258 | + }, | |
| 259 | +}; | |
| 260 | + | |
| 261 | +/** | |
| 262 | + * struct reg_data - data from the registers | |
| 263 | + * @meas_regs: snapshot of raw measurements registers | |
| 264 | + * @ctrl_regs: snapshot of control registers | |
| 265 | + * @energy_sec_acc: snapshot of energy values | |
| 266 | + * @vpower_acc: accumulated vpower values | |
| 267 | + * @vpower: snapshot of vpower registers | |
| 268 | + * @vbus: snapshot of vbus registers | |
| 269 | + * @vbus_avg: averages of vbus registers | |
| 270 | + * @vsense: snapshot of vsense registers | |
| 271 | + * @vsense_avg: averages of vsense registers | |
| 272 | + * @num_enabled_channels: count of how many chip channels are currently enabled | |
| 273 | + */ | |
| 274 | +struct reg_data { | |
| 275 | + u8 meas_regs[PAC1934_MEAS_REG_LEN]; | |
| 276 | + u8 ctrl_regs[PAC1934_CTRL_REG_LEN]; | |
| 277 | + s64 energy_sec_acc[PAC1934_MAX_NUM_CHANNELS]; | |
| 278 | + s64 vpower_acc[PAC1934_MAX_NUM_CHANNELS]; | |
| 279 | + s32 vpower[PAC1934_MAX_NUM_CHANNELS]; | |
| 280 | + s32 vbus[PAC1934_MAX_NUM_CHANNELS]; | |
| 281 | + s32 vbus_avg[PAC1934_MAX_NUM_CHANNELS]; | |
| 282 | + s32 vsense[PAC1934_MAX_NUM_CHANNELS]; | |
| 283 | + s32 vsense_avg[PAC1934_MAX_NUM_CHANNELS]; | |
| 284 | + u8 num_enabled_channels; | |
| 285 | +}; | |
| 286 | + | |
| 287 | +/** | |
| 288 | + * struct pac1934_chip_info - information about the chip | |
| 289 | + * @client: the i2c-client attached to the device | |
| 290 | + * @lock: synchronize access to driver's state members | |
| 291 | + * @work_chip_rfsh: work queue used for refresh commands | |
| 292 | + * @phys_channels: phys channels count | |
| 293 | + * @active_channels: array of values, true means that channel is active | |
| 294 | + * @enable_energy: array of values, true means that channel energy is measured | |
| 295 | + * @bi_dir: array of bools, true means that channel is bidirectional | |
| 296 | + * @chip_variant: chip variant | |
| 297 | + * @chip_revision: chip revision | |
| 298 | + * @shunts: shunts | |
| 299 | + * @chip_reg_data: chip reg data | |
| 300 | + * @sample_rate_value: sampling frequency | |
| 301 | + * @labels: table with channels labels | |
| 302 | + * @iio_info: iio_info | |
| 303 | + * @tstamp: chip's uptime | |
| 304 | + */ | |
| 305 | +struct pac1934_chip_info { | |
| 306 | + struct i2c_client *client; | |
| 307 | + struct mutex lock; /* synchronize access to driver's state members */ | |
| 308 | + struct delayed_work work_chip_rfsh; | |
| 309 | + u8 phys_channels; | |
| 310 | + bool active_channels[PAC1934_MAX_NUM_CHANNELS]; | |
| 311 | + bool enable_energy[PAC1934_MAX_NUM_CHANNELS]; | |
| 312 | + bool bi_dir[PAC1934_MAX_NUM_CHANNELS]; | |
| 313 | + u8 chip_variant; | |
| 314 | + u8 chip_revision; | |
| 315 | + u32 shunts[PAC1934_MAX_NUM_CHANNELS]; | |
| 316 | + struct reg_data chip_reg_data; | |
| 317 | + s32 sample_rate_value; | |
| 318 | + char *labels[PAC1934_MAX_NUM_CHANNELS]; | |
| 319 | + struct iio_info iio_info; | |
| 320 | + unsigned long tstamp; | |
| 321 | +}; | |
| 322 | + | |
| 323 | +#define TO_PAC1934_CHIP_INFO(d) container_of(d, struct pac1934_chip_info, work_chip_rfsh) | |
| 324 | + | |
| 325 | +#define PAC1934_VPOWER_ACC_CHANNEL(_index, _si, _address) { \ | |
| 326 | + .type = IIO_ENERGY, \ | |
| 327 | + .address = (_address), \ | |
| 328 | + .indexed = 1, \ | |
| 329 | + .channel = (_index), \ | |
| 330 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
| 331 | + BIT(IIO_CHAN_INFO_SCALE) | \ | |
| 332 | + BIT(IIO_CHAN_INFO_ENABLE), \ | |
| 333 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 334 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 335 | + .scan_index = (_si), \ | |
| 336 | + .scan_type = { \ | |
| 337 | + .sign = 'u', \ | |
| 338 | + .realbits = 48, \ | |
| 339 | + .storagebits = 64, \ | |
| 340 | + .endianness = IIO_CPU, \ | |
| 341 | + } \ | |
| 342 | +} | |
| 343 | + | |
| 344 | +#define PAC1934_VBUS_CHANNEL(_index, _si, _address) { \ | |
| 345 | + .type = IIO_VOLTAGE, \ | |
| 346 | + .address = (_address), \ | |
| 347 | + .indexed = 1, \ | |
| 348 | + .channel = (_index), \ | |
| 349 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
| 350 | + BIT(IIO_CHAN_INFO_SCALE), \ | |
| 351 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 352 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 353 | + .scan_index = (_si), \ | |
| 354 | + .scan_type = { \ | |
| 355 | + .sign = 'u', \ | |
| 356 | + .realbits = 16, \ | |
| 357 | + .storagebits = 16, \ | |
| 358 | + .endianness = IIO_CPU, \ | |
| 359 | + } \ | |
| 360 | +} | |
| 361 | + | |
| 362 | +#define PAC1934_VBUS_AVG_CHANNEL(_index, _si, _address) { \ | |
| 363 | + .type = IIO_VOLTAGE, \ | |
| 364 | + .address = (_address), \ | |
| 365 | + .indexed = 1, \ | |
| 366 | + .channel = (_index), \ | |
| 367 | + .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ | |
| 368 | + BIT(IIO_CHAN_INFO_SCALE), \ | |
| 369 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 370 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 371 | + .scan_index = (_si), \ | |
| 372 | + .scan_type = { \ | |
| 373 | + .sign = 'u', \ | |
| 374 | + .realbits = 16, \ | |
| 375 | + .storagebits = 16, \ | |
| 376 | + .endianness = IIO_CPU, \ | |
| 377 | + } \ | |
| 378 | +} | |
| 379 | + | |
| 380 | +#define PAC1934_VSENSE_CHANNEL(_index, _si, _address) { \ | |
| 381 | + .type = IIO_CURRENT, \ | |
| 382 | + .address = (_address), \ | |
| 383 | + .indexed = 1, \ | |
| 384 | + .channel = (_index), \ | |
| 385 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
| 386 | + BIT(IIO_CHAN_INFO_SCALE), \ | |
| 387 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 388 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 389 | + .scan_index = (_si), \ | |
| 390 | + .scan_type = { \ | |
| 391 | + .sign = 'u', \ | |
| 392 | + .realbits = 16, \ | |
| 393 | + .storagebits = 16, \ | |
| 394 | + .endianness = IIO_CPU, \ | |
| 395 | + } \ | |
| 396 | +} | |
| 397 | + | |
| 398 | +#define PAC1934_VSENSE_AVG_CHANNEL(_index, _si, _address) { \ | |
| 399 | + .type = IIO_CURRENT, \ | |
| 400 | + .address = (_address), \ | |
| 401 | + .indexed = 1, \ | |
| 402 | + .channel = (_index), \ | |
| 403 | + .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ | |
| 404 | + BIT(IIO_CHAN_INFO_SCALE), \ | |
| 405 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 406 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 407 | + .scan_index = (_si), \ | |
| 408 | + .scan_type = { \ | |
| 409 | + .sign = 'u', \ | |
| 410 | + .realbits = 16, \ | |
| 411 | + .storagebits = 16, \ | |
| 412 | + .endianness = IIO_CPU, \ | |
| 413 | + } \ | |
| 414 | +} | |
| 415 | + | |
| 416 | +#define PAC1934_VPOWER_CHANNEL(_index, _si, _address) { \ | |
| 417 | + .type = IIO_POWER, \ | |
| 418 | + .address = (_address), \ | |
| 419 | + .indexed = 1, \ | |
| 420 | + .channel = (_index), \ | |
| 421 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
| 422 | + BIT(IIO_CHAN_INFO_SCALE), \ | |
| 423 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 424 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 425 | + .scan_index = (_si), \ | |
| 426 | + .scan_type = { \ | |
| 427 | + .sign = 'u', \ | |
| 428 | + .realbits = 28, \ | |
| 429 | + .storagebits = 32, \ | |
| 430 | + .shift = 4, \ | |
| 431 | + .endianness = IIO_CPU, \ | |
| 432 | + } \ | |
| 433 | +} | |
| 434 | + | |
| 435 | +static const struct iio_chan_spec pac1934_single_channel[] = { | |
| 436 | + PAC1934_VPOWER_ACC_CHANNEL(0, 0, PAC1934_VPOWER_ACC_1_ADDR), | |
| 437 | + PAC1934_VPOWER_CHANNEL(0, 0, PAC1934_VPOWER_1_ADDR), | |
| 438 | + PAC1934_VBUS_CHANNEL(0, 0, PAC1934_VBUS_1_ADDR), | |
| 439 | + PAC1934_VSENSE_CHANNEL(0, 0, PAC1934_VSENSE_1_ADDR), | |
| 440 | + PAC1934_VBUS_AVG_CHANNEL(0, 0, PAC1934_VBUS_AVG_1_ADDR), | |
| 441 | + PAC1934_VSENSE_AVG_CHANNEL(0, 0, PAC1934_VSENSE_AVG_1_ADDR), | |
| 442 | +}; | |
| 443 | + | |
| 444 | +/* Low-level I2c functions used to transfer up to 76 bytes at once */ | |
| 445 | +static int pac1934_i2c_read(struct i2c_client *client, u8 reg_addr, | |
| 446 | + void *databuf, u8 len) | |
| 447 | +{ | |
| 448 | + int ret; | |
| 449 | + struct i2c_msg msgs[2] = { | |
| 450 | + { | |
| 451 | + .addr = client->addr, | |
| 452 | + .len = 1, | |
| 453 | + .buf = (u8 *)®_addr, | |
| 454 | + }, | |
| 455 | + { | |
| 456 | + .addr = client->addr, | |
| 457 | + .len = len, | |
| 458 | + .buf = databuf, | |
| 459 | + .flags = I2C_M_RD | |
| 460 | + } | |
| 461 | + }; | |
| 462 | + | |
| 463 | + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | |
| 464 | + if (ret < 0) | |
| 465 | + return ret; | |
| 466 | + | |
| 467 | + return 0; | |
| 468 | +} | |
| 469 | + | |
| 470 | +static int pac1934_get_samp_rate_idx(struct pac1934_chip_info *info, | |
| 471 | + u32 new_samp_rate) | |
| 472 | +{ | |
| 473 | + int cnt; | |
| 474 | + | |
| 475 | + for (cnt = 0; cnt < ARRAY_SIZE(samp_rate_map_tbl); cnt++) | |
| 476 | + if (new_samp_rate == samp_rate_map_tbl[cnt]) | |
| 477 | + return cnt; | |
| 478 | + | |
| 479 | + /* not a valid sample rate value */ | |
| 480 | + return -EINVAL; | |
| 481 | +} | |
| 482 | + | |
| 483 | +static ssize_t pac1934_shunt_value_show(struct device *dev, | |
| 484 | + struct device_attribute *attr, | |
| 485 | + char *buf) | |
| 486 | +{ | |
| 487 | + struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| 488 | + struct pac1934_chip_info *info = iio_priv(indio_dev); | |
| 489 | + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
| 490 | + | |
| 491 | + return sysfs_emit(buf, "%u\n", info->shunts[this_attr->address]); | |
| 492 | +} | |
| 493 | + | |
| 494 | +static ssize_t pac1934_shunt_value_store(struct device *dev, | |
| 495 | + struct device_attribute *attr, | |
| 496 | + const char *buf, size_t count) | |
| 497 | +{ | |
| 498 | + struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| 499 | + struct pac1934_chip_info *info = iio_priv(indio_dev); | |
| 500 | + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
| 501 | + int sh_val; | |
| 502 | + | |
| 503 | + if (kstrtouint(buf, 10, &sh_val)) { | |
| 504 | + dev_err(dev, "Shunt value is not valid\n"); | |
| 505 | + return -EINVAL; | |
| 506 | + } | |
| 507 | + | |
| 508 | + scoped_guard(mutex, &info->lock) | |
| 509 | + info->shunts[this_attr->address] = sh_val; | |
| 510 | + | |
| 511 | + return count; | |
| 512 | +} | |
| 513 | + | |
| 514 | +static int pac1934_read_avail(struct iio_dev *indio_dev, | |
| 515 | + struct iio_chan_spec const *channel, | |
| 516 | + const int **vals, int *type, int *length, long mask) | |
| 517 | +{ | |
| 518 | + switch (mask) { | |
| 519 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 520 | + *type = IIO_VAL_INT; | |
| 521 | + *vals = samp_rate_map_tbl; | |
| 522 | + *length = ARRAY_SIZE(samp_rate_map_tbl); | |
| 523 | + return IIO_AVAIL_LIST; | |
| 524 | + } | |
| 525 | + | |
| 526 | + return -EINVAL; | |
| 527 | +} | |
| 528 | + | |
| 529 | +static int pac1934_send_refresh(struct pac1934_chip_info *info, | |
| 530 | + u8 refresh_cmd, u32 wait_time) | |
| 531 | +{ | |
| 532 | + /* this function only sends REFRESH or REFRESH_V */ | |
| 533 | + struct i2c_client *client = info->client; | |
| 534 | + int ret; | |
| 535 | + u8 bidir_reg; | |
| 536 | + bool revision_bug = false; | |
| 537 | + | |
| 538 | + if (info->chip_revision == 2 || info->chip_revision == 3) { | |
| 539 | + /* | |
| 540 | + * chip rev 2 and 3 bug workaround | |
| 541 | + * see: PAC1934 Family Data Sheet Errata DS80000836A.pdf | |
| 542 | + */ | |
| 543 | + revision_bug = true; | |
| 544 | + | |
| 545 | + bidir_reg = | |
| 546 | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) | | |
| 547 | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) | | |
| 548 | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) | | |
| 549 | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) | | |
| 550 | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) | | |
| 551 | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) | | |
| 552 | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) | | |
| 553 | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]); | |
| 554 | + | |
| 555 | + ret = i2c_smbus_write_byte_data(client, | |
| 556 | + PAC1934_CTRL_STAT_REGS_ADDR + | |
| 557 | + PAC1934_NEG_PWR_REG_OFF, | |
| 558 | + bidir_reg); | |
| 559 | + if (ret) | |
| 560 | + return ret; | |
| 561 | + } | |
| 562 | + | |
| 563 | + ret = i2c_smbus_write_byte(client, refresh_cmd); | |
| 564 | + if (ret) { | |
| 565 | + dev_err(&client->dev, "%s - cannot send 0x%02X\n", | |
| 566 | + __func__, refresh_cmd); | |
| 567 | + return ret; | |
| 568 | + } | |
| 569 | + | |
| 570 | + if (revision_bug) { | |
| 571 | + /* | |
| 572 | + * chip rev 2 and 3 bug workaround - write again the same | |
| 573 | + * register write the updated registers back | |
| 574 | + */ | |
| 575 | + ret = i2c_smbus_write_byte_data(client, | |
| 576 | + PAC1934_CTRL_STAT_REGS_ADDR + | |
| 577 | + PAC1934_NEG_PWR_REG_OFF, bidir_reg); | |
| 578 | + if (ret) | |
| 579 | + return ret; | |
| 580 | + } | |
| 581 | + | |
| 582 | + /* register data retrieval timestamp */ | |
| 583 | + info->tstamp = jiffies; | |
| 584 | + | |
| 585 | + /* wait till the data is available */ | |
| 586 | + usleep_range(wait_time, wait_time + 100); | |
| 587 | + | |
| 588 | + return ret; | |
| 589 | +} | |
| 590 | + | |
| 591 | +static int pac1934_reg_snapshot(struct pac1934_chip_info *info, | |
| 592 | + bool do_refresh, u8 refresh_cmd, u32 wait_time) | |
| 593 | +{ | |
| 594 | + int ret; | |
| 595 | + struct i2c_client *client = info->client; | |
| 596 | + u8 samp_shift, ctrl_regs_tmp; | |
| 597 | + u8 *offset_reg_data_p; | |
| 598 | + u16 tmp_value; | |
| 599 | + u32 samp_rate, cnt, tmp; | |
| 600 | + s64 curr_energy, inc; | |
| 601 | + u64 tmp_energy; | |
| 602 | + struct reg_data *reg_data; | |
| 603 | + | |
| 604 | + guard(mutex)(&info->lock); | |
| 605 | + | |
| 606 | + if (do_refresh) { | |
| 607 | + ret = pac1934_send_refresh(info, refresh_cmd, wait_time); | |
| 608 | + if (ret < 0) { | |
| 609 | + dev_err(&client->dev, | |
| 610 | + "%s - cannot send refresh\n", | |
| 611 | + __func__); | |
| 612 | + return ret; | |
| 613 | + } | |
| 614 | + } | |
| 615 | + | |
| 616 | + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, | |
| 617 | + PAC1934_CTRL_REG_LEN, | |
| 618 | + (u8 *)info->chip_reg_data.ctrl_regs); | |
| 619 | + if (ret < 0) { | |
| 620 | + dev_err(&client->dev, | |
| 621 | + "%s - cannot read ctrl/status registers\n", | |
| 622 | + __func__); | |
| 623 | + return ret; | |
| 624 | + } | |
| 625 | + | |
| 626 | + reg_data = &info->chip_reg_data; | |
| 627 | + | |
| 628 | + /* read the data registers */ | |
| 629 | + ret = pac1934_i2c_read(client, PAC1934_ACC_COUNT_REG_ADDR, | |
| 630 | + (u8 *)reg_data->meas_regs, PAC1934_MEAS_REG_LEN); | |
| 631 | + if (ret) { | |
| 632 | + dev_err(&client->dev, | |
| 633 | + "%s - cannot read ACC_COUNT register: %d:%d\n", | |
| 634 | + __func__, ret, PAC1934_MEAS_REG_LEN); | |
| 635 | + return ret; | |
| 636 | + } | |
| 637 | + | |
| 638 | + /* see how much shift is required by the sample rate */ | |
| 639 | + samp_rate = samp_rate_map_tbl[((reg_data->ctrl_regs[PAC1934_CTRL_LAT_REG_OFF]) >> 6)]; | |
| 640 | + samp_shift = get_count_order(samp_rate); | |
| 641 | + | |
| 642 | + ctrl_regs_tmp = reg_data->ctrl_regs[PAC1934_CHANNEL_DIS_LAT_REG_OFF]; | |
| 643 | + offset_reg_data_p = ®_data->meas_regs[PAC1934_ACC_REG_LEN]; | |
| 644 | + | |
| 645 | + /* start with VPOWER_ACC */ | |
| 646 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 647 | + /* check if the channel is active, skip all fields if disabled */ | |
| 648 | + if ((ctrl_regs_tmp << cnt) & 0x80) | |
| 649 | + continue; | |
| 650 | + | |
| 651 | + /* skip if the energy accumulation is disabled */ | |
| 652 | + if (info->enable_energy[cnt]) { | |
| 653 | + curr_energy = info->chip_reg_data.energy_sec_acc[cnt]; | |
| 654 | + | |
| 655 | + tmp_energy = get_unaligned_be48(offset_reg_data_p); | |
| 656 | + | |
| 657 | + if (info->bi_dir[cnt]) | |
| 658 | + reg_data->vpower_acc[cnt] = sign_extend64(tmp_energy, 47); | |
| 659 | + else | |
| 660 | + reg_data->vpower_acc[cnt] = tmp_energy; | |
| 661 | + | |
| 662 | + /* | |
| 663 | + * compute the scaled to 1 second accumulated energy value; | |
| 664 | + * energy accumulator scaled to 1sec = VPOWER_ACC/2^samp_shift | |
| 665 | + * the chip's sampling rate is 2^samp_shift samples/sec | |
| 666 | + */ | |
| 667 | + inc = (reg_data->vpower_acc[cnt] >> samp_shift); | |
| 668 | + | |
| 669 | + /* add the power_acc field */ | |
| 670 | + curr_energy += inc; | |
| 671 | + | |
| 672 | + clamp(curr_energy, PAC_193X_MIN_POWER_ACC, PAC_193X_MAX_POWER_ACC); | |
| 673 | + | |
| 674 | + reg_data->energy_sec_acc[cnt] = curr_energy; | |
| 675 | + } | |
| 676 | + | |
| 677 | + offset_reg_data_p += PAC1934_VPOWER_ACC_REG_LEN; | |
| 678 | + } | |
| 679 | + | |
| 680 | + /* continue with VBUS */ | |
| 681 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 682 | + if ((ctrl_regs_tmp << cnt) & 0x80) | |
| 683 | + continue; | |
| 684 | + | |
| 685 | + tmp_value = get_unaligned_be16(offset_reg_data_p); | |
| 686 | + | |
| 687 | + if (info->bi_dir[cnt]) | |
| 688 | + reg_data->vbus[cnt] = sign_extend32((u32)(tmp_value), 15); | |
| 689 | + else | |
| 690 | + reg_data->vbus[cnt] = tmp_value; | |
| 691 | + | |
| 692 | + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; | |
| 693 | + } | |
| 694 | + | |
| 695 | + /* VSENSE */ | |
| 696 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 697 | + if ((ctrl_regs_tmp << cnt) & 0x80) | |
| 698 | + continue; | |
| 699 | + | |
| 700 | + tmp_value = get_unaligned_be16(offset_reg_data_p); | |
| 701 | + | |
| 702 | + if (info->bi_dir[cnt]) | |
| 703 | + reg_data->vsense[cnt] = sign_extend32((u32)(tmp_value), 15); | |
| 704 | + else | |
| 705 | + reg_data->vsense[cnt] = tmp_value; | |
| 706 | + | |
| 707 | + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; | |
| 708 | + } | |
| 709 | + | |
| 710 | + /* VBUS_AVG */ | |
| 711 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 712 | + if ((ctrl_regs_tmp << cnt) & 0x80) | |
| 713 | + continue; | |
| 714 | + | |
| 715 | + tmp_value = get_unaligned_be16(offset_reg_data_p); | |
| 716 | + | |
| 717 | + if (info->bi_dir[cnt]) | |
| 718 | + reg_data->vbus_avg[cnt] = sign_extend32((u32)(tmp_value), 15); | |
| 719 | + else | |
| 720 | + reg_data->vbus_avg[cnt] = tmp_value; | |
| 721 | + | |
| 722 | + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; | |
| 723 | + } | |
| 724 | + | |
| 725 | + /* VSENSE_AVG */ | |
| 726 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 727 | + if ((ctrl_regs_tmp << cnt) & 0x80) | |
| 728 | + continue; | |
| 729 | + | |
| 730 | + tmp_value = get_unaligned_be16(offset_reg_data_p); | |
| 731 | + | |
| 732 | + if (info->bi_dir[cnt]) | |
| 733 | + reg_data->vsense_avg[cnt] = sign_extend32((u32)(tmp_value), 15); | |
| 734 | + else | |
| 735 | + reg_data->vsense_avg[cnt] = tmp_value; | |
| 736 | + | |
| 737 | + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; | |
| 738 | + } | |
| 739 | + | |
| 740 | + /* VPOWER */ | |
| 741 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 742 | + if ((ctrl_regs_tmp << cnt) & 0x80) | |
| 743 | + continue; | |
| 744 | + | |
| 745 | + tmp = get_unaligned_be32(offset_reg_data_p) >> 4; | |
| 746 | + | |
| 747 | + if (info->bi_dir[cnt]) | |
| 748 | + reg_data->vpower[cnt] = sign_extend32(tmp, 27); | |
| 749 | + else | |
| 750 | + reg_data->vpower[cnt] = tmp; | |
| 751 | + | |
| 752 | + offset_reg_data_p += PAC1934_VPOWER_REG_LEN; | |
| 753 | + } | |
| 754 | + | |
| 755 | + return 0; | |
| 756 | +} | |
| 757 | + | |
| 758 | +static int pac1934_retrieve_data(struct pac1934_chip_info *info, | |
| 759 | + u32 wait_time) | |
| 760 | +{ | |
| 761 | + int ret = 0; | |
| 762 | + | |
| 763 | + /* | |
| 764 | + * check if the minimal elapsed time has passed and if so, | |
| 765 | + * re-read the chip, otherwise the cached info is just fine | |
| 766 | + */ | |
| 767 | + if (time_after(jiffies, info->tstamp + msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS))) { | |
| 768 | + ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, | |
| 769 | + wait_time); | |
| 770 | + | |
| 771 | + /* | |
| 772 | + * Re-schedule the work for the read registers on timeout | |
| 773 | + * (to prevent chip registers saturation) | |
| 774 | + */ | |
| 775 | + mod_delayed_work(system_wq, &info->work_chip_rfsh, | |
| 776 | + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); | |
| 777 | + } | |
| 778 | + | |
| 779 | + return ret; | |
| 780 | +} | |
| 781 | + | |
| 782 | +static int pac1934_read_raw(struct iio_dev *indio_dev, | |
| 783 | + struct iio_chan_spec const *chan, int *val, | |
| 784 | + int *val2, long mask) | |
| 785 | +{ | |
| 786 | + struct pac1934_chip_info *info = iio_priv(indio_dev); | |
| 787 | + s64 curr_energy; | |
| 788 | + int ret, channel = chan->channel - 1; | |
| 789 | + | |
| 790 | + ret = pac1934_retrieve_data(info, PAC1934_MIN_UPDATE_WAIT_TIME_US); | |
| 791 | + if (ret < 0) | |
| 792 | + return ret; | |
| 793 | + | |
| 794 | + switch (mask) { | |
| 795 | + case IIO_CHAN_INFO_RAW: | |
| 796 | + switch (chan->type) { | |
| 797 | + case IIO_VOLTAGE: | |
| 798 | + *val = info->chip_reg_data.vbus[channel]; | |
| 799 | + return IIO_VAL_INT; | |
| 800 | + case IIO_CURRENT: | |
| 801 | + *val = info->chip_reg_data.vsense[channel]; | |
| 802 | + return IIO_VAL_INT; | |
| 803 | + case IIO_POWER: | |
| 804 | + *val = info->chip_reg_data.vpower[channel]; | |
| 805 | + return IIO_VAL_INT; | |
| 806 | + case IIO_ENERGY: | |
| 807 | + curr_energy = info->chip_reg_data.energy_sec_acc[channel]; | |
| 808 | + *val = (u32)curr_energy; | |
| 809 | + *val2 = (u32)(curr_energy >> 32); | |
| 810 | + return IIO_VAL_INT_64; | |
| 811 | + default: | |
| 812 | + return -EINVAL; | |
| 813 | + } | |
| 814 | + case IIO_CHAN_INFO_AVERAGE_RAW: | |
| 815 | + switch (chan->type) { | |
| 816 | + case IIO_VOLTAGE: | |
| 817 | + *val = info->chip_reg_data.vbus_avg[channel]; | |
| 818 | + return IIO_VAL_INT; | |
| 819 | + case IIO_CURRENT: | |
| 820 | + *val = info->chip_reg_data.vsense_avg[channel]; | |
| 821 | + return IIO_VAL_INT; | |
| 822 | + default: | |
| 823 | + return -EINVAL; | |
| 824 | + } | |
| 825 | + case IIO_CHAN_INFO_SCALE: | |
| 826 | + switch (chan->address) { | |
| 827 | + /* Voltages - scale for millivolts */ | |
| 828 | + case PAC1934_VBUS_1_ADDR: | |
| 829 | + case PAC1934_VBUS_2_ADDR: | |
| 830 | + case PAC1934_VBUS_3_ADDR: | |
| 831 | + case PAC1934_VBUS_4_ADDR: | |
| 832 | + case PAC1934_VBUS_AVG_1_ADDR: | |
| 833 | + case PAC1934_VBUS_AVG_2_ADDR: | |
| 834 | + case PAC1934_VBUS_AVG_3_ADDR: | |
| 835 | + case PAC1934_VBUS_AVG_4_ADDR: | |
| 836 | + *val = PAC1934_VOLTAGE_MILLIVOLTS_MAX; | |
| 837 | + if (chan->scan_type.sign == 'u') | |
| 838 | + *val2 = PAC1934_VOLTAGE_U_RES; | |
| 839 | + else | |
| 840 | + *val2 = PAC1934_VOLTAGE_S_RES; | |
| 841 | + return IIO_VAL_FRACTIONAL_LOG2; | |
| 842 | + /* | |
| 843 | + * Currents - scale for mA - depends on the | |
| 844 | + * channel's shunt value | |
| 845 | + * (100mV * 1000000) / (2^16 * shunt(uohm)) | |
| 846 | + */ | |
| 847 | + case PAC1934_VSENSE_1_ADDR: | |
| 848 | + case PAC1934_VSENSE_2_ADDR: | |
| 849 | + case PAC1934_VSENSE_3_ADDR: | |
| 850 | + case PAC1934_VSENSE_4_ADDR: | |
| 851 | + case PAC1934_VSENSE_AVG_1_ADDR: | |
| 852 | + case PAC1934_VSENSE_AVG_2_ADDR: | |
| 853 | + case PAC1934_VSENSE_AVG_3_ADDR: | |
| 854 | + case PAC1934_VSENSE_AVG_4_ADDR: | |
| 855 | + *val = PAC1934_MAX_VSENSE_RSHIFTED_BY_16B; | |
| 856 | + if (chan->scan_type.sign == 'u') | |
| 857 | + *val2 = info->shunts[channel]; | |
| 858 | + else | |
| 859 | + *val2 = info->shunts[channel] >> 1; | |
| 860 | + return IIO_VAL_FRACTIONAL; | |
| 861 | + /* | |
| 862 | + * Power - uW - it will use the combined scale | |
| 863 | + * for current and voltage | |
| 864 | + * current(mA) * voltage(mV) = power (uW) | |
| 865 | + */ | |
| 866 | + case PAC1934_VPOWER_1_ADDR: | |
| 867 | + case PAC1934_VPOWER_2_ADDR: | |
| 868 | + case PAC1934_VPOWER_3_ADDR: | |
| 869 | + case PAC1934_VPOWER_4_ADDR: | |
| 870 | + *val = PAC1934_MAX_VPOWER_RSHIFTED_BY_28B; | |
| 871 | + if (chan->scan_type.sign == 'u') | |
| 872 | + *val2 = info->shunts[channel]; | |
| 873 | + else | |
| 874 | + *val2 = info->shunts[channel] >> 1; | |
| 875 | + return IIO_VAL_FRACTIONAL; | |
| 876 | + case PAC1934_VPOWER_ACC_1_ADDR: | |
| 877 | + case PAC1934_VPOWER_ACC_2_ADDR: | |
| 878 | + case PAC1934_VPOWER_ACC_3_ADDR: | |
| 879 | + case PAC1934_VPOWER_ACC_4_ADDR: | |
| 880 | + /* | |
| 881 | + * expresses the 32 bit scale value here compute | |
| 882 | + * the scale for energy (miliWatt-second or miliJoule) | |
| 883 | + */ | |
| 884 | + *val = PAC1934_SCALE_CONSTANT; | |
| 885 | + | |
| 886 | + if (chan->scan_type.sign == 'u') | |
| 887 | + *val2 = info->shunts[channel]; | |
| 888 | + else | |
| 889 | + *val2 = info->shunts[channel] >> 1; | |
| 890 | + return IIO_VAL_FRACTIONAL; | |
| 891 | + default: | |
| 892 | + return -EINVAL; | |
| 893 | + } | |
| 894 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 895 | + *val = info->sample_rate_value; | |
| 896 | + return IIO_VAL_INT; | |
| 897 | + case IIO_CHAN_INFO_ENABLE: | |
| 898 | + *val = info->enable_energy[channel]; | |
| 899 | + return IIO_VAL_INT; | |
| 900 | + default: | |
| 901 | + return -EINVAL; | |
| 902 | + } | |
| 903 | +} | |
| 904 | + | |
| 905 | +static int pac1934_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, | |
| 906 | + int val, int val2, long mask) | |
| 907 | +{ | |
| 908 | + struct pac1934_chip_info *info = iio_priv(indio_dev); | |
| 909 | + struct i2c_client *client = info->client; | |
| 910 | + int ret = -EINVAL; | |
| 911 | + s32 old_samp_rate; | |
| 912 | + u8 ctrl_reg; | |
| 913 | + | |
| 914 | + switch (mask) { | |
| 915 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 916 | + ret = pac1934_get_samp_rate_idx(info, val); | |
| 917 | + if (ret < 0) | |
| 918 | + return ret; | |
| 919 | + | |
| 920 | + /* write the new sampling value and trigger a snapshot(incl refresh) */ | |
| 921 | + scoped_guard(mutex, &info->lock) { | |
| 922 | + ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, ret); | |
| 923 | + ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg); | |
| 924 | + if (ret) { | |
| 925 | + dev_err(&client->dev, | |
| 926 | + "%s - can't update sample rate\n", | |
| 927 | + __func__); | |
| 928 | + return ret; | |
| 929 | + } | |
| 930 | + } | |
| 931 | + | |
| 932 | + old_samp_rate = info->sample_rate_value; | |
| 933 | + info->sample_rate_value = val; | |
| 934 | + | |
| 935 | + /* | |
| 936 | + * now, force a snapshot with refresh - call retrieve | |
| 937 | + * data in order to update the refresh timer | |
| 938 | + * alter the timestamp in order to force trigger a | |
| 939 | + * register snapshot and a timestamp update | |
| 940 | + */ | |
| 941 | + info->tstamp -= msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS); | |
| 942 | + ret = pac1934_retrieve_data(info, (1024 / old_samp_rate) * 1000); | |
| 943 | + if (ret < 0) { | |
| 944 | + dev_err(&client->dev, | |
| 945 | + "%s - cannot snapshot ctrl and measurement regs\n", | |
| 946 | + __func__); | |
| 947 | + return ret; | |
| 948 | + } | |
| 949 | + | |
| 950 | + return 0; | |
| 951 | + case IIO_CHAN_INFO_ENABLE: | |
| 952 | + scoped_guard(mutex, &info->lock) { | |
| 953 | + info->enable_energy[chan->channel - 1] = val ? true : false; | |
| 954 | + if (!val) | |
| 955 | + info->chip_reg_data.energy_sec_acc[chan->channel - 1] = 0; | |
| 956 | + } | |
| 957 | + | |
| 958 | + return 0; | |
| 959 | + default: | |
| 960 | + return -EINVAL; | |
| 961 | + } | |
| 962 | +} | |
| 963 | + | |
| 964 | +static int pac1934_read_label(struct iio_dev *indio_dev, | |
| 965 | + struct iio_chan_spec const *chan, char *label) | |
| 966 | +{ | |
| 967 | + struct pac1934_chip_info *info = iio_priv(indio_dev); | |
| 968 | + | |
| 969 | + switch (chan->address) { | |
| 970 | + case PAC1934_VBUS_1_ADDR: | |
| 971 | + case PAC1934_VBUS_2_ADDR: | |
| 972 | + case PAC1934_VBUS_3_ADDR: | |
| 973 | + case PAC1934_VBUS_4_ADDR: | |
| 974 | + return sysfs_emit(label, "%s_VBUS_%d\n", | |
| 975 | + info->labels[chan->scan_index], | |
| 976 | + chan->scan_index + 1); | |
| 977 | + case PAC1934_VBUS_AVG_1_ADDR: | |
| 978 | + case PAC1934_VBUS_AVG_2_ADDR: | |
| 979 | + case PAC1934_VBUS_AVG_3_ADDR: | |
| 980 | + case PAC1934_VBUS_AVG_4_ADDR: | |
| 981 | + return sysfs_emit(label, "%s_VBUS_AVG_%d\n", | |
| 982 | + info->labels[chan->scan_index], | |
| 983 | + chan->scan_index + 1); | |
| 984 | + case PAC1934_VSENSE_1_ADDR: | |
| 985 | + case PAC1934_VSENSE_2_ADDR: | |
| 986 | + case PAC1934_VSENSE_3_ADDR: | |
| 987 | + case PAC1934_VSENSE_4_ADDR: | |
| 988 | + return sysfs_emit(label, "%s_IBUS_%d\n", | |
| 989 | + info->labels[chan->scan_index], | |
| 990 | + chan->scan_index + 1); | |
| 991 | + case PAC1934_VSENSE_AVG_1_ADDR: | |
| 992 | + case PAC1934_VSENSE_AVG_2_ADDR: | |
| 993 | + case PAC1934_VSENSE_AVG_3_ADDR: | |
| 994 | + case PAC1934_VSENSE_AVG_4_ADDR: | |
| 995 | + return sysfs_emit(label, "%s_IBUS_AVG_%d\n", | |
| 996 | + info->labels[chan->scan_index], | |
| 997 | + chan->scan_index + 1); | |
| 998 | + case PAC1934_VPOWER_1_ADDR: | |
| 999 | + case PAC1934_VPOWER_2_ADDR: | |
| 1000 | + case PAC1934_VPOWER_3_ADDR: | |
| 1001 | + case PAC1934_VPOWER_4_ADDR: | |
| 1002 | + return sysfs_emit(label, "%s_POWER_%d\n", | |
| 1003 | + info->labels[chan->scan_index], | |
| 1004 | + chan->scan_index + 1); | |
| 1005 | + case PAC1934_VPOWER_ACC_1_ADDR: | |
| 1006 | + case PAC1934_VPOWER_ACC_2_ADDR: | |
| 1007 | + case PAC1934_VPOWER_ACC_3_ADDR: | |
| 1008 | + case PAC1934_VPOWER_ACC_4_ADDR: | |
| 1009 | + return sysfs_emit(label, "%s_ENERGY_%d\n", | |
| 1010 | + info->labels[chan->scan_index], | |
| 1011 | + chan->scan_index + 1); | |
| 1012 | + } | |
| 1013 | + | |
| 1014 | + return 0; | |
| 1015 | +} | |
| 1016 | + | |
| 1017 | +static void pac1934_work_periodic_rfsh(struct work_struct *work) | |
| 1018 | +{ | |
| 1019 | + struct pac1934_chip_info *info = TO_PAC1934_CHIP_INFO((struct delayed_work *)work); | |
| 1020 | + struct device *dev = &info->client->dev; | |
| 1021 | + | |
| 1022 | + dev_dbg(dev, "%s - Periodic refresh\n", __func__); | |
| 1023 | + | |
| 1024 | + /* do a REFRESH, then read */ | |
| 1025 | + pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, | |
| 1026 | + PAC1934_MIN_UPDATE_WAIT_TIME_US); | |
| 1027 | + | |
| 1028 | + schedule_delayed_work(&info->work_chip_rfsh, | |
| 1029 | + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); | |
| 1030 | +} | |
| 1031 | + | |
| 1032 | +static int pac1934_read_revision(struct pac1934_chip_info *info, u8 *buf) | |
| 1033 | +{ | |
| 1034 | + int ret; | |
| 1035 | + struct i2c_client *client = info->client; | |
| 1036 | + | |
| 1037 | + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_PID_REG_ADDR, | |
| 1038 | + PAC1934_ID_REG_LEN, | |
| 1039 | + buf); | |
| 1040 | + if (ret < 0) { | |
| 1041 | + dev_err(&client->dev, "cannot read revision\n"); | |
| 1042 | + return ret; | |
| 1043 | + } | |
| 1044 | + | |
| 1045 | + return 0; | |
| 1046 | +} | |
| 1047 | + | |
| 1048 | +static int pac1934_chip_identify(struct pac1934_chip_info *info) | |
| 1049 | +{ | |
| 1050 | + u8 rev_info[PAC1934_ID_REG_LEN]; | |
| 1051 | + struct device *dev = &info->client->dev; | |
| 1052 | + int ret = 0; | |
| 1053 | + | |
| 1054 | + ret = pac1934_read_revision(info, (u8 *)rev_info); | |
| 1055 | + if (ret) | |
| 1056 | + return ret; | |
| 1057 | + | |
| 1058 | + info->chip_variant = rev_info[PAC1934_PID_IDX]; | |
| 1059 | + info->chip_revision = rev_info[PAC1934_RID_IDX]; | |
| 1060 | + | |
| 1061 | + dev_dbg(dev, "Chip variant: 0x%02X\n", info->chip_variant); | |
| 1062 | + dev_dbg(dev, "Chip revision: 0x%02X\n", info->chip_revision); | |
| 1063 | + | |
| 1064 | + switch (info->chip_variant) { | |
| 1065 | + case PAC1934_PID: | |
| 1066 | + return PAC1934; | |
| 1067 | + case PAC1933_PID: | |
| 1068 | + return PAC1933; | |
| 1069 | + case PAC1932_PID: | |
| 1070 | + return PAC1932; | |
| 1071 | + case PAC1931_PID: | |
| 1072 | + return PAC1931; | |
| 1073 | + default: | |
| 1074 | + return -EINVAL; | |
| 1075 | + } | |
| 1076 | +} | |
| 1077 | + | |
| 1078 | +/* | |
| 1079 | + * documentation related to the ACPI device definition | |
| 1080 | + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf | |
| 1081 | + */ | |
| 1082 | +static bool pac1934_acpi_parse_channel_config(struct i2c_client *client, | |
| 1083 | + struct pac1934_chip_info *info) | |
| 1084 | +{ | |
| 1085 | + acpi_handle handle; | |
| 1086 | + union acpi_object *rez; | |
| 1087 | + struct device *dev = &client->dev; | |
| 1088 | + unsigned short bi_dir_mask; | |
| 1089 | + int idx, i; | |
| 1090 | + guid_t guid; | |
| 1091 | + | |
| 1092 | + handle = ACPI_HANDLE(dev); | |
| 1093 | + | |
| 1094 | + guid_parse(PAC1934_DSM_UUID, &guid); | |
| 1095 | + | |
| 1096 | + rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL); | |
| 1097 | + if (!rez) | |
| 1098 | + return false; | |
| 1099 | + | |
| 1100 | + for (i = 0; i < rez->package.count; i += 2) { | |
| 1101 | + idx = i / 2; | |
| 1102 | + info->labels[idx] = | |
| 1103 | + devm_kmemdup(dev, rez->package.elements[i].string.pointer, | |
| 1104 | + (size_t)rez->package.elements[i].string.length + 1, | |
| 1105 | + GFP_KERNEL); | |
| 1106 | + info->labels[idx][rez->package.elements[i].string.length] = '\0'; | |
| 1107 | + info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000; | |
| 1108 | + info->active_channels[idx] = (info->shunts[idx] != 0); | |
| 1109 | + } | |
| 1110 | + | |
| 1111 | + ACPI_FREE(rez); | |
| 1112 | + | |
| 1113 | + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_UOHMS_VALS, NULL); | |
| 1114 | + if (!rez) { | |
| 1115 | + /* | |
| 1116 | + * initializing with default values | |
| 1117 | + * we assume all channels are unidirectional(the mask is zero) | |
| 1118 | + * and assign the default sampling rate | |
| 1119 | + */ | |
| 1120 | + info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ; | |
| 1121 | + return true; | |
| 1122 | + } | |
| 1123 | + | |
| 1124 | + for (i = 0; i < rez->package.count; i++) { | |
| 1125 | + idx = i; | |
| 1126 | + info->shunts[idx] = rez->package.elements[i].integer.value; | |
| 1127 | + info->active_channels[idx] = (info->shunts[idx] != 0); | |
| 1128 | + } | |
| 1129 | + | |
| 1130 | + ACPI_FREE(rez); | |
| 1131 | + | |
| 1132 | + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL); | |
| 1133 | + if (!rez) | |
| 1134 | + return false; | |
| 1135 | + | |
| 1136 | + bi_dir_mask = rez->package.elements[0].integer.value; | |
| 1137 | + info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0; | |
| 1138 | + info->bi_dir[1] = ((bi_dir_mask & (1 << 2)) | (bi_dir_mask & (1 << 6))) != 0; | |
| 1139 | + info->bi_dir[2] = ((bi_dir_mask & (1 << 1)) | (bi_dir_mask & (1 << 5))) != 0; | |
| 1140 | + info->bi_dir[3] = ((bi_dir_mask & (1 << 0)) | (bi_dir_mask & (1 << 4))) != 0; | |
| 1141 | + | |
| 1142 | + ACPI_FREE(rez); | |
| 1143 | + | |
| 1144 | + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL); | |
| 1145 | + if (!rez) | |
| 1146 | + return false; | |
| 1147 | + | |
| 1148 | + info->sample_rate_value = rez->package.elements[0].integer.value; | |
| 1149 | + | |
| 1150 | + ACPI_FREE(rez); | |
| 1151 | + | |
| 1152 | + return true; | |
| 1153 | +} | |
| 1154 | + | |
| 1155 | +static bool pac1934_of_parse_channel_config(struct i2c_client *client, | |
| 1156 | + struct pac1934_chip_info *info) | |
| 1157 | +{ | |
| 1158 | + struct fwnode_handle *node, *fwnode; | |
| 1159 | + struct device *dev = &client->dev; | |
| 1160 | + unsigned int current_channel; | |
| 1161 | + int idx, ret; | |
| 1162 | + | |
| 1163 | + info->sample_rate_value = 1024; | |
| 1164 | + current_channel = 1; | |
| 1165 | + | |
| 1166 | + fwnode = dev_fwnode(dev); | |
| 1167 | + fwnode_for_each_available_child_node(fwnode, node) { | |
| 1168 | + ret = fwnode_property_read_u32(node, "reg", &idx); | |
| 1169 | + if (ret) { | |
| 1170 | + dev_err_probe(dev, ret, | |
| 1171 | + "reading invalid channel index\n"); | |
| 1172 | + goto err_fwnode; | |
| 1173 | + } | |
| 1174 | + /* adjust idx to match channel index (1 to 4) from the datasheet */ | |
| 1175 | + idx--; | |
| 1176 | + | |
| 1177 | + if (current_channel >= (info->phys_channels + 1) || | |
| 1178 | + idx >= info->phys_channels || idx < 0) { | |
| 1179 | + dev_err_probe(dev, -EINVAL, | |
| 1180 | + "%s: invalid channel_index %d value\n", | |
| 1181 | + fwnode_get_name(node), idx); | |
| 1182 | + goto err_fwnode; | |
| 1183 | + } | |
| 1184 | + | |
| 1185 | + /* enable channel */ | |
| 1186 | + info->active_channels[idx] = true; | |
| 1187 | + | |
| 1188 | + ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms", | |
| 1189 | + &info->shunts[idx]); | |
| 1190 | + if (ret) { | |
| 1191 | + dev_err_probe(dev, ret, | |
| 1192 | + "%s: invalid shunt-resistor value: %d\n", | |
| 1193 | + fwnode_get_name(node), info->shunts[idx]); | |
| 1194 | + goto err_fwnode; | |
| 1195 | + } | |
| 1196 | + | |
| 1197 | + if (fwnode_property_present(node, "label")) { | |
| 1198 | + ret = fwnode_property_read_string(node, "label", | |
| 1199 | + (const char **)&info->labels[idx]); | |
| 1200 | + if (ret) { | |
| 1201 | + dev_err_probe(dev, ret, | |
| 1202 | + "%s: invalid rail-name value\n", | |
| 1203 | + fwnode_get_name(node)); | |
| 1204 | + goto err_fwnode; | |
| 1205 | + } | |
| 1206 | + } | |
| 1207 | + | |
| 1208 | + info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar"); | |
| 1209 | + | |
| 1210 | + current_channel++; | |
| 1211 | + } | |
| 1212 | + | |
| 1213 | + return true; | |
| 1214 | + | |
| 1215 | +err_fwnode: | |
| 1216 | + fwnode_handle_put(node); | |
| 1217 | + | |
| 1218 | + return false; | |
| 1219 | +} | |
| 1220 | + | |
| 1221 | +static void pac1934_cancel_delayed_work(void *dwork) | |
| 1222 | +{ | |
| 1223 | + cancel_delayed_work_sync(dwork); | |
| 1224 | +} | |
| 1225 | + | |
| 1226 | +static int pac1934_chip_configure(struct pac1934_chip_info *info) | |
| 1227 | +{ | |
| 1228 | + int cnt, ret; | |
| 1229 | + struct i2c_client *client = info->client; | |
| 1230 | + u8 regs[PAC1934_CTRL_STATUS_INFO_LEN], idx, ctrl_reg; | |
| 1231 | + u32 wait_time; | |
| 1232 | + | |
| 1233 | + info->chip_reg_data.num_enabled_channels = 0; | |
| 1234 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 1235 | + if (info->active_channels[cnt]) | |
| 1236 | + info->chip_reg_data.num_enabled_channels++; | |
| 1237 | + } | |
| 1238 | + | |
| 1239 | + /* | |
| 1240 | + * read whatever information was gathered before the driver was loaded | |
| 1241 | + * establish which channels are enabled/disabled and then establish the | |
| 1242 | + * information retrieval mode (using SKIP or no). | |
| 1243 | + * Read the chip ID values | |
| 1244 | + */ | |
| 1245 | + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, | |
| 1246 | + ARRAY_SIZE(regs), | |
| 1247 | + (u8 *)regs); | |
| 1248 | + if (ret < 0) { | |
| 1249 | + dev_err_probe(&client->dev, ret, | |
| 1250 | + "%s - cannot read regs from 0x%02X\n", | |
| 1251 | + __func__, PAC1934_CTRL_STAT_REGS_ADDR); | |
| 1252 | + return ret; | |
| 1253 | + } | |
| 1254 | + | |
| 1255 | + /* write the CHANNEL_DIS and the NEG_PWR registers */ | |
| 1256 | + regs[PAC1934_CHANNEL_DIS_REG_OFF] = | |
| 1257 | + FIELD_PREP(PAC1934_CHAN_DIS_CH1_OFF_MASK, info->active_channels[0] ? 0 : 1) | | |
| 1258 | + FIELD_PREP(PAC1934_CHAN_DIS_CH2_OFF_MASK, info->active_channels[1] ? 0 : 1) | | |
| 1259 | + FIELD_PREP(PAC1934_CHAN_DIS_CH3_OFF_MASK, info->active_channels[2] ? 0 : 1) | | |
| 1260 | + FIELD_PREP(PAC1934_CHAN_DIS_CH4_OFF_MASK, info->active_channels[3] ? 0 : 1) | | |
| 1261 | + FIELD_PREP(PAC1934_SMBUS_TIMEOUT_MASK, 0) | | |
| 1262 | + FIELD_PREP(PAC1934_SMBUS_BYTECOUNT_MASK, 0) | | |
| 1263 | + FIELD_PREP(PAC1934_SMBUS_NO_SKIP_MASK, 0); | |
| 1264 | + | |
| 1265 | + regs[PAC1934_NEG_PWR_REG_OFF] = | |
| 1266 | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) | | |
| 1267 | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) | | |
| 1268 | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) | | |
| 1269 | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) | | |
| 1270 | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) | | |
| 1271 | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) | | |
| 1272 | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) | | |
| 1273 | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]); | |
| 1274 | + | |
| 1275 | + /* no SLOW triggered REFRESH, clear POR */ | |
| 1276 | + regs[PAC1934_SLOW_REG_OFF] = 0; | |
| 1277 | + | |
| 1278 | + ret = i2c_smbus_write_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, | |
| 1279 | + ARRAY_SIZE(regs), (u8 *)regs); | |
| 1280 | + if (ret) | |
| 1281 | + return ret; | |
| 1282 | + | |
| 1283 | + /* Default sampling rate */ | |
| 1284 | + ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, PAC1934_SAMP_1024SPS); | |
| 1285 | + | |
| 1286 | + ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg); | |
| 1287 | + if (ret) | |
| 1288 | + return ret; | |
| 1289 | + | |
| 1290 | + /* | |
| 1291 | + * send a REFRESH to the chip, so the new settings take place | |
| 1292 | + * as well as resetting the accumulators | |
| 1293 | + */ | |
| 1294 | + ret = i2c_smbus_write_byte(client, PAC1934_REFRESH_REG_ADDR); | |
| 1295 | + if (ret) { | |
| 1296 | + dev_err(&client->dev, | |
| 1297 | + "%s - cannot send 0x%02X\n", | |
| 1298 | + __func__, PAC1934_REFRESH_REG_ADDR); | |
| 1299 | + return ret; | |
| 1300 | + } | |
| 1301 | + | |
| 1302 | + /* | |
| 1303 | + * get the current(in the chip) sampling speed and compute the | |
| 1304 | + * required timeout based on its value | |
| 1305 | + * the timeout is 1/sampling_speed | |
| 1306 | + */ | |
| 1307 | + idx = regs[PAC1934_CTRL_ACT_REG_OFF] >> PAC1934_SAMPLE_RATE_SHIFT; | |
| 1308 | + wait_time = (1024 / samp_rate_map_tbl[idx]) * 1000; | |
| 1309 | + | |
| 1310 | + /* | |
| 1311 | + * wait the maximum amount of time to be on the safe side | |
| 1312 | + * the maximum wait time is for 8sps | |
| 1313 | + */ | |
| 1314 | + usleep_range(wait_time, wait_time + 100); | |
| 1315 | + | |
| 1316 | + INIT_DELAYED_WORK(&info->work_chip_rfsh, pac1934_work_periodic_rfsh); | |
| 1317 | + /* Setup the latest moment for reading the regs before saturation */ | |
| 1318 | + schedule_delayed_work(&info->work_chip_rfsh, | |
| 1319 | + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); | |
| 1320 | + | |
| 1321 | + return devm_add_action_or_reset(&client->dev, pac1934_cancel_delayed_work, | |
| 1322 | + &info->work_chip_rfsh); | |
| 1323 | +} | |
| 1324 | + | |
| 1325 | +static int pac1934_prep_iio_channels(struct pac1934_chip_info *info, struct iio_dev *indio_dev) | |
| 1326 | +{ | |
| 1327 | + struct iio_chan_spec *ch_sp; | |
| 1328 | + int channel_size, attribute_count, cnt; | |
| 1329 | + void *dyn_ch_struct, *tmp_data; | |
| 1330 | + struct device *dev = &info->client->dev; | |
| 1331 | + | |
| 1332 | + /* find out dynamically how many IIO channels we need */ | |
| 1333 | + attribute_count = 0; | |
| 1334 | + channel_size = 0; | |
| 1335 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 1336 | + if (!info->active_channels[cnt]) | |
| 1337 | + continue; | |
| 1338 | + | |
| 1339 | + /* add the size of the properties of one chip physical channel */ | |
| 1340 | + channel_size += sizeof(pac1934_single_channel); | |
| 1341 | + /* count how many enabled channels we have */ | |
| 1342 | + attribute_count += ARRAY_SIZE(pac1934_single_channel); | |
| 1343 | + dev_dbg(dev, ":%s: Channel %d active\n", __func__, cnt + 1); | |
| 1344 | + } | |
| 1345 | + | |
| 1346 | + dyn_ch_struct = devm_kzalloc(dev, channel_size, GFP_KERNEL); | |
| 1347 | + if (!dyn_ch_struct) | |
| 1348 | + return -EINVAL; | |
| 1349 | + | |
| 1350 | + tmp_data = dyn_ch_struct; | |
| 1351 | + | |
| 1352 | + /* populate the dynamic channels and make all the adjustments */ | |
| 1353 | + for (cnt = 0; cnt < info->phys_channels; cnt++) { | |
| 1354 | + if (!info->active_channels[cnt]) | |
| 1355 | + continue; | |
| 1356 | + | |
| 1357 | + memcpy(tmp_data, pac1934_single_channel, sizeof(pac1934_single_channel)); | |
| 1358 | + ch_sp = (struct iio_chan_spec *)tmp_data; | |
| 1359 | + ch_sp[PAC1934_CH_ENERGY].channel = cnt + 1; | |
| 1360 | + ch_sp[PAC1934_CH_ENERGY].scan_index = cnt; | |
| 1361 | + ch_sp[PAC1934_CH_ENERGY].address = cnt + PAC1934_VPOWER_ACC_1_ADDR; | |
| 1362 | + ch_sp[PAC1934_CH_POWER].channel = cnt + 1; | |
| 1363 | + ch_sp[PAC1934_CH_POWER].scan_index = cnt; | |
| 1364 | + ch_sp[PAC1934_CH_POWER].address = cnt + PAC1934_VPOWER_1_ADDR; | |
| 1365 | + ch_sp[PAC1934_CH_VOLTAGE].channel = cnt + 1; | |
| 1366 | + ch_sp[PAC1934_CH_VOLTAGE].scan_index = cnt; | |
| 1367 | + ch_sp[PAC1934_CH_VOLTAGE].address = cnt + PAC1934_VBUS_1_ADDR; | |
| 1368 | + ch_sp[PAC1934_CH_CURRENT].channel = cnt + 1; | |
| 1369 | + ch_sp[PAC1934_CH_CURRENT].scan_index = cnt; | |
| 1370 | + ch_sp[PAC1934_CH_CURRENT].address = cnt + PAC1934_VSENSE_1_ADDR; | |
| 1371 | + | |
| 1372 | + /* | |
| 1373 | + * In order to be able to use labels for PAC1934_CH_VOLTAGE, and | |
| 1374 | + * PAC1934_CH_VOLTAGE_AVERAGE,respectively PAC1934_CH_CURRENT | |
| 1375 | + * and PAC1934_CH_CURRENT_AVERAGE we need to use different | |
| 1376 | + * channel numbers. We will add +5 (+1 to maximum PAC channels). | |
| 1377 | + */ | |
| 1378 | + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].channel = cnt + 5; | |
| 1379 | + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_index = cnt; | |
| 1380 | + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].address = cnt + PAC1934_VBUS_AVG_1_ADDR; | |
| 1381 | + ch_sp[PAC1934_CH_CURRENT_AVERAGE].channel = cnt + 5; | |
| 1382 | + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_index = cnt; | |
| 1383 | + ch_sp[PAC1934_CH_CURRENT_AVERAGE].address = cnt + PAC1934_VSENSE_AVG_1_ADDR; | |
| 1384 | + | |
| 1385 | + /* | |
| 1386 | + * now modify the parameters in all channels if the | |
| 1387 | + * whole chip rail(channel) is bi-directional | |
| 1388 | + */ | |
| 1389 | + if (info->bi_dir[cnt]) { | |
| 1390 | + ch_sp[PAC1934_CH_ENERGY].scan_type.sign = 's'; | |
| 1391 | + ch_sp[PAC1934_CH_ENERGY].scan_type.realbits = 47; | |
| 1392 | + ch_sp[PAC1934_CH_POWER].scan_type.sign = 's'; | |
| 1393 | + ch_sp[PAC1934_CH_POWER].scan_type.realbits = 27; | |
| 1394 | + ch_sp[PAC1934_CH_VOLTAGE].scan_type.sign = 's'; | |
| 1395 | + ch_sp[PAC1934_CH_VOLTAGE].scan_type.realbits = 15; | |
| 1396 | + ch_sp[PAC1934_CH_CURRENT].scan_type.sign = 's'; | |
| 1397 | + ch_sp[PAC1934_CH_CURRENT].scan_type.realbits = 15; | |
| 1398 | + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.sign = 's'; | |
| 1399 | + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.realbits = 15; | |
| 1400 | + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.sign = 's'; | |
| 1401 | + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.realbits = 15; | |
| 1402 | + } | |
| 1403 | + tmp_data += sizeof(pac1934_single_channel); | |
| 1404 | + } | |
| 1405 | + | |
| 1406 | + /* | |
| 1407 | + * send the updated dynamic channel structure information towards IIO | |
| 1408 | + * prepare the required field for IIO class registration | |
| 1409 | + */ | |
| 1410 | + indio_dev->num_channels = attribute_count; | |
| 1411 | + indio_dev->channels = (const struct iio_chan_spec *)dyn_ch_struct; | |
| 1412 | + | |
| 1413 | + return 0; | |
| 1414 | +} | |
| 1415 | + | |
| 1416 | +static IIO_DEVICE_ATTR(in_shunt_resistor1, 0644, | |
| 1417 | + pac1934_shunt_value_show, pac1934_shunt_value_store, 0); | |
| 1418 | +static IIO_DEVICE_ATTR(in_shunt_resistor2, 0644, | |
| 1419 | + pac1934_shunt_value_show, pac1934_shunt_value_store, 1); | |
| 1420 | +static IIO_DEVICE_ATTR(in_shunt_resistor3, 0644, | |
| 1421 | + pac1934_shunt_value_show, pac1934_shunt_value_store, 2); | |
| 1422 | +static IIO_DEVICE_ATTR(in_shunt_resistor4, 0644, | |
| 1423 | + pac1934_shunt_value_show, pac1934_shunt_value_store, 3); | |
| 1424 | + | |
| 1425 | +static int pac1934_prep_custom_attributes(struct pac1934_chip_info *info, | |
| 1426 | + struct iio_dev *indio_dev) | |
| 1427 | +{ | |
| 1428 | + int i, active_channels_count = 0; | |
| 1429 | + struct attribute **pac1934_custom_attr; | |
| 1430 | + struct attribute_group *pac1934_group; | |
| 1431 | + struct device *dev = &info->client->dev; | |
| 1432 | + | |
| 1433 | + for (i = 0 ; i < info->phys_channels; i++) | |
| 1434 | + if (info->active_channels[i]) | |
| 1435 | + active_channels_count++; | |
| 1436 | + | |
| 1437 | + pac1934_group = devm_kzalloc(dev, sizeof(*pac1934_group), GFP_KERNEL); | |
| 1438 | + if (!pac1934_group) | |
| 1439 | + return -ENOMEM; | |
| 1440 | + | |
| 1441 | + pac1934_custom_attr = devm_kzalloc(dev, | |
| 1442 | + (PAC1934_CUSTOM_ATTR_FOR_CHANNEL * | |
| 1443 | + active_channels_count) | |
| 1444 | + * sizeof(*pac1934_group) + 1, | |
| 1445 | + GFP_KERNEL); | |
| 1446 | + if (!pac1934_custom_attr) | |
| 1447 | + return -ENOMEM; | |
| 1448 | + | |
| 1449 | + i = 0; | |
| 1450 | + if (info->active_channels[0]) | |
| 1451 | + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor1); | |
| 1452 | + | |
| 1453 | + if (info->active_channels[1]) | |
| 1454 | + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor2); | |
| 1455 | + | |
| 1456 | + if (info->active_channels[2]) | |
| 1457 | + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor3); | |
| 1458 | + | |
| 1459 | + if (info->active_channels[3]) | |
| 1460 | + pac1934_custom_attr[i] = PAC1934_DEV_ATTR(in_shunt_resistor4); | |
| 1461 | + | |
| 1462 | + pac1934_group->attrs = pac1934_custom_attr; | |
| 1463 | + info->iio_info.attrs = pac1934_group; | |
| 1464 | + | |
| 1465 | + return 0; | |
| 1466 | +} | |
| 1467 | + | |
| 1468 | +static void pac1934_mutex_destroy(void *data) | |
| 1469 | +{ | |
| 1470 | + struct mutex *lock = data; | |
| 1471 | + | |
| 1472 | + mutex_destroy(lock); | |
| 1473 | +} | |
| 1474 | + | |
| 1475 | +static const struct iio_info pac1934_info = { | |
| 1476 | + .read_raw = pac1934_read_raw, | |
| 1477 | + .write_raw = pac1934_write_raw, | |
| 1478 | + .read_avail = pac1934_read_avail, | |
| 1479 | + .read_label = pac1934_read_label, | |
| 1480 | +}; | |
| 1481 | + | |
| 1482 | +static int pac1934_probe(struct i2c_client *client) | |
| 1483 | +{ | |
| 1484 | + struct pac1934_chip_info *info; | |
| 1485 | + const struct pac1934_features *chip; | |
| 1486 | + struct iio_dev *indio_dev; | |
| 1487 | + int cnt, ret; | |
| 1488 | + bool match = false; | |
| 1489 | + struct device *dev = &client->dev; | |
| 1490 | + | |
| 1491 | + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); | |
| 1492 | + if (!indio_dev) | |
| 1493 | + return -ENOMEM; | |
| 1494 | + | |
| 1495 | + info = iio_priv(indio_dev); | |
| 1496 | + | |
| 1497 | + info->client = client; | |
| 1498 | + | |
| 1499 | + /* always start with energy accumulation enabled */ | |
| 1500 | + for (cnt = 0; cnt < PAC1934_MAX_NUM_CHANNELS; cnt++) | |
| 1501 | + info->enable_energy[cnt] = true; | |
| 1502 | + | |
| 1503 | + ret = pac1934_chip_identify(info); | |
| 1504 | + if (ret < 0) { | |
| 1505 | + /* | |
| 1506 | + * If failed to identify the hardware based on internal | |
| 1507 | + * registers, try using fallback compatible in device tree | |
| 1508 | + * to deal with some newer part number. | |
| 1509 | + */ | |
| 1510 | + chip = i2c_get_match_data(client); | |
| 1511 | + if (!chip) | |
| 1512 | + return -EINVAL; | |
| 1513 | + | |
| 1514 | + info->phys_channels = chip->phys_channels; | |
| 1515 | + indio_dev->name = chip->name; | |
| 1516 | + } else { | |
| 1517 | + info->phys_channels = pac1934_chip_config[ret].phys_channels; | |
| 1518 | + indio_dev->name = pac1934_chip_config[ret].name; | |
| 1519 | + } | |
| 1520 | + | |
| 1521 | + if (acpi_match_device(dev->driver->acpi_match_table, dev)) | |
| 1522 | + match = pac1934_acpi_parse_channel_config(client, info); | |
| 1523 | + else | |
| 1524 | + /* | |
| 1525 | + * This makes it possible to use also ACPI PRP0001 for | |
| 1526 | + * registering the device using device tree properties. | |
| 1527 | + */ | |
| 1528 | + match = pac1934_of_parse_channel_config(client, info); | |
| 1529 | + | |
| 1530 | + if (!match) | |
| 1531 | + return dev_err_probe(dev, -EINVAL, | |
| 1532 | + "parameter parsing returned an error\n"); | |
| 1533 | + | |
| 1534 | + mutex_init(&info->lock); | |
| 1535 | + ret = devm_add_action_or_reset(dev, pac1934_mutex_destroy, | |
| 1536 | + &info->lock); | |
| 1537 | + if (ret < 0) | |
| 1538 | + return ret; | |
| 1539 | + | |
| 1540 | + /* | |
| 1541 | + * do now any chip specific initialization (e.g. read/write | |
| 1542 | + * some registers), enable/disable certain channels, change the sampling | |
| 1543 | + * rate to the requested value | |
| 1544 | + */ | |
| 1545 | + ret = pac1934_chip_configure(info); | |
| 1546 | + if (ret < 0) | |
| 1547 | + return ret; | |
| 1548 | + | |
| 1549 | + /* prepare the channel information */ | |
| 1550 | + ret = pac1934_prep_iio_channels(info, indio_dev); | |
| 1551 | + if (ret < 0) | |
| 1552 | + return ret; | |
| 1553 | + | |
| 1554 | + info->iio_info = pac1934_info; | |
| 1555 | + indio_dev->info = &info->iio_info; | |
| 1556 | + indio_dev->modes = INDIO_DIRECT_MODE; | |
| 1557 | + | |
| 1558 | + ret = pac1934_prep_custom_attributes(info, indio_dev); | |
| 1559 | + if (ret < 0) | |
| 1560 | + return dev_err_probe(dev, ret, | |
| 1561 | + "Can't configure custom attributes for PAC1934 device\n"); | |
| 1562 | + | |
| 1563 | + /* | |
| 1564 | + * read whatever has been accumulated in the chip so far | |
| 1565 | + * and reset the accumulators | |
| 1566 | + */ | |
| 1567 | + ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, | |
| 1568 | + PAC1934_MIN_UPDATE_WAIT_TIME_US); | |
| 1569 | + if (ret < 0) | |
| 1570 | + return ret; | |
| 1571 | + | |
| 1572 | + ret = devm_iio_device_register(dev, indio_dev); | |
| 1573 | + if (ret < 0) | |
| 1574 | + return dev_err_probe(dev, ret, | |
| 1575 | + "Can't register IIO device\n"); | |
| 1576 | + | |
| 1577 | + return 0; | |
| 1578 | +} | |
| 1579 | + | |
| 1580 | +static const struct i2c_device_id pac1934_id[] = { | |
| 1581 | + { .name = "pac1931", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1931] }, | |
| 1582 | + { .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] }, | |
| 1583 | + { .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] }, | |
| 1584 | + { .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, | |
| 1585 | + {} | |
| 1586 | +}; | |
| 1587 | +MODULE_DEVICE_TABLE(i2c, pac1934_id); | |
| 1588 | + | |
| 1589 | +static const struct of_device_id pac1934_of_match[] = { | |
| 1590 | + { | |
| 1591 | + .compatible = "microchip,pac1931", | |
| 1592 | + .data = &pac1934_chip_config[PAC1931] | |
| 1593 | + }, | |
| 1594 | + { | |
| 1595 | + .compatible = "microchip,pac1932", | |
| 1596 | + .data = &pac1934_chip_config[PAC1932] | |
| 1597 | + }, | |
| 1598 | + { | |
| 1599 | + .compatible = "microchip,pac1933", | |
| 1600 | + .data = &pac1934_chip_config[PAC1933] | |
| 1601 | + }, | |
| 1602 | + { | |
| 1603 | + .compatible = "microchip,pac1934", | |
| 1604 | + .data = &pac1934_chip_config[PAC1934] | |
| 1605 | + }, | |
| 1606 | + {} | |
| 1607 | +}; | |
| 1608 | +MODULE_DEVICE_TABLE(of, pac1934_of_match); | |
| 1609 | + | |
| 1610 | +/* | |
| 1611 | + * using MCHP1930 to be compatible with BIOS ACPI. See example: | |
| 1612 | + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf | |
| 1613 | + */ | |
| 1614 | +static const struct acpi_device_id pac1934_acpi_match[] = { | |
| 1615 | + { "MCHP1930", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, | |
| 1616 | + {} | |
| 1617 | +}; | |
| 1618 | +MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match); | |
| 1619 | + | |
| 1620 | +static struct i2c_driver pac1934_driver = { | |
| 1621 | + .driver = { | |
| 1622 | + .name = "pac1934", | |
| 1623 | + .of_match_table = pac1934_of_match, | |
| 1624 | + .acpi_match_table = pac1934_acpi_match | |
| 1625 | + }, | |
| 1626 | + .probe = pac1934_probe, | |
| 1627 | + .id_table = pac1934_id, | |
| 1628 | +}; | |
| 1629 | + | |
| 1630 | +module_i2c_driver(pac1934_driver); | |
| 1631 | + | |
| 1632 | +MODULE_AUTHOR("Bogdan Bolocan <bogdan.bolocan@microchip.com>"); | |
| 1633 | +MODULE_AUTHOR("Victor Tudose"); | |
| 1634 | +MODULE_AUTHOR("Marius Cristea <marius.cristea@microchip.com>"); | |
| 1635 | +MODULE_DESCRIPTION("IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor"); | |
| 1636 | +MODULE_LICENSE("GPL"); | |
| @@ -372,7 +372,6 @@ static const struct xoadc_channel pm8921_xoadc_channels[] = { · 371 unmodified lines | ||
| 372 | 372 | * @name: name of this channel |
| 373 | 373 | * @hwchan: pointer to hardware channel information (muxing & scaling settings) |
| 374 | 374 | * @calibration: whether to use absolute or ratiometric calibration |
| 375 | − * @scale_fn_type: scaling function type | |
| 376 | 375 | * @decimation: 0,1,2,3 |
| 377 | 376 | * @amux_ip_rsv: ratiometric scale value if using ratiometric |
| 378 | 377 | * calibration: 0, 1, 2, 4, 5. |
| @@ -52,7 +52,7 @@ · 51 unmodified lines | ||
| 52 | 52 | #define SARADC2_START BIT(4) |
| 53 | 53 | #define SARADC2_SINGLE_MODE BIT(5) |
| 54 | 54 | |
| 55 | −#define SARADC2_CONV_CHANNELS GENMASK(15, 0) | |
| 55 | +#define SARADC2_CONV_CHANNELS GENMASK(3, 0) | |
| 56 | 56 | |
| 57 | 57 | struct rockchip_saradc; |
| 58 | 58 | |
| @@ -102,12 +102,12 @@ static void rockchip_saradc_start_v2(struct rockchip_saradc *info, int chn) · 43 unmodified lines | ||
| 102 | 102 | writel_relaxed(0xc, info->regs + SARADC_T_DAS_SOC); |
| 103 | 103 | writel_relaxed(0x20, info->regs + SARADC_T_PD_SOC); |
| 104 | 104 | val = FIELD_PREP(SARADC2_EN_END_INT, 1); |
| 105 | − val |= val << 16; | |
| 105 | + val |= SARADC2_EN_END_INT << 16; | |
| 106 | 106 | writel_relaxed(val, info->regs + SARADC2_END_INT_EN); |
| 107 | 107 | val = FIELD_PREP(SARADC2_START, 1) | |
| 108 | 108 | FIELD_PREP(SARADC2_SINGLE_MODE, 1) | |
| 109 | 109 | FIELD_PREP(SARADC2_CONV_CHANNELS, chn); |
| 110 | − val |= val << 16; | |
| 110 | + val |= (SARADC2_START | SARADC2_SINGLE_MODE | SARADC2_CONV_CHANNELS) << 16; | |
| 111 | 111 | writel(val, info->regs + SARADC2_CONV_CON); |
| 112 | 112 | } |
| 113 | 113 | |
| @@ -450,16 +450,11 @@ static int rockchip_saradc_probe(struct platform_device *pdev) · 336 unmodified lines | ||
| 450 | 450 | * The reset should be an optional property, as it should work |
| 451 | 451 | * with old devicetrees as well |
| 452 | 452 | */ |
| 453 | − info->reset = devm_reset_control_get_exclusive(&pdev->dev, | |
| 454 | − "saradc-apb"); | |
| 453 | + info->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, | |
| 454 | + "saradc-apb"); | |
| 455 | 455 | if (IS_ERR(info->reset)) { |
| 456 | 456 | ret = PTR_ERR(info->reset); |
| 457 | − if (ret != -ENOENT) | |
| 458 | − return dev_err_probe(&pdev->dev, ret, | |
| 459 | − "failed to get saradc-apb\n"); | |
| 460 | − | |
| 461 | − dev_dbg(&pdev->dev, "no reset control found\n"); | |
| 462 | − info->reset = NULL; | |
| 457 | + return dev_err_probe(&pdev->dev, ret, "failed to get saradc-apb\n"); | |
| 463 | 458 | } |
| 464 | 459 | |
| 465 | 460 | init_completion(&info->completion); |
| @@ -39,6 +39,10 @@ · 38 unmodified lines | ||
| 39 | 39 | #define RTQ6056_DEFAULT_CONFIG 0x4127 |
| 40 | 40 | #define RTQ6056_CONT_ALLON 7 |
| 41 | 41 | |
| 42 | +#define RTQ6059_DEFAULT_CONFIG 0x3C47 | |
| 43 | +#define RTQ6059_VBUS_LSB_OFFSET 3 | |
| 44 | +#define RTQ6059_AVG_BASE 8 | |
| 45 | + | |
| 42 | 46 | enum { |
| 43 | 47 | RTQ6056_CH_VSHUNT = 0, |
| 44 | 48 | RTQ6056_CH_VBUS, |
| @@ -47,19 +51,46 @@ enum { · 2 unmodified lines | ||
| 47 | 51 | RTQ6056_MAX_CHANNEL |
| 48 | 52 | }; |
| 49 | 53 | |
| 54 | +/* | |
| 55 | + * The enum is to present the 0x00 CONFIG RG bitfield for the 16bit RG value | |
| 56 | + * field value order from LSB to MSB | |
| 57 | + * RTQ6053/6 is OPMODE->VSHUNTCT->VBUSCT->AVG->RESET | |
| 58 | + * RTQ6059 is OPMODE->SADC->BADC->PGA->RESET | |
| 59 | + */ | |
| 50 | 60 | enum { |
| 51 | 61 | F_OPMODE = 0, |
| 52 | 62 | F_VSHUNTCT, |
| 63 | + F_RTQ6059_SADC = F_VSHUNTCT, | |
| 53 | 64 | F_VBUSCT, |
| 65 | + F_RTQ6059_BADC = F_VBUSCT, | |
| 54 | 66 | F_AVG, |
| 67 | + F_RTQ6059_PGA = F_AVG, | |
| 55 | 68 | F_RESET, |
| 56 | 69 | F_MAX_FIELDS |
| 57 | 70 | }; |
| 58 | 71 | |
| 72 | +struct rtq6056_priv; | |
| 73 | + | |
| 74 | +struct richtek_dev_data { | |
| 75 | + bool fixed_samp_freq; | |
| 76 | + u8 vbus_offset; | |
| 77 | + int default_conv_time_us; | |
| 78 | + unsigned int default_config; | |
| 79 | + unsigned int calib_coefficient; | |
| 80 | + const int *avg_sample_list; | |
| 81 | + int avg_sample_list_length; | |
| 82 | + const struct reg_field *reg_fields; | |
| 83 | + const struct iio_chan_spec *channels; | |
| 84 | + int num_channels; | |
| 85 | + int (*read_scale)(struct iio_chan_spec const *ch, int *val, int *val2); | |
| 86 | + int (*set_average)(struct rtq6056_priv *priv, int val); | |
| 87 | +}; | |
| 88 | + | |
| 59 | 89 | struct rtq6056_priv { |
| 60 | 90 | struct device *dev; |
| 61 | 91 | struct regmap *regmap; |
| 62 | 92 | struct regmap_field *rm_fields[F_MAX_FIELDS]; |
| 93 | + const struct richtek_dev_data *devdata; | |
| 63 | 94 | u32 shunt_resistor_uohm; |
| 64 | 95 | int vshuntct_us; |
| 65 | 96 | int vbusct_us; |
| @@ -74,6 +105,14 @@ static const struct reg_field rtq6056_reg_fields[F_MAX_FIELDS] = { · 8 unmodified lines | ||
| 74 | 105 | [F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15), |
| 75 | 106 | }; |
| 76 | 107 | |
| 108 | +static const struct reg_field rtq6059_reg_fields[F_MAX_FIELDS] = { | |
| 109 | + [F_OPMODE] = REG_FIELD(RTQ6056_REG_CONFIG, 0, 2), | |
| 110 | + [F_RTQ6059_SADC] = REG_FIELD(RTQ6056_REG_CONFIG, 3, 6), | |
| 111 | + [F_RTQ6059_BADC] = REG_FIELD(RTQ6056_REG_CONFIG, 7, 10), | |
| 112 | + [F_RTQ6059_PGA] = REG_FIELD(RTQ6056_REG_CONFIG, 11, 12), | |
| 113 | + [F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15), | |
| 114 | +}; | |
| 115 | + | |
| 77 | 116 | static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = { |
| 78 | 117 | { |
| 79 | 118 | .type = IIO_VOLTAGE, |
| @@ -151,10 +190,93 @@ static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = { · 71 unmodified lines | ||
| 151 | 190 | IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL), |
| 152 | 191 | }; |
| 153 | 192 | |
| 193 | +/* | |
| 194 | + * Difference between RTQ6056 and RTQ6059 | |
| 195 | + * - Fixed sampling conversion time | |
| 196 | + * - Average sample numbers | |
| 197 | + * - Channel scale | |
| 198 | + * - calibration coefficient | |
| 199 | + */ | |
| 200 | +static const struct iio_chan_spec rtq6059_channels[RTQ6056_MAX_CHANNEL + 1] = { | |
| 201 | + { | |
| 202 | + .type = IIO_VOLTAGE, | |
| 203 | + .indexed = 1, | |
| 204 | + .channel = 0, | |
| 205 | + .address = RTQ6056_REG_SHUNTVOLT, | |
| 206 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 207 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 208 | + BIT(IIO_CHAN_INFO_SAMP_FREQ), | |
| 209 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 210 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 211 | + .scan_index = 0, | |
| 212 | + .scan_type = { | |
| 213 | + .sign = 's', | |
| 214 | + .realbits = 16, | |
| 215 | + .storagebits = 16, | |
| 216 | + .endianness = IIO_CPU, | |
| 217 | + }, | |
| 218 | + }, | |
| 219 | + { | |
| 220 | + .type = IIO_VOLTAGE, | |
| 221 | + .indexed = 1, | |
| 222 | + .channel = 1, | |
| 223 | + .address = RTQ6056_REG_BUSVOLT, | |
| 224 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 225 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 226 | + BIT(IIO_CHAN_INFO_SAMP_FREQ), | |
| 227 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 228 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 229 | + .scan_index = 1, | |
| 230 | + .scan_type = { | |
| 231 | + .sign = 'u', | |
| 232 | + .realbits = 16, | |
| 233 | + .storagebits = 16, | |
| 234 | + .endianness = IIO_CPU, | |
| 235 | + }, | |
| 236 | + }, | |
| 237 | + { | |
| 238 | + .type = IIO_POWER, | |
| 239 | + .indexed = 1, | |
| 240 | + .channel = 2, | |
| 241 | + .address = RTQ6056_REG_POWER, | |
| 242 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 243 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 244 | + BIT(IIO_CHAN_INFO_SAMP_FREQ), | |
| 245 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 246 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 247 | + .scan_index = 2, | |
| 248 | + .scan_type = { | |
| 249 | + .sign = 'u', | |
| 250 | + .realbits = 16, | |
| 251 | + .storagebits = 16, | |
| 252 | + .endianness = IIO_CPU, | |
| 253 | + }, | |
| 254 | + }, | |
| 255 | + { | |
| 256 | + .type = IIO_CURRENT, | |
| 257 | + .indexed = 1, | |
| 258 | + .channel = 3, | |
| 259 | + .address = RTQ6056_REG_CURRENT, | |
| 260 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 261 | + BIT(IIO_CHAN_INFO_SAMP_FREQ), | |
| 262 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 263 | + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), | |
| 264 | + .scan_index = 3, | |
| 265 | + .scan_type = { | |
| 266 | + .sign = 's', | |
| 267 | + .realbits = 16, | |
| 268 | + .storagebits = 16, | |
| 269 | + .endianness = IIO_CPU, | |
| 270 | + }, | |
| 271 | + }, | |
| 272 | + IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL), | |
| 273 | +}; | |
| 274 | + | |
| 154 | 275 | static int rtq6056_adc_read_channel(struct rtq6056_priv *priv, |
| 155 | 276 | struct iio_chan_spec const *ch, |
| 156 | 277 | int *val) |
| 157 | 278 | { |
| 279 | + const struct richtek_dev_data *devdata = priv->devdata; | |
| 158 | 280 | struct device *dev = priv->dev; |
| 159 | 281 | unsigned int addr = ch->address; |
| 160 | 282 | unsigned int regval; |
| @@ -168,12 +290,21 @@ static int rtq6056_adc_read_channel(struct rtq6056_priv *priv, · 7 unmodified lines | ||
| 168 | 290 | return ret; |
| 169 | 291 | |
| 170 | 292 | /* Power and VBUS is unsigned 16-bit, others are signed 16-bit */ |
| 171 | − if (addr == RTQ6056_REG_BUSVOLT || addr == RTQ6056_REG_POWER) | |
| 293 | + switch (addr) { | |
| 294 | + case RTQ6056_REG_BUSVOLT: | |
| 295 | + regval >>= devdata->vbus_offset; | |
| 172 | 296 | *val = regval; |
| 173 | − else | |
| 297 | + return IIO_VAL_INT; | |
| 298 | + case RTQ6056_REG_POWER: | |
| 299 | + *val = regval; | |
| 300 | + return IIO_VAL_INT; | |
| 301 | + case RTQ6056_REG_SHUNTVOLT: | |
| 302 | + case RTQ6056_REG_CURRENT: | |
| 174 | 303 | *val = sign_extend32(regval, 16); |
| 175 | − | |
| 176 | − return IIO_VAL_INT; | |
| 304 | + return IIO_VAL_INT; | |
| 305 | + default: | |
| 306 | + return -EINVAL; | |
| 307 | + } | |
| 177 | 308 | } |
| 178 | 309 | |
| 179 | 310 | static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val, |
| @@ -199,6 +330,28 @@ static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val, · 19 unmodified lines | ||
| 199 | 330 | } |
| 200 | 331 | } |
| 201 | 332 | |
| 333 | +static int rtq6059_adc_read_scale(struct iio_chan_spec const *ch, int *val, | |
| 334 | + int *val2) | |
| 335 | +{ | |
| 336 | + switch (ch->address) { | |
| 337 | + case RTQ6056_REG_SHUNTVOLT: | |
| 338 | + /* VSHUNT lsb 10uV */ | |
| 339 | + *val = 10000; | |
| 340 | + *val2 = 1000000; | |
| 341 | + return IIO_VAL_FRACTIONAL; | |
| 342 | + case RTQ6056_REG_BUSVOLT: | |
| 343 | + /* VBUS lsb 4mV */ | |
| 344 | + *val = 4; | |
| 345 | + return IIO_VAL_INT; | |
| 346 | + case RTQ6056_REG_POWER: | |
| 347 | + /* Power lsb 20mW */ | |
| 348 | + *val = 20; | |
| 349 | + return IIO_VAL_INT; | |
| 350 | + default: | |
| 351 | + return -EINVAL; | |
| 352 | + } | |
| 353 | +} | |
| 354 | + | |
| 202 | 355 | /* |
| 203 | 356 | * Sample frequency for channel VSHUNT and VBUS. The indices correspond |
| 204 | 357 | * with the bit value expected by the chip. And it can be found at |
| @@ -248,6 +401,10 @@ static const int rtq6056_avg_sample_list[] = { · 43 unmodified lines | ||
| 248 | 401 | 1, 4, 16, 64, 128, 256, 512, 1024, |
| 249 | 402 | }; |
| 250 | 403 | |
| 404 | +static const int rtq6059_avg_sample_list[] = { | |
| 405 | + 1, 2, 4, 8, 16, 32, 64, 128, | |
| 406 | +}; | |
| 407 | + | |
| 251 | 408 | static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val) |
| 252 | 409 | { |
| 253 | 410 | unsigned int selector; |
| @@ -268,6 +425,30 @@ static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val) · 14 unmodified lines | ||
| 268 | 425 | return 0; |
| 269 | 426 | } |
| 270 | 427 | |
| 428 | +static int rtq6059_adc_set_average(struct rtq6056_priv *priv, int val) | |
| 429 | +{ | |
| 430 | + unsigned int selector; | |
| 431 | + int ret; | |
| 432 | + | |
| 433 | + if (val > 128 || val < 1) | |
| 434 | + return -EINVAL; | |
| 435 | + | |
| 436 | + /* The supported average sample is 2^x (x from 0 to 7) */ | |
| 437 | + selector = fls(val) - 1; | |
| 438 | + | |
| 439 | + ret = regmap_field_write(priv->rm_fields[F_RTQ6059_BADC], | |
| 440 | + RTQ6059_AVG_BASE + selector); | |
| 441 | + if (ret) | |
| 442 | + return ret; | |
| 443 | + | |
| 444 | + ret = regmap_field_write(priv->rm_fields[F_RTQ6059_SADC], | |
| 445 | + RTQ6059_AVG_BASE + selector); | |
| 446 | + | |
| 447 | + priv->avg_sample = BIT(selector); | |
| 448 | + | |
| 449 | + return 0; | |
| 450 | +} | |
| 451 | + | |
| 271 | 452 | static int rtq6056_adc_get_sample_freq(struct rtq6056_priv *priv, |
| 272 | 453 | struct iio_chan_spec const *ch, int *val) |
| 273 | 454 | { |
| @@ -292,12 +473,13 @@ static int rtq6056_adc_read_raw(struct iio_dev *indio_dev, · 18 unmodified lines | ||
| 292 | 473 | int *val2, long mask) |
| 293 | 474 | { |
| 294 | 475 | struct rtq6056_priv *priv = iio_priv(indio_dev); |
| 476 | + const struct richtek_dev_data *devdata = priv->devdata; | |
| 295 | 477 | |
| 296 | 478 | switch (mask) { |
| 297 | 479 | case IIO_CHAN_INFO_RAW: |
| 298 | 480 | return rtq6056_adc_read_channel(priv, chan, val); |
| 299 | 481 | case IIO_CHAN_INFO_SCALE: |
| 300 | − return rtq6056_adc_read_scale(chan, val, val2); | |
| 482 | + return devdata->read_scale(chan, val, val2); | |
| 301 | 483 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| 302 | 484 | *val = priv->avg_sample; |
| 303 | 485 | return IIO_VAL_INT; |
| @@ -313,6 +495,9 @@ static int rtq6056_adc_read_avail(struct iio_dev *indio_dev, · 9 unmodified lines | ||
| 313 | 495 | const int **vals, int *type, int *length, |
| 314 | 496 | long mask) |
| 315 | 497 | { |
| 498 | + struct rtq6056_priv *priv = iio_priv(indio_dev); | |
| 499 | + const struct richtek_dev_data *devdata = priv->devdata; | |
| 500 | + | |
| 316 | 501 | switch (mask) { |
| 317 | 502 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 318 | 503 | *vals = rtq6056_samp_freq_list; |
| @@ -320,9 +505,9 @@ static int rtq6056_adc_read_avail(struct iio_dev *indio_dev, · 1 unmodified line | ||
| 320 | 505 | *length = ARRAY_SIZE(rtq6056_samp_freq_list); |
| 321 | 506 | return IIO_AVAIL_LIST; |
| 322 | 507 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| 323 | − *vals = rtq6056_avg_sample_list; | |
| 508 | + *vals = devdata->avg_sample_list; | |
| 509 | + *length = devdata->avg_sample_list_length; | |
| 324 | 510 | *type = IIO_VAL_INT; |
| 325 | − *length = ARRAY_SIZE(rtq6056_avg_sample_list); | |
| 326 | 511 | return IIO_AVAIL_LIST; |
| 327 | 512 | default: |
| 328 | 513 | return -EINVAL; |
| @@ -334,6 +519,7 @@ static int rtq6056_adc_write_raw(struct iio_dev *indio_dev, · 5 unmodified lines | ||
| 334 | 519 | int val2, long mask) |
| 335 | 520 | { |
| 336 | 521 | struct rtq6056_priv *priv = iio_priv(indio_dev); |
| 522 | + const struct richtek_dev_data *devdata = priv->devdata; | |
| 337 | 523 | int ret; |
| 338 | 524 | |
| 339 | 525 | ret = iio_device_claim_direct_mode(indio_dev); |
| @@ -342,10 +528,15 @@ static int rtq6056_adc_write_raw(struct iio_dev *indio_dev, · 2 unmodified lines | ||
| 342 | 528 | |
| 343 | 529 | switch (mask) { |
| 344 | 530 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 531 | + if (devdata->fixed_samp_freq) { | |
| 532 | + ret = -EINVAL; | |
| 533 | + break; | |
| 534 | + } | |
| 535 | + | |
| 345 | 536 | ret = rtq6056_adc_set_samp_freq(priv, chan, val); |
| 346 | 537 | break; |
| 347 | 538 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| 348 | − ret = rtq6056_adc_set_average(priv, val); | |
| 539 | + ret = devdata->set_average(priv, val); | |
| 349 | 540 | break; |
| 350 | 541 | default: |
| 351 | 542 | ret = -EINVAL; |
| @@ -374,6 +565,7 @@ static int rtq6056_adc_read_label(struct iio_dev *indio_dev, · 22 unmodified lines | ||
| 374 | 565 | static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv, |
| 375 | 566 | int resistor_uohm) |
| 376 | 567 | { |
| 568 | + const struct richtek_dev_data *devdata = priv->devdata; | |
| 377 | 569 | unsigned int calib_val; |
| 378 | 570 | int ret; |
| 379 | 571 | |
| @@ -382,8 +574,8 @@ static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv, · 2 unmodified lines | ||
| 382 | 574 | return -EINVAL; |
| 383 | 575 | } |
| 384 | 576 | |
| 385 | − /* calibration = 5120000 / (Rshunt (uOhm) * current lsb (1mA)) */ | |
| 386 | − calib_val = 5120000 / resistor_uohm; | |
| 577 | + /* calibration = coefficient / (Rshunt (uOhm) * current lsb (1mA)) */ | |
| 578 | + calib_val = devdata->calib_coefficient / resistor_uohm; | |
| 387 | 579 | ret = regmap_write(priv->regmap, RTQ6056_REG_CALIBRATION, calib_val); |
| 388 | 580 | if (ret) |
| 389 | 581 | return ret; |
| @@ -450,6 +642,7 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) · 60 unmodified lines | ||
| 450 | 642 | struct iio_poll_func *pf = p; |
| 451 | 643 | struct iio_dev *indio_dev = pf->indio_dev; |
| 452 | 644 | struct rtq6056_priv *priv = iio_priv(indio_dev); |
| 645 | + const struct richtek_dev_data *devdata = priv->devdata; | |
| 453 | 646 | struct device *dev = priv->dev; |
| 454 | 647 | struct { |
| 455 | 648 | u16 vals[RTQ6056_MAX_CHANNEL]; |
| @@ -469,6 +662,9 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) · 13 unmodified lines | ||
| 469 | 662 | if (ret) |
| 470 | 663 | goto out; |
| 471 | 664 | |
| 665 | + if (addr == RTQ6056_REG_BUSVOLT) | |
| 666 | + raw >>= devdata->vbus_offset; | |
| 667 | + | |
| 472 | 668 | data.vals[i++] = raw; |
| 473 | 669 | } |
| 474 | 670 | |
| @@ -528,20 +724,26 @@ static int rtq6056_probe(struct i2c_client *i2c) · 53 unmodified lines | ||
| 528 | 724 | struct rtq6056_priv *priv; |
| 529 | 725 | struct device *dev = &i2c->dev; |
| 530 | 726 | struct regmap *regmap; |
| 727 | + const struct richtek_dev_data *devdata; | |
| 531 | 728 | unsigned int vendor_id, shunt_resistor_uohm; |
| 532 | 729 | int ret; |
| 533 | 730 | |
| 534 | 731 | if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) |
| 535 | 732 | return -EOPNOTSUPP; |
| 536 | 733 | |
| 734 | + devdata = device_get_match_data(dev); | |
| 735 | + if (!devdata) | |
| 736 | + return dev_err_probe(dev, -EINVAL, "Invalid dev data\n"); | |
| 737 | + | |
| 537 | 738 | indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); |
| 538 | 739 | if (!indio_dev) |
| 539 | 740 | return -ENOMEM; |
| 540 | 741 | |
| 541 | 742 | priv = iio_priv(indio_dev); |
| 542 | 743 | priv->dev = dev; |
| 543 | − priv->vshuntct_us = priv->vbusct_us = 1037; | |
| 744 | + priv->vshuntct_us = priv->vbusct_us = devdata->default_conv_time_us; | |
| 544 | 745 | priv->avg_sample = 1; |
| 746 | + priv->devdata = devdata; | |
| 545 | 747 | i2c_set_clientdata(i2c, priv); |
| 546 | 748 | |
| 547 | 749 | regmap = devm_regmap_init_i2c(i2c, &rtq6056_regmap_config); |
| @@ -561,15 +763,11 @@ static int rtq6056_probe(struct i2c_client *i2c) · 13 unmodified lines | ||
| 561 | 763 | "Invalid vendor id 0x%04x\n", vendor_id); |
| 562 | 764 | |
| 563 | 765 | ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->rm_fields, |
| 564 | − rtq6056_reg_fields, F_MAX_FIELDS); | |
| 766 | + devdata->reg_fields, F_MAX_FIELDS); | |
| 565 | 767 | if (ret) |
| 566 | 768 | return dev_err_probe(dev, ret, "Failed to init regmap field\n"); |
| 567 | 769 | |
| 568 | − /* | |
| 569 | − * By default, configure average sample as 1, bus and shunt conversion | |
| 570 | − * time as 1037 microsecond, and operating mode to all on. | |
| 571 | − */ | |
| 572 | − ret = regmap_write(regmap, RTQ6056_REG_CONFIG, RTQ6056_DEFAULT_CONFIG); | |
| 770 | + ret = regmap_write(regmap, RTQ6056_REG_CONFIG, devdata->default_config); | |
| 573 | 771 | if (ret) |
| 574 | 772 | return dev_err_probe(dev, ret, |
| 575 | 773 | "Failed to enable continuous sensing\n"); |
| @@ -598,8 +796,8 @@ static int rtq6056_probe(struct i2c_client *i2c) · 22 unmodified lines | ||
| 598 | 796 | |
| 599 | 797 | indio_dev->name = "rtq6056"; |
| 600 | 798 | indio_dev->modes = INDIO_DIRECT_MODE; |
| 601 | − indio_dev->channels = rtq6056_channels; | |
| 602 | − indio_dev->num_channels = ARRAY_SIZE(rtq6056_channels); | |
| 799 | + indio_dev->channels = devdata->channels; | |
| 800 | + indio_dev->num_channels = devdata->num_channels; | |
| 603 | 801 | indio_dev->info = &rtq6056_info; |
| 604 | 802 | |
| 605 | 803 | ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, |
| @@ -640,8 +838,45 @@ static int rtq6056_runtime_resume(struct device *dev) · 34 unmodified lines | ||
| 640 | 838 | static DEFINE_RUNTIME_DEV_PM_OPS(rtq6056_pm_ops, rtq6056_runtime_suspend, |
| 641 | 839 | rtq6056_runtime_resume, NULL); |
| 642 | 840 | |
| 841 | +static const struct richtek_dev_data rtq6056_devdata = { | |
| 842 | + .default_conv_time_us = 1037, | |
| 843 | + .calib_coefficient = 5120000, | |
| 844 | + /* | |
| 845 | + * By default, configure average sample as 1, bus and shunt conversion | |
| 846 | + * time as 1037 microsecond, and operating mode to all on. | |
| 847 | + */ | |
| 848 | + .default_config = RTQ6056_DEFAULT_CONFIG, | |
| 849 | + .avg_sample_list = rtq6056_avg_sample_list, | |
| 850 | + .avg_sample_list_length = ARRAY_SIZE(rtq6056_avg_sample_list), | |
| 851 | + .reg_fields = rtq6056_reg_fields, | |
| 852 | + .channels = rtq6056_channels, | |
| 853 | + .num_channels = ARRAY_SIZE(rtq6056_channels), | |
| 854 | + .read_scale = rtq6056_adc_read_scale, | |
| 855 | + .set_average = rtq6056_adc_set_average, | |
| 856 | +}; | |
| 857 | + | |
| 858 | +static const struct richtek_dev_data rtq6059_devdata = { | |
| 859 | + .fixed_samp_freq = true, | |
| 860 | + .vbus_offset = RTQ6059_VBUS_LSB_OFFSET, | |
| 861 | + .default_conv_time_us = 532, | |
| 862 | + .calib_coefficient = 40960000, | |
| 863 | + /* | |
| 864 | + * By default, configure average sample as 1, bus and shunt conversion | |
| 865 | + * time as 532 microsecond, and operating mode to all on. | |
| 866 | + */ | |
| 867 | + .default_config = RTQ6059_DEFAULT_CONFIG, | |
| 868 | + .avg_sample_list = rtq6059_avg_sample_list, | |
| 869 | + .avg_sample_list_length = ARRAY_SIZE(rtq6059_avg_sample_list), | |
| 870 | + .reg_fields = rtq6059_reg_fields, | |
| 871 | + .channels = rtq6059_channels, | |
| 872 | + .num_channels = ARRAY_SIZE(rtq6059_channels), | |
| 873 | + .read_scale = rtq6059_adc_read_scale, | |
| 874 | + .set_average = rtq6059_adc_set_average, | |
| 875 | +}; | |
| 876 | + | |
| 643 | 877 | static const struct of_device_id rtq6056_device_match[] = { |
| 644 | − { .compatible = "richtek,rtq6056" }, | |
| 878 | + { .compatible = "richtek,rtq6056", .data = &rtq6056_devdata }, | |
| 879 | + { .compatible = "richtek,rtq6059", .data = &rtq6059_devdata }, | |
| 645 | 880 | {} |
| 646 | 881 | }; |
| 647 | 882 | MODULE_DEVICE_TABLE(of, rtq6056_device_match); |
| @@ -293,13 +293,11 @@ static const struct of_device_id adc108s102_of_match[] = { · 292 unmodified lines | ||
| 293 | 293 | }; |
| 294 | 294 | MODULE_DEVICE_TABLE(of, adc108s102_of_match); |
| 295 | 295 | |
| 296 | −#ifdef CONFIG_ACPI | |
| 297 | 296 | static const struct acpi_device_id adc108s102_acpi_ids[] = { |
| 298 | 297 | { "INT3495", 0 }, |
| 299 | 298 | { } |
| 300 | 299 | }; |
| 301 | 300 | MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids); |
| 302 | −#endif | |
| 303 | 301 | |
| 304 | 302 | static const struct spi_device_id adc108s102_id[] = { |
| 305 | 303 | { "adc108s102", 0 }, |
| @@ -311,7 +309,7 @@ static struct spi_driver adc108s102_driver = { · 5 unmodified lines | ||
| 311 | 309 | .driver = { |
| 312 | 310 | .name = "adc108s102", |
| 313 | 311 | .of_match_table = adc108s102_of_match, |
| 314 | − .acpi_match_table = ACPI_PTR(adc108s102_acpi_ids), | |
| 312 | + .acpi_match_table = adc108s102_acpi_ids, | |
| 315 | 313 | }, |
| 316 | 314 | .probe = adc108s102_probe, |
| 317 | 315 | .id_table = adc108s102_id, |
| @@ -925,7 +925,7 @@ static int ads1015_client_get_channels_config(struct i2c_client *client) · 924 unmodified lines | ||
| 925 | 925 | |
| 926 | 926 | if (!fwnode_property_read_u32(node, "ti,gain", &pval)) { |
| 927 | 927 | pga = pval; |
| 928 | − if (pga > 6) { | |
| 928 | + if (pga > 5) { | |
| 929 | 929 | dev_err(dev, "invalid gain on %pfw\n", node); |
| 930 | 930 | fwnode_handle_put(node); |
| 931 | 931 | return -EINVAL; |
| @@ -0,0 +1,771 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* TI ADS1298 chip family driver | |
| 3 | + * Copyright (C) 2023 - 2024 Topic Embedded Products | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bitfield.h> | |
| 7 | +#include <linux/cleanup.h> | |
| 8 | +#include <linux/clk.h> | |
| 9 | +#include <linux/err.h> | |
| 10 | +#include <linux/delay.h> | |
| 11 | +#include <linux/device.h> | |
| 12 | +#include <linux/gpio/consumer.h> | |
| 13 | +#include <linux/log2.h> | |
| 14 | +#include <linux/math.h> | |
| 15 | +#include <linux/module.h> | |
| 16 | +#include <linux/regmap.h> | |
| 17 | +#include <linux/regulator/consumer.h> | |
| 18 | +#include <linux/slab.h> | |
| 19 | +#include <linux/spi/spi.h> | |
| 20 | +#include <linux/units.h> | |
| 21 | + | |
| 22 | +#include <linux/iio/iio.h> | |
| 23 | +#include <linux/iio/buffer.h> | |
| 24 | +#include <linux/iio/kfifo_buf.h> | |
| 25 | + | |
| 26 | +#include <asm/unaligned.h> | |
| 27 | + | |
| 28 | +/* Commands */ | |
| 29 | +#define ADS1298_CMD_WAKEUP 0x02 | |
| 30 | +#define ADS1298_CMD_STANDBY 0x04 | |
| 31 | +#define ADS1298_CMD_RESET 0x06 | |
| 32 | +#define ADS1298_CMD_START 0x08 | |
| 33 | +#define ADS1298_CMD_STOP 0x0a | |
| 34 | +#define ADS1298_CMD_RDATAC 0x10 | |
| 35 | +#define ADS1298_CMD_SDATAC 0x11 | |
| 36 | +#define ADS1298_CMD_RDATA 0x12 | |
| 37 | +#define ADS1298_CMD_RREG 0x20 | |
| 38 | +#define ADS1298_CMD_WREG 0x40 | |
| 39 | + | |
| 40 | +/* Registers */ | |
| 41 | +#define ADS1298_REG_ID 0x00 | |
| 42 | +#define ADS1298_MASK_ID_FAMILY GENMASK(7, 3) | |
| 43 | +#define ADS1298_MASK_ID_CHANNELS GENMASK(2, 0) | |
| 44 | +#define ADS1298_ID_FAMILY_ADS129X 0x90 | |
| 45 | +#define ADS1298_ID_FAMILY_ADS129XR 0xd0 | |
| 46 | + | |
| 47 | +#define ADS1298_REG_CONFIG1 0x01 | |
| 48 | +#define ADS1298_MASK_CONFIG1_HR BIT(7) | |
| 49 | +#define ADS1298_MASK_CONFIG1_DR GENMASK(2, 0) | |
| 50 | +#define ADS1298_SHIFT_DR_HR 6 | |
| 51 | +#define ADS1298_SHIFT_DR_LP 7 | |
| 52 | +#define ADS1298_LOWEST_DR 0x06 | |
| 53 | + | |
| 54 | +#define ADS1298_REG_CONFIG2 0x02 | |
| 55 | +#define ADS1298_MASK_CONFIG2_RESERVED BIT(6) | |
| 56 | +#define ADS1298_MASK_CONFIG2_WCT_CHOP BIT(5) | |
| 57 | +#define ADS1298_MASK_CONFIG2_INT_TEST BIT(4) | |
| 58 | +#define ADS1298_MASK_CONFIG2_TEST_AMP BIT(2) | |
| 59 | +#define ADS1298_MASK_CONFIG2_TEST_FREQ_DC GENMASK(1, 0) | |
| 60 | +#define ADS1298_MASK_CONFIG2_TEST_FREQ_SLOW 0 | |
| 61 | +#define ADS1298_MASK_CONFIG2_TEST_FREQ_FAST BIT(0) | |
| 62 | + | |
| 63 | +#define ADS1298_REG_CONFIG3 0x03 | |
| 64 | +#define ADS1298_MASK_CONFIG3_PWR_REFBUF BIT(7) | |
| 65 | +#define ADS1298_MASK_CONFIG3_RESERVED BIT(6) | |
| 66 | +#define ADS1298_MASK_CONFIG3_VREF_4V BIT(5) | |
| 67 | + | |
| 68 | +#define ADS1298_REG_LOFF 0x04 | |
| 69 | +#define ADS1298_REG_CHnSET(n) (0x05 + n) | |
| 70 | +#define ADS1298_MASK_CH_PD BIT(7) | |
| 71 | +#define ADS1298_MASK_CH_PGA GENMASK(6, 4) | |
| 72 | +#define ADS1298_MASK_CH_MUX GENMASK(2, 0) | |
| 73 | + | |
| 74 | +#define ADS1298_REG_LOFF_STATP 0x12 | |
| 75 | +#define ADS1298_REG_LOFF_STATN 0x13 | |
| 76 | +#define ADS1298_REG_CONFIG4 0x17 | |
| 77 | +#define ADS1298_MASK_CONFIG4_SINGLE_SHOT BIT(3) | |
| 78 | + | |
| 79 | +#define ADS1298_REG_WCT1 0x18 | |
| 80 | +#define ADS1298_REG_WCT2 0x19 | |
| 81 | + | |
| 82 | +#define ADS1298_MAX_CHANNELS 8 | |
| 83 | +#define ADS1298_BITS_PER_SAMPLE 24 | |
| 84 | +#define ADS1298_CLK_RATE_HZ 2048000 | |
| 85 | +#define ADS1298_CLOCKS_TO_USECS(x) \ | |
| 86 | + (DIV_ROUND_UP((x) * MICROHZ_PER_HZ, ADS1298_CLK_RATE_HZ)) | |
| 87 | +/* | |
| 88 | + * Read/write register commands require 4 clocks to decode, for speeds above | |
| 89 | + * 2x the clock rate, this would require extra time between the command byte and | |
| 90 | + * the data. Much simpler is to just limit the SPI transfer speed while doing | |
| 91 | + * register access. | |
| 92 | + */ | |
| 93 | +#define ADS1298_SPI_BUS_SPEED_SLOW ADS1298_CLK_RATE_HZ | |
| 94 | +/* For reading and writing registers, we need a 3-byte buffer */ | |
| 95 | +#define ADS1298_SPI_CMD_BUFFER_SIZE 3 | |
| 96 | +/* Outputs status word and 'n' 24-bit samples, plus the command byte */ | |
| 97 | +#define ADS1298_SPI_RDATA_BUFFER_SIZE(n) (((n) + 1) * 3 + 1) | |
| 98 | +#define ADS1298_SPI_RDATA_BUFFER_SIZE_MAX \ | |
| 99 | + ADS1298_SPI_RDATA_BUFFER_SIZE(ADS1298_MAX_CHANNELS) | |
| 100 | + | |
| 101 | +struct ads1298_private { | |
| 102 | + const struct ads1298_chip_info *chip_info; | |
| 103 | + struct spi_device *spi; | |
| 104 | + struct regulator *reg_avdd; | |
| 105 | + struct regulator *reg_vref; | |
| 106 | + struct clk *clk; | |
| 107 | + struct regmap *regmap; | |
| 108 | + struct completion completion; | |
| 109 | + struct iio_trigger *trig; | |
| 110 | + struct spi_transfer rdata_xfer; | |
| 111 | + struct spi_message rdata_msg; | |
| 112 | + spinlock_t irq_busy_lock; /* Handshake between SPI and DRDY irqs */ | |
| 113 | + /* | |
| 114 | + * rdata_xfer_busy increments when a DRDY occurs and decrements when SPI | |
| 115 | + * completion is reported. Hence its meaning is: | |
| 116 | + * 0 = Waiting for DRDY interrupt | |
| 117 | + * 1 = SPI transfer in progress | |
| 118 | + * 2 = DRDY during SPI transfer, start another transfer on completion | |
| 119 | + * >2 = Multiple DRDY during transfer, lost rdata_xfer_busy - 2 samples | |
| 120 | + */ | |
| 121 | + unsigned int rdata_xfer_busy; | |
| 122 | + | |
| 123 | + /* Temporary storage for demuxing data after SPI transfer */ | |
| 124 | + u32 bounce_buffer[ADS1298_MAX_CHANNELS]; | |
| 125 | + | |
| 126 | + /* For synchronous SPI exchanges (read/write registers) */ | |
| 127 | + u8 cmd_buffer[ADS1298_SPI_CMD_BUFFER_SIZE] __aligned(IIO_DMA_MINALIGN); | |
| 128 | + | |
| 129 | + /* Buffer used for incoming SPI data */ | |
| 130 | + u8 rx_buffer[ADS1298_SPI_RDATA_BUFFER_SIZE_MAX]; | |
| 131 | + /* Contains the RDATA command and zeroes to clock out */ | |
| 132 | + u8 tx_buffer[ADS1298_SPI_RDATA_BUFFER_SIZE_MAX]; | |
| 133 | +}; | |
| 134 | + | |
| 135 | +/* Three bytes per sample in RX buffer, starting at offset 4 */ | |
| 136 | +#define ADS1298_OFFSET_IN_RX_BUFFER(index) (3 * (index) + 4) | |
| 137 | + | |
| 138 | +#define ADS1298_CHAN(index) \ | |
| 139 | +{ \ | |
| 140 | + .type = IIO_VOLTAGE, \ | |
| 141 | + .indexed = 1, \ | |
| 142 | + .channel = index, \ | |
| 143 | + .address = ADS1298_OFFSET_IN_RX_BUFFER(index), \ | |
| 144 | + .info_mask_separate = \ | |
| 145 | + BIT(IIO_CHAN_INFO_RAW) | \ | |
| 146 | + BIT(IIO_CHAN_INFO_SCALE), \ | |
| 147 | + .info_mask_shared_by_all = \ | |
| 148 | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ | |
| 149 | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ | |
| 150 | + .scan_index = index, \ | |
| 151 | + .scan_type = { \ | |
| 152 | + .sign = 's', \ | |
| 153 | + .realbits = ADS1298_BITS_PER_SAMPLE, \ | |
| 154 | + .storagebits = 32, \ | |
| 155 | + .endianness = IIO_CPU, \ | |
| 156 | + }, \ | |
| 157 | +} | |
| 158 | + | |
| 159 | +static const struct iio_chan_spec ads1298_channels[] = { | |
| 160 | + ADS1298_CHAN(0), | |
| 161 | + ADS1298_CHAN(1), | |
| 162 | + ADS1298_CHAN(2), | |
| 163 | + ADS1298_CHAN(3), | |
| 164 | + ADS1298_CHAN(4), | |
| 165 | + ADS1298_CHAN(5), | |
| 166 | + ADS1298_CHAN(6), | |
| 167 | + ADS1298_CHAN(7), | |
| 168 | +}; | |
| 169 | + | |
| 170 | +static int ads1298_write_cmd(struct ads1298_private *priv, u8 command) | |
| 171 | +{ | |
| 172 | + struct spi_transfer xfer = { | |
| 173 | + .tx_buf = priv->cmd_buffer, | |
| 174 | + .rx_buf = priv->cmd_buffer, | |
| 175 | + .len = 1, | |
| 176 | + .speed_hz = ADS1298_SPI_BUS_SPEED_SLOW, | |
| 177 | + .delay = { | |
| 178 | + .value = 2, | |
| 179 | + .unit = SPI_DELAY_UNIT_USECS, | |
| 180 | + }, | |
| 181 | + }; | |
| 182 | + | |
| 183 | + priv->cmd_buffer[0] = command; | |
| 184 | + | |
| 185 | + return spi_sync_transfer(priv->spi, &xfer, 1); | |
| 186 | +} | |
| 187 | + | |
| 188 | +static int ads1298_read_one(struct ads1298_private *priv, int chan_index) | |
| 189 | +{ | |
| 190 | + int ret; | |
| 191 | + | |
| 192 | + /* Enable the channel */ | |
| 193 | + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CHnSET(chan_index), | |
| 194 | + ADS1298_MASK_CH_PD, 0); | |
| 195 | + if (ret) | |
| 196 | + return ret; | |
| 197 | + | |
| 198 | + /* Enable single-shot mode, so we don't need to send a STOP */ | |
| 199 | + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG4, | |
| 200 | + ADS1298_MASK_CONFIG4_SINGLE_SHOT, | |
| 201 | + ADS1298_MASK_CONFIG4_SINGLE_SHOT); | |
| 202 | + if (ret) | |
| 203 | + return ret; | |
| 204 | + | |
| 205 | + reinit_completion(&priv->completion); | |
| 206 | + | |
| 207 | + ret = ads1298_write_cmd(priv, ADS1298_CMD_START); | |
| 208 | + if (ret < 0) { | |
| 209 | + dev_err(&priv->spi->dev, "CMD_START error: %d\n", ret); | |
| 210 | + return ret; | |
| 211 | + } | |
| 212 | + | |
| 213 | + /* Cannot take longer than 40ms (250Hz) */ | |
| 214 | + ret = wait_for_completion_timeout(&priv->completion, msecs_to_jiffies(50)); | |
| 215 | + if (!ret) | |
| 216 | + return -ETIMEDOUT; | |
| 217 | + | |
| 218 | + return 0; | |
| 219 | +} | |
| 220 | + | |
| 221 | +static int ads1298_get_samp_freq(struct ads1298_private *priv, int *val) | |
| 222 | +{ | |
| 223 | + unsigned long rate; | |
| 224 | + unsigned int cfg; | |
| 225 | + int ret; | |
| 226 | + | |
| 227 | + ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG1, &cfg); | |
| 228 | + if (ret) | |
| 229 | + return ret; | |
| 230 | + | |
| 231 | + if (priv->clk) | |
| 232 | + rate = clk_get_rate(priv->clk); | |
| 233 | + else | |
| 234 | + rate = ADS1298_CLK_RATE_HZ; | |
| 235 | + if (!rate) | |
| 236 | + return -EINVAL; | |
| 237 | + | |
| 238 | + /* Data rate shift depends on HR/LP mode */ | |
| 239 | + if (cfg & ADS1298_MASK_CONFIG1_HR) | |
| 240 | + rate >>= ADS1298_SHIFT_DR_HR; | |
| 241 | + else | |
| 242 | + rate >>= ADS1298_SHIFT_DR_LP; | |
| 243 | + | |
| 244 | + *val = rate >> (cfg & ADS1298_MASK_CONFIG1_DR); | |
| 245 | + | |
| 246 | + return IIO_VAL_INT; | |
| 247 | +} | |
| 248 | + | |
| 249 | +static int ads1298_set_samp_freq(struct ads1298_private *priv, int val) | |
| 250 | +{ | |
| 251 | + unsigned long rate; | |
| 252 | + unsigned int factor; | |
| 253 | + unsigned int cfg; | |
| 254 | + | |
| 255 | + if (priv->clk) | |
| 256 | + rate = clk_get_rate(priv->clk); | |
| 257 | + else | |
| 258 | + rate = ADS1298_CLK_RATE_HZ; | |
| 259 | + if (!rate) | |
| 260 | + return -EINVAL; | |
| 261 | + if (val <= 0) | |
| 262 | + return -EINVAL; | |
| 263 | + | |
| 264 | + factor = (rate >> ADS1298_SHIFT_DR_HR) / val; | |
| 265 | + if (factor >= BIT(ADS1298_SHIFT_DR_LP)) | |
| 266 | + cfg = ADS1298_LOWEST_DR; | |
| 267 | + else if (factor) | |
| 268 | + cfg = ADS1298_MASK_CONFIG1_HR | ilog2(factor); /* Use HR mode */ | |
| 269 | + else | |
| 270 | + cfg = ADS1298_MASK_CONFIG1_HR; /* Fastest possible */ | |
| 271 | + | |
| 272 | + return regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG1, | |
| 273 | + ADS1298_MASK_CONFIG1_HR | ADS1298_MASK_CONFIG1_DR, | |
| 274 | + cfg); | |
| 275 | +} | |
| 276 | + | |
| 277 | +static const u8 ads1298_pga_settings[] = { 6, 1, 2, 3, 4, 8, 12 }; | |
| 278 | + | |
| 279 | +static int ads1298_get_scale(struct ads1298_private *priv, | |
| 280 | + int channel, int *val, int *val2) | |
| 281 | +{ | |
| 282 | + int ret; | |
| 283 | + unsigned int regval; | |
| 284 | + u8 gain; | |
| 285 | + | |
| 286 | + if (priv->reg_vref) { | |
| 287 | + ret = regulator_get_voltage(priv->reg_vref); | |
| 288 | + if (ret < 0) | |
| 289 | + return ret; | |
| 290 | + | |
| 291 | + *val = ret / MILLI; /* Convert to millivolts */ | |
| 292 | + } else { | |
| 293 | + ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG3, ®val); | |
| 294 | + if (ret) | |
| 295 | + return ret; | |
| 296 | + | |
| 297 | + /* Refererence in millivolts */ | |
| 298 | + *val = regval & ADS1298_MASK_CONFIG3_VREF_4V ? 4000 : 2400; | |
| 299 | + } | |
| 300 | + | |
| 301 | + ret = regmap_read(priv->regmap, ADS1298_REG_CHnSET(channel), ®val); | |
| 302 | + if (ret) | |
| 303 | + return ret; | |
| 304 | + | |
| 305 | + gain = ads1298_pga_settings[FIELD_GET(ADS1298_MASK_CH_PGA, regval)]; | |
| 306 | + *val /= gain; /* Full scale is VREF / gain */ | |
| 307 | + | |
| 308 | + *val2 = ADS1298_BITS_PER_SAMPLE - 1; /* Signed, hence the -1 */ | |
| 309 | + | |
| 310 | + return IIO_VAL_FRACTIONAL_LOG2; | |
| 311 | +} | |
| 312 | + | |
| 313 | +static int ads1298_read_raw(struct iio_dev *indio_dev, | |
| 314 | + struct iio_chan_spec const *chan, | |
| 315 | + int *val, int *val2, long mask) | |
| 316 | +{ | |
| 317 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 318 | + int ret; | |
| 319 | + | |
| 320 | + switch (mask) { | |
| 321 | + case IIO_CHAN_INFO_RAW: | |
| 322 | + ret = iio_device_claim_direct_mode(indio_dev); | |
| 323 | + if (ret) | |
| 324 | + return ret; | |
| 325 | + | |
| 326 | + ret = ads1298_read_one(priv, chan->scan_index); | |
| 327 | + | |
| 328 | + iio_device_release_direct_mode(indio_dev); | |
| 329 | + | |
| 330 | + if (ret) | |
| 331 | + return ret; | |
| 332 | + | |
| 333 | + *val = sign_extend32(get_unaligned_be24(priv->rx_buffer + chan->address), | |
| 334 | + ADS1298_BITS_PER_SAMPLE - 1); | |
| 335 | + return IIO_VAL_INT; | |
| 336 | + case IIO_CHAN_INFO_SCALE: | |
| 337 | + return ads1298_get_scale(priv, chan->channel, val, val2); | |
| 338 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 339 | + return ads1298_get_samp_freq(priv, val); | |
| 340 | + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: | |
| 341 | + ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG1, val); | |
| 342 | + if (ret) | |
| 343 | + return ret; | |
| 344 | + | |
| 345 | + *val = 16 << (*val & ADS1298_MASK_CONFIG1_DR); | |
| 346 | + return IIO_VAL_INT; | |
| 347 | + default: | |
| 348 | + return -EINVAL; | |
| 349 | + } | |
| 350 | +} | |
| 351 | + | |
| 352 | +static int ads1298_write_raw(struct iio_dev *indio_dev, | |
| 353 | + struct iio_chan_spec const *chan, int val, | |
| 354 | + int val2, long mask) | |
| 355 | +{ | |
| 356 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 357 | + | |
| 358 | + switch (mask) { | |
| 359 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 360 | + return ads1298_set_samp_freq(priv, val); | |
| 361 | + default: | |
| 362 | + return -EINVAL; | |
| 363 | + } | |
| 364 | +} | |
| 365 | + | |
| 366 | +static int ads1298_reg_write(void *context, unsigned int reg, unsigned int val) | |
| 367 | +{ | |
| 368 | + struct ads1298_private *priv = context; | |
| 369 | + struct spi_transfer reg_write_xfer = { | |
| 370 | + .tx_buf = priv->cmd_buffer, | |
| 371 | + .rx_buf = priv->cmd_buffer, | |
| 372 | + .len = 3, | |
| 373 | + .speed_hz = ADS1298_SPI_BUS_SPEED_SLOW, | |
| 374 | + .delay = { | |
| 375 | + .value = 2, | |
| 376 | + .unit = SPI_DELAY_UNIT_USECS, | |
| 377 | + }, | |
| 378 | + }; | |
| 379 | + | |
| 380 | + priv->cmd_buffer[0] = ADS1298_CMD_WREG | reg; | |
| 381 | + priv->cmd_buffer[1] = 0; /* Number of registers to be written - 1 */ | |
| 382 | + priv->cmd_buffer[2] = val; | |
| 383 | + | |
| 384 | + return spi_sync_transfer(priv->spi, ®_write_xfer, 1); | |
| 385 | +} | |
| 386 | + | |
| 387 | +static int ads1298_reg_read(void *context, unsigned int reg, unsigned int *val) | |
| 388 | +{ | |
| 389 | + struct ads1298_private *priv = context; | |
| 390 | + struct spi_transfer reg_read_xfer = { | |
| 391 | + .tx_buf = priv->cmd_buffer, | |
| 392 | + .rx_buf = priv->cmd_buffer, | |
| 393 | + .len = 3, | |
| 394 | + .speed_hz = ADS1298_SPI_BUS_SPEED_SLOW, | |
| 395 | + .delay = { | |
| 396 | + .value = 2, | |
| 397 | + .unit = SPI_DELAY_UNIT_USECS, | |
| 398 | + }, | |
| 399 | + }; | |
| 400 | + int ret; | |
| 401 | + | |
| 402 | + priv->cmd_buffer[0] = ADS1298_CMD_RREG | reg; | |
| 403 | + priv->cmd_buffer[1] = 0; /* Number of registers to be read - 1 */ | |
| 404 | + priv->cmd_buffer[2] = 0; | |
| 405 | + | |
| 406 | + ret = spi_sync_transfer(priv->spi, ®_read_xfer, 1); | |
| 407 | + if (ret) | |
| 408 | + return ret; | |
| 409 | + | |
| 410 | + *val = priv->cmd_buffer[2]; | |
| 411 | + | |
| 412 | + return 0; | |
| 413 | +} | |
| 414 | + | |
| 415 | +static int ads1298_reg_access(struct iio_dev *indio_dev, unsigned int reg, | |
| 416 | + unsigned int writeval, unsigned int *readval) | |
| 417 | +{ | |
| 418 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 419 | + | |
| 420 | + if (readval) | |
| 421 | + return regmap_read(priv->regmap, reg, readval); | |
| 422 | + | |
| 423 | + return regmap_write(priv->regmap, reg, writeval); | |
| 424 | +} | |
| 425 | + | |
| 426 | +static void ads1298_rdata_unmark_busy(struct ads1298_private *priv) | |
| 427 | +{ | |
| 428 | + /* Notify we're no longer waiting for the SPI transfer to complete */ | |
| 429 | + guard(spinlock_irqsave)(&priv->irq_busy_lock); | |
| 430 | + priv->rdata_xfer_busy = 0; | |
| 431 | +} | |
| 432 | + | |
| 433 | +static int ads1298_update_scan_mode(struct iio_dev *indio_dev, | |
| 434 | + const unsigned long *scan_mask) | |
| 435 | +{ | |
| 436 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 437 | + unsigned int val; | |
| 438 | + int ret; | |
| 439 | + int i; | |
| 440 | + | |
| 441 | + /* Make the interrupt routines start with a clean slate */ | |
| 442 | + ads1298_rdata_unmark_busy(priv); | |
| 443 | + | |
| 444 | + /* Configure power-down bits to match scan mask */ | |
| 445 | + for (i = 0; i < indio_dev->num_channels; i++) { | |
| 446 | + val = test_bit(i, scan_mask) ? 0 : ADS1298_MASK_CH_PD; | |
| 447 | + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CHnSET(i), | |
| 448 | + ADS1298_MASK_CH_PD, val); | |
| 449 | + if (ret) | |
| 450 | + return ret; | |
| 451 | + } | |
| 452 | + | |
| 453 | + return 0; | |
| 454 | +} | |
| 455 | + | |
| 456 | +static const struct iio_info ads1298_info = { | |
| 457 | + .read_raw = &ads1298_read_raw, | |
| 458 | + .write_raw = &ads1298_write_raw, | |
| 459 | + .update_scan_mode = &ads1298_update_scan_mode, | |
| 460 | + .debugfs_reg_access = &ads1298_reg_access, | |
| 461 | +}; | |
| 462 | + | |
| 463 | +static void ads1298_rdata_release_busy_or_restart(struct ads1298_private *priv) | |
| 464 | +{ | |
| 465 | + guard(spinlock_irqsave)(&priv->irq_busy_lock); | |
| 466 | + | |
| 467 | + if (priv->rdata_xfer_busy > 1) { | |
| 468 | + /* | |
| 469 | + * DRDY interrupt occurred before SPI completion. Start a new | |
| 470 | + * SPI transaction now to retrieve the data that wasn't latched | |
| 471 | + * into the ADS1298 chip's transfer buffer yet. | |
| 472 | + */ | |
| 473 | + spi_async(priv->spi, &priv->rdata_msg); | |
| 474 | + /* | |
| 475 | + * If more than one DRDY took place, there was an overrun. Since | |
| 476 | + * the sample is already lost, reset the counter to 1 so that | |
| 477 | + * we will wait for a DRDY interrupt after this SPI transaction. | |
| 478 | + */ | |
| 479 | + priv->rdata_xfer_busy = 1; | |
| 480 | + } else { | |
| 481 | + /* No pending data, wait for DRDY */ | |
| 482 | + priv->rdata_xfer_busy = 0; | |
| 483 | + } | |
| 484 | +} | |
| 485 | + | |
| 486 | +/* Called from SPI completion interrupt handler */ | |
| 487 | +static void ads1298_rdata_complete(void *context) | |
| 488 | +{ | |
| 489 | + struct iio_dev *indio_dev = context; | |
| 490 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 491 | + int scan_index; | |
| 492 | + u32 *bounce = priv->bounce_buffer; | |
| 493 | + | |
| 494 | + if (!iio_buffer_enabled(indio_dev)) { | |
| 495 | + /* | |
| 496 | + * for a single transfer mode we're kept in direct_mode until | |
| 497 | + * completion, avoiding a race with buffered IO. | |
| 498 | + */ | |
| 499 | + ads1298_rdata_unmark_busy(priv); | |
| 500 | + complete(&priv->completion); | |
| 501 | + return; | |
| 502 | + } | |
| 503 | + | |
| 504 | + /* Demux the channel data into our bounce buffer */ | |
| 505 | + for_each_set_bit(scan_index, indio_dev->active_scan_mask, | |
| 506 | + indio_dev->masklength) { | |
| 507 | + const struct iio_chan_spec *scan_chan = | |
| 508 | + &indio_dev->channels[scan_index]; | |
| 509 | + const u8 *data = priv->rx_buffer + scan_chan->address; | |
| 510 | + | |
| 511 | + *bounce++ = get_unaligned_be24(data); | |
| 512 | + } | |
| 513 | + | |
| 514 | + /* rx_buffer can be overwritten from this point on */ | |
| 515 | + ads1298_rdata_release_busy_or_restart(priv); | |
| 516 | + | |
| 517 | + iio_push_to_buffers(indio_dev, priv->bounce_buffer); | |
| 518 | +} | |
| 519 | + | |
| 520 | +static irqreturn_t ads1298_interrupt(int irq, void *dev_id) | |
| 521 | +{ | |
| 522 | + struct iio_dev *indio_dev = dev_id; | |
| 523 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 524 | + unsigned int wasbusy; | |
| 525 | + | |
| 526 | + guard(spinlock_irqsave)(&priv->irq_busy_lock); | |
| 527 | + | |
| 528 | + wasbusy = priv->rdata_xfer_busy++; | |
| 529 | + /* When no SPI transfer in transit, start one now */ | |
| 530 | + if (!wasbusy) | |
| 531 | + spi_async(priv->spi, &priv->rdata_msg); | |
| 532 | + | |
| 533 | + return IRQ_HANDLED; | |
| 534 | +}; | |
| 535 | + | |
| 536 | +static int ads1298_buffer_postenable(struct iio_dev *indio_dev) | |
| 537 | +{ | |
| 538 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 539 | + int ret; | |
| 540 | + | |
| 541 | + /* Disable single-shot mode */ | |
| 542 | + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG4, | |
| 543 | + ADS1298_MASK_CONFIG4_SINGLE_SHOT, 0); | |
| 544 | + if (ret) | |
| 545 | + return ret; | |
| 546 | + | |
| 547 | + return ads1298_write_cmd(priv, ADS1298_CMD_START); | |
| 548 | +} | |
| 549 | + | |
| 550 | +static int ads1298_buffer_predisable(struct iio_dev *indio_dev) | |
| 551 | +{ | |
| 552 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 553 | + | |
| 554 | + return ads1298_write_cmd(priv, ADS1298_CMD_STOP); | |
| 555 | +} | |
| 556 | + | |
| 557 | +static const struct iio_buffer_setup_ops ads1298_setup_ops = { | |
| 558 | + .postenable = &ads1298_buffer_postenable, | |
| 559 | + .predisable = &ads1298_buffer_predisable, | |
| 560 | +}; | |
| 561 | + | |
| 562 | +static void ads1298_reg_disable(void *reg) | |
| 563 | +{ | |
| 564 | + regulator_disable(reg); | |
| 565 | +} | |
| 566 | + | |
| 567 | +static const struct regmap_range ads1298_regmap_volatile_range[] = { | |
| 568 | + regmap_reg_range(ADS1298_REG_LOFF_STATP, ADS1298_REG_LOFF_STATN), | |
| 569 | +}; | |
| 570 | + | |
| 571 | +static const struct regmap_access_table ads1298_regmap_volatile = { | |
| 572 | + .yes_ranges = ads1298_regmap_volatile_range, | |
| 573 | + .n_yes_ranges = ARRAY_SIZE(ads1298_regmap_volatile_range), | |
| 574 | +}; | |
| 575 | + | |
| 576 | +static const struct regmap_config ads1298_regmap_config = { | |
| 577 | + .reg_bits = 8, | |
| 578 | + .val_bits = 8, | |
| 579 | + .reg_read = ads1298_reg_read, | |
| 580 | + .reg_write = ads1298_reg_write, | |
| 581 | + .max_register = ADS1298_REG_WCT2, | |
| 582 | + .volatile_table = &ads1298_regmap_volatile, | |
| 583 | + .cache_type = REGCACHE_MAPLE, | |
| 584 | +}; | |
| 585 | + | |
| 586 | +static int ads1298_init(struct iio_dev *indio_dev) | |
| 587 | +{ | |
| 588 | + struct ads1298_private *priv = iio_priv(indio_dev); | |
| 589 | + struct device *dev = &priv->spi->dev; | |
| 590 | + const char *suffix; | |
| 591 | + unsigned int val; | |
| 592 | + int ret; | |
| 593 | + | |
| 594 | + /* Device initializes into RDATAC mode, which we don't want */ | |
| 595 | + ret = ads1298_write_cmd(priv, ADS1298_CMD_SDATAC); | |
| 596 | + if (ret) | |
| 597 | + return ret; | |
| 598 | + | |
| 599 | + ret = regmap_read(priv->regmap, ADS1298_REG_ID, &val); | |
| 600 | + if (ret) | |
| 601 | + return ret; | |
| 602 | + | |
| 603 | + /* Fill in name and channel count based on what the chip told us */ | |
| 604 | + indio_dev->num_channels = 4 + 2 * (val & ADS1298_MASK_ID_CHANNELS); | |
| 605 | + switch (val & ADS1298_MASK_ID_FAMILY) { | |
| 606 | + case ADS1298_ID_FAMILY_ADS129X: | |
| 607 | + suffix = ""; | |
| 608 | + break; | |
| 609 | + case ADS1298_ID_FAMILY_ADS129XR: | |
| 610 | + suffix = "r"; | |
| 611 | + break; | |
| 612 | + default: | |
| 613 | + return dev_err_probe(dev, -ENODEV, "Unknown ID: 0x%x\n", val); | |
| 614 | + } | |
| 615 | + indio_dev->name = devm_kasprintf(dev, GFP_KERNEL, "ads129%u%s", | |
| 616 | + indio_dev->num_channels, suffix); | |
| 617 | + | |
| 618 | + /* Enable internal test signal, double amplitude, double frequency */ | |
| 619 | + ret = regmap_write(priv->regmap, ADS1298_REG_CONFIG2, | |
| 620 | + ADS1298_MASK_CONFIG2_RESERVED | | |
| 621 | + ADS1298_MASK_CONFIG2_INT_TEST | | |
| 622 | + ADS1298_MASK_CONFIG2_TEST_AMP | | |
| 623 | + ADS1298_MASK_CONFIG2_TEST_FREQ_FAST); | |
| 624 | + if (ret) | |
| 625 | + return ret; | |
| 626 | + | |
| 627 | + val = ADS1298_MASK_CONFIG3_RESERVED; /* Must write 1 always */ | |
| 628 | + if (!priv->reg_vref) { | |
| 629 | + /* Enable internal reference */ | |
| 630 | + val |= ADS1298_MASK_CONFIG3_PWR_REFBUF; | |
| 631 | + /* Use 4V VREF when power supply is at least 4.4V */ | |
| 632 | + if (regulator_get_voltage(priv->reg_avdd) >= 4400000) | |
| 633 | + val |= ADS1298_MASK_CONFIG3_VREF_4V; | |
| 634 | + } | |
| 635 | + return regmap_write(priv->regmap, ADS1298_REG_CONFIG3, val); | |
| 636 | +} | |
| 637 | + | |
| 638 | +static int ads1298_probe(struct spi_device *spi) | |
| 639 | +{ | |
| 640 | + struct ads1298_private *priv; | |
| 641 | + struct iio_dev *indio_dev; | |
| 642 | + struct device *dev = &spi->dev; | |
| 643 | + struct gpio_desc *reset_gpio; | |
| 644 | + int ret; | |
| 645 | + | |
| 646 | + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); | |
| 647 | + if (!indio_dev) | |
| 648 | + return -ENOMEM; | |
| 649 | + | |
| 650 | + priv = iio_priv(indio_dev); | |
| 651 | + | |
| 652 | + /* Reset to be asserted before enabling clock and power */ | |
| 653 | + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); | |
| 654 | + if (IS_ERR(reset_gpio)) | |
| 655 | + return dev_err_probe(dev, PTR_ERR(reset_gpio), | |
| 656 | + "Cannot get reset GPIO\n"); | |
| 657 | + | |
| 658 | + /* VREF can be supplied externally, otherwise use internal reference */ | |
| 659 | + priv->reg_vref = devm_regulator_get_optional(dev, "vref"); | |
| 660 | + if (IS_ERR(priv->reg_vref)) { | |
| 661 | + if (PTR_ERR(priv->reg_vref) != -ENODEV) | |
| 662 | + return dev_err_probe(dev, PTR_ERR(priv->reg_vref), | |
| 663 | + "Failed to get vref regulator\n"); | |
| 664 | + | |
| 665 | + priv->reg_vref = NULL; | |
| 666 | + } else { | |
| 667 | + ret = regulator_enable(priv->reg_vref); | |
| 668 | + if (ret) | |
| 669 | + return ret; | |
| 670 | + | |
| 671 | + ret = devm_add_action_or_reset(dev, ads1298_reg_disable, priv->reg_vref); | |
| 672 | + if (ret) | |
| 673 | + return ret; | |
| 674 | + } | |
| 675 | + | |
| 676 | + priv->clk = devm_clk_get_optional_enabled(dev, "clk"); | |
| 677 | + if (IS_ERR(priv->clk)) | |
| 678 | + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clk\n"); | |
| 679 | + | |
| 680 | + priv->reg_avdd = devm_regulator_get(dev, "avdd"); | |
| 681 | + if (IS_ERR(priv->reg_avdd)) | |
| 682 | + return dev_err_probe(dev, PTR_ERR(priv->reg_avdd), | |
| 683 | + "Failed to get avdd regulator\n"); | |
| 684 | + | |
| 685 | + ret = regulator_enable(priv->reg_avdd); | |
| 686 | + if (ret) | |
| 687 | + return dev_err_probe(dev, ret, "Failed to enable avdd regulator\n"); | |
| 688 | + | |
| 689 | + ret = devm_add_action_or_reset(dev, ads1298_reg_disable, priv->reg_avdd); | |
| 690 | + if (ret) | |
| 691 | + return ret; | |
| 692 | + | |
| 693 | + priv->spi = spi; | |
| 694 | + init_completion(&priv->completion); | |
| 695 | + spin_lock_init(&priv->irq_busy_lock); | |
| 696 | + priv->regmap = devm_regmap_init(dev, NULL, priv, &ads1298_regmap_config); | |
| 697 | + if (IS_ERR(priv->regmap)) | |
| 698 | + return PTR_ERR(priv->regmap); | |
| 699 | + | |
| 700 | + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; | |
| 701 | + indio_dev->channels = ads1298_channels; | |
| 702 | + indio_dev->info = &ads1298_info; | |
| 703 | + | |
| 704 | + if (reset_gpio) { | |
| 705 | + /* | |
| 706 | + * Deassert reset now that clock and power are active. | |
| 707 | + * Minimum reset pulsewidth is 2 clock cycles. | |
| 708 | + */ | |
| 709 | + fsleep(ADS1298_CLOCKS_TO_USECS(2)); | |
| 710 | + gpiod_set_value_cansleep(reset_gpio, 0); | |
| 711 | + } else { | |
| 712 | + ret = ads1298_write_cmd(priv, ADS1298_CMD_RESET); | |
| 713 | + if (ret) | |
| 714 | + return dev_err_probe(dev, ret, "RESET failed\n"); | |
| 715 | + } | |
| 716 | + /* Wait 18 clock cycles for reset command to complete */ | |
| 717 | + fsleep(ADS1298_CLOCKS_TO_USECS(18)); | |
| 718 | + | |
| 719 | + ret = ads1298_init(indio_dev); | |
| 720 | + if (ret) | |
| 721 | + return dev_err_probe(dev, ret, "Init failed\n"); | |
| 722 | + | |
| 723 | + priv->tx_buffer[0] = ADS1298_CMD_RDATA; | |
| 724 | + priv->rdata_xfer.tx_buf = priv->tx_buffer; | |
| 725 | + priv->rdata_xfer.rx_buf = priv->rx_buffer; | |
| 726 | + priv->rdata_xfer.len = ADS1298_SPI_RDATA_BUFFER_SIZE(indio_dev->num_channels); | |
| 727 | + /* Must keep CS low for 4 clocks */ | |
| 728 | + priv->rdata_xfer.delay.value = 2; | |
| 729 | + priv->rdata_xfer.delay.unit = SPI_DELAY_UNIT_USECS; | |
| 730 | + spi_message_init_with_transfers(&priv->rdata_msg, &priv->rdata_xfer, 1); | |
| 731 | + priv->rdata_msg.complete = &ads1298_rdata_complete; | |
| 732 | + priv->rdata_msg.context = indio_dev; | |
| 733 | + | |
| 734 | + ret = devm_request_irq(dev, spi->irq, &ads1298_interrupt, | |
| 735 | + IRQF_TRIGGER_FALLING, indio_dev->name, | |
| 736 | + indio_dev); | |
| 737 | + if (ret) | |
| 738 | + return ret; | |
| 739 | + | |
| 740 | + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &ads1298_setup_ops); | |
| 741 | + if (ret) | |
| 742 | + return ret; | |
| 743 | + | |
| 744 | + return devm_iio_device_register(dev, indio_dev); | |
| 745 | +} | |
| 746 | + | |
| 747 | +static const struct spi_device_id ads1298_id[] = { | |
| 748 | + { "ads1298" }, | |
| 749 | + { } | |
| 750 | +}; | |
| 751 | +MODULE_DEVICE_TABLE(spi, ads1298_id); | |
| 752 | + | |
| 753 | +static const struct of_device_id ads1298_of_table[] = { | |
| 754 | + { .compatible = "ti,ads1298" }, | |
| 755 | + { } | |
| 756 | +}; | |
| 757 | +MODULE_DEVICE_TABLE(of, ads1298_of_table); | |
| 758 | + | |
| 759 | +static struct spi_driver ads1298_driver = { | |
| 760 | + .driver = { | |
| 761 | + .name = "ads1298", | |
| 762 | + .of_match_table = ads1298_of_table, | |
| 763 | + }, | |
| 764 | + .probe = ads1298_probe, | |
| 765 | + .id_table = ads1298_id, | |
| 766 | +}; | |
| 767 | +module_spi_driver(ads1298_driver); | |
| 768 | + | |
| 769 | +MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); | |
| 770 | +MODULE_DESCRIPTION("TI ADS1298 ADC"); | |
| 771 | +MODULE_LICENSE("GPL"); | |
| @@ -11,7 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <linux/regulator/consumer.h> |
| 12 | 12 | #include <linux/err.h> |
| 13 | 13 | #include <linux/module.h> |
| 14 | −#include <linux/of.h> | |
| 14 | +#include <linux/mod_devicetable.h> | |
| 15 | 15 | |
| 16 | 16 | #include <linux/iio/iio.h> |
| 17 | 17 | #include <linux/iio/buffer.h> |
| @@ -2,9 +2,10 @@ · 1 unmodified line | ||
| 2 | 2 | /* |
| 3 | 3 | * HMC425A and similar Gain Amplifiers |
| 4 | 4 | * |
| 5 | − * Copyright 2020 Analog Devices Inc. | |
| 5 | + * Copyright 2020, 2024 Analog Devices Inc. | |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | +#include <linux/bits.h> | |
| 8 | 9 | #include <linux/bitops.h> |
| 9 | 10 | #include <linux/device.h> |
| 10 | 11 | #include <linux/err.h> |
| @@ -12,6 +13,7 @@ · 1 unmodified line | ||
| 12 | 13 | #include <linux/iio/iio.h> |
| 13 | 14 | #include <linux/iio/sysfs.h> |
| 14 | 15 | #include <linux/kernel.h> |
| 16 | +#include <linux/math.h> | |
| 15 | 17 | #include <linux/mod_devicetable.h> |
| 16 | 18 | #include <linux/module.h> |
| 17 | 19 | #include <linux/platform_device.h> |
| @@ -20,10 +22,24 @@ · 2 unmodified lines | ||
| 20 | 22 | #include <linux/regulator/consumer.h> |
| 21 | 23 | #include <linux/sysfs.h> |
| 22 | 24 | |
| 25 | +/* | |
| 26 | + * The LTC6373 amplifier supports configuring gain using GPIO's with the following | |
| 27 | + * values (OUTPUT_V / INPUT_V): 0(shutdown), 0.25, 0.5, 1, 2, 4, 8, 16 | |
| 28 | + * | |
| 29 | + * Except for the shutdown value, all can be converted to dB using 20 * log10(x) | |
| 30 | + * From here, it is observed that all values are multiples of the '2' gain setting, | |
| 31 | + * with the correspondent of 6.020dB. | |
| 32 | + */ | |
| 33 | +#define LTC6373_CONVERSION_CONSTANT 6020 | |
| 34 | +#define LTC6373_MIN_GAIN_CODE 0x6 | |
| 35 | +#define LTC6373_CONVERSION_MASK GENMASK(2, 0) | |
| 36 | +#define LTC6373_SHUTDOWN GENMASK(2, 0) | |
| 37 | + | |
| 23 | 38 | enum hmc425a_type { |
| 24 | 39 | ID_HMC425A, |
| 25 | 40 | ID_HMC540S, |
| 26 | − ID_ADRF5740 | |
| 41 | + ID_ADRF5740, | |
| 42 | + ID_LTC6373, | |
| 27 | 43 | }; |
| 28 | 44 | |
| 29 | 45 | struct hmc425a_chip_info { |
| @@ -34,16 +50,110 @@ struct hmc425a_chip_info { · 4 unmodified lines | ||
| 34 | 50 | int gain_min; |
| 35 | 51 | int gain_max; |
| 36 | 52 | int default_gain; |
| 53 | + int powerdown_val; | |
| 54 | + bool has_powerdown; | |
| 55 | + | |
| 56 | + int (*gain_dB_to_code)(int gain, int *code); | |
| 57 | + int (*code_to_gain_dB)(int code, int *val, int *val2); | |
| 37 | 58 | }; |
| 38 | 59 | |
| 39 | 60 | struct hmc425a_state { |
| 40 | 61 | struct mutex lock; /* protect sensor state */ |
| 41 | − struct hmc425a_chip_info *chip_info; | |
| 62 | + const struct hmc425a_chip_info *chip_info; | |
| 42 | 63 | struct gpio_descs *gpios; |
| 43 | − enum hmc425a_type type; | |
| 44 | 64 | u32 gain; |
| 65 | + bool powerdown; | |
| 45 | 66 | }; |
| 46 | 67 | |
| 68 | +static int gain_dB_to_code(struct hmc425a_state *st, int val, int val2, int *code) | |
| 69 | +{ | |
| 70 | + const struct hmc425a_chip_info *inf = st->chip_info; | |
| 71 | + int gain; | |
| 72 | + | |
| 73 | + if (val < 0) | |
| 74 | + gain = (val * 1000) - (val2 / 1000); | |
| 75 | + else | |
| 76 | + gain = (val * 1000) + (val2 / 1000); | |
| 77 | + | |
| 78 | + if (gain > inf->gain_max || gain < inf->gain_min) | |
| 79 | + return -EINVAL; | |
| 80 | + if (st->powerdown) | |
| 81 | + return -EPERM; | |
| 82 | + | |
| 83 | + return st->chip_info->gain_dB_to_code(gain, code); | |
| 84 | +} | |
| 85 | + | |
| 86 | +static int hmc425a_gain_dB_to_code(int gain, int *code) | |
| 87 | +{ | |
| 88 | + *code = ~((abs(gain) / 500) & 0x3F); | |
| 89 | + return 0; | |
| 90 | +} | |
| 91 | + | |
| 92 | +static int hmc540s_gain_dB_to_code(int gain, int *code) | |
| 93 | +{ | |
| 94 | + *code = ~((abs(gain) / 1000) & 0xF); | |
| 95 | + return 0; | |
| 96 | +} | |
| 97 | + | |
| 98 | +static int adrf5740_gain_dB_to_code(int gain, int *code) | |
| 99 | +{ | |
| 100 | + int temp = (abs(gain) / 2000) & 0xF; | |
| 101 | + | |
| 102 | + /* Bit [0-3]: 2dB 4dB 8dB 8dB */ | |
| 103 | + *code = temp & BIT(3) ? temp | BIT(2) : temp; | |
| 104 | + return 0; | |
| 105 | +} | |
| 106 | + | |
| 107 | +static int ltc6373_gain_dB_to_code(int gain, int *code) | |
| 108 | +{ | |
| 109 | + *code = ~(DIV_ROUND_CLOSEST(gain, LTC6373_CONVERSION_CONSTANT) + 3) | |
| 110 | + & LTC6373_CONVERSION_MASK; | |
| 111 | + return 0; | |
| 112 | +} | |
| 113 | + | |
| 114 | +static int code_to_gain_dB(struct hmc425a_state *st, int *val, int *val2) | |
| 115 | +{ | |
| 116 | + if (st->powerdown) | |
| 117 | + return -EPERM; | |
| 118 | + return st->chip_info->code_to_gain_dB(st->gain, val, val2); | |
| 119 | +} | |
| 120 | + | |
| 121 | +static int hmc425a_code_to_gain_dB(int code, int *val, int *val2) | |
| 122 | +{ | |
| 123 | + *val = (~code * -500) / 1000; | |
| 124 | + *val2 = ((~code * -500) % 1000) * 1000; | |
| 125 | + return 0; | |
| 126 | +} | |
| 127 | + | |
| 128 | +static int hmc540s_code_to_gain_dB(int code, int *val, int *val2) | |
| 129 | +{ | |
| 130 | + *val = (~code * -1000) / 1000; | |
| 131 | + *val2 = ((~code * -1000) % 1000) * 1000; | |
| 132 | + return 0; | |
| 133 | +} | |
| 134 | + | |
| 135 | +static int adrf5740_code_to_gain_dB(int code, int *val, int *val2) | |
| 136 | +{ | |
| 137 | + /* | |
| 138 | + * Bit [0-3]: 2dB 4dB 8dB 8dB | |
| 139 | + * When BIT(3) is set, unset BIT(2) and use 3 as double the place value | |
| 140 | + */ | |
| 141 | + code = code & BIT(3) ? code & ~BIT(2) : code; | |
| 142 | + *val = (code * -2000) / 1000; | |
| 143 | + *val2 = ((code * -2000) % 1000) * 1000; | |
| 144 | + return 0; | |
| 145 | +} | |
| 146 | + | |
| 147 | +static int ltc6373_code_to_gain_dB(int code, int *val, int *val2) | |
| 148 | +{ | |
| 149 | + int gain = ((~code & LTC6373_CONVERSION_MASK) - 3) * | |
| 150 | + LTC6373_CONVERSION_CONSTANT; | |
| 151 | + | |
| 152 | + *val = gain / 1000; | |
| 153 | + *val2 = (gain % 1000) * 1000; | |
| 154 | + return 0; | |
| 155 | +} | |
| 156 | + | |
| 47 | 157 | static int hmc425a_write(struct iio_dev *indio_dev, u32 value) |
| 48 | 158 | { |
| 49 | 159 | struct hmc425a_state *st = iio_priv(indio_dev); |
| @@ -61,30 +171,14 @@ static int hmc425a_read_raw(struct iio_dev *indio_dev, · 11 unmodified lines | ||
| 61 | 171 | int *val2, long m) |
| 62 | 172 | { |
| 63 | 173 | struct hmc425a_state *st = iio_priv(indio_dev); |
| 64 | − int code, gain = 0; | |
| 65 | 174 | int ret; |
| 66 | 175 | |
| 67 | 176 | mutex_lock(&st->lock); |
| 68 | 177 | switch (m) { |
| 69 | 178 | case IIO_CHAN_INFO_HARDWAREGAIN: |
| 70 | − code = st->gain; | |
| 71 | − | |
| 72 | − switch (st->type) { | |
| 73 | − case ID_HMC425A: | |
| 74 | − gain = ~code * -500; | |
| 179 | + ret = code_to_gain_dB(st, val, val2); | |
| 180 | + if (ret) | |
| 75 | 181 | break; |
| 76 | − case ID_HMC540S: | |
| 77 | − gain = ~code * -1000; | |
| 78 | − break; | |
| 79 | − case ID_ADRF5740: | |
| 80 | − code = code & BIT(3) ? code & ~BIT(2) : code; | |
| 81 | − gain = code * -2000; | |
| 82 | − break; | |
| 83 | − } | |
| 84 | − | |
| 85 | − *val = gain / 1000; | |
| 86 | − *val2 = (gain % 1000) * 1000; | |
| 87 | − | |
| 88 | 182 | ret = IIO_VAL_INT_PLUS_MICRO_DB; |
| 89 | 183 | break; |
| 90 | 184 | default: |
| @@ -100,34 +194,14 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev, · 9 unmodified lines | ||
| 100 | 194 | int val2, long mask) |
| 101 | 195 | { |
| 102 | 196 | struct hmc425a_state *st = iio_priv(indio_dev); |
| 103 | − struct hmc425a_chip_info *inf = st->chip_info; | |
| 104 | − int code = 0, gain; | |
| 105 | − int ret; | |
| 106 | − | |
| 107 | − if (val < 0) | |
| 108 | − gain = (val * 1000) - (val2 / 1000); | |
| 109 | − else | |
| 110 | − gain = (val * 1000) + (val2 / 1000); | |
| 111 | − | |
| 112 | − if (gain > inf->gain_max || gain < inf->gain_min) | |
| 113 | − return -EINVAL; | |
| 114 | − | |
| 115 | − switch (st->type) { | |
| 116 | − case ID_HMC425A: | |
| 117 | − code = ~((abs(gain) / 500) & 0x3F); | |
| 118 | − break; | |
| 119 | − case ID_HMC540S: | |
| 120 | − code = ~((abs(gain) / 1000) & 0xF); | |
| 121 | − break; | |
| 122 | − case ID_ADRF5740: | |
| 123 | − code = (abs(gain) / 2000) & 0xF; | |
| 124 | − code = code & BIT(3) ? code | BIT(2) : code; | |
| 125 | − break; | |
| 126 | − } | |
| 197 | + int code = 0, ret; | |
| 127 | 198 | |
| 128 | 199 | mutex_lock(&st->lock); |
| 129 | 200 | switch (mask) { |
| 130 | 201 | case IIO_CHAN_INFO_HARDWAREGAIN: |
| 202 | + ret = gain_dB_to_code(st, val, val2, &code); | |
| 203 | + if (ret) | |
| 204 | + break; | |
| 131 | 205 | st->gain = code; |
| 132 | 206 | |
| 133 | 207 | ret = hmc425a_write(indio_dev, st->gain); |
| @@ -158,6 +232,48 @@ static const struct iio_info hmc425a_info = { · 24 unmodified lines | ||
| 158 | 232 | .write_raw_get_fmt = &hmc425a_write_raw_get_fmt, |
| 159 | 233 | }; |
| 160 | 234 | |
| 235 | +static ssize_t ltc6373_read_powerdown(struct iio_dev *indio_dev, | |
| 236 | + uintptr_t private, | |
| 237 | + const struct iio_chan_spec *chan, | |
| 238 | + char *buf) | |
| 239 | +{ | |
| 240 | + struct hmc425a_state *st = iio_priv(indio_dev); | |
| 241 | + | |
| 242 | + return sysfs_emit(buf, "%d\n", st->powerdown); | |
| 243 | +} | |
| 244 | + | |
| 245 | +static ssize_t ltc6373_write_powerdown(struct iio_dev *indio_dev, | |
| 246 | + uintptr_t private, | |
| 247 | + const struct iio_chan_spec *chan, | |
| 248 | + const char *buf, | |
| 249 | + size_t len) | |
| 250 | +{ | |
| 251 | + struct hmc425a_state *st = iio_priv(indio_dev); | |
| 252 | + bool powerdown; | |
| 253 | + int code, ret; | |
| 254 | + | |
| 255 | + ret = kstrtobool(buf, &powerdown); | |
| 256 | + if (ret) | |
| 257 | + return ret; | |
| 258 | + | |
| 259 | + mutex_lock(&st->lock); | |
| 260 | + st->powerdown = powerdown; | |
| 261 | + code = (powerdown) ? LTC6373_SHUTDOWN : st->gain; | |
| 262 | + hmc425a_write(indio_dev, code); | |
| 263 | + mutex_unlock(&st->lock); | |
| 264 | + return len; | |
| 265 | +} | |
| 266 | + | |
| 267 | +static const struct iio_chan_spec_ext_info ltc6373_ext_info[] = { | |
| 268 | + { | |
| 269 | + .name = "powerdown", | |
| 270 | + .read = ltc6373_read_powerdown, | |
| 271 | + .write = ltc6373_write_powerdown, | |
| 272 | + .shared = IIO_SEPARATE, | |
| 273 | + }, | |
| 274 | + {} | |
| 275 | +}; | |
| 276 | + | |
| 161 | 277 | #define HMC425A_CHAN(_channel) \ |
| 162 | 278 | { \ |
| 163 | 279 | .type = IIO_VOLTAGE, \ |
| @@ -167,20 +283,25 @@ static const struct iio_info hmc425a_info = { · 3 unmodified lines | ||
| 167 | 283 | .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ |
| 168 | 284 | } |
| 169 | 285 | |
| 286 | +#define LTC6373_CHAN(_channel) \ | |
| 287 | +{ \ | |
| 288 | + .type = IIO_VOLTAGE, \ | |
| 289 | + .output = 1, \ | |
| 290 | + .indexed = 1, \ | |
| 291 | + .channel = _channel, \ | |
| 292 | + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ | |
| 293 | + .ext_info = ltc6373_ext_info, \ | |
| 294 | +} | |
| 295 | + | |
| 170 | 296 | static const struct iio_chan_spec hmc425a_channels[] = { |
| 171 | 297 | HMC425A_CHAN(0), |
| 172 | 298 | }; |
| 173 | 299 | |
| 174 | −/* Match table for of_platform binding */ | |
| 175 | −static const struct of_device_id hmc425a_of_match[] = { | |
| 176 | − { .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A }, | |
| 177 | − { .compatible = "adi,hmc540s", .data = (void *)ID_HMC540S }, | |
| 178 | − { .compatible = "adi,adrf5740", .data = (void *)ID_ADRF5740 }, | |
| 179 | − {}, | |
| 300 | +static const struct iio_chan_spec ltc6373_channels[] = { | |
| 301 | + LTC6373_CHAN(0), | |
| 180 | 302 | }; |
| 181 | −MODULE_DEVICE_TABLE(of, hmc425a_of_match); | |
| 182 | 303 | |
| 183 | −static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { | |
| 304 | +static const struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { | |
| 184 | 305 | [ID_HMC425A] = { |
| 185 | 306 | .name = "hmc425a", |
| 186 | 307 | .channels = hmc425a_channels, |
| @@ -189,6 +310,8 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { · 2 unmodified lines | ||
| 189 | 310 | .gain_min = -31500, |
| 190 | 311 | .gain_max = 0, |
| 191 | 312 | .default_gain = -0x40, /* set default gain -31.5db*/ |
| 313 | + .gain_dB_to_code = hmc425a_gain_dB_to_code, | |
| 314 | + .code_to_gain_dB = hmc425a_code_to_gain_dB, | |
| 192 | 315 | }, |
| 193 | 316 | [ID_HMC540S] = { |
| 194 | 317 | .name = "hmc540s", |
| @@ -198,6 +321,8 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { · 3 unmodified lines | ||
| 198 | 321 | .gain_min = -15000, |
| 199 | 322 | .gain_max = 0, |
| 200 | 323 | .default_gain = -0x10, /* set default gain -15.0db*/ |
| 324 | + .gain_dB_to_code = hmc540s_gain_dB_to_code, | |
| 325 | + .code_to_gain_dB = hmc540s_code_to_gain_dB, | |
| 201 | 326 | }, |
| 202 | 327 | [ID_ADRF5740] = { |
| 203 | 328 | .name = "adrf5740", |
| @@ -207,6 +332,21 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { · 3 unmodified lines | ||
| 207 | 332 | .gain_min = -22000, |
| 208 | 333 | .gain_max = 0, |
| 209 | 334 | .default_gain = 0xF, /* set default gain -22.0db*/ |
| 335 | + .gain_dB_to_code = adrf5740_gain_dB_to_code, | |
| 336 | + .code_to_gain_dB = adrf5740_code_to_gain_dB, | |
| 337 | + }, | |
| 338 | + [ID_LTC6373] = { | |
| 339 | + .name = "ltc6373", | |
| 340 | + .channels = ltc6373_channels, | |
| 341 | + .num_channels = ARRAY_SIZE(ltc6373_channels), | |
| 342 | + .num_gpios = 3, | |
| 343 | + .gain_min = -12041, /* gain setting x0.25*/ | |
| 344 | + .gain_max = 24082, /* gain setting x16 */ | |
| 345 | + .default_gain = LTC6373_MIN_GAIN_CODE, | |
| 346 | + .powerdown_val = LTC6373_SHUTDOWN, | |
| 347 | + .has_powerdown = true, | |
| 348 | + .gain_dB_to_code = ltc6373_gain_dB_to_code, | |
| 349 | + .code_to_gain_dB = ltc6373_code_to_gain_dB, | |
| 210 | 350 | }, |
| 211 | 351 | }; |
| 212 | 352 | |
| @@ -221,9 +361,8 @@ static int hmc425a_probe(struct platform_device *pdev) · 8 unmodified lines | ||
| 221 | 361 | return -ENOMEM; |
| 222 | 362 | |
| 223 | 363 | st = iio_priv(indio_dev); |
| 224 | − st->type = (uintptr_t)device_get_match_data(&pdev->dev); | |
| 225 | 364 | |
| 226 | − st->chip_info = &hmc425a_chip_info_tbl[st->type]; | |
| 365 | + st->chip_info = device_get_match_data(&pdev->dev); | |
| 227 | 366 | indio_dev->num_channels = st->chip_info->num_channels; |
| 228 | 367 | indio_dev->channels = st->chip_info->channels; |
| 229 | 368 | indio_dev->name = st->chip_info->name; |
| @@ -249,12 +388,31 @@ static int hmc425a_probe(struct platform_device *pdev) · 19 unmodified lines | ||
| 249 | 388 | indio_dev->info = &hmc425a_info; |
| 250 | 389 | indio_dev->modes = INDIO_DIRECT_MODE; |
| 251 | 390 | |
| 252 | − /* Set default gain */ | |
| 253 | − hmc425a_write(indio_dev, st->gain); | |
| 391 | + if (st->chip_info->has_powerdown) { | |
| 392 | + st->powerdown = true; | |
| 393 | + hmc425a_write(indio_dev, st->chip_info->powerdown_val); | |
| 394 | + } else { | |
| 395 | + /* Set default gain */ | |
| 396 | + hmc425a_write(indio_dev, st->gain); | |
| 397 | + } | |
| 254 | 398 | |
| 255 | 399 | return devm_iio_device_register(&pdev->dev, indio_dev); |
| 256 | 400 | } |
| 257 | 401 | |
| 402 | +/* Match table for of_platform binding */ | |
| 403 | +static const struct of_device_id hmc425a_of_match[] = { | |
| 404 | + { .compatible = "adi,hmc425a", | |
| 405 | + .data = &hmc425a_chip_info_tbl[ID_HMC425A]}, | |
| 406 | + { .compatible = "adi,hmc540s", | |
| 407 | + .data = &hmc425a_chip_info_tbl[ID_HMC540S]}, | |
| 408 | + { .compatible = "adi,adrf5740", | |
| 409 | + .data = &hmc425a_chip_info_tbl[ID_ADRF5740]}, | |
| 410 | + { .compatible = "adi,ltc6373", | |
| 411 | + .data = &hmc425a_chip_info_tbl[ID_LTC6373]}, | |
| 412 | + {} | |
| 413 | +}; | |
| 414 | +MODULE_DEVICE_TABLE(of, hmc425a_of_match); | |
| 415 | + | |
| 258 | 416 | static struct platform_driver hmc425a_driver = { |
| 259 | 417 | .driver = { |
| 260 | 418 | .name = KBUILD_MODNAME, |
| @@ -159,7 +159,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = { · 158 unmodified lines | ||
| 159 | 159 | * Once done using the buffer iio_dmaengine_buffer_free() should be used to |
| 160 | 160 | * release it. |
| 161 | 161 | */ |
| 162 | −static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, | |
| 162 | +struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, | |
| 163 | 163 | const char *channel) |
| 164 | 164 | { |
| 165 | 165 | struct dmaengine_buffer *dmaengine_buffer; |
| @@ -210,6 +210,7 @@ err_free: · 44 unmodified lines | ||
| 210 | 210 | kfree(dmaengine_buffer); |
| 211 | 211 | return ERR_PTR(ret); |
| 212 | 212 | } |
| 213 | +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER); | |
| 213 | 214 | |
| 214 | 215 | /** |
| 215 | 216 | * iio_dmaengine_buffer_free() - Free dmaengine buffer |
| @@ -217,7 +218,7 @@ err_free: · 1 unmodified line | ||
| 217 | 218 | * |
| 218 | 219 | * Frees a buffer previously allocated with iio_dmaengine_buffer_alloc(). |
| 219 | 220 | */ |
| 220 | −static void iio_dmaengine_buffer_free(struct iio_buffer *buffer) | |
| 221 | +void iio_dmaengine_buffer_free(struct iio_buffer *buffer) | |
| 221 | 222 | { |
| 222 | 223 | struct dmaengine_buffer *dmaengine_buffer = |
| 223 | 224 | iio_buffer_to_dmaengine_buffer(buffer); |
| @@ -227,6 +228,7 @@ static void iio_dmaengine_buffer_free(struct iio_buffer *buffer) · 3 unmodified lines | ||
| 227 | 228 | |
| 228 | 229 | iio_buffer_put(buffer); |
| 229 | 230 | } |
| 231 | +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER); | |
| 230 | 232 | |
| 231 | 233 | static void __devm_iio_dmaengine_buffer_free(void *buffer) |
| 232 | 234 | { |
| @@ -279,8 +281,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev, · 46 unmodified lines | ||
| 279 | 281 | { |
| 280 | 282 | struct iio_buffer *buffer; |
| 281 | 283 | |
| 282 | − buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent, | |
| 283 | − channel); | |
| 284 | + buffer = devm_iio_dmaengine_buffer_alloc(dev, channel); | |
| 284 | 285 | if (IS_ERR(buffer)) |
| 285 | 286 | return PTR_ERR(buffer); |
| 286 | 287 | |
| @@ -288,7 +289,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev, · 1 unmodified line | ||
| 288 | 289 | |
| 289 | 290 | return iio_device_attach_buffer(indio_dev, buffer); |
| 290 | 291 | } |
| 291 | −EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup); | |
| 292 | +EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER); | |
| 292 | 293 | |
| 293 | 294 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
| 294 | 295 | MODULE_DESCRIPTION("DMA buffer for the IIO framework"); |
| @@ -126,7 +126,7 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, · 125 unmodified lines | ||
| 126 | 126 | struct inv_sensors_timestamp_interval *it; |
| 127 | 127 | int64_t delta, interval; |
| 128 | 128 | const uint32_t fifo_mult = fifo_period / ts->chip.clock_period; |
| 129 | − uint32_t period = ts->period; | |
| 129 | + uint32_t period; | |
| 130 | 130 | bool valid = false; |
| 131 | 131 | |
| 132 | 132 | if (fifo_nb == 0) |
| @@ -17,7 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include <linux/module.h> |
| 20 | −#include <linux/of.h> | |
| 20 | +#include <linux/mod_devicetable.h> | |
| 21 | 21 | #include <linux/spi/spi.h> |
| 22 | 22 | |
| 23 | 23 | #include <linux/iio/iio.h> |
| @@ -31,8 +31,6 @@ · 30 unmodified lines | ||
| 31 | 31 | * @regs: irq regs we are faking |
| 32 | 32 | * @lock: protect the evgen state |
| 33 | 33 | * @inuse: mask of which irqs are connected |
| 34 | − * @irq_sim: interrupt simulator | |
| 35 | − * @base: base of irq range | |
| 36 | 34 | * @irq_sim_domain: irq simulator domain |
| 37 | 35 | */ |
| 38 | 36 | struct iio_dummy_eventgen { |
| @@ -283,65 +283,63 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev, · 282 unmodified lines | ||
| 283 | 283 | long mask) |
| 284 | 284 | { |
| 285 | 285 | struct iio_dummy_state *st = iio_priv(indio_dev); |
| 286 | − int ret = -EINVAL; | |
| 287 | 286 | |
| 288 | − mutex_lock(&st->lock); | |
| 289 | 287 | switch (mask) { |
| 290 | 288 | case IIO_CHAN_INFO_RAW: /* magic value - channel value read */ |
| 291 | − switch (chan->type) { | |
| 292 | − case IIO_VOLTAGE: | |
| 293 | − if (chan->output) { | |
| 294 | − /* Set integer part to cached value */ | |
| 295 | − *val = st->dac_val; | |
| 296 | − ret = IIO_VAL_INT; | |
| 297 | − } else if (chan->differential) { | |
| 298 | − if (chan->channel == 1) | |
| 299 | − *val = st->differential_adc_val[0]; | |
| 300 | − else | |
| 301 | − *val = st->differential_adc_val[1]; | |
| 302 | − ret = IIO_VAL_INT; | |
| 303 | − } else { | |
| 304 | − *val = st->single_ended_adc_val; | |
| 305 | − ret = IIO_VAL_INT; | |
| 289 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 290 | + guard(mutex)(&st->lock); | |
| 291 | + switch (chan->type) { | |
| 292 | + case IIO_VOLTAGE: | |
| 293 | + if (chan->output) { | |
| 294 | + /* Set integer part to cached value */ | |
| 295 | + *val = st->dac_val; | |
| 296 | + return IIO_VAL_INT; | |
| 297 | + } else if (chan->differential) { | |
| 298 | + if (chan->channel == 1) | |
| 299 | + *val = st->differential_adc_val[0]; | |
| 300 | + else | |
| 301 | + *val = st->differential_adc_val[1]; | |
| 302 | + return IIO_VAL_INT; | |
| 303 | + } else { | |
| 304 | + *val = st->single_ended_adc_val; | |
| 305 | + return IIO_VAL_INT; | |
| 306 | + } | |
| 307 | + | |
| 308 | + case IIO_ACCEL: | |
| 309 | + *val = st->accel_val; | |
| 310 | + return IIO_VAL_INT; | |
| 311 | + default: | |
| 312 | + return -EINVAL; | |
| 306 | 313 | } |
| 307 | − break; | |
| 308 | − case IIO_ACCEL: | |
| 309 | − *val = st->accel_val; | |
| 310 | − ret = IIO_VAL_INT; | |
| 311 | − break; | |
| 312 | − default: | |
| 313 | − break; | |
| 314 | 314 | } |
| 315 | − break; | |
| 315 | + unreachable(); | |
| 316 | 316 | case IIO_CHAN_INFO_PROCESSED: |
| 317 | − switch (chan->type) { | |
| 318 | − case IIO_STEPS: | |
| 319 | − *val = st->steps; | |
| 320 | − ret = IIO_VAL_INT; | |
| 321 | − break; | |
| 322 | − case IIO_ACTIVITY: | |
| 323 | − switch (chan->channel2) { | |
| 324 | − case IIO_MOD_RUNNING: | |
| 325 | − *val = st->activity_running; | |
| 326 | − ret = IIO_VAL_INT; | |
| 327 | − break; | |
| 328 | − case IIO_MOD_WALKING: | |
| 329 | − *val = st->activity_walking; | |
| 330 | − ret = IIO_VAL_INT; | |
| 331 | − break; | |
| 317 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { | |
| 318 | + guard(mutex)(&st->lock); | |
| 319 | + switch (chan->type) { | |
| 320 | + case IIO_STEPS: | |
| 321 | + *val = st->steps; | |
| 322 | + return IIO_VAL_INT; | |
| 323 | + case IIO_ACTIVITY: | |
| 324 | + switch (chan->channel2) { | |
| 325 | + case IIO_MOD_RUNNING: | |
| 326 | + *val = st->activity_running; | |
| 327 | + return IIO_VAL_INT; | |
| 328 | + case IIO_MOD_WALKING: | |
| 329 | + *val = st->activity_walking; | |
| 330 | + return IIO_VAL_INT; | |
| 331 | + default: | |
| 332 | + return -EINVAL; | |
| 333 | + } | |
| 332 | 334 | default: |
| 333 | − break; | |
| 335 | + return -EINVAL; | |
| 334 | 336 | } |
| 335 | − break; | |
| 336 | − default: | |
| 337 | − break; | |
| 338 | 337 | } |
| 339 | − break; | |
| 338 | + unreachable(); | |
| 340 | 339 | case IIO_CHAN_INFO_OFFSET: |
| 341 | 340 | /* only single ended adc -> 7 */ |
| 342 | 341 | *val = 7; |
| 343 | − ret = IIO_VAL_INT; | |
| 344 | − break; | |
| 342 | + return IIO_VAL_INT; | |
| 345 | 343 | case IIO_CHAN_INFO_SCALE: |
| 346 | 344 | switch (chan->type) { |
| 347 | 345 | case IIO_VOLTAGE: |
| @@ -350,60 +348,57 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev, · 2 unmodified lines | ||
| 350 | 348 | /* only single ended adc -> 0.001333 */ |
| 351 | 349 | *val = 0; |
| 352 | 350 | *val2 = 1333; |
| 353 | − ret = IIO_VAL_INT_PLUS_MICRO; | |
| 354 | − break; | |
| 351 | + return IIO_VAL_INT_PLUS_MICRO; | |
| 355 | 352 | case 1: |
| 356 | 353 | /* all differential adc -> 0.000001344 */ |
| 357 | 354 | *val = 0; |
| 358 | 355 | *val2 = 1344; |
| 359 | − ret = IIO_VAL_INT_PLUS_NANO; | |
| 356 | + return IIO_VAL_INT_PLUS_NANO; | |
| 357 | + default: | |
| 358 | + return -EINVAL; | |
| 360 | 359 | } |
| 361 | − break; | |
| 362 | 360 | default: |
| 363 | − break; | |
| 361 | + return -EINVAL; | |
| 364 | 362 | } |
| 365 | − break; | |
| 366 | − case IIO_CHAN_INFO_CALIBBIAS: | |
| 363 | + case IIO_CHAN_INFO_CALIBBIAS: { | |
| 364 | + guard(mutex)(&st->lock); | |
| 367 | 365 | /* only the acceleration axis - read from cache */ |
| 368 | 366 | *val = st->accel_calibbias; |
| 369 | − ret = IIO_VAL_INT; | |
| 370 | − break; | |
| 371 | − case IIO_CHAN_INFO_CALIBSCALE: | |
| 367 | + return IIO_VAL_INT; | |
| 368 | + } | |
| 369 | + case IIO_CHAN_INFO_CALIBSCALE: { | |
| 370 | + guard(mutex)(&st->lock); | |
| 372 | 371 | *val = st->accel_calibscale->val; |
| 373 | 372 | *val2 = st->accel_calibscale->val2; |
| 374 | − ret = IIO_VAL_INT_PLUS_MICRO; | |
| 375 | − break; | |
| 373 | + return IIO_VAL_INT_PLUS_MICRO; | |
| 374 | + } | |
| 376 | 375 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 377 | 376 | *val = 3; |
| 378 | 377 | *val2 = 33; |
| 379 | − ret = IIO_VAL_INT_PLUS_NANO; | |
| 380 | − break; | |
| 381 | − case IIO_CHAN_INFO_ENABLE: | |
| 378 | + return IIO_VAL_INT_PLUS_NANO; | |
| 379 | + case IIO_CHAN_INFO_ENABLE: { | |
| 380 | + guard(mutex)(&st->lock); | |
| 382 | 381 | switch (chan->type) { |
| 383 | 382 | case IIO_STEPS: |
| 384 | 383 | *val = st->steps_enabled; |
| 385 | − ret = IIO_VAL_INT; | |
| 386 | − break; | |
| 384 | + return IIO_VAL_INT; | |
| 387 | 385 | default: |
| 388 | − break; | |
| 386 | + return -EINVAL; | |
| 389 | 387 | } |
| 390 | − break; | |
| 391 | − case IIO_CHAN_INFO_CALIBHEIGHT: | |
| 388 | + } | |
| 389 | + case IIO_CHAN_INFO_CALIBHEIGHT: { | |
| 390 | + guard(mutex)(&st->lock); | |
| 392 | 391 | switch (chan->type) { |
| 393 | 392 | case IIO_STEPS: |
| 394 | 393 | *val = st->height; |
| 395 | − ret = IIO_VAL_INT; | |
| 396 | − break; | |
| 394 | + return IIO_VAL_INT; | |
| 397 | 395 | default: |
| 398 | − break; | |
| 396 | + return -EINVAL; | |
| 399 | 397 | } |
| 400 | − break; | |
| 401 | − | |
| 398 | + } | |
| 402 | 399 | default: |
| 403 | − break; | |
| 400 | + return -EINVAL; | |
| 404 | 401 | } |
| 405 | − mutex_unlock(&st->lock); | |
| 406 | − return ret; | |
| 407 | 402 | } |
| 408 | 403 | |
| 409 | 404 | /** |
| @@ -426,7 +421,6 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev, · 16 unmodified lines | ||
| 426 | 421 | long mask) |
| 427 | 422 | { |
| 428 | 423 | int i; |
| 429 | − int ret = 0; | |
| 430 | 424 | struct iio_dummy_state *st = iio_priv(indio_dev); |
| 431 | 425 | |
| 432 | 426 | switch (mask) { |
| @@ -436,10 +430,10 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev, · 3 unmodified lines | ||
| 436 | 430 | if (chan->output == 0) |
| 437 | 431 | return -EINVAL; |
| 438 | 432 | |
| 439 | − /* Locking not required as writing single value */ | |
| 440 | − mutex_lock(&st->lock); | |
| 441 | − st->dac_val = val; | |
| 442 | − mutex_unlock(&st->lock); | |
| 433 | + scoped_guard(mutex, &st->lock) { | |
| 434 | + /* Locking not required as writing single value */ | |
| 435 | + st->dac_val = val; | |
| 436 | + } | |
| 443 | 437 | return 0; |
| 444 | 438 | default: |
| 445 | 439 | return -EINVAL; |
| @@ -447,9 +441,9 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev, · 1 unmodified line | ||
| 447 | 441 | case IIO_CHAN_INFO_PROCESSED: |
| 448 | 442 | switch (chan->type) { |
| 449 | 443 | case IIO_STEPS: |
| 450 | − mutex_lock(&st->lock); | |
| 451 | − st->steps = val; | |
| 452 | − mutex_unlock(&st->lock); | |
| 444 | + scoped_guard(mutex, &st->lock) { | |
| 445 | + st->steps = val; | |
| 446 | + } | |
| 453 | 447 | return 0; |
| 454 | 448 | case IIO_ACTIVITY: |
| 455 | 449 | if (val < 0) |
| @@ -470,30 +464,29 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev, · 14 unmodified lines | ||
| 470 | 464 | default: |
| 471 | 465 | return -EINVAL; |
| 472 | 466 | } |
| 473 | − case IIO_CHAN_INFO_CALIBSCALE: | |
| 474 | − mutex_lock(&st->lock); | |
| 467 | + case IIO_CHAN_INFO_CALIBSCALE: { | |
| 468 | + guard(mutex)(&st->lock); | |
| 475 | 469 | /* Compare against table - hard matching here */ |
| 476 | 470 | for (i = 0; i < ARRAY_SIZE(dummy_scales); i++) |
| 477 | 471 | if (val == dummy_scales[i].val && |
| 478 | 472 | val2 == dummy_scales[i].val2) |
| 479 | 473 | break; |
| 480 | 474 | if (i == ARRAY_SIZE(dummy_scales)) |
| 481 | − ret = -EINVAL; | |
| 482 | − else | |
| 483 | − st->accel_calibscale = &dummy_scales[i]; | |
| 484 | − mutex_unlock(&st->lock); | |
| 485 | − return ret; | |
| 475 | + return -EINVAL; | |
| 476 | + st->accel_calibscale = &dummy_scales[i]; | |
| 477 | + return 0; | |
| 478 | + } | |
| 486 | 479 | case IIO_CHAN_INFO_CALIBBIAS: |
| 487 | − mutex_lock(&st->lock); | |
| 488 | − st->accel_calibbias = val; | |
| 489 | − mutex_unlock(&st->lock); | |
| 480 | + scoped_guard(mutex, &st->lock) { | |
| 481 | + st->accel_calibbias = val; | |
| 482 | + } | |
| 490 | 483 | return 0; |
| 491 | 484 | case IIO_CHAN_INFO_ENABLE: |
| 492 | 485 | switch (chan->type) { |
| 493 | 486 | case IIO_STEPS: |
| 494 | − mutex_lock(&st->lock); | |
| 495 | − st->steps_enabled = val; | |
| 496 | − mutex_unlock(&st->lock); | |
| 487 | + scoped_guard(mutex, &st->lock) { | |
| 488 | + st->steps_enabled = val; | |
| 489 | + } | |
| 497 | 490 | return 0; |
| 498 | 491 | default: |
| 499 | 492 | return -EINVAL; |
| @@ -60,6 +60,16 @@ config ADF4377 · 59 unmodified lines | ||
| 60 | 60 | To compile this driver as a module, choose M here: the |
| 61 | 61 | module will be called adf4377. |
| 62 | 62 | |
| 63 | +config ADMFM2000 | |
| 64 | + tristate "Analog Devices ADMFM2000 Dual Microwave Down Converter" | |
| 65 | + depends on GPIOLIB | |
| 66 | + help | |
| 67 | + Say yes here to build support for Analog Devices ADMFM2000 Dual | |
| 68 | + Microwave Down Converter. | |
| 69 | + | |
| 70 | + To compile this driver as a module, choose M here: the | |
| 71 | + module will be called admfm2000. | |
| 72 | + | |
| 63 | 73 | config ADMV1013 |
| 64 | 74 | tristate "Analog Devices ADMV1013 Microwave Upconverter" |
| 65 | 75 | depends on SPI && COMMON_CLK |
| @@ -8,6 +8,7 @@ obj-$(CONFIG_AD9523) += ad9523.o · 7 unmodified lines | ||
| 8 | 8 | obj-$(CONFIG_ADF4350) += adf4350.o |
| 9 | 9 | obj-$(CONFIG_ADF4371) += adf4371.o |
| 10 | 10 | obj-$(CONFIG_ADF4377) += adf4377.o |
| 11 | +obj-$(CONFIG_ADMFM2000) += admfm2000.o | |
| 11 | 12 | obj-$(CONFIG_ADMV1013) += admv1013.o |
| 12 | 13 | obj-$(CONFIG_ADMV1014) += admv1014.o |
| 13 | 14 | obj-$(CONFIG_ADMV4420) += admv4420.o |
| @@ -0,0 +1,282 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * ADMFM2000 Dual Microwave Down Converter | |
| 4 | + * | |
| 5 | + * Copyright 2024 Analog Devices Inc. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#include <linux/device.h> | |
| 9 | +#include <linux/err.h> | |
| 10 | +#include <linux/gpio/consumer.h> | |
| 11 | +#include <linux/iio/iio.h> | |
| 12 | +#include <linux/kernel.h> | |
| 13 | +#include <linux/module.h> | |
| 14 | +#include <linux/mod_devicetable.h> | |
| 15 | +#include <linux/platform_device.h> | |
| 16 | +#include <linux/property.h> | |
| 17 | + | |
| 18 | +#define ADMFM2000_MIXER_MODE 0 | |
| 19 | +#define ADMFM2000_DIRECT_IF_MODE 1 | |
| 20 | +#define ADMFM2000_DSA_GPIOS 5 | |
| 21 | +#define ADMFM2000_MODE_GPIOS 2 | |
| 22 | +#define ADMFM2000_MAX_GAIN 0 | |
| 23 | +#define ADMFM2000_MIN_GAIN -31000 | |
| 24 | +#define ADMFM2000_DEFAULT_GAIN -0x20 | |
| 25 | + | |
| 26 | +struct admfm2000_state { | |
| 27 | + struct mutex lock; /* protect sensor state */ | |
| 28 | + struct gpio_desc *sw1_ch[2]; | |
| 29 | + struct gpio_desc *sw2_ch[2]; | |
| 30 | + struct gpio_desc *dsa1_gpios[5]; | |
| 31 | + struct gpio_desc *dsa2_gpios[5]; | |
| 32 | + u32 gain[2]; | |
| 33 | +}; | |
| 34 | + | |
| 35 | +static int admfm2000_mode(struct iio_dev *indio_dev, u32 chan, u32 mode) | |
| 36 | +{ | |
| 37 | + struct admfm2000_state *st = iio_priv(indio_dev); | |
| 38 | + int i; | |
| 39 | + | |
| 40 | + switch (mode) { | |
| 41 | + case ADMFM2000_MIXER_MODE: | |
| 42 | + for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) { | |
| 43 | + gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 1 : 0); | |
| 44 | + gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 0 : 1); | |
| 45 | + } | |
| 46 | + return 0; | |
| 47 | + case ADMFM2000_DIRECT_IF_MODE: | |
| 48 | + for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) { | |
| 49 | + gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 0 : 1); | |
| 50 | + gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 1 : 0); | |
| 51 | + } | |
| 52 | + return 0; | |
| 53 | + default: | |
| 54 | + return -EINVAL; | |
| 55 | + } | |
| 56 | +} | |
| 57 | + | |
| 58 | +static int admfm2000_attenuation(struct iio_dev *indio_dev, u32 chan, u32 value) | |
| 59 | +{ | |
| 60 | + struct admfm2000_state *st = iio_priv(indio_dev); | |
| 61 | + int i; | |
| 62 | + | |
| 63 | + switch (chan) { | |
| 64 | + case 0: | |
| 65 | + for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) | |
| 66 | + gpiod_set_value_cansleep(st->dsa1_gpios[i], value & (1 << i)); | |
| 67 | + return 0; | |
| 68 | + case 1: | |
| 69 | + for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) | |
| 70 | + gpiod_set_value_cansleep(st->dsa2_gpios[i], value & (1 << i)); | |
| 71 | + return 0; | |
| 72 | + default: | |
| 73 | + return -EINVAL; | |
| 74 | + } | |
| 75 | +} | |
| 76 | + | |
| 77 | +static int admfm2000_read_raw(struct iio_dev *indio_dev, | |
| 78 | + struct iio_chan_spec const *chan, int *val, | |
| 79 | + int *val2, long mask) | |
| 80 | +{ | |
| 81 | + struct admfm2000_state *st = iio_priv(indio_dev); | |
| 82 | + int gain; | |
| 83 | + | |
| 84 | + switch (mask) { | |
| 85 | + case IIO_CHAN_INFO_HARDWAREGAIN: | |
| 86 | + mutex_lock(&st->lock); | |
| 87 | + gain = ~(st->gain[chan->channel]) * -1000; | |
| 88 | + *val = gain / 1000; | |
| 89 | + *val2 = (gain % 1000) * 1000; | |
| 90 | + mutex_unlock(&st->lock); | |
| 91 | + | |
| 92 | + return IIO_VAL_INT_PLUS_MICRO_DB; | |
| 93 | + default: | |
| 94 | + return -EINVAL; | |
| 95 | + } | |
| 96 | +} | |
| 97 | + | |
| 98 | +static int admfm2000_write_raw(struct iio_dev *indio_dev, | |
| 99 | + struct iio_chan_spec const *chan, int val, | |
| 100 | + int val2, long mask) | |
| 101 | +{ | |
| 102 | + struct admfm2000_state *st = iio_priv(indio_dev); | |
| 103 | + int gain, ret; | |
| 104 | + | |
| 105 | + if (val < 0) | |
| 106 | + gain = (val * 1000) - (val2 / 1000); | |
| 107 | + else | |
| 108 | + gain = (val * 1000) + (val2 / 1000); | |
| 109 | + | |
| 110 | + if (gain > ADMFM2000_MAX_GAIN || gain < ADMFM2000_MIN_GAIN) | |
| 111 | + return -EINVAL; | |
| 112 | + | |
| 113 | + switch (mask) { | |
| 114 | + case IIO_CHAN_INFO_HARDWAREGAIN: | |
| 115 | + mutex_lock(&st->lock); | |
| 116 | + st->gain[chan->channel] = ~((abs(gain) / 1000) & 0x1F); | |
| 117 | + | |
| 118 | + ret = admfm2000_attenuation(indio_dev, chan->channel, | |
| 119 | + st->gain[chan->channel]); | |
| 120 | + mutex_unlock(&st->lock); | |
| 121 | + return ret; | |
| 122 | + default: | |
| 123 | + return -EINVAL; | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +static int admfm2000_write_raw_get_fmt(struct iio_dev *indio_dev, | |
| 128 | + struct iio_chan_spec const *chan, | |
| 129 | + long mask) | |
| 130 | +{ | |
| 131 | + switch (mask) { | |
| 132 | + case IIO_CHAN_INFO_HARDWAREGAIN: | |
| 133 | + return IIO_VAL_INT_PLUS_MICRO_DB; | |
| 134 | + default: | |
| 135 | + return -EINVAL; | |
| 136 | + } | |
| 137 | +} | |
| 138 | + | |
| 139 | +static const struct iio_info admfm2000_info = { | |
| 140 | + .read_raw = &admfm2000_read_raw, | |
| 141 | + .write_raw = &admfm2000_write_raw, | |
| 142 | + .write_raw_get_fmt = &admfm2000_write_raw_get_fmt, | |
| 143 | +}; | |
| 144 | + | |
| 145 | +#define ADMFM2000_CHAN(_channel) { \ | |
| 146 | + .type = IIO_VOLTAGE, \ | |
| 147 | + .output = 1, \ | |
| 148 | + .indexed = 1, \ | |
| 149 | + .channel = _channel, \ | |
| 150 | + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ | |
| 151 | +} | |
| 152 | + | |
| 153 | +static const struct iio_chan_spec admfm2000_channels[] = { | |
| 154 | + ADMFM2000_CHAN(0), | |
| 155 | + ADMFM2000_CHAN(1), | |
| 156 | +}; | |
| 157 | + | |
| 158 | +static int admfm2000_channel_config(struct admfm2000_state *st, | |
| 159 | + struct iio_dev *indio_dev) | |
| 160 | +{ | |
| 161 | + struct platform_device *pdev = to_platform_device(indio_dev->dev.parent); | |
| 162 | + struct device *dev = &pdev->dev; | |
| 163 | + struct fwnode_handle *child; | |
| 164 | + struct gpio_desc **dsa; | |
| 165 | + struct gpio_desc **sw; | |
| 166 | + int ret, i; | |
| 167 | + bool mode; | |
| 168 | + u32 reg; | |
| 169 | + | |
| 170 | + device_for_each_child_node(dev, child) { | |
| 171 | + ret = fwnode_property_read_u32(child, "reg", ®); | |
| 172 | + if (ret) { | |
| 173 | + fwnode_handle_put(child); | |
| 174 | + return dev_err_probe(dev, ret, | |
| 175 | + "Failed to get reg property\n"); | |
| 176 | + } | |
| 177 | + | |
| 178 | + if (reg >= indio_dev->num_channels) { | |
| 179 | + fwnode_handle_put(child); | |
| 180 | + return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n", | |
| 181 | + indio_dev->num_channels); | |
| 182 | + } | |
| 183 | + | |
| 184 | + if (fwnode_property_present(child, "adi,mixer-mode")) | |
| 185 | + mode = ADMFM2000_MIXER_MODE; | |
| 186 | + else | |
| 187 | + mode = ADMFM2000_DIRECT_IF_MODE; | |
| 188 | + | |
| 189 | + switch (reg) { | |
| 190 | + case 0: | |
| 191 | + sw = st->sw1_ch; | |
| 192 | + dsa = st->dsa1_gpios; | |
| 193 | + break; | |
| 194 | + case 1: | |
| 195 | + sw = st->sw2_ch; | |
| 196 | + dsa = st->dsa2_gpios; | |
| 197 | + break; | |
| 198 | + default: | |
| 199 | + fwnode_handle_put(child); | |
| 200 | + return -EINVAL; | |
| 201 | + } | |
| 202 | + | |
| 203 | + for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) { | |
| 204 | + sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch", | |
| 205 | + i, GPIOD_OUT_LOW, NULL); | |
| 206 | + if (IS_ERR(sw[i])) { | |
| 207 | + fwnode_handle_put(child); | |
| 208 | + return dev_err_probe(dev, PTR_ERR(sw[i]), | |
| 209 | + "Failed to get gpios\n"); | |
| 210 | + } | |
| 211 | + } | |
| 212 | + | |
| 213 | + for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) { | |
| 214 | + dsa[i] = devm_fwnode_gpiod_get_index(dev, child, | |
| 215 | + "attenuation", i, | |
| 216 | + GPIOD_OUT_LOW, NULL); | |
| 217 | + if (IS_ERR(dsa[i])) { | |
| 218 | + fwnode_handle_put(child); | |
| 219 | + return dev_err_probe(dev, PTR_ERR(dsa[i]), | |
| 220 | + "Failed to get gpios\n"); | |
| 221 | + } | |
| 222 | + } | |
| 223 | + | |
| 224 | + ret = admfm2000_mode(indio_dev, reg, mode); | |
| 225 | + if (ret) { | |
| 226 | + fwnode_handle_put(child); | |
| 227 | + return ret; | |
| 228 | + } | |
| 229 | + } | |
| 230 | + | |
| 231 | + return 0; | |
| 232 | +} | |
| 233 | + | |
| 234 | +static int admfm2000_probe(struct platform_device *pdev) | |
| 235 | +{ | |
| 236 | + struct device *dev = &pdev->dev; | |
| 237 | + struct admfm2000_state *st; | |
| 238 | + struct iio_dev *indio_dev; | |
| 239 | + int ret; | |
| 240 | + | |
| 241 | + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); | |
| 242 | + if (!indio_dev) | |
| 243 | + return -ENOMEM; | |
| 244 | + | |
| 245 | + st = iio_priv(indio_dev); | |
| 246 | + | |
| 247 | + indio_dev->name = "admfm2000"; | |
| 248 | + indio_dev->num_channels = ARRAY_SIZE(admfm2000_channels); | |
| 249 | + indio_dev->channels = admfm2000_channels; | |
| 250 | + indio_dev->info = &admfm2000_info; | |
| 251 | + indio_dev->modes = INDIO_DIRECT_MODE; | |
| 252 | + | |
| 253 | + st->gain[0] = ADMFM2000_DEFAULT_GAIN; | |
| 254 | + st->gain[1] = ADMFM2000_DEFAULT_GAIN; | |
| 255 | + | |
| 256 | + mutex_init(&st->lock); | |
| 257 | + | |
| 258 | + ret = admfm2000_channel_config(st, indio_dev); | |
| 259 | + if (ret) | |
| 260 | + return ret; | |
| 261 | + | |
| 262 | + return devm_iio_device_register(dev, indio_dev); | |
| 263 | +} | |
| 264 | + | |
| 265 | +static const struct of_device_id admfm2000_of_match[] = { | |
| 266 | + { .compatible = "adi,admfm2000" }, | |
| 267 | + { } | |
| 268 | +}; | |
| 269 | +MODULE_DEVICE_TABLE(of, admfm2000_of_match); | |
| 270 | + | |
| 271 | +static struct platform_driver admfm2000_driver = { | |
| 272 | + .driver = { | |
| 273 | + .name = "admfm2000", | |
| 274 | + .of_match_table = admfm2000_of_match, | |
| 275 | + }, | |
| 276 | + .probe = admfm2000_probe, | |
| 277 | +}; | |
| 278 | +module_platform_driver(admfm2000_driver); | |
| 279 | + | |
| 280 | +MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>"); | |
| 281 | +MODULE_DESCRIPTION("ADMFM2000 Dual Microwave Down Converter"); | |
| 282 | +MODULE_LICENSE("GPL"); | |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | #include <linux/regmap.h> |
| 4 | 4 | #include <linux/iio/iio.h> |
| 5 | 5 | #include <linux/module.h> |
| 6 | −#include <linux/acpi.h> | |
| 6 | +#include <linux/mod_devicetable.h> | |
| 7 | 7 | |
| 8 | 8 | #include "bmg160.h" |
| 9 | 9 | |
| @@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(of, bmg160_of_match); · 56 unmodified lines | ||
| 66 | 66 | static struct i2c_driver bmg160_i2c_driver = { |
| 67 | 67 | .driver = { |
| 68 | 68 | .name = "bmg160_i2c", |
| 69 | − .acpi_match_table = ACPI_PTR(bmg160_acpi_match), | |
| 69 | + .acpi_match_table = bmg160_acpi_match, | |
| 70 | 70 | .of_match_table = bmg160_of_match, |
| 71 | 71 | .pm = &bmg160_pm_ops, |
| 72 | 72 | }, |
| @@ -346,6 +346,13 @@ err: · 345 unmodified lines | ||
| 346 | 346 | return IRQ_HANDLED; |
| 347 | 347 | } |
| 348 | 348 | |
| 349 | +static void afe4403_regulator_disable(void *data) | |
| 350 | +{ | |
| 351 | + struct regulator *regulator = data; | |
| 352 | + | |
| 353 | + regulator_disable(regulator); | |
| 354 | +} | |
| 355 | + | |
| 349 | 356 | #define AFE4403_TIMING_PAIRS \ |
| 350 | 357 | { AFE440X_LED2STC, 0x000050 }, \ |
| 351 | 358 | { AFE440X_LED2ENDC, 0x0003e7 }, \ |
| @@ -495,19 +502,24 @@ static int afe4403_probe(struct spi_device *spi) · 143 unmodified lines | ||
| 495 | 502 | dev_err(afe->dev, "Unable to enable regulator\n"); |
| 496 | 503 | return ret; |
| 497 | 504 | } |
| 505 | + ret = devm_add_action_or_reset(afe->dev, afe4403_regulator_disable, afe->regulator); | |
| 506 | + if (ret) { | |
| 507 | + dev_err(afe->dev, "Unable to add regulator disable action\n"); | |
| 508 | + return ret; | |
| 509 | + } | |
| 498 | 510 | |
| 499 | 511 | ret = regmap_write(afe->regmap, AFE440X_CONTROL0, |
| 500 | 512 | AFE440X_CONTROL0_SW_RESET); |
| 501 | 513 | if (ret) { |
| 502 | 514 | dev_err(afe->dev, "Unable to reset device\n"); |
| 503 | − goto err_disable_reg; | |
| 515 | + return ret; | |
| 504 | 516 | } |
| 505 | 517 | |
| 506 | 518 | ret = regmap_multi_reg_write(afe->regmap, afe4403_reg_sequences, |
| 507 | 519 | ARRAY_SIZE(afe4403_reg_sequences)); |
| 508 | 520 | if (ret) { |
| 509 | 521 | dev_err(afe->dev, "Unable to set register defaults\n"); |
| 510 | − goto err_disable_reg; | |
| 522 | + return ret; | |
| 511 | 523 | } |
| 512 | 524 | |
| 513 | 525 | indio_dev->modes = INDIO_DIRECT_MODE; |
| @@ -523,16 +535,15 @@ static int afe4403_probe(struct spi_device *spi) · 9 unmodified lines | ||
| 523 | 535 | iio_device_id(indio_dev)); |
| 524 | 536 | if (!afe->trig) { |
| 525 | 537 | dev_err(afe->dev, "Unable to allocate IIO trigger\n"); |
| 526 | − ret = -ENOMEM; | |
| 527 | − goto err_disable_reg; | |
| 538 | + return -ENOMEM; | |
| 528 | 539 | } |
| 529 | 540 | |
| 530 | 541 | iio_trigger_set_drvdata(afe->trig, indio_dev); |
| 531 | 542 | |
| 532 | − ret = iio_trigger_register(afe->trig); | |
| 543 | + ret = devm_iio_trigger_register(afe->dev, afe->trig); | |
| 533 | 544 | if (ret) { |
| 534 | 545 | dev_err(afe->dev, "Unable to register IIO trigger\n"); |
| 535 | − goto err_disable_reg; | |
| 546 | + return ret; | |
| 536 | 547 | } |
| 537 | 548 | |
| 538 | 549 | ret = devm_request_threaded_irq(afe->dev, afe->irq, |
| @@ -542,52 +553,25 @@ static int afe4403_probe(struct spi_device *spi) · 3 unmodified lines | ||
| 542 | 553 | afe->trig); |
| 543 | 554 | if (ret) { |
| 544 | 555 | dev_err(afe->dev, "Unable to request IRQ\n"); |
| 545 | − goto err_trig; | |
| 556 | + return ret; | |
| 546 | 557 | } |
| 547 | 558 | } |
| 548 | 559 | |
| 549 | − ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, | |
| 550 | − afe4403_trigger_handler, NULL); | |
| 560 | + ret = devm_iio_triggered_buffer_setup(afe->dev, indio_dev, | |
| 561 | + &iio_pollfunc_store_time, | |
| 562 | + afe4403_trigger_handler, NULL); | |
| 551 | 563 | if (ret) { |
| 552 | 564 | dev_err(afe->dev, "Unable to setup buffer\n"); |
| 553 | − goto err_trig; | |
| 565 | + return ret; | |
| 554 | 566 | } |
| 555 | 567 | |
| 556 | − ret = iio_device_register(indio_dev); | |
| 568 | + ret = devm_iio_device_register(afe->dev, indio_dev); | |
| 557 | 569 | if (ret) { |
| 558 | 570 | dev_err(afe->dev, "Unable to register IIO device\n"); |
| 559 | − goto err_buff; | |
| 571 | + return ret; | |
| 560 | 572 | } |
| 561 | 573 | |
| 562 | 574 | return 0; |
| 563 | − | |
| 564 | −err_buff: | |
| 565 | − iio_triggered_buffer_cleanup(indio_dev); | |
| 566 | −err_trig: | |
| 567 | − if (afe->irq > 0) | |
| 568 | − iio_trigger_unregister(afe->trig); | |
| 569 | −err_disable_reg: | |
| 570 | − regulator_disable(afe->regulator); | |
| 571 | − | |
| 572 | − return ret; | |
| 573 | −} | |
| 574 | − | |
| 575 | −static void afe4403_remove(struct spi_device *spi) | |
| 576 | −{ | |
| 577 | − struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
| 578 | − struct afe4403_data *afe = iio_priv(indio_dev); | |
| 579 | − int ret; | |
| 580 | − | |
| 581 | − iio_device_unregister(indio_dev); | |
| 582 | − | |
| 583 | − iio_triggered_buffer_cleanup(indio_dev); | |
| 584 | − | |
| 585 | − if (afe->irq > 0) | |
| 586 | − iio_trigger_unregister(afe->trig); | |
| 587 | − | |
| 588 | − ret = regulator_disable(afe->regulator); | |
| 589 | − if (ret) | |
| 590 | − dev_warn(afe->dev, "Unable to disable regulator\n"); | |
| 591 | 575 | } |
| 592 | 576 | |
| 593 | 577 | static const struct spi_device_id afe4403_ids[] = { |
| @@ -603,7 +587,6 @@ static struct spi_driver afe4403_spi_driver = { · 9 unmodified lines | ||
| 603 | 587 | .pm = pm_sleep_ptr(&afe4403_pm_ops), |
| 604 | 588 | }, |
| 605 | 589 | .probe = afe4403_probe, |
| 606 | − .remove = afe4403_remove, | |
| 607 | 590 | .id_table = afe4403_ids, |
| 608 | 591 | }; |
| 609 | 592 | module_spi_driver(afe4403_spi_driver); |
| @@ -349,6 +349,13 @@ err: · 348 unmodified lines | ||
| 349 | 349 | return IRQ_HANDLED; |
| 350 | 350 | } |
| 351 | 351 | |
| 352 | +static void afe4404_regulator_disable(void *data) | |
| 353 | +{ | |
| 354 | + struct regulator *regulator = data; | |
| 355 | + | |
| 356 | + regulator_disable(regulator); | |
| 357 | +} | |
| 358 | + | |
| 352 | 359 | /* Default timings from data-sheet */ |
| 353 | 360 | #define AFE4404_TIMING_PAIRS \ |
| 354 | 361 | { AFE440X_PRPCOUNT, 39999 }, \ |
| @@ -502,19 +509,24 @@ static int afe4404_probe(struct i2c_client *client) · 147 unmodified lines | ||
| 502 | 509 | dev_err(afe->dev, "Unable to enable regulator\n"); |
| 503 | 510 | return ret; |
| 504 | 511 | } |
| 512 | + ret = devm_add_action_or_reset(afe->dev, afe4404_regulator_disable, afe->regulator); | |
| 513 | + if (ret) { | |
| 514 | + dev_err(afe->dev, "Unable to enable regulator\n"); | |
| 515 | + return ret; | |
| 516 | + } | |
| 505 | 517 | |
| 506 | 518 | ret = regmap_write(afe->regmap, AFE440X_CONTROL0, |
| 507 | 519 | AFE440X_CONTROL0_SW_RESET); |
| 508 | 520 | if (ret) { |
| 509 | 521 | dev_err(afe->dev, "Unable to reset device\n"); |
| 510 | − goto disable_reg; | |
| 522 | + return ret; | |
| 511 | 523 | } |
| 512 | 524 | |
| 513 | 525 | ret = regmap_multi_reg_write(afe->regmap, afe4404_reg_sequences, |
| 514 | 526 | ARRAY_SIZE(afe4404_reg_sequences)); |
| 515 | 527 | if (ret) { |
| 516 | 528 | dev_err(afe->dev, "Unable to set register defaults\n"); |
| 517 | − goto disable_reg; | |
| 529 | + return ret; | |
| 518 | 530 | } |
| 519 | 531 | |
| 520 | 532 | indio_dev->modes = INDIO_DIRECT_MODE; |
| @@ -530,16 +542,15 @@ static int afe4404_probe(struct i2c_client *client) · 9 unmodified lines | ||
| 530 | 542 | iio_device_id(indio_dev)); |
| 531 | 543 | if (!afe->trig) { |
| 532 | 544 | dev_err(afe->dev, "Unable to allocate IIO trigger\n"); |
| 533 | − ret = -ENOMEM; | |
| 534 | − goto disable_reg; | |
| 545 | + return -ENOMEM; | |
| 535 | 546 | } |
| 536 | 547 | |
| 537 | 548 | iio_trigger_set_drvdata(afe->trig, indio_dev); |
| 538 | 549 | |
| 539 | − ret = iio_trigger_register(afe->trig); | |
| 550 | + ret = devm_iio_trigger_register(afe->dev, afe->trig); | |
| 540 | 551 | if (ret) { |
| 541 | 552 | dev_err(afe->dev, "Unable to register IIO trigger\n"); |
| 542 | − goto disable_reg; | |
| 553 | + return ret; | |
| 543 | 554 | } |
| 544 | 555 | |
| 545 | 556 | ret = devm_request_threaded_irq(afe->dev, afe->irq, |
| @@ -549,52 +560,25 @@ static int afe4404_probe(struct i2c_client *client) · 3 unmodified lines | ||
| 549 | 560 | afe->trig); |
| 550 | 561 | if (ret) { |
| 551 | 562 | dev_err(afe->dev, "Unable to request IRQ\n"); |
| 552 | − goto disable_reg; | |
| 563 | + return ret; | |
| 553 | 564 | } |
| 554 | 565 | } |
| 555 | 566 | |
| 556 | − ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, | |
| 557 | − afe4404_trigger_handler, NULL); | |
| 567 | + ret = devm_iio_triggered_buffer_setup(afe->dev, indio_dev, | |
| 568 | + &iio_pollfunc_store_time, | |
| 569 | + afe4404_trigger_handler, NULL); | |
| 558 | 570 | if (ret) { |
| 559 | 571 | dev_err(afe->dev, "Unable to setup buffer\n"); |
| 560 | − goto unregister_trigger; | |
| 572 | + return ret; | |
| 561 | 573 | } |
| 562 | 574 | |
| 563 | − ret = iio_device_register(indio_dev); | |
| 575 | + ret = devm_iio_device_register(afe->dev, indio_dev); | |
| 564 | 576 | if (ret) { |
| 565 | 577 | dev_err(afe->dev, "Unable to register IIO device\n"); |
| 566 | − goto unregister_triggered_buffer; | |
| 578 | + return ret; | |
| 567 | 579 | } |
| 568 | 580 | |
| 569 | 581 | return 0; |
| 570 | − | |
| 571 | −unregister_triggered_buffer: | |
| 572 | − iio_triggered_buffer_cleanup(indio_dev); | |
| 573 | −unregister_trigger: | |
| 574 | − if (afe->irq > 0) | |
| 575 | − iio_trigger_unregister(afe->trig); | |
| 576 | −disable_reg: | |
| 577 | − regulator_disable(afe->regulator); | |
| 578 | − | |
| 579 | − return ret; | |
| 580 | −} | |
| 581 | − | |
| 582 | −static void afe4404_remove(struct i2c_client *client) | |
| 583 | −{ | |
| 584 | − struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
| 585 | − struct afe4404_data *afe = iio_priv(indio_dev); | |
| 586 | − int ret; | |
| 587 | − | |
| 588 | − iio_device_unregister(indio_dev); | |
| 589 | − | |
| 590 | − iio_triggered_buffer_cleanup(indio_dev); | |
| 591 | − | |
| 592 | − if (afe->irq > 0) | |
| 593 | − iio_trigger_unregister(afe->trig); | |
| 594 | − | |
| 595 | − ret = regulator_disable(afe->regulator); | |
| 596 | − if (ret) | |
| 597 | − dev_err(afe->dev, "Unable to disable regulator\n"); | |
| 598 | 582 | } |
| 599 | 583 | |
| 600 | 584 | static const struct i2c_device_id afe4404_ids[] = { |
| @@ -610,7 +594,6 @@ static struct i2c_driver afe4404_i2c_driver = { · 9 unmodified lines | ||
| 610 | 594 | .pm = pm_sleep_ptr(&afe4404_pm_ops), |
| 611 | 595 | }, |
| 612 | 596 | .probe = afe4404_probe, |
| 613 | − .remove = afe4404_remove, | |
| 614 | 597 | .id_table = afe4404_ids, |
| 615 | 598 | }; |
| 616 | 599 | module_i2c_driver(afe4404_i2c_driver); |
| @@ -5,41 +5,66 @@ · 4 unmodified lines | ||
| 5 | 5 | * |
| 6 | 6 | * Copyright (C) 2023 |
| 7 | 7 | * |
| 8 | + * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH | |
| 9 | + * | |
| 8 | 10 | * Datasheet: https://www.ti.com/lit/ds/symlink/hdc3020.pdf |
| 9 | 11 | */ |
| 10 | 12 | |
| 13 | +#include <linux/bitfield.h> | |
| 11 | 14 | #include <linux/bitops.h> |
| 12 | 15 | #include <linux/cleanup.h> |
| 13 | 16 | #include <linux/crc8.h> |
| 14 | 17 | #include <linux/delay.h> |
| 15 | 18 | #include <linux/i2c.h> |
| 16 | 19 | #include <linux/init.h> |
| 20 | +#include <linux/interrupt.h> | |
| 17 | 21 | #include <linux/module.h> |
| 18 | 22 | #include <linux/mutex.h> |
| 23 | +#include <linux/units.h> | |
| 19 | 24 | |
| 20 | 25 | #include <asm/unaligned.h> |
| 21 | 26 | |
| 27 | +#include <linux/iio/events.h> | |
| 22 | 28 | #include <linux/iio/iio.h> |
| 23 | 29 | |
| 24 | −#define HDC3020_HEATER_CMD_MSB 0x30 /* shared by all heater commands */ | |
| 25 | −#define HDC3020_HEATER_ENABLE 0x6D | |
| 26 | −#define HDC3020_HEATER_DISABLE 0x66 | |
| 27 | −#define HDC3020_HEATER_CONFIG 0x6E | |
| 30 | +#define HDC3020_S_AUTO_10HZ_MOD0 0x2737 | |
| 31 | +#define HDC3020_S_STATUS 0x3041 | |
| 32 | +#define HDC3020_HEATER_DISABLE 0x3066 | |
| 33 | +#define HDC3020_HEATER_ENABLE 0x306D | |
| 34 | +#define HDC3020_HEATER_CONFIG 0x306E | |
| 35 | +#define HDC3020_EXIT_AUTO 0x3093 | |
| 36 | +#define HDC3020_S_T_RH_THRESH_LOW 0x6100 | |
| 37 | +#define HDC3020_S_T_RH_THRESH_LOW_CLR 0x610B | |
| 38 | +#define HDC3020_S_T_RH_THRESH_HIGH_CLR 0x6116 | |
| 39 | +#define HDC3020_S_T_RH_THRESH_HIGH 0x611D | |
| 40 | +#define HDC3020_R_T_RH_AUTO 0xE000 | |
| 41 | +#define HDC3020_R_T_LOW_AUTO 0xE002 | |
| 42 | +#define HDC3020_R_T_HIGH_AUTO 0xE003 | |
| 43 | +#define HDC3020_R_RH_LOW_AUTO 0xE004 | |
| 44 | +#define HDC3020_R_RH_HIGH_AUTO 0xE005 | |
| 45 | +#define HDC3020_R_T_RH_THRESH_LOW 0xE102 | |
| 46 | +#define HDC3020_R_T_RH_THRESH_LOW_CLR 0xE109 | |
| 47 | +#define HDC3020_R_T_RH_THRESH_HIGH_CLR 0xE114 | |
| 48 | +#define HDC3020_R_T_RH_THRESH_HIGH 0xE11F | |
| 49 | +#define HDC3020_R_STATUS 0xF32D | |
| 50 | + | |
| 51 | +#define HDC3020_THRESH_TEMP_MASK GENMASK(8, 0) | |
| 52 | +#define HDC3020_THRESH_TEMP_TRUNC_SHIFT 7 | |
| 53 | +#define HDC3020_THRESH_HUM_MASK GENMASK(15, 9) | |
| 54 | +#define HDC3020_THRESH_HUM_TRUNC_SHIFT 9 | |
| 55 | + | |
| 56 | +#define HDC3020_STATUS_T_LOW_ALERT BIT(6) | |
| 57 | +#define HDC3020_STATUS_T_HIGH_ALERT BIT(7) | |
| 58 | +#define HDC3020_STATUS_RH_LOW_ALERT BIT(8) | |
| 59 | +#define HDC3020_STATUS_RH_HIGH_ALERT BIT(9) | |
| 28 | 60 | |
| 29 | 61 | #define HDC3020_READ_RETRY_TIMES 10 |
| 30 | 62 | #define HDC3020_BUSY_DELAY_MS 10 |
| 31 | 63 | |
| 32 | 64 | #define HDC3020_CRC8_POLYNOMIAL 0x31 |
| 33 | 65 | |
| 34 | −static const u8 HDC3020_S_AUTO_10HZ_MOD0[2] = { 0x27, 0x37 }; | |
| 35 | − | |
| 36 | −static const u8 HDC3020_EXIT_AUTO[2] = { 0x30, 0x93 }; | |
| 37 | − | |
| 38 | −static const u8 HDC3020_R_T_RH_AUTO[2] = { 0xE0, 0x00 }; | |
| 39 | −static const u8 HDC3020_R_T_LOW_AUTO[2] = { 0xE0, 0x02 }; | |
| 40 | −static const u8 HDC3020_R_T_HIGH_AUTO[2] = { 0xE0, 0x03 }; | |
| 41 | −static const u8 HDC3020_R_RH_LOW_AUTO[2] = { 0xE0, 0x04 }; | |
| 42 | −static const u8 HDC3020_R_RH_HIGH_AUTO[2] = { 0xE0, 0x05 }; | |
| 66 | +#define HDC3020_MIN_TEMP -40 | |
| 67 | +#define HDC3020_MAX_TEMP 125 | |
| 43 | 68 | |
| 44 | 69 | struct hdc3020_data { |
| 45 | 70 | struct i2c_client *client; |
| @@ -54,18 +79,37 @@ struct hdc3020_data { · 8 unmodified lines | ||
| 54 | 79 | |
| 55 | 80 | static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF}; |
| 56 | 81 | |
| 82 | +static const struct iio_event_spec hdc3020_t_rh_event[] = { | |
| 83 | + { | |
| 84 | + .type = IIO_EV_TYPE_THRESH, | |
| 85 | + .dir = IIO_EV_DIR_RISING, | |
| 86 | + .mask_separate = BIT(IIO_EV_INFO_VALUE) | | |
| 87 | + BIT(IIO_EV_INFO_HYSTERESIS), | |
| 88 | + }, | |
| 89 | + { | |
| 90 | + .type = IIO_EV_TYPE_THRESH, | |
| 91 | + .dir = IIO_EV_DIR_FALLING, | |
| 92 | + .mask_separate = BIT(IIO_EV_INFO_VALUE) | | |
| 93 | + BIT(IIO_EV_INFO_HYSTERESIS), | |
| 94 | + }, | |
| 95 | +}; | |
| 96 | + | |
| 57 | 97 | static const struct iio_chan_spec hdc3020_channels[] = { |
| 58 | 98 | { |
| 59 | 99 | .type = IIO_TEMP, |
| 60 | 100 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
| 61 | 101 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) | |
| 62 | 102 | BIT(IIO_CHAN_INFO_TROUGH) | BIT(IIO_CHAN_INFO_OFFSET), |
| 103 | + .event_spec = hdc3020_t_rh_event, | |
| 104 | + .num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event), | |
| 63 | 105 | }, |
| 64 | 106 | { |
| 65 | 107 | .type = IIO_HUMIDITYRELATIVE, |
| 66 | 108 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
| 67 | 109 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) | |
| 68 | 110 | BIT(IIO_CHAN_INFO_TROUGH), |
| 111 | + .event_spec = hdc3020_t_rh_event, | |
| 112 | + .num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event), | |
| 69 | 113 | }, |
| 70 | 114 | { |
| 71 | 115 | /* |
| @@ -82,7 +126,7 @@ static const struct iio_chan_spec hdc3020_channels[] = { · 10 unmodified lines | ||
| 82 | 126 | |
| 83 | 127 | DECLARE_CRC8_TABLE(hdc3020_crc8_table); |
| 84 | 128 | |
| 85 | −static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len) | |
| 129 | +static int hdc3020_write_bytes(struct hdc3020_data *data, u8 *buf, u8 len) | |
| 86 | 130 | { |
| 87 | 131 | struct i2c_client *client = data->client; |
| 88 | 132 | struct i2c_msg msg; |
| @@ -90,7 +134,7 @@ static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len) · 1 unmodified line | ||
| 90 | 134 | |
| 91 | 135 | msg.addr = client->addr; |
| 92 | 136 | msg.flags = 0; |
| 93 | − msg.buf = (char *)buf; | |
| 137 | + msg.buf = buf; | |
| 94 | 138 | msg.len = len; |
| 95 | 139 | |
| 96 | 140 | /* |
| @@ -109,26 +153,28 @@ static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len) · 12 unmodified lines | ||
| 109 | 153 | return -ETIMEDOUT; |
| 110 | 154 | } |
| 111 | 155 | |
| 112 | −static int hdc3020_read_bytes(struct hdc3020_data *data, const u8 *buf, | |
| 113 | − void *val, int len) | |
| 156 | +static | |
| 157 | +int hdc3020_read_bytes(struct hdc3020_data *data, u16 reg, u8 *buf, int len) | |
| 114 | 158 | { |
| 159 | + u8 reg_buf[2]; | |
| 115 | 160 | int ret, cnt; |
| 116 | 161 | struct i2c_client *client = data->client; |
| 117 | 162 | struct i2c_msg msg[2] = { |
| 118 | 163 | [0] = { |
| 119 | 164 | .addr = client->addr, |
| 120 | 165 | .flags = 0, |
| 121 | − .buf = (char *)buf, | |
| 166 | + .buf = reg_buf, | |
| 122 | 167 | .len = 2, |
| 123 | 168 | }, |
| 124 | 169 | [1] = { |
| 125 | 170 | .addr = client->addr, |
| 126 | 171 | .flags = I2C_M_RD, |
| 127 | − .buf = val, | |
| 172 | + .buf = buf, | |
| 128 | 173 | .len = len, |
| 129 | 174 | }, |
| 130 | 175 | }; |
| 131 | 176 | |
| 177 | + put_unaligned_be16(reg, reg_buf); | |
| 132 | 178 | /* |
| 133 | 179 | * During the measurement process, HDC3020 will not return data. |
| 134 | 180 | * So wait for a while and try again |
| @@ -145,48 +191,12 @@ static int hdc3020_read_bytes(struct hdc3020_data *data, const u8 *buf, · 10 unmodified lines | ||
| 145 | 191 | return -ETIMEDOUT; |
| 146 | 192 | } |
| 147 | 193 | |
| 148 | −static int hdc3020_read_measurement(struct hdc3020_data *data, | |
| 149 | − enum iio_chan_type type, int *val) | |
| 150 | −{ | |
| 151 | − u8 crc, buf[6]; | |
| 152 | − int ret; | |
| 153 | − | |
| 154 | − ret = hdc3020_read_bytes(data, HDC3020_R_T_RH_AUTO, buf, 6); | |
| 155 | − if (ret < 0) | |
| 156 | − return ret; | |
| 157 | − | |
| 158 | − /* CRC check of the temperature measurement */ | |
| 159 | − crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); | |
| 160 | − if (crc != buf[2]) | |
| 161 | − return -EINVAL; | |
| 162 | − | |
| 163 | − /* CRC check of the relative humidity measurement */ | |
| 164 | − crc = crc8(hdc3020_crc8_table, buf + 3, 2, CRC8_INIT_VALUE); | |
| 165 | − if (crc != buf[5]) | |
| 166 | − return -EINVAL; | |
| 167 | − | |
| 168 | − if (type == IIO_TEMP) | |
| 169 | − *val = get_unaligned_be16(buf); | |
| 170 | − else if (type == IIO_HUMIDITYRELATIVE) | |
| 171 | − *val = get_unaligned_be16(&buf[3]); | |
| 172 | − else | |
| 173 | − return -EINVAL; | |
| 174 | − | |
| 175 | − return 0; | |
| 176 | −} | |
| 177 | − | |
| 178 | −/* | |
| 179 | − * After exiting the automatic measurement mode or resetting, the peak | |
| 180 | − * value will be reset to the default value | |
| 181 | − * This method is used to get the highest temp measured during automatic | |
| 182 | − * measurement | |
| 183 | − */ | |
| 184 | −static int hdc3020_read_high_peak_t(struct hdc3020_data *data, int *val) | |
| 194 | +static int hdc3020_read_be16(struct hdc3020_data *data, u16 reg) | |
| 185 | 195 | { |
| 186 | 196 | u8 crc, buf[3]; |
| 187 | 197 | int ret; |
| 188 | 198 | |
| 189 | − ret = hdc3020_read_bytes(data, HDC3020_R_T_HIGH_AUTO, buf, 3); | |
| 199 | + ret = hdc3020_read_bytes(data, reg, buf, 3); | |
| 190 | 200 | if (ret < 0) |
| 191 | 201 | return ret; |
| 192 | 202 | |
| @@ -194,73 +204,43 @@ static int hdc3020_read_high_peak_t(struct hdc3020_data *data, int *val) · 1 unmodified line | ||
| 194 | 204 | if (crc != buf[2]) |
| 195 | 205 | return -EINVAL; |
| 196 | 206 | |
| 197 | − *val = get_unaligned_be16(buf); | |
| 198 | − | |
| 199 | − return 0; | |
| 207 | + return get_unaligned_be16(buf); | |
| 200 | 208 | } |
| 201 | 209 | |
| 202 | −/* | |
| 203 | − * This method is used to get the lowest temp measured during automatic | |
| 204 | − * measurement | |
| 205 | − */ | |
| 206 | −static int hdc3020_read_low_peak_t(struct hdc3020_data *data, int *val) | |
| 210 | +static int hdc3020_exec_cmd(struct hdc3020_data *data, u16 reg) | |
| 207 | 211 | { |
| 208 | − u8 crc, buf[3]; | |
| 209 | − int ret; | |
| 212 | + u8 reg_buf[2]; | |
| 210 | 213 | |
| 211 | − ret = hdc3020_read_bytes(data, HDC3020_R_T_LOW_AUTO, buf, 3); | |
| 212 | − if (ret < 0) | |
| 213 | − return ret; | |
| 214 | − | |
| 215 | − crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); | |
| 216 | − if (crc != buf[2]) | |
| 217 | − return -EINVAL; | |
| 218 | − | |
| 219 | − *val = get_unaligned_be16(buf); | |
| 220 | − | |
| 221 | − return 0; | |
| 214 | + put_unaligned_be16(reg, reg_buf); | |
| 215 | + return hdc3020_write_bytes(data, reg_buf, 2); | |
| 222 | 216 | } |
| 223 | 217 | |
| 224 | −/* | |
| 225 | − * This method is used to get the highest humidity measured during automatic | |
| 226 | − * measurement | |
| 227 | − */ | |
| 228 | −static int hdc3020_read_high_peak_rh(struct hdc3020_data *data, int *val) | |
| 218 | +static int hdc3020_read_measurement(struct hdc3020_data *data, | |
| 219 | + enum iio_chan_type type, int *val) | |
| 229 | 220 | { |
| 230 | − u8 crc, buf[3]; | |
| 221 | + u8 crc, buf[6]; | |
| 231 | 222 | int ret; |
| 232 | 223 | |
| 233 | − ret = hdc3020_read_bytes(data, HDC3020_R_RH_HIGH_AUTO, buf, 3); | |
| 224 | + ret = hdc3020_read_bytes(data, HDC3020_R_T_RH_AUTO, buf, 6); | |
| 234 | 225 | if (ret < 0) |
| 235 | 226 | return ret; |
| 236 | 227 | |
| 228 | + /* CRC check of the temperature measurement */ | |
| 237 | 229 | crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); |
| 238 | 230 | if (crc != buf[2]) |
| 239 | 231 | return -EINVAL; |
| 240 | 232 | |
| 241 | − *val = get_unaligned_be16(buf); | |
| 242 | − | |
| 243 | − return 0; | |
| 244 | −} | |
| 245 | − | |
| 246 | −/* | |
| 247 | − * This method is used to get the lowest humidity measured during automatic | |
| 248 | − * measurement | |
| 249 | − */ | |
| 250 | −static int hdc3020_read_low_peak_rh(struct hdc3020_data *data, int *val) | |
| 251 | −{ | |
| 252 | − u8 crc, buf[3]; | |
| 253 | − int ret; | |
| 254 | − | |
| 255 | − ret = hdc3020_read_bytes(data, HDC3020_R_RH_LOW_AUTO, buf, 3); | |
| 256 | − if (ret < 0) | |
| 257 | − return ret; | |
| 258 | − | |
| 259 | − crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE); | |
| 260 | − if (crc != buf[2]) | |
| 233 | + /* CRC check of the relative humidity measurement */ | |
| 234 | + crc = crc8(hdc3020_crc8_table, buf + 3, 2, CRC8_INIT_VALUE); | |
| 235 | + if (crc != buf[5]) | |
| 261 | 236 | return -EINVAL; |
| 262 | 237 | |
| 263 | − *val = get_unaligned_be16(buf); | |
| 238 | + if (type == IIO_TEMP) | |
| 239 | + *val = get_unaligned_be16(buf); | |
| 240 | + else if (type == IIO_HUMIDITYRELATIVE) | |
| 241 | + *val = get_unaligned_be16(&buf[3]); | |
| 242 | + else | |
| 243 | + return -EINVAL; | |
| 264 | 244 | |
| 265 | 245 | return 0; |
| 266 | 246 | } |
| @@ -286,28 +266,28 @@ static int hdc3020_read_raw(struct iio_dev *indio_dev, · 19 unmodified lines | ||
| 286 | 266 | } |
| 287 | 267 | case IIO_CHAN_INFO_PEAK: { |
| 288 | 268 | guard(mutex)(&data->lock); |
| 289 | − if (chan->type == IIO_TEMP) { | |
| 290 | − ret = hdc3020_read_high_peak_t(data, val); | |
| 291 | − if (ret < 0) | |
| 292 | − return ret; | |
| 293 | − } else { | |
| 294 | − ret = hdc3020_read_high_peak_rh(data, val); | |
| 295 | − if (ret < 0) | |
| 296 | − return ret; | |
| 297 | − } | |
| 269 | + if (chan->type == IIO_TEMP) | |
| 270 | + ret = hdc3020_read_be16(data, HDC3020_R_T_HIGH_AUTO); | |
| 271 | + else | |
| 272 | + ret = hdc3020_read_be16(data, HDC3020_R_RH_HIGH_AUTO); | |
| 273 | + | |
| 274 | + if (ret < 0) | |
| 275 | + return ret; | |
| 276 | + | |
| 277 | + *val = ret; | |
| 298 | 278 | return IIO_VAL_INT; |
| 299 | 279 | } |
| 300 | 280 | case IIO_CHAN_INFO_TROUGH: { |
| 301 | 281 | guard(mutex)(&data->lock); |
| 302 | − if (chan->type == IIO_TEMP) { | |
| 303 | − ret = hdc3020_read_low_peak_t(data, val); | |
| 304 | − if (ret < 0) | |
| 305 | − return ret; | |
| 306 | − } else { | |
| 307 | − ret = hdc3020_read_low_peak_rh(data, val); | |
| 308 | − if (ret < 0) | |
| 309 | − return ret; | |
| 310 | − } | |
| 282 | + if (chan->type == IIO_TEMP) | |
| 283 | + ret = hdc3020_read_be16(data, HDC3020_R_T_LOW_AUTO); | |
| 284 | + else | |
| 285 | + ret = hdc3020_read_be16(data, HDC3020_R_RH_LOW_AUTO); | |
| 286 | + | |
| 287 | + if (ret < 0) | |
| 288 | + return ret; | |
| 289 | + | |
| 290 | + *val = ret; | |
| 311 | 291 | return IIO_VAL_INT; |
| 312 | 292 | } |
| 313 | 293 | case IIO_CHAN_INFO_SCALE: |
| @@ -352,23 +332,17 @@ static int hdc3020_update_heater(struct hdc3020_data *data, int val) · 38 unmodified lines | ||
| 352 | 332 | if (val < hdc3020_heater_vals[0] || val > hdc3020_heater_vals[2]) |
| 353 | 333 | return -EINVAL; |
| 354 | 334 | |
| 355 | − buf[0] = HDC3020_HEATER_CMD_MSB; | |
| 335 | + if (!val) | |
| 336 | + hdc3020_exec_cmd(data, HDC3020_HEATER_DISABLE); | |
| 356 | 337 | |
| 357 | − if (!val) { | |
| 358 | − buf[1] = HDC3020_HEATER_DISABLE; | |
| 359 | − return hdc3020_write_bytes(data, buf, 2); | |
| 360 | − } | |
| 361 | − | |
| 362 | − buf[1] = HDC3020_HEATER_CONFIG; | |
| 338 | + put_unaligned_be16(HDC3020_HEATER_CONFIG, buf); | |
| 363 | 339 | put_unaligned_be16(val & GENMASK(13, 0), &buf[2]); |
| 364 | 340 | buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE); |
| 365 | 341 | ret = hdc3020_write_bytes(data, buf, 5); |
| 366 | 342 | if (ret < 0) |
| 367 | 343 | return ret; |
| 368 | 344 | |
| 369 | − buf[1] = HDC3020_HEATER_ENABLE; | |
| 370 | − | |
| 371 | − return hdc3020_write_bytes(data, buf, 2); | |
| 345 | + return hdc3020_exec_cmd(data, HDC3020_HEATER_ENABLE); | |
| 372 | 346 | } |
| 373 | 347 | |
| 374 | 348 | static int hdc3020_write_raw(struct iio_dev *indio_dev, |
| @@ -389,15 +363,197 @@ static int hdc3020_write_raw(struct iio_dev *indio_dev, · 14 unmodified lines | ||
| 389 | 363 | return -EINVAL; |
| 390 | 364 | } |
| 391 | 365 | |
| 366 | +static int hdc3020_write_thresh(struct iio_dev *indio_dev, | |
| 367 | + const struct iio_chan_spec *chan, | |
| 368 | + enum iio_event_type type, | |
| 369 | + enum iio_event_direction dir, | |
| 370 | + enum iio_event_info info, | |
| 371 | + int val, int val2) | |
| 372 | +{ | |
| 373 | + struct hdc3020_data *data = iio_priv(indio_dev); | |
| 374 | + u8 buf[5]; | |
| 375 | + u64 tmp; | |
| 376 | + u16 reg; | |
| 377 | + int ret; | |
| 378 | + | |
| 379 | + /* Supported temperature range is from –40 to 125 degree celsius */ | |
| 380 | + if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP) | |
| 381 | + return -EINVAL; | |
| 382 | + | |
| 383 | + /* Select threshold register */ | |
| 384 | + if (info == IIO_EV_INFO_VALUE) { | |
| 385 | + if (dir == IIO_EV_DIR_RISING) | |
| 386 | + reg = HDC3020_S_T_RH_THRESH_HIGH; | |
| 387 | + else | |
| 388 | + reg = HDC3020_S_T_RH_THRESH_LOW; | |
| 389 | + } else { | |
| 390 | + if (dir == IIO_EV_DIR_RISING) | |
| 391 | + reg = HDC3020_S_T_RH_THRESH_HIGH_CLR; | |
| 392 | + else | |
| 393 | + reg = HDC3020_S_T_RH_THRESH_LOW_CLR; | |
| 394 | + } | |
| 395 | + | |
| 396 | + guard(mutex)(&data->lock); | |
| 397 | + ret = hdc3020_read_be16(data, reg); | |
| 398 | + if (ret < 0) | |
| 399 | + return ret; | |
| 400 | + | |
| 401 | + switch (chan->type) { | |
| 402 | + case IIO_TEMP: | |
| 403 | + /* | |
| 404 | + * Calculate temperature threshold, shift it down to get the | |
| 405 | + * truncated threshold representation in the 9LSBs while keeping | |
| 406 | + * the current humidity threshold in the 7 MSBs. | |
| 407 | + */ | |
| 408 | + tmp = ((u64)(((val + 45) * MICRO) + val2)) * 65535ULL; | |
| 409 | + tmp = div_u64(tmp, MICRO * 175); | |
| 410 | + val = tmp >> HDC3020_THRESH_TEMP_TRUNC_SHIFT; | |
| 411 | + val = FIELD_PREP(HDC3020_THRESH_TEMP_MASK, val); | |
| 412 | + val |= (FIELD_GET(HDC3020_THRESH_HUM_MASK, ret) << | |
| 413 | + HDC3020_THRESH_HUM_TRUNC_SHIFT); | |
| 414 | + break; | |
| 415 | + case IIO_HUMIDITYRELATIVE: | |
| 416 | + /* | |
| 417 | + * Calculate humidity threshold, shift it down and up to get the | |
| 418 | + * truncated threshold representation in the 7MSBs while keeping | |
| 419 | + * the current temperature threshold in the 9 LSBs. | |
| 420 | + */ | |
| 421 | + tmp = ((u64)((val * MICRO) + val2)) * 65535ULL; | |
| 422 | + tmp = div_u64(tmp, MICRO * 100); | |
| 423 | + val = tmp >> HDC3020_THRESH_HUM_TRUNC_SHIFT; | |
| 424 | + val = FIELD_PREP(HDC3020_THRESH_HUM_MASK, val); | |
| 425 | + val |= FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret); | |
| 426 | + break; | |
| 427 | + default: | |
| 428 | + return -EOPNOTSUPP; | |
| 429 | + } | |
| 430 | + | |
| 431 | + put_unaligned_be16(reg, buf); | |
| 432 | + put_unaligned_be16(val, buf + 2); | |
| 433 | + buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE); | |
| 434 | + return hdc3020_write_bytes(data, buf, 5); | |
| 435 | +} | |
| 436 | + | |
| 437 | +static int hdc3020_read_thresh(struct iio_dev *indio_dev, | |
| 438 | + const struct iio_chan_spec *chan, | |
| 439 | + enum iio_event_type type, | |
| 440 | + enum iio_event_direction dir, | |
| 441 | + enum iio_event_info info, | |
| 442 | + int *val, int *val2) | |
| 443 | +{ | |
| 444 | + struct hdc3020_data *data = iio_priv(indio_dev); | |
| 445 | + u16 reg; | |
| 446 | + int ret; | |
| 447 | + | |
| 448 | + /* Select threshold register */ | |
| 449 | + if (info == IIO_EV_INFO_VALUE) { | |
| 450 | + if (dir == IIO_EV_DIR_RISING) | |
| 451 | + reg = HDC3020_R_T_RH_THRESH_HIGH; | |
| 452 | + else | |
| 453 | + reg = HDC3020_R_T_RH_THRESH_LOW; | |
| 454 | + } else { | |
| 455 | + if (dir == IIO_EV_DIR_RISING) | |
| 456 | + reg = HDC3020_R_T_RH_THRESH_HIGH_CLR; | |
| 457 | + else | |
| 458 | + reg = HDC3020_R_T_RH_THRESH_LOW_CLR; | |
| 459 | + } | |
| 460 | + | |
| 461 | + guard(mutex)(&data->lock); | |
| 462 | + ret = hdc3020_read_be16(data, reg); | |
| 463 | + if (ret < 0) | |
| 464 | + return ret; | |
| 465 | + | |
| 466 | + switch (chan->type) { | |
| 467 | + case IIO_TEMP: | |
| 468 | + /* | |
| 469 | + * Get the temperature threshold from 9 LSBs, shift them to get | |
| 470 | + * the truncated temperature threshold representation and | |
| 471 | + * calculate the threshold according to the formula in the | |
| 472 | + * datasheet. | |
| 473 | + */ | |
| 474 | + *val = FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret); | |
| 475 | + *val = *val << HDC3020_THRESH_TEMP_TRUNC_SHIFT; | |
| 476 | + *val = -2949075 + (175 * (*val)); | |
| 477 | + *val2 = 65535; | |
| 478 | + return IIO_VAL_FRACTIONAL; | |
| 479 | + case IIO_HUMIDITYRELATIVE: | |
| 480 | + /* | |
| 481 | + * Get the humidity threshold from 7 MSBs, shift them to get the | |
| 482 | + * truncated humidity threshold representation and calculate the | |
| 483 | + * threshold according to the formula in the datasheet. | |
| 484 | + */ | |
| 485 | + *val = FIELD_GET(HDC3020_THRESH_HUM_MASK, ret); | |
| 486 | + *val = (*val << HDC3020_THRESH_HUM_TRUNC_SHIFT) * 100; | |
| 487 | + *val2 = 65535; | |
| 488 | + return IIO_VAL_FRACTIONAL; | |
| 489 | + default: | |
| 490 | + return -EOPNOTSUPP; | |
| 491 | + } | |
| 492 | +} | |
| 493 | + | |
| 494 | +static irqreturn_t hdc3020_interrupt_handler(int irq, void *private) | |
| 495 | +{ | |
| 496 | + struct iio_dev *indio_dev = private; | |
| 497 | + struct hdc3020_data *data; | |
| 498 | + s64 time; | |
| 499 | + int ret; | |
| 500 | + | |
| 501 | + data = iio_priv(indio_dev); | |
| 502 | + ret = hdc3020_read_be16(data, HDC3020_R_STATUS); | |
| 503 | + if (ret < 0) | |
| 504 | + return IRQ_HANDLED; | |
| 505 | + | |
| 506 | + if (!(ret & (HDC3020_STATUS_T_HIGH_ALERT | HDC3020_STATUS_T_LOW_ALERT | | |
| 507 | + HDC3020_STATUS_RH_HIGH_ALERT | HDC3020_STATUS_RH_LOW_ALERT))) | |
| 508 | + return IRQ_NONE; | |
| 509 | + | |
| 510 | + time = iio_get_time_ns(indio_dev); | |
| 511 | + if (ret & HDC3020_STATUS_T_HIGH_ALERT) | |
| 512 | + iio_push_event(indio_dev, | |
| 513 | + IIO_MOD_EVENT_CODE(IIO_TEMP, 0, | |
| 514 | + IIO_NO_MOD, | |
| 515 | + IIO_EV_TYPE_THRESH, | |
| 516 | + IIO_EV_DIR_RISING), | |
| 517 | + time); | |
| 518 | + | |
| 519 | + if (ret & HDC3020_STATUS_T_LOW_ALERT) | |
| 520 | + iio_push_event(indio_dev, | |
| 521 | + IIO_MOD_EVENT_CODE(IIO_TEMP, 0, | |
| 522 | + IIO_NO_MOD, | |
| 523 | + IIO_EV_TYPE_THRESH, | |
| 524 | + IIO_EV_DIR_FALLING), | |
| 525 | + time); | |
| 526 | + | |
| 527 | + if (ret & HDC3020_STATUS_RH_HIGH_ALERT) | |
| 528 | + iio_push_event(indio_dev, | |
| 529 | + IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0, | |
| 530 | + IIO_NO_MOD, | |
| 531 | + IIO_EV_TYPE_THRESH, | |
| 532 | + IIO_EV_DIR_RISING), | |
| 533 | + time); | |
| 534 | + | |
| 535 | + if (ret & HDC3020_STATUS_RH_LOW_ALERT) | |
| 536 | + iio_push_event(indio_dev, | |
| 537 | + IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0, | |
| 538 | + IIO_NO_MOD, | |
| 539 | + IIO_EV_TYPE_THRESH, | |
| 540 | + IIO_EV_DIR_FALLING), | |
| 541 | + time); | |
| 542 | + | |
| 543 | + return IRQ_HANDLED; | |
| 544 | +} | |
| 545 | + | |
| 392 | 546 | static const struct iio_info hdc3020_info = { |
| 393 | 547 | .read_raw = hdc3020_read_raw, |
| 394 | 548 | .write_raw = hdc3020_write_raw, |
| 395 | 549 | .read_avail = hdc3020_read_available, |
| 550 | + .read_event_value = hdc3020_read_thresh, | |
| 551 | + .write_event_value = hdc3020_write_thresh, | |
| 396 | 552 | }; |
| 397 | 553 | |
| 398 | 554 | static void hdc3020_stop(void *data) |
| 399 | 555 | { |
| 400 | − hdc3020_write_bytes((struct hdc3020_data *)data, HDC3020_EXIT_AUTO, 2); | |
| 556 | + hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO); | |
| 401 | 557 | } |
| 402 | 558 | |
| 403 | 559 | static int hdc3020_probe(struct i2c_client *client) |
| @@ -424,8 +580,25 @@ static int hdc3020_probe(struct i2c_client *client) · 20 unmodified lines | ||
| 424 | 580 | indio_dev->info = &hdc3020_info; |
| 425 | 581 | indio_dev->channels = hdc3020_channels; |
| 426 | 582 | indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels); |
| 583 | + if (client->irq) { | |
| 584 | + ret = devm_request_threaded_irq(&client->dev, client->irq, | |
| 585 | + NULL, hdc3020_interrupt_handler, | |
| 586 | + IRQF_ONESHOT, "hdc3020", | |
| 587 | + indio_dev); | |
| 588 | + if (ret) | |
| 589 | + return dev_err_probe(&client->dev, ret, | |
| 590 | + "Failed to request IRQ\n"); | |
| 591 | + | |
| 592 | + /* | |
| 593 | + * The alert output is activated by default upon power up, | |
| 594 | + * hardware reset, and soft reset. Clear the status register. | |
| 595 | + */ | |
| 596 | + ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS); | |
| 597 | + if (ret) | |
| 598 | + return ret; | |
| 599 | + } | |
| 427 | 600 | |
| 428 | − ret = hdc3020_write_bytes(data, HDC3020_S_AUTO_10HZ_MOD0, 2); | |
| 601 | + ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0); | |
| 429 | 602 | if (ret) |
| 430 | 603 | return dev_err_probe(&client->dev, ret, |
| 431 | 604 | "Unable to set up measurement\n"); |
| @@ -9,7 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | #include <linux/kernel.h> |
| 11 | 11 | #include <linux/module.h> |
| 12 | −#include <linux/acpi.h> | |
| 12 | +#include <linux/mod_devicetable.h> | |
| 13 | 13 | #include <linux/i2c.h> |
| 14 | 14 | #include <linux/slab.h> |
| 15 | 15 | #include <linux/regmap.h> |
| @@ -63,7 +63,7 @@ static struct i2c_driver hts221_driver = { · 47 unmodified lines | ||
| 63 | 63 | .name = "hts221_i2c", |
| 64 | 64 | .pm = pm_sleep_ptr(&hts221_pm_ops), |
| 65 | 65 | .of_match_table = hts221_i2c_of_match, |
| 66 | − .acpi_match_table = ACPI_PTR(hts221_acpi_match), | |
| 66 | + .acpi_match_table = hts221_acpi_match, | |
| 67 | 67 | }, |
| 68 | 68 | .probe = hts221_i2c_probe, |
| 69 | 69 | .id_table = hts221_i2c_id_table, |
| @@ -1363,22 +1363,16 @@ static int adis16475_config_sync_mode(struct adis16475 *st) · 1362 unmodified lines | ||
| 1363 | 1363 | static int adis16475_config_irq_pin(struct adis16475 *st) |
| 1364 | 1364 | { |
| 1365 | 1365 | int ret; |
| 1366 | − struct irq_data *desc; | |
| 1367 | 1366 | u32 irq_type; |
| 1368 | 1367 | u16 val = 0; |
| 1369 | 1368 | u8 polarity; |
| 1370 | 1369 | struct spi_device *spi = st->adis.spi; |
| 1371 | 1370 | |
| 1372 | − desc = irq_get_irq_data(spi->irq); | |
| 1373 | − if (!desc) { | |
| 1374 | − dev_err(&spi->dev, "Could not find IRQ %d\n", spi->irq); | |
| 1375 | − return -EINVAL; | |
| 1376 | − } | |
| 1377 | 1371 | /* |
| 1378 | 1372 | * It is possible to configure the data ready polarity. Furthermore, we |
| 1379 | 1373 | * need to update the adis struct if we want data ready as active low. |
| 1380 | 1374 | */ |
| 1381 | − irq_type = irqd_get_trigger_type(desc); | |
| 1375 | + irq_type = irq_get_trigger_type(spi->irq); | |
| 1382 | 1376 | if (irq_type == IRQ_TYPE_EDGE_RISING) { |
| 1383 | 1377 | polarity = 1; |
| 1384 | 1378 | st->adis.irq_flag = IRQF_TRIGGER_RISING; |
| @@ -1246,18 +1246,11 @@ static int adis16480_config_irq_pin(struct adis16480 *st) · 1245 unmodified lines | ||
| 1246 | 1246 | { |
| 1247 | 1247 | struct device *dev = &st->adis.spi->dev; |
| 1248 | 1248 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
| 1249 | − struct irq_data *desc; | |
| 1250 | 1249 | enum adis16480_int_pin pin; |
| 1251 | 1250 | unsigned int irq_type; |
| 1252 | 1251 | uint16_t val; |
| 1253 | 1252 | int i, irq = 0; |
| 1254 | 1253 | |
| 1255 | − desc = irq_get_irq_data(st->adis.spi->irq); | |
| 1256 | − if (!desc) { | |
| 1257 | − dev_err(dev, "Could not find IRQ %d\n", irq); | |
| 1258 | − return -EINVAL; | |
| 1259 | − } | |
| 1260 | − | |
| 1261 | 1254 | /* Disable data ready since the default after reset is on */ |
| 1262 | 1255 | val = ADIS16480_DRDY_EN(0); |
| 1263 | 1256 | |
| @@ -1285,7 +1278,7 @@ static int adis16480_config_irq_pin(struct adis16480 *st) · 21 unmodified lines | ||
| 1285 | 1278 | * configured as positive or negative, corresponding to |
| 1286 | 1279 | * IRQ_TYPE_EDGE_RISING or IRQ_TYPE_EDGE_FALLING respectively. |
| 1287 | 1280 | */ |
| 1288 | − irq_type = irqd_get_trigger_type(desc); | |
| 1281 | + irq_type = irq_get_trigger_type(st->adis.spi->irq); | |
| 1289 | 1282 | if (irq_type == IRQ_TYPE_EDGE_RISING) { /* Default */ |
| 1290 | 1283 | val |= ADIS16480_DRDY_POL(1); |
| 1291 | 1284 | } else if (irq_type == IRQ_TYPE_EDGE_FALLING) { |
| @@ -43,6 +43,15 @@ static const struct i2c_device_id bmi160_i2c_id[] = { · 42 unmodified lines | ||
| 43 | 43 | MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id); |
| 44 | 44 | |
| 45 | 45 | static const struct acpi_device_id bmi160_acpi_match[] = { |
| 46 | + /* | |
| 47 | + * FIRMWARE BUG WORKAROUND | |
| 48 | + * Some manufacturers like GPD, Lenovo or Aya used the incorrect | |
| 49 | + * ID "10EC5280" for bmi160 in their DSDT. A fixed firmware is not | |
| 50 | + * available as of Feb 2024 after trying to work with OEMs, and | |
| 51 | + * this is not expected to change anymore since at least some of | |
| 52 | + * the affected devices are from 2021/2022. | |
| 53 | + */ | |
| 54 | + {"10EC5280", 0}, | |
| 46 | 55 | {"BMI0160", 0}, |
| 47 | 56 | { }, |
| 48 | 57 | }; |
| @@ -1668,52 +1668,41 @@ static int bmi323_write_raw(struct iio_dev *indio_dev, · 1667 unmodified lines | ||
| 1668 | 1668 | int val2, long mask) |
| 1669 | 1669 | { |
| 1670 | 1670 | struct bmi323_data *data = iio_priv(indio_dev); |
| 1671 | − int ret; | |
| 1672 | 1671 | |
| 1673 | 1672 | switch (mask) { |
| 1674 | 1673 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 1675 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 1676 | − if (ret) | |
| 1677 | − return ret; | |
| 1678 | − | |
| 1679 | − ret = bmi323_set_odr(data, bmi323_iio_to_sensor(chan->type), | |
| 1680 | − val, val2); | |
| 1681 | − iio_device_release_direct_mode(indio_dev); | |
| 1682 | − return ret; | |
| 1674 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 1675 | + return bmi323_set_odr(data, | |
| 1676 | + bmi323_iio_to_sensor(chan->type), | |
| 1677 | + val, val2); | |
| 1678 | + unreachable(); | |
| 1683 | 1679 | case IIO_CHAN_INFO_SCALE: |
| 1684 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 1685 | − if (ret) | |
| 1686 | − return ret; | |
| 1687 | − | |
| 1688 | − ret = bmi323_set_scale(data, bmi323_iio_to_sensor(chan->type), | |
| 1689 | − val, val2); | |
| 1690 | − iio_device_release_direct_mode(indio_dev); | |
| 1691 | − return ret; | |
| 1680 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 1681 | + return bmi323_set_scale(data, | |
| 1682 | + bmi323_iio_to_sensor(chan->type), | |
| 1683 | + val, val2); | |
| 1684 | + unreachable(); | |
| 1692 | 1685 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
| 1693 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 1694 | − if (ret) | |
| 1695 | − return ret; | |
| 1696 | − | |
| 1697 | − ret = bmi323_set_average(data, bmi323_iio_to_sensor(chan->type), | |
| 1698 | − val); | |
| 1699 | − | |
| 1700 | − iio_device_release_direct_mode(indio_dev); | |
| 1701 | − return ret; | |
| 1686 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 1687 | + return bmi323_set_average(data, | |
| 1688 | + bmi323_iio_to_sensor(chan->type), | |
| 1689 | + val); | |
| 1690 | + unreachable(); | |
| 1702 | 1691 | case IIO_CHAN_INFO_ENABLE: |
| 1703 | 1692 | return bmi323_enable_steps(data, val); |
| 1704 | − case IIO_CHAN_INFO_PROCESSED: | |
| 1705 | − scoped_guard(mutex, &data->mutex) { | |
| 1706 | − if (val || !FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, | |
| 1707 | − data->feature_events)) | |
| 1708 | − return -EINVAL; | |
| 1693 | + case IIO_CHAN_INFO_PROCESSED: { | |
| 1694 | + guard(mutex)(&data->mutex); | |
| 1709 | 1695 | |
| 1710 | − /* Clear step counter value */ | |
| 1711 | − ret = bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, | |
| 1712 | − BMI323_STEP_SC1_RST_CNT_MSK, | |
| 1713 | − FIELD_PREP(BMI323_STEP_SC1_RST_CNT_MSK, | |
| 1714 | − 1)); | |
| 1715 | − } | |
| 1716 | − return ret; | |
| 1696 | + if (val || !FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK, | |
| 1697 | + data->feature_events)) | |
| 1698 | + return -EINVAL; | |
| 1699 | + | |
| 1700 | + /* Clear step counter value */ | |
| 1701 | + return bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG, | |
| 1702 | + BMI323_STEP_SC1_RST_CNT_MSK, | |
| 1703 | + FIELD_PREP(BMI323_STEP_SC1_RST_CNT_MSK, | |
| 1704 | + 1)); | |
| 1705 | + } | |
| 1717 | 1706 | default: |
| 1718 | 1707 | return -EINVAL; |
| 1719 | 1708 | } |
| @@ -1724,7 +1713,6 @@ static int bmi323_read_raw(struct iio_dev *indio_dev, · 4 unmodified lines | ||
| 1724 | 1713 | int *val2, long mask) |
| 1725 | 1714 | { |
| 1726 | 1715 | struct bmi323_data *data = iio_priv(indio_dev); |
| 1727 | − int ret; | |
| 1728 | 1716 | |
| 1729 | 1717 | switch (mask) { |
| 1730 | 1718 | case IIO_CHAN_INFO_PROCESSED: |
| @@ -1733,14 +1721,10 @@ static int bmi323_read_raw(struct iio_dev *indio_dev, · 2 unmodified lines | ||
| 1733 | 1721 | switch (chan->type) { |
| 1734 | 1722 | case IIO_ACCEL: |
| 1735 | 1723 | case IIO_ANGL_VEL: |
| 1736 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 1737 | − if (ret) | |
| 1738 | − return ret; | |
| 1739 | − | |
| 1740 | − ret = bmi323_read_axis(data, chan, val); | |
| 1741 | − | |
| 1742 | − iio_device_release_direct_mode(indio_dev); | |
| 1743 | − return ret; | |
| 1724 | + iio_device_claim_direct_scoped(return -EBUSY, | |
| 1725 | + indio_dev) | |
| 1726 | + return bmi323_read_axis(data, chan, val); | |
| 1727 | + unreachable(); | |
| 1744 | 1728 | case IIO_TEMP: |
| 1745 | 1729 | return bmi323_get_temp_data(data, val); |
| 1746 | 1730 | default: |
| @@ -93,6 +93,26 @@ static int bmi323_i2c_probe(struct i2c_client *i2c) · 92 unmodified lines | ||
| 93 | 93 | return bmi323_core_probe(dev); |
| 94 | 94 | } |
| 95 | 95 | |
| 96 | +static const struct acpi_device_id bmi323_acpi_match[] = { | |
| 97 | + /* | |
| 98 | + * The "BOSC0200" identifier used here is not unique to bmi323 devices. | |
| 99 | + * The same "BOSC0200" identifier is found in the ACPI tables of devices | |
| 100 | + * using the bmc150 chip. This creates a conflict with duplicate ACPI | |
| 101 | + * identifiers which multiple drivers want to use. If a non-bmi323 | |
| 102 | + * device starts to load with this "BOSC0200" ACPI match here, then the | |
| 103 | + * chip ID check portion should fail because the chip IDs received (via | |
| 104 | + * i2c) are unique between bmc150 and bmi323 and the driver should | |
| 105 | + * relinquish the device. If and when a different driver (such as | |
| 106 | + * bmc150) starts to load with the "BOSC0200" ACPI match, a short reset | |
| 107 | + * should ensure that the device is not in a bad state during that | |
| 108 | + * driver initialization. This device reset does occur in both the | |
| 109 | + * bmi323 and bmc150 init sequences. | |
| 110 | + */ | |
| 111 | + { "BOSC0200" }, | |
| 112 | + { } | |
| 113 | +}; | |
| 114 | +MODULE_DEVICE_TABLE(acpi, bmi323_acpi_match); | |
| 115 | + | |
| 96 | 116 | static const struct i2c_device_id bmi323_i2c_ids[] = { |
| 97 | 117 | { "bmi323" }, |
| 98 | 118 | { } |
| @@ -109,6 +129,7 @@ static struct i2c_driver bmi323_i2c_driver = { · 10 unmodified lines | ||
| 109 | 129 | .driver = { |
| 110 | 130 | .name = "bmi323", |
| 111 | 131 | .of_match_table = bmi323_of_i2c_match, |
| 132 | + .acpi_match_table = bmi323_acpi_match, | |
| 112 | 133 | }, |
| 113 | 134 | .probe = bmi323_i2c_probe, |
| 114 | 135 | .id_table = bmi323_i2c_ids, |
| @@ -10,7 +10,6 @@ · 9 unmodified lines | ||
| 10 | 10 | * 1 | 0 | 0x1C |
| 11 | 11 | * 1 | 1 | 0x1F |
| 12 | 12 | */ |
| 13 | −#include <linux/acpi.h> | |
| 14 | 13 | #include <linux/i2c.h> |
| 15 | 14 | #include <linux/module.h> |
| 16 | 15 | #include <linux/mod_devicetable.h> |
| @@ -57,7 +56,7 @@ MODULE_DEVICE_TABLE(of, fxos8700_of_match); · 40 unmodified lines | ||
| 57 | 56 | static struct i2c_driver fxos8700_i2c_driver = { |
| 58 | 57 | .driver = { |
| 59 | 58 | .name = "fxos8700_i2c", |
| 60 | − .acpi_match_table = ACPI_PTR(fxos8700_acpi_match), | |
| 59 | + .acpi_match_table = fxos8700_acpi_match, | |
| 61 | 60 | .of_match_table = fxos8700_of_match, |
| 62 | 61 | }, |
| 63 | 62 | .probe = fxos8700_i2c_probe, |
| @@ -2,7 +2,6 @@ · 1 unmodified line | ||
| 2 | 2 | /* |
| 3 | 3 | * FXOS8700 - NXP IMU, SPI bits |
| 4 | 4 | */ |
| 5 | −#include <linux/acpi.h> | |
| 6 | 5 | #include <linux/module.h> |
| 7 | 6 | #include <linux/mod_devicetable.h> |
| 8 | 7 | #include <linux/regmap.h> |
| @@ -46,7 +45,7 @@ static struct spi_driver fxos8700_spi_driver = { · 37 unmodified lines | ||
| 46 | 45 | .probe = fxos8700_spi_probe, |
| 47 | 46 | .id_table = fxos8700_spi_id, |
| 48 | 47 | .driver = { |
| 49 | − .acpi_match_table = ACPI_PTR(fxos8700_acpi_match), | |
| 48 | + .acpi_match_table = fxos8700_acpi_match, | |
| 50 | 49 | .of_match_table = fxos8700_of_match, |
| 51 | 50 | .name = "fxos8700_spi", |
| 52 | 51 | }, |
| @@ -1514,7 +1514,7 @@ MODULE_DEVICE_TABLE(i2c, kmx61_id); · 1513 unmodified lines | ||
| 1514 | 1514 | static struct i2c_driver kmx61_driver = { |
| 1515 | 1515 | .driver = { |
| 1516 | 1516 | .name = KMX61_DRV_NAME, |
| 1517 | − .acpi_match_table = ACPI_PTR(kmx61_acpi_match), | |
| 1517 | + .acpi_match_table = kmx61_acpi_match, | |
| 1518 | 1518 | .pm = pm_ptr(&kmx61_pm_ops), |
| 1519 | 1519 | }, |
| 1520 | 1520 | .probe = kmx61_probe, |
| @@ -11,11 +11,32 @@ config IIO_ST_LSM6DSX · 10 unmodified lines | ||
| 11 | 11 | select IIO_ST_LSM6DSX_I3C if (I3C) |
| 12 | 12 | help |
| 13 | 13 | Say yes here to build support for STMicroelectronics LSM6DSx imu |
| 14 | − sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, | |
| 15 | − ism330dlc, lsm6dso, lsm6dsox, asm330lhh, asm330lhhx, lsm6dsr, | |
| 16 | − lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, lsm6dstx, | |
| 17 | − lsm6dsv, lsm6dsv16x, lsm6dso16is, ism330is, asm330lhb, lsm6dst | |
| 18 | − and the accelerometer/gyroscope of lsm9ds1. | |
| 14 | + sensor. | |
| 15 | + Supported devices: | |
| 16 | + - asm330lhb | |
| 17 | + - asm330lhh | |
| 18 | + - asm330lhhx | |
| 19 | + - asm330lhhxg1 | |
| 20 | + - ism330dhcx | |
| 21 | + - ism330dlc | |
| 22 | + - ism330is | |
| 23 | + - lsm6ds0 | |
| 24 | + - lsm6ds3 | |
| 25 | + - lsm6ds3h | |
| 26 | + - lsm6ds3tr-c | |
| 27 | + - lsm6dsl | |
| 28 | + - lsm6dsm | |
| 29 | + - lsm6dso | |
| 30 | + - lsm6dso16is | |
| 31 | + - lsm6dsop | |
| 32 | + - lsm6dsox | |
| 33 | + - lsm6dsr | |
| 34 | + - lsm6dsrx | |
| 35 | + - lsm6dst | |
| 36 | + - lsm6dstx | |
| 37 | + - lsm6dsv | |
| 38 | + - lsm6dsv16x | |
| 39 | + - lsm9ds1 | |
| 19 | 40 | |
| 20 | 41 | To compile this driver as a module, choose M here: the module |
| 21 | 42 | will be called st_lsm6dsx. |
| @@ -38,6 +38,7 @@ · 37 unmodified lines | ||
| 38 | 38 | #define ST_LSM6DSO16IS_DEV_NAME "lsm6dso16is" |
| 39 | 39 | #define ST_ISM330IS_DEV_NAME "ism330is" |
| 40 | 40 | #define ST_ASM330LHB_DEV_NAME "asm330lhb" |
| 41 | +#define ST_ASM330LHHXG1_DEV_NAME "asm330lhhxg1" | |
| 41 | 42 | |
| 42 | 43 | enum st_lsm6dsx_hw_id { |
| 43 | 44 | ST_LSM6DS3_ID = 1, |
| @@ -63,6 +64,7 @@ enum st_lsm6dsx_hw_id { · 19 unmodified lines | ||
| 63 | 64 | ST_LSM6DSO16IS_ID, |
| 64 | 65 | ST_ISM330IS_ID, |
| 65 | 66 | ST_ASM330LHB_ID, |
| 67 | + ST_ASM330LHHXG1_ID, | |
| 66 | 68 | ST_LSM6DSX_MAX_ID, |
| 67 | 69 | }; |
| 68 | 70 | |
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | /* |
| 3 | 3 | * STMicroelectronics st_lsm6dsx FIFO buffer library driver |
| 4 | 4 | * |
| 5 | − * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C: | |
| 5 | + * Pattern FIFO: | |
| 6 | 6 | * The FIFO buffer can be configured to store data from gyroscope and |
| 7 | 7 | * accelerometer. Samples are queued without any tag according to a |
| 8 | 8 | * specific pattern based on 'FIFO data sets' (6 bytes each): |
| @@ -14,12 +14,34 @@ · 5 unmodified lines | ||
| 14 | 14 | * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the |
| 15 | 15 | * value of the decimation factor and ODR set for each FIFO data set. |
| 16 | 16 | * |
| 17 | − * LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/LSM6DSRX/ISM330DHCX/ | |
| 18 | − * LSM6DST/LSM6DSOP/LSM6DSTX/LSM6DSV/ASM330LHB: | |
| 17 | + * Supported devices: | |
| 18 | + * - ISM330DLC | |
| 19 | + * - LSM6DS3 | |
| 20 | + * - LSM6DS3H | |
| 21 | + * - LSM6DS3TR-C | |
| 22 | + * - LSM6DSL | |
| 23 | + * - LSM6DSM | |
| 24 | + * | |
| 25 | + * Tagged FIFO: | |
| 19 | 26 | * The FIFO buffer can be configured to store data from gyroscope and |
| 20 | 27 | * accelerometer. Each sample is queued with a tag (1B) indicating data |
| 21 | 28 | * source (gyroscope, accelerometer, hw timer). |
| 22 | 29 | * |
| 30 | + * Supported devices: | |
| 31 | + * - ASM330LHB | |
| 32 | + * - ASM330LHH | |
| 33 | + * - ASM330LHHX | |
| 34 | + * - ASM330LHHXG1 | |
| 35 | + * - ISM330DHCX | |
| 36 | + * - LSM6DSO | |
| 37 | + * - LSM6DSOP | |
| 38 | + * - LSM6DSOX | |
| 39 | + * - LSM6DSR | |
| 40 | + * - LSM6DSRX | |
| 41 | + * - LSM6DST | |
| 42 | + * - LSM6DSTX | |
| 43 | + * - LSM6DSV | |
| 44 | + * | |
| 23 | 45 | * FIFO supported modes: |
| 24 | 46 | * - BYPASS: FIFO disabled |
| 25 | 47 | * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index |
| @@ -14,34 +14,51 @@ · 13 unmodified lines | ||
| 14 | 14 | * by a different driver. |
| 15 | 15 | * |
| 16 | 16 | * Supported sensors: |
| 17 | − * - LSM6DS3: | |
| 17 | + * | |
| 18 | + * - LSM6DS3 | |
| 18 | 19 | * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 |
| 19 | 20 | * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 |
| 20 | 21 | * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 |
| 21 | 22 | * - FIFO size: 8KB |
| 22 | 23 | * |
| 23 | − * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C: | |
| 24 | + * - ISM330DLC | |
| 25 | + * - LSM6DS3H | |
| 26 | + * - LSM6DS3TR-C | |
| 27 | + * - LSM6DSL | |
| 28 | + * - LSM6DSM | |
| 24 | 29 | * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 |
| 25 | 30 | * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 |
| 26 | 31 | * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 |
| 27 | 32 | * - FIFO size: 4KB |
| 28 | 33 | * |
| 29 | − * - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP/ | |
| 30 | − * LSM6DSTX/LSM6DSO16IS/ISM330IS: | |
| 34 | + * - ASM330LHH | |
| 35 | + * - ASM330LHHX | |
| 36 | + * - ASM330LHHXG1 | |
| 37 | + * - ISM330DHCX | |
| 38 | + * - ISM330IS | |
| 39 | + * - LSM6DSO | |
| 40 | + * - LSM6DSO16IS | |
| 41 | + * - LSM6DSOP | |
| 42 | + * - LSM6DSOX | |
| 43 | + * - LSM6DSR | |
| 44 | + * - LSM6DST | |
| 45 | + * - LSM6DSTX | |
| 31 | 46 | * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416, |
| 32 | 47 | * 833 |
| 33 | 48 | * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 |
| 34 | 49 | * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 |
| 35 | 50 | * - FIFO size: 3KB |
| 36 | 51 | * |
| 37 | − * - LSM6DSV/LSM6DSV16X: | |
| 52 | + * - LSM6DSV | |
| 53 | + * - LSM6DSV16X | |
| 38 | 54 | * - Accelerometer/Gyroscope supported ODR [Hz]: 7.5, 15, 30, 60, 120, 240, |
| 39 | 55 | * 480, 960 |
| 40 | 56 | * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 |
| 41 | 57 | * - Gyroscope supported full-scale [dps]: +-125/+-250/+-500/+-1000/+-2000 |
| 42 | 58 | * - FIFO size: 3KB |
| 43 | 59 | * |
| 44 | − * - LSM9DS1/LSM6DS0: | |
| 60 | + * - LSM6DS0 | |
| 61 | + * - LSM9DS1 | |
| 45 | 62 | * - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952 |
| 46 | 63 | * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 |
| 47 | 64 | * - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952 |
| @@ -820,6 +837,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { · 772 unmodified lines | ||
| 820 | 837 | .hw_id = ST_ASM330LHHX_ID, |
| 821 | 838 | .name = ST_ASM330LHHX_DEV_NAME, |
| 822 | 839 | .wai = 0x6b, |
| 840 | + }, { | |
| 841 | + .hw_id = ST_ASM330LHHXG1_ID, | |
| 842 | + .name = ST_ASM330LHHXG1_DEV_NAME, | |
| 843 | + .wai = 0x6b, | |
| 823 | 844 | }, { |
| 824 | 845 | .hw_id = ST_LSM6DSTX_ID, |
| 825 | 846 | .name = ST_LSM6DSTX_DEV_NAME, |
| @@ -134,6 +134,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { · 133 unmodified lines | ||
| 134 | 134 | .compatible = "st,asm330lhb", |
| 135 | 135 | .data = (void *)ST_ASM330LHB_ID, |
| 136 | 136 | }, |
| 137 | + { | |
| 138 | + .compatible = "st,asm330lhhxg1", | |
| 139 | + .data = (void *)ST_ASM330LHHXG1_ID, | |
| 140 | + }, | |
| 137 | 141 | {}, |
| 138 | 142 | }; |
| 139 | 143 | MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); |
| @@ -168,6 +172,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { · 28 unmodified lines | ||
| 168 | 172 | { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, |
| 169 | 173 | { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, |
| 170 | 174 | { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, |
| 175 | + { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, | |
| 171 | 176 | {}, |
| 172 | 177 | }; |
| 173 | 178 | MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); |
| @@ -129,6 +129,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { · 128 unmodified lines | ||
| 129 | 129 | .compatible = "st,asm330lhb", |
| 130 | 130 | .data = (void *)ST_ASM330LHB_ID, |
| 131 | 131 | }, |
| 132 | + { | |
| 133 | + .compatible = "st,asm330lhhxg1", | |
| 134 | + .data = (void *)ST_ASM330LHHXG1_ID, | |
| 135 | + }, | |
| 132 | 136 | {}, |
| 133 | 137 | }; |
| 134 | 138 | MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); |
| @@ -157,6 +161,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { · 22 unmodified lines | ||
| 157 | 161 | { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, |
| 158 | 162 | { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, |
| 159 | 163 | { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, |
| 164 | + { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, | |
| 160 | 165 | {}, |
| 161 | 166 | }; |
| 162 | 167 | MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); |
| @@ -4,9 +4,12 @@ · 3 unmodified lines | ||
| 4 | 4 | #ifndef ST_LSM9DS0_H |
| 5 | 5 | #define ST_LSM9DS0_H |
| 6 | 6 | |
| 7 | −struct iio_dev; | |
| 7 | +struct device; | |
| 8 | +struct regmap; | |
| 8 | 9 | struct regulator; |
| 9 | 10 | |
| 11 | +struct iio_dev; | |
| 12 | + | |
| 10 | 13 | struct st_lsm9ds0 { |
| 11 | 14 | struct device *dev; |
| 12 | 15 | const char *name; |
| @@ -7,10 +7,10 @@ · 6 unmodified lines | ||
| 7 | 7 | * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | −#include <linux/device.h> | |
| 10 | +#include <linux/array_size.h> | |
| 11 | +#include <linux/dev_printk.h> | |
| 11 | 12 | #include <linux/err.h> |
| 12 | 13 | #include <linux/module.h> |
| 13 | −#include <linux/regmap.h> | |
| 14 | 14 | #include <linux/regulator/consumer.h> |
| 15 | 15 | |
| 16 | 16 | #include <linux/iio/common/st_sensors.h> |
| @@ -25,10 +25,9 @@ static int st_lsm9ds0_probe_accel(struct st_lsm9ds0 *lsm9ds0, struct regmap *reg · 8 unmodified lines | ||
| 25 | 25 | struct st_sensor_data *data; |
| 26 | 26 | |
| 27 | 27 | settings = st_accel_get_settings(lsm9ds0->name); |
| 28 | − if (!settings) { | |
| 29 | − dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name); | |
| 30 | − return -ENODEV; | |
| 31 | − } | |
| 28 | + if (!settings) | |
| 29 | + return dev_err_probe(dev, -ENODEV, "device name %s not recognized.\n", | |
| 30 | + lsm9ds0->name); | |
| 32 | 31 | |
| 33 | 32 | lsm9ds0->accel = devm_iio_device_alloc(dev, sizeof(*data)); |
| 34 | 33 | if (!lsm9ds0->accel) |
| @@ -51,10 +50,9 @@ static int st_lsm9ds0_probe_magn(struct st_lsm9ds0 *lsm9ds0, struct regmap *regm · 16 unmodified lines | ||
| 51 | 50 | struct st_sensor_data *data; |
| 52 | 51 | |
| 53 | 52 | settings = st_magn_get_settings(lsm9ds0->name); |
| 54 | − if (!settings) { | |
| 55 | − dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name); | |
| 56 | − return -ENODEV; | |
| 57 | − } | |
| 53 | + if (!settings) | |
| 54 | + return dev_err_probe(dev, -ENODEV, "device name %s not recognized.\n", | |
| 55 | + lsm9ds0->name); | |
| 58 | 56 | |
| 59 | 57 | lsm9ds0->magn = devm_iio_device_alloc(dev, sizeof(*data)); |
| 60 | 58 | if (!lsm9ds0->magn) |
| @@ -80,8 +78,7 @@ int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) · 19 unmodified lines | ||
| 80 | 78 | ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), |
| 81 | 79 | regulator_names); |
| 82 | 80 | if (ret) |
| 83 | − return dev_err_probe(dev, ret, | |
| 84 | − "unable to enable Vdd supply\n"); | |
| 81 | + return dev_err_probe(dev, ret, "unable to enable Vdd supply\n"); | |
| 85 | 82 | |
| 86 | 83 | /* Setup accelerometer device */ |
| 87 | 84 | ret = st_lsm9ds0_probe_accel(lsm9ds0, regmap); |
| @@ -7,8 +7,10 @@ · 6 unmodified lines | ||
| 7 | 7 | * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | +#include <linux/device.h> | |
| 11 | +#include <linux/err.h> | |
| 12 | +#include <linux/gfp_types.h> | |
| 10 | 13 | #include <linux/i2c.h> |
| 11 | −#include <linux/kernel.h> | |
| 12 | 14 | #include <linux/module.h> |
| 13 | 15 | #include <linux/mod_devicetable.h> |
| 14 | 16 | #include <linux/regmap.h> |
| @@ -39,7 +41,7 @@ MODULE_DEVICE_TABLE(i2c, st_lsm9ds0_id_table); · 24 unmodified lines | ||
| 39 | 41 | |
| 40 | 42 | static const struct acpi_device_id st_lsm9ds0_acpi_match[] = { |
| 41 | 43 | {"ACCL0001", (kernel_ulong_t)LSM303D_IMU_DEV_NAME}, |
| 42 | − { }, | |
| 44 | + {} | |
| 43 | 45 | }; |
| 44 | 46 | MODULE_DEVICE_TABLE(acpi, st_lsm9ds0_acpi_match); |
| 45 | 47 | |
| @@ -7,7 +7,9 @@ · 6 unmodified lines | ||
| 7 | 7 | * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | −#include <linux/kernel.h> | |
| 10 | +#include <linux/device.h> | |
| 11 | +#include <linux/err.h> | |
| 12 | +#include <linux/gfp_types.h> | |
| 11 | 13 | #include <linux/module.h> |
| 12 | 14 | #include <linux/mod_devicetable.h> |
| 13 | 15 | #include <linux/regmap.h> |
| @@ -0,0 +1,418 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Framework to handle complex IIO aggregate devices. | |
| 4 | + * | |
| 5 | + * The typical architecture is to have one device as the frontend device which | |
| 6 | + * can be "linked" against one or multiple backend devices. All the IIO and | |
| 7 | + * userspace interface is expected to be registers/managed by the frontend | |
| 8 | + * device which will callback into the backends when needed (to get/set some | |
| 9 | + * configuration that it does not directly control). | |
| 10 | + * | |
| 11 | + * ------------------------------------------------------- | |
| 12 | + * ------------------ | ------------ ------------ ------- FPGA| | |
| 13 | + * | ADC |------------------------| | ADC CORE |---------| DMA CORE |------| RAM | | | |
| 14 | + * | (Frontend/IIO) | Serial Data (eg: LVDS) | |(backend) |---------| |------| | | | |
| 15 | + * | |------------------------| ------------ ------------ ------- | | |
| 16 | + * ------------------ ------------------------------------------------------- | |
| 17 | + * | |
| 18 | + * The framework interface is pretty simple: | |
| 19 | + * - Backends should register themselves with devm_iio_backend_register() | |
| 20 | + * - Frontend devices should get backends with devm_iio_backend_get() | |
| 21 | + * | |
| 22 | + * Also to note that the primary target for this framework are converters like | |
| 23 | + * ADC/DACs so iio_backend_ops will have some operations typical of converter | |
| 24 | + * devices. On top of that, this is "generic" for all IIO which means any kind | |
| 25 | + * of device can make use of the framework. That said, If the iio_backend_ops | |
| 26 | + * struct begins to grow out of control, we can always refactor things so that | |
| 27 | + * the industrialio-backend.c is only left with the really generic stuff. Then, | |
| 28 | + * we can build on top of it depending on the needs. | |
| 29 | + * | |
| 30 | + * Copyright (C) 2023-2024 Analog Devices Inc. | |
| 31 | + */ | |
| 32 | +#define dev_fmt(fmt) "iio-backend: " fmt | |
| 33 | + | |
| 34 | +#include <linux/cleanup.h> | |
| 35 | +#include <linux/device.h> | |
| 36 | +#include <linux/err.h> | |
| 37 | +#include <linux/errno.h> | |
| 38 | +#include <linux/list.h> | |
| 39 | +#include <linux/module.h> | |
| 40 | +#include <linux/mutex.h> | |
| 41 | +#include <linux/property.h> | |
| 42 | +#include <linux/slab.h> | |
| 43 | +#include <linux/types.h> | |
| 44 | + | |
| 45 | +#include <linux/iio/backend.h> | |
| 46 | + | |
| 47 | +struct iio_backend { | |
| 48 | + struct list_head entry; | |
| 49 | + const struct iio_backend_ops *ops; | |
| 50 | + struct device *dev; | |
| 51 | + struct module *owner; | |
| 52 | + void *priv; | |
| 53 | +}; | |
| 54 | + | |
| 55 | +/* | |
| 56 | + * Helper struct for requesting buffers. This ensures that we have all data | |
| 57 | + * that we need to free the buffer in a device managed action. | |
| 58 | + */ | |
| 59 | +struct iio_backend_buffer_pair { | |
| 60 | + struct iio_backend *back; | |
| 61 | + struct iio_buffer *buffer; | |
| 62 | +}; | |
| 63 | + | |
| 64 | +static LIST_HEAD(iio_back_list); | |
| 65 | +static DEFINE_MUTEX(iio_back_lock); | |
| 66 | + | |
| 67 | +/* | |
| 68 | + * Helper macros to call backend ops. Makes sure the option is supported. | |
| 69 | + */ | |
| 70 | +#define iio_backend_check_op(back, op) ({ \ | |
| 71 | + struct iio_backend *____back = back; \ | |
| 72 | + int ____ret = 0; \ | |
| 73 | + \ | |
| 74 | + if (!____back->ops->op) \ | |
| 75 | + ____ret = -EOPNOTSUPP; \ | |
| 76 | + \ | |
| 77 | + ____ret; \ | |
| 78 | +}) | |
| 79 | + | |
| 80 | +#define iio_backend_op_call(back, op, args...) ({ \ | |
| 81 | + struct iio_backend *__back = back; \ | |
| 82 | + int __ret; \ | |
| 83 | + \ | |
| 84 | + __ret = iio_backend_check_op(__back, op); \ | |
| 85 | + if (!__ret) \ | |
| 86 | + __ret = __back->ops->op(__back, ##args); \ | |
| 87 | + \ | |
| 88 | + __ret; \ | |
| 89 | +}) | |
| 90 | + | |
| 91 | +#define iio_backend_ptr_op_call(back, op, args...) ({ \ | |
| 92 | + struct iio_backend *__back = back; \ | |
| 93 | + void *ptr_err; \ | |
| 94 | + int __ret; \ | |
| 95 | + \ | |
| 96 | + __ret = iio_backend_check_op(__back, op); \ | |
| 97 | + if (__ret) \ | |
| 98 | + ptr_err = ERR_PTR(__ret); \ | |
| 99 | + else \ | |
| 100 | + ptr_err = __back->ops->op(__back, ##args); \ | |
| 101 | + \ | |
| 102 | + ptr_err; \ | |
| 103 | +}) | |
| 104 | + | |
| 105 | +#define iio_backend_void_op_call(back, op, args...) { \ | |
| 106 | + struct iio_backend *__back = back; \ | |
| 107 | + int __ret; \ | |
| 108 | + \ | |
| 109 | + __ret = iio_backend_check_op(__back, op); \ | |
| 110 | + if (!__ret) \ | |
| 111 | + __back->ops->op(__back, ##args); \ | |
| 112 | +} | |
| 113 | + | |
| 114 | +/** | |
| 115 | + * iio_backend_chan_enable - Enable a backend channel | |
| 116 | + * @back: Backend device | |
| 117 | + * @chan: Channel number | |
| 118 | + * | |
| 119 | + * RETURNS: | |
| 120 | + * 0 on success, negative error number on failure. | |
| 121 | + */ | |
| 122 | +int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan) | |
| 123 | +{ | |
| 124 | + return iio_backend_op_call(back, chan_enable, chan); | |
| 125 | +} | |
| 126 | +EXPORT_SYMBOL_NS_GPL(iio_backend_chan_enable, IIO_BACKEND); | |
| 127 | + | |
| 128 | +/** | |
| 129 | + * iio_backend_chan_disable - Disable a backend channel | |
| 130 | + * @back: Backend device | |
| 131 | + * @chan: Channel number | |
| 132 | + * | |
| 133 | + * RETURNS: | |
| 134 | + * 0 on success, negative error number on failure. | |
| 135 | + */ | |
| 136 | +int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan) | |
| 137 | +{ | |
| 138 | + return iio_backend_op_call(back, chan_disable, chan); | |
| 139 | +} | |
| 140 | +EXPORT_SYMBOL_NS_GPL(iio_backend_chan_disable, IIO_BACKEND); | |
| 141 | + | |
| 142 | +static void __iio_backend_disable(void *back) | |
| 143 | +{ | |
| 144 | + iio_backend_void_op_call(back, disable); | |
| 145 | +} | |
| 146 | + | |
| 147 | +/** | |
| 148 | + * devm_iio_backend_enable - Device managed backend enable | |
| 149 | + * @dev: Consumer device for the backend | |
| 150 | + * @back: Backend device | |
| 151 | + * | |
| 152 | + * RETURNS: | |
| 153 | + * 0 on success, negative error number on failure. | |
| 154 | + */ | |
| 155 | +int devm_iio_backend_enable(struct device *dev, struct iio_backend *back) | |
| 156 | +{ | |
| 157 | + int ret; | |
| 158 | + | |
| 159 | + ret = iio_backend_op_call(back, enable); | |
| 160 | + if (ret) | |
| 161 | + return ret; | |
| 162 | + | |
| 163 | + return devm_add_action_or_reset(dev, __iio_backend_disable, back); | |
| 164 | +} | |
| 165 | +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_enable, IIO_BACKEND); | |
| 166 | + | |
| 167 | +/** | |
| 168 | + * iio_backend_data_format_set - Configure the channel data format | |
| 169 | + * @back: Backend device | |
| 170 | + * @chan: Channel number | |
| 171 | + * @data: Data format | |
| 172 | + * | |
| 173 | + * Properly configure a channel with respect to the expected data format. A | |
| 174 | + * @struct iio_backend_data_fmt must be passed with the settings. | |
| 175 | + * | |
| 176 | + * RETURNS: | |
| 177 | + * 0 on success, negative error number on failure. | |
| 178 | + */ | |
| 179 | +int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan, | |
| 180 | + const struct iio_backend_data_fmt *data) | |
| 181 | +{ | |
| 182 | + if (!data || data->type >= IIO_BACKEND_DATA_TYPE_MAX) | |
| 183 | + return -EINVAL; | |
| 184 | + | |
| 185 | + return iio_backend_op_call(back, data_format_set, chan, data); | |
| 186 | +} | |
| 187 | +EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND); | |
| 188 | + | |
| 189 | +static void iio_backend_free_buffer(void *arg) | |
| 190 | +{ | |
| 191 | + struct iio_backend_buffer_pair *pair = arg; | |
| 192 | + | |
| 193 | + iio_backend_void_op_call(pair->back, free_buffer, pair->buffer); | |
| 194 | +} | |
| 195 | + | |
| 196 | +/** | |
| 197 | + * devm_iio_backend_request_buffer - Device managed buffer request | |
| 198 | + * @dev: Consumer device for the backend | |
| 199 | + * @back: Backend device | |
| 200 | + * @indio_dev: IIO device | |
| 201 | + * | |
| 202 | + * Request an IIO buffer from the backend. The type of the buffer (typically | |
| 203 | + * INDIO_BUFFER_HARDWARE) is up to the backend to decide. This is because, | |
| 204 | + * normally, the backend dictates what kind of buffering we can get. | |
| 205 | + * | |
| 206 | + * The backend .free_buffer() hooks is automatically called on @dev detach. | |
| 207 | + * | |
| 208 | + * RETURNS: | |
| 209 | + * 0 on success, negative error number on failure. | |
| 210 | + */ | |
| 211 | +int devm_iio_backend_request_buffer(struct device *dev, | |
| 212 | + struct iio_backend *back, | |
| 213 | + struct iio_dev *indio_dev) | |
| 214 | +{ | |
| 215 | + struct iio_backend_buffer_pair *pair; | |
| 216 | + struct iio_buffer *buffer; | |
| 217 | + | |
| 218 | + pair = devm_kzalloc(dev, sizeof(*pair), GFP_KERNEL); | |
| 219 | + if (!pair) | |
| 220 | + return -ENOMEM; | |
| 221 | + | |
| 222 | + buffer = iio_backend_ptr_op_call(back, request_buffer, indio_dev); | |
| 223 | + if (IS_ERR(buffer)) | |
| 224 | + return PTR_ERR(buffer); | |
| 225 | + | |
| 226 | + /* weak reference should be all what we need */ | |
| 227 | + pair->back = back; | |
| 228 | + pair->buffer = buffer; | |
| 229 | + | |
| 230 | + return devm_add_action_or_reset(dev, iio_backend_free_buffer, pair); | |
| 231 | +} | |
| 232 | +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND); | |
| 233 | + | |
| 234 | +static void iio_backend_release(void *arg) | |
| 235 | +{ | |
| 236 | + struct iio_backend *back = arg; | |
| 237 | + | |
| 238 | + module_put(back->owner); | |
| 239 | +} | |
| 240 | + | |
| 241 | +static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) | |
| 242 | +{ | |
| 243 | + struct device_link *link; | |
| 244 | + int ret; | |
| 245 | + | |
| 246 | + /* | |
| 247 | + * Make sure the provider cannot be unloaded before the consumer module. | |
| 248 | + * Note that device_links would still guarantee that nothing is | |
| 249 | + * accessible (and breaks) but this makes it explicit that the consumer | |
| 250 | + * module must be also unloaded. | |
| 251 | + */ | |
| 252 | + if (!try_module_get(back->owner)) | |
| 253 | + return dev_err_probe(dev, -ENODEV, | |
| 254 | + "Cannot get module reference\n"); | |
| 255 | + | |
| 256 | + ret = devm_add_action_or_reset(dev, iio_backend_release, back); | |
| 257 | + if (ret) | |
| 258 | + return ret; | |
| 259 | + | |
| 260 | + link = device_link_add(dev, back->dev, DL_FLAG_AUTOREMOVE_CONSUMER); | |
| 261 | + if (!link) | |
| 262 | + return dev_err_probe(dev, -EINVAL, | |
| 263 | + "Could not link to supplier(%s)\n", | |
| 264 | + dev_name(back->dev)); | |
| 265 | + | |
| 266 | + dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev)); | |
| 267 | + | |
| 268 | + return 0; | |
| 269 | +} | |
| 270 | + | |
| 271 | +/** | |
| 272 | + * devm_iio_backend_get - Device managed backend device get | |
| 273 | + * @dev: Consumer device for the backend | |
| 274 | + * @name: Backend name | |
| 275 | + * | |
| 276 | + * Get's the backend associated with @dev. | |
| 277 | + * | |
| 278 | + * RETURNS: | |
| 279 | + * A backend pointer, negative error pointer otherwise. | |
| 280 | + */ | |
| 281 | +struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name) | |
| 282 | +{ | |
| 283 | + struct fwnode_handle *fwnode; | |
| 284 | + struct iio_backend *back; | |
| 285 | + unsigned int index; | |
| 286 | + int ret; | |
| 287 | + | |
| 288 | + if (name) { | |
| 289 | + ret = device_property_match_string(dev, "io-backend-names", | |
| 290 | + name); | |
| 291 | + if (ret < 0) | |
| 292 | + return ERR_PTR(ret); | |
| 293 | + index = ret; | |
| 294 | + } else { | |
| 295 | + index = 0; | |
| 296 | + } | |
| 297 | + | |
| 298 | + fwnode = fwnode_find_reference(dev_fwnode(dev), "io-backends", index); | |
| 299 | + if (IS_ERR(fwnode)) { | |
| 300 | + dev_err_probe(dev, PTR_ERR(fwnode), | |
| 301 | + "Cannot get Firmware reference\n"); | |
| 302 | + return ERR_CAST(fwnode); | |
| 303 | + } | |
| 304 | + | |
| 305 | + guard(mutex)(&iio_back_lock); | |
| 306 | + list_for_each_entry(back, &iio_back_list, entry) { | |
| 307 | + if (!device_match_fwnode(back->dev, fwnode)) | |
| 308 | + continue; | |
| 309 | + | |
| 310 | + fwnode_handle_put(fwnode); | |
| 311 | + ret = __devm_iio_backend_get(dev, back); | |
| 312 | + if (ret) | |
| 313 | + return ERR_PTR(ret); | |
| 314 | + | |
| 315 | + return back; | |
| 316 | + } | |
| 317 | + | |
| 318 | + fwnode_handle_put(fwnode); | |
| 319 | + return ERR_PTR(-EPROBE_DEFER); | |
| 320 | +} | |
| 321 | +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, IIO_BACKEND); | |
| 322 | + | |
| 323 | +/** | |
| 324 | + * __devm_iio_backend_get_from_fwnode_lookup - Device managed fwnode backend device get | |
| 325 | + * @dev: Consumer device for the backend | |
| 326 | + * @fwnode: Firmware node of the backend device | |
| 327 | + * | |
| 328 | + * Search the backend list for a device matching @fwnode. | |
| 329 | + * This API should not be used and it's only present for preventing the first | |
| 330 | + * user of this framework to break it's DT ABI. | |
| 331 | + * | |
| 332 | + * RETURNS: | |
| 333 | + * A backend pointer, negative error pointer otherwise. | |
| 334 | + */ | |
| 335 | +struct iio_backend * | |
| 336 | +__devm_iio_backend_get_from_fwnode_lookup(struct device *dev, | |
| 337 | + struct fwnode_handle *fwnode) | |
| 338 | +{ | |
| 339 | + struct iio_backend *back; | |
| 340 | + int ret; | |
| 341 | + | |
| 342 | + guard(mutex)(&iio_back_lock); | |
| 343 | + list_for_each_entry(back, &iio_back_list, entry) { | |
| 344 | + if (!device_match_fwnode(back->dev, fwnode)) | |
| 345 | + continue; | |
| 346 | + | |
| 347 | + ret = __devm_iio_backend_get(dev, back); | |
| 348 | + if (ret) | |
| 349 | + return ERR_PTR(ret); | |
| 350 | + | |
| 351 | + return back; | |
| 352 | + } | |
| 353 | + | |
| 354 | + return ERR_PTR(-EPROBE_DEFER); | |
| 355 | +} | |
| 356 | +EXPORT_SYMBOL_NS_GPL(__devm_iio_backend_get_from_fwnode_lookup, IIO_BACKEND); | |
| 357 | + | |
| 358 | +/** | |
| 359 | + * iio_backend_get_priv - Get driver private data | |
| 360 | + * @back: Backend device | |
| 361 | + */ | |
| 362 | +void *iio_backend_get_priv(const struct iio_backend *back) | |
| 363 | +{ | |
| 364 | + return back->priv; | |
| 365 | +} | |
| 366 | +EXPORT_SYMBOL_NS_GPL(iio_backend_get_priv, IIO_BACKEND); | |
| 367 | + | |
| 368 | +static void iio_backend_unregister(void *arg) | |
| 369 | +{ | |
| 370 | + struct iio_backend *back = arg; | |
| 371 | + | |
| 372 | + guard(mutex)(&iio_back_lock); | |
| 373 | + list_del(&back->entry); | |
| 374 | +} | |
| 375 | + | |
| 376 | +/** | |
| 377 | + * devm_iio_backend_register - Device managed backend device register | |
| 378 | + * @dev: Backend device being registered | |
| 379 | + * @ops: Backend ops | |
| 380 | + * @priv: Device private data | |
| 381 | + * | |
| 382 | + * @ops is mandatory. Not providing it results in -EINVAL. | |
| 383 | + * | |
| 384 | + * RETURNS: | |
| 385 | + * 0 on success, negative error number on failure. | |
| 386 | + */ | |
| 387 | +int devm_iio_backend_register(struct device *dev, | |
| 388 | + const struct iio_backend_ops *ops, void *priv) | |
| 389 | +{ | |
| 390 | + struct iio_backend *back; | |
| 391 | + | |
| 392 | + if (!ops) | |
| 393 | + return dev_err_probe(dev, -EINVAL, "No backend ops given\n"); | |
| 394 | + | |
| 395 | + /* | |
| 396 | + * Through device_links, we guarantee that a frontend device cannot be | |
| 397 | + * bound/exist if the backend driver is not around. Hence, we can bind | |
| 398 | + * the backend object lifetime with the device being passed since | |
| 399 | + * removing it will tear the frontend/consumer down. | |
| 400 | + */ | |
| 401 | + back = devm_kzalloc(dev, sizeof(*back), GFP_KERNEL); | |
| 402 | + if (!back) | |
| 403 | + return -ENOMEM; | |
| 404 | + | |
| 405 | + back->ops = ops; | |
| 406 | + back->owner = dev->driver->owner; | |
| 407 | + back->dev = dev; | |
| 408 | + back->priv = priv; | |
| 409 | + scoped_guard(mutex, &iio_back_lock) | |
| 410 | + list_add(&back->entry, &iio_back_list); | |
| 411 | + | |
| 412 | + return devm_add_action_or_reset(dev, iio_backend_unregister, back); | |
| 413 | +} | |
| 414 | +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_register, IIO_BACKEND); | |
| 415 | + | |
| 416 | +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); | |
| 417 | +MODULE_DESCRIPTION("Framework to handle complex IIO aggregate devices"); | |
| 418 | +MODULE_LICENSE("GPL"); | |
| @@ -42,7 +42,7 @@ static DEFINE_IDA(iio_ida); · 41 unmodified lines | ||
| 42 | 42 | static dev_t iio_devt; |
| 43 | 43 | |
| 44 | 44 | #define IIO_DEV_MAX 256 |
| 45 | −struct bus_type iio_bus_type = { | |
| 45 | +const struct bus_type iio_bus_type = { | |
| 46 | 46 | .name = "iio", |
| 47 | 47 | }; |
| 48 | 48 | EXPORT_SYMBOL(iio_bus_type); |
| @@ -213,9 +213,7 @@ bool iio_buffer_enabled(struct iio_dev *indio_dev) · 164 unmodified lines | ||
| 213 | 213 | { |
| 214 | 214 | struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); |
| 215 | 215 | |
| 216 | − return iio_dev_opaque->currentmode & | |
| 217 | − (INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE | | |
| 218 | − INDIO_BUFFER_TRIGGERED); | |
| 216 | + return iio_dev_opaque->currentmode & INDIO_ALL_BUFFER_MODES; | |
| 219 | 217 | } |
| 220 | 218 | EXPORT_SYMBOL_GPL(iio_buffer_enabled); |
| 221 | 219 | |
| @@ -34,24 +34,11 @@ · 33 unmodified lines | ||
| 34 | 34 | static int iio_gts_get_gain(const u64 max, const u64 scale) |
| 35 | 35 | { |
| 36 | 36 | u64 full = max; |
| 37 | − int tmp = 1; | |
| 38 | 37 | |
| 39 | 38 | if (scale > full || !scale) |
| 40 | 39 | return -EINVAL; |
| 41 | 40 | |
| 42 | − if (U64_MAX - full < scale) { | |
| 43 | − /* Risk of overflow */ | |
| 44 | − if (full - scale < scale) | |
| 45 | − return 1; | |
| 46 | − | |
| 47 | − full -= scale; | |
| 48 | − tmp++; | |
| 49 | − } | |
| 50 | − | |
| 51 | − while (full > scale * (u64)tmp) | |
| 52 | − tmp++; | |
| 53 | − | |
| 54 | − return tmp; | |
| 41 | + return div64_u64(full, scale); | |
| 55 | 42 | } |
| 56 | 43 | |
| 57 | 44 | /** |
| @@ -87,13 +87,14 @@ config APDS9960 · 86 unmodified lines | ||
| 87 | 87 | module will be called apds9960 |
| 88 | 88 | |
| 89 | 89 | config AS73211 |
| 90 | − tristate "AMS AS73211 XYZ color sensor" | |
| 90 | + tristate "AMS AS73211 XYZ color sensor and AMS AS7331 UV sensor" | |
| 91 | 91 | depends on I2C |
| 92 | 92 | select IIO_BUFFER |
| 93 | 93 | select IIO_TRIGGERED_BUFFER |
| 94 | 94 | help |
| 95 | 95 | If you say yes here you get support for the AMS AS73211 |
| 96 | − JENCOLOR(R) Digital XYZ Sensor. | |
| 96 | + JENCOLOR(R) Digital XYZ and the AMS AS7331 UVA, UVB and UVC | |
| 97 | + ultraviolet sensors. | |
| 97 | 98 | |
| 98 | 99 | For triggered measurements, you will need an additional trigger driver |
| 99 | 100 | like IIO_HRTIMER_TRIGGER or IIO_SYSFS_TRIGGER. |
| @@ -17,7 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <linux/bitfield.h> |
| 18 | 18 | #include <linux/i2c.h> |
| 19 | 19 | #include <linux/module.h> |
| 20 | −#include <linux/of.h> | |
| 20 | +#include <linux/mod_devicetable.h> | |
| 21 | 21 | |
| 22 | 22 | #include <linux/iio/iio.h> |
| 23 | 23 | #include <linux/iio/sysfs.h> |
| @@ -15,7 +15,6 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/bitfield.h> |
| 16 | 16 | #include <linux/i2c.h> |
| 17 | 17 | #include <linux/module.h> |
| 18 | −#include <linux/of.h> | |
| 19 | 18 | #include <linux/mod_devicetable.h> |
| 20 | 19 | |
| 21 | 20 | #include <linux/iio/iio.h> |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | /* |
| 3 | − * Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor | |
| 3 | + * Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor and AMS AS7331 | |
| 4 | + * UVA, UVB and UVC (DUV) Ultraviolet Sensor | |
| 4 | 5 | * |
| 5 | 6 | * Author: Christian Eggers <ceggers@arri.de> |
| 6 | 7 | * |
| @@ -9,7 +10,9 @@ · 2 unmodified lines | ||
| 9 | 10 | * Color light sensor with 16-bit channels for x, y, z and temperature); |
| 10 | 11 | * 7-bit I2C slave address 0x74 .. 0x77. |
| 11 | 12 | * |
| 12 | − * Datasheet: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf | |
| 13 | + * Datasheets: | |
| 14 | + * AS73211: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf | |
| 15 | + * AS7331: https://ams.com/documents/20143/9106314/AS7331_DS001047_4-00.pdf | |
| 13 | 16 | */ |
| 14 | 17 | |
| 15 | 18 | #include <linux/bitfield.h> |
| @@ -84,6 +87,20 @@ static const int as73211_hardwaregain_avail[] = { · 68 unmodified lines | ||
| 84 | 87 | 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, |
| 85 | 88 | }; |
| 86 | 89 | |
| 90 | +struct as73211_data; | |
| 91 | + | |
| 92 | +/** | |
| 93 | + * struct as73211_spec_dev_data - device-specific data | |
| 94 | + * @intensity_scale: Function to retrieve intensity scale values. | |
| 95 | + * @channels: Device channels. | |
| 96 | + * @num_channels: Number of channels of the device. | |
| 97 | + */ | |
| 98 | +struct as73211_spec_dev_data { | |
| 99 | + int (*intensity_scale)(struct as73211_data *data, int chan, int *val, int *val2); | |
| 100 | + struct iio_chan_spec const *channels; | |
| 101 | + int num_channels; | |
| 102 | +}; | |
| 103 | + | |
| 87 | 104 | /** |
| 88 | 105 | * struct as73211_data - Instance data for one AS73211 |
| 89 | 106 | * @client: I2C client. |
| @@ -94,6 +111,7 @@ static const int as73211_hardwaregain_avail[] = { · 4 unmodified lines | ||
| 94 | 111 | * @mutex: Keeps cached registers in sync with the device. |
| 95 | 112 | * @completion: Completion to wait for interrupt. |
| 96 | 113 | * @int_time_avail: Available integration times (depend on sampling frequency). |
| 114 | + * @spec_dev: device-specific configuration. | |
| 97 | 115 | */ |
| 98 | 116 | struct as73211_data { |
| 99 | 117 | struct i2c_client *client; |
| @@ -104,6 +122,7 @@ struct as73211_data { · 4 unmodified lines | ||
| 104 | 122 | struct mutex mutex; |
| 105 | 123 | struct completion completion; |
| 106 | 124 | int int_time_avail[AS73211_SAMPLE_TIME_NUM * 2]; |
| 125 | + const struct as73211_spec_dev_data *spec_dev; | |
| 107 | 126 | }; |
| 108 | 127 | |
| 109 | 128 | #define AS73211_COLOR_CHANNEL(_color, _si, _addr) { \ |
| @@ -138,6 +157,10 @@ struct as73211_data { · 28 unmodified lines | ||
| 138 | 157 | #define AS73211_SCALE_Y 298384270 /* nW/m^2 */ |
| 139 | 158 | #define AS73211_SCALE_Z 160241927 /* nW/m^2 */ |
| 140 | 159 | |
| 160 | +#define AS7331_SCALE_UVA 340000 /* nW/cm^2 */ | |
| 161 | +#define AS7331_SCALE_UVB 378000 /* nW/cm^2 */ | |
| 162 | +#define AS7331_SCALE_UVC 166000 /* nW/cm^2 */ | |
| 163 | + | |
| 141 | 164 | /* Channel order MUST match devices result register order */ |
| 142 | 165 | #define AS73211_SCAN_INDEX_TEMP 0 |
| 143 | 166 | #define AS73211_SCAN_INDEX_X 1 |
| @@ -176,6 +199,28 @@ static const struct iio_chan_spec as73211_channels[] = { · 32 unmodified lines | ||
| 176 | 199 | IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS), |
| 177 | 200 | }; |
| 178 | 201 | |
| 202 | +static const struct iio_chan_spec as7331_channels[] = { | |
| 203 | + { | |
| 204 | + .type = IIO_TEMP, | |
| 205 | + .info_mask_separate = | |
| 206 | + BIT(IIO_CHAN_INFO_RAW) | | |
| 207 | + BIT(IIO_CHAN_INFO_OFFSET) | | |
| 208 | + BIT(IIO_CHAN_INFO_SCALE), | |
| 209 | + .address = AS73211_OUT_TEMP, | |
| 210 | + .scan_index = AS73211_SCAN_INDEX_TEMP, | |
| 211 | + .scan_type = { | |
| 212 | + .sign = 'u', | |
| 213 | + .realbits = 16, | |
| 214 | + .storagebits = 16, | |
| 215 | + .endianness = IIO_LE, | |
| 216 | + } | |
| 217 | + }, | |
| 218 | + AS73211_COLOR_CHANNEL(LIGHT_UVA, AS73211_SCAN_INDEX_X, AS73211_OUT_MRES1), | |
| 219 | + AS73211_COLOR_CHANNEL(LIGHT_UVB, AS73211_SCAN_INDEX_Y, AS73211_OUT_MRES2), | |
| 220 | + AS73211_COLOR_CHANNEL(LIGHT_DUV, AS73211_SCAN_INDEX_Z, AS73211_OUT_MRES3), | |
| 221 | + IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS), | |
| 222 | +}; | |
| 223 | + | |
| 179 | 224 | static unsigned int as73211_integration_time_1024cyc(struct as73211_data *data) |
| 180 | 225 | { |
| 181 | 226 | /* |
| @@ -316,6 +361,48 @@ static int as73211_req_data(struct as73211_data *data) · 134 unmodified lines | ||
| 316 | 361 | return 0; |
| 317 | 362 | } |
| 318 | 363 | |
| 364 | +static int as73211_intensity_scale(struct as73211_data *data, int chan, | |
| 365 | + int *val, int *val2) | |
| 366 | +{ | |
| 367 | + switch (chan) { | |
| 368 | + case IIO_MOD_X: | |
| 369 | + *val = AS73211_SCALE_X; | |
| 370 | + break; | |
| 371 | + case IIO_MOD_Y: | |
| 372 | + *val = AS73211_SCALE_Y; | |
| 373 | + break; | |
| 374 | + case IIO_MOD_Z: | |
| 375 | + *val = AS73211_SCALE_Z; | |
| 376 | + break; | |
| 377 | + default: | |
| 378 | + return -EINVAL; | |
| 379 | + } | |
| 380 | + *val2 = as73211_integration_time_1024cyc(data) * as73211_gain(data); | |
| 381 | + | |
| 382 | + return IIO_VAL_FRACTIONAL; | |
| 383 | +} | |
| 384 | + | |
| 385 | +static int as7331_intensity_scale(struct as73211_data *data, int chan, | |
| 386 | + int *val, int *val2) | |
| 387 | +{ | |
| 388 | + switch (chan) { | |
| 389 | + case IIO_MOD_LIGHT_UVA: | |
| 390 | + *val = AS7331_SCALE_UVA; | |
| 391 | + break; | |
| 392 | + case IIO_MOD_LIGHT_UVB: | |
| 393 | + *val = AS7331_SCALE_UVB; | |
| 394 | + break; | |
| 395 | + case IIO_MOD_LIGHT_DUV: | |
| 396 | + *val = AS7331_SCALE_UVC; | |
| 397 | + break; | |
| 398 | + default: | |
| 399 | + return -EINVAL; | |
| 400 | + } | |
| 401 | + *val2 = as73211_integration_time_1024cyc(data) * as73211_gain(data); | |
| 402 | + | |
| 403 | + return IIO_VAL_FRACTIONAL; | |
| 404 | +} | |
| 405 | + | |
| 319 | 406 | static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, |
| 320 | 407 | int *val, int *val2, long mask) |
| 321 | 408 | { |
| @@ -355,30 +442,13 @@ static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec cons · 33 unmodified lines | ||
| 355 | 442 | *val2 = AS73211_SCALE_TEMP_MICRO; |
| 356 | 443 | return IIO_VAL_INT_PLUS_MICRO; |
| 357 | 444 | |
| 358 | − case IIO_INTENSITY: { | |
| 359 | − unsigned int scale; | |
| 360 | − | |
| 361 | − switch (chan->channel2) { | |
| 362 | − case IIO_MOD_X: | |
| 363 | − scale = AS73211_SCALE_X; | |
| 364 | − break; | |
| 365 | − case IIO_MOD_Y: | |
| 366 | − scale = AS73211_SCALE_Y; | |
| 367 | − break; | |
| 368 | − case IIO_MOD_Z: | |
| 369 | − scale = AS73211_SCALE_Z; | |
| 370 | − break; | |
| 371 | − default: | |
| 372 | − return -EINVAL; | |
| 373 | − } | |
| 374 | − scale /= as73211_gain(data); | |
| 375 | − scale /= as73211_integration_time_1024cyc(data); | |
| 376 | − *val = scale; | |
| 377 | − return IIO_VAL_INT; | |
| 445 | + case IIO_INTENSITY: | |
| 446 | + return data->spec_dev->intensity_scale(data, chan->channel2, | |
| 447 | + val, val2); | |
| 378 | 448 | |
| 379 | 449 | default: |
| 380 | 450 | return -EINVAL; |
| 381 | − }} | |
| 451 | + } | |
| 382 | 452 | |
| 383 | 453 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 384 | 454 | /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */ |
| @@ -676,13 +746,17 @@ static int as73211_probe(struct i2c_client *client) · 291 unmodified lines | ||
| 676 | 746 | i2c_set_clientdata(client, indio_dev); |
| 677 | 747 | data->client = client; |
| 678 | 748 | |
| 749 | + data->spec_dev = i2c_get_match_data(client); | |
| 750 | + if (!data->spec_dev) | |
| 751 | + return -EINVAL; | |
| 752 | + | |
| 679 | 753 | mutex_init(&data->mutex); |
| 680 | 754 | init_completion(&data->completion); |
| 681 | 755 | |
| 682 | 756 | indio_dev->info = &as73211_info; |
| 683 | 757 | indio_dev->name = AS73211_DRV_NAME; |
| 684 | − indio_dev->channels = as73211_channels; | |
| 685 | − indio_dev->num_channels = ARRAY_SIZE(as73211_channels); | |
| 758 | + indio_dev->channels = data->spec_dev->channels; | |
| 759 | + indio_dev->num_channels = data->spec_dev->num_channels; | |
| 686 | 760 | indio_dev->modes = INDIO_DIRECT_MODE; |
| 687 | 761 | |
| 688 | 762 | ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR); |
| @@ -772,14 +846,28 @@ static int as73211_resume(struct device *dev) · 83 unmodified lines | ||
| 772 | 846 | static DEFINE_SIMPLE_DEV_PM_OPS(as73211_pm_ops, as73211_suspend, |
| 773 | 847 | as73211_resume); |
| 774 | 848 | |
| 849 | +static const struct as73211_spec_dev_data as73211_spec = { | |
| 850 | + .intensity_scale = as73211_intensity_scale, | |
| 851 | + .channels = as73211_channels, | |
| 852 | + .num_channels = ARRAY_SIZE(as73211_channels), | |
| 853 | +}; | |
| 854 | + | |
| 855 | +static const struct as73211_spec_dev_data as7331_spec = { | |
| 856 | + .intensity_scale = as7331_intensity_scale, | |
| 857 | + .channels = as7331_channels, | |
| 858 | + .num_channels = ARRAY_SIZE(as7331_channels), | |
| 859 | +}; | |
| 860 | + | |
| 775 | 861 | static const struct of_device_id as73211_of_match[] = { |
| 776 | − { .compatible = "ams,as73211" }, | |
| 862 | + { .compatible = "ams,as73211", &as73211_spec }, | |
| 863 | + { .compatible = "ams,as7331", &as7331_spec }, | |
| 777 | 864 | { } |
| 778 | 865 | }; |
| 779 | 866 | MODULE_DEVICE_TABLE(of, as73211_of_match); |
| 780 | 867 | |
| 781 | 868 | static const struct i2c_device_id as73211_id[] = { |
| 782 | − { "as73211", 0 }, | |
| 869 | + { "as73211", (kernel_ulong_t)&as73211_spec }, | |
| 870 | + { "as7331", (kernel_ulong_t)&as7331_spec }, | |
| 783 | 871 | { } |
| 784 | 872 | }; |
| 785 | 873 | MODULE_DEVICE_TABLE(i2c, as73211_id); |
| @@ -14,8 +14,11 @@ · 13 unmodified lines | ||
| 14 | 14 | #include "../common/hid-sensors/hid-sensor-trigger.h" |
| 15 | 15 | |
| 16 | 16 | enum { |
| 17 | − CHANNEL_SCAN_INDEX_INTENSITY = 0, | |
| 18 | − CHANNEL_SCAN_INDEX_ILLUM = 1, | |
| 17 | + CHANNEL_SCAN_INDEX_INTENSITY, | |
| 18 | + CHANNEL_SCAN_INDEX_ILLUM, | |
| 19 | + CHANNEL_SCAN_INDEX_COLOR_TEMP, | |
| 20 | + CHANNEL_SCAN_INDEX_CHROMATICITY_X, | |
| 21 | + CHANNEL_SCAN_INDEX_CHROMATICITY_Y, | |
| 19 | 22 | CHANNEL_SCAN_INDEX_MAX |
| 20 | 23 | }; |
| 21 | 24 | |
| @@ -25,6 +28,7 @@ struct als_state { · 3 unmodified lines | ||
| 25 | 28 | struct hid_sensor_hub_callbacks callbacks; |
| 26 | 29 | struct hid_sensor_common common_attributes; |
| 27 | 30 | struct hid_sensor_hub_attribute_info als[CHANNEL_SCAN_INDEX_MAX]; |
| 31 | + struct iio_chan_spec channels[CHANNEL_SCAN_INDEX_MAX + 1]; | |
| 28 | 32 | struct { |
| 29 | 33 | u32 illum[CHANNEL_SCAN_INDEX_MAX]; |
| 30 | 34 | u64 timestamp __aligned(8); |
| @@ -33,7 +37,18 @@ struct als_state { · 2 unmodified lines | ||
| 33 | 37 | int scale_post_decml; |
| 34 | 38 | int scale_precision; |
| 35 | 39 | int value_offset; |
| 40 | + int num_channels; | |
| 36 | 41 | s64 timestamp; |
| 42 | + unsigned long als_scan_mask[2]; | |
| 43 | +}; | |
| 44 | + | |
| 45 | +/* The order of usage ids must match scan index starting from CHANNEL_SCAN_INDEX_INTENSITY */ | |
| 46 | +static const u32 als_usage_ids[] = { | |
| 47 | + HID_USAGE_SENSOR_LIGHT_ILLUM, | |
| 48 | + HID_USAGE_SENSOR_LIGHT_ILLUM, | |
| 49 | + HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE, | |
| 50 | + HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X, | |
| 51 | + HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y, | |
| 37 | 52 | }; |
| 38 | 53 | |
| 39 | 54 | static const u32 als_sensitivity_addresses[] = { |
| @@ -65,6 +80,40 @@ static const struct iio_chan_spec als_channels[] = { · 25 unmodified lines | ||
| 65 | 80 | BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), |
| 66 | 81 | .scan_index = CHANNEL_SCAN_INDEX_ILLUM, |
| 67 | 82 | }, |
| 83 | + { | |
| 84 | + .type = IIO_COLORTEMP, | |
| 85 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | |
| 86 | + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | | |
| 87 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 88 | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | | |
| 89 | + BIT(IIO_CHAN_INFO_HYSTERESIS) | | |
| 90 | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), | |
| 91 | + .scan_index = CHANNEL_SCAN_INDEX_COLOR_TEMP, | |
| 92 | + }, | |
| 93 | + { | |
| 94 | + .type = IIO_CHROMATICITY, | |
| 95 | + .modified = 1, | |
| 96 | + .channel2 = IIO_MOD_X, | |
| 97 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | |
| 98 | + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | | |
| 99 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 100 | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | | |
| 101 | + BIT(IIO_CHAN_INFO_HYSTERESIS) | | |
| 102 | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), | |
| 103 | + .scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_X, | |
| 104 | + }, | |
| 105 | + { | |
| 106 | + .type = IIO_CHROMATICITY, | |
| 107 | + .modified = 1, | |
| 108 | + .channel2 = IIO_MOD_Y, | |
| 109 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | |
| 110 | + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | | |
| 111 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 112 | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | | |
| 113 | + BIT(IIO_CHAN_INFO_HYSTERESIS) | | |
| 114 | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), | |
| 115 | + .scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_Y, | |
| 116 | + }, | |
| 68 | 117 | IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) |
| 69 | 118 | }; |
| 70 | 119 | |
| @@ -103,6 +152,21 @@ static int als_read_raw(struct iio_dev *indio_dev, · 32 unmodified lines | ||
| 103 | 152 | min = als_state->als[chan->scan_index].logical_minimum; |
| 104 | 153 | address = HID_USAGE_SENSOR_LIGHT_ILLUM; |
| 105 | 154 | break; |
| 155 | + case CHANNEL_SCAN_INDEX_COLOR_TEMP: | |
| 156 | + report_id = als_state->als[chan->scan_index].report_id; | |
| 157 | + min = als_state->als[chan->scan_index].logical_minimum; | |
| 158 | + address = HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE; | |
| 159 | + break; | |
| 160 | + case CHANNEL_SCAN_INDEX_CHROMATICITY_X: | |
| 161 | + report_id = als_state->als[chan->scan_index].report_id; | |
| 162 | + min = als_state->als[chan->scan_index].logical_minimum; | |
| 163 | + address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X; | |
| 164 | + break; | |
| 165 | + case CHANNEL_SCAN_INDEX_CHROMATICITY_Y: | |
| 166 | + report_id = als_state->als[chan->scan_index].report_id; | |
| 167 | + min = als_state->als[chan->scan_index].logical_minimum; | |
| 168 | + address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y; | |
| 169 | + break; | |
| 106 | 170 | default: |
| 107 | 171 | report_id = -1; |
| 108 | 172 | break; |
| @@ -223,6 +287,18 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, · 114 unmodified lines | ||
| 223 | 287 | als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data; |
| 224 | 288 | ret = 0; |
| 225 | 289 | break; |
| 290 | + case HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE: | |
| 291 | + als_state->scan.illum[CHANNEL_SCAN_INDEX_COLOR_TEMP] = sample_data; | |
| 292 | + ret = 0; | |
| 293 | + break; | |
| 294 | + case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X: | |
| 295 | + als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_X] = sample_data; | |
| 296 | + ret = 0; | |
| 297 | + break; | |
| 298 | + case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y: | |
| 299 | + als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_Y] = sample_data; | |
| 300 | + ret = 0; | |
| 301 | + break; | |
| 226 | 302 | case HID_USAGE_SENSOR_TIME_TIMESTAMP: |
| 227 | 303 | als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes, |
| 228 | 304 | *(s64 *)raw_data); |
| @@ -238,27 +314,38 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev, · 9 unmodified lines | ||
| 238 | 314 | /* Parse report which is specific to an usage id*/ |
| 239 | 315 | static int als_parse_report(struct platform_device *pdev, |
| 240 | 316 | struct hid_sensor_hub_device *hsdev, |
| 241 | − struct iio_chan_spec *channels, | |
| 242 | 317 | unsigned usage_id, |
| 243 | 318 | struct als_state *st) |
| 244 | 319 | { |
| 245 | − int ret; | |
| 320 | + struct iio_chan_spec *channels; | |
| 321 | + int ret, index = 0; | |
| 246 | 322 | int i; |
| 247 | 323 | |
| 248 | − for (i = 0; i <= CHANNEL_SCAN_INDEX_ILLUM; ++i) { | |
| 324 | + channels = st->channels; | |
| 325 | + | |
| 326 | + for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; ++i) { | |
| 249 | 327 | ret = sensor_hub_input_get_attribute_info(hsdev, |
| 250 | 328 | HID_INPUT_REPORT, |
| 251 | 329 | usage_id, |
| 252 | − HID_USAGE_SENSOR_LIGHT_ILLUM, | |
| 330 | + als_usage_ids[i], | |
| 253 | 331 | &st->als[i]); |
| 254 | 332 | if (ret < 0) |
| 255 | − return ret; | |
| 256 | − als_adjust_channel_bit_mask(channels, i, st->als[i].size); | |
| 333 | + continue; | |
| 334 | + | |
| 335 | + channels[index] = als_channels[i]; | |
| 336 | + st->als_scan_mask[0] |= BIT(i); | |
| 337 | + als_adjust_channel_bit_mask(channels, index, st->als[i].size); | |
| 338 | + ++index; | |
| 257 | 339 | |
| 258 | 340 | dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index, |
| 259 | 341 | st->als[i].report_id); |
| 260 | 342 | } |
| 261 | 343 | |
| 344 | + st->num_channels = index; | |
| 345 | + /* Return success even if one usage id is present */ | |
| 346 | + if (index) | |
| 347 | + ret = 0; | |
| 348 | + | |
| 262 | 349 | st->scale_precision = hid_sensor_format_scale(usage_id, |
| 263 | 350 | &st->als[CHANNEL_SCAN_INDEX_INTENSITY], |
| 264 | 351 | &st->scale_pre_decml, &st->scale_post_decml); |
| @@ -294,15 +381,7 @@ static int hid_als_probe(struct platform_device *pdev) · 29 unmodified lines | ||
| 294 | 381 | return ret; |
| 295 | 382 | } |
| 296 | 383 | |
| 297 | − indio_dev->channels = devm_kmemdup(&pdev->dev, als_channels, | |
| 298 | − sizeof(als_channels), GFP_KERNEL); | |
| 299 | − if (!indio_dev->channels) { | |
| 300 | − dev_err(&pdev->dev, "failed to duplicate channels\n"); | |
| 301 | − return -ENOMEM; | |
| 302 | − } | |
| 303 | − | |
| 304 | 384 | ret = als_parse_report(pdev, hsdev, |
| 305 | − (struct iio_chan_spec *)indio_dev->channels, | |
| 306 | 385 | hsdev->usage, |
| 307 | 386 | als_state); |
| 308 | 387 | if (ret) { |
| @@ -310,8 +389,15 @@ static int hid_als_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 310 | 389 | return ret; |
| 311 | 390 | } |
| 312 | 391 | |
| 313 | − indio_dev->num_channels = | |
| 314 | − ARRAY_SIZE(als_channels); | |
| 392 | + /* Add timestamp channel */ | |
| 393 | + als_state->channels[als_state->num_channels] = als_channels[CHANNEL_SCAN_INDEX_TIMESTAMP]; | |
| 394 | + | |
| 395 | + /* +1 for adding timestamp channel */ | |
| 396 | + indio_dev->num_channels = als_state->num_channels + 1; | |
| 397 | + | |
| 398 | + indio_dev->channels = als_state->channels; | |
| 399 | + indio_dev->available_scan_masks = als_state->als_scan_mask; | |
| 400 | + | |
| 315 | 401 | indio_dev->info = &als_info; |
| 316 | 402 | indio_dev->name = name; |
| 317 | 403 | indio_dev->modes = INDIO_DIRECT_MODE; |
| @@ -12,10 +12,10 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <linux/kernel.h> |
| 13 | 13 | #include <linux/slab.h> |
| 14 | 14 | #include <linux/module.h> |
| 15 | +#include <linux/mod_devicetable.h> | |
| 15 | 16 | #include <linux/delay.h> |
| 16 | 17 | #include <linux/i2c.h> |
| 17 | 18 | #include <linux/mutex.h> |
| 18 | −#include <linux/acpi.h> | |
| 19 | 19 | #include <linux/regmap.h> |
| 20 | 20 | #include <linux/iio/iio.h> |
| 21 | 21 | #include <linux/iio/sysfs.h> |
| @@ -438,7 +438,7 @@ static struct i2c_driver jsa1212_driver = { · 416 unmodified lines | ||
| 438 | 438 | .driver = { |
| 439 | 439 | .name = JSA1212_DRIVER_NAME, |
| 440 | 440 | .pm = pm_sleep_ptr(&jsa1212_pm_ops), |
| 441 | − .acpi_match_table = ACPI_PTR(jsa1212_acpi_match), | |
| 441 | + .acpi_match_table = jsa1212_acpi_match, | |
| 442 | 442 | }, |
| 443 | 443 | .probe = jsa1212_probe, |
| 444 | 444 | .remove = jsa1212_remove, |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | 12 | #include <linux/module.h> |
| 13 | +#include <linux/mod_devicetable.h> | |
| 13 | 14 | #include <linux/i2c.h> |
| 14 | 15 | #include <linux/err.h> |
| 15 | 16 | #include <linux/delay.h> |
| @@ -1639,7 +1640,7 @@ static struct i2c_driver ltr501_driver = { · 1623 unmodified lines | ||
| 1639 | 1640 | .name = LTR501_DRV_NAME, |
| 1640 | 1641 | .of_match_table = ltr501_of_match, |
| 1641 | 1642 | .pm = pm_sleep_ptr(<r501_pm_ops), |
| 1642 | − .acpi_match_table = ACPI_PTR(ltr_acpi_match), | |
| 1643 | + .acpi_match_table = ltr_acpi_match, | |
| 1643 | 1644 | }, |
| 1644 | 1645 | .probe = ltr501_probe, |
| 1645 | 1646 | .remove = ltr501_remove, |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | 12 | #include <linux/module.h> |
| 13 | +#include <linux/mod_devicetable.h> | |
| 13 | 14 | #include <linux/init.h> |
| 14 | 15 | #include <linux/i2c.h> |
| 15 | 16 | #include <linux/regmap.h> |
| @@ -19,7 +20,6 @@ · 3 unmodified lines | ||
| 19 | 20 | #include <linux/iio/buffer.h> |
| 20 | 21 | #include <linux/iio/trigger_consumer.h> |
| 21 | 22 | #include <linux/iio/triggered_buffer.h> |
| 22 | −#include <linux/acpi.h> | |
| 23 | 23 | |
| 24 | 24 | #define MAX44000_DRV_NAME "max44000" |
| 25 | 25 | |
| @@ -603,18 +603,16 @@ static const struct i2c_device_id max44000_id[] = { · 577 unmodified lines | ||
| 603 | 603 | }; |
| 604 | 604 | MODULE_DEVICE_TABLE(i2c, max44000_id); |
| 605 | 605 | |
| 606 | −#ifdef CONFIG_ACPI | |
| 607 | 606 | static const struct acpi_device_id max44000_acpi_match[] = { |
| 608 | 607 | {"MAX44000", 0}, |
| 609 | 608 | { } |
| 610 | 609 | }; |
| 611 | 610 | MODULE_DEVICE_TABLE(acpi, max44000_acpi_match); |
| 612 | −#endif | |
| 613 | 611 | |
| 614 | 612 | static struct i2c_driver max44000_driver = { |
| 615 | 613 | .driver = { |
| 616 | 614 | .name = MAX44000_DRV_NAME, |
| 617 | − .acpi_match_table = ACPI_PTR(max44000_acpi_match), | |
| 615 | + .acpi_match_table = max44000_acpi_match, | |
| 618 | 616 | }, |
| 619 | 617 | .probe = max44000_probe, |
| 620 | 618 | .id_table = max44000_id, |
| @@ -10,11 +10,11 @@ · 9 unmodified lines | ||
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | 12 | #include <linux/module.h> |
| 13 | +#include <linux/mod_devicetable.h> | |
| 13 | 14 | #include <linux/init.h> |
| 14 | 15 | #include <linux/i2c.h> |
| 15 | 16 | #include <linux/regmap.h> |
| 16 | 17 | #include <linux/delay.h> |
| 17 | −#include <linux/acpi.h> | |
| 18 | 18 | |
| 19 | 19 | #include <linux/iio/iio.h> |
| 20 | 20 | #include <linux/iio/buffer.h> |
| @@ -1119,7 +1119,7 @@ static struct i2c_driver rpr0521_driver = { · 1098 unmodified lines | ||
| 1119 | 1119 | .driver = { |
| 1120 | 1120 | .name = RPR0521_DRV_NAME, |
| 1121 | 1121 | .pm = pm_ptr(&rpr0521_pm_ops), |
| 1122 | − .acpi_match_table = ACPI_PTR(rpr0521_acpi_match), | |
| 1122 | + .acpi_match_table = rpr0521_acpi_match, | |
| 1123 | 1123 | }, |
| 1124 | 1124 | .probe = rpr0521_probe, |
| 1125 | 1125 | .remove = rpr0521_remove, |
| @@ -7,11 +7,11 @@ · 6 unmodified lines | ||
| 7 | 7 | * IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48. |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | −#include <linux/acpi.h> | |
| 11 | 10 | #include <linux/i2c.h> |
| 12 | 11 | #include <linux/interrupt.h> |
| 13 | 12 | #include <linux/kernel.h> |
| 14 | 13 | #include <linux/module.h> |
| 14 | +#include <linux/mod_devicetable.h> | |
| 15 | 15 | #include <linux/regmap.h> |
| 16 | 16 | #include <linux/iio/events.h> |
| 17 | 17 | #include <linux/iio/iio.h> |
| @@ -712,7 +712,7 @@ static struct i2c_driver stk3310_driver = { · 694 unmodified lines | ||
| 712 | 712 | .name = "stk3310", |
| 713 | 713 | .of_match_table = stk3310_of_match, |
| 714 | 714 | .pm = pm_sleep_ptr(&stk3310_pm_ops), |
| 715 | − .acpi_match_table = ACPI_PTR(stk3310_acpi_id), | |
| 715 | + .acpi_match_table = stk3310_acpi_id, | |
| 716 | 716 | }, |
| 717 | 717 | .probe = stk3310_probe, |
| 718 | 718 | .remove = stk3310_remove, |
| @@ -9,7 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | #include <linux/kernel.h> |
| 11 | 11 | #include <linux/module.h> |
| 12 | −#include <linux/acpi.h> | |
| 12 | +#include <linux/mod_devicetable.h> | |
| 13 | 13 | #include <linux/delay.h> |
| 14 | 14 | #include <linux/i2c.h> |
| 15 | 15 | #include <linux/iio/events.h> |
| @@ -972,7 +972,7 @@ static struct i2c_driver us5182d_driver = { · 956 unmodified lines | ||
| 972 | 972 | .name = US5182D_DRV_NAME, |
| 973 | 973 | .pm = pm_ptr(&us5182d_pm_ops), |
| 974 | 974 | .of_match_table = us5182d_of_match, |
| 975 | − .acpi_match_table = ACPI_PTR(us5182d_acpi_match), | |
| 975 | + .acpi_match_table = us5182d_acpi_match, | |
| 976 | 976 | }, |
| 977 | 977 | .probe = us5182d_probe, |
| 978 | 978 | .remove = us5182d_remove, |
| @@ -90,6 +90,7 @@ · 89 unmodified lines | ||
| 90 | 90 | #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) |
| 91 | 91 | #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ |
| 92 | 92 | #define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */ |
| 93 | +#define VCNL4040_PS_CONF2_PS_HD BIT(11) /* Proximity high definition */ | |
| 93 | 94 | #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ |
| 94 | 95 | #define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */ |
| 95 | 96 | #define VCNL4040_PS_MS_LED_I GENMASK(10, 8) /* Proximity current */ |
| @@ -114,6 +115,13 @@ · 18 unmodified lines | ||
| 114 | 115 | #define VCNL4010_INT_DRDY \ |
| 115 | 116 | (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) |
| 116 | 117 | |
| 118 | +#define VCNL4040_CONF3_PS_MPS_16BITS 3 /* 8 multi pulses */ | |
| 119 | +#define VCNL4040_CONF3_PS_LED_I_16BITS 3 /* 120 mA */ | |
| 120 | + | |
| 121 | +#define VCNL4040_CONF3_PS_SAMPLE_16BITS \ | |
| 122 | + (FIELD_PREP(VCNL4040_PS_CONF3_MPS, VCNL4040_CONF3_PS_MPS_16BITS) | \ | |
| 123 | + FIELD_PREP(VCNL4040_PS_MS_LED_I, VCNL4040_CONF3_PS_LED_I_16BITS)) | |
| 124 | + | |
| 117 | 125 | static const int vcnl4010_prox_sampling_frequency[][2] = { |
| 118 | 126 | {1, 950000}, |
| 119 | 127 | {3, 906250}, |
| @@ -195,6 +203,7 @@ struct vcnl4000_data { · 75 unmodified lines | ||
| 195 | 203 | enum vcnl4000_device_ids id; |
| 196 | 204 | int rev; |
| 197 | 205 | int al_scale; |
| 206 | + int ps_scale; | |
| 198 | 207 | u8 ps_int; /* proximity interrupt mode */ |
| 199 | 208 | u8 als_int; /* ambient light interrupt mode*/ |
| 200 | 209 | const struct vcnl4000_chip_spec *chip_spec; |
| @@ -345,6 +354,7 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) · 144 unmodified lines | ||
| 345 | 354 | static int vcnl4200_init(struct vcnl4000_data *data) |
| 346 | 355 | { |
| 347 | 356 | int ret, id; |
| 357 | + u16 regval; | |
| 348 | 358 | |
| 349 | 359 | ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); |
| 350 | 360 | if (ret < 0) |
| @@ -386,9 +396,32 @@ static int vcnl4200_init(struct vcnl4000_data *data) · 35 unmodified lines | ||
| 386 | 396 | break; |
| 387 | 397 | } |
| 388 | 398 | data->al_scale = data->chip_spec->ulux_step; |
| 399 | + data->ps_scale = 16; | |
| 389 | 400 | mutex_init(&data->vcnl4200_al.lock); |
| 390 | 401 | mutex_init(&data->vcnl4200_ps.lock); |
| 391 | 402 | |
| 403 | + /* Use 16 bits proximity sensor readings */ | |
| 404 | + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); | |
| 405 | + if (ret < 0) | |
| 406 | + return ret; | |
| 407 | + | |
| 408 | + regval = ret | VCNL4040_PS_CONF2_PS_HD; | |
| 409 | + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, | |
| 410 | + regval); | |
| 411 | + if (ret < 0) | |
| 412 | + return ret; | |
| 413 | + | |
| 414 | + /* Align proximity sensor sample rate to 16 bits data width */ | |
| 415 | + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); | |
| 416 | + if (ret < 0) | |
| 417 | + return ret; | |
| 418 | + | |
| 419 | + regval = ret | VCNL4040_CONF3_PS_SAMPLE_16BITS; | |
| 420 | + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, | |
| 421 | + regval); | |
| 422 | + if (ret < 0) | |
| 423 | + return ret; | |
| 424 | + | |
| 392 | 425 | ret = data->chip_spec->set_power_state(data, true); |
| 393 | 426 | if (ret < 0) |
| 394 | 427 | return ret; |
| @@ -901,8 +934,9 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, · 506 unmodified lines | ||
| 901 | 934 | break; |
| 902 | 935 | case IIO_PROXIMITY: |
| 903 | 936 | ret = data->chip_spec->measure_proximity(data, val); |
| 937 | + *val2 = data->ps_scale; | |
| 904 | 938 | if (!ret) |
| 905 | − ret = IIO_VAL_INT; | |
| 939 | + ret = IIO_VAL_FRACTIONAL; | |
| 906 | 940 | break; |
| 907 | 941 | default: |
| 908 | 942 | ret = -EINVAL; |
| @@ -20,7 +20,6 @@ · 19 unmodified lines | ||
| 20 | 20 | #include <linux/i2c.h> |
| 21 | 21 | #include <linux/mutex.h> |
| 22 | 22 | #include <linux/err.h> |
| 23 | −#include <linux/of.h> | |
| 24 | 23 | #include <linux/delay.h> |
| 25 | 24 | #include <linux/util_macros.h> |
| 26 | 25 | |
| @@ -6,6 +6,18 @@ · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | menu "Magnetometer sensors" |
| 8 | 8 | |
| 9 | +config AF8133J | |
| 10 | + tristate "Voltafield AF8133J 3-Axis Magnetometer" | |
| 11 | + depends on I2C | |
| 12 | + depends on OF | |
| 13 | + select REGMAP_I2C | |
| 14 | + help | |
| 15 | + Say yes here to build support for Voltafield AF8133J I2C-based | |
| 16 | + 3-axis magnetometer chip. | |
| 17 | + | |
| 18 | + To compile this driver as a module, choose M here: the module | |
| 19 | + will be called af8133j. | |
| 20 | + | |
| 9 | 21 | config AK8974 |
| 10 | 22 | tristate "Asahi Kasei AK8974 3-Axis Magnetometer" |
| 11 | 23 | depends on I2C |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | # |
| 5 | 5 | |
| 6 | 6 | # When adding new entries keep the list in alphabetical order |
| 7 | +obj-$(CONFIG_AF8133J) += af8133j.o | |
| 7 | 8 | obj-$(CONFIG_AK8974) += ak8974.o |
| 8 | 9 | obj-$(CONFIG_AK8975) += ak8975.o |
| 9 | 10 | obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o |
| @@ -0,0 +1,528 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * af8133j.c - Voltafield AF8133J magnetometer driver | |
| 4 | + * | |
| 5 | + * Copyright 2021 Icenowy Zheng <icenowy@aosc.io> | |
| 6 | + * Copyright 2024 Ondřej Jirman <megi@xff.cz> | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/delay.h> | |
| 10 | +#include <linux/gpio/consumer.h> | |
| 11 | +#include <linux/i2c.h> | |
| 12 | +#include <linux/module.h> | |
| 13 | +#include <linux/pm_runtime.h> | |
| 14 | +#include <linux/regmap.h> | |
| 15 | +#include <linux/regulator/consumer.h> | |
| 16 | + | |
| 17 | +#include <linux/iio/iio.h> | |
| 18 | +#include <linux/iio/trigger_consumer.h> | |
| 19 | +#include <linux/iio/triggered_buffer.h> | |
| 20 | + | |
| 21 | +#define AF8133J_REG_OUT 0x03 | |
| 22 | +#define AF8133J_REG_PCODE 0x00 | |
| 23 | +#define AF8133J_REG_PCODE_VAL 0x5e | |
| 24 | +#define AF8133J_REG_STATUS 0x02 | |
| 25 | +#define AF8133J_REG_STATUS_ACQ BIT(0) | |
| 26 | +#define AF8133J_REG_STATE 0x0a | |
| 27 | +#define AF8133J_REG_STATE_STBY 0x00 | |
| 28 | +#define AF8133J_REG_STATE_WORK 0x01 | |
| 29 | +#define AF8133J_REG_RANGE 0x0b | |
| 30 | +#define AF8133J_REG_RANGE_22G 0x12 | |
| 31 | +#define AF8133J_REG_RANGE_12G 0x34 | |
| 32 | +#define AF8133J_REG_SWR 0x11 | |
| 33 | +#define AF8133J_REG_SWR_PERFORM 0x81 | |
| 34 | + | |
| 35 | +static const char * const af8133j_supply_names[] = { | |
| 36 | + "avdd", | |
| 37 | + "dvdd", | |
| 38 | +}; | |
| 39 | + | |
| 40 | +struct af8133j_data { | |
| 41 | + struct i2c_client *client; | |
| 42 | + struct regmap *regmap; | |
| 43 | + /* | |
| 44 | + * Protect device internal state between starting a measurement | |
| 45 | + * and reading the result. | |
| 46 | + */ | |
| 47 | + struct mutex mutex; | |
| 48 | + struct iio_mount_matrix orientation; | |
| 49 | + | |
| 50 | + struct gpio_desc *reset_gpiod; | |
| 51 | + struct regulator_bulk_data supplies[ARRAY_SIZE(af8133j_supply_names)]; | |
| 52 | + | |
| 53 | + u8 range; | |
| 54 | +}; | |
| 55 | + | |
| 56 | +enum af8133j_axis { | |
| 57 | + AXIS_X = 0, | |
| 58 | + AXIS_Y, | |
| 59 | + AXIS_Z, | |
| 60 | +}; | |
| 61 | + | |
| 62 | +static struct iio_mount_matrix * | |
| 63 | +af8133j_get_mount_matrix(struct iio_dev *indio_dev, | |
| 64 | + const struct iio_chan_spec *chan) | |
| 65 | +{ | |
| 66 | + struct af8133j_data *data = iio_priv(indio_dev); | |
| 67 | + | |
| 68 | + return &data->orientation; | |
| 69 | +} | |
| 70 | + | |
| 71 | +static const struct iio_chan_spec_ext_info af8133j_ext_info[] = { | |
| 72 | + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, af8133j_get_mount_matrix), | |
| 73 | + { } | |
| 74 | +}; | |
| 75 | + | |
| 76 | +#define AF8133J_CHANNEL(_si, _axis) { \ | |
| 77 | + .type = IIO_MAGN, \ | |
| 78 | + .modified = 1, \ | |
| 79 | + .channel2 = IIO_MOD_ ## _axis, \ | |
| 80 | + .address = AXIS_ ## _axis, \ | |
| 81 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
| 82 | + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
| 83 | + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ | |
| 84 | + .ext_info = af8133j_ext_info, \ | |
| 85 | + .scan_index = _si, \ | |
| 86 | + .scan_type = { \ | |
| 87 | + .sign = 's', \ | |
| 88 | + .realbits = 16, \ | |
| 89 | + .storagebits = 16, \ | |
| 90 | + .endianness = IIO_LE, \ | |
| 91 | + }, \ | |
| 92 | +} | |
| 93 | + | |
| 94 | +static const struct iio_chan_spec af8133j_channels[] = { | |
| 95 | + AF8133J_CHANNEL(0, X), | |
| 96 | + AF8133J_CHANNEL(1, Y), | |
| 97 | + AF8133J_CHANNEL(2, Z), | |
| 98 | + IIO_CHAN_SOFT_TIMESTAMP(3), | |
| 99 | +}; | |
| 100 | + | |
| 101 | +static int af8133j_product_check(struct af8133j_data *data) | |
| 102 | +{ | |
| 103 | + struct device *dev = &data->client->dev; | |
| 104 | + unsigned int val; | |
| 105 | + int ret; | |
| 106 | + | |
| 107 | + ret = regmap_read(data->regmap, AF8133J_REG_PCODE, &val); | |
| 108 | + if (ret) { | |
| 109 | + dev_err(dev, "Error reading product code (%d)\n", ret); | |
| 110 | + return ret; | |
| 111 | + } | |
| 112 | + | |
| 113 | + if (val != AF8133J_REG_PCODE_VAL) { | |
| 114 | + dev_warn(dev, "Invalid product code (0x%02x)\n", val); | |
| 115 | + return 0; /* Allow unknown ID so fallback compatibles work */ | |
| 116 | + } | |
| 117 | + | |
| 118 | + return 0; | |
| 119 | +} | |
| 120 | + | |
| 121 | +static int af8133j_reset(struct af8133j_data *data) | |
| 122 | +{ | |
| 123 | + struct device *dev = &data->client->dev; | |
| 124 | + int ret; | |
| 125 | + | |
| 126 | + if (data->reset_gpiod) { | |
| 127 | + /* If we have GPIO reset line, use it */ | |
| 128 | + gpiod_set_value_cansleep(data->reset_gpiod, 1); | |
| 129 | + udelay(10); | |
| 130 | + gpiod_set_value_cansleep(data->reset_gpiod, 0); | |
| 131 | + } else { | |
| 132 | + /* Otherwise use software reset */ | |
| 133 | + ret = regmap_write(data->regmap, AF8133J_REG_SWR, | |
| 134 | + AF8133J_REG_SWR_PERFORM); | |
| 135 | + if (ret) { | |
| 136 | + dev_err(dev, "Failed to reset the chip\n"); | |
| 137 | + return ret; | |
| 138 | + } | |
| 139 | + } | |
| 140 | + | |
| 141 | + /* Wait for reset to finish */ | |
| 142 | + usleep_range(1000, 1100); | |
| 143 | + | |
| 144 | + /* Restore range setting */ | |
| 145 | + if (data->range == AF8133J_REG_RANGE_22G) { | |
| 146 | + ret = regmap_write(data->regmap, AF8133J_REG_RANGE, data->range); | |
| 147 | + if (ret) | |
| 148 | + return ret; | |
| 149 | + } | |
| 150 | + | |
| 151 | + return 0; | |
| 152 | +} | |
| 153 | + | |
| 154 | +static void af8133j_power_down(struct af8133j_data *data) | |
| 155 | +{ | |
| 156 | + gpiod_set_value_cansleep(data->reset_gpiod, 1); | |
| 157 | + regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies); | |
| 158 | +} | |
| 159 | + | |
| 160 | +static int af8133j_power_up(struct af8133j_data *data) | |
| 161 | +{ | |
| 162 | + struct device *dev = &data->client->dev; | |
| 163 | + int ret; | |
| 164 | + | |
| 165 | + ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies); | |
| 166 | + if (ret) { | |
| 167 | + dev_err(dev, "Could not enable regulators\n"); | |
| 168 | + return ret; | |
| 169 | + } | |
| 170 | + | |
| 171 | + gpiod_set_value_cansleep(data->reset_gpiod, 0); | |
| 172 | + | |
| 173 | + /* Wait for power on reset */ | |
| 174 | + usleep_range(15000, 16000); | |
| 175 | + | |
| 176 | + ret = af8133j_reset(data); | |
| 177 | + if (ret) { | |
| 178 | + af8133j_power_down(data); | |
| 179 | + return ret; | |
| 180 | + } | |
| 181 | + | |
| 182 | + return 0; | |
| 183 | +} | |
| 184 | + | |
| 185 | +static int af8133j_take_measurement(struct af8133j_data *data) | |
| 186 | +{ | |
| 187 | + unsigned int val; | |
| 188 | + int ret; | |
| 189 | + | |
| 190 | + ret = regmap_write(data->regmap, | |
| 191 | + AF8133J_REG_STATE, AF8133J_REG_STATE_WORK); | |
| 192 | + if (ret) | |
| 193 | + return ret; | |
| 194 | + | |
| 195 | + /* The datasheet says "Mesaure Time <1.5ms" */ | |
| 196 | + ret = regmap_read_poll_timeout(data->regmap, AF8133J_REG_STATUS, val, | |
| 197 | + val & AF8133J_REG_STATUS_ACQ, | |
| 198 | + 500, 1500); | |
| 199 | + if (ret) | |
| 200 | + return ret; | |
| 201 | + | |
| 202 | + ret = regmap_write(data->regmap, | |
| 203 | + AF8133J_REG_STATE, AF8133J_REG_STATE_STBY); | |
| 204 | + if (ret) | |
| 205 | + return ret; | |
| 206 | + | |
| 207 | + return 0; | |
| 208 | +} | |
| 209 | + | |
| 210 | +static int af8133j_read_measurement(struct af8133j_data *data, __le16 buf[3]) | |
| 211 | +{ | |
| 212 | + struct device *dev = &data->client->dev; | |
| 213 | + int ret; | |
| 214 | + | |
| 215 | + ret = pm_runtime_resume_and_get(dev); | |
| 216 | + if (ret) { | |
| 217 | + /* | |
| 218 | + * Ignore EACCES because that happens when RPM is disabled | |
| 219 | + * during system sleep, while userspace leave eg. hrtimer | |
| 220 | + * trigger attached and IIO core keeps trying to do measurements. | |
| 221 | + */ | |
| 222 | + if (ret != -EACCES) | |
| 223 | + dev_err(dev, "Failed to power on (%d)\n", ret); | |
| 224 | + return ret; | |
| 225 | + } | |
| 226 | + | |
| 227 | + scoped_guard(mutex, &data->mutex) { | |
| 228 | + ret = af8133j_take_measurement(data); | |
| 229 | + if (ret) | |
| 230 | + goto out_rpm_put; | |
| 231 | + | |
| 232 | + ret = regmap_bulk_read(data->regmap, AF8133J_REG_OUT, | |
| 233 | + buf, sizeof(__le16) * 3); | |
| 234 | + } | |
| 235 | + | |
| 236 | +out_rpm_put: | |
| 237 | + pm_runtime_mark_last_busy(dev); | |
| 238 | + pm_runtime_put_autosuspend(dev); | |
| 239 | + | |
| 240 | + return ret; | |
| 241 | +} | |
| 242 | + | |
| 243 | +static const int af8133j_scales[][2] = { | |
| 244 | + [0] = { 0, 366210 }, /* 12 gauss */ | |
| 245 | + [1] = { 0, 671386 }, /* 22 gauss */ | |
| 246 | +}; | |
| 247 | + | |
| 248 | +static int af8133j_read_raw(struct iio_dev *indio_dev, | |
| 249 | + struct iio_chan_spec const *chan, int *val, | |
| 250 | + int *val2, long mask) | |
| 251 | +{ | |
| 252 | + struct af8133j_data *data = iio_priv(indio_dev); | |
| 253 | + __le16 buf[3]; | |
| 254 | + int ret; | |
| 255 | + | |
| 256 | + switch (mask) { | |
| 257 | + case IIO_CHAN_INFO_RAW: | |
| 258 | + ret = af8133j_read_measurement(data, buf); | |
| 259 | + if (ret) | |
| 260 | + return ret; | |
| 261 | + | |
| 262 | + *val = sign_extend32(le16_to_cpu(buf[chan->address]), | |
| 263 | + chan->scan_type.realbits - 1); | |
| 264 | + return IIO_VAL_INT; | |
| 265 | + case IIO_CHAN_INFO_SCALE: | |
| 266 | + *val = 0; | |
| 267 | + | |
| 268 | + if (data->range == AF8133J_REG_RANGE_12G) | |
| 269 | + *val2 = af8133j_scales[0][1]; | |
| 270 | + else | |
| 271 | + *val2 = af8133j_scales[1][1]; | |
| 272 | + | |
| 273 | + return IIO_VAL_INT_PLUS_NANO; | |
| 274 | + default: | |
| 275 | + return -EINVAL; | |
| 276 | + } | |
| 277 | +} | |
| 278 | + | |
| 279 | +static int af8133j_read_avail(struct iio_dev *indio_dev, | |
| 280 | + struct iio_chan_spec const *chan, | |
| 281 | + const int **vals, int *type, int *length, | |
| 282 | + long mask) | |
| 283 | +{ | |
| 284 | + switch (mask) { | |
| 285 | + case IIO_CHAN_INFO_SCALE: | |
| 286 | + *vals = (const int *)af8133j_scales; | |
| 287 | + *length = ARRAY_SIZE(af8133j_scales) * 2; | |
| 288 | + *type = IIO_VAL_INT_PLUS_NANO; | |
| 289 | + return IIO_AVAIL_LIST; | |
| 290 | + default: | |
| 291 | + return -EINVAL; | |
| 292 | + } | |
| 293 | +} | |
| 294 | + | |
| 295 | +static int af8133j_set_scale(struct af8133j_data *data, | |
| 296 | + unsigned int val, unsigned int val2) | |
| 297 | +{ | |
| 298 | + struct device *dev = &data->client->dev; | |
| 299 | + u8 range; | |
| 300 | + int ret = 0; | |
| 301 | + | |
| 302 | + if (af8133j_scales[0][0] == val && af8133j_scales[0][1] == val2) | |
| 303 | + range = AF8133J_REG_RANGE_12G; | |
| 304 | + else if (af8133j_scales[1][0] == val && af8133j_scales[1][1] == val2) | |
| 305 | + range = AF8133J_REG_RANGE_22G; | |
| 306 | + else | |
| 307 | + return -EINVAL; | |
| 308 | + | |
| 309 | + pm_runtime_disable(dev); | |
| 310 | + | |
| 311 | + /* | |
| 312 | + * When suspended, just store the new range to data->range to be | |
| 313 | + * applied later during power up. | |
| 314 | + */ | |
| 315 | + if (!pm_runtime_status_suspended(dev)) | |
| 316 | + scoped_guard(mutex, &data->mutex) | |
| 317 | + ret = regmap_write(data->regmap, | |
| 318 | + AF8133J_REG_RANGE, range); | |
| 319 | + | |
| 320 | + pm_runtime_enable(dev); | |
| 321 | + | |
| 322 | + data->range = range; | |
| 323 | + return ret; | |
| 324 | +} | |
| 325 | + | |
| 326 | +static int af8133j_write_raw(struct iio_dev *indio_dev, | |
| 327 | + struct iio_chan_spec const *chan, | |
| 328 | + int val, int val2, long mask) | |
| 329 | +{ | |
| 330 | + struct af8133j_data *data = iio_priv(indio_dev); | |
| 331 | + | |
| 332 | + switch (mask) { | |
| 333 | + case IIO_CHAN_INFO_SCALE: | |
| 334 | + return af8133j_set_scale(data, val, val2); | |
| 335 | + default: | |
| 336 | + return -EINVAL; | |
| 337 | + } | |
| 338 | +} | |
| 339 | + | |
| 340 | +static int af8133j_write_raw_get_fmt(struct iio_dev *indio_dev, | |
| 341 | + struct iio_chan_spec const *chan, | |
| 342 | + long mask) | |
| 343 | +{ | |
| 344 | + return IIO_VAL_INT_PLUS_NANO; | |
| 345 | +} | |
| 346 | + | |
| 347 | +static const struct iio_info af8133j_info = { | |
| 348 | + .read_raw = af8133j_read_raw, | |
| 349 | + .read_avail = af8133j_read_avail, | |
| 350 | + .write_raw = af8133j_write_raw, | |
| 351 | + .write_raw_get_fmt = af8133j_write_raw_get_fmt, | |
| 352 | +}; | |
| 353 | + | |
| 354 | +static irqreturn_t af8133j_trigger_handler(int irq, void *p) | |
| 355 | +{ | |
| 356 | + struct iio_poll_func *pf = p; | |
| 357 | + struct iio_dev *indio_dev = pf->indio_dev; | |
| 358 | + struct af8133j_data *data = iio_priv(indio_dev); | |
| 359 | + s64 timestamp = iio_get_time_ns(indio_dev); | |
| 360 | + struct { | |
| 361 | + __le16 values[3]; | |
| 362 | + s64 timestamp __aligned(8); | |
| 363 | + } sample; | |
| 364 | + int ret; | |
| 365 | + | |
| 366 | + memset(&sample, 0, sizeof(sample)); | |
| 367 | + | |
| 368 | + ret = af8133j_read_measurement(data, sample.values); | |
| 369 | + if (ret) | |
| 370 | + goto out_done; | |
| 371 | + | |
| 372 | + iio_push_to_buffers_with_timestamp(indio_dev, &sample, timestamp); | |
| 373 | + | |
| 374 | +out_done: | |
| 375 | + iio_trigger_notify_done(indio_dev->trig); | |
| 376 | + | |
| 377 | + return IRQ_HANDLED; | |
| 378 | +} | |
| 379 | + | |
| 380 | +static const struct regmap_config af8133j_regmap_config = { | |
| 381 | + .name = "af8133j_regmap", | |
| 382 | + .reg_bits = 8, | |
| 383 | + .val_bits = 8, | |
| 384 | + .max_register = AF8133J_REG_SWR, | |
| 385 | + .cache_type = REGCACHE_NONE, | |
| 386 | +}; | |
| 387 | + | |
| 388 | +static void af8133j_power_down_action(void *ptr) | |
| 389 | +{ | |
| 390 | + struct af8133j_data *data = ptr; | |
| 391 | + | |
| 392 | + if (!pm_runtime_status_suspended(&data->client->dev)) | |
| 393 | + af8133j_power_down(data); | |
| 394 | +} | |
| 395 | + | |
| 396 | +static int af8133j_probe(struct i2c_client *client) | |
| 397 | +{ | |
| 398 | + struct device *dev = &client->dev; | |
| 399 | + struct af8133j_data *data; | |
| 400 | + struct iio_dev *indio_dev; | |
| 401 | + struct regmap *regmap; | |
| 402 | + int ret, i; | |
| 403 | + | |
| 404 | + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); | |
| 405 | + if (!indio_dev) | |
| 406 | + return -ENOMEM; | |
| 407 | + | |
| 408 | + regmap = devm_regmap_init_i2c(client, &af8133j_regmap_config); | |
| 409 | + if (IS_ERR(regmap)) | |
| 410 | + return dev_err_probe(dev, PTR_ERR(regmap), | |
| 411 | + "regmap initialization failed\n"); | |
| 412 | + | |
| 413 | + data = iio_priv(indio_dev); | |
| 414 | + i2c_set_clientdata(client, indio_dev); | |
| 415 | + data->client = client; | |
| 416 | + data->regmap = regmap; | |
| 417 | + data->range = AF8133J_REG_RANGE_12G; | |
| 418 | + mutex_init(&data->mutex); | |
| 419 | + | |
| 420 | + data->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); | |
| 421 | + if (IS_ERR(data->reset_gpiod)) | |
| 422 | + return dev_err_probe(dev, PTR_ERR(data->reset_gpiod), | |
| 423 | + "Failed to get reset gpio\n"); | |
| 424 | + | |
| 425 | + for (i = 0; i < ARRAY_SIZE(af8133j_supply_names); i++) | |
| 426 | + data->supplies[i].supply = af8133j_supply_names[i]; | |
| 427 | + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), | |
| 428 | + data->supplies); | |
| 429 | + if (ret) | |
| 430 | + return ret; | |
| 431 | + | |
| 432 | + ret = iio_read_mount_matrix(dev, &data->orientation); | |
| 433 | + if (ret) | |
| 434 | + return dev_err_probe(dev, ret, "Failed to read mount matrix\n"); | |
| 435 | + | |
| 436 | + ret = af8133j_power_up(data); | |
| 437 | + if (ret) | |
| 438 | + return ret; | |
| 439 | + | |
| 440 | + pm_runtime_set_active(dev); | |
| 441 | + | |
| 442 | + ret = devm_add_action_or_reset(dev, af8133j_power_down_action, data); | |
| 443 | + if (ret) | |
| 444 | + return ret; | |
| 445 | + | |
| 446 | + ret = af8133j_product_check(data); | |
| 447 | + if (ret) | |
| 448 | + return ret; | |
| 449 | + | |
| 450 | + pm_runtime_get_noresume(dev); | |
| 451 | + pm_runtime_use_autosuspend(dev); | |
| 452 | + pm_runtime_set_autosuspend_delay(dev, 500); | |
| 453 | + ret = devm_pm_runtime_enable(dev); | |
| 454 | + if (ret) | |
| 455 | + return ret; | |
| 456 | + | |
| 457 | + pm_runtime_put_autosuspend(dev); | |
| 458 | + | |
| 459 | + indio_dev->info = &af8133j_info; | |
| 460 | + indio_dev->name = "af8133j"; | |
| 461 | + indio_dev->channels = af8133j_channels; | |
| 462 | + indio_dev->num_channels = ARRAY_SIZE(af8133j_channels); | |
| 463 | + indio_dev->modes = INDIO_DIRECT_MODE; | |
| 464 | + | |
| 465 | + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, | |
| 466 | + &af8133j_trigger_handler, NULL); | |
| 467 | + if (ret) | |
| 468 | + return dev_err_probe(&client->dev, ret, | |
| 469 | + "Failed to setup iio triggered buffer\n"); | |
| 470 | + | |
| 471 | + ret = devm_iio_device_register(dev, indio_dev); | |
| 472 | + if (ret) | |
| 473 | + return dev_err_probe(dev, ret, "Failed to register iio device"); | |
| 474 | + | |
| 475 | + return 0; | |
| 476 | +} | |
| 477 | + | |
| 478 | +static int af8133j_runtime_suspend(struct device *dev) | |
| 479 | +{ | |
| 480 | + struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
| 481 | + struct af8133j_data *data = iio_priv(indio_dev); | |
| 482 | + | |
| 483 | + af8133j_power_down(data); | |
| 484 | + | |
| 485 | + return 0; | |
| 486 | +} | |
| 487 | + | |
| 488 | +static int af8133j_runtime_resume(struct device *dev) | |
| 489 | +{ | |
| 490 | + struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
| 491 | + struct af8133j_data *data = iio_priv(indio_dev); | |
| 492 | + | |
| 493 | + return af8133j_power_up(data); | |
| 494 | +} | |
| 495 | + | |
| 496 | +static const struct dev_pm_ops af8133j_pm_ops = { | |
| 497 | + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) | |
| 498 | + RUNTIME_PM_OPS(af8133j_runtime_suspend, af8133j_runtime_resume, NULL) | |
| 499 | +}; | |
| 500 | + | |
| 501 | +static const struct of_device_id af8133j_of_match[] = { | |
| 502 | + { .compatible = "voltafield,af8133j", }, | |
| 503 | + { } | |
| 504 | +}; | |
| 505 | +MODULE_DEVICE_TABLE(of, af8133j_of_match); | |
| 506 | + | |
| 507 | +static const struct i2c_device_id af8133j_id[] = { | |
| 508 | + { "af8133j", 0 }, | |
| 509 | + { } | |
| 510 | +}; | |
| 511 | +MODULE_DEVICE_TABLE(i2c, af8133j_id); | |
| 512 | + | |
| 513 | +static struct i2c_driver af8133j_driver = { | |
| 514 | + .driver = { | |
| 515 | + .name = "af8133j", | |
| 516 | + .of_match_table = af8133j_of_match, | |
| 517 | + .pm = pm_ptr(&af8133j_pm_ops), | |
| 518 | + }, | |
| 519 | + .probe = af8133j_probe, | |
| 520 | + .id_table = af8133j_id, | |
| 521 | +}; | |
| 522 | + | |
| 523 | +module_i2c_driver(af8133j_driver); | |
| 524 | + | |
| 525 | +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); | |
| 526 | +MODULE_AUTHOR("Ondřej Jirman <megi@xff.cz>"); | |
| 527 | +MODULE_DESCRIPTION("Voltafield AF8133J magnetic sensor driver"); | |
| 528 | +MODULE_LICENSE("GPL"); | |
| @@ -11,7 +11,6 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <linux/mod_devicetable.h> |
| 12 | 12 | #include <linux/i2c.h> |
| 13 | 13 | #include <linux/module.h> |
| 14 | −#include <linux/acpi.h> | |
| 15 | 14 | #include <linux/regmap.h> |
| 16 | 15 | |
| 17 | 16 | #include "bmc150_magn.h" |
| @@ -68,7 +67,7 @@ static struct i2c_driver bmc150_magn_driver = { · 50 unmodified lines | ||
| 68 | 67 | .driver = { |
| 69 | 68 | .name = "bmc150_magn_i2c", |
| 70 | 69 | .of_match_table = bmc150_magn_of_match, |
| 71 | − .acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match), | |
| 70 | + .acpi_match_table = bmc150_magn_acpi_match, | |
| 72 | 71 | .pm = &bmc150_magn_pm_ops, |
| 73 | 72 | }, |
| 74 | 73 | .probe = bmc150_magn_i2c_probe, |
| @@ -10,7 +10,6 @@ · 9 unmodified lines | ||
| 10 | 10 | #include <linux/module.h> |
| 11 | 11 | #include <linux/mod_devicetable.h> |
| 12 | 12 | #include <linux/spi/spi.h> |
| 13 | −#include <linux/acpi.h> | |
| 14 | 13 | #include <linux/regmap.h> |
| 15 | 14 | |
| 16 | 15 | #include "bmc150_magn.h" |
| @@ -55,7 +54,7 @@ static struct spi_driver bmc150_magn_spi_driver = { · 38 unmodified lines | ||
| 55 | 54 | .remove = bmc150_magn_spi_remove, |
| 56 | 55 | .id_table = bmc150_magn_spi_id, |
| 57 | 56 | .driver = { |
| 58 | − .acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match), | |
| 57 | + .acpi_match_table = bmc150_magn_acpi_match, | |
| 59 | 58 | .name = "bmc150_magn_spi", |
| 60 | 59 | }, |
| 61 | 60 | }; |
| @@ -10,11 +10,11 @@ · 9 unmodified lines | ||
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | 12 | #include <linux/module.h> |
| 13 | +#include <linux/mod_devicetable.h> | |
| 13 | 14 | #include <linux/init.h> |
| 14 | 15 | #include <linux/i2c.h> |
| 15 | 16 | #include <linux/delay.h> |
| 16 | 17 | #include <linux/regmap.h> |
| 17 | −#include <linux/acpi.h> | |
| 18 | 18 | #include <linux/pm.h> |
| 19 | 19 | |
| 20 | 20 | #include <linux/iio/iio.h> |
| @@ -573,7 +573,7 @@ static struct i2c_driver mmc35240_driver = { · 552 unmodified lines | ||
| 573 | 573 | .name = MMC35240_DRV_NAME, |
| 574 | 574 | .of_match_table = mmc35240_of_match, |
| 575 | 575 | .pm = pm_sleep_ptr(&mmc35240_pm_ops), |
| 576 | − .acpi_match_table = ACPI_PTR(mmc35240_acpi_match), | |
| 576 | + .acpi_match_table = mmc35240_acpi_match, | |
| 577 | 577 | }, |
| 578 | 578 | .probe = mmc35240_probe, |
| 579 | 579 | .id_table = mmc35240_id, |
| @@ -5,8 +5,8 @@ · 4 unmodified lines | ||
| 5 | 5 | * Copyright (C) 2016 Cristina-Gabriela Moraru <cristina.moraru09@gmail.com> |
| 6 | 6 | */ |
| 7 | 7 | #include <linux/module.h> |
| 8 | +#include <linux/mod_devicetable.h> | |
| 8 | 9 | #include <linux/spi/spi.h> |
| 9 | −#include <linux/acpi.h> | |
| 10 | 10 | |
| 11 | 11 | #include <linux/iio/sysfs.h> |
| 12 | 12 | #include <linux/iio/iio.h> |
| @@ -144,7 +144,7 @@ MODULE_DEVICE_TABLE(acpi, max5487_acpi_match); · 131 unmodified lines | ||
| 144 | 144 | static struct spi_driver max5487_driver = { |
| 145 | 145 | .driver = { |
| 146 | 146 | .name = "max5487", |
| 147 | − .acpi_match_table = ACPI_PTR(max5487_acpi_match), | |
| 147 | + .acpi_match_table = max5487_acpi_match, | |
| 148 | 148 | }, |
| 149 | 149 | .id_table = max5487_id, |
| 150 | 150 | .probe = max5487_spi_probe, |
| @@ -114,6 +114,8 @@ config HSC030PA · 113 unmodified lines | ||
| 114 | 114 | depends on (I2C || SPI_MASTER) |
| 115 | 115 | select HSC030PA_I2C if I2C |
| 116 | 116 | select HSC030PA_SPI if SPI_MASTER |
| 117 | + select IIO_BUFFER | |
| 118 | + select IIO_TRIGGERED_BUFFER | |
| 117 | 119 | help |
| 118 | 120 | Say Y here to build support for the Honeywell TruStability |
| 119 | 121 | HSC and SSC pressure and temperature sensor series. |
| @@ -181,7 +183,9 @@ config MPL3115 · 61 unmodified lines | ||
| 181 | 183 | |
| 182 | 184 | config MPRLS0025PA |
| 183 | 185 | tristate "Honeywell MPRLS0025PA (MicroPressure sensors series)" |
| 184 | − depends on I2C | |
| 186 | + depends on (I2C || SPI_MASTER) | |
| 187 | + select MPRLS0025PA_I2C if I2C | |
| 188 | + select MPRLS0025PA_SPI if SPI_MASTER | |
| 185 | 189 | select IIO_BUFFER |
| 186 | 190 | select IIO_TRIGGERED_BUFFER |
| 187 | 191 | help |
| @@ -192,6 +196,16 @@ config MPRLS0025PA · 4 unmodified lines | ||
| 192 | 196 | To compile this driver as a module, choose M here: the module will be |
| 193 | 197 | called mprls0025pa. |
| 194 | 198 | |
| 199 | +config MPRLS0025PA_I2C | |
| 200 | + tristate | |
| 201 | + depends on MPRLS0025PA | |
| 202 | + depends on I2C | |
| 203 | + | |
| 204 | +config MPRLS0025PA_SPI | |
| 205 | + tristate | |
| 206 | + depends on MPRLS0025PA | |
| 207 | + depends on SPI_MASTER | |
| 208 | + | |
| 195 | 209 | config MS5611 |
| 196 | 210 | tristate "Measurement Specialties MS5611 pressure sensor driver" |
| 197 | 211 | select IIO_BUFFER |
| @@ -24,6 +24,8 @@ obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o · 23 unmodified lines | ||
| 24 | 24 | obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o |
| 25 | 25 | obj-$(CONFIG_MPL3115) += mpl3115.o |
| 26 | 26 | obj-$(CONFIG_MPRLS0025PA) += mprls0025pa.o |
| 27 | +obj-$(CONFIG_MPRLS0025PA_I2C) += mprls0025pa_i2c.o | |
| 28 | +obj-$(CONFIG_MPRLS0025PA_SPI) += mprls0025pa_spi.o | |
| 27 | 29 | obj-$(CONFIG_MS5611) += ms5611_core.o |
| 28 | 30 | obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o |
| 29 | 31 | obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o |
| @@ -11,12 +11,12 @@ · 10 unmodified lines | ||
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | 13 | #include <linux/module.h> |
| 14 | +#include <linux/mod_devicetable.h> | |
| 14 | 15 | #include <linux/i2c.h> |
| 15 | 16 | #include <linux/iio/iio.h> |
| 16 | 17 | #include <linux/iio/sysfs.h> |
| 17 | 18 | #include <linux/delay.h> |
| 18 | 19 | #include <linux/util_macros.h> |
| 19 | −#include <linux/acpi.h> | |
| 20 | 20 | |
| 21 | 21 | #include <asm/unaligned.h> |
| 22 | 22 | |
| @@ -400,20 +400,18 @@ static const struct i2c_device_id hp206c_id[] = { · 377 unmodified lines | ||
| 400 | 400 | }; |
| 401 | 401 | MODULE_DEVICE_TABLE(i2c, hp206c_id); |
| 402 | 402 | |
| 403 | −#ifdef CONFIG_ACPI | |
| 404 | 403 | static const struct acpi_device_id hp206c_acpi_match[] = { |
| 405 | 404 | {"HOP206C", 0}, |
| 406 | 405 | { }, |
| 407 | 406 | }; |
| 408 | 407 | MODULE_DEVICE_TABLE(acpi, hp206c_acpi_match); |
| 409 | −#endif | |
| 410 | 408 | |
| 411 | 409 | static struct i2c_driver hp206c_driver = { |
| 412 | 410 | .probe = hp206c_probe, |
| 413 | 411 | .id_table = hp206c_id, |
| 414 | 412 | .driver = { |
| 415 | 413 | .name = "hp206c", |
| 416 | − .acpi_match_table = ACPI_PTR(hp206c_acpi_match), | |
| 414 | + .acpi_match_table = hp206c_acpi_match, | |
| 417 | 415 | }, |
| 418 | 416 | }; |
| 419 | 417 | |
| @@ -22,8 +22,11 @@ · 21 unmodified lines | ||
| 22 | 22 | #include <linux/types.h> |
| 23 | 23 | #include <linux/units.h> |
| 24 | 24 | |
| 25 | +#include <linux/iio/buffer.h> | |
| 25 | 26 | #include <linux/iio/iio.h> |
| 26 | 27 | #include <linux/iio/sysfs.h> |
| 28 | +#include <linux/iio/trigger_consumer.h> | |
| 29 | +#include <linux/iio/triggered_buffer.h> | |
| 27 | 30 | |
| 28 | 31 | #include <asm/unaligned.h> |
| 29 | 32 | |
| @@ -297,6 +300,29 @@ static int hsc_get_measurement(struct hsc_data *data) · 267 unmodified lines | ||
| 297 | 300 | return 0; |
| 298 | 301 | } |
| 299 | 302 | |
| 303 | +static irqreturn_t hsc_trigger_handler(int irq, void *private) | |
| 304 | +{ | |
| 305 | + struct iio_poll_func *pf = private; | |
| 306 | + struct iio_dev *indio_dev = pf->indio_dev; | |
| 307 | + struct hsc_data *data = iio_priv(indio_dev); | |
| 308 | + int ret; | |
| 309 | + | |
| 310 | + ret = hsc_get_measurement(data); | |
| 311 | + if (ret) | |
| 312 | + goto error; | |
| 313 | + | |
| 314 | + memcpy(&data->scan.chan[0], &data->buffer[0], 2); | |
| 315 | + memcpy(&data->scan.chan[1], &data->buffer[2], 2); | |
| 316 | + | |
| 317 | + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, | |
| 318 | + iio_get_time_ns(indio_dev)); | |
| 319 | + | |
| 320 | +error: | |
| 321 | + iio_trigger_notify_done(indio_dev->trig); | |
| 322 | + | |
| 323 | + return IRQ_HANDLED; | |
| 324 | +} | |
| 325 | + | |
| 300 | 326 | /* |
| 301 | 327 | * IIO ABI expects |
| 302 | 328 | * value = (conv + offset) * scale |
| @@ -382,13 +408,29 @@ static const struct iio_chan_spec hsc_channels[] = { · 79 unmodified lines | ||
| 382 | 408 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
| 383 | 409 | BIT(IIO_CHAN_INFO_SCALE) | |
| 384 | 410 | BIT(IIO_CHAN_INFO_OFFSET), |
| 411 | + .scan_index = 0, | |
| 412 | + .scan_type = { | |
| 413 | + .sign = 'u', | |
| 414 | + .realbits = 14, | |
| 415 | + .storagebits = 16, | |
| 416 | + .endianness = IIO_BE, | |
| 417 | + }, | |
| 385 | 418 | }, |
| 386 | 419 | { |
| 387 | 420 | .type = IIO_TEMP, |
| 388 | 421 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
| 389 | 422 | BIT(IIO_CHAN_INFO_SCALE) | |
| 390 | 423 | BIT(IIO_CHAN_INFO_OFFSET), |
| 424 | + .scan_index = 1, | |
| 425 | + .scan_type = { | |
| 426 | + .sign = 'u', | |
| 427 | + .realbits = 11, | |
| 428 | + .storagebits = 16, | |
| 429 | + .shift = 5, | |
| 430 | + .endianness = IIO_BE, | |
| 431 | + }, | |
| 391 | 432 | }, |
| 433 | + IIO_CHAN_SOFT_TIMESTAMP(2), | |
| 392 | 434 | }; |
| 393 | 435 | |
| 394 | 436 | static const struct iio_info hsc_info = { |
| @@ -406,7 +448,7 @@ int hsc_common_probe(struct device *dev, hsc_recv_fn recv) · 11 unmodified lines | ||
| 406 | 448 | struct hsc_data *hsc; |
| 407 | 449 | struct iio_dev *indio_dev; |
| 408 | 450 | const char *triplet; |
| 409 | − u64 tmp; | |
| 451 | + s64 tmp; | |
| 410 | 452 | int ret; |
| 411 | 453 | |
| 412 | 454 | indio_dev = devm_iio_device_alloc(dev, sizeof(*hsc)); |
| @@ -485,6 +527,11 @@ int hsc_common_probe(struct device *dev, hsc_recv_fn recv) · 72 unmodified lines | ||
| 485 | 527 | indio_dev->channels = hsc->chip->channels; |
| 486 | 528 | indio_dev->num_channels = hsc->chip->num_channels; |
| 487 | 529 | |
| 530 | + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, | |
| 531 | + hsc_trigger_handler, NULL); | |
| 532 | + if (ret) | |
| 533 | + return ret; | |
| 534 | + | |
| 488 | 535 | return devm_iio_device_register(dev, indio_dev); |
| 489 | 536 | } |
| 490 | 537 | EXPORT_SYMBOL_NS(hsc_common_probe, IIO_HONEYWELL_HSC030PA); |
| @@ -10,7 +10,10 @@ · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | #include <linux/types.h> |
| 12 | 12 | |
| 13 | +#include <linux/iio/iio.h> | |
| 14 | + | |
| 13 | 15 | #define HSC_REG_MEASUREMENT_RD_SIZE 4 |
| 16 | +#define HSC_RESP_TIME_MS 2 | |
| 14 | 17 | |
| 15 | 18 | struct device; |
| 16 | 19 | |
| @@ -53,6 +56,10 @@ struct hsc_data { · 36 unmodified lines | ||
| 53 | 56 | s32 p_scale_dec; |
| 54 | 57 | s64 p_offset; |
| 55 | 58 | s32 p_offset_dec; |
| 59 | + struct { | |
| 60 | + __be16 chan[2]; | |
| 61 | + s64 timestamp __aligned(8); | |
| 62 | + } scan; | |
| 56 | 63 | u8 buffer[HSC_REG_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN); |
| 57 | 64 | }; |
| 58 | 65 | |
| @@ -4,14 +4,17 @@ · 3 unmodified lines | ||
| 4 | 4 | * |
| 5 | 5 | * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro> |
| 6 | 6 | * |
| 7 | − * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf [hsc] | |
| 8 | − * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/common/documents/sps-siot-i2c-comms-digital-output-pressure-sensors-tn-008201-3-en-ciid-45841.pdf [i2c related] | |
| 7 | + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/common/documents/sps-siot-i2c-comms-digital-output-pressure-sensors-tn-008201-3-en-ciid-45841.pdf | |
| 8 | + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-sleep-mode-technical-note-008286-1-en-ciid-155793.pdf | |
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | +#include <linux/delay.h> | |
| 12 | +#include <linux/device.h> | |
| 11 | 13 | #include <linux/errno.h> |
| 12 | 14 | #include <linux/i2c.h> |
| 13 | 15 | #include <linux/mod_devicetable.h> |
| 14 | 16 | #include <linux/module.h> |
| 17 | +#include <linux/types.h> | |
| 15 | 18 | |
| 16 | 19 | #include <linux/iio/iio.h> |
| 17 | 20 | |
| @@ -23,6 +26,8 @@ static int hsc_i2c_recv(struct hsc_data *data) · 5 unmodified lines | ||
| 23 | 26 | struct i2c_msg msg; |
| 24 | 27 | int ret; |
| 25 | 28 | |
| 29 | + msleep_interruptible(HSC_RESP_TIME_MS); | |
| 30 | + | |
| 26 | 31 | msg.addr = client->addr; |
| 27 | 32 | msg.flags = client->flags | I2C_M_RD; |
| 28 | 33 | msg.len = HSC_REG_MEASUREMENT_RD_SIZE; |
| @@ -4,13 +4,17 @@ · 3 unmodified lines | ||
| 4 | 4 | * |
| 5 | 5 | * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro> |
| 6 | 6 | * |
| 7 | − * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf | |
| 7 | + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-spi-comms-digital-ouptu-pressure-sensors-tn-008202-3-en-ciid-45843.pdf | |
| 8 | + * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-sleep-mode-technical-note-008286-1-en-ciid-155793.pdf | |
| 8 | 9 | */ |
| 9 | 10 | |
| 11 | +#include <linux/delay.h> | |
| 12 | +#include <linux/device.h> | |
| 10 | 13 | #include <linux/mod_devicetable.h> |
| 11 | 14 | #include <linux/module.h> |
| 12 | 15 | #include <linux/spi/spi.h> |
| 13 | 16 | #include <linux/stddef.h> |
| 17 | +#include <linux/types.h> | |
| 14 | 18 | |
| 15 | 19 | #include <linux/iio/iio.h> |
| 16 | 20 | |
| @@ -25,6 +29,7 @@ static int hsc_spi_recv(struct hsc_data *data) · 8 unmodified lines | ||
| 25 | 29 | .len = HSC_REG_MEASUREMENT_RD_SIZE, |
| 26 | 30 | }; |
| 27 | 31 | |
| 32 | + msleep_interruptible(HSC_RESP_TIME_MS); | |
| 28 | 33 | return spi_sync_transfer(spi, &xfer, 1); |
| 29 | 34 | } |
| 30 | 35 | |
| @@ -5,17 +5,13 @@ · 4 unmodified lines | ||
| 5 | 5 | * Copyright (c) Andreas Klinger <ak@it-klinger.de> |
| 6 | 6 | * |
| 7 | 7 | * Data sheet: |
| 8 | − * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/ | |
| 9 | − * products/sensors/pressure-sensors/board-mount-pressure-sensors/ | |
| 10 | − * micropressure-mpr-series/documents/ | |
| 11 | − * sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf | |
| 8 | + * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf | |
| 12 | 9 | * |
| 13 | − * 7-bit I2C default slave address: 0x18 | |
| 14 | 10 | */ |
| 15 | 11 | |
| 16 | −#include <linux/delay.h> | |
| 17 | −#include <linux/device.h> | |
| 18 | −#include <linux/i2c.h> | |
| 12 | +#include <linux/array_size.h> | |
| 13 | +#include <linux/bitfield.h> | |
| 14 | +#include <linux/bits.h> | |
| 19 | 15 | #include <linux/math64.h> |
| 20 | 16 | #include <linux/mod_devicetable.h> |
| 21 | 17 | #include <linux/module.h> |
| @@ -25,7 +21,6 @@ · 3 unmodified lines | ||
| 25 | 21 | #include <linux/gpio/consumer.h> |
| 26 | 22 | |
| 27 | 23 | #include <linux/iio/buffer.h> |
| 28 | −#include <linux/iio/iio.h> | |
| 29 | 24 | #include <linux/iio/trigger_consumer.h> |
| 30 | 25 | #include <linux/iio/triggered_buffer.h> |
| 31 | 26 | |
| @@ -33,11 +28,15 @@ · 1 unmodified line | ||
| 33 | 28 | |
| 34 | 29 | #include <asm/unaligned.h> |
| 35 | 30 | |
| 36 | −/* bits in i2c status byte */ | |
| 37 | −#define MPR_I2C_POWER BIT(6) /* device is powered */ | |
| 38 | −#define MPR_I2C_BUSY BIT(5) /* device is busy */ | |
| 39 | −#define MPR_I2C_MEMORY BIT(2) /* integrity test passed */ | |
| 40 | −#define MPR_I2C_MATH BIT(0) /* internal math saturation */ | |
| 31 | +#include "mprls0025pa.h" | |
| 32 | + | |
| 33 | +/* bits in status byte */ | |
| 34 | +#define MPR_ST_POWER BIT(6) /* device is powered */ | |
| 35 | +#define MPR_ST_BUSY BIT(5) /* device is busy */ | |
| 36 | +#define MPR_ST_MEMORY BIT(2) /* integrity test passed */ | |
| 37 | +#define MPR_ST_MATH BIT(0) /* internal math saturation */ | |
| 38 | + | |
| 39 | +#define MPR_ST_ERR_FLAG (MPR_ST_BUSY | MPR_ST_MEMORY | MPR_ST_MATH) | |
| 41 | 40 | |
| 42 | 41 | /* |
| 43 | 42 | * support _RAW sysfs interface: |
| @@ -70,60 +69,87 @@ · 26 unmodified lines | ||
| 70 | 69 | * transfer function B: 2.5% to 22.5% of 2^24 |
| 71 | 70 | * transfer function C: 20% to 80% of 2^24 |
| 72 | 71 | */ |
| 73 | −enum mpr_func_id { | |
| 74 | − MPR_FUNCTION_A, | |
| 75 | − MPR_FUNCTION_B, | |
| 76 | − MPR_FUNCTION_C, | |
| 77 | −}; | |
| 78 | − | |
| 79 | 72 | struct mpr_func_spec { |
| 80 | 73 | u32 output_min; |
| 81 | 74 | u32 output_max; |
| 82 | 75 | }; |
| 83 | 76 | |
| 84 | 77 | static const struct mpr_func_spec mpr_func_spec[] = { |
| 85 | − [MPR_FUNCTION_A] = {.output_min = 1677722, .output_max = 15099494}, | |
| 86 | − [MPR_FUNCTION_B] = {.output_min = 419430, .output_max = 3774874}, | |
| 87 | − [MPR_FUNCTION_C] = {.output_min = 3355443, .output_max = 13421773}, | |
| 78 | + [MPR_FUNCTION_A] = { .output_min = 1677722, .output_max = 15099494 }, | |
| 79 | + [MPR_FUNCTION_B] = { .output_min = 419430, .output_max = 3774874 }, | |
| 80 | + [MPR_FUNCTION_C] = { .output_min = 3355443, .output_max = 13421773 }, | |
| 88 | 81 | }; |
| 89 | 82 | |
| 90 | −struct mpr_chan { | |
| 91 | − s32 pres; /* pressure value */ | |
| 92 | − s64 ts; /* timestamp */ | |
| 83 | +enum mpr_variants { | |
| 84 | + MPR0001BA = 0x00, MPR01_6BA = 0x01, MPR02_5BA = 0x02, MPR0060MG = 0x03, | |
| 85 | + MPR0100MG = 0x04, MPR0160MG = 0x05, MPR0250MG = 0x06, MPR0400MG = 0x07, | |
| 86 | + MPR0600MG = 0x08, MPR0001BG = 0x09, MPR01_6BG = 0x0a, MPR02_5BG = 0x0b, | |
| 87 | + MPR0100KA = 0x0c, MPR0160KA = 0x0d, MPR0250KA = 0x0e, MPR0006KG = 0x0f, | |
| 88 | + MPR0010KG = 0x10, MPR0016KG = 0x11, MPR0025KG = 0x12, MPR0040KG = 0x13, | |
| 89 | + MPR0060KG = 0x14, MPR0100KG = 0x15, MPR0160KG = 0x16, MPR0250KG = 0x17, | |
| 90 | + MPR0015PA = 0x18, MPR0025PA = 0x19, MPR0030PA = 0x1a, MPR0001PG = 0x1b, | |
| 91 | + MPR0005PG = 0x1c, MPR0015PG = 0x1d, MPR0030PG = 0x1e, MPR0300YG = 0x1f, | |
| 92 | + MPR_VARIANTS_MAX | |
| 93 | +}; | |
| 94 | + | |
| 95 | +static const char * const mpr_triplet_variants[MPR_VARIANTS_MAX] = { | |
| 96 | + [MPR0001BA] = "0001BA", [MPR01_6BA] = "01.6BA", [MPR02_5BA] = "02.5BA", | |
| 97 | + [MPR0060MG] = "0060MG", [MPR0100MG] = "0100MG", [MPR0160MG] = "0160MG", | |
| 98 | + [MPR0250MG] = "0250MG", [MPR0400MG] = "0400MG", [MPR0600MG] = "0600MG", | |
| 99 | + [MPR0001BG] = "0001BG", [MPR01_6BG] = "01.6BG", [MPR02_5BG] = "02.5BG", | |
| 100 | + [MPR0100KA] = "0100KA", [MPR0160KA] = "0160KA", [MPR0250KA] = "0250KA", | |
| 101 | + [MPR0006KG] = "0006KG", [MPR0010KG] = "0010KG", [MPR0016KG] = "0016KG", | |
| 102 | + [MPR0025KG] = "0025KG", [MPR0040KG] = "0040KG", [MPR0060KG] = "0060KG", | |
| 103 | + [MPR0100KG] = "0100KG", [MPR0160KG] = "0160KG", [MPR0250KG] = "0250KG", | |
| 104 | + [MPR0015PA] = "0015PA", [MPR0025PA] = "0025PA", [MPR0030PA] = "0030PA", | |
| 105 | + [MPR0001PG] = "0001PG", [MPR0005PG] = "0005PG", [MPR0015PG] = "0015PG", | |
| 106 | + [MPR0030PG] = "0030PG", [MPR0300YG] = "0300YG" | |
| 107 | +}; | |
| 108 | + | |
| 109 | +/** | |
| 110 | + * struct mpr_range_config - list of pressure ranges based on nomenclature | |
| 111 | + * @pmin: lowest pressure that can be measured | |
| 112 | + * @pmax: highest pressure that can be measured | |
| 113 | + */ | |
| 114 | +struct mpr_range_config { | |
| 115 | + const s32 pmin; | |
| 116 | + const s32 pmax; | |
| 93 | 117 | }; |
| 94 | 118 | |
| 95 | −struct mpr_data { | |
| 96 | − struct i2c_client *client; | |
| 97 | − struct mutex lock; /* | |
| 98 | − * access to device during read | |
| 99 | − */ | |
| 100 | − u32 pmin; /* minimal pressure in pascal */ | |
| 101 | − u32 pmax; /* maximal pressure in pascal */ | |
| 102 | − enum mpr_func_id function; /* transfer function */ | |
| 103 | − u32 outmin; /* | |
| 104 | − * minimal numerical range raw | |
| 105 | − * value from sensor | |
| 106 | − */ | |
| 107 | − u32 outmax; /* | |
| 108 | − * maximal numerical range raw | |
| 109 | − * value from sensor | |
| 110 | − */ | |
| 111 | − int scale; /* int part of scale */ | |
| 112 | − int scale2; /* nano part of scale */ | |
| 113 | − int offset; /* int part of offset */ | |
| 114 | − int offset2; /* nano part of offset */ | |
| 115 | − struct gpio_desc *gpiod_reset; /* reset */ | |
| 116 | − int irq; /* | |
| 117 | − * end of conversion irq; | |
| 118 | − * used to distinguish between | |
| 119 | − * irq mode and reading in a | |
| 120 | − * loop until data is ready | |
| 121 | − */ | |
| 122 | − struct completion completion; /* handshake from irq to read */ | |
| 123 | − struct mpr_chan chan; /* | |
| 124 | − * channel values for buffered | |
| 125 | − * mode | |
| 126 | − */ | |
| 119 | +/* All min max limits have been converted to pascals */ | |
| 120 | +static const struct mpr_range_config mpr_range_config[MPR_VARIANTS_MAX] = { | |
| 121 | + [MPR0001BA] = { .pmin = 0, .pmax = 100000 }, | |
| 122 | + [MPR01_6BA] = { .pmin = 0, .pmax = 160000 }, | |
| 123 | + [MPR02_5BA] = { .pmin = 0, .pmax = 250000 }, | |
| 124 | + [MPR0060MG] = { .pmin = 0, .pmax = 6000 }, | |
| 125 | + [MPR0100MG] = { .pmin = 0, .pmax = 10000 }, | |
| 126 | + [MPR0160MG] = { .pmin = 0, .pmax = 16000 }, | |
| 127 | + [MPR0250MG] = { .pmin = 0, .pmax = 25000 }, | |
| 128 | + [MPR0400MG] = { .pmin = 0, .pmax = 40000 }, | |
| 129 | + [MPR0600MG] = { .pmin = 0, .pmax = 60000 }, | |
| 130 | + [MPR0001BG] = { .pmin = 0, .pmax = 100000 }, | |
| 131 | + [MPR01_6BG] = { .pmin = 0, .pmax = 160000 }, | |
| 132 | + [MPR02_5BG] = { .pmin = 0, .pmax = 250000 }, | |
| 133 | + [MPR0100KA] = { .pmin = 0, .pmax = 100000 }, | |
| 134 | + [MPR0160KA] = { .pmin = 0, .pmax = 160000 }, | |
| 135 | + [MPR0250KA] = { .pmin = 0, .pmax = 250000 }, | |
| 136 | + [MPR0006KG] = { .pmin = 0, .pmax = 6000 }, | |
| 137 | + [MPR0010KG] = { .pmin = 0, .pmax = 10000 }, | |
| 138 | + [MPR0016KG] = { .pmin = 0, .pmax = 16000 }, | |
| 139 | + [MPR0025KG] = { .pmin = 0, .pmax = 25000 }, | |
| 140 | + [MPR0040KG] = { .pmin = 0, .pmax = 40000 }, | |
| 141 | + [MPR0060KG] = { .pmin = 0, .pmax = 60000 }, | |
| 142 | + [MPR0100KG] = { .pmin = 0, .pmax = 100000 }, | |
| 143 | + [MPR0160KG] = { .pmin = 0, .pmax = 160000 }, | |
| 144 | + [MPR0250KG] = { .pmin = 0, .pmax = 250000 }, | |
| 145 | + [MPR0015PA] = { .pmin = 0, .pmax = 103421 }, | |
| 146 | + [MPR0025PA] = { .pmin = 0, .pmax = 172369 }, | |
| 147 | + [MPR0030PA] = { .pmin = 0, .pmax = 206843 }, | |
| 148 | + [MPR0001PG] = { .pmin = 0, .pmax = 6895 }, | |
| 149 | + [MPR0005PG] = { .pmin = 0, .pmax = 34474 }, | |
| 150 | + [MPR0015PG] = { .pmin = 0, .pmax = 103421 }, | |
| 151 | + [MPR0030PG] = { .pmin = 0, .pmax = 206843 }, | |
| 152 | + [MPR0300YG] = { .pmin = 0, .pmax = 39997 } | |
| 127 | 153 | }; |
| 128 | 154 | |
| 129 | 155 | static const struct iio_chan_spec mpr_channels[] = { |
| @@ -153,11 +179,11 @@ static void mpr_reset(struct mpr_data *data) · 23 unmodified lines | ||
| 153 | 179 | } |
| 154 | 180 | |
| 155 | 181 | /** |
| 156 | − * mpr_read_pressure() - Read pressure value from sensor via I2C | |
| 182 | + * mpr_read_pressure() - Read pressure value from sensor | |
| 157 | 183 | * @data: Pointer to private data struct. |
| 158 | 184 | * @press: Output value read from sensor. |
| 159 | 185 | * |
| 160 | − * Reading from the sensor by sending and receiving I2C telegrams. | |
| 186 | + * Reading from the sensor by sending and receiving telegrams. | |
| 161 | 187 | * |
| 162 | 188 | * If there is an end of conversion (EOC) interrupt registered the function |
| 163 | 189 | * waits for a maximum of one second for the interrupt. |
| @@ -170,25 +196,17 @@ static void mpr_reset(struct mpr_data *data) · 6 unmodified lines | ||
| 170 | 196 | */ |
| 171 | 197 | static int mpr_read_pressure(struct mpr_data *data, s32 *press) |
| 172 | 198 | { |
| 173 | − struct device *dev = &data->client->dev; | |
| 199 | + struct device *dev = data->dev; | |
| 174 | 200 | int ret, i; |
| 175 | − u8 wdata[] = {0xAA, 0x00, 0x00}; | |
| 176 | − s32 status; | |
| 177 | 201 | int nloops = 10; |
| 178 | − u8 buf[4]; | |
| 179 | 202 | |
| 180 | 203 | reinit_completion(&data->completion); |
| 181 | 204 | |
| 182 | − ret = i2c_master_send(data->client, wdata, sizeof(wdata)); | |
| 205 | + ret = data->ops->write(data, MPR_CMD_SYNC, MPR_PKT_SYNC_LEN); | |
| 183 | 206 | if (ret < 0) { |
| 184 | 207 | dev_err(dev, "error while writing ret: %d\n", ret); |
| 185 | 208 | return ret; |
| 186 | 209 | } |
| 187 | − if (ret != sizeof(wdata)) { | |
| 188 | − dev_err(dev, "received size doesn't fit - ret: %d / %u\n", ret, | |
| 189 | − (u32)sizeof(wdata)); | |
| 190 | − return -EIO; | |
| 191 | − } | |
| 192 | 210 | |
| 193 | 211 | if (data->irq > 0) { |
| 194 | 212 | ret = wait_for_completion_timeout(&data->completion, HZ); |
| @@ -206,14 +224,14 @@ static int mpr_read_pressure(struct mpr_data *data, s32 *press) · 11 unmodified lines | ||
| 206 | 224 | * quite long |
| 207 | 225 | */ |
| 208 | 226 | usleep_range(5000, 10000); |
| 209 | − status = i2c_smbus_read_byte(data->client); | |
| 210 | − if (status < 0) { | |
| 227 | + ret = data->ops->read(data, MPR_CMD_NOP, 1); | |
| 228 | + if (ret < 0) { | |
| 211 | 229 | dev_err(dev, |
| 212 | 230 | "error while reading, status: %d\n", |
| 213 | − status); | |
| 214 | − return status; | |
| 231 | + ret); | |
| 232 | + return ret; | |
| 215 | 233 | } |
| 216 | − if (!(status & MPR_I2C_BUSY)) | |
| 234 | + if (!(data->buffer[0] & MPR_ST_ERR_FLAG)) | |
| 217 | 235 | break; |
| 218 | 236 | } |
| 219 | 237 | if (i == nloops) { |
| @@ -222,29 +240,19 @@ static int mpr_read_pressure(struct mpr_data *data, s32 *press) · 2 unmodified lines | ||
| 222 | 240 | } |
| 223 | 241 | } |
| 224 | 242 | |
| 225 | − ret = i2c_master_recv(data->client, buf, sizeof(buf)); | |
| 226 | − if (ret < 0) { | |
| 227 | − dev_err(dev, "error in i2c_master_recv ret: %d\n", ret); | |
| 243 | + ret = data->ops->read(data, MPR_CMD_NOP, MPR_PKT_NOP_LEN); | |
| 244 | + if (ret < 0) | |
| 228 | 245 | return ret; |
| 229 | − } | |
| 230 | − if (ret != sizeof(buf)) { | |
| 231 | − dev_err(dev, "received size doesn't fit - ret: %d / %u\n", ret, | |
| 232 | − (u32)sizeof(buf)); | |
| 233 | − return -EIO; | |
| 234 | − } | |
| 235 | 246 | |
| 236 | − if (buf[0] & MPR_I2C_BUSY) { | |
| 237 | − /* | |
| 238 | − * it should never be the case that status still indicates | |
| 239 | − * business | |
| 240 | − */ | |
| 241 | − dev_err(dev, "data still not ready: %08x\n", buf[0]); | |
| 247 | + if (data->buffer[0] & MPR_ST_ERR_FLAG) { | |
| 248 | + dev_err(data->dev, | |
| 249 | + "unexpected status byte %02x\n", data->buffer[0]); | |
| 242 | 250 | return -ETIMEDOUT; |
| 243 | 251 | } |
| 244 | 252 | |
| 245 | − *press = get_unaligned_be24(&buf[1]); | |
| 253 | + *press = get_unaligned_be24(&data->buffer[1]); | |
| 246 | 254 | |
| 247 | − dev_dbg(dev, "received: %*ph cnt: %d\n", ret, buf, *press); | |
| 255 | + dev_dbg(dev, "received: %*ph cnt: %d\n", ret, data->buffer, *press); | |
| 248 | 256 | |
| 249 | 257 | return 0; |
| 250 | 258 | } |
| @@ -271,7 +279,7 @@ static irqreturn_t mpr_trigger_handler(int irq, void *p) · 20 unmodified lines | ||
| 271 | 279 | goto err; |
| 272 | 280 | |
| 273 | 281 | iio_push_to_buffers_with_timestamp(indio_dev, &data->chan, |
| 274 | − iio_get_time_ns(indio_dev)); | |
| 282 | + iio_get_time_ns(indio_dev)); | |
| 275 | 283 | |
| 276 | 284 | err: |
| 277 | 285 | mutex_unlock(&data->lock); |
| @@ -316,25 +324,23 @@ static const struct iio_info mpr_info = { · 38 unmodified lines | ||
| 316 | 324 | .read_raw = &mpr_read_raw, |
| 317 | 325 | }; |
| 318 | 326 | |
| 319 | −static int mpr_probe(struct i2c_client *client) | |
| 327 | +int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq) | |
| 320 | 328 | { |
| 321 | 329 | int ret; |
| 322 | 330 | struct mpr_data *data; |
| 323 | 331 | struct iio_dev *indio_dev; |
| 324 | − struct device *dev = &client->dev; | |
| 332 | + const char *triplet; | |
| 325 | 333 | s64 scale, offset; |
| 326 | − | |
| 327 | − if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) | |
| 328 | − return dev_err_probe(dev, -EOPNOTSUPP, | |
| 329 | − "I2C functionality not supported\n"); | |
| 334 | + u32 func; | |
| 330 | 335 | |
| 331 | 336 | indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); |
| 332 | 337 | if (!indio_dev) |
| 333 | − return dev_err_probe(dev, -ENOMEM, "couldn't get iio_dev\n"); | |
| 338 | + return -ENOMEM; | |
| 334 | 339 | |
| 335 | 340 | data = iio_priv(indio_dev); |
| 336 | − data->client = client; | |
| 337 | − data->irq = client->irq; | |
| 341 | + data->dev = dev; | |
| 342 | + data->ops = ops; | |
| 343 | + data->irq = irq; | |
| 338 | 344 | |
| 339 | 345 | mutex_init(&data->lock); |
| 340 | 346 | init_completion(&data->completion); |
| @@ -348,103 +354,102 @@ static int mpr_probe(struct i2c_client *client) · 7 unmodified lines | ||
| 348 | 354 | ret = devm_regulator_get_enable(dev, "vdd"); |
| 349 | 355 | if (ret) |
| 350 | 356 | return dev_err_probe(dev, ret, |
| 351 | − "can't get and enable vdd supply\n"); | |
| 357 | + "can't get and enable vdd supply\n"); | |
| 352 | 358 | |
| 353 | − if (dev_fwnode(dev)) { | |
| 359 | + ret = data->ops->init(data->dev); | |
| 360 | + if (ret) | |
| 361 | + return ret; | |
| 362 | + | |
| 363 | + ret = device_property_read_u32(dev, | |
| 364 | + "honeywell,transfer-function", &func); | |
| 365 | + if (ret) | |
| 366 | + return dev_err_probe(dev, ret, | |
| 367 | + "honeywell,transfer-function could not be read\n"); | |
| 368 | + data->function = func - 1; | |
| 369 | + if (data->function > MPR_FUNCTION_C) | |
| 370 | + return dev_err_probe(dev, -EINVAL, | |
| 371 | + "honeywell,transfer-function %d invalid\n", | |
| 372 | + data->function); | |
| 373 | + | |
| 374 | + ret = device_property_read_string(dev, "honeywell,pressure-triplet", | |
| 375 | + &triplet); | |
| 376 | + if (ret) { | |
| 354 | 377 | ret = device_property_read_u32(dev, "honeywell,pmin-pascal", |
| 355 | − &data->pmin); | |
| 378 | + &data->pmin); | |
| 356 | 379 | if (ret) |
| 357 | 380 | return dev_err_probe(dev, ret, |
| 358 | − "honeywell,pmin-pascal could not be read\n"); | |
| 381 | + "honeywell,pmin-pascal could not be read\n"); | |
| 382 | + | |
| 359 | 383 | ret = device_property_read_u32(dev, "honeywell,pmax-pascal", |
| 360 | − &data->pmax); | |
| 384 | + &data->pmax); | |
| 361 | 385 | if (ret) |
| 362 | 386 | return dev_err_probe(dev, ret, |
| 363 | − "honeywell,pmax-pascal could not be read\n"); | |
| 364 | − ret = device_property_read_u32(dev, | |
| 365 | − "honeywell,transfer-function", &data->function); | |
| 366 | − if (ret) | |
| 367 | − return dev_err_probe(dev, ret, | |
| 368 | − "honeywell,transfer-function could not be read\n"); | |
| 369 | − if (data->function > MPR_FUNCTION_C) | |
| 370 | − return dev_err_probe(dev, -EINVAL, | |
| 371 | − "honeywell,transfer-function %d invalid\n", | |
| 372 | − data->function); | |
| 387 | + "honeywell,pmax-pascal could not be read\n"); | |
| 373 | 388 | } else { |
| 374 | − /* when loaded as i2c device we need to use default values */ | |
| 375 | − dev_notice(dev, "firmware node not found; using defaults\n"); | |
| 376 | − data->pmin = 0; | |
| 377 | − data->pmax = 172369; /* 25 psi */ | |
| 378 | − data->function = MPR_FUNCTION_A; | |
| 389 | + ret = device_property_match_property_string(dev, | |
| 390 | + "honeywell,pressure-triplet", | |
| 391 | + mpr_triplet_variants, | |
| 392 | + MPR_VARIANTS_MAX); | |
| 393 | + if (ret < 0) | |
| 394 | + return dev_err_probe(dev, -EINVAL, | |
| 395 | + "honeywell,pressure-triplet is invalid\n"); | |
| 396 | + | |
| 397 | + data->pmin = mpr_range_config[ret].pmin; | |
| 398 | + data->pmax = mpr_range_config[ret].pmax; | |
| 379 | 399 | } |
| 380 | 400 | |
| 401 | + if (data->pmin >= data->pmax) | |
| 402 | + return dev_err_probe(dev, -EINVAL, | |
| 403 | + "pressure limits are invalid\n"); | |
| 404 | + | |
| 381 | 405 | data->outmin = mpr_func_spec[data->function].output_min; |
| 382 | 406 | data->outmax = mpr_func_spec[data->function].output_max; |
| 383 | 407 | |
| 384 | 408 | /* use 64 bit calculation for preserving a reasonable precision */ |
| 385 | 409 | scale = div_s64(((s64)(data->pmax - data->pmin)) * NANO, |
| 386 | − data->outmax - data->outmin); | |
| 410 | + data->outmax - data->outmin); | |
| 387 | 411 | data->scale = div_s64_rem(scale, NANO, &data->scale2); |
| 388 | 412 | /* |
| 389 | 413 | * multiply with NANO before dividing by scale and later divide by NANO |
| 390 | 414 | * again. |
| 391 | 415 | */ |
| 392 | 416 | offset = ((-1LL) * (s64)data->outmin) * NANO - |
| 393 | − div_s64(div_s64((s64)data->pmin * NANO, scale), NANO); | |
| 417 | + div_s64(div_s64((s64)data->pmin * NANO, scale), NANO); | |
| 394 | 418 | data->offset = div_s64_rem(offset, NANO, &data->offset2); |
| 395 | 419 | |
| 396 | 420 | if (data->irq > 0) { |
| 397 | 421 | ret = devm_request_irq(dev, data->irq, mpr_eoc_handler, |
| 398 | − IRQF_TRIGGER_RISING, client->name, data); | |
| 422 | + IRQF_TRIGGER_RISING, | |
| 423 | + dev_name(dev), | |
| 424 | + data); | |
| 399 | 425 | if (ret) |
| 400 | 426 | return dev_err_probe(dev, ret, |
| 401 | − "request irq %d failed\n", data->irq); | |
| 427 | + "request irq %d failed\n", data->irq); | |
| 402 | 428 | } |
| 403 | 429 | |
| 404 | 430 | data->gpiod_reset = devm_gpiod_get_optional(dev, "reset", |
| 405 | − GPIOD_OUT_HIGH); | |
| 431 | + GPIOD_OUT_HIGH); | |
| 406 | 432 | if (IS_ERR(data->gpiod_reset)) |
| 407 | 433 | return dev_err_probe(dev, PTR_ERR(data->gpiod_reset), |
| 408 | − "request reset-gpio failed\n"); | |
| 434 | + "request reset-gpio failed\n"); | |
| 409 | 435 | |
| 410 | 436 | mpr_reset(data); |
| 411 | 437 | |
| 412 | 438 | ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, |
| 413 | − mpr_trigger_handler, NULL); | |
| 439 | + mpr_trigger_handler, NULL); | |
| 414 | 440 | if (ret) |
| 415 | 441 | return dev_err_probe(dev, ret, |
| 416 | − "iio triggered buffer setup failed\n"); | |
| 442 | + "iio triggered buffer setup failed\n"); | |
| 417 | 443 | |
| 418 | 444 | ret = devm_iio_device_register(dev, indio_dev); |
| 419 | 445 | if (ret) |
| 420 | 446 | return dev_err_probe(dev, ret, |
| 421 | − "unable to register iio device\n"); | |
| 447 | + "unable to register iio device\n"); | |
| 422 | 448 | |
| 423 | 449 | return 0; |
| 424 | 450 | } |
| 425 | − | |
| 426 | −static const struct of_device_id mpr_matches[] = { | |
| 427 | − { .compatible = "honeywell,mprls0025pa" }, | |
| 428 | − { } | |
| 429 | −}; | |
| 430 | −MODULE_DEVICE_TABLE(of, mpr_matches); | |
| 431 | − | |
| 432 | −static const struct i2c_device_id mpr_id[] = { | |
| 433 | − { "mprls0025pa" }, | |
| 434 | − { } | |
| 435 | −}; | |
| 436 | −MODULE_DEVICE_TABLE(i2c, mpr_id); | |
| 437 | − | |
| 438 | −static struct i2c_driver mpr_driver = { | |
| 439 | − .probe = mpr_probe, | |
| 440 | − .id_table = mpr_id, | |
| 441 | − .driver = { | |
| 442 | − .name = "mprls0025pa", | |
| 443 | − .of_match_table = mpr_matches, | |
| 444 | − }, | |
| 445 | −}; | |
| 446 | −module_i2c_driver(mpr_driver); | |
| 451 | +EXPORT_SYMBOL_NS(mpr_common_probe, IIO_HONEYWELL_MPRLS0025PA); | |
| 447 | 452 | |
| 448 | 453 | MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>"); |
| 449 | −MODULE_DESCRIPTION("Honeywell MPRLS0025PA I2C driver"); | |
| 454 | +MODULE_DESCRIPTION("Honeywell MPR pressure sensor core driver"); | |
| 450 | 455 | MODULE_LICENSE("GPL"); |
| @@ -0,0 +1,102 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +/* | |
| 3 | + * MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver | |
| 4 | + * | |
| 5 | + * Copyright (c) Andreas Klinger <ak@it-klinger.de> | |
| 6 | + * | |
| 7 | + * Data sheet: | |
| 8 | + * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#ifndef _MPRLS0025PA_H | |
| 12 | +#define _MPRLS0025PA_H | |
| 13 | + | |
| 14 | +#include <linux/completion.h> | |
| 15 | +#include <linux/delay.h> | |
| 16 | +#include <linux/device.h> | |
| 17 | +#include <linux/mutex.h> | |
| 18 | +#include <linux/stddef.h> | |
| 19 | +#include <linux/types.h> | |
| 20 | + | |
| 21 | +#include <linux/iio/iio.h> | |
| 22 | + | |
| 23 | +#define MPR_MEASUREMENT_RD_SIZE 4 | |
| 24 | +#define MPR_CMD_NOP 0xf0 | |
| 25 | +#define MPR_CMD_SYNC 0xaa | |
| 26 | +#define MPR_PKT_NOP_LEN MPR_MEASUREMENT_RD_SIZE | |
| 27 | +#define MPR_PKT_SYNC_LEN 3 | |
| 28 | + | |
| 29 | +struct device; | |
| 30 | + | |
| 31 | +struct iio_chan_spec; | |
| 32 | +struct iio_dev; | |
| 33 | + | |
| 34 | +struct mpr_data; | |
| 35 | +struct mpr_ops; | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * struct mpr_chan | |
| 39 | + * @pres: pressure value | |
| 40 | + * @ts: timestamp | |
| 41 | + */ | |
| 42 | +struct mpr_chan { | |
| 43 | + s32 pres; | |
| 44 | + s64 ts; | |
| 45 | +}; | |
| 46 | + | |
| 47 | +enum mpr_func_id { | |
| 48 | + MPR_FUNCTION_A, | |
| 49 | + MPR_FUNCTION_B, | |
| 50 | + MPR_FUNCTION_C, | |
| 51 | +}; | |
| 52 | + | |
| 53 | +/** | |
| 54 | + * struct mpr_data | |
| 55 | + * @dev: current device structure | |
| 56 | + * @ops: functions that implement the sensor reads/writes, bus init | |
| 57 | + * @lock: access to device during read | |
| 58 | + * @pmin: minimal pressure in pascal | |
| 59 | + * @pmax: maximal pressure in pascal | |
| 60 | + * @function: transfer function | |
| 61 | + * @outmin: minimum raw pressure in counts (based on transfer function) | |
| 62 | + * @outmax: maximum raw pressure in counts (based on transfer function) | |
| 63 | + * @scale: pressure scale | |
| 64 | + * @scale2: pressure scale, decimal number | |
| 65 | + * @offset: pressure offset | |
| 66 | + * @offset2: pressure offset, decimal number | |
| 67 | + * @gpiod_reset: reset | |
| 68 | + * @irq: end of conversion irq. used to distinguish between irq mode and | |
| 69 | + * reading in a loop until data is ready | |
| 70 | + * @completion: handshake from irq to read | |
| 71 | + * @chan: channel values for buffered mode | |
| 72 | + * @buffer: raw conversion data | |
| 73 | + */ | |
| 74 | +struct mpr_data { | |
| 75 | + struct device *dev; | |
| 76 | + const struct mpr_ops *ops; | |
| 77 | + struct mutex lock; | |
| 78 | + u32 pmin; | |
| 79 | + u32 pmax; | |
| 80 | + enum mpr_func_id function; | |
| 81 | + u32 outmin; | |
| 82 | + u32 outmax; | |
| 83 | + int scale; | |
| 84 | + int scale2; | |
| 85 | + int offset; | |
| 86 | + int offset2; | |
| 87 | + struct gpio_desc *gpiod_reset; | |
| 88 | + int irq; | |
| 89 | + struct completion completion; | |
| 90 | + struct mpr_chan chan; | |
| 91 | + u8 buffer[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN); | |
| 92 | +}; | |
| 93 | + | |
| 94 | +struct mpr_ops { | |
| 95 | + int (*init)(struct device *dev); | |
| 96 | + int (*read)(struct mpr_data *data, const u8 cmd, const u8 cnt); | |
| 97 | + int (*write)(struct mpr_data *data, const u8 cmd, const u8 cnt); | |
| 98 | +}; | |
| 99 | + | |
| 100 | +int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq); | |
| 101 | + | |
| 102 | +#endif | |
| @@ -0,0 +1,100 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later | |
| 2 | +/* | |
| 3 | + * MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver | |
| 4 | + * | |
| 5 | + * Copyright (c) Andreas Klinger <ak@it-klinger.de> | |
| 6 | + * | |
| 7 | + * Data sheet: | |
| 8 | + * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#include <linux/device.h> | |
| 12 | +#include <linux/errno.h> | |
| 13 | +#include <linux/i2c.h> | |
| 14 | +#include <linux/mod_devicetable.h> | |
| 15 | +#include <linux/module.h> | |
| 16 | +#include <linux/types.h> | |
| 17 | + | |
| 18 | +#include "mprls0025pa.h" | |
| 19 | + | |
| 20 | +static int mpr_i2c_init(struct device *unused) | |
| 21 | +{ | |
| 22 | + return 0; | |
| 23 | +} | |
| 24 | + | |
| 25 | +static int mpr_i2c_read(struct mpr_data *data, const u8 unused, const u8 cnt) | |
| 26 | +{ | |
| 27 | + int ret; | |
| 28 | + struct i2c_client *client = to_i2c_client(data->dev); | |
| 29 | + | |
| 30 | + if (cnt > MPR_MEASUREMENT_RD_SIZE) | |
| 31 | + return -EOVERFLOW; | |
| 32 | + | |
| 33 | + memset(data->buffer, 0, MPR_MEASUREMENT_RD_SIZE); | |
| 34 | + ret = i2c_master_recv(client, data->buffer, cnt); | |
| 35 | + if (ret < 0) | |
| 36 | + return ret; | |
| 37 | + else if (ret != cnt) | |
| 38 | + return -EIO; | |
| 39 | + | |
| 40 | + return 0; | |
| 41 | +} | |
| 42 | + | |
| 43 | +static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused) | |
| 44 | +{ | |
| 45 | + int ret; | |
| 46 | + struct i2c_client *client = to_i2c_client(data->dev); | |
| 47 | + u8 wdata[MPR_PKT_SYNC_LEN]; | |
| 48 | + | |
| 49 | + memset(wdata, 0, sizeof(wdata)); | |
| 50 | + wdata[0] = cmd; | |
| 51 | + | |
| 52 | + ret = i2c_master_send(client, wdata, MPR_PKT_SYNC_LEN); | |
| 53 | + if (ret < 0) | |
| 54 | + return ret; | |
| 55 | + else if (ret != MPR_PKT_SYNC_LEN) | |
| 56 | + return -EIO; | |
| 57 | + | |
| 58 | + return 0; | |
| 59 | +} | |
| 60 | + | |
| 61 | +static const struct mpr_ops mpr_i2c_ops = { | |
| 62 | + .init = mpr_i2c_init, | |
| 63 | + .read = mpr_i2c_read, | |
| 64 | + .write = mpr_i2c_write, | |
| 65 | +}; | |
| 66 | + | |
| 67 | +static int mpr_i2c_probe(struct i2c_client *client) | |
| 68 | +{ | |
| 69 | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE)) | |
| 70 | + return -EOPNOTSUPP; | |
| 71 | + | |
| 72 | + return mpr_common_probe(&client->dev, &mpr_i2c_ops, client->irq); | |
| 73 | +} | |
| 74 | + | |
| 75 | +static const struct of_device_id mpr_i2c_match[] = { | |
| 76 | + { .compatible = "honeywell,mprls0025pa" }, | |
| 77 | + {} | |
| 78 | +}; | |
| 79 | +MODULE_DEVICE_TABLE(of, mpr_i2c_match); | |
| 80 | + | |
| 81 | +static const struct i2c_device_id mpr_i2c_id[] = { | |
| 82 | + { "mprls0025pa" }, | |
| 83 | + {} | |
| 84 | +}; | |
| 85 | +MODULE_DEVICE_TABLE(i2c, mpr_i2c_id); | |
| 86 | + | |
| 87 | +static struct i2c_driver mpr_i2c_driver = { | |
| 88 | + .probe = mpr_i2c_probe, | |
| 89 | + .id_table = mpr_i2c_id, | |
| 90 | + .driver = { | |
| 91 | + .name = "mprls0025pa", | |
| 92 | + .of_match_table = mpr_i2c_match, | |
| 93 | + }, | |
| 94 | +}; | |
| 95 | +module_i2c_driver(mpr_i2c_driver); | |
| 96 | + | |
| 97 | +MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>"); | |
| 98 | +MODULE_DESCRIPTION("Honeywell MPR pressure sensor i2c driver"); | |
| 99 | +MODULE_LICENSE("GPL"); | |
| 100 | +MODULE_IMPORT_NS(IIO_HONEYWELL_MPRLS0025PA); | |
| @@ -0,0 +1,92 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * MPRLS0025PA - Honeywell MicroPressure MPR series SPI sensor driver | |
| 4 | + * | |
| 5 | + * Copyright (c) 2024 Petre Rodan <petre.rodan@subdimension.ro> | |
| 6 | + * | |
| 7 | + * Data sheet: | |
| 8 | + * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#include <linux/device.h> | |
| 12 | +#include <linux/errno.h> | |
| 13 | +#include <linux/mod_devicetable.h> | |
| 14 | +#include <linux/module.h> | |
| 15 | +#include <linux/spi/spi.h> | |
| 16 | +#include <linux/stddef.h> | |
| 17 | +#include <linux/types.h> | |
| 18 | + | |
| 19 | +#include "mprls0025pa.h" | |
| 20 | + | |
| 21 | +struct mpr_spi_buf { | |
| 22 | + u8 tx[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN); | |
| 23 | +}; | |
| 24 | + | |
| 25 | +static int mpr_spi_init(struct device *dev) | |
| 26 | +{ | |
| 27 | + struct spi_device *spi = to_spi_device(dev); | |
| 28 | + struct mpr_spi_buf *buf; | |
| 29 | + | |
| 30 | + buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL); | |
| 31 | + if (!buf) | |
| 32 | + return -ENOMEM; | |
| 33 | + | |
| 34 | + spi_set_drvdata(spi, buf); | |
| 35 | + | |
| 36 | + return 0; | |
| 37 | +} | |
| 38 | + | |
| 39 | +static int mpr_spi_xfer(struct mpr_data *data, const u8 cmd, const u8 pkt_len) | |
| 40 | +{ | |
| 41 | + struct spi_device *spi = to_spi_device(data->dev); | |
| 42 | + struct mpr_spi_buf *buf = spi_get_drvdata(spi); | |
| 43 | + struct spi_transfer xfer; | |
| 44 | + | |
| 45 | + if (pkt_len > MPR_MEASUREMENT_RD_SIZE) | |
| 46 | + return -EOVERFLOW; | |
| 47 | + | |
| 48 | + buf->tx[0] = cmd; | |
| 49 | + xfer.tx_buf = buf->tx; | |
| 50 | + xfer.rx_buf = data->buffer; | |
| 51 | + xfer.len = pkt_len; | |
| 52 | + | |
| 53 | + return spi_sync_transfer(spi, &xfer, 1); | |
| 54 | +} | |
| 55 | + | |
| 56 | +static const struct mpr_ops mpr_spi_ops = { | |
| 57 | + .init = mpr_spi_init, | |
| 58 | + .read = mpr_spi_xfer, | |
| 59 | + .write = mpr_spi_xfer, | |
| 60 | +}; | |
| 61 | + | |
| 62 | +static int mpr_spi_probe(struct spi_device *spi) | |
| 63 | +{ | |
| 64 | + return mpr_common_probe(&spi->dev, &mpr_spi_ops, spi->irq); | |
| 65 | +} | |
| 66 | + | |
| 67 | +static const struct of_device_id mpr_spi_match[] = { | |
| 68 | + { .compatible = "honeywell,mprls0025pa" }, | |
| 69 | + {} | |
| 70 | +}; | |
| 71 | +MODULE_DEVICE_TABLE(of, mpr_spi_match); | |
| 72 | + | |
| 73 | +static const struct spi_device_id mpr_spi_id[] = { | |
| 74 | + { "mprls0025pa" }, | |
| 75 | + {} | |
| 76 | +}; | |
| 77 | +MODULE_DEVICE_TABLE(spi, mpr_spi_id); | |
| 78 | + | |
| 79 | +static struct spi_driver mpr_spi_driver = { | |
| 80 | + .driver = { | |
| 81 | + .name = "mprls0025pa", | |
| 82 | + .of_match_table = mpr_spi_match, | |
| 83 | + }, | |
| 84 | + .probe = mpr_spi_probe, | |
| 85 | + .id_table = mpr_spi_id, | |
| 86 | +}; | |
| 87 | +module_spi_driver(mpr_spi_driver); | |
| 88 | + | |
| 89 | +MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>"); | |
| 90 | +MODULE_DESCRIPTION("Honeywell MPR pressure sensor spi driver"); | |
| 91 | +MODULE_LICENSE("GPL"); | |
| 92 | +MODULE_IMPORT_NS(IIO_HONEYWELL_MPRLS0025PA); | |
| @@ -7,7 +7,6 @@ · 6 unmodified lines | ||
| 7 | 7 | * Denis Ciocca <denis.ciocca@st.com> |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | −#include <linux/acpi.h> | |
| 11 | 10 | #include <linux/kernel.h> |
| 12 | 11 | #include <linux/module.h> |
| 13 | 12 | #include <linux/mod_devicetable.h> |
| @@ -55,13 +54,11 @@ static const struct of_device_id st_press_of_match[] = { · 41 unmodified lines | ||
| 55 | 54 | }; |
| 56 | 55 | MODULE_DEVICE_TABLE(of, st_press_of_match); |
| 57 | 56 | |
| 58 | −#ifdef CONFIG_ACPI | |
| 59 | 57 | static const struct acpi_device_id st_press_acpi_match[] = { |
| 60 | 58 | {"SNO9210", LPS22HB}, |
| 61 | 59 | { }, |
| 62 | 60 | }; |
| 63 | 61 | MODULE_DEVICE_TABLE(acpi, st_press_acpi_match); |
| 64 | −#endif | |
| 65 | 62 | |
| 66 | 63 | static const struct i2c_device_id st_press_id_table[] = { |
| 67 | 64 | { LPS001WP_PRESS_DEV_NAME, LPS001WP }, |
| @@ -114,7 +111,7 @@ static struct i2c_driver st_press_driver = { · 46 unmodified lines | ||
| 114 | 111 | .driver = { |
| 115 | 112 | .name = "st-press-i2c", |
| 116 | 113 | .of_match_table = st_press_of_match, |
| 117 | − .acpi_match_table = ACPI_PTR(st_press_acpi_match), | |
| 114 | + .acpi_match_table = st_press_acpi_match, | |
| 118 | 115 | }, |
| 119 | 116 | .probe = st_press_i2c_probe, |
| 120 | 117 | .id_table = st_press_id_table, |
| @@ -995,17 +995,16 @@ static const struct i2c_device_id isl29501_id[] = { · 994 unmodified lines | ||
| 995 | 995 | |
| 996 | 996 | MODULE_DEVICE_TABLE(i2c, isl29501_id); |
| 997 | 997 | |
| 998 | −#if defined(CONFIG_OF) | |
| 999 | 998 | static const struct of_device_id isl29501_i2c_matches[] = { |
| 1000 | 999 | { .compatible = "renesas,isl29501" }, |
| 1001 | 1000 | { } |
| 1002 | 1001 | }; |
| 1003 | 1002 | MODULE_DEVICE_TABLE(of, isl29501_i2c_matches); |
| 1004 | −#endif | |
| 1005 | 1003 | |
| 1006 | 1004 | static struct i2c_driver isl29501_driver = { |
| 1007 | 1005 | .driver = { |
| 1008 | 1006 | .name = "isl29501", |
| 1007 | + .of_match_table = isl29501_i2c_matches, | |
| 1009 | 1008 | }, |
| 1010 | 1009 | .id_table = isl29501_id, |
| 1011 | 1010 | .probe = isl29501_probe, |
| @@ -337,28 +337,19 @@ static int sx9310_read_raw(struct iio_dev *indio_dev, · 336 unmodified lines | ||
| 337 | 337 | int *val2, long mask) |
| 338 | 338 | { |
| 339 | 339 | struct sx_common_data *data = iio_priv(indio_dev); |
| 340 | − int ret; | |
| 341 | 340 | |
| 342 | 341 | if (chan->type != IIO_PROXIMITY) |
| 343 | 342 | return -EINVAL; |
| 344 | 343 | |
| 345 | 344 | switch (mask) { |
| 346 | 345 | case IIO_CHAN_INFO_RAW: |
| 347 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 348 | − if (ret) | |
| 349 | − return ret; | |
| 350 | − | |
| 351 | − ret = sx_common_read_proximity(data, chan, val); | |
| 352 | − iio_device_release_direct_mode(indio_dev); | |
| 353 | − return ret; | |
| 346 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 347 | + return sx_common_read_proximity(data, chan, val); | |
| 348 | + unreachable(); | |
| 354 | 349 | case IIO_CHAN_INFO_HARDWAREGAIN: |
| 355 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 356 | − if (ret) | |
| 357 | − return ret; | |
| 358 | − | |
| 359 | − ret = sx9310_read_gain(data, chan, val); | |
| 360 | − iio_device_release_direct_mode(indio_dev); | |
| 361 | − return ret; | |
| 350 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 351 | + return sx9310_read_gain(data, chan, val); | |
| 352 | + unreachable(); | |
| 362 | 353 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 363 | 354 | return sx9310_read_samp_freq(data, val, val2); |
| 364 | 355 | default: |
| @@ -546,12 +537,10 @@ static int sx9310_write_thresh(struct sx_common_data *data, · 181 unmodified lines | ||
| 546 | 537 | return -EINVAL; |
| 547 | 538 | |
| 548 | 539 | regval = FIELD_PREP(SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval); |
| 549 | − mutex_lock(&data->mutex); | |
| 550 | − ret = regmap_update_bits(data->regmap, reg, | |
| 551 | − SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval); | |
| 552 | − mutex_unlock(&data->mutex); | |
| 553 | 540 | |
| 554 | − return ret; | |
| 541 | + guard(mutex)(&data->mutex); | |
| 542 | + return regmap_update_bits(data->regmap, reg, | |
| 543 | + SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval); | |
| 555 | 544 | } |
| 556 | 545 | |
| 557 | 546 | static int sx9310_write_hysteresis(struct sx_common_data *data, |
| @@ -576,17 +565,14 @@ static int sx9310_write_hysteresis(struct sx_common_data *data, · 18 unmodified lines | ||
| 576 | 565 | return -EINVAL; |
| 577 | 566 | |
| 578 | 567 | hyst = FIELD_PREP(SX9310_REG_PROX_CTRL10_HYST_MASK, hyst); |
| 579 | − mutex_lock(&data->mutex); | |
| 580 | − ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, | |
| 581 | − SX9310_REG_PROX_CTRL10_HYST_MASK, hyst); | |
| 582 | − mutex_unlock(&data->mutex); | |
| 583 | 568 | |
| 584 | − return ret; | |
| 569 | + guard(mutex)(&data->mutex); | |
| 570 | + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, | |
| 571 | + SX9310_REG_PROX_CTRL10_HYST_MASK, hyst); | |
| 585 | 572 | } |
| 586 | 573 | |
| 587 | 574 | static int sx9310_write_far_debounce(struct sx_common_data *data, int val) |
| 588 | 575 | { |
| 589 | − int ret; | |
| 590 | 576 | unsigned int regval; |
| 591 | 577 | |
| 592 | 578 | if (val > 0) |
| @@ -596,18 +582,14 @@ static int sx9310_write_far_debounce(struct sx_common_data *data, int val) · 3 unmodified lines | ||
| 596 | 582 | |
| 597 | 583 | regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, val); |
| 598 | 584 | |
| 599 | − mutex_lock(&data->mutex); | |
| 600 | − ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, | |
| 601 | − SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, | |
| 602 | − regval); | |
| 603 | − mutex_unlock(&data->mutex); | |
| 604 | − | |
| 605 | − return ret; | |
| 585 | + guard(mutex)(&data->mutex); | |
| 586 | + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, | |
| 587 | + SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, | |
| 588 | + regval); | |
| 606 | 589 | } |
| 607 | 590 | |
| 608 | 591 | static int sx9310_write_close_debounce(struct sx_common_data *data, int val) |
| 609 | 592 | { |
| 610 | − int ret; | |
| 611 | 593 | unsigned int regval; |
| 612 | 594 | |
| 613 | 595 | if (val > 0) |
| @@ -617,13 +599,10 @@ static int sx9310_write_close_debounce(struct sx_common_data *data, int val) · 3 unmodified lines | ||
| 617 | 599 | |
| 618 | 600 | regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, val); |
| 619 | 601 | |
| 620 | − mutex_lock(&data->mutex); | |
| 621 | − ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, | |
| 622 | − SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, | |
| 623 | − regval); | |
| 624 | − mutex_unlock(&data->mutex); | |
| 625 | − | |
| 626 | − return ret; | |
| 602 | + guard(mutex)(&data->mutex); | |
| 603 | + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, | |
| 604 | + SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, | |
| 605 | + regval); | |
| 627 | 606 | } |
| 628 | 607 | |
| 629 | 608 | static int sx9310_write_event_val(struct iio_dev *indio_dev, |
| @@ -658,7 +637,7 @@ static int sx9310_write_event_val(struct iio_dev *indio_dev, · 28 unmodified lines | ||
| 658 | 637 | |
| 659 | 638 | static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2) |
| 660 | 639 | { |
| 661 | − int i, ret; | |
| 640 | + int i; | |
| 662 | 641 | |
| 663 | 642 | for (i = 0; i < ARRAY_SIZE(sx9310_samp_freq_table); i++) |
| 664 | 643 | if (val == sx9310_samp_freq_table[i].val && |
| @@ -668,23 +647,17 @@ static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2) · 3 unmodified lines | ||
| 668 | 647 | if (i == ARRAY_SIZE(sx9310_samp_freq_table)) |
| 669 | 648 | return -EINVAL; |
| 670 | 649 | |
| 671 | − mutex_lock(&data->mutex); | |
| 672 | − | |
| 673 | − ret = regmap_update_bits( | |
| 650 | + guard(mutex)(&data->mutex); | |
| 651 | + return regmap_update_bits( | |
| 674 | 652 | data->regmap, SX9310_REG_PROX_CTRL0, |
| 675 | 653 | SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, |
| 676 | 654 | FIELD_PREP(SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, i)); |
| 677 | − | |
| 678 | − mutex_unlock(&data->mutex); | |
| 679 | − | |
| 680 | − return ret; | |
| 681 | 655 | } |
| 682 | 656 | |
| 683 | 657 | static int sx9310_write_gain(struct sx_common_data *data, |
| 684 | 658 | const struct iio_chan_spec *chan, int val) |
| 685 | 659 | { |
| 686 | 660 | unsigned int gain, mask; |
| 687 | − int ret; | |
| 688 | 661 | |
| 689 | 662 | gain = ilog2(val); |
| 690 | 663 | |
| @@ -703,12 +676,9 @@ static int sx9310_write_gain(struct sx_common_data *data, · 12 unmodified lines | ||
| 703 | 676 | return -EINVAL; |
| 704 | 677 | } |
| 705 | 678 | |
| 706 | − mutex_lock(&data->mutex); | |
| 707 | − ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL3, mask, | |
| 708 | − gain); | |
| 709 | − mutex_unlock(&data->mutex); | |
| 710 | − | |
| 711 | − return ret; | |
| 679 | + guard(mutex)(&data->mutex); | |
| 680 | + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL3, mask, | |
| 681 | + gain); | |
| 712 | 682 | } |
| 713 | 683 | |
| 714 | 684 | static int sx9310_write_raw(struct iio_dev *indio_dev, |
| @@ -969,22 +939,18 @@ static int sx9310_suspend(struct device *dev) · 254 unmodified lines | ||
| 969 | 939 | |
| 970 | 940 | disable_irq_nosync(data->client->irq); |
| 971 | 941 | |
| 972 | − mutex_lock(&data->mutex); | |
| 942 | + guard(mutex)(&data->mutex); | |
| 973 | 943 | ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0, |
| 974 | 944 | &data->suspend_ctrl); |
| 975 | 945 | if (ret) |
| 976 | − goto out; | |
| 946 | + return ret; | |
| 977 | 947 | |
| 978 | 948 | ctrl0 = data->suspend_ctrl & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK; |
| 979 | 949 | ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0); |
| 980 | 950 | if (ret) |
| 981 | − goto out; | |
| 982 | − | |
| 983 | − ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 0); | |
| 951 | + return ret; | |
| 984 | 952 | |
| 985 | −out: | |
| 986 | − mutex_unlock(&data->mutex); | |
| 987 | − return ret; | |
| 953 | + return regmap_write(data->regmap, SX9310_REG_PAUSE, 0); | |
| 988 | 954 | } |
| 989 | 955 | |
| 990 | 956 | static int sx9310_resume(struct device *dev) |
| @@ -992,18 +958,16 @@ static int sx9310_resume(struct device *dev) · 1 unmodified line | ||
| 992 | 958 | struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); |
| 993 | 959 | int ret; |
| 994 | 960 | |
| 995 | − mutex_lock(&data->mutex); | |
| 996 | − ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 1); | |
| 997 | − if (ret) | |
| 998 | − goto out; | |
| 999 | − | |
| 1000 | − ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, | |
| 1001 | − data->suspend_ctrl); | |
| 961 | + scoped_guard(mutex, &data->mutex) { | |
| 962 | + ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 1); | |
| 963 | + if (ret) | |
| 964 | + return ret; | |
| 1002 | 965 | |
| 1003 | −out: | |
| 1004 | − mutex_unlock(&data->mutex); | |
| 1005 | − if (ret) | |
| 1006 | − return ret; | |
| 966 | + ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, | |
| 967 | + data->suspend_ctrl); | |
| 968 | + if (ret) | |
| 969 | + return ret; | |
| 970 | + } | |
| 1007 | 971 | |
| 1008 | 972 | enable_irq(data->client->irq); |
| 1009 | 973 | return 0; |
| @@ -429,25 +429,16 @@ static int sx9324_read_raw(struct iio_dev *indio_dev, · 428 unmodified lines | ||
| 429 | 429 | int *val, int *val2, long mask) |
| 430 | 430 | { |
| 431 | 431 | struct sx_common_data *data = iio_priv(indio_dev); |
| 432 | − int ret; | |
| 433 | 432 | |
| 434 | 433 | switch (mask) { |
| 435 | 434 | case IIO_CHAN_INFO_RAW: |
| 436 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 437 | − if (ret) | |
| 438 | − return ret; | |
| 439 | − | |
| 440 | − ret = sx_common_read_proximity(data, chan, val); | |
| 441 | − iio_device_release_direct_mode(indio_dev); | |
| 442 | − return ret; | |
| 435 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 436 | + return sx_common_read_proximity(data, chan, val); | |
| 437 | + unreachable(); | |
| 443 | 438 | case IIO_CHAN_INFO_HARDWAREGAIN: |
| 444 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 445 | − if (ret) | |
| 446 | − return ret; | |
| 447 | − | |
| 448 | − ret = sx9324_read_gain(data, chan, val); | |
| 449 | − iio_device_release_direct_mode(indio_dev); | |
| 450 | − return ret; | |
| 439 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 440 | + return sx9324_read_gain(data, chan, val); | |
| 441 | + unreachable(); | |
| 451 | 442 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 452 | 443 | return sx9324_read_samp_freq(data, val, val2); |
| 453 | 444 | default: |
| @@ -484,7 +475,7 @@ static int sx9324_read_avail(struct iio_dev *indio_dev, · 30 unmodified lines | ||
| 484 | 475 | static int sx9324_set_samp_freq(struct sx_common_data *data, |
| 485 | 476 | int val, int val2) |
| 486 | 477 | { |
| 487 | − int i, ret; | |
| 478 | + int i; | |
| 488 | 479 | |
| 489 | 480 | for (i = 0; i < ARRAY_SIZE(sx9324_samp_freq_table); i++) |
| 490 | 481 | if (val == sx9324_samp_freq_table[i].val && |
| @@ -494,15 +485,11 @@ static int sx9324_set_samp_freq(struct sx_common_data *data, · 3 unmodified lines | ||
| 494 | 485 | if (i == ARRAY_SIZE(sx9324_samp_freq_table)) |
| 495 | 486 | return -EINVAL; |
| 496 | 487 | |
| 497 | − mutex_lock(&data->mutex); | |
| 498 | − | |
| 499 | − ret = regmap_update_bits(data->regmap, | |
| 500 | − SX9324_REG_GNRL_CTRL0, | |
| 501 | − SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, i); | |
| 502 | − | |
| 503 | − mutex_unlock(&data->mutex); | |
| 488 | + guard(mutex)(&data->mutex); | |
| 504 | 489 | |
| 505 | − return ret; | |
| 490 | + return regmap_update_bits(data->regmap, | |
| 491 | + SX9324_REG_GNRL_CTRL0, | |
| 492 | + SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, i); | |
| 506 | 493 | } |
| 507 | 494 | |
| 508 | 495 | static int sx9324_read_thresh(struct sx_common_data *data, |
| @@ -623,7 +610,6 @@ static int sx9324_write_thresh(struct sx_common_data *data, · 114 unmodified lines | ||
| 623 | 610 | const struct iio_chan_spec *chan, int _val) |
| 624 | 611 | { |
| 625 | 612 | unsigned int reg, val = _val; |
| 626 | − int ret; | |
| 627 | 613 | |
| 628 | 614 | reg = SX9324_REG_PROX_CTRL6 + chan->channel / 2; |
| 629 | 615 | |
| @@ -633,11 +619,9 @@ static int sx9324_write_thresh(struct sx_common_data *data, · 3 unmodified lines | ||
| 633 | 619 | if (val > 0xff) |
| 634 | 620 | return -EINVAL; |
| 635 | 621 | |
| 636 | − mutex_lock(&data->mutex); | |
| 637 | − ret = regmap_write(data->regmap, reg, val); | |
| 638 | − mutex_unlock(&data->mutex); | |
| 622 | + guard(mutex)(&data->mutex); | |
| 639 | 623 | |
| 640 | − return ret; | |
| 624 | + return regmap_write(data->regmap, reg, val); | |
| 641 | 625 | } |
| 642 | 626 | |
| 643 | 627 | static int sx9324_write_hysteresis(struct sx_common_data *data, |
| @@ -662,18 +646,15 @@ static int sx9324_write_hysteresis(struct sx_common_data *data, · 18 unmodified lines | ||
| 662 | 646 | return -EINVAL; |
| 663 | 647 | |
| 664 | 648 | hyst = FIELD_PREP(SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); |
| 665 | − mutex_lock(&data->mutex); | |
| 666 | − ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, | |
| 667 | − SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); | |
| 668 | − mutex_unlock(&data->mutex); | |
| 649 | + guard(mutex)(&data->mutex); | |
| 669 | 650 | |
| 670 | − return ret; | |
| 651 | + return regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, | |
| 652 | + SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); | |
| 671 | 653 | } |
| 672 | 654 | |
| 673 | 655 | static int sx9324_write_far_debounce(struct sx_common_data *data, int _val) |
| 674 | 656 | { |
| 675 | 657 | unsigned int regval, val = _val; |
| 676 | − int ret; | |
| 677 | 658 | |
| 678 | 659 | if (val > 0) |
| 679 | 660 | val = ilog2(val); |
| @@ -682,19 +663,16 @@ static int sx9324_write_far_debounce(struct sx_common_data *data, int _val) · 2 unmodified lines | ||
| 682 | 663 | |
| 683 | 664 | regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, val); |
| 684 | 665 | |
| 685 | − mutex_lock(&data->mutex); | |
| 686 | − ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, | |
| 687 | − SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, | |
| 688 | − regval); | |
| 689 | − mutex_unlock(&data->mutex); | |
| 666 | + guard(mutex)(&data->mutex); | |
| 690 | 667 | |
| 691 | − return ret; | |
| 668 | + return regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, | |
| 669 | + SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, | |
| 670 | + regval); | |
| 692 | 671 | } |
| 693 | 672 | |
| 694 | 673 | static int sx9324_write_close_debounce(struct sx_common_data *data, int _val) |
| 695 | 674 | { |
| 696 | 675 | unsigned int regval, val = _val; |
| 697 | − int ret; | |
| 698 | 676 | |
| 699 | 677 | if (val > 0) |
| 700 | 678 | val = ilog2(val); |
| @@ -703,13 +681,11 @@ static int sx9324_write_close_debounce(struct sx_common_data *data, int _val) · 2 unmodified lines | ||
| 703 | 681 | |
| 704 | 682 | regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, val); |
| 705 | 683 | |
| 706 | − mutex_lock(&data->mutex); | |
| 707 | − ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, | |
| 708 | − SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, | |
| 709 | − regval); | |
| 710 | − mutex_unlock(&data->mutex); | |
| 684 | + guard(mutex)(&data->mutex); | |
| 711 | 685 | |
| 712 | − return ret; | |
| 686 | + return regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, | |
| 687 | + SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, | |
| 688 | + regval); | |
| 713 | 689 | } |
| 714 | 690 | |
| 715 | 691 | static int sx9324_write_event_val(struct iio_dev *indio_dev, |
| @@ -746,7 +722,6 @@ static int sx9324_write_gain(struct sx_common_data *data, · 30 unmodified lines | ||
| 746 | 722 | const struct iio_chan_spec *chan, int val) |
| 747 | 723 | { |
| 748 | 724 | unsigned int gain, reg; |
| 749 | − int ret; | |
| 750 | 725 | |
| 751 | 726 | reg = SX9324_REG_PROX_CTRL0 + chan->channel / 2; |
| 752 | 727 | |
| @@ -756,13 +731,11 @@ static int sx9324_write_gain(struct sx_common_data *data, · 3 unmodified lines | ||
| 756 | 731 | |
| 757 | 732 | gain = FIELD_PREP(SX9324_REG_PROX_CTRL0_GAIN_MASK, gain); |
| 758 | 733 | |
| 759 | − mutex_lock(&data->mutex); | |
| 760 | − ret = regmap_update_bits(data->regmap, reg, | |
| 761 | − SX9324_REG_PROX_CTRL0_GAIN_MASK, | |
| 762 | − gain); | |
| 763 | − mutex_unlock(&data->mutex); | |
| 734 | + guard(mutex)(&data->mutex); | |
| 764 | 735 | |
| 765 | − return ret; | |
| 736 | + return regmap_update_bits(data->regmap, reg, | |
| 737 | + SX9324_REG_PROX_CTRL0_GAIN_MASK, | |
| 738 | + gain); | |
| 766 | 739 | } |
| 767 | 740 | |
| 768 | 741 | static int sx9324_write_raw(struct iio_dev *indio_dev, |
| @@ -873,6 +846,29 @@ static int sx9324_init_compensation(struct iio_dev *indio_dev) · 104 unmodified lines | ||
| 873 | 846 | 20000, 2000000); |
| 874 | 847 | } |
| 875 | 848 | |
| 849 | +static u8 sx9324_parse_phase_prop(struct device *dev, | |
| 850 | + struct sx_common_reg_default *reg_def, | |
| 851 | + const char *prop) | |
| 852 | +{ | |
| 853 | + unsigned int pin_defs[SX9324_NUM_PINS]; | |
| 854 | + int count, ret, pin; | |
| 855 | + u32 raw = 0; | |
| 856 | + | |
| 857 | + count = device_property_count_u32(dev, prop); | |
| 858 | + if (count != ARRAY_SIZE(pin_defs)) | |
| 859 | + return reg_def->def; | |
| 860 | + ret = device_property_read_u32_array(dev, prop, pin_defs, | |
| 861 | + ARRAY_SIZE(pin_defs)); | |
| 862 | + if (ret) | |
| 863 | + return reg_def->def; | |
| 864 | + | |
| 865 | + for (pin = 0; pin < SX9324_NUM_PINS; pin++) | |
| 866 | + raw |= (pin_defs[pin] << (2 * pin)) & | |
| 867 | + SX9324_REG_AFE_PH0_PIN_MASK(pin); | |
| 868 | + | |
| 869 | + return raw; | |
| 870 | +} | |
| 871 | + | |
| 876 | 872 | static const struct sx_common_reg_default * |
| 877 | 873 | sx9324_get_default_reg(struct device *dev, int idx, |
| 878 | 874 | struct sx_common_reg_default *reg_def) |
| @@ -881,37 +877,29 @@ sx9324_get_default_reg(struct device *dev, int idx, · 2 unmodified lines | ||
| 881 | 877 | "highest" }; |
| 882 | 878 | static const char * const sx9324_csidle[] = { "hi-z", "hi-z", "gnd", |
| 883 | 879 | "vdd" }; |
| 884 | −#define SX9324_PIN_DEF "semtech,ph0-pin" | |
| 885 | −#define SX9324_RESOLUTION_DEF "semtech,ph01-resolution" | |
| 886 | −#define SX9324_PROXRAW_DEF "semtech,ph01-proxraw-strength" | |
| 887 | − unsigned int pin_defs[SX9324_NUM_PINS]; | |
| 888 | − char prop[] = SX9324_PROXRAW_DEF; | |
| 889 | 880 | u32 start = 0, raw = 0, pos = 0; |
| 890 | − int ret, count, ph, pin; | |
| 881 | + const char *prop; | |
| 882 | + int ret; | |
| 891 | 883 | |
| 892 | 884 | memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*reg_def)); |
| 893 | 885 | |
| 894 | 886 | sx_common_get_raw_register_config(dev, reg_def); |
| 895 | 887 | switch (reg_def->reg) { |
| 896 | 888 | case SX9324_REG_AFE_PH0: |
| 889 | + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, | |
| 890 | + "semtech,ph0-pin"); | |
| 891 | + break; | |
| 897 | 892 | case SX9324_REG_AFE_PH1: |
| 893 | + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, | |
| 894 | + "semtech,ph1-pin"); | |
| 895 | + break; | |
| 898 | 896 | case SX9324_REG_AFE_PH2: |
| 897 | + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, | |
| 898 | + "semtech,ph2-pin"); | |
| 899 | + break; | |
| 899 | 900 | case SX9324_REG_AFE_PH3: |
| 900 | − ph = reg_def->reg - SX9324_REG_AFE_PH0; | |
| 901 | − snprintf(prop, ARRAY_SIZE(prop), "semtech,ph%d-pin", ph); | |
| 902 | − | |
| 903 | − count = device_property_count_u32(dev, prop); | |
| 904 | − if (count != ARRAY_SIZE(pin_defs)) | |
| 905 | − break; | |
| 906 | − ret = device_property_read_u32_array(dev, prop, pin_defs, | |
| 907 | − ARRAY_SIZE(pin_defs)); | |
| 908 | − if (ret) | |
| 909 | − break; | |
| 910 | − | |
| 911 | − for (pin = 0; pin < SX9324_NUM_PINS; pin++) | |
| 912 | − raw |= (pin_defs[pin] << (2 * pin)) & | |
| 913 | − SX9324_REG_AFE_PH0_PIN_MASK(pin); | |
| 914 | − reg_def->def = raw; | |
| 901 | + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, | |
| 902 | + "semtech,ph3-pin"); | |
| 915 | 903 | break; |
| 916 | 904 | case SX9324_REG_AFE_CTRL0: |
| 917 | 905 | ret = device_property_match_property_string(dev, "semtech,cs-idle-sleep", |
| @@ -933,11 +921,9 @@ sx9324_get_default_reg(struct device *dev, int idx, · 15 unmodified lines | ||
| 933 | 921 | case SX9324_REG_AFE_CTRL4: |
| 934 | 922 | case SX9324_REG_AFE_CTRL7: |
| 935 | 923 | if (reg_def->reg == SX9324_REG_AFE_CTRL4) |
| 936 | − strncpy(prop, "semtech,ph01-resolution", | |
| 937 | − ARRAY_SIZE(prop)); | |
| 924 | + prop = "semtech,ph01-resolution"; | |
| 938 | 925 | else |
| 939 | − strncpy(prop, "semtech,ph23-resolution", | |
| 940 | − ARRAY_SIZE(prop)); | |
| 926 | + prop = "semtech,ph23-resolution"; | |
| 941 | 927 | |
| 942 | 928 | ret = device_property_read_u32(dev, prop, &raw); |
| 943 | 929 | if (ret) |
| @@ -1008,11 +994,9 @@ sx9324_get_default_reg(struct device *dev, int idx, · 64 unmodified lines | ||
| 1008 | 994 | case SX9324_REG_PROX_CTRL0: |
| 1009 | 995 | case SX9324_REG_PROX_CTRL1: |
| 1010 | 996 | if (reg_def->reg == SX9324_REG_PROX_CTRL0) |
| 1011 | − strncpy(prop, "semtech,ph01-proxraw-strength", | |
| 1012 | − ARRAY_SIZE(prop)); | |
| 997 | + prop = "semtech,ph01-proxraw-strength"; | |
| 1013 | 998 | else |
| 1014 | − strncpy(prop, "semtech,ph23-proxraw-strength", | |
| 1015 | − ARRAY_SIZE(prop)); | |
| 999 | + prop = "semtech,ph23-proxraw-strength"; | |
| 1016 | 1000 | ret = device_property_read_u32(dev, prop, &raw); |
| 1017 | 1001 | if (ret) |
| 1018 | 1002 | break; |
| @@ -1081,34 +1065,30 @@ static int sx9324_suspend(struct device *dev) · 62 unmodified lines | ||
| 1081 | 1065 | |
| 1082 | 1066 | disable_irq_nosync(data->client->irq); |
| 1083 | 1067 | |
| 1084 | − mutex_lock(&data->mutex); | |
| 1068 | + guard(mutex)(&data->mutex); | |
| 1085 | 1069 | ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL1, ®val); |
| 1070 | + if (ret < 0) | |
| 1071 | + return ret; | |
| 1086 | 1072 | |
| 1087 | 1073 | data->suspend_ctrl = |
| 1088 | 1074 | FIELD_GET(SX9324_REG_GNRL_CTRL1_PHEN_MASK, regval); |
| 1089 | 1075 | |
| 1090 | − if (ret < 0) | |
| 1091 | − goto out; | |
| 1092 | 1076 | |
| 1093 | 1077 | /* Disable all phases, send the device to sleep. */ |
| 1094 | − ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, 0); | |
| 1095 | − | |
| 1096 | −out: | |
| 1097 | − mutex_unlock(&data->mutex); | |
| 1098 | − return ret; | |
| 1078 | + return regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, 0); | |
| 1099 | 1079 | } |
| 1100 | 1080 | |
| 1101 | 1081 | static int sx9324_resume(struct device *dev) |
| 1102 | 1082 | { |
| 1103 | 1083 | struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); |
| 1104 | − int ret; | |
| 1105 | 1084 | |
| 1106 | − mutex_lock(&data->mutex); | |
| 1107 | − ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, | |
| 1108 | − data->suspend_ctrl | SX9324_REG_GNRL_CTRL1_PAUSECTRL); | |
| 1109 | − mutex_unlock(&data->mutex); | |
| 1110 | − if (ret) | |
| 1111 | − return ret; | |
| 1085 | + scoped_guard(mutex, &data->mutex) { | |
| 1086 | + int ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, | |
| 1087 | + data->suspend_ctrl | | |
| 1088 | + SX9324_REG_GNRL_CTRL1_PAUSECTRL); | |
| 1089 | + if (ret) | |
| 1090 | + return ret; | |
| 1091 | + } | |
| 1112 | 1092 | |
| 1113 | 1093 | enable_irq(data->client->irq); |
| 1114 | 1094 | return 0; |
| @@ -322,25 +322,16 @@ static int sx9360_read_raw(struct iio_dev *indio_dev, · 321 unmodified lines | ||
| 322 | 322 | int *val, int *val2, long mask) |
| 323 | 323 | { |
| 324 | 324 | struct sx_common_data *data = iio_priv(indio_dev); |
| 325 | − int ret; | |
| 326 | 325 | |
| 327 | 326 | switch (mask) { |
| 328 | 327 | case IIO_CHAN_INFO_RAW: |
| 329 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 330 | − if (ret) | |
| 331 | − return ret; | |
| 332 | − | |
| 333 | − ret = sx_common_read_proximity(data, chan, val); | |
| 334 | − iio_device_release_direct_mode(indio_dev); | |
| 335 | − return ret; | |
| 328 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 329 | + return sx_common_read_proximity(data, chan, val); | |
| 330 | + unreachable(); | |
| 336 | 331 | case IIO_CHAN_INFO_HARDWAREGAIN: |
| 337 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 338 | − if (ret) | |
| 339 | − return ret; | |
| 340 | − | |
| 341 | − ret = sx9360_read_gain(data, chan, val); | |
| 342 | − iio_device_release_direct_mode(indio_dev); | |
| 343 | − return ret; | |
| 332 | + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) | |
| 333 | + return sx9360_read_gain(data, chan, val); | |
| 334 | + unreachable(); | |
| 344 | 335 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 345 | 336 | return sx9360_read_samp_freq(data, val, val2); |
| 346 | 337 | default: |
| @@ -387,19 +378,15 @@ static int sx9360_read_avail(struct iio_dev *indio_dev, · 40 unmodified lines | ||
| 387 | 378 | static int sx9360_set_samp_freq(struct sx_common_data *data, |
| 388 | 379 | int val, int val2) |
| 389 | 380 | { |
| 390 | − int ret, reg; | |
| 381 | + int reg; | |
| 391 | 382 | __be16 buf; |
| 392 | 383 | |
| 393 | 384 | reg = val * 8192 / SX9360_FOSC_HZ + val2 * 8192 / (SX9360_FOSC_MHZ); |
| 394 | 385 | buf = cpu_to_be16(reg); |
| 395 | − mutex_lock(&data->mutex); | |
| 396 | − | |
| 397 | − ret = regmap_bulk_write(data->regmap, SX9360_REG_GNRL_CTRL1, &buf, | |
| 398 | − sizeof(buf)); | |
| 386 | + guard(mutex)(&data->mutex); | |
| 399 | 387 | |
| 400 | − mutex_unlock(&data->mutex); | |
| 401 | − | |
| 402 | − return ret; | |
| 388 | + return regmap_bulk_write(data->regmap, SX9360_REG_GNRL_CTRL1, &buf, | |
| 389 | + sizeof(buf)); | |
| 403 | 390 | } |
| 404 | 391 | |
| 405 | 392 | static int sx9360_read_thresh(struct sx_common_data *data, int *val) |
| @@ -510,7 +497,6 @@ static int sx9360_read_event_val(struct iio_dev *indio_dev, · 104 unmodified lines | ||
| 510 | 497 | static int sx9360_write_thresh(struct sx_common_data *data, int _val) |
| 511 | 498 | { |
| 512 | 499 | unsigned int val = _val; |
| 513 | − int ret; | |
| 514 | 500 | |
| 515 | 501 | if (val >= 1) |
| 516 | 502 | val = int_sqrt(2 * val); |
| @@ -518,11 +504,8 @@ static int sx9360_write_thresh(struct sx_common_data *data, int _val) · 1 unmodified line | ||
| 518 | 504 | if (val > 0xff) |
| 519 | 505 | return -EINVAL; |
| 520 | 506 | |
| 521 | − mutex_lock(&data->mutex); | |
| 522 | − ret = regmap_write(data->regmap, SX9360_REG_PROX_CTRL5, val); | |
| 523 | − mutex_unlock(&data->mutex); | |
| 524 | − | |
| 525 | − return ret; | |
| 507 | + guard(mutex)(&data->mutex); | |
| 508 | + return regmap_write(data->regmap, SX9360_REG_PROX_CTRL5, val); | |
| 526 | 509 | } |
| 527 | 510 | |
| 528 | 511 | static int sx9360_write_hysteresis(struct sx_common_data *data, int _val) |
| @@ -546,18 +529,14 @@ static int sx9360_write_hysteresis(struct sx_common_data *data, int _val) · 17 unmodified lines | ||
| 546 | 529 | return -EINVAL; |
| 547 | 530 | |
| 548 | 531 | hyst = FIELD_PREP(SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); |
| 549 | − mutex_lock(&data->mutex); | |
| 550 | − ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, | |
| 551 | − SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); | |
| 552 | − mutex_unlock(&data->mutex); | |
| 553 | − | |
| 554 | − return ret; | |
| 532 | + guard(mutex)(&data->mutex); | |
| 533 | + return regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, | |
| 534 | + SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); | |
| 555 | 535 | } |
| 556 | 536 | |
| 557 | 537 | static int sx9360_write_far_debounce(struct sx_common_data *data, int _val) |
| 558 | 538 | { |
| 559 | 539 | unsigned int regval, val = _val; |
| 560 | − int ret; | |
| 561 | 540 | |
| 562 | 541 | if (val > 0) |
| 563 | 542 | val = ilog2(val); |
| @@ -566,19 +545,15 @@ static int sx9360_write_far_debounce(struct sx_common_data *data, int _val) · 2 unmodified lines | ||
| 566 | 545 | |
| 567 | 546 | regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val); |
| 568 | 547 | |
| 569 | − mutex_lock(&data->mutex); | |
| 570 | − ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, | |
| 571 | − SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, | |
| 572 | − regval); | |
| 573 | − mutex_unlock(&data->mutex); | |
| 574 | − | |
| 575 | − return ret; | |
| 548 | + guard(mutex)(&data->mutex); | |
| 549 | + return regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, | |
| 550 | + SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, | |
| 551 | + regval); | |
| 576 | 552 | } |
| 577 | 553 | |
| 578 | 554 | static int sx9360_write_close_debounce(struct sx_common_data *data, int _val) |
| 579 | 555 | { |
| 580 | 556 | unsigned int regval, val = _val; |
| 581 | − int ret; | |
| 582 | 557 | |
| 583 | 558 | if (val > 0) |
| 584 | 559 | val = ilog2(val); |
| @@ -587,13 +562,10 @@ static int sx9360_write_close_debounce(struct sx_common_data *data, int _val) · 2 unmodified lines | ||
| 587 | 562 | |
| 588 | 563 | regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val); |
| 589 | 564 | |
| 590 | − mutex_lock(&data->mutex); | |
| 591 | − ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, | |
| 592 | − SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, | |
| 593 | − regval); | |
| 594 | − mutex_unlock(&data->mutex); | |
| 595 | − | |
| 596 | − return ret; | |
| 565 | + guard(mutex)(&data->mutex); | |
| 566 | + return regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, | |
| 567 | + SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, | |
| 568 | + regval); | |
| 597 | 569 | } |
| 598 | 570 | |
| 599 | 571 | static int sx9360_write_event_val(struct iio_dev *indio_dev, |
| @@ -630,19 +602,15 @@ static int sx9360_write_gain(struct sx_common_data *data, · 30 unmodified lines | ||
| 630 | 602 | const struct iio_chan_spec *chan, int val) |
| 631 | 603 | { |
| 632 | 604 | unsigned int gain, reg; |
| 633 | − int ret; | |
| 634 | 605 | |
| 635 | 606 | gain = ilog2(val); |
| 636 | 607 | reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel; |
| 637 | 608 | gain = FIELD_PREP(SX9360_REG_PROX_CTRL0_GAIN_MASK, gain); |
| 638 | 609 | |
| 639 | − mutex_lock(&data->mutex); | |
| 640 | − ret = regmap_update_bits(data->regmap, reg, | |
| 641 | − SX9360_REG_PROX_CTRL0_GAIN_MASK, | |
| 642 | − gain); | |
| 643 | − mutex_unlock(&data->mutex); | |
| 644 | − | |
| 645 | − return ret; | |
| 610 | + guard(mutex)(&data->mutex); | |
| 611 | + return regmap_update_bits(data->regmap, reg, | |
| 612 | + SX9360_REG_PROX_CTRL0_GAIN_MASK, | |
| 613 | + gain); | |
| 646 | 614 | } |
| 647 | 615 | |
| 648 | 616 | static int sx9360_write_raw(struct iio_dev *indio_dev, |
| @@ -827,36 +795,31 @@ static int sx9360_suspend(struct device *dev) · 178 unmodified lines | ||
| 827 | 795 | |
| 828 | 796 | disable_irq_nosync(data->client->irq); |
| 829 | 797 | |
| 830 | − mutex_lock(&data->mutex); | |
| 798 | + guard(mutex)(&data->mutex); | |
| 831 | 799 | ret = regmap_read(data->regmap, SX9360_REG_GNRL_CTRL0, ®val); |
| 800 | + if (ret < 0) | |
| 801 | + return ret; | |
| 832 | 802 | |
| 833 | 803 | data->suspend_ctrl = |
| 834 | 804 | FIELD_GET(SX9360_REG_GNRL_CTRL0_PHEN_MASK, regval); |
| 835 | 805 | |
| 836 | − if (ret < 0) | |
| 837 | − goto out; | |
| 838 | 806 | |
| 839 | 807 | /* Disable all phases, send the device to sleep. */ |
| 840 | − ret = regmap_write(data->regmap, SX9360_REG_GNRL_CTRL0, 0); | |
| 841 | − | |
| 842 | −out: | |
| 843 | − mutex_unlock(&data->mutex); | |
| 844 | − return ret; | |
| 808 | + return regmap_write(data->regmap, SX9360_REG_GNRL_CTRL0, 0); | |
| 845 | 809 | } |
| 846 | 810 | |
| 847 | 811 | static int sx9360_resume(struct device *dev) |
| 848 | 812 | { |
| 849 | 813 | struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); |
| 850 | − int ret; | |
| 851 | − | |
| 852 | − mutex_lock(&data->mutex); | |
| 853 | − ret = regmap_update_bits(data->regmap, SX9360_REG_GNRL_CTRL0, | |
| 854 | − SX9360_REG_GNRL_CTRL0_PHEN_MASK, | |
| 855 | − data->suspend_ctrl); | |
| 856 | − mutex_unlock(&data->mutex); | |
| 857 | − if (ret) | |
| 858 | − return ret; | |
| 859 | 814 | |
| 815 | + scoped_guard(mutex, &data->mutex) { | |
| 816 | + int ret = regmap_update_bits(data->regmap, | |
| 817 | + SX9360_REG_GNRL_CTRL0, | |
| 818 | + SX9360_REG_GNRL_CTRL0_PHEN_MASK, | |
| 819 | + data->suspend_ctrl); | |
| 820 | + if (ret) | |
| 821 | + return ret; | |
| 822 | + } | |
| 860 | 823 | enable_irq(data->client->irq); |
| 861 | 824 | return 0; |
| 862 | 825 | } |
| @@ -207,6 +207,7 @@ enum { · 206 unmodified lines | ||
| 207 | 207 | container_of(_sensor, struct ltc2983_temp, sensor) |
| 208 | 208 | |
| 209 | 209 | struct ltc2983_chip_info { |
| 210 | + const char *name; | |
| 210 | 211 | unsigned int max_channels_nr; |
| 211 | 212 | bool has_temp; |
| 212 | 213 | bool has_eeprom; |
| @@ -1346,7 +1347,7 @@ static irqreturn_t ltc2983_irq_handler(int irq, void *data) · 1133 unmodified lines | ||
| 1346 | 1347 | __chan; \ |
| 1347 | 1348 | }) |
| 1348 | 1349 | |
| 1349 | −static int ltc2983_parse_dt(struct ltc2983_data *st) | |
| 1350 | +static int ltc2983_parse_fw(struct ltc2983_data *st) | |
| 1350 | 1351 | { |
| 1351 | 1352 | struct device *dev = &st->spi->dev; |
| 1352 | 1353 | struct fwnode_handle *child; |
| @@ -1605,7 +1606,6 @@ static int ltc2983_probe(struct spi_device *spi) · 252 unmodified lines | ||
| 1605 | 1606 | struct ltc2983_data *st; |
| 1606 | 1607 | struct iio_dev *indio_dev; |
| 1607 | 1608 | struct gpio_desc *gpio; |
| 1608 | − const char *name = spi_get_device_id(spi)->name; | |
| 1609 | 1609 | int ret; |
| 1610 | 1610 | |
| 1611 | 1611 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
| @@ -1614,9 +1614,7 @@ static int ltc2983_probe(struct spi_device *spi) · 2 unmodified lines | ||
| 1614 | 1614 | |
| 1615 | 1615 | st = iio_priv(indio_dev); |
| 1616 | 1616 | |
| 1617 | − st->info = device_get_match_data(&spi->dev); | |
| 1618 | − if (!st->info) | |
| 1619 | − st->info = (void *)spi_get_device_id(spi)->driver_data; | |
| 1617 | + st->info = spi_get_device_match_data(spi); | |
| 1620 | 1618 | if (!st->info) |
| 1621 | 1619 | return -ENODEV; |
| 1622 | 1620 | |
| @@ -1632,7 +1630,7 @@ static int ltc2983_probe(struct spi_device *spi) · 9 unmodified lines | ||
| 1632 | 1630 | st->eeprom_key = cpu_to_be32(LTC2983_EEPROM_KEY); |
| 1633 | 1631 | spi_set_drvdata(spi, st); |
| 1634 | 1632 | |
| 1635 | − ret = ltc2983_parse_dt(st); | |
| 1633 | + ret = ltc2983_parse_fw(st); | |
| 1636 | 1634 | if (ret) |
| 1637 | 1635 | return ret; |
| 1638 | 1636 | |
| @@ -1657,7 +1655,7 @@ static int ltc2983_probe(struct spi_device *spi) · 18 unmodified lines | ||
| 1657 | 1655 | return ret; |
| 1658 | 1656 | |
| 1659 | 1657 | ret = devm_request_irq(&spi->dev, spi->irq, ltc2983_irq_handler, |
| 1660 | − IRQF_TRIGGER_RISING, name, st); | |
| 1658 | + IRQF_TRIGGER_RISING, st->info->name, st); | |
| 1661 | 1659 | if (ret) { |
| 1662 | 1660 | dev_err(&spi->dev, "failed to request an irq, %d", ret); |
| 1663 | 1661 | return ret; |
| @@ -1672,7 +1670,7 @@ static int ltc2983_probe(struct spi_device *spi) · 8 unmodified lines | ||
| 1672 | 1670 | return ret; |
| 1673 | 1671 | } |
| 1674 | 1672 | |
| 1675 | − indio_dev->name = name; | |
| 1673 | + indio_dev->name = st->info->name; | |
| 1676 | 1674 | indio_dev->num_channels = st->iio_channels; |
| 1677 | 1675 | indio_dev->channels = st->iio_chan; |
| 1678 | 1676 | indio_dev->modes = INDIO_DIRECT_MODE; |
| @@ -1703,15 +1701,25 @@ static DEFINE_SIMPLE_DEV_PM_OPS(ltc2983_pm_ops, ltc2983_suspend, · 24 unmodified lines | ||
| 1703 | 1701 | ltc2983_resume); |
| 1704 | 1702 | |
| 1705 | 1703 | static const struct ltc2983_chip_info ltc2983_chip_info_data = { |
| 1704 | + .name = "ltc2983", | |
| 1706 | 1705 | .max_channels_nr = 20, |
| 1707 | 1706 | }; |
| 1708 | 1707 | |
| 1709 | 1708 | static const struct ltc2983_chip_info ltc2984_chip_info_data = { |
| 1709 | + .name = "ltc2984", | |
| 1710 | 1710 | .max_channels_nr = 20, |
| 1711 | 1711 | .has_eeprom = true, |
| 1712 | 1712 | }; |
| 1713 | 1713 | |
| 1714 | 1714 | static const struct ltc2983_chip_info ltc2986_chip_info_data = { |
| 1715 | + .name = "ltc2986", | |
| 1716 | + .max_channels_nr = 10, | |
| 1717 | + .has_temp = true, | |
| 1718 | + .has_eeprom = true, | |
| 1719 | +}; | |
| 1720 | + | |
| 1721 | +static const struct ltc2983_chip_info ltm2985_chip_info_data = { | |
| 1722 | + .name = "ltm2985", | |
| 1715 | 1723 | .max_channels_nr = 10, |
| 1716 | 1724 | .has_temp = true, |
| 1717 | 1725 | .has_eeprom = true, |
| @@ -1721,7 +1729,7 @@ static const struct spi_device_id ltc2983_id_table[] = { · 3 unmodified lines | ||
| 1721 | 1729 | { "ltc2983", (kernel_ulong_t)<c2983_chip_info_data }, |
| 1722 | 1730 | { "ltc2984", (kernel_ulong_t)<c2984_chip_info_data }, |
| 1723 | 1731 | { "ltc2986", (kernel_ulong_t)<c2986_chip_info_data }, |
| 1724 | − { "ltm2985", (kernel_ulong_t)<c2986_chip_info_data }, | |
| 1732 | + { "ltm2985", (kernel_ulong_t)<m2985_chip_info_data }, | |
| 1725 | 1733 | {}, |
| 1726 | 1734 | }; |
| 1727 | 1735 | MODULE_DEVICE_TABLE(spi, ltc2983_id_table); |
| @@ -1730,7 +1738,7 @@ static const struct of_device_id ltc2983_of_match[] = { · 2 unmodified lines | ||
| 1730 | 1738 | { .compatible = "adi,ltc2983", .data = <c2983_chip_info_data }, |
| 1731 | 1739 | { .compatible = "adi,ltc2984", .data = <c2984_chip_info_data }, |
| 1732 | 1740 | { .compatible = "adi,ltc2986", .data = <c2986_chip_info_data }, |
| 1733 | − { .compatible = "adi,ltm2985", .data = <c2986_chip_info_data }, | |
| 1741 | + { .compatible = "adi,ltm2985", .data = <m2985_chip_info_data }, | |
| 1734 | 1742 | {}, |
| 1735 | 1743 | }; |
| 1736 | 1744 | MODULE_DEVICE_TABLE(of, ltc2983_of_match); |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | * Note: This driver assumes that the sensor has been calibrated beforehand. |
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | +#include <linux/delay.h> | |
| 12 | 13 | #include <linux/err.h> |
| 13 | 14 | #include <linux/i2c.h> |
| 14 | 15 | #include <linux/module.h> |
| @@ -17,6 +18,7 @@ · 2 unmodified lines | ||
| 17 | 18 | #include <linux/kernel.h> |
| 18 | 19 | #include <linux/limits.h> |
| 19 | 20 | #include <linux/property.h> |
| 21 | +#include <linux/regulator/consumer.h> | |
| 20 | 22 | |
| 21 | 23 | #include <linux/iio/iio.h> |
| 22 | 24 | |
| @@ -148,10 +150,17 @@ static int tmp117_probe(struct i2c_client *client) · 125 unmodified lines | ||
| 148 | 150 | struct tmp117_data *data; |
| 149 | 151 | struct iio_dev *indio_dev; |
| 150 | 152 | int dev_id; |
| 153 | + int ret; | |
| 151 | 154 | |
| 152 | 155 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) |
| 153 | 156 | return -EOPNOTSUPP; |
| 154 | 157 | |
| 158 | + ret = devm_regulator_get_enable(&client->dev, "vcc"); | |
| 159 | + if (ret) | |
| 160 | + return ret; | |
| 161 | + | |
| 162 | + fsleep(1500); | |
| 163 | + | |
| 155 | 164 | dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID); |
| 156 | 165 | if (dev_id < 0) |
| 157 | 166 | return dev_id; |
| @@ -4,6 +4,20 @@ · 3 unmodified lines | ||
| 4 | 4 | # |
| 5 | 5 | |
| 6 | 6 | # Keep in alphabetical order |
| 7 | +config IIO_GTS_KUNIT_TEST | |
| 8 | + tristate "Test IIO formatting functions" if !KUNIT_ALL_TESTS | |
| 9 | + depends on KUNIT | |
| 10 | + select IIO_GTS_HELPER | |
| 11 | + select TEST_KUNIT_DEVICE_HELPERS | |
| 12 | + default KUNIT_ALL_TESTS | |
| 13 | + help | |
| 14 | + build unit tests for the IIO light sensor gain-time-scale helpers. | |
| 15 | + | |
| 16 | + For more information on KUnit and unit tests in general, please refer | |
| 17 | + to the KUnit documentation in Documentation/dev-tools/kunit/. | |
| 18 | + | |
| 19 | + If unsure, say N. Keep in alphabetical order | |
| 20 | + | |
| 7 | 21 | config IIO_RESCALE_KUNIT_TEST |
| 8 | 22 | tristate "Test IIO rescale conversion functions" if !KUNIT_ALL_TESTS |
| 9 | 23 | depends on KUNIT && IIO_RESCALE |
| @@ -6,4 +6,5 @@ · 5 unmodified lines | ||
| 6 | 6 | # Keep in alphabetical order |
| 7 | 7 | obj-$(CONFIG_IIO_RESCALE_KUNIT_TEST) += iio-test-rescale.o |
| 8 | 8 | obj-$(CONFIG_IIO_FORMAT_KUNIT_TEST) += iio-test-format.o |
| 9 | +obj-$(CONFIG_IIO_GTS_KUNIT_TEST) += iio-test-gts.o | |
| 9 | 10 | CFLAGS_iio-test-format.o += $(DISABLE_STRUCTLEAK_PLUGIN) |
| @@ -0,0 +1,513 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* Unit tests for IIO light sensor gain-time-scale helpers | |
| 3 | + * | |
| 4 | + * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com> | |
| 5 | + */ | |
| 6 | + | |
| 7 | +#include <kunit/device.h> | |
| 8 | +#include <kunit/test.h> | |
| 9 | +#include <linux/device.h> | |
| 10 | +#include <linux/iio/iio-gts-helper.h> | |
| 11 | +#include <linux/iio/types.h> | |
| 12 | + | |
| 13 | +/* | |
| 14 | + * Please, read the "rant" from the top of the lib/test_linear_ranges.c if | |
| 15 | + * you see a line of helper code which is not being tested. | |
| 16 | + * | |
| 17 | + * Then, please look at the line which is not being tested. Is this line | |
| 18 | + * somehow unusually complex? If answer is "no", then chances are that the | |
| 19 | + * "development inertia" caused by adding a test exceeds the benefits. | |
| 20 | + * | |
| 21 | + * If yes, then adding a test is probably a good idea but please stop for a | |
| 22 | + * moment and consider the effort of changing all the tests when code gets | |
| 23 | + * refactored. Eventually it neeeds to be. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +#define TEST_TSEL_50 1 | |
| 27 | +#define TEST_TSEL_X_MIN TEST_TSEL_50 | |
| 28 | +#define TEST_TSEL_100 0 | |
| 29 | +#define TEST_TSEL_200 2 | |
| 30 | +#define TEST_TSEL_400 4 | |
| 31 | +#define TEST_TSEL_X_MAX TEST_TSEL_400 | |
| 32 | + | |
| 33 | +#define TEST_GSEL_1 0x00 | |
| 34 | +#define TEST_GSEL_X_MIN TEST_GSEL_1 | |
| 35 | +#define TEST_GSEL_4 0x08 | |
| 36 | +#define TEST_GSEL_16 0x0a | |
| 37 | +#define TEST_GSEL_32 0x0b | |
| 38 | +#define TEST_GSEL_64 0x0c | |
| 39 | +#define TEST_GSEL_256 0x18 | |
| 40 | +#define TEST_GSEL_512 0x19 | |
| 41 | +#define TEST_GSEL_1024 0x1a | |
| 42 | +#define TEST_GSEL_2048 0x1b | |
| 43 | +#define TEST_GSEL_4096 0x1c | |
| 44 | +#define TEST_GSEL_X_MAX TEST_GSEL_4096 | |
| 45 | + | |
| 46 | +#define TEST_SCALE_1X 64 | |
| 47 | +#define TEST_SCALE_MIN_X TEST_SCALE_1X | |
| 48 | +#define TEST_SCALE_2X 32 | |
| 49 | +#define TEST_SCALE_4X 16 | |
| 50 | +#define TEST_SCALE_8X 8 | |
| 51 | +#define TEST_SCALE_16X 4 | |
| 52 | +#define TEST_SCALE_32X 2 | |
| 53 | +#define TEST_SCALE_64X 1 | |
| 54 | + | |
| 55 | +#define TEST_SCALE_NANO_128X 500000000 | |
| 56 | +#define TEST_SCALE_NANO_256X 250000000 | |
| 57 | +#define TEST_SCALE_NANO_512X 125000000 | |
| 58 | +#define TEST_SCALE_NANO_1024X 62500000 | |
| 59 | +#define TEST_SCALE_NANO_2048X 31250000 | |
| 60 | +#define TEST_SCALE_NANO_4096X 15625000 | |
| 61 | +#define TEST_SCALE_NANO_4096X2 7812500 | |
| 62 | +#define TEST_SCALE_NANO_4096X4 3906250 | |
| 63 | +#define TEST_SCALE_NANO_4096X8 1953125 | |
| 64 | + | |
| 65 | +#define TEST_SCALE_NANO_MAX_X TEST_SCALE_NANO_4096X8 | |
| 66 | + | |
| 67 | +/* | |
| 68 | + * Can't have this allocated from stack because the kunit clean-up will | |
| 69 | + * happen only after the test function has already gone | |
| 70 | + */ | |
| 71 | +static struct iio_gts gts; | |
| 72 | + | |
| 73 | +static const struct iio_gain_sel_pair gts_test_gains[] = { | |
| 74 | + GAIN_SCALE_GAIN(1, TEST_GSEL_1), | |
| 75 | + GAIN_SCALE_GAIN(4, TEST_GSEL_4), | |
| 76 | + GAIN_SCALE_GAIN(16, TEST_GSEL_16), | |
| 77 | + GAIN_SCALE_GAIN(32, TEST_GSEL_32), | |
| 78 | + GAIN_SCALE_GAIN(64, TEST_GSEL_64), | |
| 79 | + GAIN_SCALE_GAIN(256, TEST_GSEL_256), | |
| 80 | + GAIN_SCALE_GAIN(512, TEST_GSEL_512), | |
| 81 | + GAIN_SCALE_GAIN(1024, TEST_GSEL_1024), | |
| 82 | + GAIN_SCALE_GAIN(2048, TEST_GSEL_2048), | |
| 83 | + GAIN_SCALE_GAIN(4096, TEST_GSEL_4096), | |
| 84 | +#define HWGAIN_MAX 4096 | |
| 85 | +}; | |
| 86 | + | |
| 87 | +static const struct iio_itime_sel_mul gts_test_itimes[] = { | |
| 88 | + GAIN_SCALE_ITIME_US(400 * 1000, TEST_TSEL_400, 8), | |
| 89 | + GAIN_SCALE_ITIME_US(200 * 1000, TEST_TSEL_200, 4), | |
| 90 | + GAIN_SCALE_ITIME_US(100 * 1000, TEST_TSEL_100, 2), | |
| 91 | + GAIN_SCALE_ITIME_US(50 * 1000, TEST_TSEL_50, 1), | |
| 92 | +#define TIMEGAIN_MAX 8 | |
| 93 | +}; | |
| 94 | +#define TOTAL_GAIN_MAX (HWGAIN_MAX * TIMEGAIN_MAX) | |
| 95 | +#define IIO_GTS_TEST_DEV "iio-gts-test-dev" | |
| 96 | + | |
| 97 | +static struct device *__test_init_iio_gain_scale(struct kunit *test, | |
| 98 | + struct iio_gts *gts, const struct iio_gain_sel_pair *g_table, | |
| 99 | + int num_g, const struct iio_itime_sel_mul *i_table, int num_i) | |
| 100 | +{ | |
| 101 | + struct device *dev; | |
| 102 | + int ret; | |
| 103 | + | |
| 104 | + dev = kunit_device_register(test, IIO_GTS_TEST_DEV); | |
| 105 | + | |
| 106 | + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev); | |
| 107 | + if (IS_ERR_OR_NULL(dev)) | |
| 108 | + return NULL; | |
| 109 | + | |
| 110 | + ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, g_table, num_g, | |
| 111 | + i_table, num_i, gts); | |
| 112 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 113 | + if (ret) | |
| 114 | + return NULL; | |
| 115 | + | |
| 116 | + return dev; | |
| 117 | +} | |
| 118 | + | |
| 119 | +#define test_init_iio_gain_scale(test, gts) \ | |
| 120 | + __test_init_iio_gain_scale(test, gts, gts_test_gains, \ | |
| 121 | + ARRAY_SIZE(gts_test_gains), gts_test_itimes, \ | |
| 122 | + ARRAY_SIZE(gts_test_itimes)) | |
| 123 | + | |
| 124 | +static void test_init_iio_gts_invalid(struct kunit *test) | |
| 125 | +{ | |
| 126 | + struct device *dev; | |
| 127 | + int ret; | |
| 128 | + const struct iio_itime_sel_mul itimes_neg[] = { | |
| 129 | + GAIN_SCALE_ITIME_US(-10, TEST_TSEL_400, 8), | |
| 130 | + GAIN_SCALE_ITIME_US(200 * 1000, TEST_TSEL_200, 4), | |
| 131 | + }; | |
| 132 | + const struct iio_gain_sel_pair gains_neg[] = { | |
| 133 | + GAIN_SCALE_GAIN(1, TEST_GSEL_1), | |
| 134 | + GAIN_SCALE_GAIN(2, TEST_GSEL_4), | |
| 135 | + GAIN_SCALE_GAIN(-2, TEST_GSEL_16), | |
| 136 | + }; | |
| 137 | + /* 55555 * 38656 = 2147534080 => overflows 32bit int */ | |
| 138 | + const struct iio_itime_sel_mul itimes_overflow[] = { | |
| 139 | + GAIN_SCALE_ITIME_US(400 * 1000, TEST_TSEL_400, 55555), | |
| 140 | + GAIN_SCALE_ITIME_US(200 * 1000, TEST_TSEL_200, 4), | |
| 141 | + }; | |
| 142 | + const struct iio_gain_sel_pair gains_overflow[] = { | |
| 143 | + GAIN_SCALE_GAIN(1, TEST_GSEL_1), | |
| 144 | + GAIN_SCALE_GAIN(2, TEST_GSEL_4), | |
| 145 | + GAIN_SCALE_GAIN(38656, TEST_GSEL_16), | |
| 146 | + }; | |
| 147 | + | |
| 148 | + dev = kunit_device_register(test, IIO_GTS_TEST_DEV); | |
| 149 | + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev); | |
| 150 | + if (!dev) | |
| 151 | + return; | |
| 152 | + | |
| 153 | + /* Ok gains, negative time */ | |
| 154 | + ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, gts_test_gains, | |
| 155 | + ARRAY_SIZE(gts_test_gains), itimes_neg, | |
| 156 | + ARRAY_SIZE(itimes_neg), >s); | |
| 157 | + KUNIT_EXPECT_EQ(test, -EINVAL, ret); | |
| 158 | + | |
| 159 | + /* Ok times, negative gain */ | |
| 160 | + ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, gains_neg, | |
| 161 | + ARRAY_SIZE(gains_neg), gts_test_itimes, | |
| 162 | + ARRAY_SIZE(gts_test_itimes), >s); | |
| 163 | + KUNIT_EXPECT_EQ(test, -EINVAL, ret); | |
| 164 | + | |
| 165 | + /* gain * time overflow int */ | |
| 166 | + ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, gains_overflow, | |
| 167 | + ARRAY_SIZE(gains_overflow), itimes_overflow, | |
| 168 | + ARRAY_SIZE(itimes_overflow), >s); | |
| 169 | + KUNIT_EXPECT_EQ(test, -EOVERFLOW, ret); | |
| 170 | +} | |
| 171 | + | |
| 172 | +static void test_iio_gts_find_gain_for_scale_using_time(struct kunit *test) | |
| 173 | +{ | |
| 174 | + struct device *dev; | |
| 175 | + int ret, gain_sel; | |
| 176 | + | |
| 177 | + dev = test_init_iio_gain_scale(test, >s); | |
| 178 | + if (!dev) | |
| 179 | + return; | |
| 180 | + | |
| 181 | + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_100, | |
| 182 | + TEST_SCALE_8X, 0, &gain_sel); | |
| 183 | + /* | |
| 184 | + * Meas time 100 => gain by time 2x | |
| 185 | + * TEST_SCALE_8X matches total gain 8x | |
| 186 | + * => required HWGAIN 4x | |
| 187 | + */ | |
| 188 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 189 | + KUNIT_EXPECT_EQ(test, TEST_GSEL_4, gain_sel); | |
| 190 | + | |
| 191 | + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_200, 0, | |
| 192 | + TEST_SCALE_NANO_256X, &gain_sel); | |
| 193 | + /* | |
| 194 | + * Meas time 200 => gain by time 4x | |
| 195 | + * TEST_SCALE_256X matches total gain 256x | |
| 196 | + * => required HWGAIN 256/4 => 64x | |
| 197 | + */ | |
| 198 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 199 | + KUNIT_EXPECT_EQ(test, TEST_GSEL_64, gain_sel); | |
| 200 | + | |
| 201 | + /* Min time, Min gain */ | |
| 202 | + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_X_MIN, | |
| 203 | + TEST_SCALE_MIN_X, 0, &gain_sel); | |
| 204 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 205 | + KUNIT_EXPECT_EQ(test, TEST_GSEL_1, gain_sel); | |
| 206 | + | |
| 207 | + /* Max time, Max gain */ | |
| 208 | + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_X_MAX, | |
| 209 | + 0, TEST_SCALE_NANO_MAX_X, &gain_sel); | |
| 210 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 211 | + KUNIT_EXPECT_EQ(test, TEST_GSEL_4096, gain_sel); | |
| 212 | + | |
| 213 | + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_100, 0, | |
| 214 | + TEST_SCALE_NANO_256X, &gain_sel); | |
| 215 | + /* | |
| 216 | + * Meas time 100 => gain by time 2x | |
| 217 | + * TEST_SCALE_256X matches total gain 256x | |
| 218 | + * => required HWGAIN 256/2 => 128x (not in gain-table - unsupported) | |
| 219 | + */ | |
| 220 | + KUNIT_EXPECT_NE(test, 0, ret); | |
| 221 | + | |
| 222 | + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_200, 0, | |
| 223 | + TEST_SCALE_NANO_MAX_X, &gain_sel); | |
| 224 | + /* We can't reach the max gain with integration time smaller than MAX */ | |
| 225 | + KUNIT_EXPECT_NE(test, 0, ret); | |
| 226 | + | |
| 227 | + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_50, 0, | |
| 228 | + TEST_SCALE_NANO_MAX_X, &gain_sel); | |
| 229 | + /* We can't reach the max gain with integration time smaller than MAX */ | |
| 230 | + KUNIT_EXPECT_NE(test, 0, ret); | |
| 231 | +} | |
| 232 | + | |
| 233 | +static void test_iio_gts_find_new_gain_sel_by_old_gain_time(struct kunit *test) | |
| 234 | +{ | |
| 235 | + struct device *dev; | |
| 236 | + int ret, old_gain, new_gain, old_time_sel, new_time_sel; | |
| 237 | + | |
| 238 | + dev = test_init_iio_gain_scale(test, >s); | |
| 239 | + if (!dev) | |
| 240 | + return; | |
| 241 | + | |
| 242 | + old_gain = 32; | |
| 243 | + old_time_sel = TEST_TSEL_200; | |
| 244 | + new_time_sel = TEST_TSEL_400; | |
| 245 | + | |
| 246 | + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, | |
| 247 | + old_time_sel, new_time_sel, &new_gain); | |
| 248 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 249 | + /* | |
| 250 | + * Doubling the integration time doubles the total gain - so old | |
| 251 | + * (hw)gain must be divided by two to compensate. => 32 / 2 => 16 | |
| 252 | + */ | |
| 253 | + KUNIT_EXPECT_EQ(test, 16, new_gain); | |
| 254 | + | |
| 255 | + old_gain = 4; | |
| 256 | + old_time_sel = TEST_TSEL_50; | |
| 257 | + new_time_sel = TEST_TSEL_200; | |
| 258 | + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, | |
| 259 | + old_time_sel, new_time_sel, &new_gain); | |
| 260 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 261 | + /* | |
| 262 | + * gain by time 1x => 4x - (hw)gain 4x => 1x | |
| 263 | + */ | |
| 264 | + KUNIT_EXPECT_EQ(test, 1, new_gain); | |
| 265 | + | |
| 266 | + old_gain = 512; | |
| 267 | + old_time_sel = TEST_TSEL_400; | |
| 268 | + new_time_sel = TEST_TSEL_50; | |
| 269 | + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, | |
| 270 | + old_time_sel, new_time_sel, &new_gain); | |
| 271 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 272 | + /* | |
| 273 | + * gain by time 8x => 1x - (hw)gain 512x => 4096x) | |
| 274 | + */ | |
| 275 | + KUNIT_EXPECT_EQ(test, 4096, new_gain); | |
| 276 | + | |
| 277 | + /* Unsupported gain 2x */ | |
| 278 | + old_gain = 4; | |
| 279 | + old_time_sel = TEST_TSEL_200; | |
| 280 | + new_time_sel = TEST_TSEL_400; | |
| 281 | + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, | |
| 282 | + old_time_sel, new_time_sel, &new_gain); | |
| 283 | + KUNIT_EXPECT_NE(test, 0, ret); | |
| 284 | + | |
| 285 | + /* Too small gain */ | |
| 286 | + old_gain = 4; | |
| 287 | + old_time_sel = TEST_TSEL_50; | |
| 288 | + new_time_sel = TEST_TSEL_400; | |
| 289 | + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, | |
| 290 | + old_time_sel, new_time_sel, &new_gain); | |
| 291 | + KUNIT_EXPECT_NE(test, 0, ret); | |
| 292 | + | |
| 293 | + /* Too big gain */ | |
| 294 | + old_gain = 1024; | |
| 295 | + old_time_sel = TEST_TSEL_400; | |
| 296 | + new_time_sel = TEST_TSEL_50; | |
| 297 | + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, | |
| 298 | + old_time_sel, new_time_sel, &new_gain); | |
| 299 | + KUNIT_EXPECT_NE(test, 0, ret); | |
| 300 | + | |
| 301 | +} | |
| 302 | + | |
| 303 | +static void test_iio_find_closest_gain_low(struct kunit *test) | |
| 304 | +{ | |
| 305 | + struct device *dev; | |
| 306 | + bool in_range; | |
| 307 | + int ret; | |
| 308 | + | |
| 309 | + const struct iio_gain_sel_pair gts_test_gains_gain_low[] = { | |
| 310 | + GAIN_SCALE_GAIN(4, TEST_GSEL_4), | |
| 311 | + GAIN_SCALE_GAIN(16, TEST_GSEL_16), | |
| 312 | + GAIN_SCALE_GAIN(32, TEST_GSEL_32), | |
| 313 | + }; | |
| 314 | + | |
| 315 | + dev = test_init_iio_gain_scale(test, >s); | |
| 316 | + if (!dev) | |
| 317 | + return; | |
| 318 | + | |
| 319 | + ret = iio_find_closest_gain_low(>s, 2, &in_range); | |
| 320 | + KUNIT_EXPECT_EQ(test, 1, ret); | |
| 321 | + KUNIT_EXPECT_EQ(test, true, in_range); | |
| 322 | + | |
| 323 | + ret = iio_find_closest_gain_low(>s, 1, &in_range); | |
| 324 | + KUNIT_EXPECT_EQ(test, 1, ret); | |
| 325 | + KUNIT_EXPECT_EQ(test, true, in_range); | |
| 326 | + | |
| 327 | + ret = iio_find_closest_gain_low(>s, 4095, &in_range); | |
| 328 | + KUNIT_EXPECT_EQ(test, 2048, ret); | |
| 329 | + KUNIT_EXPECT_EQ(test, true, in_range); | |
| 330 | + | |
| 331 | + ret = iio_find_closest_gain_low(>s, 4097, &in_range); | |
| 332 | + KUNIT_EXPECT_EQ(test, 4096, ret); | |
| 333 | + KUNIT_EXPECT_EQ(test, false, in_range); | |
| 334 | + | |
| 335 | + kunit_device_unregister(test, dev); | |
| 336 | + | |
| 337 | + dev = __test_init_iio_gain_scale(test, >s, gts_test_gains_gain_low, | |
| 338 | + ARRAY_SIZE(gts_test_gains_gain_low), | |
| 339 | + gts_test_itimes, ARRAY_SIZE(gts_test_itimes)); | |
| 340 | + if (!dev) | |
| 341 | + return; | |
| 342 | + | |
| 343 | + ret = iio_find_closest_gain_low(>s, 3, &in_range); | |
| 344 | + KUNIT_EXPECT_EQ(test, -EINVAL, ret); | |
| 345 | + KUNIT_EXPECT_EQ(test, false, in_range); | |
| 346 | +} | |
| 347 | + | |
| 348 | +static void test_iio_gts_total_gain_to_scale(struct kunit *test) | |
| 349 | +{ | |
| 350 | + struct device *dev; | |
| 351 | + int ret, scale_int, scale_nano; | |
| 352 | + | |
| 353 | + dev = test_init_iio_gain_scale(test, >s); | |
| 354 | + if (!dev) | |
| 355 | + return; | |
| 356 | + | |
| 357 | + ret = iio_gts_total_gain_to_scale(>s, 1, &scale_int, &scale_nano); | |
| 358 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 359 | + KUNIT_EXPECT_EQ(test, TEST_SCALE_1X, scale_int); | |
| 360 | + KUNIT_EXPECT_EQ(test, 0, scale_nano); | |
| 361 | + | |
| 362 | + ret = iio_gts_total_gain_to_scale(>s, 1, &scale_int, &scale_nano); | |
| 363 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 364 | + KUNIT_EXPECT_EQ(test, TEST_SCALE_1X, scale_int); | |
| 365 | + KUNIT_EXPECT_EQ(test, 0, scale_nano); | |
| 366 | + | |
| 367 | + ret = iio_gts_total_gain_to_scale(>s, 4096 * 8, &scale_int, | |
| 368 | + &scale_nano); | |
| 369 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 370 | + KUNIT_EXPECT_EQ(test, 0, scale_int); | |
| 371 | + KUNIT_EXPECT_EQ(test, TEST_SCALE_NANO_4096X8, scale_nano); | |
| 372 | +} | |
| 373 | + | |
| 374 | +static void test_iio_gts_chk_times(struct kunit *test, const int *vals) | |
| 375 | +{ | |
| 376 | + static const int expected[] = {0, 50000, 0, 100000, 0, 200000, 0, 400000}; | |
| 377 | + int i; | |
| 378 | + | |
| 379 | + for (i = 0; i < ARRAY_SIZE(expected); i++) | |
| 380 | + KUNIT_EXPECT_EQ(test, expected[i], vals[i]); | |
| 381 | +} | |
| 382 | + | |
| 383 | +static void test_iio_gts_chk_scales_all(struct kunit *test, struct iio_gts *gts, | |
| 384 | + const int *vals, int len) | |
| 385 | +{ | |
| 386 | + static const int gains[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, | |
| 387 | + 1024, 2048, 4096, 4096 * 2, 4096 * 4, | |
| 388 | + 4096 * 8}; | |
| 389 | + int expected[ARRAY_SIZE(gains) * 2]; | |
| 390 | + int i, ret; | |
| 391 | + int exp_len = ARRAY_SIZE(gains) * 2; | |
| 392 | + | |
| 393 | + KUNIT_EXPECT_EQ(test, exp_len, len); | |
| 394 | + if (len != exp_len) | |
| 395 | + return; | |
| 396 | + | |
| 397 | + for (i = 0; i < ARRAY_SIZE(gains); i++) { | |
| 398 | + ret = iio_gts_total_gain_to_scale(gts, gains[i], | |
| 399 | + &expected[2 * i], | |
| 400 | + &expected[2 * i + 1]); | |
| 401 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 402 | + if (ret) | |
| 403 | + return; | |
| 404 | + } | |
| 405 | + | |
| 406 | + for (i = 0; i < ARRAY_SIZE(expected); i++) | |
| 407 | + KUNIT_EXPECT_EQ(test, expected[i], vals[i]); | |
| 408 | +} | |
| 409 | + | |
| 410 | +static void test_iio_gts_chk_scales_t200(struct kunit *test, struct iio_gts *gts, | |
| 411 | + const int *vals, int len) | |
| 412 | +{ | |
| 413 | + /* The gain caused by time 200 is 4x */ | |
| 414 | + static const int gains[] = { | |
| 415 | + 1 * 4, | |
| 416 | + 4 * 4, | |
| 417 | + 16 * 4, | |
| 418 | + 32 * 4, | |
| 419 | + 64 * 4, | |
| 420 | + 256 * 4, | |
| 421 | + 512 * 4, | |
| 422 | + 1024 * 4, | |
| 423 | + 2048 * 4, | |
| 424 | + 4096 * 4 | |
| 425 | + }; | |
| 426 | + int expected[ARRAY_SIZE(gains) * 2]; | |
| 427 | + int i, ret; | |
| 428 | + | |
| 429 | + KUNIT_EXPECT_EQ(test, 2 * ARRAY_SIZE(gains), len); | |
| 430 | + if (len < 2 * ARRAY_SIZE(gains)) | |
| 431 | + return; | |
| 432 | + | |
| 433 | + for (i = 0; i < ARRAY_SIZE(gains); i++) { | |
| 434 | + ret = iio_gts_total_gain_to_scale(gts, gains[i], | |
| 435 | + &expected[2 * i], | |
| 436 | + &expected[2 * i + 1]); | |
| 437 | + KUNIT_EXPECT_EQ(test, 0, ret); | |
| 438 | + if (ret) | |
| 439 | + return; | |
| 440 | + } | |
| 441 | + | |
| 442 | + for (i = 0; i < ARRAY_SIZE(expected); i++) | |
| 443 | + KUNIT_EXPECT_EQ(test, expected[i], vals[i]); | |
| 444 | +} | |
| 445 | + | |
| 446 | +static void test_iio_gts_avail_test(struct kunit *test) | |
| 447 | +{ | |
| 448 | + struct device *dev; | |
| 449 | + int ret; | |
| 450 | + int type, len; | |
| 451 | + const int *vals; | |
| 452 | + | |
| 453 | + dev = test_init_iio_gain_scale(test, >s); | |
| 454 | + if (!dev) | |
| 455 | + return; | |
| 456 | + | |
| 457 | + /* test table building for times and iio_gts_avail_times() */ | |
| 458 | + ret = iio_gts_avail_times(>s, &vals, &type, &len); | |
| 459 | + KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret); | |
| 460 | + if (ret) | |
| 461 | + return; | |
| 462 | + | |
| 463 | + KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_MICRO, type); | |
| 464 | + KUNIT_EXPECT_EQ(test, 8, len); | |
| 465 | + if (len < 8) | |
| 466 | + return; | |
| 467 | + | |
| 468 | + test_iio_gts_chk_times(test, vals); | |
| 469 | + | |
| 470 | + /* Test table building for all scales and iio_gts_all_avail_scales() */ | |
| 471 | + ret = iio_gts_all_avail_scales(>s, &vals, &type, &len); | |
| 472 | + KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret); | |
| 473 | + if (ret) | |
| 474 | + return; | |
| 475 | + | |
| 476 | + KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_NANO, type); | |
| 477 | + | |
| 478 | + test_iio_gts_chk_scales_all(test, >s, vals, len); | |
| 479 | + | |
| 480 | + /* | |
| 481 | + * Test table building for scales/time and | |
| 482 | + * iio_gts_avail_scales_for_time() | |
| 483 | + */ | |
| 484 | + ret = iio_gts_avail_scales_for_time(>s, 200000, &vals, &type, &len); | |
| 485 | + KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret); | |
| 486 | + if (ret) | |
| 487 | + return; | |
| 488 | + | |
| 489 | + KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_NANO, type); | |
| 490 | + test_iio_gts_chk_scales_t200(test, >s, vals, len); | |
| 491 | +} | |
| 492 | + | |
| 493 | +static struct kunit_case iio_gts_test_cases[] = { | |
| 494 | + KUNIT_CASE(test_init_iio_gts_invalid), | |
| 495 | + KUNIT_CASE(test_iio_gts_find_gain_for_scale_using_time), | |
| 496 | + KUNIT_CASE(test_iio_gts_find_new_gain_sel_by_old_gain_time), | |
| 497 | + KUNIT_CASE(test_iio_find_closest_gain_low), | |
| 498 | + KUNIT_CASE(test_iio_gts_total_gain_to_scale), | |
| 499 | + KUNIT_CASE(test_iio_gts_avail_test), | |
| 500 | + {} | |
| 501 | +}; | |
| 502 | + | |
| 503 | +static struct kunit_suite iio_gts_test_suite = { | |
| 504 | + .name = "iio-gain-time-scale", | |
| 505 | + .test_cases = iio_gts_test_cases, | |
| 506 | +}; | |
| 507 | + | |
| 508 | +kunit_test_suite(iio_gts_test_suite); | |
| 509 | + | |
| 510 | +MODULE_LICENSE("GPL"); | |
| 511 | +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); | |
| 512 | +MODULE_DESCRIPTION("Test IIO light sensor gain-time-scale helpers"); | |
| 513 | +MODULE_IMPORT_NS(IIO_GTS_HELPER); | |
| @@ -343,7 +343,7 @@ EXPORT_SYMBOL_GPL(icc_std_aggregate); · 342 unmodified lines | ||
| 343 | 343 | * an array of icc nodes specified in the icc_onecell_data struct when |
| 344 | 344 | * registering the provider. |
| 345 | 345 | */ |
| 346 | −struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec, | |
| 346 | +struct icc_node *of_icc_xlate_onecell(const struct of_phandle_args *spec, | |
| 347 | 347 | void *data) |
| 348 | 348 | { |
| 349 | 349 | struct icc_onecell_data *icc_data = data; |
| @@ -368,7 +368,7 @@ EXPORT_SYMBOL_GPL(of_icc_xlate_onecell); · 18 unmodified lines | ||
| 368 | 368 | * Returns a valid pointer to struct icc_node_data on success or ERR_PTR() |
| 369 | 369 | * on failure. |
| 370 | 370 | */ |
| 371 | −struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec) | |
| 371 | +struct icc_node_data *of_icc_get_from_provider(const struct of_phandle_args *spec) | |
| 372 | 372 | { |
| 373 | 373 | struct icc_node *node = ERR_PTR(-EPROBE_DEFER); |
| 374 | 374 | struct icc_node_data *data = NULL; |
| @@ -8,6 +8,15 @@ config INTERCONNECT_QCOM · 7 unmodified lines | ||
| 8 | 8 | config INTERCONNECT_QCOM_BCM_VOTER |
| 9 | 9 | tristate |
| 10 | 10 | |
| 11 | +config INTERCONNECT_QCOM_MSM8909 | |
| 12 | + tristate "Qualcomm MSM8909 interconnect driver" | |
| 13 | + depends on INTERCONNECT_QCOM | |
| 14 | + depends on QCOM_SMD_RPM | |
| 15 | + select INTERCONNECT_QCOM_SMD_RPM | |
| 16 | + help | |
| 17 | + This is a driver for the Qualcomm Network-on-Chip on msm8909-based | |
| 18 | + platforms. | |
| 19 | + | |
| 11 | 20 | config INTERCONNECT_QCOM_MSM8916 |
| 12 | 21 | tristate "Qualcomm MSM8916 interconnect driver" |
| 13 | 22 | depends on INTERCONNECT_QCOM |
| @@ -209,6 +218,15 @@ config INTERCONNECT_QCOM_SM6350 · 195 unmodified lines | ||
| 209 | 218 | This is a driver for the Qualcomm Network-on-Chip on sm6350-based |
| 210 | 219 | platforms. |
| 211 | 220 | |
| 221 | +config INTERCONNECT_QCOM_SM7150 | |
| 222 | + tristate "Qualcomm SM7150 interconnect driver" | |
| 223 | + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE | |
| 224 | + select INTERCONNECT_QCOM_RPMH | |
| 225 | + select INTERCONNECT_QCOM_BCM_VOTER | |
| 226 | + help | |
| 227 | + This is a driver for the Qualcomm Network-on-Chip on sm7150-based | |
| 228 | + platforms. | |
| 229 | + | |
| 212 | 230 | config INTERCONNECT_QCOM_SM8150 |
| 213 | 231 | tristate "Qualcomm SM8150 interconnect driver" |
| 214 | 232 | depends on INTERCONNECT_QCOM_RPMH_POSSIBLE |
| @@ -4,6 +4,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM) += interconnect_qcom.o · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | interconnect_qcom-y := icc-common.o |
| 6 | 6 | icc-bcm-voter-objs := bcm-voter.o |
| 7 | +qnoc-msm8909-objs := msm8909.o | |
| 7 | 8 | qnoc-msm8916-objs := msm8916.o |
| 8 | 9 | qnoc-msm8939-objs := msm8939.o |
| 9 | 10 | qnoc-msm8974-objs := msm8974.o |
| @@ -26,6 +27,7 @@ qnoc-sdx65-objs := sdx65.o · 16 unmodified lines | ||
| 26 | 27 | qnoc-sdx75-objs := sdx75.o |
| 27 | 28 | qnoc-sm6115-objs := sm6115.o |
| 28 | 29 | qnoc-sm6350-objs := sm6350.o |
| 30 | +qnoc-sm7150-objs := sm7150.o | |
| 29 | 31 | qnoc-sm8150-objs := sm8150.o |
| 30 | 32 | qnoc-sm8250-objs := sm8250.o |
| 31 | 33 | qnoc-sm8350-objs := sm8350.o |
| @@ -36,6 +38,7 @@ qnoc-x1e80100-objs := x1e80100.o · 4 unmodified lines | ||
| 36 | 38 | icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o |
| 37 | 39 | |
| 38 | 40 | obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o |
| 41 | +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o | |
| 39 | 42 | obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o |
| 40 | 43 | obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) += qnoc-msm8939.o |
| 41 | 44 | obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o |
| @@ -58,6 +61,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SDX65) += qnoc-sdx65.o · 16 unmodified lines | ||
| 58 | 61 | obj-$(CONFIG_INTERCONNECT_QCOM_SDX75) += qnoc-sdx75.o |
| 59 | 62 | obj-$(CONFIG_INTERCONNECT_QCOM_SM6115) += qnoc-sm6115.o |
| 60 | 63 | obj-$(CONFIG_INTERCONNECT_QCOM_SM6350) += qnoc-sm6350.o |
| 64 | +obj-$(CONFIG_INTERCONNECT_QCOM_SM7150) += qnoc-sm7150.o | |
| 61 | 65 | obj-$(CONFIG_INTERCONNECT_QCOM_SM8150) += qnoc-sm8150.o |
| 62 | 66 | obj-$(CONFIG_INTERCONNECT_QCOM_SM8250) += qnoc-sm8250.o |
| 63 | 67 | obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o |
| @@ -9,7 +9,8 @@ · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | #include "icc-common.h" |
| 11 | 11 | |
| 12 | −struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data) | |
| 12 | +struct icc_node_data *qcom_icc_xlate_extended(const struct of_phandle_args *spec, | |
| 13 | + void *data) | |
| 13 | 14 | { |
| 14 | 15 | struct icc_node_data *ndata; |
| 15 | 16 | struct icc_node *node; |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | #include <linux/interconnect-provider.h> |
| 10 | 10 | |
| 11 | −struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data); | |
| 11 | +struct icc_node_data *qcom_icc_xlate_extended(const struct of_phandle_args *spec, | |
| 12 | + void *data); | |
| 12 | 13 | |
| 13 | 14 | #endif |
| @@ -0,0 +1,1329 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* | |
| 3 | + * Based on data from msm8909-bus.dtsi in Qualcomm's msm-3.18 release: | |
| 4 | + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. | |
| 5 | + */ | |
| 6 | + | |
| 7 | +#include <linux/device.h> | |
| 8 | +#include <linux/interconnect-provider.h> | |
| 9 | +#include <linux/mod_devicetable.h> | |
| 10 | +#include <linux/module.h> | |
| 11 | +#include <linux/platform_device.h> | |
| 12 | +#include <linux/regmap.h> | |
| 13 | + | |
| 14 | +#include <dt-bindings/interconnect/qcom,msm8909.h> | |
| 15 | + | |
| 16 | +#include "icc-rpm.h" | |
| 17 | + | |
| 18 | +enum { | |
| 19 | + QNOC_MASTER_AMPSS_M0 = 1, | |
| 20 | + QNOC_MASTER_GRAPHICS_3D, | |
| 21 | + QNOC_SNOC_BIMC_0_MAS, | |
| 22 | + QNOC_SNOC_BIMC_1_MAS, | |
| 23 | + QNOC_MASTER_TCU_0, | |
| 24 | + QNOC_MASTER_TCU_1, | |
| 25 | + QNOC_MASTER_AUDIO, | |
| 26 | + QNOC_MASTER_SPDM, | |
| 27 | + QNOC_MASTER_DEHR, | |
| 28 | + QNOC_MASTER_QPIC, | |
| 29 | + QNOC_MASTER_BLSP_1, | |
| 30 | + QNOC_MASTER_USB_HS, | |
| 31 | + QNOC_MASTER_CRYPTO_CORE0, | |
| 32 | + QNOC_MASTER_SDCC_1, | |
| 33 | + QNOC_MASTER_SDCC_2, | |
| 34 | + QNOC_SNOC_PNOC_MAS, | |
| 35 | + QNOC_MASTER_QDSS_BAM, | |
| 36 | + QNOC_BIMC_SNOC_MAS, | |
| 37 | + QNOC_MASTER_MDP_PORT0, | |
| 38 | + QNOC_PNOC_SNOC_MAS, | |
| 39 | + QNOC_MASTER_VIDEO_P0, | |
| 40 | + QNOC_MASTER_VFE, | |
| 41 | + QNOC_MASTER_QDSS_ETR, | |
| 42 | + QNOC_PNOC_M_0, | |
| 43 | + QNOC_PNOC_M_1, | |
| 44 | + QNOC_PNOC_INT_0, | |
| 45 | + QNOC_PNOC_INT_1, | |
| 46 | + QNOC_PNOC_SLV_0, | |
| 47 | + QNOC_PNOC_SLV_1, | |
| 48 | + QNOC_PNOC_SLV_2, | |
| 49 | + QNOC_PNOC_SLV_3, | |
| 50 | + QNOC_PNOC_SLV_4, | |
| 51 | + QNOC_PNOC_SLV_5, | |
| 52 | + QNOC_PNOC_SLV_7, | |
| 53 | + QNOC_SNOC_MM_INT_0, | |
| 54 | + QNOC_SNOC_MM_INT_1, | |
| 55 | + QNOC_SNOC_MM_INT_2, | |
| 56 | + QNOC_SNOC_MM_INT_BIMC, | |
| 57 | + QNOC_SNOC_QDSS_INT, | |
| 58 | + QNOC_SNOC_INT_0, | |
| 59 | + QNOC_SNOC_INT_1, | |
| 60 | + QNOC_SNOC_INT_BIMC, | |
| 61 | + QNOC_SLAVE_EBI_CH0, | |
| 62 | + QNOC_BIMC_SNOC_SLV, | |
| 63 | + QNOC_SLAVE_TCSR, | |
| 64 | + QNOC_SLAVE_SDCC_1, | |
| 65 | + QNOC_SLAVE_BLSP_1, | |
| 66 | + QNOC_SLAVE_CRYPTO_0_CFG, | |
| 67 | + QNOC_SLAVE_MESSAGE_RAM, | |
| 68 | + QNOC_SLAVE_PDM, | |
| 69 | + QNOC_SLAVE_PRNG, | |
| 70 | + QNOC_SLAVE_USB_HS, | |
| 71 | + QNOC_SLAVE_QPIC, | |
| 72 | + QNOC_SLAVE_SPDM, | |
| 73 | + QNOC_SLAVE_SDCC_2, | |
| 74 | + QNOC_SLAVE_AUDIO, | |
| 75 | + QNOC_SLAVE_DEHR_CFG, | |
| 76 | + QNOC_SLAVE_SNOC_CFG, | |
| 77 | + QNOC_SLAVE_QDSS_CFG, | |
| 78 | + QNOC_SLAVE_USB_PHYS_CFG, | |
| 79 | + QNOC_SLAVE_CAMERA_CFG, | |
1250 more lines aren’t shown here.
This diff is large: its 212 lines aren’t shown here.
This diff is large: its 54 lines aren’t shown here.
This diff is large: its 1755 lines aren’t shown here.
This diff is large: its 141 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 635 lines aren’t shown here.
This diff is large: its 290 lines aren’t shown here.
This diff is large: its 450 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 35 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 102 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.
This diff is large: its 252 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 151 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 110 lines aren’t shown here.
This diff is large: its 196 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 53 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 73 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 47 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.