Pull staging/IIO updates from Greg KH: "Here is the big staging and IIO driver update for 4.18-rc1. It was delayed as I wanted to make sure the final driver deletions did not cause any major merge issues, and all now looks good. There are a lot of patches here, just over 1000. The diffstat summary shows the major changes here: 1007 files changed, 16828 insertions(+), 227770 deletions(-) Because of this, we might be close to shrinking the overall kernel source code size for two releases in a row. There was loads of work in this release cycle, primarily: - tons of ks7010 driver cleanups - lots of mt7621 driver fixes and cleanups - most driver cleanups - wilc1000 fixes and cleanups - lots and lots of IIO driver cleanups and new additions - debugfs cleanups for all staging drivers - lots of other staging driver cleanups and fixes, the shortlog has the full details. but the big user-visable things here are the removal of 3 chunks of code: - ncpfs and ipx were removed on schedule, no one has cared about this code since it moved to staging last year, and if it needs to come back, it can be reverted. - lustre file system is removed. I've ranted at the lustre developers about once a year for the past 5 years, with no real forward progress at all to clean things up and get the code into the "real" part of the kernel. Given that the lustre developers continue to work on an external tree and try to port those changes to the in-kernel tree every once in a while, this whole thing really really is not working out at all. So I'm deleting it so that the developers can spend the time working in their out-of-tree location and get things cleaned up properly to get merged into the tree correctly at a later date. Because of these file removals, you will have merge issues on some of these files (2 in the ipx code, 1 in the ncpfs code, and 1 in the atomisp driver). Just delete those files, it's a simple merge :) All of this has been in linux-next for a while with no reported problems" * tag 'staging-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1011 commits) staging: ipx: delete it from the tree ncpfs: remove uapi .h files ncpfs: remove Documentation ncpfs: remove compat functionality staging: ncpfs: delete it staging: lustre: delete the filesystem from the tree. staging: vc04_services: no need to save the log debufs dentries staging: vc04_services: vchiq_debugfs_log_entry can be a void * staging: vc04_services: remove struct vchiq_debugfs_info staging: vc04_services: move client dbg directory into static variable staging: vc04_services: remove odd vchiq_debugfs_top() wrapper staging: vc04_services: no need to check debugfs return values staging: mt7621-gpio: reorder includes alphabetically staging: mt7621-gpio: change gc_map to don't use pointers staging: mt7621-gpio: use GPIOF_DIR_OUT and GPIOF_DIR_IN macros instead of custom values staging: mt7621-gpio: change 'to_mediatek_gpio' to make just a one line return staging: mt7621-gpio: dt-bindings: update documentation for #interrupt-cells property staging: mt7621-gpio: update #interrupt-cells for the gpio node staging: mt7621-gpio: dt-bindings: complete documentation for the gpio staging: mt7621-dts: add missing properties to gpio node ...
Unified · Split
| @@ -190,6 +190,13 @@ Description: · 189 unmodified lines | |||
| 190 | but should match other such assignments on device). | 190 | but should match other such assignments on device). |
| 191 | Units after application of scale and offset are m/s^2. | 191 | Units after application of scale and offset are m/s^2. |
| 192 | 192 | ||
| 193 | What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw | ||
| 194 | KernelVersion: 4.17 | ||
| 195 | Contact: linux-iio@vger.kernel.org | ||
| 196 | Description: | ||
| 197 | Angle of rotation. Units after application of scale and offset | ||
| 198 | are radians. | ||
| 199 | |||
| 193 | What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_raw | 200 | What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_raw |
| 194 | What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw | 201 | What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw |
| 195 | What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_raw | 202 | What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_raw |
| @@ -297,6 +304,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset · 101 unmodified lines | |||
| 297 | What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_offset | 304 | What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_offset |
| 298 | What: /sys/bus/iio/devices/iio:deviceX/in_magn_offset | 305 | What: /sys/bus/iio/devices/iio:deviceX/in_magn_offset |
| 299 | What: /sys/bus/iio/devices/iio:deviceX/in_rot_offset | 306 | What: /sys/bus/iio/devices/iio:deviceX/in_rot_offset |
| 307 | What: /sys/bus/iio/devices/iio:deviceX/in_angl_offset | ||
| 300 | KernelVersion: 2.6.35 | 308 | KernelVersion: 2.6.35 |
| 301 | Contact: linux-iio@vger.kernel.org | 309 | Contact: linux-iio@vger.kernel.org |
| 302 | Description: | 310 | Description: |
| @@ -350,6 +358,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale · 47 unmodified lines | |||
| 350 | What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale | 358 | What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale |
| 351 | What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale | 359 | What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale |
| 352 | What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale | 360 | What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale |
| 361 | What: /sys/bus/iio/devices/iio:deviceX/in_angl_scale | ||
| 353 | KernelVersion: 2.6.35 | 362 | KernelVersion: 2.6.35 |
| 354 | Contact: linux-iio@vger.kernel.org | 363 | Contact: linux-iio@vger.kernel.org |
| 355 | Description: | 364 | Description: |
| @@ -7,6 +7,7 @@ Required properties: · 6 unmodified lines | |||
| 7 | - "amlogic,meson-gxbb-saradc" for GXBB | 7 | - "amlogic,meson-gxbb-saradc" for GXBB |
| 8 | - "amlogic,meson-gxl-saradc" for GXL | 8 | - "amlogic,meson-gxl-saradc" for GXL |
| 9 | - "amlogic,meson-gxm-saradc" for GXM | 9 | - "amlogic,meson-gxm-saradc" for GXM |
| 10 | - "amlogic,meson-axg-saradc" for AXG | ||
| 10 | along with the generic "amlogic,meson-saradc" | 11 | along with the generic "amlogic,meson-saradc" |
| 11 | - reg: the physical base address and length of the registers | 12 | - reg: the physical base address and length of the registers |
| 12 | - interrupts: the interrupt indicating end of sampling | 13 | - interrupts: the interrupt indicating end of sampling |
| @@ -49,7 +49,7 @@ Required properties: · 48 unmodified lines | |||
| 49 | Examples: | 49 | Examples: |
| 50 | spi_controller { | 50 | spi_controller { |
| 51 | mcp3x0x@0 { | 51 | mcp3x0x@0 { |
| 52 | compatible = "mcp3002"; | 52 | compatible = "microchip,mcp3002"; |
| 53 | reg = <0>; | 53 | reg = <0>; |
| 54 | spi-max-frequency = <1000000>; | 54 | spi-max-frequency = <1000000>; |
| 55 | vref-supply = <&vref_reg>; | 55 | vref-supply = <&vref_reg>; |
| @@ -24,8 +24,11 @@ Required properties: · 23 unmodified lines | |||
| 24 | - compatible: Should be one of: | 24 | - compatible: Should be one of: |
| 25 | "st,stm32f4-adc-core" | 25 | "st,stm32f4-adc-core" |
| 26 | "st,stm32h7-adc-core" | 26 | "st,stm32h7-adc-core" |
| 27 | "st,stm32mp1-adc-core" | ||
| 27 | - reg: Offset and length of the ADC block register set. | 28 | - reg: Offset and length of the ADC block register set. |
| 28 | - interrupts: Must contain the interrupt for ADC block. | 29 | - interrupts: One or more interrupts for ADC block. Some parts like stm32f4 |
| 30 | and stm32h7 share a common ADC interrupt line. stm32mp1 has two separate | ||
| 31 | interrupt lines, one for each ADC within ADC block. | ||
| 29 | - clocks: Core can use up to two clocks, depending on part used: | 32 | - clocks: Core can use up to two clocks, depending on part used: |
| 30 | - "adc" clock: for the analog circuitry, common to all ADCs. | 33 | - "adc" clock: for the analog circuitry, common to all ADCs. |
| 31 | It's required on stm32f4. | 34 | It's required on stm32f4. |
| @@ -53,6 +56,7 @@ Required properties: · 21 unmodified lines | |||
| 53 | - compatible: Should be one of: | 56 | - compatible: Should be one of: |
| 54 | "st,stm32f4-adc" | 57 | "st,stm32f4-adc" |
| 55 | "st,stm32h7-adc" | 58 | "st,stm32h7-adc" |
| 59 | "st,stm32mp1-adc" | ||
| 56 | - reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200). | 60 | - reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200). |
| 57 | - clocks: Input clock private to this ADC instance. It's required only on | 61 | - clocks: Input clock private to this ADC instance. It's required only on |
| 58 | stm32f4, that has per instance clock input for registers access. | 62 | stm32f4, that has per instance clock input for registers access. |
| @@ -8,14 +8,16 @@ It is mainly targeted for: · 7 unmodified lines | |||
| 8 | - PDM microphones (audio digital microphone) | 8 | - PDM microphones (audio digital microphone) |
| 9 | 9 | ||
| 10 | It features up to 8 serial digital interfaces (SPI or Manchester) and | 10 | It features up to 8 serial digital interfaces (SPI or Manchester) and |
| 11 | up to 4 filters on stm32h7. | 11 | up to 4 filters on stm32h7 or 6 filters on stm32mp1. |
| 12 | 12 | ||
| 13 | Each child node match with a filter instance. | 13 | Each child node match with a filter instance. |
| 14 | 14 | ||
| 15 | Contents of a STM32 DFSDM root node: | 15 | Contents of a STM32 DFSDM root node: |
| 16 | ------------------------------------ | 16 | ------------------------------------ |
| 17 | Required properties: | 17 | Required properties: |
| 18 | - compatible: Should be "st,stm32h7-dfsdm". | 18 | - compatible: Should be one of: |
| 19 | "st,stm32h7-dfsdm" | ||
| 20 | "st,stm32mp1-dfsdm" | ||
| 19 | - reg: Offset and length of the DFSDM block register set. | 21 | - reg: Offset and length of the DFSDM block register set. |
| 20 | - clocks: IP and serial interfaces clocking. Should be set according | 22 | - clocks: IP and serial interfaces clocking. Should be set according |
| 21 | to rcc clock ID and "clock-names". | 23 | to rcc clock ID and "clock-names". |
| @@ -45,6 +47,7 @@ Required properties: · 23 unmodified lines | |||
| 45 | "st,stm32-dfsdm-adc" for sigma delta ADCs | 47 | "st,stm32-dfsdm-adc" for sigma delta ADCs |
| 46 | "st,stm32-dfsdm-dmic" for audio digital microphone. | 48 | "st,stm32-dfsdm-dmic" for audio digital microphone. |
| 47 | - reg: Specifies the DFSDM filter instance used. | 49 | - reg: Specifies the DFSDM filter instance used. |
| 50 | Valid values are from 0 to 3 on stm32h7, 0 to 5 on stm32mp1. | ||
| 48 | - interrupts: IRQ lines connected to each DFSDM filter instance. | 51 | - interrupts: IRQ lines connected to each DFSDM filter instance. |
| 49 | - st,adc-channels: List of single-ended channels muxed for this ADC. | 52 | - st,adc-channels: List of single-ended channels muxed for this ADC. |
| 50 | valid values: | 53 | valid values: |
| @@ -0,0 +1,26 @@ | |||
| 1 | Current Sense Amplifier | ||
| 2 | ======================= | ||
| 3 | |||
| 4 | When an io-channel measures the output voltage from a current sense | ||
| 5 | amplifier, the interesting measurement is almost always the current | ||
| 6 | through the sense resistor, not the voltage output. This binding | ||
| 7 | describes such a current sense circuit. | ||
| 8 | |||
| 9 | Required properties: | ||
| 10 | - compatible : "current-sense-amplifier" | ||
| 11 | - io-channels : Channel node of a voltage io-channel. | ||
| 12 | - sense-resistor-micro-ohms : The sense resistance in microohms. | ||
| 13 | |||
| 14 | Optional properties: | ||
| 15 | - sense-gain-mult: Amplifier gain multiplier. The default is <1>. | ||
| 16 | - sense-gain-div: Amplifier gain divider. The default is <1>. | ||
| 17 | |||
| 18 | Example: | ||
| 19 | |||
| 20 | sysi { | ||
| 21 | compatible = "current-sense-amplifier"; | ||
| 22 | io-channels = <&tiadc 0>; | ||
| 23 | |||
| 24 | sense-resistor-micro-ohms = <20000>; | ||
| 25 | sense-gain-mul = <50>; | ||
| 26 | }; | ||
| @@ -0,0 +1,41 @@ | |||
| 1 | Current Sense Shunt | ||
| 2 | =================== | ||
| 3 | |||
| 4 | When an io-channel measures the voltage over a current sense shunt, | ||
| 5 | the interesting measurement is almost always the current through the | ||
| 6 | shunt, not the voltage over it. This binding describes such a current | ||
| 7 | sense circuit. | ||
| 8 | |||
| 9 | Required properties: | ||
| 10 | - compatible : "current-sense-shunt" | ||
| 11 | - io-channels : Channel node of a voltage io-channel. | ||
| 12 | - shunt-resistor-micro-ohms : The shunt resistance in microohms. | ||
| 13 | |||
| 14 | Example: | ||
| 15 | The system current is measured by measuring the voltage over a | ||
| 16 | 3.3 ohms shunt resistor. | ||
| 17 | |||
| 18 | sysi { | ||
| 19 | compatible = "current-sense-shunt"; | ||
| 20 | io-channels = <&tiadc 0>; | ||
| 21 | |||
| 22 | /* Divide the voltage by 3300000/1000000 (or 3.3) for the current. */ | ||
| 23 | shunt-resistor-micro-ohms = <3300000>; | ||
| 24 | }; | ||
| 25 | |||
| 26 | &i2c { | ||
| 27 | tiadc: adc@48 { | ||
| 28 | compatible = "ti,ads1015"; | ||
| 29 | reg = <0x48>; | ||
| 30 | #io-channel-cells = <1>; | ||
| 31 | |||
| 32 | #address-cells = <1>; | ||
| 33 | #size-cells = <0>; | ||
| 34 | |||
| 35 | channel@0 { /* IN0,IN1 differential */ | ||
| 36 | reg = <0>; | ||
| 37 | ti,gain = <1>; | ||
| 38 | ti,datarate = <4>; | ||
| 39 | }; | ||
| 40 | }; | ||
| 41 | }; | ||
| @@ -0,0 +1,53 @@ | |||
| 1 | Voltage divider | ||
| 2 | =============== | ||
| 3 | |||
| 4 | When an io-channel measures the midpoint of a voltage divider, the | ||
| 5 | interesting voltage is often the voltage over the full resistance | ||
| 6 | of the divider. This binding describes the voltage divider in such | ||
| 7 | a curcuit. | ||
| 8 | |||
| 9 | Vin ----. | ||
| 10 | | | ||
| 11 | .-----. | ||
| 12 | | R | | ||
| 13 | '-----' | ||
| 14 | | | ||
| 15 | +---- Vout | ||
| 16 | | | ||
| 17 | .-----. | ||
| 18 | | Rout| | ||
| 19 | '-----' | ||
| 20 | | | ||
| 21 | GND | ||
| 22 | |||
| 23 | Required properties: | ||
| 24 | - compatible : "voltage-divider" | ||
| 25 | - io-channels : Channel node of a voltage io-channel measuring Vout. | ||
| 26 | - output-ohms : Resistance Rout over which the output voltage is measured. | ||
| 27 | See full-ohms. | ||
| 28 | - full-ohms : Resistance R + Rout for the full divider. The io-channel | ||
| 29 | is scaled by the Rout / (R + Rout) quotient. | ||
| 30 | |||
| 31 | Example: | ||
| 32 | The system voltage is circa 12V, but divided down with a 22/222 | ||
| 33 | voltage divider (R = 200 Ohms, Rout = 22 Ohms) and fed to an ADC. | ||
| 34 | |||
| 35 | sysv { | ||
| 36 | compatible = "voltage-divider"; | ||
| 37 | io-channels = <&maxadc 1>; | ||
| 38 | |||
| 39 | /* Scale the system voltage by 22/222 to fit the ADC range. */ | ||
| 40 | output-ohms = <22>; | ||
| 41 | full-ohms = <222>; /* 200 + 22 */ | ||
| 42 | }; | ||
| 43 | |||
| 44 | &spi { | ||
| 45 | maxadc: adc@0 { | ||
| 46 | compatible = "maxim,max1027"; | ||
| 47 | reg = <0>; | ||
| 48 | #io-channel-cells = <1>; | ||
| 49 | interrupt-parent = <&gpio5>; | ||
| 50 | interrupts = <15 IRQ_TYPE_EDGE_RISING>; | ||
| 51 | spi-max-frequency = <1000000>; | ||
| 52 | }; | ||
| 53 | }; | ||
| @@ -12,12 +12,26 @@ Required properties: · 11 unmodified lines | |||
| 12 | Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt | 12 | Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt |
| 13 | apply. In particular, "reg" and "spi-max-frequency" properties must be given. | 13 | apply. In particular, "reg" and "spi-max-frequency" properties must be given. |
| 14 | 14 | ||
| 15 | Optional properties: | ||
| 16 | - vref-supply: Phandle to the external reference voltage supply. This should | ||
| 17 | only be set if there is an external reference voltage connected to the VREF | ||
| 18 | pin. If the property is not set the internal reference is used. | ||
| 19 | |||
| 15 | Example: | 20 | Example: |
| 16 | 21 | ||
| 22 | vref: regulator-vref { | ||
| 23 | compatible = "regulator-fixed"; | ||
| 24 | regulator-name = "vref-ltc2632"; | ||
| 25 | regulator-min-microvolt = <1250000>; | ||
| 26 | regulator-max-microvolt = <1250000>; | ||
| 27 | regulator-always-on; | ||
| 28 | }; | ||
| 29 | |||
| 17 | spi_master { | 30 | spi_master { |
| 18 | dac: ltc2632@0 { | 31 | dac: ltc2632@0 { |
| 19 | compatible = "lltc,ltc2632-l12"; | 32 | compatible = "lltc,ltc2632-l12"; |
| 20 | reg = <0>; /* CS0 */ | 33 | reg = <0>; /* CS0 */ |
| 21 | spi-max-frequency = <1000000>; | 34 | spi-max-frequency = <1000000>; |
| 35 | vref-supply = <&vref>; /* optional */ | ||
| 22 | }; | 36 | }; |
| 23 | }; | 37 | }; |
| @@ -0,0 +1,24 @@ | |||
| 1 | * Texas Instruments DAC5571 Family | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: Should contain | ||
| 5 | "ti,dac5571" | ||
| 6 | "ti,dac6571" | ||
| 7 | "ti,dac7571" | ||
| 8 | "ti,dac5574" | ||
| 9 | "ti,dac6574" | ||
| 10 | "ti,dac7574" | ||
| 11 | "ti,dac5573" | ||
| 12 | "ti,dac6573" | ||
| 13 | "ti,dac7573" | ||
| 14 | - reg: Should contain the DAC I2C address | ||
| 15 | |||
| 16 | Optional properties: | ||
| 17 | - vref-supply: The regulator supply for DAC reference voltage | ||
| 18 | |||
| 19 | Example: | ||
| 20 | dac@0 { | ||
| 21 | compatible = "ti,dac5571"; | ||
| 22 | reg = <0x4C>; | ||
| 23 | vref-supply = <&vdd_supply>; | ||
| 24 | }; | ||
| @@ -8,10 +8,16 @@ Required properties: · 7 unmodified lines | |||
| 8 | "invensense,mpu6500" | 8 | "invensense,mpu6500" |
| 9 | "invensense,mpu9150" | 9 | "invensense,mpu9150" |
| 10 | "invensense,mpu9250" | 10 | "invensense,mpu9250" |
| 11 | "invensense,mpu9255" | ||
| 11 | "invensense,icm20608" | 12 | "invensense,icm20608" |
| 12 | - reg : the I2C address of the sensor | 13 | - reg : the I2C address of the sensor |
| 13 | - interrupt-parent : should be the phandle for the interrupt controller | 14 | - interrupt-parent : should be the phandle for the interrupt controller |
| 14 | - interrupts : interrupt mapping for GPIO IRQ | 15 | - interrupts: interrupt mapping for IRQ. It should be configured with flags |
| 16 | IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or | ||
| 17 | IRQ_TYPE_EDGE_FALLING. | ||
| 18 | |||
| 19 | Refer to interrupt-controller/interrupts.txt for generic interrupt client node | ||
| 20 | bindings. | ||
| 15 | 21 | ||
| 16 | Optional properties: | 22 | Optional properties: |
| 17 | - mount-matrix: an optional 3x3 mounting rotation matrix | 23 | - mount-matrix: an optional 3x3 mounting rotation matrix |
| @@ -24,7 +30,7 @@ Example: · 6 unmodified lines | |||
| 24 | compatible = "invensense,mpu6050"; | 30 | compatible = "invensense,mpu6050"; |
| 25 | reg = <0x68>; | 31 | reg = <0x68>; |
| 26 | interrupt-parent = <&gpio1>; | 32 | interrupt-parent = <&gpio1>; |
| 27 | interrupts = <18 1>; | 33 | interrupts = <18 IRQ_TYPE_EDGE_RISING>; |
| 28 | mount-matrix = "-0.984807753012208", /* x0 */ | 34 | mount-matrix = "-0.984807753012208", /* x0 */ |
| 29 | "0", /* y0 */ | 35 | "0", /* y0 */ |
| 30 | "-0.173648177666930", /* z0 */ | 36 | "-0.173648177666930", /* z0 */ |
| @@ -41,7 +47,7 @@ Example: · 10 unmodified lines | |||
| 41 | compatible = "invensense,mpu9250"; | 47 | compatible = "invensense,mpu9250"; |
| 42 | reg = <0x68>; | 48 | reg = <0x68>; |
| 43 | interrupt-parent = <&gpio3>; | 49 | interrupt-parent = <&gpio3>; |
| 44 | interrupts = <21 1>; | 50 | interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; |
| 45 | i2c-gate { | 51 | i2c-gate { |
| 46 | #address-cells = <1>; | 52 | #address-cells = <1>; |
| 47 | #size-cells = <0>; | 53 | #size-cells = <0>; |
| @@ -6,6 +6,7 @@ Required properties: · 5 unmodified lines | |||
| 6 | "st,lsm6ds3h" | 6 | "st,lsm6ds3h" |
| 7 | "st,lsm6dsl" | 7 | "st,lsm6dsl" |
| 8 | "st,lsm6dsm" | 8 | "st,lsm6dsm" |
| 9 | "st,ism330dlc" | ||
| 9 | - reg: i2c address of the sensor / spi cs line | 10 | - reg: i2c address of the sensor / spi cs line |
| 10 | 11 | ||
| 11 | Optional properties: | 12 | Optional properties: |
| @@ -1,10 +1,13 @@ | |||
| 1 | * Texas Instruments LMP91000 potentiostat | 1 | * Texas Instruments LMP91000 series of potentiostats |
| 2 | 2 | ||
| 3 | http://www.ti.com/lit/ds/symlink/lmp91000.pdf | 3 | LMP91000: http://www.ti.com/lit/ds/symlink/lmp91000.pdf |
| 4 | LMP91002: http://www.ti.com/lit/ds/symlink/lmp91002.pdf | ||
| 4 | 5 | ||
| 5 | Required properties: | 6 | Required properties: |
| 6 | 7 | ||
| 7 | - compatible: should be "ti,lmp91000" | 8 | - compatible: should be one of the following: |
| 9 | "ti,lmp91000" | ||
| 10 | "ti,lmp91002" | ||
| 8 | - reg: the I2C address of the device | 11 | - reg: the I2C address of the device |
| 9 | - io-channels: the phandle of the iio provider | 12 | - io-channels: the phandle of the iio provider |
| 10 | 13 | ||
| @@ -89,8 +89,6 @@ locks.txt · 88 unmodified lines | |||
| 89 | - info on file locking implementations, flock() vs. fcntl(), etc. | 89 | - info on file locking implementations, flock() vs. fcntl(), etc. |
| 90 | mandatory-locking.txt | 90 | mandatory-locking.txt |
| 91 | - info on the Linux implementation of Sys V mandatory file locking. | 91 | - info on the Linux implementation of Sys V mandatory file locking. |
| 92 | ncpfs.txt | ||
| 93 | - info on Novell Netware(tm) filesystem using NCP protocol. | ||
| 94 | nfs/ | 92 | nfs/ |
| 95 | - nfs-related documentation. | 93 | - nfs-related documentation. |
| 96 | nilfs2.txt | 94 | nilfs2.txt |
| @@ -1,12 +0,0 @@ | |||
| 1 | The ncpfs filesystem understands the NCP protocol, designed by the | ||
| 2 | Novell Corporation for their NetWare(tm) product. NCP is functionally | ||
| 3 | similar to the NFS used in the TCP/IP community. | ||
| 4 | To mount a NetWare filesystem, you need a special mount program, which | ||
| 5 | can be found in the ncpfs package. The home site for ncpfs is | ||
| 6 | ftp.gwdg.de/pub/linux/misc/ncpfs, but sunsite and its many mirrors | ||
| 7 | will have it as well. | ||
| 8 | |||
| 9 | Related products are linware and mars_nwe, which will give Linux partial | ||
| 10 | NetWare server functionality. | ||
| 11 | |||
| 12 | mars_nwe can be found on ftp.gwdg.de/pub/linux/misc/ncpfs. | ||
| @@ -796,6 +796,14 @@ M: Michael Hanselmann <linux-kernel@hansmi.ch> · 795 unmodified lines | |||
| 796 | S: Supported | 796 | S: Supported |
| 797 | F: drivers/macintosh/ams/ | 797 | F: drivers/macintosh/ams/ |
| 798 | 798 | ||
| 799 | ANALOG DEVICES INC AD5686 DRIVER | ||
| 800 | M: Stefan Popa <stefan.popa@analog.com> | ||
| 801 | L: linux-pm@vger.kernel.org | ||
| 802 | W: http://ez.analog.com/community/linux-device-drivers | ||
| 803 | S: Supported | ||
| 804 | F: drivers/iio/dac/ad5686* | ||
| 805 | F: drivers/iio/dac/ad5696* | ||
| 806 | |||
| 799 | ANALOG DEVICES INC AD9389B DRIVER | 807 | ANALOG DEVICES INC AD9389B DRIVER |
| 800 | M: Hans Verkuil <hans.verkuil@cisco.com> | 808 | M: Hans Verkuil <hans.verkuil@cisco.com> |
| 801 | L: linux-media@vger.kernel.org | 809 | L: linux-media@vger.kernel.org |
| @@ -4421,6 +4429,12 @@ L: linux-kernel@vger.kernel.org · 3619 unmodified lines | |||
| 4421 | S: Maintained | 4429 | S: Maintained |
| 4422 | F: drivers/staging/fsl-dpaa2/ethsw | 4430 | F: drivers/staging/fsl-dpaa2/ethsw |
| 4423 | 4431 | ||
| 4432 | DPAA2 PTP CLOCK DRIVER | ||
| 4433 | M: Yangbo Lu <yangbo.lu@nxp.com> | ||
| 4434 | L: linux-kernel@vger.kernel.org | ||
| 4435 | S: Maintained | ||
| 4436 | F: drivers/staging/fsl-dpaa2/rtc | ||
| 4437 | |||
| 4424 | DPT_I2O SCSI RAID DRIVER | 4438 | DPT_I2O SCSI RAID DRIVER |
| 4425 | M: Adaptec OEM Raid Solutions <aacraid@microsemi.com> | 4439 | M: Adaptec OEM Raid Solutions <aacraid@microsemi.com> |
| 4426 | L: linux-scsi@vger.kernel.org | 4440 | L: linux-scsi@vger.kernel.org |
| @@ -6968,6 +6982,15 @@ F: drivers/staging/iio/ · 2541 unmodified lines | |||
| 6968 | F: include/linux/iio/ | 6982 | F: include/linux/iio/ |
| 6969 | F: tools/iio/ | 6983 | F: tools/iio/ |
| 6970 | 6984 | ||
| 6985 | IIO UNIT CONVERTER | ||
| 6986 | M: Peter Rosin <peda@axentia.se> | ||
| 6987 | L: linux-iio@vger.kernel.org | ||
| 6988 | S: Maintained | ||
| 6989 | F: Documentation/devicetree/bindings/iio/afe/current-sense-amplifier.txt | ||
| 6990 | F: Documentation/devicetree/bindings/iio/afe/current-sense-shunt.txt | ||
| 6991 | F: Documentation/devicetree/bindings/iio/afe/voltage-divider.txt | ||
| 6992 | F: drivers/iio/afe/iio-rescale.c | ||
| 6993 | |||
| 6971 | IKANOS/ADI EAGLE ADSL USB DRIVER | 6994 | IKANOS/ADI EAGLE ADSL USB DRIVER |
| 6972 | M: Matthieu Castet <castet.matthieu@free.fr> | 6995 | M: Matthieu Castet <castet.matthieu@free.fr> |
| 6973 | M: Stanislaw Gruszka <stf_xl@wp.pl> | 6996 | M: Stanislaw Gruszka <stf_xl@wp.pl> |
| @@ -13433,15 +13456,6 @@ S: Odd Fixes · 6459 unmodified lines | |||
| 13433 | F: Documentation/devicetree/bindings/staging/iio/ | 13456 | F: Documentation/devicetree/bindings/staging/iio/ |
| 13434 | F: drivers/staging/iio/ | 13457 | F: drivers/staging/iio/ |
| 13435 | 13458 | ||
| 13436 | STAGING - LUSTRE PARALLEL FILESYSTEM | ||
| 13437 | M: Oleg Drokin <oleg.drokin@intel.com> | ||
| 13438 | M: Andreas Dilger <andreas.dilger@intel.com> | ||
| 13439 | M: James Simmons <jsimmons@infradead.org> | ||
| 13440 | L: lustre-devel@lists.lustre.org (moderated for non-subscribers) | ||
| 13441 | W: http://wiki.lustre.org/ | ||
| 13442 | S: Maintained | ||
| 13443 | F: drivers/staging/lustre | ||
| 13444 | |||
| 13445 | STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec) | 13459 | STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec) |
| 13446 | M: Marc Dietrich <marvin24@gmx.de> | 13460 | M: Marc Dietrich <marvin24@gmx.de> |
| 13447 | L: ac100@lists.launchpad.net (moderated for non-subscribers) | 13461 | L: ac100@lists.launchpad.net (moderated for non-subscribers) |
| @@ -70,6 +70,7 @@ config IIO_TRIGGERED_EVENT · 69 unmodified lines | |||
| 70 | 70 | ||
| 71 | source "drivers/iio/accel/Kconfig" | 71 | source "drivers/iio/accel/Kconfig" |
| 72 | source "drivers/iio/adc/Kconfig" | 72 | source "drivers/iio/adc/Kconfig" |
| 73 | source "drivers/iio/afe/Kconfig" | ||
| 73 | source "drivers/iio/amplifiers/Kconfig" | 74 | source "drivers/iio/amplifiers/Kconfig" |
| 74 | source "drivers/iio/chemical/Kconfig" | 75 | source "drivers/iio/chemical/Kconfig" |
| 75 | source "drivers/iio/common/Kconfig" | 76 | source "drivers/iio/common/Kconfig" |
| @@ -92,6 +93,7 @@ source "drivers/iio/potentiometer/Kconfig" · 16 unmodified lines | |||
| 92 | source "drivers/iio/potentiostat/Kconfig" | 93 | source "drivers/iio/potentiostat/Kconfig" |
| 93 | source "drivers/iio/pressure/Kconfig" | 94 | source "drivers/iio/pressure/Kconfig" |
| 94 | source "drivers/iio/proximity/Kconfig" | 95 | source "drivers/iio/proximity/Kconfig" |
| 96 | source "drivers/iio/resolver/Kconfig" | ||
| 95 | source "drivers/iio/temperature/Kconfig" | 97 | source "drivers/iio/temperature/Kconfig" |
| 96 | 98 | ||
| 97 | endif # IIO | 99 | endif # IIO |
| @@ -15,6 +15,7 @@ obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o · 14 unmodified lines | |||
| 15 | 15 | ||
| 16 | obj-y += accel/ | 16 | obj-y += accel/ |
| 17 | obj-y += adc/ | 17 | obj-y += adc/ |
| 18 | obj-y += afe/ | ||
| 18 | obj-y += amplifiers/ | 19 | obj-y += amplifiers/ |
| 19 | obj-y += buffer/ | 20 | obj-y += buffer/ |
| 20 | obj-y += chemical/ | 21 | obj-y += chemical/ |
| @@ -35,5 +36,6 @@ obj-y += potentiometer/ · 14 unmodified lines | |||
| 35 | obj-y += potentiostat/ | 36 | obj-y += potentiostat/ |
| 36 | obj-y += pressure/ | 37 | obj-y += pressure/ |
| 37 | obj-y += proximity/ | 38 | obj-y += proximity/ |
| 39 | obj-y += resolver/ | ||
| 38 | obj-y += temperature/ | 40 | obj-y += temperature/ |
| 39 | obj-y += trigger/ | 41 | obj-y += trigger/ |
| @@ -5,6 +5,30 @@ · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | menu "Accelerometers" | 6 | menu "Accelerometers" |
| 7 | 7 | ||
| 8 | config ADIS16201 | ||
| 9 | tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer" | ||
| 10 | depends on SPI | ||
| 11 | select IIO_ADIS_LIB | ||
| 12 | select IIO_ADIS_LIB_BUFFER if IIO_BUFFER | ||
| 13 | help | ||
| 14 | Say Y here to build support for Analog Devices adis16201 dual-axis | ||
| 15 | digital inclinometer and accelerometer. | ||
| 16 | |||
| 17 | To compile this driver as a module, say M here: the module will | ||
| 18 | be called adis16201. | ||
| 19 | |||
| 20 | config ADIS16209 | ||
| 21 | tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer" | ||
| 22 | depends on SPI | ||
| 23 | select IIO_ADIS_LIB | ||
| 24 | select IIO_ADIS_LIB_BUFFER if IIO_BUFFER | ||
| 25 | help | ||
| 26 | Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer | ||
| 27 | and accelerometer. | ||
| 28 | |||
| 29 | To compile this driver as a module, say M here: the module will be | ||
| 30 | called adis16209. | ||
| 31 | |||
| 8 | config ADXL345 | 32 | config ADXL345 |
| 9 | tristate | 33 | tristate |
| 10 | 34 | ||
| @@ -4,6 +4,8 @@ · 3 unmodified lines | |||
| 4 | # | 4 | # |
| 5 | 5 | ||
| 6 | # When adding new entries keep the list in alphabetical order | 6 | # When adding new entries keep the list in alphabetical order |
| 7 | obj-$(CONFIG_ADIS16201) += adis16201.o | ||
| 8 | obj-$(CONFIG_ADIS16209) += adis16209.o | ||
| 7 | obj-$(CONFIG_ADXL345) += adxl345_core.o | 9 | obj-$(CONFIG_ADXL345) += adxl345_core.o |
| 8 | obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o | 10 | obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o |
| 9 | obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o | 11 | obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o |
| @@ -0,0 +1,321 @@ | |||
| 1 | /* | ||
| 2 | * ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer | ||
| 3 | * | ||
| 4 | * Copyright 2010 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2 or later. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/device.h> | ||
| 10 | #include <linux/kernel.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/mutex.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | #include <linux/spi/spi.h> | ||
| 15 | #include <linux/sysfs.h> | ||
| 16 | |||
| 17 | #include <linux/iio/iio.h> | ||
| 18 | #include <linux/iio/imu/adis.h> | ||
| 19 | |||
| 20 | #define ADIS16201_STARTUP_DELAY_MS 220 | ||
| 21 | #define ADIS16201_FLASH_CNT 0x00 | ||
| 22 | |||
| 23 | /* Data Output Register Information */ | ||
| 24 | #define ADIS16201_SUPPLY_OUT_REG 0x02 | ||
| 25 | #define ADIS16201_XACCL_OUT_REG 0x04 | ||
| 26 | #define ADIS16201_YACCL_OUT_REG 0x06 | ||
| 27 | #define ADIS16201_AUX_ADC_REG 0x08 | ||
| 28 | #define ADIS16201_TEMP_OUT_REG 0x0A | ||
| 29 | #define ADIS16201_XINCL_OUT_REG 0x0C | ||
| 30 | #define ADIS16201_YINCL_OUT_REG 0x0E | ||
| 31 | |||
| 32 | /* Calibration Register Definition */ | ||
| 33 | #define ADIS16201_XACCL_OFFS_REG 0x10 | ||
| 34 | #define ADIS16201_YACCL_OFFS_REG 0x12 | ||
| 35 | #define ADIS16201_XACCL_SCALE_REG 0x14 | ||
| 36 | #define ADIS16201_YACCL_SCALE_REG 0x16 | ||
| 37 | #define ADIS16201_XINCL_OFFS_REG 0x18 | ||
| 38 | #define ADIS16201_YINCL_OFFS_REG 0x1A | ||
| 39 | #define ADIS16201_XINCL_SCALE_REG 0x1C | ||
| 40 | #define ADIS16201_YINCL_SCALE_REG 0x1E | ||
| 41 | |||
| 42 | /* Alarm Register Definition */ | ||
| 43 | #define ADIS16201_ALM_MAG1_REG 0x20 | ||
| 44 | #define ADIS16201_ALM_MAG2_REG 0x22 | ||
| 45 | #define ADIS16201_ALM_SMPL1_REG 0x24 | ||
| 46 | #define ADIS16201_ALM_SMPL2_REG 0x26 | ||
| 47 | #define ADIS16201_ALM_CTRL_REG 0x28 | ||
| 48 | |||
| 49 | #define ADIS16201_AUX_DAC_REG 0x30 | ||
| 50 | #define ADIS16201_GPIO_CTRL_REG 0x32 | ||
| 51 | #define ADIS16201_SMPL_PRD_REG 0x36 | ||
| 52 | /* Operation, filter configuration */ | ||
| 53 | #define ADIS16201_AVG_CNT_REG 0x38 | ||
| 54 | #define ADIS16201_SLP_CNT_REG 0x3A | ||
| 55 | |||
| 56 | /* Miscellaneous Control Register Definition */ | ||
| 57 | #define ADIS16201_MSC_CTRL_REG 0x34 | ||
| 58 | #define ADIS16201_MSC_CTRL_SELF_TEST_EN BIT(8) | ||
| 59 | /* Data-ready enable: 1 = enabled, 0 = disabled */ | ||
| 60 | #define ADIS16201_MSC_CTRL_DATA_RDY_EN BIT(2) | ||
| 61 | /* Data-ready polarity: 1 = active high, 0 = active low */ | ||
| 62 | #define ADIS16201_MSC_CTRL_ACTIVE_DATA_RDY_HIGH BIT(1) | ||
| 63 | /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ | ||
| 64 | #define ADIS16201_MSC_CTRL_DATA_RDY_DIO1 BIT(0) | ||
| 65 | |||
| 66 | /* Diagnostics System Status Register Definition */ | ||
| 67 | #define ADIS16201_DIAG_STAT_REG 0x3C | ||
| 68 | #define ADIS16201_DIAG_STAT_ALARM2 BIT(9) | ||
| 69 | #define ADIS16201_DIAG_STAT_ALARM1 BIT(8) | ||
| 70 | #define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 | ||
| 71 | #define ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT 2 | ||
| 72 | /* Power supply above 3.625 V */ | ||
| 73 | #define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 | ||
| 74 | /* Power supply below 3.15 V */ | ||
| 75 | #define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 | ||
| 76 | |||
| 77 | /* System Command Register Definition */ | ||
| 78 | #define ADIS16201_GLOB_CMD_REG 0x3E | ||
| 79 | #define ADIS16201_GLOB_CMD_SW_RESET BIT(7) | ||
| 80 | #define ADIS16201_GLOB_CMD_FACTORY_RESET BIT(1) | ||
| 81 | |||
| 82 | #define ADIS16201_ERROR_ACTIVE BIT(14) | ||
| 83 | |||
| 84 | enum adis16201_scan { | ||
| 85 | ADIS16201_SCAN_ACC_X, | ||
| 86 | ADIS16201_SCAN_ACC_Y, | ||
| 87 | ADIS16201_SCAN_INCLI_X, | ||
| 88 | ADIS16201_SCAN_INCLI_Y, | ||
| 89 | ADIS16201_SCAN_SUPPLY, | ||
| 90 | ADIS16201_SCAN_AUX_ADC, | ||
| 91 | ADIS16201_SCAN_TEMP, | ||
| 92 | }; | ||
| 93 | |||
| 94 | static const u8 adis16201_addresses[] = { | ||
| 95 | [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS_REG, | ||
| 96 | [ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS_REG, | ||
| 97 | [ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS_REG, | ||
| 98 | [ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS_REG, | ||
| 99 | }; | ||
| 100 | |||
| 101 | static int adis16201_read_raw(struct iio_dev *indio_dev, | ||
| 102 | struct iio_chan_spec const *chan, | ||
| 103 | int *val, int *val2, | ||
| 104 | long mask) | ||
| 105 | { | ||
| 106 | struct adis *st = iio_priv(indio_dev); | ||
| 107 | int ret; | ||
| 108 | int bits; | ||
| 109 | u8 addr; | ||
| 110 | s16 val16; | ||
| 111 | |||
| 112 | switch (mask) { | ||
| 113 | case IIO_CHAN_INFO_RAW: | ||
| 114 | return adis_single_conversion(indio_dev, chan, | ||
| 115 | ADIS16201_ERROR_ACTIVE, val); | ||
| 116 | case IIO_CHAN_INFO_SCALE: | ||
| 117 | switch (chan->type) { | ||
| 118 | case IIO_VOLTAGE: | ||
| 119 | if (chan->channel == 0) { | ||
| 120 | /* Voltage base units are mV hence 1.22 mV */ | ||
| 121 | *val = 1; | ||
| 122 | *val2 = 220000; | ||
| 123 | } else { | ||
| 124 | /* Voltage base units are mV hence 0.61 mV */ | ||
| 125 | *val = 0; | ||
| 126 | *val2 = 610000; | ||
| 127 | } | ||
| 128 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 129 | case IIO_TEMP: | ||
| 130 | *val = -470; | ||
| 131 | *val2 = 0; | ||
| 132 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 133 | case IIO_ACCEL: | ||
| 134 | /* | ||
| 135 | * IIO base unit for sensitivity of accelerometer | ||
| 136 | * is milli g. | ||
| 137 | * 1 LSB represents 0.244 mg. | ||
| 138 | */ | ||
| 139 | *val = 0; | ||
| 140 | *val2 = IIO_G_TO_M_S_2(462400); | ||
| 141 | return IIO_VAL_INT_PLUS_NANO; | ||
| 142 | case IIO_INCLI: | ||
| 143 | *val = 0; | ||
| 144 | *val2 = 100000; | ||
| 145 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 146 | default: | ||
| 147 | return -EINVAL; | ||
| 148 | } | ||
| 149 | break; | ||
| 150 | case IIO_CHAN_INFO_OFFSET: | ||
| 151 | /* | ||
| 152 | * The raw ADC value is 1278 when the temperature | ||
| 153 | * is 25 degrees and the scale factor per milli | ||
| 154 | * degree celcius is -470. | ||
| 155 | */ | ||
| 156 | *val = 25000 / -470 - 1278; | ||
| 157 | return IIO_VAL_INT; | ||
| 158 | case IIO_CHAN_INFO_CALIBBIAS: | ||
| 159 | switch (chan->type) { | ||
| 160 | case IIO_ACCEL: | ||
| 161 | bits = 12; | ||
| 162 | break; | ||
| 163 | case IIO_INCLI: | ||
| 164 | bits = 9; | ||
| 165 | break; | ||
| 166 | default: | ||
| 167 | return -EINVAL; | ||
| 168 | } | ||
| 169 | addr = adis16201_addresses[chan->scan_index]; | ||
| 170 | ret = adis_read_reg_16(st, addr, &val16); | ||
| 171 | if (ret) | ||
| 172 | return ret; | ||
| 173 | |||
| 174 | *val = sign_extend32(val16, bits - 1); | ||
| 175 | return IIO_VAL_INT; | ||
| 176 | } | ||
| 177 | |||
| 178 | return -EINVAL; | ||
| 179 | } | ||
| 180 | |||
| 181 | static int adis16201_write_raw(struct iio_dev *indio_dev, | ||
| 182 | struct iio_chan_spec const *chan, | ||
| 183 | int val, | ||
| 184 | int val2, | ||
| 185 | long mask) | ||
| 186 | { | ||
| 187 | struct adis *st = iio_priv(indio_dev); | ||
| 188 | int m; | ||
| 189 | |||
| 190 | if (mask != IIO_CHAN_INFO_CALIBBIAS) | ||
| 191 | return -EINVAL; | ||
| 192 | |||
| 193 | switch (chan->type) { | ||
| 194 | case IIO_ACCEL: | ||
| 195 | m = GENMASK(11, 0); | ||
| 196 | break; | ||
| 197 | case IIO_INCLI: | ||
| 198 | m = GENMASK(8, 0); | ||
| 199 | break; | ||
| 200 | default: | ||
| 201 | return -EINVAL; | ||
| 202 | } | ||
| 203 | |||
| 204 | return adis_write_reg_16(st, adis16201_addresses[chan->scan_index], | ||
| 205 | val & m); | ||
| 206 | } | ||
| 207 | |||
| 208 | static const struct iio_chan_spec adis16201_channels[] = { | ||
| 209 | ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0, | ||
| 210 | 12), | ||
| 211 | ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12), | ||
| 212 | ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT_REG, ADIS16201_SCAN_ACC_X, | ||
| 213 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 214 | ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT_REG, ADIS16201_SCAN_ACC_Y, | ||
| 215 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 216 | ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), | ||
| 217 | ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X, | ||
| 218 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 219 | ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, | ||
| 220 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 221 | IIO_CHAN_SOFT_TIMESTAMP(7) | ||
| 222 | }; | ||
| 223 | |||
| 224 | static const struct iio_info adis16201_info = { | ||
| 225 | .read_raw = adis16201_read_raw, | ||
| 226 | .write_raw = adis16201_write_raw, | ||
| 227 | .update_scan_mode = adis_update_scan_mode, | ||
| 228 | }; | ||
| 229 | |||
| 230 | static const char * const adis16201_status_error_msgs[] = { | ||
| 231 | [ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", | ||
| 232 | [ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed", | ||
| 233 | [ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", | ||
| 234 | [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", | ||
| 235 | }; | ||
| 236 | |||
| 237 | static const struct adis_data adis16201_data = { | ||
| 238 | .read_delay = 20, | ||
| 239 | .msc_ctrl_reg = ADIS16201_MSC_CTRL_REG, | ||
| 240 | .glob_cmd_reg = ADIS16201_GLOB_CMD_REG, | ||
| 241 | .diag_stat_reg = ADIS16201_DIAG_STAT_REG, | ||
| 242 | |||
| 243 | .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, | ||
| 244 | .self_test_no_autoclear = true, | ||
| 245 | .startup_delay = ADIS16201_STARTUP_DELAY_MS, | ||
| 246 | |||
| 247 | .status_error_msgs = adis16201_status_error_msgs, | ||
| 248 | .status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) | | ||
| 249 | BIT(ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT) | | ||
| 250 | BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) | | ||
| 251 | BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT), | ||
| 252 | }; | ||
| 253 | |||
| 254 | static int adis16201_probe(struct spi_device *spi) | ||
| 255 | { | ||
| 256 | struct iio_dev *indio_dev; | ||
| 257 | struct adis *st; | ||
| 258 | int ret; | ||
| 259 | |||
| 260 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | ||
| 261 | if (!indio_dev) | ||
| 262 | return -ENOMEM; | ||
| 263 | |||
| 264 | st = iio_priv(indio_dev); | ||
| 265 | spi_set_drvdata(spi, indio_dev); | ||
| 266 | |||
| 267 | indio_dev->name = spi->dev.driver->name; | ||
| 268 | indio_dev->dev.parent = &spi->dev; | ||
| 269 | indio_dev->info = &adis16201_info; | ||
| 270 | |||
| 271 | indio_dev->channels = adis16201_channels; | ||
| 272 | indio_dev->num_channels = ARRAY_SIZE(adis16201_channels); | ||
| 273 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 274 | |||
| 275 | ret = adis_init(st, indio_dev, spi, &adis16201_data); | ||
| 276 | if (ret) | ||
| 277 | return ret; | ||
| 278 | |||
| 279 | ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); | ||
| 280 | if (ret) | ||
| 281 | return ret; | ||
| 282 | |||
| 283 | ret = adis_initial_startup(st); | ||
| 284 | if (ret) | ||
| 285 | goto error_cleanup_buffer_trigger; | ||
| 286 | |||
| 287 | ret = iio_device_register(indio_dev); | ||
| 288 | if (ret < 0) | ||
| 289 | goto error_cleanup_buffer_trigger; | ||
| 290 | |||
| 291 | return 0; | ||
| 292 | |||
| 293 | error_cleanup_buffer_trigger: | ||
| 294 | adis_cleanup_buffer_and_trigger(st, indio_dev); | ||
| 295 | return ret; | ||
| 296 | } | ||
| 297 | |||
| 298 | static int adis16201_remove(struct spi_device *spi) | ||
| 299 | { | ||
| 300 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
| 301 | struct adis *st = iio_priv(indio_dev); | ||
| 302 | |||
| 303 | iio_device_unregister(indio_dev); | ||
| 304 | adis_cleanup_buffer_and_trigger(st, indio_dev); | ||
| 305 | |||
| 306 | return 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | static struct spi_driver adis16201_driver = { | ||
| 310 | .driver = { | ||
| 311 | .name = "adis16201", | ||
| 312 | }, | ||
| 313 | .probe = adis16201_probe, | ||
| 314 | .remove = adis16201_remove, | ||
| 315 | }; | ||
| 316 | module_spi_driver(adis16201_driver); | ||
| 317 | |||
| 318 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | ||
| 319 | MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"); | ||
| 320 | MODULE_LICENSE("GPL v2"); | ||
| 321 | MODULE_ALIAS("spi:adis16201"); | ||
| @@ -6,7 +6,6 @@ · 5 unmodified lines | |||
| 6 | * Licensed under the GPL-2 or later. | 6 | * Licensed under the GPL-2 or later. |
| 7 | */ | 7 | */ |
| 8 | 8 | ||
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/device.h> | 9 | #include <linux/device.h> |
| 11 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 12 | #include <linux/list.h> | 11 | #include <linux/list.h> |
| @@ -16,8 +15,6 @@ · 3 unmodified lines | |||
| 16 | #include <linux/sysfs.h> | 15 | #include <linux/sysfs.h> |
| 17 | 16 | ||
| 18 | #include <linux/iio/iio.h> | 17 | #include <linux/iio/iio.h> |
| 19 | #include <linux/iio/sysfs.h> | ||
| 20 | #include <linux/iio/buffer.h> | ||
| 21 | #include <linux/iio/imu/adis.h> | 18 | #include <linux/iio/imu/adis.h> |
| 22 | 19 | ||
| 23 | #define ADIS16209_STARTUP_DELAY_MS 220 | 20 | #define ADIS16209_STARTUP_DELAY_MS 220 |
| @@ -71,13 +68,13 @@ · 47 unmodified lines | |||
| 71 | #define ADIS16209_STAT_REG 0x3C | 68 | #define ADIS16209_STAT_REG 0x3C |
| 72 | #define ADIS16209_STAT_ALARM2 BIT(9) | 69 | #define ADIS16209_STAT_ALARM2 BIT(9) |
| 73 | #define ADIS16209_STAT_ALARM1 BIT(8) | 70 | #define ADIS16209_STAT_ALARM1 BIT(8) |
| 74 | #define ADIS16209_STAT_SELFTEST_FAIL_BIT 5 | 71 | #define ADIS16209_STAT_SELFTEST_FAIL_BIT 5 |
| 75 | #define ADIS16209_STAT_SPI_FAIL_BIT 3 | 72 | #define ADIS16209_STAT_SPI_FAIL_BIT 3 |
| 76 | #define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2 | 73 | #define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2 |
| 77 | /* Power supply above 3.625 V */ | 74 | /* Power supply above 3.625 V */ |
| 78 | #define ADIS16209_STAT_POWER_HIGH_BIT 1 | 75 | #define ADIS16209_STAT_POWER_HIGH_BIT 1 |
| 79 | /* Power supply below 3.15 V */ | 76 | /* Power supply below 3.15 V */ |
| 80 | #define ADIS16209_STAT_POWER_LOW_BIT 0 | 77 | #define ADIS16209_STAT_POWER_LOW_BIT 0 |
| 81 | 78 | ||
| 82 | #define ADIS16209_CMD_REG 0x3E | 79 | #define ADIS16209_CMD_REG 0x3E |
| 83 | #define ADIS16209_CMD_SW_RESET BIT(7) | 80 | #define ADIS16209_CMD_SW_RESET BIT(7) |
| @@ -115,25 +112,22 @@ static int adis16209_write_raw(struct iio_dev *indio_dev, · 31 unmodified lines | |||
| 115 | long mask) | 112 | long mask) |
| 116 | { | 113 | { |
| 117 | struct adis *st = iio_priv(indio_dev); | 114 | struct adis *st = iio_priv(indio_dev); |
| 118 | int bits; | 115 | int m; |
| 119 | s16 val16; | ||
| 120 | u8 addr; | ||
| 121 | 116 | ||
| 122 | switch (mask) { | 117 | if (mask != IIO_CHAN_INFO_CALIBBIAS) |
| 123 | case IIO_CHAN_INFO_CALIBBIAS: | 118 | return -EINVAL; |
| 124 | switch (chan->type) { | 119 | |
| 125 | case IIO_ACCEL: | 120 | switch (chan->type) { |
| 126 | case IIO_INCLI: | 121 | case IIO_ACCEL: |
| 127 | bits = 14; | 122 | case IIO_INCLI: |
| 128 | break; | 123 | m = GENMASK(13, 0); |
| 129 | default: | 124 | break; |
| 130 | return -EINVAL; | 125 | default: |
| 131 | } | 126 | return -EINVAL; |
| 132 | val16 = val & ((1 << bits) - 1); | ||
| 133 | addr = adis16209_addresses[chan->scan_index][0]; | ||
| 134 | return adis_write_reg_16(st, addr, val16); | ||
| 135 | } | 127 | } |
| 136 | return -EINVAL; | 128 | |
| 129 | return adis_write_reg_16(st, adis16209_addresses[chan->scan_index][0], | ||
| 130 | val & m); | ||
| 137 | } | 131 | } |
| 138 | 132 | ||
| 139 | static int adis16209_read_raw(struct iio_dev *indio_dev, | 133 | static int adis16209_read_raw(struct iio_dev *indio_dev, |
| @@ -195,7 +189,7 @@ static int adis16209_read_raw(struct iio_dev *indio_dev, · 55 unmodified lines | |||
| 195 | case IIO_CHAN_INFO_OFFSET: | 189 | case IIO_CHAN_INFO_OFFSET: |
| 196 | /* | 190 | /* |
| 197 | * The raw ADC value is 0x4FE when the temperature | 191 | * The raw ADC value is 0x4FE when the temperature |
| 198 | * is 25 degrees and the scale factor per milli | 192 | * is 45 degrees and the scale factor per milli |
| 199 | * degree celcius is -470. | 193 | * degree celcius is -470. |
| 200 | */ | 194 | */ |
| 201 | *val = 25000 / -470 - 0x4FE; | 195 | *val = 25000 / -470 - 0x4FE; |
| @@ -270,13 +264,14 @@ static const struct adis_data adis16209_data = { · 68 unmodified lines | |||
| 270 | 264 | ||
| 271 | static int adis16209_probe(struct spi_device *spi) | 265 | static int adis16209_probe(struct spi_device *spi) |
| 272 | { | 266 | { |
| 273 | int ret; | ||
| 274 | struct adis *st; | ||
| 275 | struct iio_dev *indio_dev; | 267 | struct iio_dev *indio_dev; |
| 268 | struct adis *st; | ||
| 269 | int ret; | ||
| 276 | 270 | ||
| 277 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | 271 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
| 278 | if (!indio_dev) | 272 | if (!indio_dev) |
| 279 | return -ENOMEM; | 273 | return -ENOMEM; |
| 274 | |||
| 280 | st = iio_priv(indio_dev); | 275 | st = iio_priv(indio_dev); |
| 281 | spi_set_drvdata(spi, indio_dev); | 276 | spi_set_drvdata(spi, indio_dev); |
| 282 | 277 | ||
| @@ -290,6 +285,7 @@ static int adis16209_probe(struct spi_device *spi) · 7 unmodified lines | |||
| 290 | ret = adis_init(st, indio_dev, spi, &adis16209_data); | 285 | ret = adis_init(st, indio_dev, spi, &adis16209_data); |
| 291 | if (ret) | 286 | if (ret) |
| 292 | return ret; | 287 | return ret; |
| 288 | |||
| 293 | ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); | 289 | ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); |
| 294 | if (ret) | 290 | if (ret) |
| 295 | return ret; | 291 | return ret; |
| @@ -837,29 +837,12 @@ static int bmc150_accel_fifo_transfer(struct bmc150_accel_data *data, · 836 unmodified lines | |||
| 837 | int sample_length = 3 * 2; | 837 | int sample_length = 3 * 2; |
| 838 | int ret; | 838 | int ret; |
| 839 | int total_length = samples * sample_length; | 839 | int total_length = samples * sample_length; |
| 840 | int i; | ||
| 841 | size_t step = regmap_get_raw_read_max(data->regmap); | ||
| 842 | |||
| 843 | if (!step || step > total_length) | ||
| 844 | step = total_length; | ||
| 845 | else if (step < total_length) | ||
| 846 | step = sample_length; | ||
| 847 | |||
| 848 | /* | ||
| 849 | * Seems we have a bus with size limitation so we have to execute | ||
| 850 | * multiple reads | ||
| 851 | */ | ||
| 852 | for (i = 0; i < total_length; i += step) { | ||
| 853 | ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA, | ||
| 854 | &buffer[i], step); | ||
| 855 | if (ret) | ||
| 856 | break; | ||
| 857 | } | ||
| 858 | 840 | ||
| 841 | ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA, | ||
| 842 | buffer, total_length); | ||
| 859 | if (ret) | 843 | if (ret) |
| 860 | dev_err(dev, | 844 | dev_err(dev, |
| 861 | "Error transferring data from fifo in single steps of %zu\n", | 845 | "Error transferring data from fifo: %d\n", ret); |
| 862 | step); | ||
| 863 | 846 | ||
| 864 | return ret; | 847 | return ret; |
| 865 | } | 848 | } |
| @@ -106,6 +106,7 @@ struct mma8452_data { · 105 unmodified lines | |||
| 106 | u8 ctrl_reg1; | 106 | u8 ctrl_reg1; |
| 107 | u8 data_cfg; | 107 | u8 data_cfg; |
| 108 | const struct mma_chip_info *chip_info; | 108 | const struct mma_chip_info *chip_info; |
| 109 | int sleep_val; | ||
| 109 | }; | 110 | }; |
| 110 | 111 | ||
| 111 | /** | 112 | /** |
| @@ -193,7 +194,11 @@ static int mma8452_drdy(struct mma8452_data *data) · 81 unmodified lines | |||
| 193 | if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY) | 194 | if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY) |
| 194 | return 0; | 195 | return 0; |
| 195 | 196 | ||
| 196 | msleep(20); | 197 | if (data->sleep_val <= 20) |
| 198 | usleep_range(data->sleep_val * 250, | ||
| 199 | data->sleep_val * 500); | ||
| 200 | else | ||
| 201 | msleep(20); | ||
| 197 | } | 202 | } |
| 198 | 203 | ||
| 199 | dev_err(&data->client->dev, "data not ready\n"); | 204 | dev_err(&data->client->dev, "data not ready\n"); |
| @@ -544,6 +549,18 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, · 344 unmodified lines | |||
| 544 | return -EINVAL; | 549 | return -EINVAL; |
| 545 | } | 550 | } |
| 546 | 551 | ||
| 552 | static int mma8452_calculate_sleep(struct mma8452_data *data) | ||
| 553 | { | ||
| 554 | int ret, i = mma8452_get_odr_index(data); | ||
| 555 | |||
| 556 | if (mma8452_samp_freq[i][0] > 0) | ||
| 557 | ret = 1000 / mma8452_samp_freq[i][0]; | ||
| 558 | else | ||
| 559 | ret = 1000; | ||
| 560 | |||
| 561 | return ret == 0 ? 1 : ret; | ||
| 562 | } | ||
| 563 | |||
| 547 | static int mma8452_standby(struct mma8452_data *data) | 564 | static int mma8452_standby(struct mma8452_data *data) |
| 548 | { | 565 | { |
| 549 | return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1, | 566 | return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1, |
| @@ -700,6 +717,8 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, · 150 unmodified lines | |||
| 700 | data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK; | 717 | data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK; |
| 701 | data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT; | 718 | data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT; |
| 702 | 719 | ||
| 720 | data->sleep_val = mma8452_calculate_sleep(data); | ||
| 721 | |||
| 703 | ret = mma8452_change_config(data, MMA8452_CTRL_REG1, | 722 | ret = mma8452_change_config(data, MMA8452_CTRL_REG1, |
| 704 | data->ctrl_reg1); | 723 | data->ctrl_reg1); |
| 705 | break; | 724 | break; |
| @@ -1593,6 +1612,9 @@ static int mma8452_probe(struct i2c_client *client, · 887 unmodified lines | |||
| 1593 | 1612 | ||
| 1594 | data->ctrl_reg1 = MMA8452_CTRL_ACTIVE | | 1613 | data->ctrl_reg1 = MMA8452_CTRL_ACTIVE | |
| 1595 | (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT); | 1614 | (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT); |
| 1615 | |||
| 1616 | data->sleep_val = mma8452_calculate_sleep(data); | ||
| 1617 | |||
| 1596 | ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, | 1618 | ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, |
| 1597 | data->ctrl_reg1); | 1619 | data->ctrl_reg1); |
| 1598 | if (ret < 0) | 1620 | if (ret < 0) |
| @@ -1277,7 +1277,7 @@ static int sca3000_configure_ring(struct iio_dev *indio_dev) · 1276 unmodified lines | |||
| 1277 | { | 1277 | { |
| 1278 | struct iio_buffer *buffer; | 1278 | struct iio_buffer *buffer; |
| 1279 | 1279 | ||
| 1280 | buffer = iio_kfifo_allocate(); | 1280 | buffer = devm_iio_kfifo_allocate(&indio_dev->dev); |
| 1281 | if (!buffer) | 1281 | if (!buffer) |
| 1282 | return -ENOMEM; | 1282 | return -ENOMEM; |
| 1283 | 1283 | ||
| @@ -1287,11 +1287,6 @@ static int sca3000_configure_ring(struct iio_dev *indio_dev) · 3 unmodified lines | |||
| 1287 | return 0; | 1287 | return 0; |
| 1288 | } | 1288 | } |
| 1289 | 1289 | ||
| 1290 | static void sca3000_unconfigure_ring(struct iio_dev *indio_dev) | ||
| 1291 | { | ||
| 1292 | iio_kfifo_free(indio_dev->buffer); | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | static inline | 1290 | static inline |
| 1296 | int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state) | 1291 | int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state) |
| 1297 | { | 1292 | { |
| @@ -1486,7 +1481,9 @@ static int sca3000_probe(struct spi_device *spi) · 188 unmodified lines | |||
| 1486 | } | 1481 | } |
| 1487 | indio_dev->modes = INDIO_DIRECT_MODE; | 1482 | indio_dev->modes = INDIO_DIRECT_MODE; |
| 1488 | 1483 | ||
| 1489 | sca3000_configure_ring(indio_dev); | 1484 | ret = sca3000_configure_ring(indio_dev); |
| 1485 | if (ret) | ||
| 1486 | return ret; | ||
| 1490 | 1487 | ||
| 1491 | if (spi->irq) { | 1488 | if (spi->irq) { |
| 1492 | ret = request_threaded_irq(spi->irq, | 1489 | ret = request_threaded_irq(spi->irq, |
| @@ -1546,8 +1543,6 @@ static int sca3000_remove(struct spi_device *spi) · 53 unmodified lines | |||
| 1546 | if (spi->irq) | 1543 | if (spi->irq) |
| 1547 | free_irq(spi->irq, indio_dev); | 1544 | free_irq(spi->irq, indio_dev); |
| 1548 | 1545 | ||
| 1549 | sca3000_unconfigure_ring(indio_dev); | ||
| 1550 | |||
| 1551 | return 0; | 1546 | return 0; |
| 1552 | } | 1547 | } |
| 1553 | 1548 | ||
| @@ -107,6 +107,7 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match); · 106 unmodified lines | |||
| 107 | 107 | ||
| 108 | #ifdef CONFIG_ACPI | 108 | #ifdef CONFIG_ACPI |
| 109 | static const struct acpi_device_id st_accel_acpi_match[] = { | 109 | static const struct acpi_device_id st_accel_acpi_match[] = { |
| 110 | {"SMO8840", LNG2DM}, | ||
| 110 | {"SMO8A90", LNG2DM}, | 111 | {"SMO8A90", LNG2DM}, |
| 111 | { }, | 112 | { }, |
| 112 | }; | 113 | }; |
| @@ -153,6 +153,17 @@ struct ad7791_state { · 152 unmodified lines | |||
| 153 | const struct ad7791_chip_info *info; | 153 | const struct ad7791_chip_info *info; |
| 154 | }; | 154 | }; |
| 155 | 155 | ||
| 156 | static const int ad7791_sample_freq_avail[8][2] = { | ||
| 157 | [AD7791_FILTER_RATE_120] = { 120, 0 }, | ||
| 158 | [AD7791_FILTER_RATE_100] = { 100, 0 }, | ||
| 159 | [AD7791_FILTER_RATE_33_3] = { 33, 300000 }, | ||
| 160 | [AD7791_FILTER_RATE_20] = { 20, 0 }, | ||
| 161 | [AD7791_FILTER_RATE_16_6] = { 16, 600000 }, | ||
| 162 | [AD7791_FILTER_RATE_16_7] = { 16, 700000 }, | ||
| 163 | [AD7791_FILTER_RATE_13_3] = { 13, 300000 }, | ||
| 164 | [AD7791_FILTER_RATE_9_5] = { 9, 500000 }, | ||
| 165 | }; | ||
| 166 | |||
| 156 | static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) | 167 | static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) |
| 157 | { | 168 | { |
| 158 | return container_of(sd, struct ad7791_state, sd); | 169 | return container_of(sd, struct ad7791_state, sd); |
| @@ -202,6 +213,7 @@ static int ad7791_read_raw(struct iio_dev *indio_dev, · 43 unmodified lines | |||
| 202 | { | 213 | { |
| 203 | struct ad7791_state *st = iio_priv(indio_dev); | 214 | struct ad7791_state *st = iio_priv(indio_dev); |
| 204 | bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); | 215 | bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); |
| 216 | unsigned int rate; | ||
| 205 | 217 | ||
| 206 | switch (info) { | 218 | switch (info) { |
| 207 | case IIO_CHAN_INFO_RAW: | 219 | case IIO_CHAN_INFO_RAW: |
| @@ -239,63 +251,56 @@ static int ad7791_read_raw(struct iio_dev *indio_dev, · 31 unmodified lines | |||
| 239 | *val2 = chan->scan_type.realbits - 1; | 251 | *val2 = chan->scan_type.realbits - 1; |
| 240 | 252 | ||
| 241 | return IIO_VAL_FRACTIONAL_LOG2; | 253 | return IIO_VAL_FRACTIONAL_LOG2; |
| 254 | case IIO_CHAN_INFO_SAMP_FREQ: | ||
| 255 | rate = st->filter & AD7791_FILTER_RATE_MASK; | ||
| 256 | *val = ad7791_sample_freq_avail[rate][0]; | ||
| 257 | *val2 = ad7791_sample_freq_avail[rate][1]; | ||
| 258 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 242 | } | 259 | } |
| 243 | 260 | ||
| 244 | return -EINVAL; | 261 | return -EINVAL; |
| 245 | } | 262 | } |
| 246 | 263 | ||
| 247 | static const char * const ad7791_sample_freq_avail[] = { | 264 | static int ad7791_write_raw(struct iio_dev *indio_dev, |
| 248 | [AD7791_FILTER_RATE_120] = "120", | 265 | struct iio_chan_spec const *chan, int val, int val2, long mask) |
| 249 | [AD7791_FILTER_RATE_100] = "100", | ||
| 250 | [AD7791_FILTER_RATE_33_3] = "33.3", | ||
| 251 | [AD7791_FILTER_RATE_20] = "20", | ||
| 252 | [AD7791_FILTER_RATE_16_6] = "16.6", | ||
| 253 | [AD7791_FILTER_RATE_16_7] = "16.7", | ||
| 254 | [AD7791_FILTER_RATE_13_3] = "13.3", | ||
| 255 | [AD7791_FILTER_RATE_9_5] = "9.5", | ||
| 256 | }; | ||
| 257 | |||
| 258 | static ssize_t ad7791_read_frequency(struct device *dev, | ||
| 259 | struct device_attribute *attr, char *buf) | ||
| 260 | { | 266 | { |
| 261 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 262 | struct ad7791_state *st = iio_priv(indio_dev); | 267 | struct ad7791_state *st = iio_priv(indio_dev); |
| 263 | unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK; | 268 | int ret, i; |
| 264 | |||
| 265 | return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]); | ||
| 266 | } | ||
| 267 | |||
| 268 | static ssize_t ad7791_write_frequency(struct device *dev, | ||
| 269 | struct device_attribute *attr, const char *buf, size_t len) | ||
| 270 | { | ||
| 271 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 272 | struct ad7791_state *st = iio_priv(indio_dev); | ||
| 273 | int i, ret; | ||
| 274 | |||
| 275 | i = sysfs_match_string(ad7791_sample_freq_avail, buf); | ||
| 276 | if (i < 0) | ||
| 277 | return i; | ||
| 278 | 269 | ||
| 279 | ret = iio_device_claim_direct_mode(indio_dev); | 270 | ret = iio_device_claim_direct_mode(indio_dev); |
| 280 | if (ret) | 271 | if (ret) |
| 281 | return ret; | 272 | return ret; |
| 282 | st->filter &= ~AD7791_FILTER_RATE_MASK; | ||
| 283 | st->filter |= i; | ||
| 284 | ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, sizeof(st->filter), | ||
| 285 | st->filter); | ||
| 286 | iio_device_release_direct_mode(indio_dev); | ||
| 287 | 273 | ||
| 288 | return len; | 274 | switch (mask) { |
| 289 | } | 275 | case IIO_CHAN_INFO_SAMP_FREQ: |
| 276 | for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { | ||
| 277 | if (ad7791_sample_freq_avail[i][0] == val && | ||
| 278 | ad7791_sample_freq_avail[i][1] == val2) | ||
| 279 | break; | ||
| 280 | } | ||
| 281 | |||
| 282 | if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) { | ||
| 283 | ret = -EINVAL; | ||
| 284 | break; | ||
| 285 | } | ||
| 286 | |||
| 287 | st->filter &= ~AD7791_FILTER_RATE_MASK; | ||
| 288 | st->filter |= i; | ||
| 289 | ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, | ||
| 290 | sizeof(st->filter), | ||
| 291 | st->filter); | ||
| 292 | break; | ||
| 293 | default: | ||
| 294 | ret = -EINVAL; | ||
| 295 | } | ||
| 290 | 296 | ||
| 291 | static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, | 297 | iio_device_release_direct_mode(indio_dev); |
| 292 | ad7791_read_frequency, | 298 | return ret; |
| 293 | ad7791_write_frequency); | 299 | } |
| 294 | 300 | ||
| 295 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); | 301 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); |
| 296 | 302 | ||
| 297 | static struct attribute *ad7791_attributes[] = { | 303 | static struct attribute *ad7791_attributes[] = { |
| 298 | &iio_dev_attr_sampling_frequency.dev_attr.attr, | ||
| 299 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, | 304 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, |
| 300 | NULL | 305 | NULL |
| 301 | }; | 306 | }; |
| @@ -306,12 +311,14 @@ static const struct attribute_group ad7791_attribute_group = { · 4 unmodified lines | |||
| 306 | 311 | ||
| 307 | static const struct iio_info ad7791_info = { | 312 | static const struct iio_info ad7791_info = { |
| 308 | .read_raw = &ad7791_read_raw, | 313 | .read_raw = &ad7791_read_raw, |
| 314 | .write_raw = &ad7791_write_raw, | ||
| 309 | .attrs = &ad7791_attribute_group, | 315 | .attrs = &ad7791_attribute_group, |
| 310 | .validate_trigger = ad_sd_validate_trigger, | 316 | .validate_trigger = ad_sd_validate_trigger, |
| 311 | }; | 317 | }; |
| 312 | 318 | ||
| 313 | static const struct iio_info ad7791_no_filter_info = { | 319 | static const struct iio_info ad7791_no_filter_info = { |
| 314 | .read_raw = &ad7791_read_raw, | 320 | .read_raw = &ad7791_read_raw, |
| 321 | .write_raw = &ad7791_write_raw, | ||
| 315 | .validate_trigger = ad_sd_validate_trigger, | 322 | .validate_trigger = ad_sd_validate_trigger, |
| 316 | }; | 323 | }; |
| 317 | 324 | ||
| @@ -604,5 +604,5 @@ static struct platform_driver imx7d_adc_driver = { · 603 unmodified lines | |||
| 604 | module_platform_driver(imx7d_adc_driver); | 604 | module_platform_driver(imx7d_adc_driver); |
| 605 | 605 | ||
| 606 | MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); | 606 | MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); |
| 607 | MODULE_DESCRIPTION("Freeacale IMX7D ADC driver"); | 607 | MODULE_DESCRIPTION("Freescale IMX7D ADC driver"); |
| 608 | MODULE_LICENSE("GPL v2"); | 608 | MODULE_LICENSE("GPL v2"); |
| @@ -219,15 +219,19 @@ enum meson_sar_adc_chan7_mux_sel { · 218 unmodified lines | |||
| 219 | CHAN7_MUX_CH7_INPUT = 0x7, | 219 | CHAN7_MUX_CH7_INPUT = 0x7, |
| 220 | }; | 220 | }; |
| 221 | 221 | ||
| 222 | struct meson_sar_adc_data { | 222 | struct meson_sar_adc_param { |
| 223 | bool has_bl30_integration; | 223 | bool has_bl30_integration; |
| 224 | unsigned long clock_rate; | 224 | unsigned long clock_rate; |
| 225 | u32 bandgap_reg; | 225 | u32 bandgap_reg; |
| 226 | unsigned int resolution; | 226 | unsigned int resolution; |
| 227 | const char *name; | ||
| 228 | const struct regmap_config *regmap_config; | 227 | const struct regmap_config *regmap_config; |
| 229 | }; | 228 | }; |
| 230 | 229 | ||
| 230 | struct meson_sar_adc_data { | ||
| 231 | const struct meson_sar_adc_param *param; | ||
| 232 | const char *name; | ||
| 233 | }; | ||
| 234 | |||
| 231 | struct meson_sar_adc_priv { | 235 | struct meson_sar_adc_priv { |
| 232 | struct regmap *regmap; | 236 | struct regmap *regmap; |
| 233 | struct regulator *vref; | 237 | struct regulator *vref; |
| @@ -276,7 +280,7 @@ static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val) · 42 unmodified lines | |||
| 276 | /* use val_calib = scale * val_raw + offset calibration function */ | 280 | /* use val_calib = scale * val_raw + offset calibration function */ |
| 277 | tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; | 281 | tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; |
| 278 | 282 | ||
| 279 | return clamp(tmp, 0, (1 << priv->data->resolution) - 1); | 283 | return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1); |
| 280 | } | 284 | } |
| 281 | 285 | ||
| 282 | static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) | 286 | static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) |
| @@ -328,7 +332,7 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, · 45 unmodified lines | |||
| 328 | } | 332 | } |
| 329 | 333 | ||
| 330 | fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); | 334 | fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); |
| 331 | fifo_val &= GENMASK(priv->data->resolution - 1, 0); | 335 | fifo_val &= GENMASK(priv->data->param->resolution - 1, 0); |
| 332 | *val = meson_sar_adc_calib_val(indio_dev, fifo_val); | 336 | *val = meson_sar_adc_calib_val(indio_dev, fifo_val); |
| 333 | 337 | ||
| 334 | return 0; | 338 | return 0; |
| @@ -447,7 +451,7 @@ static int meson_sar_adc_lock(struct iio_dev *indio_dev) · 112 unmodified lines | |||
| 447 | 451 | ||
| 448 | mutex_lock(&indio_dev->mlock); | 452 | mutex_lock(&indio_dev->mlock); |
| 449 | 453 | ||
| 450 | if (priv->data->has_bl30_integration) { | 454 | if (priv->data->param->has_bl30_integration) { |
| 451 | /* prevent BL30 from using the SAR ADC while we are using it */ | 455 | /* prevent BL30 from using the SAR ADC while we are using it */ |
| 452 | regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, | 456 | regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, |
| 453 | MESON_SAR_ADC_DELAY_KERNEL_BUSY, | 457 | MESON_SAR_ADC_DELAY_KERNEL_BUSY, |
| @@ -475,7 +479,7 @@ static void meson_sar_adc_unlock(struct iio_dev *indio_dev) · 21 unmodified lines | |||
| 475 | { | 479 | { |
| 476 | struct meson_sar_adc_priv *priv = iio_priv(indio_dev); | 480 | struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| 477 | 481 | ||
| 478 | if (priv->data->has_bl30_integration) | 482 | if (priv->data->param->has_bl30_integration) |
| 479 | /* allow BL30 to use the SAR ADC again */ | 483 | /* allow BL30 to use the SAR ADC again */ |
| 480 | regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, | 484 | regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, |
| 481 | MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0); | 485 | MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0); |
| @@ -559,7 +563,7 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, · 77 unmodified lines | |||
| 559 | } | 563 | } |
| 560 | 564 | ||
| 561 | *val = ret / 1000; | 565 | *val = ret / 1000; |
| 562 | *val2 = priv->data->resolution; | 566 | *val2 = priv->data->param->resolution; |
| 563 | return IIO_VAL_FRACTIONAL_LOG2; | 567 | return IIO_VAL_FRACTIONAL_LOG2; |
| 564 | 568 | ||
| 565 | case IIO_CHAN_INFO_CALIBBIAS: | 569 | case IIO_CHAN_INFO_CALIBBIAS: |
| @@ -632,7 +636,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) · 66 unmodified lines | |||
| 632 | */ | 636 | */ |
| 633 | meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); | 637 | meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); |
| 634 | 638 | ||
| 635 | if (priv->data->has_bl30_integration) { | 639 | if (priv->data->param->has_bl30_integration) { |
| 636 | /* | 640 | /* |
| 637 | * leave sampling delay and the input clocks as configured by | 641 | * leave sampling delay and the input clocks as configured by |
| 638 | * BL30 to make sure BL30 gets the values it expects when | 642 | * BL30 to make sure BL30 gets the values it expects when |
| @@ -712,7 +716,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) · 73 unmodified lines | |||
| 712 | return ret; | 716 | return ret; |
| 713 | } | 717 | } |
| 714 | 718 | ||
| 715 | ret = clk_set_rate(priv->adc_clk, priv->data->clock_rate); | 719 | ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate); |
| 716 | if (ret) { | 720 | if (ret) { |
| 717 | dev_err(indio_dev->dev.parent, | 721 | dev_err(indio_dev->dev.parent, |
| 718 | "failed to set adc clock rate\n"); | 722 | "failed to set adc clock rate\n"); |
| @@ -725,14 +729,15 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) · 6 unmodified lines | |||
| 725 | static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off) | 729 | static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off) |
| 726 | { | 730 | { |
| 727 | struct meson_sar_adc_priv *priv = iio_priv(indio_dev); | 731 | struct meson_sar_adc_priv *priv = iio_priv(indio_dev); |
| 732 | const struct meson_sar_adc_param *param = priv->data->param; | ||
| 728 | u32 enable_mask; | 733 | u32 enable_mask; |
| 729 | 734 | ||
| 730 | if (priv->data->bandgap_reg == MESON_SAR_ADC_REG11) | 735 | if (param->bandgap_reg == MESON_SAR_ADC_REG11) |
| 731 | enable_mask = MESON_SAR_ADC_REG11_BANDGAP_EN; | 736 | enable_mask = MESON_SAR_ADC_REG11_BANDGAP_EN; |
| 732 | else | 737 | else |
| 733 | enable_mask = MESON_SAR_ADC_DELTA_10_TS_VBG_EN; | 738 | enable_mask = MESON_SAR_ADC_DELTA_10_TS_VBG_EN; |
| 734 | 739 | ||
| 735 | regmap_update_bits(priv->regmap, priv->data->bandgap_reg, enable_mask, | 740 | regmap_update_bits(priv->regmap, param->bandgap_reg, enable_mask, |
| 736 | on_off ? enable_mask : 0); | 741 | on_off ? enable_mask : 0); |
| 737 | } | 742 | } |
| 738 | 743 | ||
| @@ -844,8 +849,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev) · 105 unmodified lines | |||
| 844 | int ret, nominal0, nominal1, value0, value1; | 849 | int ret, nominal0, nominal1, value0, value1; |
| 845 | 850 | ||
| 846 | /* use points 25% and 75% for calibration */ | 851 | /* use points 25% and 75% for calibration */ |
| 847 | nominal0 = (1 << priv->data->resolution) / 4; | 852 | nominal0 = (1 << priv->data->param->resolution) / 4; |
| 848 | nominal1 = (1 << priv->data->resolution) * 3 / 4; | 853 | nominal1 = (1 << priv->data->param->resolution) * 3 / 4; |
| 849 | 854 | ||
| 850 | meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4); | 855 | meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4); |
| 851 | usleep_range(10, 20); | 856 | usleep_range(10, 20); |
| @@ -883,51 +888,60 @@ static const struct iio_info meson_sar_adc_iio_info = { · 31 unmodified lines | |||
| 883 | .read_raw = meson_sar_adc_iio_info_read_raw, | 888 | .read_raw = meson_sar_adc_iio_info_read_raw, |
| 884 | }; | 889 | }; |
| 885 | 890 | ||
| 886 | static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { | 891 | static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { |
| 887 | .has_bl30_integration = false, | ||
| 888 | .clock_rate = 1150000, | ||
| 889 | .bandgap_reg = MESON_SAR_ADC_DELTA_10, | ||
| 890 | .regmap_config = &meson_sar_adc_regmap_config_meson8, | ||
| 891 | .resolution = 10, | ||
| 892 | .name = "meson-meson8-saradc", | ||
| 893 | }; | ||
| 894 | |||
| 895 | static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { | ||
| 896 | .has_bl30_integration = false, | 892 | .has_bl30_integration = false, |
| 897 | .clock_rate = 1150000, | 893 | .clock_rate = 1150000, |
| 898 | .bandgap_reg = MESON_SAR_ADC_DELTA_10, | 894 | .bandgap_reg = MESON_SAR_ADC_DELTA_10, |
| 899 | .regmap_config = &meson_sar_adc_regmap_config_meson8, | 895 | .regmap_config = &meson_sar_adc_regmap_config_meson8, |
| 900 | .resolution = 10, | 896 | .resolution = 10, |
| 901 | .name = "meson-meson8b-saradc", | ||
| 902 | }; | 897 | }; |
| 903 | 898 | ||
| 904 | static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { | 899 | static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { |
| 905 | .has_bl30_integration = true, | 900 | .has_bl30_integration = true, |
| 906 | .clock_rate = 1200000, | 901 | .clock_rate = 1200000, |
| 907 | .bandgap_reg = MESON_SAR_ADC_REG11, | 902 | .bandgap_reg = MESON_SAR_ADC_REG11, |
| 908 | .regmap_config = &meson_sar_adc_regmap_config_gxbb, | 903 | .regmap_config = &meson_sar_adc_regmap_config_gxbb, |
| 909 | .resolution = 10, | 904 | .resolution = 10, |
| 910 | .name = "meson-gxbb-saradc", | ||
| 911 | }; | 905 | }; |
| 912 | 906 | ||
| 913 | static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { | 907 | static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { |
| 914 | .has_bl30_integration = true, | 908 | .has_bl30_integration = true, |
| 915 | .clock_rate = 1200000, | 909 | .clock_rate = 1200000, |
| 916 | .bandgap_reg = MESON_SAR_ADC_REG11, | 910 | .bandgap_reg = MESON_SAR_ADC_REG11, |
| 917 | .regmap_config = &meson_sar_adc_regmap_config_gxbb, | 911 | .regmap_config = &meson_sar_adc_regmap_config_gxbb, |
| 918 | .resolution = 12, | 912 | .resolution = 12, |
| 913 | }; | ||
| 914 | |||
| 915 | static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { | ||
| 916 | .param = &meson_sar_adc_meson8_param, | ||
| 917 | .name = "meson-meson8-saradc", | ||
| 918 | }; | ||
| 919 | |||
| 920 | static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { | ||
| 921 | .param = &meson_sar_adc_meson8_param, | ||
| 922 | .name = "meson-meson8b-saradc", | ||
| 923 | }; | ||
| 924 | |||
| 925 | static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { | ||
| 926 | .param = &meson_sar_adc_gxbb_param, | ||
| 927 | .name = "meson-gxbb-saradc", | ||
| 928 | }; | ||
| 929 | |||
| 930 | static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { | ||
| 931 | .param = &meson_sar_adc_gxl_param, | ||
| 919 | .name = "meson-gxl-saradc", | 932 | .name = "meson-gxl-saradc", |
| 920 | }; | 933 | }; |
| 921 | 934 | ||
| 922 | static const struct meson_sar_adc_data meson_sar_adc_gxm_data = { | 935 | static const struct meson_sar_adc_data meson_sar_adc_gxm_data = { |
| 923 | .has_bl30_integration = true, | 936 | .param = &meson_sar_adc_gxl_param, |
| 924 | .clock_rate = 1200000, | ||
| 925 | .bandgap_reg = MESON_SAR_ADC_REG11, | ||
| 926 | .regmap_config = &meson_sar_adc_regmap_config_gxbb, | ||
| 927 | .resolution = 12, | ||
| 928 | .name = "meson-gxm-saradc", | 937 | .name = "meson-gxm-saradc", |
| 929 | }; | 938 | }; |
| 930 | 939 | ||
| 940 | static const struct meson_sar_adc_data meson_sar_adc_axg_data = { | ||
| 941 | .param = &meson_sar_adc_gxl_param, | ||
| 942 | .name = "meson-axg-saradc", | ||
| 943 | }; | ||
| 944 | |||
| 931 | static const struct of_device_id meson_sar_adc_of_match[] = { | 945 | static const struct of_device_id meson_sar_adc_of_match[] = { |
| 932 | { | 946 | { |
| 933 | .compatible = "amlogic,meson8-saradc", | 947 | .compatible = "amlogic,meson8-saradc", |
| @@ -946,6 +960,9 @@ static const struct of_device_id meson_sar_adc_of_match[] = { · 12 unmodified lines | |||
| 946 | }, { | 960 | }, { |
| 947 | .compatible = "amlogic,meson-gxm-saradc", | 961 | .compatible = "amlogic,meson-gxm-saradc", |
| 948 | .data = &meson_sar_adc_gxm_data, | 962 | .data = &meson_sar_adc_gxm_data, |
| 963 | }, { | ||
| 964 | .compatible = "amlogic,meson-axg-saradc", | ||
| 965 | .data = &meson_sar_adc_axg_data, | ||
| 949 | }, | 966 | }, |
| 950 | {}, | 967 | {}, |
| 951 | }; | 968 | }; |
| @@ -1001,7 +1018,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) · 49 unmodified lines | |||
| 1001 | return ret; | 1018 | return ret; |
| 1002 | 1019 | ||
| 1003 | priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, | 1020 | priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, |
| 1004 | priv->data->regmap_config); | 1021 | priv->data->param->regmap_config); |
| 1005 | if (IS_ERR(priv->regmap)) | 1022 | if (IS_ERR(priv->regmap)) |
| 1006 | return PTR_ERR(priv->regmap); | 1023 | return PTR_ERR(priv->regmap); |
| 1007 | 1024 | ||
| @@ -34,9 +34,6 @@ · 33 unmodified lines | |||
| 34 | #define STM32F4_ADC_ADCPRE_SHIFT 16 | 34 | #define STM32F4_ADC_ADCPRE_SHIFT 16 |
| 35 | #define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16) | 35 | #define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16) |
| 36 | 36 | ||
| 37 | /* STM32 F4 maximum analog clock rate (from datasheet) */ | ||
| 38 | #define STM32F4_ADC_MAX_CLK_RATE 36000000 | ||
| 39 | |||
| 40 | /* STM32H7 - common registers for all ADC instances */ | 37 | /* STM32H7 - common registers for all ADC instances */ |
| 41 | #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) | 38 | #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) |
| 42 | #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) | 39 | #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) |
| @@ -51,9 +48,6 @@ · 8 unmodified lines | |||
| 51 | #define STM32H7_CKMODE_SHIFT 16 | 48 | #define STM32H7_CKMODE_SHIFT 16 |
| 52 | #define STM32H7_CKMODE_MASK GENMASK(17, 16) | 49 | #define STM32H7_CKMODE_MASK GENMASK(17, 16) |
| 53 | 50 | ||
| 54 | /* STM32 H7 maximum analog clock rate (from datasheet) */ | ||
| 55 | #define STM32H7_ADC_MAX_CLK_RATE 36000000 | ||
| 56 | |||
| 57 | /** | 51 | /** |
| 58 | * stm32_adc_common_regs - stm32 common registers, compatible dependent data | 52 | * stm32_adc_common_regs - stm32 common registers, compatible dependent data |
| 59 | * @csr: common status register offset | 53 | * @csr: common status register offset |
| @@ -74,15 +68,17 @@ struct stm32_adc_priv; · 14 unmodified lines | |||
| 74 | * stm32_adc_priv_cfg - stm32 core compatible configuration data | 68 | * stm32_adc_priv_cfg - stm32 core compatible configuration data |
| 75 | * @regs: common registers for all instances | 69 | * @regs: common registers for all instances |
| 76 | * @clk_sel: clock selection routine | 70 | * @clk_sel: clock selection routine |
| 71 | * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) | ||
| 77 | */ | 72 | */ |
| 78 | struct stm32_adc_priv_cfg { | 73 | struct stm32_adc_priv_cfg { |
| 79 | const struct stm32_adc_common_regs *regs; | 74 | const struct stm32_adc_common_regs *regs; |
| 80 | int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); | 75 | int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); |
| 76 | u32 max_clk_rate_hz; | ||
| 81 | }; | 77 | }; |
| 82 | 78 | ||
| 83 | /** | 79 | /** |
| 84 | * struct stm32_adc_priv - stm32 ADC core private data | 80 | * struct stm32_adc_priv - stm32 ADC core private data |
| 85 | * @irq: irq for ADC block | 81 | * @irq: irq(s) for ADC block |
| 86 | * @domain: irq domain reference | 82 | * @domain: irq domain reference |
| 87 | * @aclk: clock reference for the analog circuitry | 83 | * @aclk: clock reference for the analog circuitry |
| 88 | * @bclk: bus clock common for all ADCs, depends on part used | 84 | * @bclk: bus clock common for all ADCs, depends on part used |
| @@ -91,7 +87,7 @@ struct stm32_adc_priv_cfg { · 2 unmodified lines | |||
| 91 | * @common: common data for all ADC instances | 87 | * @common: common data for all ADC instances |
| 92 | */ | 88 | */ |
| 93 | struct stm32_adc_priv { | 89 | struct stm32_adc_priv { |
| 94 | int irq; | 90 | int irq[STM32_ADC_MAX_ADCS]; |
| 95 | struct irq_domain *domain; | 91 | struct irq_domain *domain; |
| 96 | struct clk *aclk; | 92 | struct clk *aclk; |
| 97 | struct clk *bclk; | 93 | struct clk *bclk; |
| @@ -133,7 +129,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev, · 35 unmodified lines | |||
| 133 | } | 129 | } |
| 134 | 130 | ||
| 135 | for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) { | 131 | for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) { |
| 136 | if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE) | 132 | if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz) |
| 137 | break; | 133 | break; |
| 138 | } | 134 | } |
| 139 | if (i >= ARRAY_SIZE(stm32f4_pclk_div)) { | 135 | if (i >= ARRAY_SIZE(stm32f4_pclk_div)) { |
| @@ -222,7 +218,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev, · 82 unmodified lines | |||
| 222 | if (ckmode) | 218 | if (ckmode) |
| 223 | continue; | 219 | continue; |
| 224 | 220 | ||
| 225 | if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE) | 221 | if ((rate / div) <= priv->cfg->max_clk_rate_hz) |
| 226 | goto out; | 222 | goto out; |
| 227 | } | 223 | } |
| 228 | } | 224 | } |
| @@ -242,7 +238,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev, · 13 unmodified lines | |||
| 242 | if (!ckmode) | 238 | if (!ckmode) |
| 243 | continue; | 239 | continue; |
| 244 | 240 | ||
| 245 | if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE) | 241 | if ((rate / div) <= priv->cfg->max_clk_rate_hz) |
| 246 | goto out; | 242 | goto out; |
| 247 | } | 243 | } |
| 248 | 244 | ||
| @@ -328,11 +324,24 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, · 79 unmodified lines | |||
| 328 | struct stm32_adc_priv *priv) | 324 | struct stm32_adc_priv *priv) |
| 329 | { | 325 | { |
| 330 | struct device_node *np = pdev->dev.of_node; | 326 | struct device_node *np = pdev->dev.of_node; |
| 327 | unsigned int i; | ||
| 328 | |||
| 329 | for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { | ||
| 330 | priv->irq[i] = platform_get_irq(pdev, i); | ||
| 331 | if (priv->irq[i] < 0) { | ||
| 332 | /* | ||
| 333 | * At least one interrupt must be provided, make others | ||
| 334 | * optional: | ||
| 335 | * - stm32f4/h7 shares a common interrupt. | ||
| 336 | * - stm32mp1, has one line per ADC (either for ADC1, | ||
| 337 | * ADC2 or both). | ||
| 338 | */ | ||
| 339 | if (i && priv->irq[i] == -ENXIO) | ||
| 340 | continue; | ||
| 341 | dev_err(&pdev->dev, "failed to get irq\n"); | ||
| 331 | 342 | ||
| 332 | priv->irq = platform_get_irq(pdev, 0); | 343 | return priv->irq[i]; |
| 333 | if (priv->irq < 0) { | 344 | } |
| 334 | dev_err(&pdev->dev, "failed to get irq\n"); | ||
| 335 | return priv->irq; | ||
| 336 | } | 345 | } |
| 337 | 346 | ||
| 338 | priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0, | 347 | priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0, |
| @@ -343,8 +352,12 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, · 4 unmodified lines | |||
| 343 | return -ENOMEM; | 352 | return -ENOMEM; |
| 344 | } | 353 | } |
| 345 | 354 | ||
| 346 | irq_set_chained_handler(priv->irq, stm32_adc_irq_handler); | 355 | for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { |
| 347 | irq_set_handler_data(priv->irq, priv); | 356 | if (priv->irq[i] < 0) |
| 357 | continue; | ||
| 358 | irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler); | ||
| 359 | irq_set_handler_data(priv->irq[i], priv); | ||
| 360 | } | ||
| 348 | 361 | ||
| 349 | return 0; | 362 | return 0; |
| 350 | } | 363 | } |
| @@ -353,11 +366,17 @@ static void stm32_adc_irq_remove(struct platform_device *pdev, · 2 unmodified lines | |||
| 353 | struct stm32_adc_priv *priv) | 366 | struct stm32_adc_priv *priv) |
| 354 | { | 367 | { |
| 355 | int hwirq; | 368 | int hwirq; |
| 369 | unsigned int i; | ||
| 356 | 370 | ||
| 357 | for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++) | 371 | for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++) |
| 358 | irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); | 372 | irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); |
| 359 | irq_domain_remove(priv->domain); | 373 | irq_domain_remove(priv->domain); |
| 360 | irq_set_chained_handler(priv->irq, NULL); | 374 | |
| 375 | for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { | ||
| 376 | if (priv->irq[i] < 0) | ||
| 377 | continue; | ||
| 378 | irq_set_chained_handler(priv->irq[i], NULL); | ||
| 379 | } | ||
| 361 | } | 380 | } |
| 362 | 381 | ||
| 363 | static int stm32_adc_probe(struct platform_device *pdev) | 382 | static int stm32_adc_probe(struct platform_device *pdev) |
| @@ -497,11 +516,19 @@ static int stm32_adc_remove(struct platform_device *pdev) · 133 unmodified lines | |||
| 497 | static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { | 516 | static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { |
| 498 | .regs = &stm32f4_adc_common_regs, | 517 | .regs = &stm32f4_adc_common_regs, |
| 499 | .clk_sel = stm32f4_adc_clk_sel, | 518 | .clk_sel = stm32f4_adc_clk_sel, |
| 519 | .max_clk_rate_hz = 36000000, | ||
| 500 | }; | 520 | }; |
| 501 | 521 | ||
| 502 | static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { | 522 | static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { |
| 503 | .regs = &stm32h7_adc_common_regs, | 523 | .regs = &stm32h7_adc_common_regs, |
| 504 | .clk_sel = stm32h7_adc_clk_sel, | 524 | .clk_sel = stm32h7_adc_clk_sel, |
| 525 | .max_clk_rate_hz = 36000000, | ||
| 526 | }; | ||
| 527 | |||
| 528 | static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { | ||
| 529 | .regs = &stm32h7_adc_common_regs, | ||
| 530 | .clk_sel = stm32h7_adc_clk_sel, | ||
| 531 | .max_clk_rate_hz = 40000000, | ||
| 505 | }; | 532 | }; |
| 506 | 533 | ||
| 507 | static const struct of_device_id stm32_adc_of_match[] = { | 534 | static const struct of_device_id stm32_adc_of_match[] = { |
| @@ -511,6 +538,9 @@ static const struct of_device_id stm32_adc_of_match[] = { · 3 unmodified lines | |||
| 511 | }, { | 538 | }, { |
| 512 | .compatible = "st,stm32h7-adc-core", | 539 | .compatible = "st,stm32h7-adc-core", |
| 513 | .data = (void *)&stm32h7_adc_priv_cfg | 540 | .data = (void *)&stm32h7_adc_priv_cfg |
| 541 | }, { | ||
| 542 | .compatible = "st,stm32mp1-adc-core", | ||
| 543 | .data = (void *)&stm32mp1_adc_priv_cfg | ||
| 514 | }, { | 544 | }, { |
| 515 | }, | 545 | }, |
| 516 | }; | 546 | }; |
| @@ -84,6 +84,7 @@ · 83 unmodified lines | |||
| 84 | #define STM32H7_ADC_CALFACT2 0xC8 | 84 | #define STM32H7_ADC_CALFACT2 0xC8 |
| 85 | 85 | ||
| 86 | /* STM32H7_ADC_ISR - bit fields */ | 86 | /* STM32H7_ADC_ISR - bit fields */ |
| 87 | #define STM32MP1_VREGREADY BIT(12) | ||
| 87 | #define STM32H7_EOC BIT(2) | 88 | #define STM32H7_EOC BIT(2) |
| 88 | #define STM32H7_ADRDY BIT(0) | 89 | #define STM32H7_ADRDY BIT(0) |
| 89 | 90 | ||
| @@ -249,6 +250,7 @@ struct stm32_adc; · 159 unmodified lines | |||
| 249 | * @adc_info: per instance input channels definitions | 250 | * @adc_info: per instance input channels definitions |
| 250 | * @trigs: external trigger sources | 251 | * @trigs: external trigger sources |
| 251 | * @clk_required: clock is required | 252 | * @clk_required: clock is required |
| 253 | * @has_vregready: vregready status flag presence | ||
| 252 | * @selfcalib: optional routine for self-calibration | 254 | * @selfcalib: optional routine for self-calibration |
| 253 | * @prepare: optional prepare routine (power-up, enable) | 255 | * @prepare: optional prepare routine (power-up, enable) |
| 254 | * @start_conv: routine to start conversions | 256 | * @start_conv: routine to start conversions |
| @@ -261,6 +263,7 @@ struct stm32_adc_cfg { · 6 unmodified lines | |||
| 261 | const struct stm32_adc_info *adc_info; | 263 | const struct stm32_adc_info *adc_info; |
| 262 | struct stm32_adc_trig_info *trigs; | 264 | struct stm32_adc_trig_info *trigs; |
| 263 | bool clk_required; | 265 | bool clk_required; |
| 266 | bool has_vregready; | ||
| 264 | int (*selfcalib)(struct stm32_adc *); | 267 | int (*selfcalib)(struct stm32_adc *); |
| 265 | int (*prepare)(struct stm32_adc *); | 268 | int (*prepare)(struct stm32_adc *); |
| 266 | void (*start_conv)(struct stm32_adc *, bool dma); | 269 | void (*start_conv)(struct stm32_adc *, bool dma); |
| @@ -695,8 +698,12 @@ static void stm32h7_adc_stop_conv(struct stm32_adc *adc) · 428 unmodified lines | |||
| 695 | stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK); | 698 | stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK); |
| 696 | } | 699 | } |
| 697 | 700 | ||
| 698 | static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc) | 701 | static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc) |
| 699 | { | 702 | { |
| 703 | struct iio_dev *indio_dev = iio_priv_to_dev(adc); | ||
| 704 | int ret; | ||
| 705 | u32 val; | ||
| 706 | |||
| 700 | /* Exit deep power down, then enable ADC voltage regulator */ | 707 | /* Exit deep power down, then enable ADC voltage regulator */ |
| 701 | stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); | 708 | stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); |
| 702 | stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN); | 709 | stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN); |
| @@ -705,7 +712,20 @@ static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc) · 2 unmodified lines | |||
| 705 | stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); | 712 | stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); |
| 706 | 713 | ||
| 707 | /* Wait for startup time */ | 714 | /* Wait for startup time */ |
| 708 | usleep_range(10, 20); | 715 | if (!adc->cfg->has_vregready) { |
| 716 | usleep_range(10, 20); | ||
| 717 | return 0; | ||
| 718 | } | ||
| 719 | |||
| 720 | ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val, | ||
| 721 | val & STM32MP1_VREGREADY, 100, | ||
| 722 | STM32_ADC_TIMEOUT_US); | ||
| 723 | if (ret) { | ||
| 724 | stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); | ||
| 725 | dev_err(&indio_dev->dev, "Failed to exit power down\n"); | ||
| 726 | } | ||
| 727 | |||
| 728 | return ret; | ||
| 709 | } | 729 | } |
| 710 | 730 | ||
| 711 | static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc) | 731 | static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc) |
| @@ -888,7 +908,9 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc) · 176 unmodified lines | |||
| 888 | int ret; | 908 | int ret; |
| 889 | u32 val; | 909 | u32 val; |
| 890 | 910 | ||
| 891 | stm32h7_adc_exit_pwr_down(adc); | 911 | ret = stm32h7_adc_exit_pwr_down(adc); |
| 912 | if (ret) | ||
| 913 | return ret; | ||
| 892 | 914 | ||
| 893 | /* | 915 | /* |
| 894 | * Select calibration mode: | 916 | * Select calibration mode: |
| @@ -952,7 +974,10 @@ static int stm32h7_adc_prepare(struct stm32_adc *adc) · 57 unmodified lines | |||
| 952 | { | 974 | { |
| 953 | int ret; | 975 | int ret; |
| 954 | 976 | ||
| 955 | stm32h7_adc_exit_pwr_down(adc); | 977 | ret = stm32h7_adc_exit_pwr_down(adc); |
| 978 | if (ret) | ||
| 979 | return ret; | ||
| 980 | |||
| 956 | stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); | 981 | stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); |
| 957 | 982 | ||
| 958 | ret = stm32h7_adc_enable(adc); | 983 | ret = stm32h7_adc_enable(adc); |
| @@ -1944,9 +1969,23 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { · 985 unmodified lines | |||
| 1944 | .smp_cycles = stm32h7_adc_smp_cycles, | 1969 | .smp_cycles = stm32h7_adc_smp_cycles, |
| 1945 | }; | 1970 | }; |
| 1946 | 1971 | ||
| 1972 | static const struct stm32_adc_cfg stm32mp1_adc_cfg = { | ||
| 1973 | .regs = &stm32h7_adc_regspec, | ||
| 1974 | .adc_info = &stm32h7_adc_info, | ||
| 1975 | .trigs = stm32h7_adc_trigs, | ||
| 1976 | .has_vregready = true, | ||
| 1977 | .selfcalib = stm32h7_adc_selfcalib, | ||
| 1978 | .start_conv = stm32h7_adc_start_conv, | ||
| 1979 | .stop_conv = stm32h7_adc_stop_conv, | ||
| 1980 | .prepare = stm32h7_adc_prepare, | ||
| 1981 | .unprepare = stm32h7_adc_unprepare, | ||
| 1982 | .smp_cycles = stm32h7_adc_smp_cycles, | ||
| 1983 | }; | ||
| 1984 | |||
| 1947 | static const struct of_device_id stm32_adc_of_match[] = { | 1985 | static const struct of_device_id stm32_adc_of_match[] = { |
| 1948 | { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg }, | 1986 | { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg }, |
| 1949 | { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg }, | 1987 | { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg }, |
| 1988 | { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg }, | ||
| 1950 | {}, | 1989 | {}, |
| 1951 | }; | 1990 | }; |
| 1952 | MODULE_DEVICE_TABLE(of, stm32_adc_of_match); | 1991 | MODULE_DEVICE_TABLE(of, stm32_adc_of_match); |
| @@ -8,11 +8,11 @@ · 7 unmodified lines | |||
| 8 | 8 | ||
| 9 | #include <linux/dmaengine.h> | 9 | #include <linux/dmaengine.h> |
| 10 | #include <linux/dma-mapping.h> | 10 | #include <linux/dma-mapping.h> |
| 11 | #include <linux/interrupt.h> | 11 | #include <linux/iio/adc/stm32-dfsdm-adc.h> |
| 12 | #include <linux/iio/buffer.h> | 12 | #include <linux/iio/buffer.h> |
| 13 | #include <linux/iio/hw-consumer.h> | 13 | #include <linux/iio/hw-consumer.h> |
| 14 | #include <linux/iio/iio.h> | ||
| 15 | #include <linux/iio/sysfs.h> | 14 | #include <linux/iio/sysfs.h> |
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
| 17 | #include <linux/of_device.h> | 17 | #include <linux/of_device.h> |
| 18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
| @@ -254,7 +254,8 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, · 235 unmodified lines | |||
| 254 | DFSDM_CR1_RSWSTART(1)); | 254 | DFSDM_CR1_RSWSTART(1)); |
| 255 | } | 255 | } |
| 256 | 256 | ||
| 257 | static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id) | 257 | static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, |
| 258 | unsigned int fl_id) | ||
| 258 | { | 259 | { |
| 259 | /* Disable conversion */ | 260 | /* Disable conversion */ |
| 260 | regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), | 261 | regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), |
| @@ -338,7 +339,7 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, · 77 unmodified lines | |||
| 338 | "st,adc-channel-types", chan_idx, | 339 | "st,adc-channel-types", chan_idx, |
| 339 | &of_str); | 340 | &of_str); |
| 340 | if (!ret) { | 341 | if (!ret) { |
| 341 | val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); | 342 | val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); |
| 342 | if (val < 0) | 343 | if (val < 0) |
| 343 | return val; | 344 | return val; |
| 344 | } else { | 345 | } else { |
| @@ -350,7 +351,7 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, · 5 unmodified lines | |||
| 350 | "st,adc-channel-clk-src", chan_idx, | 351 | "st,adc-channel-clk-src", chan_idx, |
| 351 | &of_str); | 352 | &of_str); |
| 352 | if (!ret) { | 353 | if (!ret) { |
| 353 | val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); | 354 | val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); |
| 354 | if (val < 0) | 355 | if (val < 0) |
| 355 | return val; | 356 | return val; |
| 356 | } else { | 357 | } else { |
| @@ -1104,7 +1105,6 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) · 747 unmodified lines | |||
| 1104 | char *name; | 1105 | char *name; |
| 1105 | int ret, irq, val; | 1106 | int ret, irq, val; |
| 1106 | 1107 | ||
| 1107 | |||
| 1108 | dev_data = of_device_get_match_data(dev); | 1108 | dev_data = of_device_get_match_data(dev); |
| 1109 | iio = devm_iio_device_alloc(dev, sizeof(*adc)); | 1109 | iio = devm_iio_device_alloc(dev, sizeof(*adc)); |
| 1110 | if (!iio) { | 1110 | if (!iio) { |
| @@ -1122,8 +1122,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) · 11 unmodified lines | |||
| 1122 | platform_set_drvdata(pdev, adc); | 1122 | platform_set_drvdata(pdev, adc); |
| 1123 | 1123 | ||
| 1124 | ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id); | 1124 | ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id); |
| 1125 | if (ret != 0) { | 1125 | if (ret != 0 || adc->fl_id >= adc->dfsdm->num_fls) { |
| 1126 | dev_err(dev, "Missing reg property\n"); | 1126 | dev_err(dev, "Missing or bad reg property\n"); |
| 1127 | return -EINVAL; | 1127 | return -EINVAL; |
| 1128 | } | 1128 | } |
| 1129 | 1129 | ||
| @@ -1172,7 +1172,6 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) · 42 unmodified lines | |||
| 1172 | if (ret < 0) | 1172 | if (ret < 0) |
| 1173 | goto err_cleanup; | 1173 | goto err_cleanup; |
| 1174 | 1174 | ||
| 1175 | dev_err(dev, "of_platform_populate\n"); | ||
| 1176 | if (dev_data->type == DFSDM_AUDIO) { | 1175 | if (dev_data->type == DFSDM_AUDIO) { |
| 1177 | ret = of_platform_populate(np, NULL, NULL, dev); | 1176 | ret = of_platform_populate(np, NULL, NULL, dev); |
| 1178 | if (ret < 0) { | 1177 | if (ret < 0) { |
| @@ -25,6 +25,8 @@ struct stm32_dfsdm_dev_data { · 24 unmodified lines | |||
| 25 | 25 | ||
| 26 | #define STM32H7_DFSDM_NUM_FILTERS 4 | 26 | #define STM32H7_DFSDM_NUM_FILTERS 4 |
| 27 | #define STM32H7_DFSDM_NUM_CHANNELS 8 | 27 | #define STM32H7_DFSDM_NUM_CHANNELS 8 |
| 28 | #define STM32MP1_DFSDM_NUM_FILTERS 6 | ||
| 29 | #define STM32MP1_DFSDM_NUM_CHANNELS 8 | ||
| 28 | 30 | ||
| 29 | static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) | 31 | static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) |
| 30 | { | 32 | { |
| @@ -61,6 +63,21 @@ static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = { · 30 unmodified lines | |||
| 61 | .regmap_cfg = &stm32h7_dfsdm_regmap_cfg, | 63 | .regmap_cfg = &stm32h7_dfsdm_regmap_cfg, |
| 62 | }; | 64 | }; |
| 63 | 65 | ||
| 66 | static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = { | ||
| 67 | .reg_bits = 32, | ||
| 68 | .val_bits = 32, | ||
| 69 | .reg_stride = sizeof(u32), | ||
| 70 | .max_register = 0x7fc, | ||
| 71 | .volatile_reg = stm32_dfsdm_volatile_reg, | ||
| 72 | .fast_io = true, | ||
| 73 | }; | ||
| 74 | |||
| 75 | static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = { | ||
| 76 | .num_filters = STM32MP1_DFSDM_NUM_FILTERS, | ||
| 77 | .num_channels = STM32MP1_DFSDM_NUM_CHANNELS, | ||
| 78 | .regmap_cfg = &stm32mp1_dfsdm_regmap_cfg, | ||
| 79 | }; | ||
| 80 | |||
| 64 | struct dfsdm_priv { | 81 | struct dfsdm_priv { |
| 65 | struct platform_device *pdev; /* platform device */ | 82 | struct platform_device *pdev; /* platform device */ |
| 66 | 83 | ||
| @@ -227,6 +244,11 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev, · 160 unmodified lines | |||
| 227 | } | 244 | } |
| 228 | 245 | ||
| 229 | priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1; | 246 | priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1; |
| 247 | if (!priv->spi_clk_out_div) { | ||
| 248 | /* spi_clk_out_div == 0 means ckout is OFF */ | ||
| 249 | dev_err(&pdev->dev, "spi-max-frequency not achievable\n"); | ||
| 250 | return -EINVAL; | ||
| 251 | } | ||
| 230 | priv->dfsdm.spi_master_freq = spi_freq; | 252 | priv->dfsdm.spi_master_freq = spi_freq; |
| 231 | 253 | ||
| 232 | if (rem) { | 254 | if (rem) { |
| @@ -243,6 +265,10 @@ static const struct of_device_id stm32_dfsdm_of_match[] = { · 10 unmodified lines | |||
| 243 | .compatible = "st,stm32h7-dfsdm", | 265 | .compatible = "st,stm32h7-dfsdm", |
| 244 | .data = &stm32h7_dfsdm_data, | 266 | .data = &stm32h7_dfsdm_data, |
| 245 | }, | 267 | }, |
| 268 | { | ||
| 269 | .compatible = "st,stm32mp1-dfsdm", | ||
| 270 | .data = &stm32mp1_dfsdm_data, | ||
| 271 | }, | ||
| 246 | {} | 272 | {} |
| 247 | }; | 273 | }; |
| 248 | MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); | 274 | MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); |
| @@ -233,6 +233,16 @@ static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) · 232 unmodified lines | |||
| 233 | return !!(inb(stx104gpio->base) & BIT(offset)); | 233 | return !!(inb(stx104gpio->base) & BIT(offset)); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, | ||
| 237 | unsigned long *bits) | ||
| 238 | { | ||
| 239 | struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); | ||
| 240 | |||
| 241 | *bits = inb(stx104gpio->base); | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 236 | static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, | 246 | static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, |
| 237 | int value) | 247 | int value) |
| 238 | { | 248 | { |
| @@ -342,6 +352,7 @@ static int stx104_probe(struct device *dev, unsigned int id) · 103 unmodified lines | |||
| 342 | stx104gpio->chip.direction_input = stx104_gpio_direction_input; | 352 | stx104gpio->chip.direction_input = stx104_gpio_direction_input; |
| 343 | stx104gpio->chip.direction_output = stx104_gpio_direction_output; | 353 | stx104gpio->chip.direction_output = stx104_gpio_direction_output; |
| 344 | stx104gpio->chip.get = stx104_gpio_get; | 354 | stx104gpio->chip.get = stx104_gpio_get; |
| 355 | stx104gpio->chip.get_multiple = stx104_gpio_get_multiple; | ||
| 345 | stx104gpio->chip.set = stx104_gpio_set; | 356 | stx104gpio->chip.set = stx104_gpio_set; |
| 346 | stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; | 357 | stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; |
| 347 | stx104gpio->base = base[id] + 3; | 358 | stx104gpio->base = base[id] + 3; |
| @@ -17,6 +17,9 @@ · 16 unmodified lines | |||
| 17 | #include <linux/of.h> | 17 | #include <linux/of.h> |
| 18 | 18 | ||
| 19 | #include <linux/iio/iio.h> | 19 | #include <linux/iio/iio.h> |
| 20 | #include <linux/iio/buffer.h> | ||
| 21 | #include <linux/iio/trigger_consumer.h> | ||
| 22 | #include <linux/iio/triggered_buffer.h> | ||
| 20 | #include <linux/iio/sysfs.h> | 23 | #include <linux/iio/sysfs.h> |
| 21 | 24 | ||
| 22 | #define ADS8688_CMD_REG(x) (x << 8) | 25 | #define ADS8688_CMD_REG(x) (x << 8) |
| @@ -155,6 +158,13 @@ static const struct attribute_group ads8688_attribute_group = { · 132 unmodified lines | |||
| 155 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ | 158 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ |
| 156 | | BIT(IIO_CHAN_INFO_SCALE) \ | 159 | | BIT(IIO_CHAN_INFO_SCALE) \ |
| 157 | | BIT(IIO_CHAN_INFO_OFFSET), \ | 160 | | BIT(IIO_CHAN_INFO_OFFSET), \ |
| 161 | .scan_index = index, \ | ||
| 162 | .scan_type = { \ | ||
| 163 | .sign = 'u', \ | ||
| 164 | .realbits = 16, \ | ||
| 165 | .storagebits = 16, \ | ||
| 166 | .endianness = IIO_BE, \ | ||
| 167 | }, \ | ||
| 158 | } | 168 | } |
| 159 | 169 | ||
| 160 | static const struct iio_chan_spec ads8684_channels[] = { | 170 | static const struct iio_chan_spec ads8684_channels[] = { |
| @@ -371,6 +381,28 @@ static const struct iio_info ads8688_info = { · 210 unmodified lines | |||
| 371 | .attrs = &ads8688_attribute_group, | 381 | .attrs = &ads8688_attribute_group, |
| 372 | }; | 382 | }; |
| 373 | 383 | ||
| 384 | static irqreturn_t ads8688_trigger_handler(int irq, void *p) | ||
| 385 | { | ||
| 386 | struct iio_poll_func *pf = p; | ||
| 387 | struct iio_dev *indio_dev = pf->indio_dev; | ||
| 388 | u16 buffer[8]; | ||
| 389 | int i, j = 0; | ||
| 390 | |||
| 391 | for (i = 0; i < indio_dev->masklength; i++) { | ||
| 392 | if (!test_bit(i, indio_dev->active_scan_mask)) | ||
| 393 | continue; | ||
| 394 | buffer[j] = ads8688_read(indio_dev, i); | ||
| 395 | j++; | ||
| 396 | } | ||
| 397 | |||
| 398 | iio_push_to_buffers_with_timestamp(indio_dev, buffer, | ||
| 399 | pf->timestamp); | ||
| 400 | |||
| 401 | iio_trigger_notify_done(indio_dev->trig); | ||
| 402 | |||
| 403 | return IRQ_HANDLED; | ||
| 404 | } | ||
| 405 | |||
| 374 | static const struct ads8688_chip_info ads8688_chip_info_tbl[] = { | 406 | static const struct ads8688_chip_info ads8688_chip_info_tbl[] = { |
| 375 | [ID_ADS8684] = { | 407 | [ID_ADS8684] = { |
| 376 | .channels = ads8684_channels, | 408 | .channels = ads8684_channels, |
| @@ -402,7 +434,7 @@ static int ads8688_probe(struct spi_device *spi) · 25 unmodified lines | |||
| 402 | 434 | ||
| 403 | ret = regulator_get_voltage(st->reg); | 435 | ret = regulator_get_voltage(st->reg); |
| 404 | if (ret < 0) | 436 | if (ret < 0) |
| 405 | goto error_out; | 437 | goto err_regulator_disable; |
| 406 | 438 | ||
| 407 | st->vref_mv = ret / 1000; | 439 | st->vref_mv = ret / 1000; |
| 408 | } else { | 440 | } else { |
| @@ -430,13 +462,22 @@ static int ads8688_probe(struct spi_device *spi) · 21 unmodified lines | |||
| 430 | 462 | ||
| 431 | mutex_init(&st->lock); | 463 | mutex_init(&st->lock); |
| 432 | 464 | ||
| 465 | ret = iio_triggered_buffer_setup(indio_dev, NULL, ads8688_trigger_handler, NULL); | ||
| 466 | if (ret < 0) { | ||
| 467 | dev_err(&spi->dev, "iio triggered buffer setup failed\n"); | ||
| 468 | goto err_regulator_disable; | ||
| 469 | } | ||
| 470 | |||
| 433 | ret = iio_device_register(indio_dev); | 471 | ret = iio_device_register(indio_dev); |
| 434 | if (ret) | 472 | if (ret) |
| 435 | goto error_out; | 473 | goto err_buffer_cleanup; |
| 436 | 474 | ||
| 437 | return 0; | 475 | return 0; |
| 438 | 476 | ||
| 439 | error_out: | 477 | err_buffer_cleanup: |
| 478 | iio_triggered_buffer_cleanup(indio_dev); | ||
| 479 | |||
| 480 | err_regulator_disable: | ||
| 440 | if (!IS_ERR(st->reg)) | 481 | if (!IS_ERR(st->reg)) |
| 441 | regulator_disable(st->reg); | 482 | regulator_disable(st->reg); |
| 442 | 483 | ||
| @@ -449,6 +490,7 @@ static int ads8688_remove(struct spi_device *spi) · 6 unmodified lines | |||
| 449 | struct ads8688_state *st = iio_priv(indio_dev); | 490 | struct ads8688_state *st = iio_priv(indio_dev); |
| 450 | 491 | ||
| 451 | iio_device_unregister(indio_dev); | 492 | iio_device_unregister(indio_dev); |
| 493 | iio_triggered_buffer_cleanup(indio_dev); | ||
| 452 | 494 | ||
| 453 | if (!IS_ERR(st->reg)) | 495 | if (!IS_ERR(st->reg)) |
| 454 | regulator_disable(st->reg); | 496 | regulator_disable(st->reg); |
| @@ -0,0 +1,19 @@ | |||
| 1 | # | ||
| 2 | # Analog Front End drivers | ||
| 3 | # | ||
| 4 | # When adding new entries keep the list in alphabetical order | ||
| 5 | |||
| 6 | menu "Analog Front Ends" | ||
| 7 | |||
| 8 | config IIO_RESCALE | ||
| 9 | tristate "IIO rescale" | ||
| 10 | depends on OF || COMPILE_TEST | ||
| 11 | help | ||
| 12 | Say yes here to build support for the IIO rescaling | ||
| 13 | that handles voltage dividers, current sense shunts and | ||
| 14 | current sense amplifiers. | ||
| 15 | |||
| 16 | To compile this driver as a module, choose M here: the | ||
| 17 | module will be called iio-rescale. | ||
| 18 | |||
| 19 | endmenu | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | # | ||
| 2 | # Makefile for industrial I/O Analog Front Ends (AFE) | ||
| 3 | # | ||
| 4 | |||
| 5 | # When adding new entries keep the list in alphabetical order | ||
| 6 | obj-$(CONFIG_IIO_RESCALE) += iio-rescale.o | ||
| @@ -0,0 +1,359 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * IIO rescale driver | ||
| 4 | * | ||
| 5 | * Copyright (C) 2018 Axentia Technologies AB | ||
| 6 | * | ||
| 7 | * Author: Peter Rosin <peda@axentia.se> | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/err.h> | ||
| 11 | #include <linux/gcd.h> | ||
| 12 | #include <linux/iio/consumer.h> | ||
| 13 | #include <linux/iio/iio.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/of.h> | ||
| 16 | #include <linux/of_device.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/property.h> | ||
| 19 | |||
| 20 | struct rescale; | ||
| 21 | |||
| 22 | struct rescale_cfg { | ||
| 23 | enum iio_chan_type type; | ||
| 24 | int (*props)(struct device *dev, struct rescale *rescale); | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct rescale { | ||
| 28 | const struct rescale_cfg *cfg; | ||
| 29 | struct iio_channel *source; | ||
| 30 | struct iio_chan_spec chan; | ||
| 31 | struct iio_chan_spec_ext_info *ext_info; | ||
| 32 | s32 numerator; | ||
| 33 | s32 denominator; | ||
| 34 | }; | ||
| 35 | |||
| 36 | static int rescale_read_raw(struct iio_dev *indio_dev, | ||
| 37 | struct iio_chan_spec const *chan, | ||
| 38 | int *val, int *val2, long mask) | ||
| 39 | { | ||
| 40 | struct rescale *rescale = iio_priv(indio_dev); | ||
| 41 | unsigned long long tmp; | ||
| 42 | int ret; | ||
| 43 | |||
| 44 | switch (mask) { | ||
| 45 | case IIO_CHAN_INFO_RAW: | ||
| 46 | return iio_read_channel_raw(rescale->source, val); | ||
| 47 | |||
| 48 | case IIO_CHAN_INFO_SCALE: | ||
| 49 | ret = iio_read_channel_scale(rescale->source, val, val2); | ||
| 50 | switch (ret) { | ||
| 51 | case IIO_VAL_FRACTIONAL: | ||
| 52 | *val *= rescale->numerator; | ||
| 53 | *val2 *= rescale->denominator; | ||
| 54 | return ret; | ||
| 55 | case IIO_VAL_INT: | ||
| 56 | *val *= rescale->numerator; | ||
| 57 | if (rescale->denominator == 1) | ||
| 58 | return ret; | ||
| 59 | *val2 = rescale->denominator; | ||
| 60 | return IIO_VAL_FRACTIONAL; | ||
| 61 | case IIO_VAL_FRACTIONAL_LOG2: | ||
| 62 | tmp = *val * 1000000000LL; | ||
| 63 | do_div(tmp, rescale->denominator); | ||
| 64 | tmp *= rescale->numerator; | ||
| 65 | do_div(tmp, 1000000000LL); | ||
| 66 | *val = tmp; | ||
| 67 | return ret; | ||
| 68 | default: | ||
| 69 | return -EOPNOTSUPP; | ||
| 70 | } | ||
| 71 | default: | ||
| 72 | return -EINVAL; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | static int rescale_read_avail(struct iio_dev *indio_dev, | ||
| 77 | struct iio_chan_spec const *chan, | ||
| 78 | const int **vals, int *type, int *length, | ||
| 79 | long mask) | ||
| 80 | { | ||
| 81 | struct rescale *rescale = iio_priv(indio_dev); | ||
| 82 | |||
| 83 | switch (mask) { | ||
| 84 | case IIO_CHAN_INFO_RAW: | ||
| 85 | *type = IIO_VAL_INT; | ||
| 86 | return iio_read_avail_channel_raw(rescale->source, | ||
| 87 | vals, length); | ||
| 88 | default: | ||
| 89 | return -EINVAL; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | static const struct iio_info rescale_info = { | ||
| 94 | .read_raw = rescale_read_raw, | ||
| 95 | .read_avail = rescale_read_avail, | ||
| 96 | }; | ||
| 97 | |||
| 98 | static ssize_t rescale_read_ext_info(struct iio_dev *indio_dev, | ||
| 99 | uintptr_t private, | ||
| 100 | struct iio_chan_spec const *chan, | ||
| 101 | char *buf) | ||
| 102 | { | ||
| 103 | struct rescale *rescale = iio_priv(indio_dev); | ||
| 104 | |||
| 105 | return iio_read_channel_ext_info(rescale->source, | ||
| 106 | rescale->ext_info[private].name, | ||
| 107 | buf); | ||
| 108 | } | ||
| 109 | |||
| 110 | static ssize_t rescale_write_ext_info(struct iio_dev *indio_dev, | ||
| 111 | uintptr_t private, | ||
| 112 | struct iio_chan_spec const *chan, | ||
| 113 | const char *buf, size_t len) | ||
| 114 | { | ||
| 115 | struct rescale *rescale = iio_priv(indio_dev); | ||
| 116 | |||
| 117 | return iio_write_channel_ext_info(rescale->source, | ||
| 118 | rescale->ext_info[private].name, | ||
| 119 | buf, len); | ||
| 120 | } | ||
| 121 | |||
| 122 | static int rescale_configure_channel(struct device *dev, | ||
| 123 | struct rescale *rescale) | ||
| 124 | { | ||
| 125 | struct iio_chan_spec *chan = &rescale->chan; | ||
| 126 | struct iio_chan_spec const *schan = rescale->source->channel; | ||
| 127 | |||
| 128 | chan->indexed = 1; | ||
| 129 | chan->output = schan->output; | ||
| 130 | chan->ext_info = rescale->ext_info; | ||
| 131 | chan->type = rescale->cfg->type; | ||
| 132 | |||
| 133 | if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) || | ||
| 134 | !iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) { | ||
| 135 | dev_err(dev, "source channel does not support raw/scale\n"); | ||
| 136 | return -EINVAL; | ||
| 137 | } | ||
| 138 | |||
| 139 | chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 140 | BIT(IIO_CHAN_INFO_SCALE); | ||
| 141 | |||
| 142 | if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW)) | ||
| 143 | chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); | ||
| 144 | |||
| 145 | return 0; | ||
| 146 | } | ||
| 147 | |||
| 148 | static int rescale_current_sense_amplifier_props(struct device *dev, | ||
| 149 | struct rescale *rescale) | ||
| 150 | { | ||
| 151 | u32 sense; | ||
| 152 | u32 gain_mult = 1; | ||
| 153 | u32 gain_div = 1; | ||
| 154 | u32 factor; | ||
| 155 | int ret; | ||
| 156 | |||
| 157 | ret = device_property_read_u32(dev, "sense-resistor-micro-ohms", | ||
| 158 | &sense); | ||
| 159 | if (ret) { | ||
| 160 | dev_err(dev, "failed to read the sense resistance: %d\n", ret); | ||
| 161 | return ret; | ||
| 162 | } | ||
| 163 | |||
| 164 | device_property_read_u32(dev, "sense-gain-mult", &gain_mult); | ||
| 165 | device_property_read_u32(dev, "sense-gain-div", &gain_div); | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Calculate the scaling factor, 1 / (gain * sense), or | ||
| 169 | * gain_div / (gain_mult * sense), while trying to keep the | ||
| 170 | * numerator/denominator from overflowing. | ||
| 171 | */ | ||
| 172 | factor = gcd(sense, 1000000); | ||
| 173 | rescale->numerator = 1000000 / factor; | ||
| 174 | rescale->denominator = sense / factor; | ||
| 175 | |||
| 176 | factor = gcd(rescale->numerator, gain_mult); | ||
| 177 | rescale->numerator /= factor; | ||
| 178 | rescale->denominator *= gain_mult / factor; | ||
| 179 | |||
| 180 | factor = gcd(rescale->denominator, gain_div); | ||
| 181 | rescale->numerator *= gain_div / factor; | ||
| 182 | rescale->denominator /= factor; | ||
| 183 | |||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | static int rescale_current_sense_shunt_props(struct device *dev, | ||
| 188 | struct rescale *rescale) | ||
| 189 | { | ||
| 190 | u32 shunt; | ||
| 191 | u32 factor; | ||
| 192 | int ret; | ||
| 193 | |||
| 194 | ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", | ||
| 195 | &shunt); | ||
| 196 | if (ret) { | ||
| 197 | dev_err(dev, "failed to read the shunt resistance: %d\n", ret); | ||
| 198 | return ret; | ||
| 199 | } | ||
| 200 | |||
| 201 | factor = gcd(shunt, 1000000); | ||
| 202 | rescale->numerator = 1000000 / factor; | ||
| 203 | rescale->denominator = shunt / factor; | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | static int rescale_voltage_divider_props(struct device *dev, | ||
| 209 | struct rescale *rescale) | ||
| 210 | { | ||
| 211 | int ret; | ||
| 212 | u32 factor; | ||
| 213 | |||
| 214 | ret = device_property_read_u32(dev, "output-ohms", | ||
| 215 | &rescale->denominator); | ||
| 216 | if (ret) { | ||
| 217 | dev_err(dev, "failed to read output-ohms: %d\n", ret); | ||
| 218 | return ret; | ||
| 219 | } | ||
| 220 | |||
| 221 | ret = device_property_read_u32(dev, "full-ohms", | ||
| 222 | &rescale->numerator); | ||
| 223 | if (ret) { | ||
| 224 | dev_err(dev, "failed to read full-ohms: %d\n", ret); | ||
| 225 | return ret; | ||
| 226 | } | ||
| 227 | |||
| 228 | factor = gcd(rescale->numerator, rescale->denominator); | ||
| 229 | rescale->numerator /= factor; | ||
| 230 | rescale->denominator /= factor; | ||
| 231 | |||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | enum rescale_variant { | ||
| 236 | CURRENT_SENSE_AMPLIFIER, | ||
| 237 | CURRENT_SENSE_SHUNT, | ||
| 238 | VOLTAGE_DIVIDER, | ||
| 239 | }; | ||
| 240 | |||
| 241 | static const struct rescale_cfg rescale_cfg[] = { | ||
| 242 | [CURRENT_SENSE_AMPLIFIER] = { | ||
| 243 | .type = IIO_CURRENT, | ||
| 244 | .props = rescale_current_sense_amplifier_props, | ||
| 245 | }, | ||
| 246 | [CURRENT_SENSE_SHUNT] = { | ||
| 247 | .type = IIO_CURRENT, | ||
| 248 | .props = rescale_current_sense_shunt_props, | ||
| 249 | }, | ||
| 250 | [VOLTAGE_DIVIDER] = { | ||
| 251 | .type = IIO_VOLTAGE, | ||
| 252 | .props = rescale_voltage_divider_props, | ||
| 253 | }, | ||
| 254 | }; | ||
| 255 | |||
| 256 | static const struct of_device_id rescale_match[] = { | ||
| 257 | { .compatible = "current-sense-amplifier", | ||
| 258 | .data = &rescale_cfg[CURRENT_SENSE_AMPLIFIER], }, | ||
| 259 | { .compatible = "current-sense-shunt", | ||
| 260 | .data = &rescale_cfg[CURRENT_SENSE_SHUNT], }, | ||
| 261 | { .compatible = "voltage-divider", | ||
| 262 | .data = &rescale_cfg[VOLTAGE_DIVIDER], }, | ||
| 263 | { /* sentinel */ } | ||
| 264 | }; | ||
| 265 | MODULE_DEVICE_TABLE(of, rescale_match); | ||
| 266 | |||
| 267 | static int rescale_probe(struct platform_device *pdev) | ||
| 268 | { | ||
| 269 | struct device *dev = &pdev->dev; | ||
| 270 | struct iio_dev *indio_dev; | ||
| 271 | struct iio_channel *source; | ||
| 272 | struct rescale *rescale; | ||
| 273 | int sizeof_ext_info; | ||
| 274 | int sizeof_priv; | ||
| 275 | int i; | ||
| 276 | int ret; | ||
| 277 | |||
| 278 | source = devm_iio_channel_get(dev, NULL); | ||
| 279 | if (IS_ERR(source)) { | ||
| 280 | if (PTR_ERR(source) != -EPROBE_DEFER) | ||
| 281 | dev_err(dev, "failed to get source channel\n"); | ||
| 282 | return PTR_ERR(source); | ||
| 283 | } | ||
| 284 | |||
| 285 | sizeof_ext_info = iio_get_channel_ext_info_count(source); | ||
| 286 | if (sizeof_ext_info) { | ||
| 287 | sizeof_ext_info += 1; /* one extra entry for the sentinel */ | ||
| 288 | sizeof_ext_info *= sizeof(*rescale->ext_info); | ||
| 289 | } | ||
| 290 | |||
| 291 | sizeof_priv = sizeof(*rescale) + sizeof_ext_info; | ||
| 292 | |||
| 293 | indio_dev = devm_iio_device_alloc(dev, sizeof_priv); | ||
| 294 | if (!indio_dev) | ||
| 295 | return -ENOMEM; | ||
| 296 | |||
| 297 | rescale = iio_priv(indio_dev); | ||
| 298 | |||
| 299 | rescale->cfg = of_device_get_match_data(dev); | ||
| 300 | rescale->numerator = 1; | ||
| 301 | rescale->denominator = 1; | ||
| 302 | |||
| 303 | ret = rescale->cfg->props(dev, rescale); | ||
| 304 | if (ret) | ||
| 305 | return ret; | ||
| 306 | |||
| 307 | if (!rescale->numerator || !rescale->denominator) { | ||
| 308 | dev_err(dev, "invalid scaling factor.\n"); | ||
| 309 | return -EINVAL; | ||
| 310 | } | ||
| 311 | |||
| 312 | platform_set_drvdata(pdev, indio_dev); | ||
| 313 | |||
| 314 | rescale->source = source; | ||
| 315 | |||
| 316 | indio_dev->name = dev_name(dev); | ||
| 317 | indio_dev->dev.parent = dev; | ||
| 318 | indio_dev->info = &rescale_info; | ||
| 319 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 320 | indio_dev->channels = &rescale->chan; | ||
| 321 | indio_dev->num_channels = 1; | ||
| 322 | if (sizeof_ext_info) { | ||
| 323 | rescale->ext_info = devm_kmemdup(dev, | ||
| 324 | source->channel->ext_info, | ||
| 325 | sizeof_ext_info, GFP_KERNEL); | ||
| 326 | if (!rescale->ext_info) | ||
| 327 | return -ENOMEM; | ||
| 328 | |||
| 329 | for (i = 0; rescale->ext_info[i].name; ++i) { | ||
| 330 | struct iio_chan_spec_ext_info *ext_info = | ||
| 331 | &rescale->ext_info[i]; | ||
| 332 | |||
| 333 | if (source->channel->ext_info[i].read) | ||
| 334 | ext_info->read = rescale_read_ext_info; | ||
| 335 | if (source->channel->ext_info[i].write) | ||
| 336 | ext_info->write = rescale_write_ext_info; | ||
| 337 | ext_info->private = i; | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | ret = rescale_configure_channel(dev, rescale); | ||
| 342 | if (ret) | ||
| 343 | return ret; | ||
| 344 | |||
| 345 | return devm_iio_device_register(dev, indio_dev); | ||
| 346 | } | ||
| 347 | |||
| 348 | static struct platform_driver rescale_driver = { | ||
| 349 | .probe = rescale_probe, | ||
| 350 | .driver = { | ||
| 351 | .name = "iio-rescale", | ||
| 352 | .of_match_table = rescale_match, | ||
| 353 | }, | ||
| 354 | }; | ||
| 355 | module_platform_driver(rescale_driver); | ||
| 356 | |||
| 357 | MODULE_DESCRIPTION("IIO rescale driver"); | ||
| 358 | MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); | ||
| 359 | MODULE_LICENSE("GPL v2"); | ||
| @@ -161,12 +161,14 @@ static int ad8366_probe(struct spi_device *spi) · 160 unmodified lines | |||
| 161 | indio_dev->channels = ad8366_channels; | 161 | indio_dev->channels = ad8366_channels; |
| 162 | indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); | 162 | indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); |
| 163 | 163 | ||
| 164 | ret = ad8366_write(indio_dev, 0 , 0); | ||
| 165 | if (ret < 0) | ||
| 166 | goto error_disable_reg; | ||
| 167 | |||
| 164 | ret = iio_device_register(indio_dev); | 168 | ret = iio_device_register(indio_dev); |
| 165 | if (ret) | 169 | if (ret) |
| 166 | goto error_disable_reg; | 170 | goto error_disable_reg; |
| 167 | 171 | ||
| 168 | ad8366_write(indio_dev, 0, 0); | ||
| 169 | |||
| 170 | return 0; | 172 | return 0; |
| 171 | 173 | ||
| 172 | error_disable_reg: | 174 | error_disable_reg: |
| @@ -61,9 +61,9 @@ · 60 unmodified lines | |||
| 61 | #define ATLAS_REG_ORP_CALIB_STATUS 0x0d | 61 | #define ATLAS_REG_ORP_CALIB_STATUS 0x0d |
| 62 | #define ATLAS_REG_ORP_DATA 0x0e | 62 | #define ATLAS_REG_ORP_DATA 0x0e |
| 63 | 63 | ||
| 64 | #define ATLAS_PH_INT_TIME_IN_US 450000 | 64 | #define ATLAS_PH_INT_TIME_IN_MS 450 |
| 65 | #define ATLAS_EC_INT_TIME_IN_US 650000 | 65 | #define ATLAS_EC_INT_TIME_IN_MS 650 |
| 66 | #define ATLAS_ORP_INT_TIME_IN_US 450000 | 66 | #define ATLAS_ORP_INT_TIME_IN_MS 450 |
| 67 | 67 | ||
| 68 | enum { | 68 | enum { |
| 69 | ATLAS_PH_SM, | 69 | ATLAS_PH_SM, |
| @@ -270,21 +270,21 @@ static struct atlas_device atlas_devices[] = { · 200 unmodified lines | |||
| 270 | .num_channels = 3, | 270 | .num_channels = 3, |
| 271 | .data_reg = ATLAS_REG_PH_DATA, | 271 | .data_reg = ATLAS_REG_PH_DATA, |
| 272 | .calibration = &atlas_check_ph_calibration, | 272 | .calibration = &atlas_check_ph_calibration, |
| 273 | .delay = ATLAS_PH_INT_TIME_IN_US, | 273 | .delay = ATLAS_PH_INT_TIME_IN_MS, |
| 274 | }, | 274 | }, |
| 275 | [ATLAS_EC_SM] = { | 275 | [ATLAS_EC_SM] = { |
| 276 | .channels = atlas_ec_channels, | 276 | .channels = atlas_ec_channels, |
| 277 | .num_channels = 5, | 277 | .num_channels = 5, |
| 278 | .data_reg = ATLAS_REG_EC_DATA, | 278 | .data_reg = ATLAS_REG_EC_DATA, |
| 279 | .calibration = &atlas_check_ec_calibration, | 279 | .calibration = &atlas_check_ec_calibration, |
| 280 | .delay = ATLAS_EC_INT_TIME_IN_US, | 280 | .delay = ATLAS_EC_INT_TIME_IN_MS, |
| 281 | }, | 281 | }, |
| 282 | [ATLAS_ORP_SM] = { | 282 | [ATLAS_ORP_SM] = { |
| 283 | .channels = atlas_orp_channels, | 283 | .channels = atlas_orp_channels, |
| 284 | .num_channels = 2, | 284 | .num_channels = 2, |
| 285 | .data_reg = ATLAS_REG_ORP_DATA, | 285 | .data_reg = ATLAS_REG_ORP_DATA, |
| 286 | .calibration = &atlas_check_orp_calibration, | 286 | .calibration = &atlas_check_orp_calibration, |
| 287 | .delay = ATLAS_ORP_INT_TIME_IN_US, | 287 | .delay = ATLAS_ORP_INT_TIME_IN_MS, |
| 288 | }, | 288 | }, |
| 289 | }; | 289 | }; |
| 290 | 290 | ||
| @@ -393,7 +393,7 @@ static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val) · 102 unmodified lines | |||
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | if (suspended) | 395 | if (suspended) |
| 396 | usleep_range(data->chip->delay, data->chip->delay + 100000); | 396 | msleep(data->chip->delay); |
| 397 | 397 | ||
| 398 | ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val)); | 398 | ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val)); |
| 399 | 399 | ||
| @@ -19,6 +19,7 @@ · 18 unmodified lines | |||
| 19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
| 20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
| 21 | #include <linux/iio/buffer.h> | 21 | #include <linux/iio/buffer.h> |
| 22 | #include <linux/iio/common/cros_ec_sensors_core.h> | ||
| 22 | #include <linux/iio/iio.h> | 23 | #include <linux/iio/iio.h> |
| 23 | #include <linux/iio/kfifo_buf.h> | 24 | #include <linux/iio/kfifo_buf.h> |
| 24 | #include <linux/iio/trigger_consumer.h> | 25 | #include <linux/iio/trigger_consumer.h> |
| @@ -31,8 +32,6 @@ · 6 unmodified lines | |||
| 31 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
| 32 | #include <linux/sysfs.h> | 33 | #include <linux/sysfs.h> |
| 33 | 34 | ||
| 34 | #include "cros_ec_sensors_core.h" | ||
| 35 | |||
| 36 | #define CROS_EC_SENSORS_MAX_CHANNELS 4 | 35 | #define CROS_EC_SENSORS_MAX_CHANNELS 4 |
| 37 | 36 | ||
| 38 | /* State data for ec_sensors iio driver. */ | 37 | /* State data for ec_sensors iio driver. */ |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | |||
| 16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
| 17 | #include <linux/device.h> | 17 | #include <linux/device.h> |
| 18 | #include <linux/iio/buffer.h> | 18 | #include <linux/iio/buffer.h> |
| 19 | #include <linux/iio/common/cros_ec_sensors_core.h> | ||
| 19 | #include <linux/iio/iio.h> | 20 | #include <linux/iio/iio.h> |
| 20 | #include <linux/iio/kfifo_buf.h> | 21 | #include <linux/iio/kfifo_buf.h> |
| 21 | #include <linux/iio/trigger_consumer.h> | 22 | #include <linux/iio/trigger_consumer.h> |
| @@ -27,8 +28,6 @@ · 5 unmodified lines | |||
| 27 | #include <linux/sysfs.h> | 28 | #include <linux/sysfs.h> |
| 28 | #include <linux/platform_device.h> | 29 | #include <linux/platform_device.h> |
| 29 | 30 | ||
| 30 | #include "cros_ec_sensors_core.h" | ||
| 31 | |||
| 32 | static char *cros_ec_loc[] = { | 31 | static char *cros_ec_loc[] = { |
| 33 | [MOTIONSENSE_LOC_BASE] = "base", | 32 | [MOTIONSENSE_LOC_BASE] = "base", |
| 34 | [MOTIONSENSE_LOC_LID] = "lid", | 33 | [MOTIONSENSE_LOC_LID] = "lid", |
| @@ -448,8 +447,7 @@ EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write); · 413 unmodified lines | |||
| 448 | 447 | ||
| 449 | static int __maybe_unused cros_ec_sensors_prepare(struct device *dev) | 448 | static int __maybe_unused cros_ec_sensors_prepare(struct device *dev) |
| 450 | { | 449 | { |
| 451 | struct platform_device *pdev = to_platform_device(dev); | 450 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 452 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 453 | struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); | 451 | struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); |
| 454 | 452 | ||
| 455 | if (st->curr_sampl_freq == 0) | 453 | if (st->curr_sampl_freq == 0) |
| @@ -471,8 +469,7 @@ static int __maybe_unused cros_ec_sensors_prepare(struct device *dev) · 15 unmodified lines | |||
| 471 | 469 | ||
| 472 | static void __maybe_unused cros_ec_sensors_complete(struct device *dev) | 470 | static void __maybe_unused cros_ec_sensors_complete(struct device *dev) |
| 473 | { | 471 | { |
| 474 | struct platform_device *pdev = to_platform_device(dev); | 472 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 475 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 476 | struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); | 473 | struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); |
| 477 | 474 | ||
| 478 | if (st->curr_sampl_freq == 0) | 475 | if (st->curr_sampl_freq == 0) |
| @@ -304,8 +304,7 @@ EXPORT_SYMBOL(hid_sensor_setup_trigger); · 303 unmodified lines | |||
| 304 | 304 | ||
| 305 | static int __maybe_unused hid_sensor_suspend(struct device *dev) | 305 | static int __maybe_unused hid_sensor_suspend(struct device *dev) |
| 306 | { | 306 | { |
| 307 | struct platform_device *pdev = to_platform_device(dev); | 307 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 308 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 309 | struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); | 308 | struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); |
| 310 | 309 | ||
| 311 | return _hid_sensor_power_state(attrb, false); | 310 | return _hid_sensor_power_state(attrb, false); |
| @@ -313,8 +312,7 @@ static int __maybe_unused hid_sensor_suspend(struct device *dev) · 1 unmodified line | |||
| 313 | 312 | ||
| 314 | static int __maybe_unused hid_sensor_resume(struct device *dev) | 313 | static int __maybe_unused hid_sensor_resume(struct device *dev) |
| 315 | { | 314 | { |
| 316 | struct platform_device *pdev = to_platform_device(dev); | 315 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 317 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 318 | struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); | 316 | struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); |
| 319 | schedule_work(&attrb->work); | 317 | schedule_work(&attrb->work); |
| 320 | return 0; | 318 | return 0; |
| @@ -322,8 +320,7 @@ static int __maybe_unused hid_sensor_resume(struct device *dev) · 1 unmodified line | |||
| 322 | 320 | ||
| 323 | static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) | 321 | static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) |
| 324 | { | 322 | { |
| 325 | struct platform_device *pdev = to_platform_device(dev); | 323 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 326 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 327 | struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); | 324 | struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); |
| 328 | return _hid_sensor_power_state(attrb, true); | 325 | return _hid_sensor_power_state(attrb, true); |
| 329 | } | 326 | } |
| @@ -131,16 +131,31 @@ config LTC2632 · 130 unmodified lines | |||
| 131 | module will be called ltc2632. | 131 | module will be called ltc2632. |
| 132 | 132 | ||
| 133 | config AD5686 | 133 | config AD5686 |
| 134 | tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" | 134 | tristate |
| 135 | |||
| 136 | config AD5686_SPI | ||
| 137 | tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)" | ||
| 135 | depends on SPI | 138 | depends on SPI |
| 139 | select AD5686 | ||
| 136 | help | 140 | help |
| 137 | Say yes here to build support for Analog Devices AD5686R, AD5685R, | 141 | Say yes here to build support for Analog Devices AD5672R, AD5676, |
| 138 | AD5684R, AD5791 Voltage Output Digital to | 142 | AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R. |
| 139 | Analog Converter. | 143 | Voltage Output Digital to Analog Converter. |
| 140 | 144 | ||
| 141 | To compile this driver as a module, choose M here: the | 145 | To compile this driver as a module, choose M here: the |
| 142 | module will be called ad5686. | 146 | module will be called ad5686. |
| 143 | 147 | ||
| 148 | config AD5696_I2C | ||
| 149 | tristate "Analog Devices AD5696 and similar multi-channel DACs (I2C)" | ||
| 150 | depends on I2C | ||
| 151 | select AD5686 | ||
| 152 | help | ||
| 153 | Say yes here to build support for Analog Devices AD5671R, AD5675R, | ||
| 154 | AD5694, AD5694R, AD5695R, AD5696, AD5696R Voltage Output Digital to | ||
| 155 | Analog Converter. | ||
| 156 | To compile this driver as a module, choose M here: the module will be | ||
| 157 | called ad5696. | ||
| 158 | |||
| 144 | config AD5755 | 159 | config AD5755 |
| 145 | tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" | 160 | tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" |
| 146 | depends on SPI_MASTER | 161 | depends on SPI_MASTER |
| @@ -321,6 +336,16 @@ config TI_DAC082S085 · 174 unmodified lines | |||
| 321 | 336 | ||
| 322 | If compiled as a module, it will be called ti-dac082s085. | 337 | If compiled as a module, it will be called ti-dac082s085. |
| 323 | 338 | ||
| 339 | config TI_DAC5571 | ||
| 340 | tristate "Texas Instruments 8/10/12/16-bit 1/2/4-channel DAC driver" | ||
| 341 | depends on I2C | ||
| 342 | help | ||
| 343 | Driver for the Texas Instruments | ||
| 344 | DAC5571, DAC6571, DAC7571, DAC5574, DAC6574, DAC7574, DAC5573, | ||
| 345 | DAC6573, DAC7573, DAC8571, DAC8574. | ||
| 346 | |||
| 347 | If compiled as a module, it will be called ti-dac5571. | ||
| 348 | |||
| 324 | config VF610_DAC | 349 | config VF610_DAC |
| 325 | tristate "Vybrid vf610 DAC driver" | 350 | tristate "Vybrid vf610 DAC driver" |
| 326 | depends on OF | 351 | depends on OF |
| @@ -20,6 +20,8 @@ obj-$(CONFIG_AD5761) += ad5761.o · 19 unmodified lines | |||
| 20 | obj-$(CONFIG_AD5764) += ad5764.o | 20 | obj-$(CONFIG_AD5764) += ad5764.o |
| 21 | obj-$(CONFIG_AD5791) += ad5791.o | 21 | obj-$(CONFIG_AD5791) += ad5791.o |
| 22 | obj-$(CONFIG_AD5686) += ad5686.o | 22 | obj-$(CONFIG_AD5686) += ad5686.o |
| 23 | obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o | ||
| 24 | obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o | ||
| 23 | obj-$(CONFIG_AD7303) += ad7303.o | 25 | obj-$(CONFIG_AD7303) += ad7303.o |
| 24 | obj-$(CONFIG_AD8801) += ad8801.o | 26 | obj-$(CONFIG_AD8801) += ad8801.o |
| 25 | obj-$(CONFIG_CIO_DAC) += cio-dac.o | 27 | obj-$(CONFIG_CIO_DAC) += cio-dac.o |
| @@ -35,4 +37,5 @@ obj-$(CONFIG_MCP4922) += mcp4922.o · 9 unmodified lines | |||
| 35 | obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o | 37 | obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o |
| 36 | obj-$(CONFIG_STM32_DAC) += stm32-dac.o | 38 | obj-$(CONFIG_STM32_DAC) += stm32-dac.o |
| 37 | obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o | 39 | obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o |
| 40 | obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o | ||
| 38 | obj-$(CONFIG_VF610_DAC) += vf610_dac.o | 41 | obj-$(CONFIG_VF610_DAC) += vf610_dac.o |
| @@ -0,0 +1,119 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * AD5672R, AD5676, AD5676R, AD5681R, AD5682R, AD5683, AD5683R, | ||
| 4 | * AD5684, AD5684R, AD5685R, AD5686, AD5686R | ||
| 5 | * Digital to analog converters driver | ||
| 6 | * | ||
| 7 | * Copyright 2018 Analog Devices Inc. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include "ad5686.h" | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/spi/spi.h> | ||
| 14 | |||
| 15 | static int ad5686_spi_write(struct ad5686_state *st, | ||
| 16 | u8 cmd, u8 addr, u16 val) | ||
| 17 | { | ||
| 18 | struct spi_device *spi = to_spi_device(st->dev); | ||
| 19 | u8 tx_len, *buf; | ||
| 20 | |||
| 21 | switch (st->chip_info->regmap_type) { | ||
| 22 | case AD5683_REGMAP: | ||
| 23 | st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | | ||
| 24 | AD5683_DATA(val)); | ||
| 25 | buf = &st->data[0].d8[1]; | ||
| 26 | tx_len = 3; | ||
| 27 | break; | ||
| 28 | case AD5686_REGMAP: | ||
| 29 | st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | | ||
| 30 | AD5686_ADDR(addr) | | ||
| 31 | val); | ||
| 32 | buf = &st->data[0].d8[1]; | ||
| 33 | tx_len = 3; | ||
| 34 | break; | ||
| 35 | default: | ||
| 36 | return -EINVAL; | ||
| 37 | } | ||
| 38 | |||
| 39 | return spi_write(spi, buf, tx_len); | ||
| 40 | } | ||
| 41 | |||
| 42 | static int ad5686_spi_read(struct ad5686_state *st, u8 addr) | ||
| 43 | { | ||
| 44 | struct spi_transfer t[] = { | ||
| 45 | { | ||
| 46 | .tx_buf = &st->data[0].d8[1], | ||
| 47 | .len = 3, | ||
| 48 | .cs_change = 1, | ||
| 49 | }, { | ||
| 50 | .tx_buf = &st->data[1].d8[1], | ||
| 51 | .rx_buf = &st->data[2].d8[1], | ||
| 52 | .len = 3, | ||
| 53 | }, | ||
| 54 | }; | ||
| 55 | struct spi_device *spi = to_spi_device(st->dev); | ||
| 56 | u8 cmd = 0; | ||
| 57 | int ret; | ||
| 58 | |||
| 59 | if (st->chip_info->regmap_type == AD5686_REGMAP) | ||
| 60 | cmd = AD5686_CMD_READBACK_ENABLE; | ||
| 61 | else if (st->chip_info->regmap_type == AD5683_REGMAP) | ||
| 62 | cmd = AD5686_CMD_READBACK_ENABLE_V2; | ||
| 63 | |||
| 64 | st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | | ||
| 65 | AD5686_ADDR(addr)); | ||
| 66 | st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); | ||
| 67 | |||
| 68 | ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); | ||
| 69 | if (ret < 0) | ||
| 70 | return ret; | ||
| 71 | |||
| 72 | return be32_to_cpu(st->data[2].d32); | ||
| 73 | } | ||
| 74 | |||
| 75 | static int ad5686_spi_probe(struct spi_device *spi) | ||
| 76 | { | ||
| 77 | const struct spi_device_id *id = spi_get_device_id(spi); | ||
| 78 | |||
| 79 | return ad5686_probe(&spi->dev, id->driver_data, id->name, | ||
| 80 | ad5686_spi_write, ad5686_spi_read); | ||
| 81 | } | ||
| 82 | |||
| 83 | static int ad5686_spi_remove(struct spi_device *spi) | ||
| 84 | { | ||
| 85 | return ad5686_remove(&spi->dev); | ||
| 86 | } | ||
| 87 | |||
| 88 | static const struct spi_device_id ad5686_spi_id[] = { | ||
| 89 | {"ad5672r", ID_AD5672R}, | ||
| 90 | {"ad5676", ID_AD5676}, | ||
| 91 | {"ad5676r", ID_AD5676R}, | ||
| 92 | {"ad5681r", ID_AD5681R}, | ||
| 93 | {"ad5682r", ID_AD5682R}, | ||
| 94 | {"ad5683", ID_AD5683}, | ||
| 95 | {"ad5683r", ID_AD5683R}, | ||
| 96 | {"ad5684", ID_AD5684}, | ||
| 97 | {"ad5684r", ID_AD5684R}, | ||
| 98 | {"ad5685", ID_AD5685R}, /* Does not exist */ | ||
| 99 | {"ad5685r", ID_AD5685R}, | ||
| 100 | {"ad5686", ID_AD5686}, | ||
| 101 | {"ad5686r", ID_AD5686R}, | ||
| 102 | {} | ||
| 103 | }; | ||
| 104 | MODULE_DEVICE_TABLE(spi, ad5686_spi_id); | ||
| 105 | |||
| 106 | static struct spi_driver ad5686_spi_driver = { | ||
| 107 | .driver = { | ||
| 108 | .name = "ad5686", | ||
| 109 | }, | ||
| 110 | .probe = ad5686_spi_probe, | ||
| 111 | .remove = ad5686_spi_remove, | ||
| 112 | .id_table = ad5686_spi_id, | ||
| 113 | }; | ||
| 114 | |||
| 115 | module_spi_driver(ad5686_spi_driver); | ||
| 116 | |||
| 117 | MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); | ||
| 118 | MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); | ||
| 119 | MODULE_LICENSE("GPL v2"); | ||
| @@ -1,9 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 1 | /* | 2 | /* |
| 2 | * AD5686R, AD5685R, AD5684R Digital to analog converters driver | 3 | * AD5686R, AD5685R, AD5684R Digital to analog converters driver |
| 3 | * | 4 | * |
| 4 | * Copyright 2011 Analog Devices Inc. | 5 | * Copyright 2011 Analog Devices Inc. |
| 5 | * | ||
| 6 | * Licensed under the GPL-2. | ||
| 7 | */ | 6 | */ |
| 8 | 7 | ||
| 9 | #include <linux/interrupt.h> | 8 | #include <linux/interrupt.h> |
| @@ -11,7 +10,6 @@ · 1 unmodified line | |||
| 11 | #include <linux/device.h> | 10 | #include <linux/device.h> |
| 12 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 13 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
| 14 | #include <linux/spi/spi.h> | ||
| 15 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
| 16 | #include <linux/sysfs.h> | 14 | #include <linux/sysfs.h> |
| 17 | #include <linux/regulator/consumer.h> | 15 | #include <linux/regulator/consumer.h> |
| @@ -19,116 +17,7 @@ · 1 unmodified line | |||
| 19 | #include <linux/iio/iio.h> | 17 | #include <linux/iio/iio.h> |
| 20 | #include <linux/iio/sysfs.h> | 18 | #include <linux/iio/sysfs.h> |
| 21 | 19 | ||
| 22 | #define AD5686_DAC_CHANNELS 4 | 20 | #include "ad5686.h" |
| 23 | |||
| 24 | #define AD5686_ADDR(x) ((x) << 16) | ||
| 25 | #define AD5686_CMD(x) ((x) << 20) | ||
| 26 | |||
| 27 | #define AD5686_ADDR_DAC(chan) (0x1 << (chan)) | ||
| 28 | #define AD5686_ADDR_ALL_DAC 0xF | ||
| 29 | |||
| 30 | #define AD5686_CMD_NOOP 0x0 | ||
| 31 | #define AD5686_CMD_WRITE_INPUT_N 0x1 | ||
| 32 | #define AD5686_CMD_UPDATE_DAC_N 0x2 | ||
| 33 | #define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 | ||
| 34 | #define AD5686_CMD_POWERDOWN_DAC 0x4 | ||
| 35 | #define AD5686_CMD_LDAC_MASK 0x5 | ||
| 36 | #define AD5686_CMD_RESET 0x6 | ||
| 37 | #define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 | ||
| 38 | #define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 | ||
| 39 | #define AD5686_CMD_READBACK_ENABLE 0x9 | ||
| 40 | |||
| 41 | #define AD5686_LDAC_PWRDN_NONE 0x0 | ||
| 42 | #define AD5686_LDAC_PWRDN_1K 0x1 | ||
| 43 | #define AD5686_LDAC_PWRDN_100K 0x2 | ||
| 44 | #define AD5686_LDAC_PWRDN_3STATE 0x3 | ||
| 45 | |||
| 46 | /** | ||
| 47 | * struct ad5686_chip_info - chip specific information | ||
| 48 | * @int_vref_mv: AD5620/40/60: the internal reference voltage | ||
| 49 | * @channel: channel specification | ||
| 50 | */ | ||
| 51 | |||
| 52 | struct ad5686_chip_info { | ||
| 53 | u16 int_vref_mv; | ||
| 54 | struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; | ||
| 55 | }; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * struct ad5446_state - driver instance specific data | ||
| 59 | * @spi: spi_device | ||
| 60 | * @chip_info: chip model specific constants, available modes etc | ||
| 61 | * @reg: supply regulator | ||
| 62 | * @vref_mv: actual reference voltage used | ||
| 63 | * @pwr_down_mask: power down mask | ||
| 64 | * @pwr_down_mode: current power down mode | ||
| 65 | * @data: spi transfer buffers | ||
| 66 | */ | ||
| 67 | |||
| 68 | struct ad5686_state { | ||
| 69 | struct spi_device *spi; | ||
| 70 | const struct ad5686_chip_info *chip_info; | ||
| 71 | struct regulator *reg; | ||
| 72 | unsigned short vref_mv; | ||
| 73 | unsigned pwr_down_mask; | ||
| 74 | unsigned pwr_down_mode; | ||
| 75 | /* | ||
| 76 | * DMA (thus cache coherency maintenance) requires the | ||
| 77 | * transfer buffers to live in their own cache lines. | ||
| 78 | */ | ||
| 79 | |||
| 80 | union { | ||
| 81 | __be32 d32; | ||
| 82 | u8 d8[4]; | ||
| 83 | } data[3] ____cacheline_aligned; | ||
| 84 | }; | ||
| 85 | |||
| 86 | /** | ||
| 87 | * ad5686_supported_device_ids: | ||
| 88 | */ | ||
| 89 | |||
| 90 | enum ad5686_supported_device_ids { | ||
| 91 | ID_AD5684, | ||
| 92 | ID_AD5685, | ||
| 93 | ID_AD5686, | ||
| 94 | }; | ||
| 95 | static int ad5686_spi_write(struct ad5686_state *st, | ||
| 96 | u8 cmd, u8 addr, u16 val, u8 shift) | ||
| 97 | { | ||
| 98 | val <<= shift; | ||
| 99 | |||
| 100 | st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | | ||
| 101 | AD5686_ADDR(addr) | | ||
| 102 | val); | ||
| 103 | |||
| 104 | return spi_write(st->spi, &st->data[0].d8[1], 3); | ||
| 105 | } | ||
| 106 | |||
| 107 | static int ad5686_spi_read(struct ad5686_state *st, u8 addr) | ||
| 108 | { | ||
| 109 | struct spi_transfer t[] = { | ||
| 110 | { | ||
| 111 | .tx_buf = &st->data[0].d8[1], | ||
| 112 | .len = 3, | ||
| 113 | .cs_change = 1, | ||
| 114 | }, { | ||
| 115 | .tx_buf = &st->data[1].d8[1], | ||
| 116 | .rx_buf = &st->data[2].d8[1], | ||
| 117 | .len = 3, | ||
| 118 | }, | ||
| 119 | }; | ||
| 120 | int ret; | ||
| 121 | |||
| 122 | st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | | ||
| 123 | AD5686_ADDR(addr)); | ||
| 124 | st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); | ||
| 125 | |||
| 126 | ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); | ||
| 127 | if (ret < 0) | ||
| 128 | return ret; | ||
| 129 | |||
| 130 | return be32_to_cpu(st->data[2].d32); | ||
| 131 | } | ||
| 132 | 21 | ||
| 133 | static const char * const ad5686_powerdown_modes[] = { | 22 | static const char * const ad5686_powerdown_modes[] = { |
| 134 | "1kohm_to_gnd", | 23 | "1kohm_to_gnd", |
| @@ -137,7 +26,7 @@ static const char * const ad5686_powerdown_modes[] = { · 2 unmodified lines | |||
| 137 | }; | 26 | }; |
| 138 | 27 | ||
| 139 | static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, | 28 | static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, |
| 140 | const struct iio_chan_spec *chan) | 29 | const struct iio_chan_spec *chan) |
| 141 | { | 30 | { |
| 142 | struct ad5686_state *st = iio_priv(indio_dev); | 31 | struct ad5686_state *st = iio_priv(indio_dev); |
| 143 | 32 | ||
| @@ -145,7 +34,8 @@ static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, · 1 unmodified line | |||
| 145 | } | 34 | } |
| 146 | 35 | ||
| 147 | static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, | 36 | static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, |
| 148 | const struct iio_chan_spec *chan, unsigned int mode) | 37 | const struct iio_chan_spec *chan, |
| 38 | unsigned int mode) | ||
| 149 | { | 39 | { |
| 150 | struct ad5686_state *st = iio_priv(indio_dev); | 40 | struct ad5686_state *st = iio_priv(indio_dev); |
| 151 | 41 | ||
| @@ -163,21 +53,25 @@ static const struct iio_enum ad5686_powerdown_mode_enum = { · 11 unmodified lines | |||
| 163 | }; | 53 | }; |
| 164 | 54 | ||
| 165 | static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, | 55 | static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, |
| 166 | uintptr_t private, const struct iio_chan_spec *chan, char *buf) | 56 | uintptr_t private, const struct iio_chan_spec *chan, char *buf) |
| 167 | { | 57 | { |
| 168 | struct ad5686_state *st = iio_priv(indio_dev); | 58 | struct ad5686_state *st = iio_priv(indio_dev); |
| 169 | 59 | ||
| 170 | return sprintf(buf, "%d\n", !!(st->pwr_down_mask & | 60 | return sprintf(buf, "%d\n", !!(st->pwr_down_mask & |
| 171 | (0x3 << (chan->channel * 2)))); | 61 | (0x3 << (chan->channel * 2)))); |
| 172 | } | 62 | } |
| 173 | 63 | ||
| 174 | static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, | 64 | static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, |
| 175 | uintptr_t private, const struct iio_chan_spec *chan, const char *buf, | 65 | uintptr_t private, |
| 176 | size_t len) | 66 | const struct iio_chan_spec *chan, |
| 67 | const char *buf, | ||
| 68 | size_t len) | ||
| 177 | { | 69 | { |
| 178 | bool readin; | 70 | bool readin; |
| 179 | int ret; | 71 | int ret; |
| 180 | struct ad5686_state *st = iio_priv(indio_dev); | 72 | struct ad5686_state *st = iio_priv(indio_dev); |
| 73 | unsigned int val, ref_bit_msk; | ||
| 74 | u8 shift; | ||
| 181 | 75 | ||
| 182 | ret = strtobool(buf, &readin); | 76 | ret = strtobool(buf, &readin); |
| 183 | if (ret) | 77 | if (ret) |
| @@ -188,8 +82,28 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, · 4 unmodified lines | |||
| 188 | else | 82 | else |
| 189 | st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); | 83 | st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); |
| 190 | 84 | ||
| 191 | ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0, | 85 | switch (st->chip_info->regmap_type) { |
| 192 | st->pwr_down_mask & st->pwr_down_mode, 0); | 86 | case AD5683_REGMAP: |
| 87 | shift = 13; | ||
| 88 | ref_bit_msk = AD5683_REF_BIT_MSK; | ||
| 89 | break; | ||
| 90 | case AD5686_REGMAP: | ||
| 91 | shift = 0; | ||
| 92 | ref_bit_msk = 0; | ||
| 93 | break; | ||
| 94 | case AD5693_REGMAP: | ||
| 95 | shift = 13; | ||
| 96 | ref_bit_msk = AD5693_REF_BIT_MSK; | ||
| 97 | break; | ||
| 98 | default: | ||
| 99 | return -EINVAL; | ||
| 100 | } | ||
| 101 | |||
| 102 | val = ((st->pwr_down_mask & st->pwr_down_mode) << shift); | ||
| 103 | if (!st->use_internal_vref) | ||
| 104 | val |= ref_bit_msk; | ||
| 105 | |||
| 106 | ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, val); | ||
| 193 | 107 | ||
| 194 | return ret ? ret : len; | 108 | return ret ? ret : len; |
| 195 | } | 109 | } |
| @@ -206,7 +120,7 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, · 10 unmodified lines | |||
| 206 | switch (m) { | 120 | switch (m) { |
| 207 | case IIO_CHAN_INFO_RAW: | 121 | case IIO_CHAN_INFO_RAW: |
| 208 | mutex_lock(&indio_dev->mlock); | 122 | mutex_lock(&indio_dev->mlock); |
| 209 | ret = ad5686_spi_read(st, chan->address); | 123 | ret = st->read(st, chan->address); |
| 210 | mutex_unlock(&indio_dev->mlock); | 124 | mutex_unlock(&indio_dev->mlock); |
| 211 | if (ret < 0) | 125 | if (ret < 0) |
| 212 | return ret; | 126 | return ret; |
| @@ -221,10 +135,10 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, · 8 unmodified lines | |||
| 221 | } | 135 | } |
| 222 | 136 | ||
| 223 | static int ad5686_write_raw(struct iio_dev *indio_dev, | 137 | static int ad5686_write_raw(struct iio_dev *indio_dev, |
| 224 | struct iio_chan_spec const *chan, | 138 | struct iio_chan_spec const *chan, |
| 225 | int val, | 139 | int val, |
| 226 | int val2, | 140 | int val2, |
| 227 | long mask) | 141 | long mask) |
| 228 | { | 142 | { |
| 229 | struct ad5686_state *st = iio_priv(indio_dev); | 143 | struct ad5686_state *st = iio_priv(indio_dev); |
| 230 | int ret; | 144 | int ret; |
| @@ -235,11 +149,10 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, · 4 unmodified lines | |||
| 235 | return -EINVAL; | 149 | return -EINVAL; |
| 236 | 150 | ||
| 237 | mutex_lock(&indio_dev->mlock); | 151 | mutex_lock(&indio_dev->mlock); |
| 238 | ret = ad5686_spi_write(st, | 152 | ret = st->write(st, |
| 239 | AD5686_CMD_WRITE_INPUT_N_UPDATE_N, | 153 | AD5686_CMD_WRITE_INPUT_N_UPDATE_N, |
| 240 | chan->address, | 154 | chan->address, |
| 241 | val, | 155 | val << chan->scan_type.shift); |
| 242 | chan->scan_type.shift); | ||
| 243 | mutex_unlock(&indio_dev->mlock); | 156 | mutex_unlock(&indio_dev->mlock); |
| 244 | break; | 157 | break; |
| 245 | default: | 158 | default: |
| @@ -266,14 +179,14 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { · 20 unmodified lines | |||
| 266 | { }, | 179 | { }, |
| 267 | }; | 180 | }; |
| 268 | 181 | ||
| 269 | #define AD5868_CHANNEL(chan, bits, _shift) { \ | 182 | #define AD5868_CHANNEL(chan, addr, bits, _shift) { \ |
| 270 | .type = IIO_VOLTAGE, \ | 183 | .type = IIO_VOLTAGE, \ |
| 271 | .indexed = 1, \ | 184 | .indexed = 1, \ |
| 272 | .output = 1, \ | 185 | .output = 1, \ |
| 273 | .channel = chan, \ | 186 | .channel = chan, \ |
| 274 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | 187 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
| 275 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ | 188 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ |
| 276 | .address = AD5686_ADDR_DAC(chan), \ | 189 | .address = addr, \ |
| 277 | .scan_type = { \ | 190 | .scan_type = { \ |
| 278 | .sign = 'u', \ | 191 | .sign = 'u', \ |
| 279 | .realbits = (bits), \ | 192 | .realbits = (bits), \ |
| @@ -283,45 +196,191 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { · 3 unmodified lines | |||
| 283 | .ext_info = ad5686_ext_info, \ | 196 | .ext_info = ad5686_ext_info, \ |
| 284 | } | 197 | } |
| 285 | 198 | ||
| 199 | #define DECLARE_AD5693_CHANNELS(name, bits, _shift) \ | ||
| 200 | static struct iio_chan_spec name[] = { \ | ||
| 201 | AD5868_CHANNEL(0, 0, bits, _shift), \ | ||
| 202 | } | ||
| 203 | |||
| 204 | #define DECLARE_AD5686_CHANNELS(name, bits, _shift) \ | ||
| 205 | static struct iio_chan_spec name[] = { \ | ||
| 206 | AD5868_CHANNEL(0, 1, bits, _shift), \ | ||
| 207 | AD5868_CHANNEL(1, 2, bits, _shift), \ | ||
| 208 | AD5868_CHANNEL(2, 4, bits, _shift), \ | ||
| 209 | AD5868_CHANNEL(3, 8, bits, _shift), \ | ||
| 210 | } | ||
| 211 | |||
| 212 | #define DECLARE_AD5676_CHANNELS(name, bits, _shift) \ | ||
| 213 | static struct iio_chan_spec name[] = { \ | ||
| 214 | AD5868_CHANNEL(0, 0, bits, _shift), \ | ||
| 215 | AD5868_CHANNEL(1, 1, bits, _shift), \ | ||
| 216 | AD5868_CHANNEL(2, 2, bits, _shift), \ | ||
| 217 | AD5868_CHANNEL(3, 3, bits, _shift), \ | ||
| 218 | AD5868_CHANNEL(4, 4, bits, _shift), \ | ||
| 219 | AD5868_CHANNEL(5, 5, bits, _shift), \ | ||
| 220 | AD5868_CHANNEL(6, 6, bits, _shift), \ | ||
| 221 | AD5868_CHANNEL(7, 7, bits, _shift), \ | ||
| 222 | } | ||
| 223 | |||
| 224 | DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4); | ||
| 225 | DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0); | ||
| 226 | DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4); | ||
| 227 | DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); | ||
| 228 | DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0); | ||
| 229 | DECLARE_AD5693_CHANNELS(ad5693_channels, 16, 0); | ||
| 230 | DECLARE_AD5693_CHANNELS(ad5692r_channels, 14, 2); | ||
| 231 | DECLARE_AD5693_CHANNELS(ad5691r_channels, 12, 4); | ||
| 232 | |||
| 286 | static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { | 233 | static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { |
| 234 | [ID_AD5671R] = { | ||
| 235 | .channels = ad5672_channels, | ||
| 236 | .int_vref_mv = 2500, | ||
| 237 | .num_channels = 8, | ||
| 238 | .regmap_type = AD5686_REGMAP, | ||
| 239 | }, | ||
| 240 | [ID_AD5672R] = { | ||
| 241 | .channels = ad5672_channels, | ||
| 242 | .int_vref_mv = 2500, | ||
| 243 | .num_channels = 8, | ||
| 244 | .regmap_type = AD5686_REGMAP, | ||
| 245 | }, | ||
| 246 | [ID_AD5675R] = { | ||
| 247 | .channels = ad5676_channels, | ||
| 248 | .int_vref_mv = 2500, | ||
| 249 | .num_channels = 8, | ||
| 250 | .regmap_type = AD5686_REGMAP, | ||
| 251 | }, | ||
| 252 | [ID_AD5676] = { | ||
| 253 | .channels = ad5676_channels, | ||
| 254 | .num_channels = 8, | ||
| 255 | .regmap_type = AD5686_REGMAP, | ||
| 256 | }, | ||
| 257 | [ID_AD5676R] = { | ||
| 258 | .channels = ad5676_channels, | ||
| 259 | .int_vref_mv = 2500, | ||
| 260 | .num_channels = 8, | ||
| 261 | .regmap_type = AD5686_REGMAP, | ||
| 262 | }, | ||
| 263 | [ID_AD5681R] = { | ||
| 264 | .channels = ad5691r_channels, | ||
| 265 | .int_vref_mv = 2500, | ||
| 266 | .num_channels = 1, | ||
| 267 | .regmap_type = AD5683_REGMAP, | ||
| 268 | }, | ||
| 269 | [ID_AD5682R] = { | ||
| 270 | .channels = ad5692r_channels, | ||
| 271 | .int_vref_mv = 2500, | ||
| 272 | .num_channels = 1, | ||
| 273 | .regmap_type = AD5683_REGMAP, | ||
| 274 | }, | ||
| 275 | [ID_AD5683] = { | ||
| 276 | .channels = ad5693_channels, | ||
| 277 | .num_channels = 1, | ||
| 278 | .regmap_type = AD5683_REGMAP, | ||
| 279 | }, | ||
| 280 | [ID_AD5683R] = { | ||
| 281 | .channels = ad5693_channels, | ||
| 282 | .int_vref_mv = 2500, | ||
| 283 | .num_channels = 1, | ||
| 284 | .regmap_type = AD5683_REGMAP, | ||
| 285 | }, | ||
| 287 | [ID_AD5684] = { | 286 | [ID_AD5684] = { |
| 288 | .channel[0] = AD5868_CHANNEL(0, 12, 4), | 287 | .channels = ad5684_channels, |
| 289 | .channel[1] = AD5868_CHANNEL(1, 12, 4), | 288 | .num_channels = 4, |
| 290 | .channel[2] = AD5868_CHANNEL(2, 12, 4), | 289 | .regmap_type = AD5686_REGMAP, |
| 291 | .channel[3] = AD5868_CHANNEL(3, 12, 4), | 290 | }, |
| 291 | [ID_AD5684R] = { | ||
| 292 | .channels = ad5684_channels, | ||
| 292 | .int_vref_mv = 2500, | 293 | .int_vref_mv = 2500, |
| 294 | .num_channels = 4, | ||
| 295 | .regmap_type = AD5686_REGMAP, | ||
| 293 | }, | 296 | }, |
| 294 | [ID_AD5685] = { | 297 | [ID_AD5685R] = { |
| 295 | .channel[0] = AD5868_CHANNEL(0, 14, 2), | 298 | .channels = ad5685r_channels, |
| 296 | .channel[1] = AD5868_CHANNEL(1, 14, 2), | ||
| 297 | .channel[2] = AD5868_CHANNEL(2, 14, 2), | ||
| 298 | .channel[3] = AD5868_CHANNEL(3, 14, 2), | ||
| 299 | .int_vref_mv = 2500, | 299 | .int_vref_mv = 2500, |
| 300 | .num_channels = 4, | ||
| 301 | .regmap_type = AD5686_REGMAP, | ||
| 300 | }, | 302 | }, |
| 301 | [ID_AD5686] = { | 303 | [ID_AD5686] = { |
| 302 | .channel[0] = AD5868_CHANNEL(0, 16, 0), | 304 | .channels = ad5686_channels, |
| 303 | .channel[1] = AD5868_CHANNEL(1, 16, 0), | 305 | .num_channels = 4, |
| 304 | .channel[2] = AD5868_CHANNEL(2, 16, 0), | 306 | .regmap_type = AD5686_REGMAP, |
| 305 | .channel[3] = AD5868_CHANNEL(3, 16, 0), | 307 | }, |
| 308 | [ID_AD5686R] = { | ||
| 309 | .channels = ad5686_channels, | ||
| 310 | .int_vref_mv = 2500, | ||
| 311 | .num_channels = 4, | ||
| 312 | .regmap_type = AD5686_REGMAP, | ||
| 313 | }, | ||
| 314 | [ID_AD5691R] = { | ||
| 315 | .channels = ad5691r_channels, | ||
| 316 | .int_vref_mv = 2500, | ||
| 317 | .num_channels = 1, | ||
| 318 | .regmap_type = AD5693_REGMAP, | ||
| 319 | }, | ||
| 320 | [ID_AD5692R] = { | ||
| 321 | .channels = ad5692r_channels, | ||
| 322 | .int_vref_mv = 2500, | ||
| 323 | .num_channels = 1, | ||
| 324 | .regmap_type = AD5693_REGMAP, | ||
| 325 | }, | ||
| 326 | [ID_AD5693] = { | ||
| 327 | .channels = ad5693_channels, | ||
| 328 | .num_channels = 1, | ||
| 329 | .regmap_type = AD5693_REGMAP, | ||
| 330 | }, | ||
| 331 | [ID_AD5693R] = { | ||
| 332 | .channels = ad5693_channels, | ||
| 306 | .int_vref_mv = 2500, | 333 | .int_vref_mv = 2500, |
| 334 | .num_channels = 1, | ||
| 335 | .regmap_type = AD5693_REGMAP, | ||
| 336 | }, | ||
| 337 | [ID_AD5694] = { | ||
| 338 | .channels = ad5684_channels, | ||
| 339 | .num_channels = 4, | ||
| 340 | .regmap_type = AD5686_REGMAP, | ||
| 341 | }, | ||
| 342 | [ID_AD5694R] = { | ||
| 343 | .channels = ad5684_channels, | ||
| 344 | .int_vref_mv = 2500, | ||
| 345 | .num_channels = 4, | ||
| 346 | .regmap_type = AD5686_REGMAP, | ||
| 347 | }, | ||
| 348 | [ID_AD5696] = { | ||
| 349 | .channels = ad5686_channels, | ||
| 350 | .num_channels = 4, | ||
| 351 | .regmap_type = AD5686_REGMAP, | ||
| 352 | }, | ||
| 353 | [ID_AD5696R] = { | ||
| 354 | .channels = ad5686_channels, | ||
| 355 | .int_vref_mv = 2500, | ||
| 356 | .num_channels = 4, | ||
| 357 | .regmap_type = AD5686_REGMAP, | ||
| 307 | }, | 358 | }, |
| 308 | }; | 359 | }; |
| 309 | 360 | ||
| 310 | 361 | int ad5686_probe(struct device *dev, | |
| 311 | static int ad5686_probe(struct spi_device *spi) | 362 | enum ad5686_supported_device_ids chip_type, |
| 363 | const char *name, ad5686_write_func write, | ||
| 364 | ad5686_read_func read) | ||
| 312 | { | 365 | { |
| 313 | struct ad5686_state *st; | 366 | struct ad5686_state *st; |
| 314 | struct iio_dev *indio_dev; | 367 | struct iio_dev *indio_dev; |
| 315 | int ret, voltage_uv = 0; | 368 | unsigned int val, ref_bit_msk; |
| 369 | u8 cmd; | ||
| 370 | int ret, i, voltage_uv = 0; | ||
| 316 | 371 | ||
| 317 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | 372 | indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); |
| 318 | if (indio_dev == NULL) | 373 | if (indio_dev == NULL) |
| 319 | return -ENOMEM; | 374 | return -ENOMEM; |
| 320 | 375 | ||
| 321 | st = iio_priv(indio_dev); | 376 | st = iio_priv(indio_dev); |
| 322 | spi_set_drvdata(spi, indio_dev); | 377 | dev_set_drvdata(dev, indio_dev); |
| 378 | |||
| 379 | st->dev = dev; | ||
| 380 | st->write = write; | ||
| 381 | st->read = read; | ||
| 323 | 382 | ||
| 324 | st->reg = devm_regulator_get_optional(&spi->dev, "vcc"); | 383 | st->reg = devm_regulator_get_optional(dev, "vcc"); |
| 325 | if (!IS_ERR(st->reg)) { | 384 | if (!IS_ERR(st->reg)) { |
| 326 | ret = regulator_enable(st->reg); | 385 | ret = regulator_enable(st->reg); |
| 327 | if (ret) | 386 | if (ret) |
| @@ -334,28 +393,47 @@ static int ad5686_probe(struct spi_device *spi) · 6 unmodified lines | |||
| 334 | voltage_uv = ret; | 393 | voltage_uv = ret; |
| 335 | } | 394 | } |
| 336 | 395 | ||
| 337 | st->chip_info = | 396 | st->chip_info = &ad5686_chip_info_tbl[chip_type]; |
| 338 | &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data]; | ||
| 339 | 397 | ||
| 340 | if (voltage_uv) | 398 | if (voltage_uv) |
| 341 | st->vref_mv = voltage_uv / 1000; | 399 | st->vref_mv = voltage_uv / 1000; |
| 342 | else | 400 | else |
| 343 | st->vref_mv = st->chip_info->int_vref_mv; | 401 | st->vref_mv = st->chip_info->int_vref_mv; |
| 344 | 402 | ||
| 345 | st->spi = spi; | ||
| 346 | |||
| 347 | /* Set all the power down mode for all channels to 1K pulldown */ | 403 | /* Set all the power down mode for all channels to 1K pulldown */ |
| 348 | st->pwr_down_mode = 0x55; | 404 | for (i = 0; i < st->chip_info->num_channels; i++) |
| 405 | st->pwr_down_mode |= (0x01 << (i * 2)); | ||
| 349 | 406 | ||
| 350 | indio_dev->dev.parent = &spi->dev; | 407 | indio_dev->dev.parent = dev; |
| 351 | indio_dev->name = spi_get_device_id(spi)->name; | 408 | indio_dev->name = name; |
| 352 | indio_dev->info = &ad5686_info; | 409 | indio_dev->info = &ad5686_info; |
| 353 | indio_dev->modes = INDIO_DIRECT_MODE; | 410 | indio_dev->modes = INDIO_DIRECT_MODE; |
| 354 | indio_dev->channels = st->chip_info->channel; | 411 | indio_dev->channels = st->chip_info->channels; |
| 355 | indio_dev->num_channels = AD5686_DAC_CHANNELS; | 412 | indio_dev->num_channels = st->chip_info->num_channels; |
| 413 | |||
| 414 | switch (st->chip_info->regmap_type) { | ||
| 415 | case AD5683_REGMAP: | ||
| 416 | cmd = AD5686_CMD_CONTROL_REG; | ||
| 417 | ref_bit_msk = AD5683_REF_BIT_MSK; | ||
| 418 | st->use_internal_vref = !voltage_uv; | ||
| 419 | break; | ||
| 420 | case AD5686_REGMAP: | ||
| 421 | cmd = AD5686_CMD_INTERNAL_REFER_SETUP; | ||
| 422 | ref_bit_msk = 0; | ||
| 423 | break; | ||
| 424 | case AD5693_REGMAP: | ||
| 425 | cmd = AD5686_CMD_CONTROL_REG; | ||
| 426 | ref_bit_msk = AD5693_REF_BIT_MSK; | ||
| 427 | st->use_internal_vref = !voltage_uv; | ||
| 428 | break; | ||
| 429 | default: | ||
| 430 | ret = -EINVAL; | ||
| 431 | goto error_disable_reg; | ||
| 432 | } | ||
| 433 | |||
| 434 | val = (voltage_uv | ref_bit_msk); | ||
| 356 | 435 | ||
| 357 | ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, | 436 | ret = st->write(st, cmd, 0, !!val); |
| 358 | !!voltage_uv, 0); | ||
| 359 | if (ret) | 437 | if (ret) |
| 360 | goto error_disable_reg; | 438 | goto error_disable_reg; |
| 361 | 439 | ||
| @@ -370,10 +448,11 @@ error_disable_reg: · 8 unmodified lines | |||
| 370 | regulator_disable(st->reg); | 448 | regulator_disable(st->reg); |
| 371 | return ret; | 449 | return ret; |
| 372 | } | 450 | } |
| 451 | EXPORT_SYMBOL_GPL(ad5686_probe); | ||
| 373 | 452 | ||
| 374 | static int ad5686_remove(struct spi_device *spi) | 453 | int ad5686_remove(struct device *dev) |
| 375 | { | 454 | { |
| 376 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | 455 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 377 | struct ad5686_state *st = iio_priv(indio_dev); | 456 | struct ad5686_state *st = iio_priv(indio_dev); |
| 378 | 457 | ||
| 379 | iio_device_unregister(indio_dev); | 458 | iio_device_unregister(indio_dev); |
| @@ -382,24 +461,7 @@ static int ad5686_remove(struct spi_device *spi) · 2 unmodified lines | |||
| 382 | 461 | ||
| 383 | return 0; | 462 | return 0; |
| 384 | } | 463 | } |
| 385 | 464 | EXPORT_SYMBOL_GPL(ad5686_remove); | |
| 386 | static const struct spi_device_id ad5686_id[] = { | ||
| 387 | {"ad5684", ID_AD5684}, | ||
| 388 | {"ad5685", ID_AD5685}, | ||
| 389 | {"ad5686", ID_AD5686}, | ||
| 390 | {} | ||
| 391 | }; | ||
| 392 | MODULE_DEVICE_TABLE(spi, ad5686_id); | ||
| 393 | |||
| 394 | static struct spi_driver ad5686_driver = { | ||
| 395 | .driver = { | ||
| 396 | .name = "ad5686", | ||
| 397 | }, | ||
| 398 | .probe = ad5686_probe, | ||
| 399 | .remove = ad5686_remove, | ||
| 400 | .id_table = ad5686_id, | ||
| 401 | }; | ||
| 402 | module_spi_driver(ad5686_driver); | ||
| 403 | 465 | ||
| 404 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | 466 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); |
| 405 | MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); | 467 | MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); |
| @@ -0,0 +1,145 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0+ */ | ||
| 2 | /* | ||
| 3 | * This file is part of AD5686 DAC driver | ||
| 4 | * | ||
| 5 | * Copyright 2018 Analog Devices Inc. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef __DRIVERS_IIO_DAC_AD5686_H__ | ||
| 9 | #define __DRIVERS_IIO_DAC_AD5686_H__ | ||
| 10 | |||
| 11 | #include <linux/types.h> | ||
| 12 | #include <linux/cache.h> | ||
| 13 | #include <linux/mutex.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | |||
| 16 | #define AD5683_DATA(x) ((x) << 4) | ||
| 17 | #define AD5686_ADDR(x) ((x) << 16) | ||
| 18 | #define AD5686_CMD(x) ((x) << 20) | ||
| 19 | |||
| 20 | #define AD5686_ADDR_DAC(chan) (0x1 << (chan)) | ||
| 21 | #define AD5686_ADDR_ALL_DAC 0xF | ||
| 22 | |||
| 23 | #define AD5686_CMD_NOOP 0x0 | ||
| 24 | #define AD5686_CMD_WRITE_INPUT_N 0x1 | ||
| 25 | #define AD5686_CMD_UPDATE_DAC_N 0x2 | ||
| 26 | #define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 | ||
| 27 | #define AD5686_CMD_POWERDOWN_DAC 0x4 | ||
| 28 | #define AD5686_CMD_LDAC_MASK 0x5 | ||
| 29 | #define AD5686_CMD_RESET 0x6 | ||
| 30 | #define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 | ||
| 31 | #define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 | ||
| 32 | #define AD5686_CMD_READBACK_ENABLE 0x9 | ||
| 33 | |||
| 34 | #define AD5686_LDAC_PWRDN_NONE 0x0 | ||
| 35 | #define AD5686_LDAC_PWRDN_1K 0x1 | ||
| 36 | #define AD5686_LDAC_PWRDN_100K 0x2 | ||
| 37 | #define AD5686_LDAC_PWRDN_3STATE 0x3 | ||
| 38 | |||
| 39 | #define AD5686_CMD_CONTROL_REG 0x4 | ||
| 40 | #define AD5686_CMD_READBACK_ENABLE_V2 0x5 | ||
| 41 | #define AD5683_REF_BIT_MSK BIT(12) | ||
| 42 | #define AD5693_REF_BIT_MSK BIT(12) | ||
| 43 | |||
| 44 | /** | ||
| 45 | * ad5686_supported_device_ids: | ||
| 46 | */ | ||
| 47 | enum ad5686_supported_device_ids { | ||
| 48 | ID_AD5671R, | ||
| 49 | ID_AD5672R, | ||
| 50 | ID_AD5675R, | ||
| 51 | ID_AD5676, | ||
| 52 | ID_AD5676R, | ||
| 53 | ID_AD5681R, | ||
| 54 | ID_AD5682R, | ||
| 55 | ID_AD5683, | ||
| 56 | ID_AD5683R, | ||
| 57 | ID_AD5684, | ||
| 58 | ID_AD5684R, | ||
| 59 | ID_AD5685R, | ||
| 60 | ID_AD5686, | ||
| 61 | ID_AD5686R, | ||
| 62 | ID_AD5691R, | ||
| 63 | ID_AD5692R, | ||
| 64 | ID_AD5693, | ||
| 65 | ID_AD5693R, | ||
| 66 | ID_AD5694, | ||
| 67 | ID_AD5694R, | ||
| 68 | ID_AD5695R, | ||
| 69 | ID_AD5696, | ||
| 70 | ID_AD5696R, | ||
| 71 | }; | ||
| 72 | |||
| 73 | enum ad5686_regmap_type { | ||
| 74 | AD5683_REGMAP, | ||
| 75 | AD5686_REGMAP, | ||
| 76 | AD5693_REGMAP | ||
| 77 | }; | ||
| 78 | |||
| 79 | struct ad5686_state; | ||
| 80 | |||
| 81 | typedef int (*ad5686_write_func)(struct ad5686_state *st, | ||
| 82 | u8 cmd, u8 addr, u16 val); | ||
| 83 | |||
| 84 | typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr); | ||
| 85 | |||
| 86 | /** | ||
| 87 | * struct ad5686_chip_info - chip specific information | ||
| 88 | * @int_vref_mv: AD5620/40/60: the internal reference voltage | ||
| 89 | * @num_channels: number of channels | ||
| 90 | * @channel: channel specification | ||
| 91 | * @regmap_type: register map layout variant | ||
| 92 | */ | ||
| 93 | |||
| 94 | struct ad5686_chip_info { | ||
| 95 | u16 int_vref_mv; | ||
| 96 | unsigned int num_channels; | ||
| 97 | struct iio_chan_spec *channels; | ||
| 98 | enum ad5686_regmap_type regmap_type; | ||
| 99 | }; | ||
| 100 | |||
| 101 | /** | ||
| 102 | * struct ad5446_state - driver instance specific data | ||
| 103 | * @spi: spi_device | ||
| 104 | * @chip_info: chip model specific constants, available modes etc | ||
| 105 | * @reg: supply regulator | ||
| 106 | * @vref_mv: actual reference voltage used | ||
| 107 | * @pwr_down_mask: power down mask | ||
| 108 | * @pwr_down_mode: current power down mode | ||
| 109 | * @use_internal_vref: set to true if the internal reference voltage is used | ||
| 110 | * @data: spi transfer buffers | ||
| 111 | */ | ||
| 112 | |||
| 113 | struct ad5686_state { | ||
| 114 | struct device *dev; | ||
| 115 | const struct ad5686_chip_info *chip_info; | ||
| 116 | struct regulator *reg; | ||
| 117 | unsigned short vref_mv; | ||
| 118 | unsigned int pwr_down_mask; | ||
| 119 | unsigned int pwr_down_mode; | ||
| 120 | ad5686_write_func write; | ||
| 121 | ad5686_read_func read; | ||
| 122 | bool use_internal_vref; | ||
| 123 | |||
| 124 | /* | ||
| 125 | * DMA (thus cache coherency maintenance) requires the | ||
| 126 | * transfer buffers to live in their own cache lines. | ||
| 127 | */ | ||
| 128 | |||
| 129 | union { | ||
| 130 | __be32 d32; | ||
| 131 | __be16 d16; | ||
| 132 | u8 d8[4]; | ||
| 133 | } data[3] ____cacheline_aligned; | ||
| 134 | }; | ||
| 135 | |||
| 136 | |||
| 137 | int ad5686_probe(struct device *dev, | ||
| 138 | enum ad5686_supported_device_ids chip_type, | ||
| 139 | const char *name, ad5686_write_func write, | ||
| 140 | ad5686_read_func read); | ||
| 141 | |||
| 142 | int ad5686_remove(struct device *dev); | ||
| 143 | |||
| 144 | |||
| 145 | #endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ | ||
| @@ -0,0 +1,102 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R, | ||
| 4 | * AD5694, AD5694R, AD5695R, AD5696, AD5696R | ||
| 5 | * Digital to analog converters driver | ||
| 6 | * | ||
| 7 | * Copyright 2018 Analog Devices Inc. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include "ad5686.h" | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/i2c.h> | ||
| 14 | |||
| 15 | static int ad5686_i2c_read(struct ad5686_state *st, u8 addr) | ||
| 16 | { | ||
| 17 | struct i2c_client *i2c = to_i2c_client(st->dev); | ||
| 18 | struct i2c_msg msg[2] = { | ||
| 19 | { | ||
| 20 | .addr = i2c->addr, | ||
| 21 | .flags = i2c->flags, | ||
| 22 | .len = 3, | ||
| 23 | .buf = &st->data[0].d8[1], | ||
| 24 | }, | ||
| 25 | { | ||
| 26 | .addr = i2c->addr, | ||
| 27 | .flags = i2c->flags | I2C_M_RD, | ||
| 28 | .len = 2, | ||
| 29 | .buf = (char *)&st->data[0].d16, | ||
| 30 | }, | ||
| 31 | }; | ||
| 32 | int ret; | ||
| 33 | |||
| 34 | st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) | | ||
| 35 | AD5686_ADDR(addr) | | ||
| 36 | 0x00); | ||
| 37 | |||
| 38 | ret = i2c_transfer(i2c->adapter, msg, 2); | ||
| 39 | if (ret < 0) | ||
| 40 | return ret; | ||
| 41 | |||
| 42 | return be16_to_cpu(st->data[0].d16); | ||
| 43 | } | ||
| 44 | |||
| 45 | static int ad5686_i2c_write(struct ad5686_state *st, | ||
| 46 | u8 cmd, u8 addr, u16 val) | ||
| 47 | { | ||
| 48 | struct i2c_client *i2c = to_i2c_client(st->dev); | ||
| 49 | int ret; | ||
| 50 | |||
| 51 | st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) | ||
| 52 | | val); | ||
| 53 | |||
| 54 | ret = i2c_master_send(i2c, &st->data[0].d8[1], 3); | ||
| 55 | if (ret < 0) | ||
| 56 | return ret; | ||
| 57 | |||
| 58 | return (ret != 3) ? -EIO : 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | static int ad5686_i2c_probe(struct i2c_client *i2c, | ||
| 62 | const struct i2c_device_id *id) | ||
| 63 | { | ||
| 64 | return ad5686_probe(&i2c->dev, id->driver_data, id->name, | ||
| 65 | ad5686_i2c_write, ad5686_i2c_read); | ||
| 66 | } | ||
| 67 | |||
| 68 | static int ad5686_i2c_remove(struct i2c_client *i2c) | ||
| 69 | { | ||
| 70 | return ad5686_remove(&i2c->dev); | ||
| 71 | } | ||
| 72 | |||
| 73 | static const struct i2c_device_id ad5686_i2c_id[] = { | ||
| 74 | {"ad5671r", ID_AD5671R}, | ||
| 75 | {"ad5675r", ID_AD5675R}, | ||
| 76 | {"ad5691r", ID_AD5691R}, | ||
| 77 | {"ad5692r", ID_AD5692R}, | ||
| 78 | {"ad5693", ID_AD5693}, | ||
| 79 | {"ad5693r", ID_AD5693R}, | ||
| 80 | {"ad5694", ID_AD5694}, | ||
| 81 | {"ad5694r", ID_AD5694R}, | ||
| 82 | {"ad5695r", ID_AD5695R}, | ||
| 83 | {"ad5696", ID_AD5696}, | ||
| 84 | {"ad5696r", ID_AD5696R}, | ||
| 85 | {} | ||
| 86 | }; | ||
| 87 | MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); | ||
| 88 | |||
| 89 | static struct i2c_driver ad5686_i2c_driver = { | ||
| 90 | .driver = { | ||
| 91 | .name = "ad5696", | ||
| 92 | }, | ||
| 93 | .probe = ad5686_i2c_probe, | ||
| 94 | .remove = ad5686_i2c_remove, | ||
| 95 | .id_table = ad5686_i2c_id, | ||
| 96 | }; | ||
| 97 | |||
| 98 | module_i2c_driver(ad5686_i2c_driver); | ||
| 99 | |||
| 100 | MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); | ||
| 101 | MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); | ||
| 102 | MODULE_LICENSE("GPL v2"); | ||
| @@ -2,6 +2,7 @@ · 1 unmodified line | |||
| 2 | * LTC2632 Digital to analog convertors spi driver | 2 | * LTC2632 Digital to analog convertors spi driver |
| 3 | * | 3 | * |
| 4 | * Copyright 2017 Maxime Roussin-B�langer | 4 | * Copyright 2017 Maxime Roussin-B�langer |
| 5 | * expanded by Silvan Murer <silvan.murer@gmail.com> | ||
| 5 | * | 6 | * |
| 6 | * Licensed under the GPL-2. | 7 | * Licensed under the GPL-2. |
| 7 | */ | 8 | */ |
| @@ -10,6 +11,7 @@ · 2 unmodified lines | |||
| 10 | #include <linux/spi/spi.h> | 11 | #include <linux/spi/spi.h> |
| 11 | #include <linux/module.h> | 12 | #include <linux/module.h> |
| 12 | #include <linux/iio/iio.h> | 13 | #include <linux/iio/iio.h> |
| 14 | #include <linux/regulator/consumer.h> | ||
| 13 | 15 | ||
| 14 | #define LTC2632_DAC_CHANNELS 2 | 16 | #define LTC2632_DAC_CHANNELS 2 |
| 15 | 17 | ||
| @@ -28,7 +30,7 @@ · 12 unmodified lines | |||
| 28 | /** | 30 | /** |
| 29 | * struct ltc2632_chip_info - chip specific information | 31 | * struct ltc2632_chip_info - chip specific information |
| 30 | * @channels: channel spec for the DAC | 32 | * @channels: channel spec for the DAC |
| 31 | * @vref_mv: reference voltage | 33 | * @vref_mv: internal reference voltage |
| 32 | */ | 34 | */ |
| 33 | struct ltc2632_chip_info { | 35 | struct ltc2632_chip_info { |
| 34 | const struct iio_chan_spec *channels; | 36 | const struct iio_chan_spec *channels; |
| @@ -39,10 +41,14 @@ struct ltc2632_chip_info { · 4 unmodified lines | |||
| 39 | * struct ltc2632_state - driver instance specific data | 41 | * struct ltc2632_state - driver instance specific data |
| 40 | * @spi_dev: pointer to the spi_device struct | 42 | * @spi_dev: pointer to the spi_device struct |
| 41 | * @powerdown_cache_mask used to show current channel powerdown state | 43 | * @powerdown_cache_mask used to show current channel powerdown state |
| 44 | * @vref_mv used reference voltage (internal or external) | ||
| 45 | * @vref_reg regulator for the reference voltage | ||
| 42 | */ | 46 | */ |
| 43 | struct ltc2632_state { | 47 | struct ltc2632_state { |
| 44 | struct spi_device *spi_dev; | 48 | struct spi_device *spi_dev; |
| 45 | unsigned int powerdown_cache_mask; | 49 | unsigned int powerdown_cache_mask; |
| 50 | int vref_mv; | ||
| 51 | struct regulator *vref_reg; | ||
| 46 | }; | 52 | }; |
| 47 | 53 | ||
| 48 | enum ltc2632_supported_device_ids { | 54 | enum ltc2632_supported_device_ids { |
| @@ -90,7 +96,7 @@ static int ltc2632_read_raw(struct iio_dev *indio_dev, · 41 unmodified lines | |||
| 90 | 96 | ||
| 91 | switch (m) { | 97 | switch (m) { |
| 92 | case IIO_CHAN_INFO_SCALE: | 98 | case IIO_CHAN_INFO_SCALE: |
| 93 | *val = chip_info->vref_mv; | 99 | *val = st->vref_mv; |
| 94 | *val2 = chan->scan_type.realbits; | 100 | *val2 = chan->scan_type.realbits; |
| 95 | return IIO_VAL_FRACTIONAL_LOG2; | 101 | return IIO_VAL_FRACTIONAL_LOG2; |
| 96 | } | 102 | } |
| @@ -246,6 +252,45 @@ static int ltc2632_probe(struct spi_device *spi) · 149 unmodified lines | |||
| 246 | chip_info = (struct ltc2632_chip_info *) | 252 | chip_info = (struct ltc2632_chip_info *) |
| 247 | spi_get_device_id(spi)->driver_data; | 253 | spi_get_device_id(spi)->driver_data; |
| 248 | 254 | ||
| 255 | st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); | ||
| 256 | if (PTR_ERR(st->vref_reg) == -ENODEV) { | ||
| 257 | /* use internal reference voltage */ | ||
| 258 | st->vref_reg = NULL; | ||
| 259 | st->vref_mv = chip_info->vref_mv; | ||
| 260 | |||
| 261 | ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, | ||
| 262 | 0, 0, 0); | ||
| 263 | if (ret) { | ||
| 264 | dev_err(&spi->dev, | ||
| 265 | "Set internal reference command failed, %d\n", | ||
| 266 | ret); | ||
| 267 | return ret; | ||
| 268 | } | ||
| 269 | } else if (IS_ERR(st->vref_reg)) { | ||
| 270 | dev_err(&spi->dev, | ||
| 271 | "Error getting voltage reference regulator\n"); | ||
| 272 | return PTR_ERR(st->vref_reg); | ||
| 273 | } else { | ||
| 274 | /* use external reference voltage */ | ||
| 275 | ret = regulator_enable(st->vref_reg); | ||
| 276 | if (ret) { | ||
| 277 | dev_err(&spi->dev, | ||
| 278 | "enable reference regulator failed, %d\n", | ||
| 279 | ret); | ||
| 280 | return ret; | ||
| 281 | } | ||
| 282 | st->vref_mv = regulator_get_voltage(st->vref_reg) / 1000; | ||
| 283 | |||
| 284 | ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER, | ||
| 285 | 0, 0, 0); | ||
| 286 | if (ret) { | ||
| 287 | dev_err(&spi->dev, | ||
| 288 | "Set external reference command failed, %d\n", | ||
| 289 | ret); | ||
| 290 | return ret; | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 249 | indio_dev->dev.parent = &spi->dev; | 294 | indio_dev->dev.parent = &spi->dev; |
| 250 | indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name | 295 | indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name |
| 251 | : spi_get_device_id(spi)->name; | 296 | : spi_get_device_id(spi)->name; |
| @@ -254,14 +299,20 @@ static int ltc2632_probe(struct spi_device *spi) · 2 unmodified lines | |||
| 254 | indio_dev->channels = chip_info->channels; | 299 | indio_dev->channels = chip_info->channels; |
| 255 | indio_dev->num_channels = LTC2632_DAC_CHANNELS; | 300 | indio_dev->num_channels = LTC2632_DAC_CHANNELS; |
| 256 | 301 | ||
| 257 | ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, 0, 0, 0); | 302 | return iio_device_register(indio_dev); |
| 258 | if (ret) { | 303 | } |
| 259 | dev_err(&spi->dev, | ||
| 260 | "Set internal reference command failed, %d\n", ret); | ||
| 261 | return ret; | ||
| 262 | } | ||
| 263 | 304 | ||
| 264 | return devm_iio_device_register(&spi->dev, indio_dev); | 305 | static int ltc2632_remove(struct spi_device *spi) |
| 306 | { | ||
| 307 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
| 308 | struct ltc2632_state *st = iio_priv(indio_dev); | ||
| 309 | |||
| 310 | iio_device_unregister(indio_dev); | ||
| 311 | |||
| 312 | if (st->vref_reg) | ||
| 313 | regulator_disable(st->vref_reg); | ||
| 314 | |||
| 315 | return 0; | ||
| 265 | } | 316 | } |
| 266 | 317 | ||
| 267 | static const struct spi_device_id ltc2632_id[] = { | 318 | static const struct spi_device_id ltc2632_id[] = { |
| @@ -275,15 +326,6 @@ static const struct spi_device_id ltc2632_id[] = { · 7 unmodified lines | |||
| 275 | }; | 326 | }; |
| 276 | MODULE_DEVICE_TABLE(spi, ltc2632_id); | 327 | MODULE_DEVICE_TABLE(spi, ltc2632_id); |
| 277 | 328 | ||
| 278 | static struct spi_driver ltc2632_driver = { | ||
| 279 | .driver = { | ||
| 280 | .name = "ltc2632", | ||
| 281 | }, | ||
| 282 | .probe = ltc2632_probe, | ||
| 283 | .id_table = ltc2632_id, | ||
| 284 | }; | ||
| 285 | module_spi_driver(ltc2632_driver); | ||
| 286 | |||
| 287 | static const struct of_device_id ltc2632_of_match[] = { | 329 | static const struct of_device_id ltc2632_of_match[] = { |
| 288 | { | 330 | { |
| 289 | .compatible = "lltc,ltc2632-l12", | 331 | .compatible = "lltc,ltc2632-l12", |
| @@ -308,6 +350,17 @@ static const struct of_device_id ltc2632_of_match[] = { · 18 unmodified lines | |||
| 308 | }; | 350 | }; |
| 309 | MODULE_DEVICE_TABLE(of, ltc2632_of_match); | 351 | MODULE_DEVICE_TABLE(of, ltc2632_of_match); |
| 310 | 352 | ||
| 353 | static struct spi_driver ltc2632_driver = { | ||
| 354 | .driver = { | ||
| 355 | .name = "ltc2632", | ||
| 356 | .of_match_table = of_match_ptr(ltc2632_of_match), | ||
| 357 | }, | ||
| 358 | .probe = ltc2632_probe, | ||
| 359 | .remove = ltc2632_remove, | ||
| 360 | .id_table = ltc2632_id, | ||
| 361 | }; | ||
| 362 | module_spi_driver(ltc2632_driver); | ||
| 363 | |||
| 311 | MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>"); | 364 | MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>"); |
| 312 | MODULE_DESCRIPTION("LTC2632 DAC SPI driver"); | 365 | MODULE_DESCRIPTION("LTC2632 DAC SPI driver"); |
| 313 | MODULE_LICENSE("GPL v2"); | 366 | MODULE_LICENSE("GPL v2"); |
| @@ -0,0 +1,439 @@ | |||
| 1 | /* | ||
| 2 | * ti-dac5571.c - Texas Instruments 8/10/12-bit 1/4-channel DAC driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2018 Prevas A/S | ||
| 5 | * | ||
| 6 | * http://www.ti.com/lit/ds/symlink/dac5571.pdf | ||
| 7 | * http://www.ti.com/lit/ds/symlink/dac6571.pdf | ||
| 8 | * http://www.ti.com/lit/ds/symlink/dac7571.pdf | ||
| 9 | * http://www.ti.com/lit/ds/symlink/dac5574.pdf | ||
| 10 | * http://www.ti.com/lit/ds/symlink/dac6574.pdf | ||
| 11 | * http://www.ti.com/lit/ds/symlink/dac7574.pdf | ||
| 12 | * http://www.ti.com/lit/ds/symlink/dac5573.pdf | ||
| 13 | * http://www.ti.com/lit/ds/symlink/dac6573.pdf | ||
| 14 | * http://www.ti.com/lit/ds/symlink/dac7573.pdf | ||
| 15 | * | ||
| 16 | * This program is free software; you can redistribute it and/or modify | ||
| 17 | * it under the terms of the GNU General Public License (version 2) as | ||
| 18 | * published by the Free Software Foundation. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/iio/iio.h> | ||
| 22 | #include <linux/i2c.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/of_device.h> | ||
| 25 | #include <linux/of.h> | ||
| 26 | #include <linux/regulator/consumer.h> | ||
| 27 | |||
| 28 | enum chip_id { | ||
| 29 | single_8bit, single_10bit, single_12bit, | ||
| 30 | quad_8bit, quad_10bit, quad_12bit | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct dac5571_spec { | ||
| 34 | u8 num_channels; | ||
| 35 | u8 resolution; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static const struct dac5571_spec dac5571_spec[] = { | ||
| 39 | [single_8bit] = {.num_channels = 1, .resolution = 8}, | ||
| 40 | [single_10bit] = {.num_channels = 1, .resolution = 10}, | ||
| 41 | [single_12bit] = {.num_channels = 1, .resolution = 12}, | ||
| 42 | [quad_8bit] = {.num_channels = 4, .resolution = 8}, | ||
| 43 | [quad_10bit] = {.num_channels = 4, .resolution = 10}, | ||
| 44 | [quad_12bit] = {.num_channels = 4, .resolution = 12}, | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct dac5571_data { | ||
| 48 | struct i2c_client *client; | ||
| 49 | int id; | ||
| 50 | struct mutex lock; | ||
| 51 | struct regulator *vref; | ||
| 52 | u16 val[4]; | ||
| 53 | bool powerdown; | ||
| 54 | u8 powerdown_mode; | ||
| 55 | struct dac5571_spec const *spec; | ||
| 56 | int (*dac5571_cmd)(struct dac5571_data *data, int channel, u16 val); | ||
| 57 | int (*dac5571_pwrdwn)(struct dac5571_data *data, int channel, u8 pwrdwn); | ||
| 58 | u8 buf[3] ____cacheline_aligned; | ||
| 59 | }; | ||
| 60 | |||
| 61 | #define DAC5571_POWERDOWN(mode) ((mode) + 1) | ||
| 62 | #define DAC5571_POWERDOWN_FLAG BIT(0) | ||
| 63 | #define DAC5571_CHANNEL_SELECT 1 | ||
| 64 | #define DAC5571_LOADMODE_DIRECT BIT(4) | ||
| 65 | #define DAC5571_SINGLE_PWRDWN_BITS 4 | ||
| 66 | #define DAC5571_QUAD_PWRDWN_BITS 6 | ||
| 67 | |||
| 68 | static int dac5571_cmd_single(struct dac5571_data *data, int channel, u16 val) | ||
| 69 | { | ||
| 70 | unsigned int shift; | ||
| 71 | |||
| 72 | shift = 12 - data->spec->resolution; | ||
| 73 | data->buf[1] = val << shift; | ||
| 74 | data->buf[0] = val >> (8 - shift); | ||
| 75 | |||
| 76 | if (i2c_master_send(data->client, data->buf, 2) != 2) | ||
| 77 | return -EIO; | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val) | ||
| 83 | { | ||
| 84 | unsigned int shift; | ||
| 85 | |||
| 86 | shift = 16 - data->spec->resolution; | ||
| 87 | data->buf[2] = val << shift; | ||
| 88 | data->buf[1] = (val >> (8 - shift)); | ||
| 89 | data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | | ||
| 90 | DAC5571_LOADMODE_DIRECT; | ||
| 91 | |||
| 92 | if (i2c_master_send(data->client, data->buf, 3) != 3) | ||
| 93 | return -EIO; | ||
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn) | ||
| 99 | { | ||
| 100 | unsigned int shift; | ||
| 101 | |||
| 102 | shift = 12 - data->spec->resolution; | ||
| 103 | data->buf[1] = 0; | ||
| 104 | data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS; | ||
| 105 | |||
| 106 | if (i2c_master_send(data->client, data->buf, 2) != 2) | ||
| 107 | return -EIO; | ||
| 108 | |||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn) | ||
| 113 | { | ||
| 114 | unsigned int shift; | ||
| 115 | |||
| 116 | shift = 16 - data->spec->resolution; | ||
| 117 | data->buf[2] = 0; | ||
| 118 | data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS; | ||
| 119 | data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | | ||
| 120 | DAC5571_LOADMODE_DIRECT | DAC5571_POWERDOWN_FLAG; | ||
| 121 | |||
| 122 | if (i2c_master_send(data->client, data->buf, 3) != 3) | ||
| 123 | return -EIO; | ||
| 124 | |||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | static const char *const dac5571_powerdown_modes[] = { | ||
| 129 | "1kohm_to_gnd", "100kohm_to_gnd", "three_state", | ||
| 130 | }; | ||
| 131 | |||
| 132 | static int dac5571_get_powerdown_mode(struct iio_dev *indio_dev, | ||
| 133 | const struct iio_chan_spec *chan) | ||
| 134 | { | ||
| 135 | struct dac5571_data *data = iio_priv(indio_dev); | ||
| 136 | |||
| 137 | return data->powerdown_mode; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int dac5571_set_powerdown_mode(struct iio_dev *indio_dev, | ||
| 141 | const struct iio_chan_spec *chan, | ||
| 142 | unsigned int mode) | ||
| 143 | { | ||
| 144 | struct dac5571_data *data = iio_priv(indio_dev); | ||
| 145 | int ret = 0; | ||
| 146 | |||
| 147 | if (data->powerdown_mode == mode) | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | mutex_lock(&data->lock); | ||
| 151 | if (data->powerdown) { | ||
| 152 | ret = data->dac5571_pwrdwn(data, chan->channel, | ||
| 153 | DAC5571_POWERDOWN(mode)); | ||
| 154 | if (ret) | ||
| 155 | goto out; | ||
| 156 | } | ||
| 157 | data->powerdown_mode = mode; | ||
| 158 | |||
| 159 | out: | ||
| 160 | mutex_unlock(&data->lock); | ||
| 161 | |||
| 162 | return ret; | ||
| 163 | } | ||
| 164 | |||
| 165 | static const struct iio_enum dac5571_powerdown_mode = { | ||
| 166 | .items = dac5571_powerdown_modes, | ||
| 167 | .num_items = ARRAY_SIZE(dac5571_powerdown_modes), | ||
| 168 | .get = dac5571_get_powerdown_mode, | ||
| 169 | .set = dac5571_set_powerdown_mode, | ||
| 170 | }; | ||
| 171 | |||
| 172 | static ssize_t dac5571_read_powerdown(struct iio_dev *indio_dev, | ||
| 173 | uintptr_t private, | ||
| 174 | const struct iio_chan_spec *chan, | ||
| 175 | char *buf) | ||
| 176 | { | ||
| 177 | struct dac5571_data *data = iio_priv(indio_dev); | ||
| 178 | |||
| 179 | return sprintf(buf, "%d\n", data->powerdown); | ||
| 180 | } | ||
| 181 | |||
| 182 | static ssize_t dac5571_write_powerdown(struct iio_dev *indio_dev, | ||
| 183 | uintptr_t private, | ||
| 184 | const struct iio_chan_spec *chan, | ||
| 185 | const char *buf, size_t len) | ||
| 186 | { | ||
| 187 | struct dac5571_data *data = iio_priv(indio_dev); | ||
| 188 | bool powerdown; | ||
| 189 | int ret; | ||
| 190 | |||
| 191 | ret = strtobool(buf, &powerdown); | ||
| 192 | if (ret) | ||
| 193 | return ret; | ||
| 194 | |||
| 195 | if (data->powerdown == powerdown) | ||
| 196 | return len; | ||
| 197 | |||
| 198 | mutex_lock(&data->lock); | ||
| 199 | if (powerdown) | ||
| 200 | ret = data->dac5571_pwrdwn(data, chan->channel, | ||
| 201 | DAC5571_POWERDOWN(data->powerdown_mode)); | ||
| 202 | else | ||
| 203 | ret = data->dac5571_cmd(data, chan->channel, data->val[0]); | ||
| 204 | if (ret) | ||
| 205 | goto out; | ||
| 206 | |||
| 207 | data->powerdown = powerdown; | ||
| 208 | |||
| 209 | out: | ||
| 210 | mutex_unlock(&data->lock); | ||
| 211 | |||
| 212 | return ret ? ret : len; | ||
| 213 | } | ||
| 214 | |||
| 215 | |||
| 216 | static const struct iio_chan_spec_ext_info dac5571_ext_info[] = { | ||
| 217 | { | ||
| 218 | .name = "powerdown", | ||
| 219 | .read = dac5571_read_powerdown, | ||
| 220 | .write = dac5571_write_powerdown, | ||
| 221 | .shared = IIO_SHARED_BY_TYPE, | ||
| 222 | }, | ||
| 223 | IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode), | ||
| 224 | IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode), | ||
| 225 | {}, | ||
| 226 | }; | ||
| 227 | |||
| 228 | #define dac5571_CHANNEL(chan, name) { \ | ||
| 229 | .type = IIO_VOLTAGE, \ | ||
| 230 | .channel = (chan), \ | ||
| 231 | .address = (chan), \ | ||
| 232 | .indexed = true, \ | ||
| 233 | .output = true, \ | ||
| 234 | .datasheet_name = name, \ | ||
| 235 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | ||
| 236 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | ||
| 237 | .ext_info = dac5571_ext_info, \ | ||
| 238 | } | ||
| 239 | |||
| 240 | static const struct iio_chan_spec dac5571_channels[] = { | ||
| 241 | dac5571_CHANNEL(0, "A"), | ||
| 242 | dac5571_CHANNEL(1, "B"), | ||
| 243 | dac5571_CHANNEL(2, "C"), | ||
| 244 | dac5571_CHANNEL(3, "D"), | ||
| 245 | }; | ||
| 246 | |||
| 247 | static int dac5571_read_raw(struct iio_dev *indio_dev, | ||
| 248 | struct iio_chan_spec const *chan, | ||
| 249 | int *val, int *val2, long mask) | ||
| 250 | { | ||
| 251 | struct dac5571_data *data = iio_priv(indio_dev); | ||
| 252 | int ret; | ||
| 253 | |||
| 254 | switch (mask) { | ||
| 255 | case IIO_CHAN_INFO_RAW: | ||
| 256 | *val = data->val[chan->channel]; | ||
| 257 | return IIO_VAL_INT; | ||
| 258 | |||
| 259 | case IIO_CHAN_INFO_SCALE: | ||
| 260 | ret = regulator_get_voltage(data->vref); | ||
| 261 | if (ret < 0) | ||
| 262 | return ret; | ||
| 263 | |||
| 264 | *val = ret / 1000; | ||
| 265 | *val2 = data->spec->resolution; | ||
| 266 | return IIO_VAL_FRACTIONAL_LOG2; | ||
| 267 | |||
| 268 | default: | ||
| 269 | return -EINVAL; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | static int dac5571_write_raw(struct iio_dev *indio_dev, | ||
| 274 | struct iio_chan_spec const *chan, | ||
| 275 | int val, int val2, long mask) | ||
| 276 | { | ||
| 277 | struct dac5571_data *data = iio_priv(indio_dev); | ||
| 278 | int ret; | ||
| 279 | |||
| 280 | switch (mask) { | ||
| 281 | case IIO_CHAN_INFO_RAW: | ||
| 282 | if (data->val[chan->channel] == val) | ||
| 283 | return 0; | ||
| 284 | |||
| 285 | if (val >= (1 << data->spec->resolution) || val < 0) | ||
| 286 | return -EINVAL; | ||
| 287 | |||
| 288 | if (data->powerdown) | ||
| 289 | return -EBUSY; | ||
| 290 | |||
| 291 | mutex_lock(&data->lock); | ||
| 292 | ret = data->dac5571_cmd(data, chan->channel, val); | ||
| 293 | if (ret == 0) | ||
| 294 | data->val[chan->channel] = val; | ||
| 295 | mutex_unlock(&data->lock); | ||
| 296 | return ret; | ||
| 297 | |||
| 298 | default: | ||
| 299 | return -EINVAL; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | static int dac5571_write_raw_get_fmt(struct iio_dev *indio_dev, | ||
| 304 | struct iio_chan_spec const *chan, | ||
| 305 | long mask) | ||
| 306 | { | ||
| 307 | return IIO_VAL_INT; | ||
| 308 | } | ||
| 309 | |||
| 310 | static const struct iio_info dac5571_info = { | ||
| 311 | .read_raw = dac5571_read_raw, | ||
| 312 | .write_raw = dac5571_write_raw, | ||
| 313 | .write_raw_get_fmt = dac5571_write_raw_get_fmt, | ||
| 314 | }; | ||
| 315 | |||
| 316 | static int dac5571_probe(struct i2c_client *client, | ||
| 317 | const struct i2c_device_id *id) | ||
| 318 | { | ||
| 319 | struct device *dev = &client->dev; | ||
| 320 | const struct dac5571_spec *spec; | ||
| 321 | struct dac5571_data *data; | ||
| 322 | struct iio_dev *indio_dev; | ||
| 323 | int ret, i; | ||
| 324 | |||
| 325 | indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); | ||
| 326 | if (!indio_dev) | ||
| 327 | return -ENOMEM; | ||
| 328 | |||
| 329 | data = iio_priv(indio_dev); | ||
| 330 | i2c_set_clientdata(client, indio_dev); | ||
| 331 | data->client = client; | ||
| 332 | |||
| 333 | indio_dev->dev.parent = dev; | ||
| 334 | indio_dev->dev.of_node = client->dev.of_node; | ||
| 335 | indio_dev->info = &dac5571_info; | ||
| 336 | indio_dev->name = id->name; | ||
| 337 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 338 | indio_dev->channels = dac5571_channels; | ||
| 339 | |||
| 340 | spec = &dac5571_spec[id->driver_data]; | ||
| 341 | indio_dev->num_channels = spec->num_channels; | ||
| 342 | data->spec = spec; | ||
| 343 | |||
| 344 | data->vref = devm_regulator_get(dev, "vref"); | ||
| 345 | if (IS_ERR(data->vref)) | ||
| 346 | return PTR_ERR(data->vref); | ||
| 347 | |||
| 348 | ret = regulator_enable(data->vref); | ||
| 349 | if (ret < 0) | ||
| 350 | return ret; | ||
| 351 | |||
| 352 | mutex_init(&data->lock); | ||
| 353 | |||
| 354 | switch (spec->num_channels) { | ||
| 355 | case 1: | ||
| 356 | data->dac5571_cmd = dac5571_cmd_single; | ||
| 357 | data->dac5571_pwrdwn = dac5571_pwrdwn_single; | ||
| 358 | break; | ||
| 359 | case 4: | ||
| 360 | data->dac5571_cmd = dac5571_cmd_quad; | ||
| 361 | data->dac5571_pwrdwn = dac5571_pwrdwn_quad; | ||
| 362 | break; | ||
| 363 | default: | ||
| 364 | goto err; | ||
| 365 | } | ||
| 366 | |||
| 367 | for (i = 0; i < spec->num_channels; i++) { | ||
| 368 | ret = data->dac5571_cmd(data, i, 0); | ||
| 369 | if (ret) { | ||
| 370 | dev_err(dev, "failed to initialize channel %d to 0\n", i); | ||
| 371 | goto err; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | ret = iio_device_register(indio_dev); | ||
| 376 | if (ret) | ||
| 377 | goto err; | ||
| 378 | |||
| 379 | return 0; | ||
| 380 | |||
| 381 | err: | ||
| 382 | regulator_disable(data->vref); | ||
| 383 | return ret; | ||
| 384 | } | ||
| 385 | |||
| 386 | static int dac5571_remove(struct i2c_client *i2c) | ||
| 387 | { | ||
| 388 | struct iio_dev *indio_dev = i2c_get_clientdata(i2c); | ||
| 389 | struct dac5571_data *data = iio_priv(indio_dev); | ||
| 390 | |||
| 391 | iio_device_unregister(indio_dev); | ||
| 392 | regulator_disable(data->vref); | ||
| 393 | |||
| 394 | return 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | #ifdef CONFIG_OF | ||
| 398 | static const struct of_device_id dac5571_of_id[] = { | ||
| 399 | {.compatible = "ti,dac5571"}, | ||
| 400 | {.compatible = "ti,dac6571"}, | ||
| 401 | {.compatible = "ti,dac7571"}, | ||
| 402 | {.compatible = "ti,dac5574"}, | ||
| 403 | {.compatible = "ti,dac6574"}, | ||
| 404 | {.compatible = "ti,dac7574"}, | ||
| 405 | {.compatible = "ti,dac5573"}, | ||
| 406 | {.compatible = "ti,dac6573"}, | ||
| 407 | {.compatible = "ti,dac7573"}, | ||
| 408 | {} | ||
| 409 | }; | ||
| 410 | MODULE_DEVICE_TABLE(of, dac5571_of_id); | ||
| 411 | #endif | ||
| 412 | |||
| 413 | static const struct i2c_device_id dac5571_id[] = { | ||
| 414 | {"dac5571", single_8bit}, | ||
| 415 | {"dac6571", single_10bit}, | ||
| 416 | {"dac7571", single_12bit}, | ||
| 417 | {"dac5574", quad_8bit}, | ||
| 418 | {"dac6574", quad_10bit}, | ||
| 419 | {"dac7574", quad_12bit}, | ||
| 420 | {"dac5573", quad_8bit}, | ||
| 421 | {"dac6573", quad_10bit}, | ||
| 422 | {"dac7573", quad_12bit}, | ||
| 423 | {} | ||
| 424 | }; | ||
| 425 | MODULE_DEVICE_TABLE(i2c, dac5571_id); | ||
| 426 | |||
| 427 | static struct i2c_driver dac5571_driver = { | ||
| 428 | .driver = { | ||
| 429 | .name = "ti-dac5571", | ||
| 430 | }, | ||
| 431 | .probe = dac5571_probe, | ||
| 432 | .remove = dac5571_remove, | ||
| 433 | .id_table = dac5571_id, | ||
| 434 | }; | ||
| 435 | module_i2c_driver(dac5571_driver); | ||
| 436 | |||
| 437 | MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>"); | ||
| 438 | MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver"); | ||
| 439 | MODULE_LICENSE("GPL v2"); | ||
| @@ -24,6 +24,7 @@ · 23 unmodified lines | |||
| 24 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
| 25 | #include <linux/iio/iio.h> | 25 | #include <linux/iio/iio.h> |
| 26 | #include <linux/acpi.h> | 26 | #include <linux/acpi.h> |
| 27 | #include <linux/platform_device.h> | ||
| 27 | #include "inv_mpu_iio.h" | 28 | #include "inv_mpu_iio.h" |
| 28 | 29 | ||
| 29 | /* | 30 | /* |
| @@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = { · 22 unmodified lines | |||
| 52 | .raw_accl = INV_MPU6050_REG_RAW_ACCEL, | 53 | .raw_accl = INV_MPU6050_REG_RAW_ACCEL, |
| 53 | .temperature = INV_MPU6050_REG_TEMPERATURE, | 54 | .temperature = INV_MPU6050_REG_TEMPERATURE, |
| 54 | .int_enable = INV_MPU6050_REG_INT_ENABLE, | 55 | .int_enable = INV_MPU6050_REG_INT_ENABLE, |
| 56 | .int_status = INV_MPU6050_REG_INT_STATUS, | ||
| 55 | .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, | 57 | .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, |
| 56 | .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, | 58 | .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, |
| 57 | .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, | 59 | .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, |
| @@ -86,6 +88,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = { · 28 unmodified lines | |||
| 86 | .gyro_fifo_enable = false, | 88 | .gyro_fifo_enable = false, |
| 87 | .accl_fifo_enable = false, | 89 | .accl_fifo_enable = false, |
| 88 | .accl_fs = INV_MPU6050_FS_02G, | 90 | .accl_fs = INV_MPU6050_FS_02G, |
| 91 | .user_ctrl = 0, | ||
| 89 | }; | 92 | }; |
| 90 | 93 | ||
| 91 | /* Indexed by enum inv_devices */ | 94 | /* Indexed by enum inv_devices */ |
| @@ -120,6 +123,12 @@ static const struct inv_mpu6050_hw hw_info[] = { · 28 unmodified lines | |||
| 120 | .reg = ®_set_6500, | 123 | .reg = ®_set_6500, |
| 121 | .config = &chip_config_6050, | 124 | .config = &chip_config_6050, |
| 122 | }, | 125 | }, |
| 126 | { | ||
| 127 | .whoami = INV_MPU9255_WHOAMI_VALUE, | ||
| 128 | .name = "MPU9255", | ||
| 129 | .reg = ®_set_6500, | ||
| 130 | .config = &chip_config_6050, | ||
| 131 | }, | ||
| 123 | { | 132 | { |
| 124 | .whoami = INV_ICM20608_WHOAMI_VALUE, | 133 | .whoami = INV_ICM20608_WHOAMI_VALUE, |
| 125 | .name = "ICM20608", | 134 | .name = "ICM20608", |
| @@ -168,7 +177,7 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) · 42 unmodified lines | |||
| 168 | return result; | 177 | return result; |
| 169 | 178 | ||
| 170 | if (en) { | 179 | if (en) { |
| 171 | /* Wait for output stabilize */ | 180 | /* Wait for output to stabilize */ |
| 172 | msleep(INV_MPU6050_TEMP_UP_TIME); | 181 | msleep(INV_MPU6050_TEMP_UP_TIME); |
| 173 | if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) { | 182 | if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) { |
| 174 | /* switch internal clock to PLL */ | 183 | /* switch internal clock to PLL */ |
| @@ -185,26 +194,29 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) · 10 unmodified lines | |||
| 185 | 194 | ||
| 186 | int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) | 195 | int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) |
| 187 | { | 196 | { |
| 188 | int result = 0; | 197 | int result; |
| 189 | 198 | ||
| 190 | if (power_on) { | 199 | if (power_on) { |
| 191 | if (!st->powerup_count) | 200 | if (!st->powerup_count) { |
| 192 | result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); | 201 | result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); |
| 193 | if (!result) | 202 | if (result) |
| 194 | st->powerup_count++; | 203 | return result; |
| 204 | usleep_range(INV_MPU6050_REG_UP_TIME_MIN, | ||
| 205 | INV_MPU6050_REG_UP_TIME_MAX); | ||
| 206 | } | ||
| 207 | st->powerup_count++; | ||
| 195 | } else { | 208 | } else { |
| 196 | st->powerup_count--; | 209 | if (st->powerup_count == 1) { |
| 197 | if (!st->powerup_count) | ||
| 198 | result = regmap_write(st->map, st->reg->pwr_mgmt_1, | 210 | result = regmap_write(st->map, st->reg->pwr_mgmt_1, |
| 199 | INV_MPU6050_BIT_SLEEP); | 211 | INV_MPU6050_BIT_SLEEP); |
| 212 | if (result) | ||
| 213 | return result; | ||
| 214 | } | ||
| 215 | st->powerup_count--; | ||
| 200 | } | 216 | } |
| 201 | 217 | ||
| 202 | if (result) | 218 | dev_dbg(regmap_get_device(st->map), "set power %d, count=%u\n", |
| 203 | return result; | 219 | power_on, st->powerup_count); |
| 204 | |||
| 205 | if (power_on) | ||
| 206 | usleep_range(INV_MPU6050_REG_UP_TIME_MIN, | ||
| 207 | INV_MPU6050_REG_UP_TIME_MAX); | ||
| 208 | 220 | ||
| 209 | return 0; | 221 | return 0; |
| 210 | } | 222 | } |
| @@ -262,26 +274,33 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) · 51 unmodified lines | |||
| 262 | d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); | 274 | d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); |
| 263 | result = regmap_write(st->map, st->reg->gyro_config, d); | 275 | result = regmap_write(st->map, st->reg->gyro_config, d); |
| 264 | if (result) | 276 | if (result) |
| 265 | return result; | 277 | goto error_power_off; |
| 266 | 278 | ||
| 267 | result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ); | 279 | result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ); |
| 268 | if (result) | 280 | if (result) |
| 269 | return result; | 281 | goto error_power_off; |
| 270 | 282 | ||
| 271 | d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1; | 283 | d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1; |
| 272 | result = regmap_write(st->map, st->reg->sample_rate_div, d); | 284 | result = regmap_write(st->map, st->reg->sample_rate_div, d); |
| 273 | if (result) | 285 | if (result) |
| 274 | return result; | 286 | goto error_power_off; |
| 275 | 287 | ||
| 276 | d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); | 288 | d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); |
| 277 | result = regmap_write(st->map, st->reg->accl_config, d); | 289 | result = regmap_write(st->map, st->reg->accl_config, d); |
| 290 | if (result) | ||
| 291 | goto error_power_off; | ||
| 292 | |||
| 293 | result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask); | ||
| 278 | if (result) | 294 | if (result) |
| 279 | return result; | 295 | return result; |
| 280 | 296 | ||
| 281 | memcpy(&st->chip_config, hw_info[st->chip_type].config, | 297 | memcpy(&st->chip_config, hw_info[st->chip_type].config, |
| 282 | sizeof(struct inv_mpu6050_chip_config)); | 298 | sizeof(struct inv_mpu6050_chip_config)); |
| 283 | result = inv_mpu6050_set_power_itg(st, false); | ||
| 284 | 299 | ||
| 300 | return inv_mpu6050_set_power_itg(st, false); | ||
| 301 | |||
| 302 | error_power_off: | ||
| 303 | inv_mpu6050_set_power_itg(st, false); | ||
| 285 | return result; | 304 | return result; |
| 286 | } | 305 | } |
| 287 | 306 | ||
| @@ -314,6 +333,65 @@ static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg, · 26 unmodified lines | |||
| 314 | return IIO_VAL_INT; | 333 | return IIO_VAL_INT; |
| 315 | } | 334 | } |
| 316 | 335 | ||
| 336 | static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, | ||
| 337 | struct iio_chan_spec const *chan, | ||
| 338 | int *val) | ||
| 339 | { | ||
| 340 | struct inv_mpu6050_state *st = iio_priv(indio_dev); | ||
| 341 | int result; | ||
| 342 | int ret; | ||
| 343 | |||
| 344 | result = inv_mpu6050_set_power_itg(st, true); | ||
| 345 | if (result) | ||
| 346 | return result; | ||
| 347 | |||
| 348 | switch (chan->type) { | ||
| 349 | case IIO_ANGL_VEL: | ||
| 350 | result = inv_mpu6050_switch_engine(st, true, | ||
| 351 | INV_MPU6050_BIT_PWR_GYRO_STBY); | ||
| 352 | if (result) | ||
| 353 | goto error_power_off; | ||
| 354 | ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, | ||
| 355 | chan->channel2, val); | ||
| 356 | result = inv_mpu6050_switch_engine(st, false, | ||
| 357 | INV_MPU6050_BIT_PWR_GYRO_STBY); | ||
| 358 | if (result) | ||
| 359 | goto error_power_off; | ||
| 360 | break; | ||
| 361 | case IIO_ACCEL: | ||
| 362 | result = inv_mpu6050_switch_engine(st, true, | ||
| 363 | INV_MPU6050_BIT_PWR_ACCL_STBY); | ||
| 364 | if (result) | ||
| 365 | goto error_power_off; | ||
| 366 | ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, | ||
| 367 | chan->channel2, val); | ||
| 368 | result = inv_mpu6050_switch_engine(st, false, | ||
| 369 | INV_MPU6050_BIT_PWR_ACCL_STBY); | ||
| 370 | if (result) | ||
| 371 | goto error_power_off; | ||
| 372 | break; | ||
| 373 | case IIO_TEMP: | ||
| 374 | /* wait for stablization */ | ||
| 375 | msleep(INV_MPU6050_SENSOR_UP_TIME); | ||
| 376 | ret = inv_mpu6050_sensor_show(st, st->reg->temperature, | ||
| 377 | IIO_MOD_X, val); | ||
| 378 | break; | ||
| 379 | default: | ||
| 380 | ret = -EINVAL; | ||
| 381 | break; | ||
| 382 | } | ||
| 383 | |||
| 384 | result = inv_mpu6050_set_power_itg(st, false); | ||
| 385 | if (result) | ||
| 386 | goto error_power_off; | ||
| 387 | |||
| 388 | return ret; | ||
| 389 | |||
| 390 | error_power_off: | ||
| 391 | inv_mpu6050_set_power_itg(st, false); | ||
| 392 | return result; | ||
| 393 | } | ||
| 394 | |||
| 317 | static int | 395 | static int |
| 318 | inv_mpu6050_read_raw(struct iio_dev *indio_dev, | 396 | inv_mpu6050_read_raw(struct iio_dev *indio_dev, |
| 319 | struct iio_chan_spec const *chan, | 397 | struct iio_chan_spec const *chan, |
| @@ -324,63 +402,14 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, · 4 unmodified lines | |||
| 324 | 402 | ||
| 325 | switch (mask) { | 403 | switch (mask) { |
| 326 | case IIO_CHAN_INFO_RAW: | 404 | case IIO_CHAN_INFO_RAW: |
| 327 | { | 405 | ret = iio_device_claim_direct_mode(indio_dev); |
| 328 | int result; | 406 | if (ret) |
| 329 | 407 | return ret; | |
| 330 | ret = IIO_VAL_INT; | ||
| 331 | mutex_lock(&st->lock); | 408 | mutex_lock(&st->lock); |
| 332 | result = iio_device_claim_direct_mode(indio_dev); | 409 | ret = inv_mpu6050_read_channel_data(indio_dev, chan, val); |
| 333 | if (result) | ||
| 334 | goto error_read_raw_unlock; | ||
| 335 | result = inv_mpu6050_set_power_itg(st, true); | ||
| 336 | if (result) | ||
| 337 | goto error_read_raw_release; | ||
| 338 | switch (chan->type) { | ||
| 339 | case IIO_ANGL_VEL: | ||
| 340 | result = inv_mpu6050_switch_engine(st, true, | ||
| 341 | INV_MPU6050_BIT_PWR_GYRO_STBY); | ||
| 342 | if (result) | ||
| 343 | goto error_read_raw_power_off; | ||
| 344 | ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, | ||
| 345 | chan->channel2, val); | ||
| 346 | result = inv_mpu6050_switch_engine(st, false, | ||
| 347 | INV_MPU6050_BIT_PWR_GYRO_STBY); | ||
| 348 | if (result) | ||
| 349 | goto error_read_raw_power_off; | ||
| 350 | break; | ||
| 351 | case IIO_ACCEL: | ||
| 352 | result = inv_mpu6050_switch_engine(st, true, | ||
| 353 | INV_MPU6050_BIT_PWR_ACCL_STBY); | ||
| 354 | if (result) | ||
| 355 | goto error_read_raw_power_off; | ||
| 356 | ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, | ||
| 357 | chan->channel2, val); | ||
| 358 | result = inv_mpu6050_switch_engine(st, false, | ||
| 359 | INV_MPU6050_BIT_PWR_ACCL_STBY); | ||
| 360 | if (result) | ||
| 361 | goto error_read_raw_power_off; | ||
| 362 | break; | ||
| 363 | case IIO_TEMP: | ||
| 364 | /* wait for stablization */ | ||
| 365 | msleep(INV_MPU6050_SENSOR_UP_TIME); | ||
| 366 | ret = inv_mpu6050_sensor_show(st, st->reg->temperature, | ||
| 367 | IIO_MOD_X, val); | ||
| 368 | break; | ||
| 369 | default: | ||
| 370 | ret = -EINVAL; | ||
| 371 | break; | ||
| 372 | } | ||
| 373 | error_read_raw_power_off: | ||
| 374 | result |= inv_mpu6050_set_power_itg(st, false); | ||
| 375 | error_read_raw_release: | ||
| 376 | iio_device_release_direct_mode(indio_dev); | ||
| 377 | error_read_raw_unlock: | ||
| 378 | mutex_unlock(&st->lock); | 410 | mutex_unlock(&st->lock); |
| 379 | if (result) | 411 | iio_device_release_direct_mode(indio_dev); |
| 380 | return result; | ||
| 381 | |||
| 382 | return ret; | 412 | return ret; |
| 383 | } | ||
| 384 | case IIO_CHAN_INFO_SCALE: | 413 | case IIO_CHAN_INFO_SCALE: |
| 385 | switch (chan->type) { | 414 | switch (chan->type) { |
| 386 | case IIO_ANGL_VEL: | 415 | case IIO_ANGL_VEL: |
| @@ -502,17 +531,18 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, · 115 unmodified lines | |||
| 502 | struct inv_mpu6050_state *st = iio_priv(indio_dev); | 531 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
| 503 | int result; | 532 | int result; |
| 504 | 533 | ||
| 505 | mutex_lock(&st->lock); | ||
| 506 | /* | 534 | /* |
| 507 | * we should only update scale when the chip is disabled, i.e. | 535 | * we should only update scale when the chip is disabled, i.e. |
| 508 | * not running | 536 | * not running |
| 509 | */ | 537 | */ |
| 510 | result = iio_device_claim_direct_mode(indio_dev); | 538 | result = iio_device_claim_direct_mode(indio_dev); |
| 511 | if (result) | 539 | if (result) |
| 512 | goto error_write_raw_unlock; | 540 | return result; |
| 541 | |||
| 542 | mutex_lock(&st->lock); | ||
| 513 | result = inv_mpu6050_set_power_itg(st, true); | 543 | result = inv_mpu6050_set_power_itg(st, true); |
| 514 | if (result) | 544 | if (result) |
| 515 | goto error_write_raw_release; | 545 | goto error_write_raw_unlock; |
| 516 | 546 | ||
| 517 | switch (mask) { | 547 | switch (mask) { |
| 518 | case IIO_CHAN_INFO_SCALE: | 548 | case IIO_CHAN_INFO_SCALE: |
| @@ -551,10 +581,9 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, · 32 unmodified lines | |||
| 551 | } | 581 | } |
| 552 | 582 | ||
| 553 | result |= inv_mpu6050_set_power_itg(st, false); | 583 | result |= inv_mpu6050_set_power_itg(st, false); |
| 554 | error_write_raw_release: | ||
| 555 | iio_device_release_direct_mode(indio_dev); | ||
| 556 | error_write_raw_unlock: | 584 | error_write_raw_unlock: |
| 557 | mutex_unlock(&st->lock); | 585 | mutex_unlock(&st->lock); |
| 586 | iio_device_release_direct_mode(indio_dev); | ||
| 558 | 587 | ||
| 559 | return result; | 588 | return result; |
| 560 | } | 589 | } |
| @@ -613,17 +642,18 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, · 52 unmodified lines | |||
| 613 | fifo_rate > INV_MPU6050_MAX_FIFO_RATE) | 642 | fifo_rate > INV_MPU6050_MAX_FIFO_RATE) |
| 614 | return -EINVAL; | 643 | return -EINVAL; |
| 615 | 644 | ||
| 645 | result = iio_device_claim_direct_mode(indio_dev); | ||
| 646 | if (result) | ||
| 647 | return result; | ||
| 648 | |||
| 616 | mutex_lock(&st->lock); | 649 | mutex_lock(&st->lock); |
| 617 | if (fifo_rate == st->chip_config.fifo_rate) { | 650 | if (fifo_rate == st->chip_config.fifo_rate) { |
| 618 | result = 0; | 651 | result = 0; |
| 619 | goto fifo_rate_fail_unlock; | 652 | goto fifo_rate_fail_unlock; |
| 620 | } | 653 | } |
| 621 | result = iio_device_claim_direct_mode(indio_dev); | ||
| 622 | if (result) | ||
| 623 | goto fifo_rate_fail_unlock; | ||
| 624 | result = inv_mpu6050_set_power_itg(st, true); | 654 | result = inv_mpu6050_set_power_itg(st, true); |
| 625 | if (result) | 655 | if (result) |
| 626 | goto fifo_rate_fail_release; | 656 | goto fifo_rate_fail_unlock; |
| 627 | 657 | ||
| 628 | d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1; | 658 | d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1; |
| 629 | result = regmap_write(st->map, st->reg->sample_rate_div, d); | 659 | result = regmap_write(st->map, st->reg->sample_rate_div, d); |
| @@ -637,10 +667,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, · 7 unmodified lines | |||
| 637 | 667 | ||
| 638 | fifo_rate_fail_power_off: | 668 | fifo_rate_fail_power_off: |
| 639 | result |= inv_mpu6050_set_power_itg(st, false); | 669 | result |= inv_mpu6050_set_power_itg(st, false); |
| 640 | fifo_rate_fail_release: | ||
| 641 | iio_device_release_direct_mode(indio_dev); | ||
| 642 | fifo_rate_fail_unlock: | 670 | fifo_rate_fail_unlock: |
| 643 | mutex_unlock(&st->lock); | 671 | mutex_unlock(&st->lock); |
| 672 | iio_device_release_direct_mode(indio_dev); | ||
| 644 | if (result) | 673 | if (result) |
| 645 | return result; | 674 | return result; |
| 646 | 675 | ||
| @@ -769,7 +798,14 @@ static const struct iio_chan_spec inv_mpu_channels[] = { · 122 unmodified lines | |||
| 769 | INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), | 798 | INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), |
| 770 | }; | 799 | }; |
| 771 | 800 | ||
| 772 | /* constant IIO attribute */ | 801 | /* |
| 802 | * The user can choose any frequency between INV_MPU6050_MIN_FIFO_RATE and | ||
| 803 | * INV_MPU6050_MAX_FIFO_RATE, but only these frequencies are matched by the | ||
| 804 | * low-pass filter. Specifically, each of these sampling rates are about twice | ||
| 805 | * the bandwidth of a corresponding low-pass filter, which should eliminate | ||
| 806 | * aliasing following the Nyquist principle. By picking a frequency different | ||
| 807 | * from these, the user risks aliasing effects. | ||
| 808 | */ | ||
| 773 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500"); | 809 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500"); |
| 774 | static IIO_CONST_ATTR(in_anglvel_scale_available, | 810 | static IIO_CONST_ATTR(in_anglvel_scale_available, |
| 775 | "0.000133090 0.000266181 0.000532362 0.001064724"); | 811 | "0.000133090 0.000266181 0.000532362 0.001064724"); |
| @@ -850,14 +886,11 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) · 74 unmodified lines | |||
| 850 | msleep(INV_MPU6050_POWER_UP_TIME); | 886 | msleep(INV_MPU6050_POWER_UP_TIME); |
| 851 | 887 | ||
| 852 | /* | 888 | /* |
| 853 | * toggle power state. After reset, the sleep bit could be on | 889 | * Turn power on. After reset, the sleep bit could be on |
| 854 | * or off depending on the OTP settings. Toggling power would | 890 | * or off depending on the OTP settings. Turning power on |
| 855 | * make it in a definite state as well as making the hardware | 891 | * make it in a definite state as well as making the hardware |
| 856 | * state align with the software state | 892 | * state align with the software state |
| 857 | */ | 893 | */ |
| 858 | result = inv_mpu6050_set_power_itg(st, false); | ||
| 859 | if (result) | ||
| 860 | return result; | ||
| 861 | result = inv_mpu6050_set_power_itg(st, true); | 894 | result = inv_mpu6050_set_power_itg(st, true); |
| 862 | if (result) | 895 | if (result) |
| 863 | return result; | 896 | return result; |
| @@ -865,13 +898,17 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) · 1 unmodified line | |||
| 865 | result = inv_mpu6050_switch_engine(st, false, | 898 | result = inv_mpu6050_switch_engine(st, false, |
| 866 | INV_MPU6050_BIT_PWR_ACCL_STBY); | 899 | INV_MPU6050_BIT_PWR_ACCL_STBY); |
| 867 | if (result) | 900 | if (result) |
| 868 | return result; | 901 | goto error_power_off; |
| 869 | result = inv_mpu6050_switch_engine(st, false, | 902 | result = inv_mpu6050_switch_engine(st, false, |
| 870 | INV_MPU6050_BIT_PWR_GYRO_STBY); | 903 | INV_MPU6050_BIT_PWR_GYRO_STBY); |
| 871 | if (result) | 904 | if (result) |
| 872 | return result; | 905 | goto error_power_off; |
| 873 | 906 | ||
| 874 | return 0; | 907 | return inv_mpu6050_set_power_itg(st, false); |
| 908 | |||
| 909 | error_power_off: | ||
| 910 | inv_mpu6050_set_power_itg(st, false); | ||
| 911 | return result; | ||
| 875 | } | 912 | } |
| 876 | 913 | ||
| 877 | int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, | 914 | int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, |
| @@ -882,6 +919,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, · 4 unmodified lines | |||
| 882 | struct inv_mpu6050_platform_data *pdata; | 919 | struct inv_mpu6050_platform_data *pdata; |
| 883 | struct device *dev = regmap_get_device(regmap); | 920 | struct device *dev = regmap_get_device(regmap); |
| 884 | int result; | 921 | int result; |
| 922 | struct irq_data *desc; | ||
| 923 | int irq_type; | ||
| 885 | 924 | ||
| 886 | indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); | 925 | indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); |
| 887 | if (!indio_dev) | 926 | if (!indio_dev) |
| @@ -913,20 +952,43 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, · 25 unmodified lines | |||
| 913 | st->plat_data = *pdata; | 952 | st->plat_data = *pdata; |
| 914 | } | 953 | } |
| 915 | 954 | ||
| 955 | desc = irq_get_irq_data(irq); | ||
| 956 | if (!desc) { | ||
| 957 | dev_err(dev, "Could not find IRQ %d\n", irq); | ||
| 958 | return -EINVAL; | ||
| 959 | } | ||
| 960 | |||
| 961 | irq_type = irqd_get_trigger_type(desc); | ||
| 962 | if (irq_type == IRQF_TRIGGER_RISING) | ||
| 963 | st->irq_mask = INV_MPU6050_ACTIVE_HIGH; | ||
| 964 | else if (irq_type == IRQF_TRIGGER_FALLING) | ||
| 965 | st->irq_mask = INV_MPU6050_ACTIVE_LOW; | ||
| 966 | else if (irq_type == IRQF_TRIGGER_HIGH) | ||
| 967 | st->irq_mask = INV_MPU6050_ACTIVE_HIGH | | ||
| 968 | INV_MPU6050_LATCH_INT_EN; | ||
| 969 | else if (irq_type == IRQF_TRIGGER_LOW) | ||
| 970 | st->irq_mask = INV_MPU6050_ACTIVE_LOW | | ||
| 971 | INV_MPU6050_LATCH_INT_EN; | ||
| 972 | else { | ||
| 973 | dev_err(dev, "Invalid interrupt type 0x%x specified\n", | ||
| 974 | irq_type); | ||
| 975 | return -EINVAL; | ||
| 976 | } | ||
| 977 | |||
| 916 | /* power is turned on inside check chip type*/ | 978 | /* power is turned on inside check chip type*/ |
| 917 | result = inv_check_and_setup_chip(st); | 979 | result = inv_check_and_setup_chip(st); |
| 918 | if (result) | 980 | if (result) |
| 919 | return result; | 981 | return result; |
| 920 | 982 | ||
| 921 | if (inv_mpu_bus_setup) | ||
| 922 | inv_mpu_bus_setup(indio_dev); | ||
| 923 | |||
| 924 | result = inv_mpu6050_init_config(indio_dev); | 983 | result = inv_mpu6050_init_config(indio_dev); |
| 925 | if (result) { | 984 | if (result) { |
| 926 | dev_err(dev, "Could not initialize device.\n"); | 985 | dev_err(dev, "Could not initialize device.\n"); |
| 927 | return result; | 986 | return result; |
| 928 | } | 987 | } |
| 929 | 988 | ||
| 989 | if (inv_mpu_bus_setup) | ||
| 990 | inv_mpu_bus_setup(indio_dev); | ||
| 991 | |||
| 930 | dev_set_drvdata(dev, indio_dev); | 992 | dev_set_drvdata(dev, indio_dev); |
| 931 | indio_dev->dev.parent = dev; | 993 | indio_dev->dev.parent = dev; |
| 932 | /* name will be NULL when enumerated via ACPI */ | 994 | /* name will be NULL when enumerated via ACPI */ |
| @@ -940,50 +1002,32 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, · 7 unmodified lines | |||
| 940 | indio_dev->info = &mpu_info; | 1002 | indio_dev->info = &mpu_info; |
| 941 | indio_dev->modes = INDIO_BUFFER_TRIGGERED; | 1003 | indio_dev->modes = INDIO_BUFFER_TRIGGERED; |
| 942 | 1004 | ||
| 943 | result = iio_triggered_buffer_setup(indio_dev, | 1005 | result = devm_iio_triggered_buffer_setup(dev, indio_dev, |
| 944 | inv_mpu6050_irq_handler, | 1006 | inv_mpu6050_irq_handler, |
| 945 | inv_mpu6050_read_fifo, | 1007 | inv_mpu6050_read_fifo, |
| 946 | NULL); | 1008 | NULL); |
| 947 | if (result) { | 1009 | if (result) { |
| 948 | dev_err(dev, "configure buffer fail %d\n", result); | 1010 | dev_err(dev, "configure buffer fail %d\n", result); |
| 949 | return result; | 1011 | return result; |
| 950 | } | 1012 | } |
| 951 | result = inv_mpu6050_probe_trigger(indio_dev); | 1013 | result = inv_mpu6050_probe_trigger(indio_dev, irq_type); |
| 952 | if (result) { | 1014 | if (result) { |
| 953 | dev_err(dev, "trigger probe fail %d\n", result); | 1015 | dev_err(dev, "trigger probe fail %d\n", result); |
| 954 | goto out_unreg_ring; | 1016 | return result; |
| 955 | } | 1017 | } |
| 956 | 1018 | ||
| 957 | INIT_KFIFO(st->timestamps); | 1019 | INIT_KFIFO(st->timestamps); |
| 958 | spin_lock_init(&st->time_stamp_lock); | 1020 | spin_lock_init(&st->time_stamp_lock); |
| 959 | result = iio_device_register(indio_dev); | 1021 | result = devm_iio_device_register(dev, indio_dev); |
| 960 | if (result) { | 1022 | if (result) { |
| 961 | dev_err(dev, "IIO register fail %d\n", result); | 1023 | dev_err(dev, "IIO register fail %d\n", result); |
| 962 | goto out_remove_trigger; | 1024 | return result; |
| 963 | } | 1025 | } |
| 964 | 1026 | ||
| 965 | return 0; | 1027 | return 0; |
| 966 | |||
| 967 | out_remove_trigger: | ||
| 968 | inv_mpu6050_remove_trigger(st); | ||
| 969 | out_unreg_ring: | ||
| 970 | iio_triggered_buffer_cleanup(indio_dev); | ||
| 971 | return result; | ||
| 972 | } | 1028 | } |
| 973 | EXPORT_SYMBOL_GPL(inv_mpu_core_probe); | 1029 | EXPORT_SYMBOL_GPL(inv_mpu_core_probe); |
| 974 | 1030 | ||
| 975 | int inv_mpu_core_remove(struct device *dev) | ||
| 976 | { | ||
| 977 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 978 | |||
| 979 | iio_device_unregister(indio_dev); | ||
| 980 | inv_mpu6050_remove_trigger(iio_priv(indio_dev)); | ||
| 981 | iio_triggered_buffer_cleanup(indio_dev); | ||
| 982 | |||
| 983 | return 0; | ||
| 984 | } | ||
| 985 | EXPORT_SYMBOL_GPL(inv_mpu_core_remove); | ||
| 986 | |||
| 987 | #ifdef CONFIG_PM_SLEEP | 1031 | #ifdef CONFIG_PM_SLEEP |
| 988 | 1032 | ||
| 989 | static int inv_mpu_resume(struct device *dev) | 1033 | static int inv_mpu_resume(struct device *dev) |
| @@ -29,25 +29,18 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) · 28 unmodified lines | |||
| 29 | { | 29 | { |
| 30 | struct iio_dev *indio_dev = i2c_mux_priv(muxc); | 30 | struct iio_dev *indio_dev = i2c_mux_priv(muxc); |
| 31 | struct inv_mpu6050_state *st = iio_priv(indio_dev); | 31 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
| 32 | int ret = 0; | 32 | int ret; |
| 33 | 33 | ||
| 34 | /* Use the same mutex which was used everywhere to protect power-op */ | ||
| 35 | mutex_lock(&st->lock); | 34 | mutex_lock(&st->lock); |
| 36 | if (!st->powerup_count) { | ||
| 37 | ret = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); | ||
| 38 | if (ret) | ||
| 39 | goto write_error; | ||
| 40 | 35 | ||
| 41 | usleep_range(INV_MPU6050_REG_UP_TIME_MIN, | 36 | ret = inv_mpu6050_set_power_itg(st, true); |
| 42 | INV_MPU6050_REG_UP_TIME_MAX); | 37 | if (ret) |
| 43 | } | 38 | goto error_unlock; |
| 44 | if (!ret) { | 39 | |
| 45 | st->powerup_count++; | 40 | ret = regmap_write(st->map, st->reg->int_pin_cfg, |
| 46 | ret = regmap_write(st->map, st->reg->int_pin_cfg, | 41 | st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); |
| 47 | INV_MPU6050_INT_PIN_CFG | | 42 | |
| 48 | INV_MPU6050_BIT_BYPASS_EN); | 43 | error_unlock: |
| 49 | } | ||
| 50 | write_error: | ||
| 51 | mutex_unlock(&st->lock); | 44 | mutex_unlock(&st->lock); |
| 52 | 45 | ||
| 53 | return ret; | 46 | return ret; |
| @@ -59,12 +52,11 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) · 5 unmodified lines | |||
| 59 | struct inv_mpu6050_state *st = iio_priv(indio_dev); | 52 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
| 60 | 53 | ||
| 61 | mutex_lock(&st->lock); | 54 | mutex_lock(&st->lock); |
| 62 | /* It doesn't really mattter, if any of the calls fails */ | 55 | |
| 63 | regmap_write(st->map, st->reg->int_pin_cfg, INV_MPU6050_INT_PIN_CFG); | 56 | /* It doesn't really matter if any of the calls fail */ |
| 64 | st->powerup_count--; | 57 | regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask); |
| 65 | if (!st->powerup_count) | 58 | inv_mpu6050_set_power_itg(st, false); |
| 66 | regmap_write(st->map, st->reg->pwr_mgmt_1, | 59 | |
| 67 | INV_MPU6050_BIT_SLEEP); | ||
| 68 | mutex_unlock(&st->lock); | 60 | mutex_unlock(&st->lock); |
| 69 | 61 | ||
| 70 | return 0; | 62 | return 0; |
| @@ -133,29 +125,32 @@ static int inv_mpu_probe(struct i2c_client *client, · 62 unmodified lines | |||
| 133 | return result; | 125 | return result; |
| 134 | 126 | ||
| 135 | st = iio_priv(dev_get_drvdata(&client->dev)); | 127 | st = iio_priv(dev_get_drvdata(&client->dev)); |
| 136 | st->muxc = i2c_mux_alloc(client->adapter, &client->dev, | 128 | switch (st->chip_type) { |
| 137 | 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, | 129 | case INV_ICM20608: |
| 138 | inv_mpu6050_select_bypass, | 130 | /* no i2c auxiliary bus on the chip */ |
| 139 | inv_mpu6050_deselect_bypass); | 131 | break; |
| 140 | if (!st->muxc) { | 132 | default: |
| 141 | result = -ENOMEM; | 133 | /* declare i2c auxiliary bus */ |
| 142 | goto out_unreg_device; | 134 | st->muxc = i2c_mux_alloc(client->adapter, &client->dev, |
| 135 | 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, | ||
| 136 | inv_mpu6050_select_bypass, | ||
| 137 | inv_mpu6050_deselect_bypass); | ||
| 138 | if (!st->muxc) | ||
| 139 | return -ENOMEM; | ||
| 140 | st->muxc->priv = dev_get_drvdata(&client->dev); | ||
| 141 | result = i2c_mux_add_adapter(st->muxc, 0, 0, 0); | ||
| 142 | if (result) | ||
| 143 | return result; | ||
| 144 | result = inv_mpu_acpi_create_mux_client(client); | ||
| 145 | if (result) | ||
| 146 | goto out_del_mux; | ||
| 147 | break; | ||
| 143 | } | 148 | } |
| 144 | st->muxc->priv = dev_get_drvdata(&client->dev); | ||
| 145 | result = i2c_mux_add_adapter(st->muxc, 0, 0, 0); | ||
| 146 | if (result) | ||
| 147 | goto out_unreg_device; | ||
| 148 | |||
| 149 | result = inv_mpu_acpi_create_mux_client(client); | ||
| 150 | if (result) | ||
| 151 | goto out_del_mux; | ||
| 152 | 149 | ||
| 153 | return 0; | 150 | return 0; |
| 154 | 151 | ||
| 155 | out_del_mux: | 152 | out_del_mux: |
| 156 | i2c_mux_del_adapters(st->muxc); | 153 | i2c_mux_del_adapters(st->muxc); |
| 157 | out_unreg_device: | ||
| 158 | inv_mpu_core_remove(&client->dev); | ||
| 159 | return result; | 154 | return result; |
| 160 | } | 155 | } |
| 161 | 156 | ||
| @@ -164,10 +159,12 @@ static int inv_mpu_remove(struct i2c_client *client) · 2 unmodified lines | |||
| 164 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | 159 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
| 165 | struct inv_mpu6050_state *st = iio_priv(indio_dev); | 160 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
| 166 | 161 | ||
| 167 | inv_mpu_acpi_delete_mux_client(client); | 162 | if (st->muxc) { |
| 168 | i2c_mux_del_adapters(st->muxc); | 163 | inv_mpu_acpi_delete_mux_client(client); |
| 164 | i2c_mux_del_adapters(st->muxc); | ||
| 165 | } | ||
| 169 | 166 | ||
| 170 | return inv_mpu_core_remove(&client->dev); | 167 | return 0; |
| 171 | } | 168 | } |
| 172 | 169 | ||
| 173 | /* | 170 | /* |
| @@ -179,6 +176,7 @@ static const struct i2c_device_id inv_mpu_id[] = { · 5 unmodified lines | |||
| 179 | {"mpu6500", INV_MPU6500}, | 176 | {"mpu6500", INV_MPU6500}, |
| 180 | {"mpu9150", INV_MPU9150}, | 177 | {"mpu9150", INV_MPU9150}, |
| 181 | {"mpu9250", INV_MPU9250}, | 178 | {"mpu9250", INV_MPU9250}, |
| 179 | {"mpu9255", INV_MPU9255}, | ||
| 182 | {"icm20608", INV_ICM20608}, | 180 | {"icm20608", INV_ICM20608}, |
| 183 | {} | 181 | {} |
| 184 | }; | 182 | }; |
| @@ -202,6 +200,10 @@ static const struct of_device_id inv_of_match[] = { · 17 unmodified lines | |||
| 202 | .compatible = "invensense,mpu9250", | 200 | .compatible = "invensense,mpu9250", |
| 203 | .data = (void *)INV_MPU9250 | 201 | .data = (void *)INV_MPU9250 |
| 204 | }, | 202 | }, |
| 203 | { | ||
| 204 | .compatible = "invensense,mpu9255", | ||
| 205 | .data = (void *)INV_MPU9255 | ||
| 206 | }, | ||
| 205 | { | 207 | { |
| 206 | .compatible = "invensense,icm20608", | 208 | .compatible = "invensense,icm20608", |
| 207 | .data = (void *)INV_ICM20608 | 209 | .data = (void *)INV_ICM20608 |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | |||
| 40 | * @raw_accl: Address of first accel register. | 40 | * @raw_accl: Address of first accel register. |
| 41 | * @temperature: temperature register | 41 | * @temperature: temperature register |
| 42 | * @int_enable: Interrupt enable register. | 42 | * @int_enable: Interrupt enable register. |
| 43 | * @int_status: Interrupt status register. | ||
| 43 | * @pwr_mgmt_1: Controls chip's power state and clock source. | 44 | * @pwr_mgmt_1: Controls chip's power state and clock source. |
| 44 | * @pwr_mgmt_2: Controls power state of individual sensors. | 45 | * @pwr_mgmt_2: Controls power state of individual sensors. |
| 45 | * @int_pin_cfg; Controls interrupt pin configuration. | 46 | * @int_pin_cfg; Controls interrupt pin configuration. |
| @@ -60,6 +61,7 @@ struct inv_mpu6050_reg_map { · 14 unmodified lines | |||
| 60 | u8 raw_accl; | 61 | u8 raw_accl; |
| 61 | u8 temperature; | 62 | u8 temperature; |
| 62 | u8 int_enable; | 63 | u8 int_enable; |
| 64 | u8 int_status; | ||
| 63 | u8 pwr_mgmt_1; | 65 | u8 pwr_mgmt_1; |
| 64 | u8 pwr_mgmt_2; | 66 | u8 pwr_mgmt_2; |
| 65 | u8 int_pin_cfg; | 67 | u8 int_pin_cfg; |
| @@ -74,6 +76,7 @@ enum inv_devices { · 8 unmodified lines | |||
| 74 | INV_MPU6000, | 76 | INV_MPU6000, |
| 75 | INV_MPU9150, | 77 | INV_MPU9150, |
| 76 | INV_MPU9250, | 78 | INV_MPU9250, |
| 79 | INV_MPU9255, | ||
| 77 | INV_ICM20608, | 80 | INV_ICM20608, |
| 78 | INV_NUM_PARTS | 81 | INV_NUM_PARTS |
| 79 | }; | 82 | }; |
| @@ -94,6 +97,7 @@ struct inv_mpu6050_chip_config { · 14 unmodified lines | |||
| 94 | unsigned int accl_fifo_enable:1; | 97 | unsigned int accl_fifo_enable:1; |
| 95 | unsigned int gyro_fifo_enable:1; | 98 | unsigned int gyro_fifo_enable:1; |
| 96 | u16 fifo_rate; | 99 | u16 fifo_rate; |
| 100 | u8 user_ctrl; | ||
| 97 | }; | 101 | }; |
| 98 | 102 | ||
| 99 | /** | 103 | /** |
| @@ -125,6 +129,7 @@ struct inv_mpu6050_hw { · 25 unmodified lines | |||
| 125 | * @timestamps: kfifo queue to store time stamp. | 129 | * @timestamps: kfifo queue to store time stamp. |
| 126 | * @map regmap pointer. | 130 | * @map regmap pointer. |
| 127 | * @irq interrupt number. | 131 | * @irq interrupt number. |
| 132 | * @irq_mask the int_pin_cfg mask to configure interrupt type. | ||
| 128 | */ | 133 | */ |
| 129 | struct inv_mpu6050_state { | 134 | struct inv_mpu6050_state { |
| 130 | #define TIMESTAMP_FIFO_SIZE 16 | 135 | #define TIMESTAMP_FIFO_SIZE 16 |
| @@ -143,6 +148,8 @@ struct inv_mpu6050_state { · 12 unmodified lines | |||
| 143 | DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); | 148 | DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); |
| 144 | struct regmap *map; | 149 | struct regmap *map; |
| 145 | int irq; | 150 | int irq; |
| 151 | u8 irq_mask; | ||
| 152 | unsigned skip_samples; | ||
| 146 | }; | 153 | }; |
| 147 | 154 | ||
| 148 | /*register and associated bit definition*/ | 155 | /*register and associated bit definition*/ |
| @@ -166,6 +173,9 @@ struct inv_mpu6050_state { · 17 unmodified lines | |||
| 166 | #define INV_MPU6050_REG_TEMPERATURE 0x41 | 173 | #define INV_MPU6050_REG_TEMPERATURE 0x41 |
| 167 | #define INV_MPU6050_REG_RAW_GYRO 0x43 | 174 | #define INV_MPU6050_REG_RAW_GYRO 0x43 |
| 168 | 175 | ||
| 176 | #define INV_MPU6050_REG_INT_STATUS 0x3A | ||
| 177 | #define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 | ||
| 178 | |||
| 169 | #define INV_MPU6050_REG_USER_CTRL 0x6A | 179 | #define INV_MPU6050_REG_USER_CTRL 0x6A |
| 170 | #define INV_MPU6050_BIT_FIFO_RST 0x04 | 180 | #define INV_MPU6050_BIT_FIFO_RST 0x04 |
| 171 | #define INV_MPU6050_BIT_DMP_RST 0x08 | 181 | #define INV_MPU6050_BIT_DMP_RST 0x08 |
| @@ -215,8 +225,12 @@ struct inv_mpu6050_state { · 43 unmodified lines | |||
| 215 | #define INV_MPU6050_OUTPUT_DATA_SIZE 24 | 225 | #define INV_MPU6050_OUTPUT_DATA_SIZE 24 |
| 216 | 226 | ||
| 217 | #define INV_MPU6050_REG_INT_PIN_CFG 0x37 | 227 | #define INV_MPU6050_REG_INT_PIN_CFG 0x37 |
| 228 | #define INV_MPU6050_ACTIVE_HIGH 0x00 | ||
| 229 | #define INV_MPU6050_ACTIVE_LOW 0x80 | ||
| 230 | /* enable level triggering */ | ||
| 231 | #define INV_MPU6050_LATCH_INT_EN 0x20 | ||
| 218 | #define INV_MPU6050_BIT_BYPASS_EN 0x2 | 232 | #define INV_MPU6050_BIT_BYPASS_EN 0x2 |
| 219 | #define INV_MPU6050_INT_PIN_CFG 0 | 233 | |
| 220 | 234 | ||
| 221 | /* init parameters */ | 235 | /* init parameters */ |
| 222 | #define INV_MPU6050_INIT_FIFO_RATE 50 | 236 | #define INV_MPU6050_INIT_FIFO_RATE 50 |
| @@ -232,6 +246,7 @@ struct inv_mpu6050_state { · 9 unmodified lines | |||
| 232 | #define INV_MPU6500_WHOAMI_VALUE 0x70 | 246 | #define INV_MPU6500_WHOAMI_VALUE 0x70 |
| 233 | #define INV_MPU9150_WHOAMI_VALUE 0x68 | 247 | #define INV_MPU9150_WHOAMI_VALUE 0x68 |
| 234 | #define INV_MPU9250_WHOAMI_VALUE 0x71 | 248 | #define INV_MPU9250_WHOAMI_VALUE 0x71 |
| 249 | #define INV_MPU9255_WHOAMI_VALUE 0x73 | ||
| 235 | #define INV_ICM20608_WHOAMI_VALUE 0xAF | 250 | #define INV_ICM20608_WHOAMI_VALUE 0xAF |
| 236 | 251 | ||
| 237 | /* scan element definition */ | 252 | /* scan element definition */ |
| @@ -287,8 +302,7 @@ enum inv_mpu6050_clock_sel_e { · 49 unmodified lines | |||
| 287 | 302 | ||
| 288 | irqreturn_t inv_mpu6050_irq_handler(int irq, void *p); | 303 | irqreturn_t inv_mpu6050_irq_handler(int irq, void *p); |
| 289 | irqreturn_t inv_mpu6050_read_fifo(int irq, void *p); | 304 | irqreturn_t inv_mpu6050_read_fifo(int irq, void *p); |
| 290 | int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev); | 305 | int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type); |
| 291 | void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st); | ||
| 292 | int inv_reset_fifo(struct iio_dev *indio_dev); | 306 | int inv_reset_fifo(struct iio_dev *indio_dev); |
| 293 | int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask); | 307 | int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask); |
| 294 | int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); | 308 | int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); |
| @@ -297,6 +311,4 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client); · 2 unmodified lines | |||
| 297 | void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); | 311 | void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); |
| 298 | int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, | 312 | int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, |
| 299 | int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type); | 313 | int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type); |
| 300 | int inv_mpu_core_remove(struct device *dev); | ||
| 301 | int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on); | ||
| 302 | extern const struct dev_pm_ops inv_mpu_pmops; | 314 | extern const struct dev_pm_ops inv_mpu_pmops; |
| @@ -51,13 +51,14 @@ int inv_reset_fifo(struct iio_dev *indio_dev) · 50 unmodified lines | |||
| 51 | if (result) | 51 | if (result) |
| 52 | goto reset_fifo_fail; | 52 | goto reset_fifo_fail; |
| 53 | /* disable fifo reading */ | 53 | /* disable fifo reading */ |
| 54 | result = regmap_write(st->map, st->reg->user_ctrl, 0); | 54 | result = regmap_write(st->map, st->reg->user_ctrl, |
| 55 | st->chip_config.user_ctrl); | ||
| 55 | if (result) | 56 | if (result) |
| 56 | goto reset_fifo_fail; | 57 | goto reset_fifo_fail; |
| 57 | 58 | ||
| 58 | /* reset FIFO*/ | 59 | /* reset FIFO*/ |
| 59 | result = regmap_write(st->map, st->reg->user_ctrl, | 60 | d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; |
| 60 | INV_MPU6050_BIT_FIFO_RST); | 61 | result = regmap_write(st->map, st->reg->user_ctrl, d); |
| 61 | if (result) | 62 | if (result) |
| 62 | goto reset_fifo_fail; | 63 | goto reset_fifo_fail; |
| 63 | 64 | ||
| @@ -72,9 +73,9 @@ int inv_reset_fifo(struct iio_dev *indio_dev) · 8 unmodified lines | |||
| 72 | if (result) | 73 | if (result) |
| 73 | return result; | 74 | return result; |
| 74 | } | 75 | } |
| 75 | /* enable FIFO reading and I2C master interface*/ | 76 | /* enable FIFO reading */ |
| 76 | result = regmap_write(st->map, st->reg->user_ctrl, | 77 | d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN; |
| 77 | INV_MPU6050_BIT_FIFO_EN); | 78 | result = regmap_write(st->map, st->reg->user_ctrl, d); |
| 78 | if (result) | 79 | if (result) |
| 79 | goto reset_fifo_fail; | 80 | goto reset_fifo_fail; |
| 80 | /* enable sensor output to FIFO */ | 81 | /* enable sensor output to FIFO */ |
| @@ -127,8 +128,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) · 46 unmodified lines | |||
| 127 | u8 data[INV_MPU6050_OUTPUT_DATA_SIZE]; | 128 | u8 data[INV_MPU6050_OUTPUT_DATA_SIZE]; |
| 128 | u16 fifo_count; | 129 | u16 fifo_count; |
| 129 | s64 timestamp; | 130 | s64 timestamp; |
| 131 | int int_status; | ||
| 130 | 132 | ||
| 131 | mutex_lock(&st->lock); | 133 | mutex_lock(&st->lock); |
| 134 | |||
| 135 | /* ack interrupt and check status */ | ||
| 136 | result = regmap_read(st->map, st->reg->int_status, &int_status); | ||
| 137 | if (result) { | ||
| 138 | dev_err(regmap_get_device(st->map), | ||
| 139 | "failed to ack interrupt\n"); | ||
| 140 | goto flush_fifo; | ||
| 141 | } | ||
| 142 | if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) { | ||
| 143 | dev_warn(regmap_get_device(st->map), | ||
| 144 | "spurious interrupt with status 0x%x\n", int_status); | ||
| 145 | goto end_session; | ||
| 146 | } | ||
| 147 | |||
| 132 | if (!(st->chip_config.accl_fifo_enable | | 148 | if (!(st->chip_config.accl_fifo_enable | |
| 133 | st->chip_config.gyro_fifo_enable)) | 149 | st->chip_config.gyro_fifo_enable)) |
| 134 | goto end_session; | 150 | goto end_session; |
| @@ -140,7 +156,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) · 5 unmodified lines | |||
| 140 | bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR; | 156 | bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR; |
| 141 | 157 | ||
| 142 | /* | 158 | /* |
| 143 | * read fifo_count register to know how many bytes inside FIFO | 159 | * read fifo_count register to know how many bytes are inside the FIFO |
| 144 | * right now | 160 | * right now |
| 145 | */ | 161 | */ |
| 146 | result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data, | 162 | result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data, |
| @@ -150,7 +166,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) · 3 unmodified lines | |||
| 150 | fifo_count = be16_to_cpup((__be16 *)(&data[0])); | 166 | fifo_count = be16_to_cpup((__be16 *)(&data[0])); |
| 151 | if (fifo_count < bytes_per_datum) | 167 | if (fifo_count < bytes_per_datum) |
| 152 | goto end_session; | 168 | goto end_session; |
| 153 | /* fifo count can't be odd number, if it is odd, reset fifo*/ | 169 | /* fifo count can't be an odd number. If it is odd, reset the FIFO. */ |
| 154 | if (fifo_count & 1) | 170 | if (fifo_count & 1) |
| 155 | goto flush_fifo; | 171 | goto flush_fifo; |
| 156 | if (fifo_count > INV_MPU6050_FIFO_THRESHOLD) | 172 | if (fifo_count > INV_MPU6050_FIFO_THRESHOLD) |
| @@ -159,7 +175,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) · 2 unmodified lines | |||
| 159 | if (kfifo_len(&st->timestamps) > | 175 | if (kfifo_len(&st->timestamps) > |
| 160 | fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR) | 176 | fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR) |
| 161 | goto flush_fifo; | 177 | goto flush_fifo; |
| 162 | while (fifo_count >= bytes_per_datum) { | 178 | do { |
| 163 | result = regmap_bulk_read(st->map, st->reg->fifo_r_w, | 179 | result = regmap_bulk_read(st->map, st->reg->fifo_r_w, |
| 164 | data, bytes_per_datum); | 180 | data, bytes_per_datum); |
| 165 | if (result) | 181 | if (result) |
| @@ -170,12 +186,15 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) · 4 unmodified lines | |||
| 170 | if (result == 0) | 186 | if (result == 0) |
| 171 | timestamp = 0; | 187 | timestamp = 0; |
| 172 | 188 | ||
| 173 | result = iio_push_to_buffers_with_timestamp(indio_dev, data, | 189 | /* skip first samples if needed */ |
| 174 | timestamp); | 190 | if (st->skip_samples) |
| 175 | if (result) | 191 | st->skip_samples--; |
| 176 | goto flush_fifo; | 192 | else |
| 193 | iio_push_to_buffers_with_timestamp(indio_dev, data, | ||
| 194 | timestamp); | ||
| 195 | |||
| 177 | fifo_count -= bytes_per_datum; | 196 | fifo_count -= bytes_per_datum; |
| 178 | } | 197 | } while (fifo_count >= bytes_per_datum); |
| 179 | 198 | ||
| 180 | end_session: | 199 | end_session: |
| 181 | mutex_unlock(&st->lock); | 200 | mutex_unlock(&st->lock); |
| @@ -31,8 +31,9 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) · 30 unmodified lines | |||
| 31 | if (ret) | 31 | if (ret) |
| 32 | return ret; | 32 | return ret; |
| 33 | 33 | ||
| 34 | ret = regmap_write(st->map, INV_MPU6050_REG_USER_CTRL, | 34 | st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS; |
| 35 | INV_MPU6050_BIT_I2C_IF_DIS); | 35 | ret = regmap_write(st->map, st->reg->user_ctrl, |
| 36 | st->chip_config.user_ctrl); | ||
| 36 | if (ret) { | 37 | if (ret) { |
| 37 | inv_mpu6050_set_power_itg(st, false); | 38 | inv_mpu6050_set_power_itg(st, false); |
| 38 | return ret; | 39 | return ret; |
| @@ -69,11 +70,6 @@ static int inv_mpu_probe(struct spi_device *spi) · 30 unmodified lines | |||
| 69 | inv_mpu_i2c_disable, chip_type); | 70 | inv_mpu_i2c_disable, chip_type); |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | static int inv_mpu_remove(struct spi_device *spi) | ||
| 73 | { | ||
| 74 | return inv_mpu_core_remove(&spi->dev); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* | 73 | /* |
| 78 | * device id table is used to identify what device can be | 74 | * device id table is used to identify what device can be |
| 79 | * supported by this driver | 75 | * supported by this driver |
| @@ -83,6 +79,7 @@ static const struct spi_device_id inv_mpu_id[] = { · 3 unmodified lines | |||
| 83 | {"mpu6500", INV_MPU6500}, | 79 | {"mpu6500", INV_MPU6500}, |
| 84 | {"mpu9150", INV_MPU9150}, | 80 | {"mpu9150", INV_MPU9150}, |
| 85 | {"mpu9250", INV_MPU9250}, | 81 | {"mpu9250", INV_MPU9250}, |
| 82 | {"mpu9255", INV_MPU9255}, | ||
| 86 | {"icm20608", INV_ICM20608}, | 83 | {"icm20608", INV_ICM20608}, |
| 87 | {} | 84 | {} |
| 88 | }; | 85 | }; |
| @@ -97,7 +94,6 @@ MODULE_DEVICE_TABLE(acpi, inv_acpi_match); · 8 unmodified lines | |||
| 97 | 94 | ||
| 98 | static struct spi_driver inv_mpu_driver = { | 95 | static struct spi_driver inv_mpu_driver = { |
| 99 | .probe = inv_mpu_probe, | 96 | .probe = inv_mpu_probe, |
| 100 | .remove = inv_mpu_remove, | ||
| 101 | .id_table = inv_mpu_id, | 97 | .id_table = inv_mpu_id, |
| 102 | .driver = { | 98 | .driver = { |
| 103 | .acpi_match_table = ACPI_PTR(inv_acpi_match), | 99 | .acpi_match_table = ACPI_PTR(inv_acpi_match), |
| @@ -49,49 +49,66 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) · 48 unmodified lines | |||
| 49 | if (result) | 49 | if (result) |
| 50 | return result; | 50 | return result; |
| 51 | inv_scan_query(indio_dev); | 51 | inv_scan_query(indio_dev); |
| 52 | st->skip_samples = 0; | ||
| 52 | if (st->chip_config.gyro_fifo_enable) { | 53 | if (st->chip_config.gyro_fifo_enable) { |
| 53 | result = inv_mpu6050_switch_engine(st, true, | 54 | result = inv_mpu6050_switch_engine(st, true, |
| 54 | INV_MPU6050_BIT_PWR_GYRO_STBY); | 55 | INV_MPU6050_BIT_PWR_GYRO_STBY); |
| 55 | if (result) | 56 | if (result) |
| 56 | return result; | 57 | goto error_power_off; |
| 58 | /* gyro first sample is out of specs, skip it */ | ||
| 59 | st->skip_samples = 1; | ||
| 57 | } | 60 | } |
| 58 | if (st->chip_config.accl_fifo_enable) { | 61 | if (st->chip_config.accl_fifo_enable) { |
| 59 | result = inv_mpu6050_switch_engine(st, true, | 62 | result = inv_mpu6050_switch_engine(st, true, |
| 60 | INV_MPU6050_BIT_PWR_ACCL_STBY); | 63 | INV_MPU6050_BIT_PWR_ACCL_STBY); |
| 61 | if (result) | 64 | if (result) |
| 62 | return result; | 65 | goto error_gyro_off; |
| 63 | } | 66 | } |
| 64 | result = inv_reset_fifo(indio_dev); | 67 | result = inv_reset_fifo(indio_dev); |
| 65 | if (result) | 68 | if (result) |
| 66 | return result; | 69 | goto error_accl_off; |
| 67 | } else { | 70 | } else { |
| 68 | result = regmap_write(st->map, st->reg->fifo_en, 0); | 71 | result = regmap_write(st->map, st->reg->fifo_en, 0); |
| 69 | if (result) | 72 | if (result) |
| 70 | return result; | 73 | goto error_accl_off; |
| 71 | 74 | ||
| 72 | result = regmap_write(st->map, st->reg->int_enable, 0); | 75 | result = regmap_write(st->map, st->reg->int_enable, 0); |
| 73 | if (result) | 76 | if (result) |
| 74 | return result; | 77 | goto error_accl_off; |
| 75 | 78 | ||
| 76 | result = regmap_write(st->map, st->reg->user_ctrl, 0); | 79 | result = regmap_write(st->map, st->reg->user_ctrl, |
| 80 | st->chip_config.user_ctrl); | ||
| 77 | if (result) | 81 | if (result) |
| 78 | return result; | 82 | goto error_accl_off; |
| 79 | 83 | ||
| 80 | result = inv_mpu6050_switch_engine(st, false, | 84 | result = inv_mpu6050_switch_engine(st, false, |
| 81 | INV_MPU6050_BIT_PWR_GYRO_STBY); | 85 | INV_MPU6050_BIT_PWR_ACCL_STBY); |
| 82 | if (result) | 86 | if (result) |
| 83 | return result; | 87 | goto error_accl_off; |
| 84 | 88 | ||
| 85 | result = inv_mpu6050_switch_engine(st, false, | 89 | result = inv_mpu6050_switch_engine(st, false, |
| 86 | INV_MPU6050_BIT_PWR_ACCL_STBY); | 90 | INV_MPU6050_BIT_PWR_GYRO_STBY); |
| 87 | if (result) | 91 | if (result) |
| 88 | return result; | 92 | goto error_gyro_off; |
| 93 | |||
| 89 | result = inv_mpu6050_set_power_itg(st, false); | 94 | result = inv_mpu6050_set_power_itg(st, false); |
| 90 | if (result) | 95 | if (result) |
| 91 | return result; | 96 | goto error_power_off; |
| 92 | } | 97 | } |
| 93 | 98 | ||
| 94 | return 0; | 99 | return 0; |
| 100 | |||
| 101 | error_accl_off: | ||
| 102 | if (st->chip_config.accl_fifo_enable) | ||
| 103 | inv_mpu6050_switch_engine(st, false, | ||
| 104 | INV_MPU6050_BIT_PWR_ACCL_STBY); | ||
| 105 | error_gyro_off: | ||
| 106 | if (st->chip_config.gyro_fifo_enable) | ||
| 107 | inv_mpu6050_switch_engine(st, false, | ||
| 108 | INV_MPU6050_BIT_PWR_GYRO_STBY); | ||
| 109 | error_power_off: | ||
| 110 | inv_mpu6050_set_power_itg(st, false); | ||
| 111 | return result; | ||
| 95 | } | 112 | } |
| 96 | 113 | ||
| 97 | /** | 114 | /** |
| @@ -117,7 +134,7 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = { · 19 unmodified lines | |||
| 117 | .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state, | 134 | .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state, |
| 118 | }; | 135 | }; |
| 119 | 136 | ||
| 120 | int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) | 137 | int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type) |
| 121 | { | 138 | { |
| 122 | int ret; | 139 | int ret; |
| 123 | struct inv_mpu6050_state *st = iio_priv(indio_dev); | 140 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
| @@ -131,7 +148,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) · 7 unmodified lines | |||
| 131 | 148 | ||
| 132 | ret = devm_request_irq(&indio_dev->dev, st->irq, | 149 | ret = devm_request_irq(&indio_dev->dev, st->irq, |
| 133 | &iio_trigger_generic_data_rdy_poll, | 150 | &iio_trigger_generic_data_rdy_poll, |
| 134 | IRQF_TRIGGER_RISING, | 151 | irq_type, |
| 135 | "inv_mpu", | 152 | "inv_mpu", |
| 136 | st->trig); | 153 | st->trig); |
| 137 | if (ret) | 154 | if (ret) |
| @@ -141,7 +158,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) · 3 unmodified lines | |||
| 141 | st->trig->ops = &inv_mpu_trigger_ops; | 158 | st->trig->ops = &inv_mpu_trigger_ops; |
| 142 | iio_trigger_set_drvdata(st->trig, indio_dev); | 159 | iio_trigger_set_drvdata(st->trig, indio_dev); |
| 143 | 160 | ||
| 144 | ret = iio_trigger_register(st->trig); | 161 | ret = devm_iio_trigger_register(&indio_dev->dev, st->trig); |
| 145 | if (ret) | 162 | if (ret) |
| 146 | return ret; | 163 | return ret; |
| 147 | 164 | ||
| @@ -149,8 +166,3 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) · 1 unmodified line | |||
| 149 | 166 | ||
| 150 | return 0; | 167 | return 0; |
| 151 | } | 168 | } |
| 152 | |||
| 153 | void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st) | ||
| 154 | { | ||
| 155 | iio_trigger_unregister(st->trig); | ||
| 156 | } | ||
| @@ -8,7 +8,8 @@ config IIO_ST_LSM6DSX · 7 unmodified lines | |||
| 8 | select IIO_ST_LSM6DSX_SPI if (SPI_MASTER) | 8 | select IIO_ST_LSM6DSX_SPI if (SPI_MASTER) |
| 9 | help | 9 | help |
| 10 | Say yes here to build support for STMicroelectronics LSM6DSx imu | 10 | Say yes here to build support for STMicroelectronics LSM6DSx imu |
| 11 | sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm | 11 | sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, |
| 12 | ism330dlc | ||
| 12 | 13 | ||
| 13 | To compile this driver as a module, choose M here: the module | 14 | To compile this driver as a module, choose M here: the module |
| 14 | will be called st_lsm6dsx. | 15 | will be called st_lsm6dsx. |
| @@ -18,12 +18,14 @@ · 17 unmodified lines | |||
| 18 | #define ST_LSM6DS3H_DEV_NAME "lsm6ds3h" | 18 | #define ST_LSM6DS3H_DEV_NAME "lsm6ds3h" |
| 19 | #define ST_LSM6DSL_DEV_NAME "lsm6dsl" | 19 | #define ST_LSM6DSL_DEV_NAME "lsm6dsl" |
| 20 | #define ST_LSM6DSM_DEV_NAME "lsm6dsm" | 20 | #define ST_LSM6DSM_DEV_NAME "lsm6dsm" |
| 21 | #define ST_ISM330DLC_DEV_NAME "ism330dlc" | ||
| 21 | 22 | ||
| 22 | enum st_lsm6dsx_hw_id { | 23 | enum st_lsm6dsx_hw_id { |
| 23 | ST_LSM6DS3_ID, | 24 | ST_LSM6DS3_ID, |
| 24 | ST_LSM6DS3H_ID, | 25 | ST_LSM6DS3H_ID, |
| 25 | ST_LSM6DSL_ID, | 26 | ST_LSM6DSL_ID, |
| 26 | ST_LSM6DSM_ID, | 27 | ST_LSM6DSM_ID, |
| 28 | ST_ISM330DLC_ID, | ||
| 27 | ST_LSM6DSX_MAX_ID, | 29 | ST_LSM6DSX_MAX_ID, |
| 28 | }; | 30 | }; |
| 29 | 31 | ||
| @@ -1,10 +1,10 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * STMicroelectronics st_lsm6dsx FIFO buffer library driver | 2 | * STMicroelectronics st_lsm6dsx FIFO buffer library driver |
| 3 | * | 3 | * |
| 4 | * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM: The FIFO buffer can be configured | 4 | * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: The FIFO buffer can be |
| 5 | * to store data from gyroscope and accelerometer. Samples are queued | 5 | * configured to store data from gyroscope and accelerometer. Samples are |
| 6 | * without any tag according to a specific pattern based on 'FIFO data sets' | 6 | * queued without any tag according to a specific pattern based on |
| 7 | * (6 bytes each): | 7 | * 'FIFO data sets' (6 bytes each): |
| 8 | * - 1st data set is reserved for gyroscope data | 8 | * - 1st data set is reserved for gyroscope data |
| 9 | * - 2nd data set is reserved for accelerometer data | 9 | * - 2nd data set is reserved for accelerometer data |
| 10 | * The FIFO pattern changes depending on the ODRs and decimation factors | 10 | * The FIFO pattern changes depending on the ODRs and decimation factors |
| @@ -276,7 +276,7 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data, · 265 unmodified lines | |||
| 276 | #define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \ | 276 | #define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \ |
| 277 | sizeof(s64)) + sizeof(s64)) | 277 | sizeof(s64)) + sizeof(s64)) |
| 278 | /** | 278 | /** |
| 279 | * st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO routine | 279 | * st_lsm6dsx_read_fifo() - hw FIFO read routine |
| 280 | * @hw: Pointer to instance of struct st_lsm6dsx_hw. | 280 | * @hw: Pointer to instance of struct st_lsm6dsx_hw. |
| 281 | * | 281 | * |
| 282 | * Read samples from the hw FIFO and push them to IIO buffers. | 282 | * Read samples from the hw FIFO and push them to IIO buffers. |
| @@ -17,7 +17,7 @@ · 16 unmodified lines | |||
| 17 | * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 | 17 | * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 |
| 18 | * - FIFO size: 8KB | 18 | * - FIFO size: 8KB |
| 19 | * | 19 | * |
| 20 | * - LSM6DS3H/LSM6DSL/LSM6DSM: | 20 | * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: |
| 21 | * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 | 21 | * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 |
| 22 | * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 | 22 | * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 |
| 23 | * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 | 23 | * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 |
| @@ -252,6 +252,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { · 228 unmodified lines | |||
| 252 | .id = { | 252 | .id = { |
| 253 | [0] = ST_LSM6DSL_ID, | 253 | [0] = ST_LSM6DSL_ID, |
| 254 | [1] = ST_LSM6DSM_ID, | 254 | [1] = ST_LSM6DSM_ID, |
| 255 | [2] = ST_ISM330DLC_ID, | ||
| 255 | }, | 256 | }, |
| 256 | .decimator = { | 257 | .decimator = { |
| 257 | [ST_LSM6DSX_ID_ACC] = { | 258 | [ST_LSM6DSX_ID_ACC] = { |
| @@ -266,11 +267,11 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { · 8 unmodified lines | |||
| 266 | .fifo_ops = { | 267 | .fifo_ops = { |
| 267 | .fifo_th = { | 268 | .fifo_th = { |
| 268 | .addr = 0x06, | 269 | .addr = 0x06, |
| 269 | .mask = GENMASK(11, 0), | 270 | .mask = GENMASK(10, 0), |
| 270 | }, | 271 | }, |
| 271 | .fifo_diff = { | 272 | .fifo_diff = { |
| 272 | .addr = 0x3a, | 273 | .addr = 0x3a, |
| 273 | .mask = GENMASK(11, 0), | 274 | .mask = GENMASK(10, 0), |
| 274 | }, | 275 | }, |
| 275 | .th_wl = 3, /* 1LSB = 2B */ | 276 | .th_wl = 3, /* 1LSB = 2B */ |
| 276 | }, | 277 | }, |
| @@ -57,6 +57,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { · 56 unmodified lines | |||
| 57 | .compatible = "st,lsm6dsm", | 57 | .compatible = "st,lsm6dsm", |
| 58 | .data = (void *)ST_LSM6DSM_ID, | 58 | .data = (void *)ST_LSM6DSM_ID, |
| 59 | }, | 59 | }, |
| 60 | { | ||
| 61 | .compatible = "st,ism330dlc", | ||
| 62 | .data = (void *)ST_ISM330DLC_ID, | ||
| 63 | }, | ||
| 60 | {}, | 64 | {}, |
| 61 | }; | 65 | }; |
| 62 | MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); | 66 | MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); |
| @@ -66,6 +70,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { · 3 unmodified lines | |||
| 66 | { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, | 70 | { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, |
| 67 | { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, | 71 | { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, |
| 68 | { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, | 72 | { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, |
| 73 | { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, | ||
| 69 | {}, | 74 | {}, |
| 70 | }; | 75 | }; |
| 71 | MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); | 76 | MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); |
| @@ -57,6 +57,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { · 56 unmodified lines | |||
| 57 | .compatible = "st,lsm6dsm", | 57 | .compatible = "st,lsm6dsm", |
| 58 | .data = (void *)ST_LSM6DSM_ID, | 58 | .data = (void *)ST_LSM6DSM_ID, |
| 59 | }, | 59 | }, |
| 60 | { | ||
| 61 | .compatible = "st,ism330dlc", | ||
| 62 | .data = (void *)ST_ISM330DLC_ID, | ||
| 63 | }, | ||
| 60 | {}, | 64 | {}, |
| 61 | }; | 65 | }; |
| 62 | MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); | 66 | MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); |
| @@ -66,6 +70,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { · 3 unmodified lines | |||
| 66 | { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, | 70 | { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, |
| 67 | { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, | 71 | { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, |
| 68 | { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, | 72 | { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, |
| 73 | { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, | ||
| 69 | {}, | 74 | {}, |
| 70 | }; | 75 | }; |
| 71 | MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); | 76 | MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); |
| @@ -409,6 +409,14 @@ config TSL2583 · 408 unmodified lines | |||
| 409 | Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. | 409 | Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. |
| 410 | Access ALS data via iio, sysfs. | 410 | Access ALS data via iio, sysfs. |
| 411 | 411 | ||
| 412 | config TSL2772 | ||
| 413 | tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" | ||
| 414 | depends on I2C | ||
| 415 | help | ||
| 416 | Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, | ||
| 417 | tmd2672, tsl2772, tmd2772 devices. | ||
| 418 | Provides iio_events and direct access via sysfs. | ||
| 419 | |||
| 412 | config TSL4531 | 420 | config TSL4531 |
| 413 | tristate "TAOS TSL4531 ambient light sensors" | 421 | tristate "TAOS TSL4531 ambient light sensors" |
| 414 | depends on I2C | 422 | depends on I2C |
| @@ -40,6 +40,7 @@ obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o · 39 unmodified lines | |||
| 40 | obj-$(CONFIG_TCS3414) += tcs3414.o | 40 | obj-$(CONFIG_TCS3414) += tcs3414.o |
| 41 | obj-$(CONFIG_TCS3472) += tcs3472.o | 41 | obj-$(CONFIG_TCS3472) += tcs3472.o |
| 42 | obj-$(CONFIG_TSL2583) += tsl2583.o | 42 | obj-$(CONFIG_TSL2583) += tsl2583.o |
| 43 | obj-$(CONFIG_TSL2772) += tsl2772.o | ||
| 43 | obj-$(CONFIG_TSL4531) += tsl4531.o | 44 | obj-$(CONFIG_TSL4531) += tsl4531.o |
| 44 | obj-$(CONFIG_US5182D) += us5182d.o | 45 | obj-$(CONFIG_US5182D) += us5182d.o |
| 45 | obj-$(CONFIG_VCNL4000) += vcnl4000.o | 46 | obj-$(CONFIG_VCNL4000) += vcnl4000.o |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | |||
| 16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
| 17 | #include <linux/device.h> | 17 | #include <linux/device.h> |
| 18 | #include <linux/iio/buffer.h> | 18 | #include <linux/iio/buffer.h> |
| 19 | #include <linux/iio/common/cros_ec_sensors_core.h> | ||
| 19 | #include <linux/iio/iio.h> | 20 | #include <linux/iio/iio.h> |
| 20 | #include <linux/iio/kfifo_buf.h> | 21 | #include <linux/iio/kfifo_buf.h> |
| 21 | #include <linux/iio/trigger.h> | 22 | #include <linux/iio/trigger.h> |
| @@ -29,8 +30,6 @@ · 7 unmodified lines | |||
| 29 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
| 30 | #include <linux/sysfs.h> | 31 | #include <linux/sysfs.h> |
| 31 | 32 | ||
| 32 | #include "../common/cros_ec_sensors/cros_ec_sensors_core.h" | ||
| 33 | |||
| 34 | /* | 33 | /* |
| 35 | * We only represent one entry for light or proximity. EC is merging different | 34 | * We only represent one entry for light or proximity. EC is merging different |
| 36 | * light sensors to return the what the eye would see. For proximity, we | 35 | * light sensors to return the what the eye would see. For proximity, we |
| @@ -600,7 +600,7 @@ done: · 599 unmodified lines | |||
| 600 | 600 | ||
| 601 | static IIO_CONST_ATTR(in_illuminance_calibscale_available, "1 8 16 111"); | 601 | static IIO_CONST_ATTR(in_illuminance_calibscale_available, "1 8 16 111"); |
| 602 | static IIO_CONST_ATTR(in_illuminance_integration_time_available, | 602 | static IIO_CONST_ATTR(in_illuminance_integration_time_available, |
| 603 | "0.000050 0.000100 0.000150 0.000200 0.000250 0.000300 0.000350 0.000400 0.000450 0.000500 0.000550 0.000600 0.000650"); | 603 | "0.050 0.100 0.150 0.200 0.250 0.300 0.350 0.400 0.450 0.500 0.550 0.600 0.650"); |
| 604 | static IIO_DEVICE_ATTR_RW(in_illuminance_input_target, 0); | 604 | static IIO_DEVICE_ATTR_RW(in_illuminance_input_target, 0); |
| 605 | static IIO_DEVICE_ATTR_WO(in_illuminance_calibrate, 0); | 605 | static IIO_DEVICE_ATTR_WO(in_illuminance_calibrate, 0); |
| 606 | static IIO_DEVICE_ATTR_RW(in_illuminance_lux_table, 0); | 606 | static IIO_DEVICE_ATTR_RW(in_illuminance_lux_table, 0); |
| @@ -0,0 +1,1800 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Device driver for monitoring ambient light intensity in (lux) and proximity | ||
| 4 | * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771, | ||
| 5 | * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices. | ||
| 6 | * | ||
| 7 | * Copyright (c) 2012, TAOS Corporation. | ||
| 8 | * Copyright (c) 2017-2018 Brian Masney <masneyb@onstation.org> | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/delay.h> | ||
| 12 | #include <linux/errno.h> | ||
| 13 | #include <linux/i2c.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/mutex.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/iio/events.h> | ||
| 20 | #include <linux/iio/iio.h> | ||
| 21 | #include <linux/iio/sysfs.h> | ||
| 22 | #include <linux/platform_data/tsl2772.h> | ||
| 23 | |||
| 24 | /* Cal defs */ | ||
| 25 | #define PROX_STAT_CAL 0 | ||
| 26 | #define PROX_STAT_SAMP 1 | ||
| 27 | #define MAX_SAMPLES_CAL 200 | ||
| 28 | |||
| 29 | /* TSL2772 Device ID */ | ||
| 30 | #define TRITON_ID 0x00 | ||
| 31 | #define SWORDFISH_ID 0x30 | ||
| 32 | #define HALIBUT_ID 0x20 | ||
| 33 | |||
| 34 | /* Lux calculation constants */ | ||
| 35 | #define TSL2772_LUX_CALC_OVER_FLOW 65535 | ||
| 36 | |||
| 37 | /* | ||
| 38 | * TAOS Register definitions - Note: depending on device, some of these register | ||
| 39 | * are not used and the register address is benign. | ||
| 40 | */ | ||
| 41 | |||
| 42 | /* Register offsets */ | ||
| 43 | #define TSL2772_MAX_CONFIG_REG 16 | ||
| 44 | |||
| 45 | /* Device Registers and Masks */ | ||
| 46 | #define TSL2772_CNTRL 0x00 | ||
| 47 | #define TSL2772_ALS_TIME 0X01 | ||
| 48 | #define TSL2772_PRX_TIME 0x02 | ||
| 49 | #define TSL2772_WAIT_TIME 0x03 | ||
| 50 | #define TSL2772_ALS_MINTHRESHLO 0X04 | ||
| 51 | #define TSL2772_ALS_MINTHRESHHI 0X05 | ||
| 52 | #define TSL2772_ALS_MAXTHRESHLO 0X06 | ||
| 53 | #define TSL2772_ALS_MAXTHRESHHI 0X07 | ||
| 54 | #define TSL2772_PRX_MINTHRESHLO 0X08 | ||
| 55 | #define TSL2772_PRX_MINTHRESHHI 0X09 | ||
| 56 | #define TSL2772_PRX_MAXTHRESHLO 0X0A | ||
| 57 | #define TSL2772_PRX_MAXTHRESHHI 0X0B | ||
| 58 | #define TSL2772_PERSISTENCE 0x0C | ||
| 59 | #define TSL2772_ALS_PRX_CONFIG 0x0D | ||
| 60 | #define TSL2772_PRX_COUNT 0x0E | ||
| 61 | #define TSL2772_GAIN 0x0F | ||
| 62 | #define TSL2772_NOTUSED 0x10 | ||
| 63 | #define TSL2772_REVID 0x11 | ||
| 64 | #define TSL2772_CHIPID 0x12 | ||
| 65 | #define TSL2772_STATUS 0x13 | ||
| 66 | #define TSL2772_ALS_CHAN0LO 0x14 | ||
| 67 | #define TSL2772_ALS_CHAN0HI 0x15 | ||
| 68 | #define TSL2772_ALS_CHAN1LO 0x16 | ||
| 69 | #define TSL2772_ALS_CHAN1HI 0x17 | ||
| 70 | #define TSL2772_PRX_LO 0x18 | ||
| 71 | #define TSL2772_PRX_HI 0x19 | ||
| 72 | |||
| 73 | /* tsl2772 cmd reg masks */ | ||
| 74 | #define TSL2772_CMD_REG 0x80 | ||
| 75 | #define TSL2772_CMD_SPL_FN 0x60 | ||
| 76 | #define TSL2772_CMD_REPEAT_PROTO 0x00 | ||
| 77 | #define TSL2772_CMD_AUTOINC_PROTO 0x20 | ||
| 78 | |||
| 79 | #define TSL2772_CMD_PROX_INT_CLR 0X05 | ||
| 80 | #define TSL2772_CMD_ALS_INT_CLR 0x06 | ||
| 81 | #define TSL2772_CMD_PROXALS_INT_CLR 0X07 | ||
| 82 | |||
| 83 | /* tsl2772 cntrl reg masks */ | ||
| 84 | #define TSL2772_CNTL_ADC_ENBL 0x02 | ||
| 85 | #define TSL2772_CNTL_PWR_ON 0x01 | ||
| 86 | |||
| 87 | /* tsl2772 status reg masks */ | ||
| 88 | #define TSL2772_STA_ADC_VALID 0x01 | ||
| 89 | #define TSL2772_STA_PRX_VALID 0x02 | ||
| 90 | #define TSL2772_STA_ADC_PRX_VALID (TSL2772_STA_ADC_VALID | \ | ||
| 91 | TSL2772_STA_PRX_VALID) | ||
| 92 | #define TSL2772_STA_ALS_INTR 0x10 | ||
| 93 | #define TSL2772_STA_PRX_INTR 0x20 | ||
| 94 | |||
| 95 | /* tsl2772 cntrl reg masks */ | ||
| 96 | #define TSL2772_CNTL_REG_CLEAR 0x00 | ||
| 97 | #define TSL2772_CNTL_PROX_INT_ENBL 0X20 | ||
| 98 | #define TSL2772_CNTL_ALS_INT_ENBL 0X10 | ||
| 99 | #define TSL2772_CNTL_WAIT_TMR_ENBL 0X08 | ||
| 100 | #define TSL2772_CNTL_PROX_DET_ENBL 0X04 | ||
| 101 | #define TSL2772_CNTL_PWRON 0x01 | ||
| 102 | #define TSL2772_CNTL_ALSPON_ENBL 0x03 | ||
| 103 | #define TSL2772_CNTL_INTALSPON_ENBL 0x13 | ||
| 104 | #define TSL2772_CNTL_PROXPON_ENBL 0x0F | ||
| 105 | #define TSL2772_CNTL_INTPROXPON_ENBL 0x2F | ||
| 106 | |||
| 107 | #define TSL2772_ALS_GAIN_TRIM_MIN 250 | ||
| 108 | #define TSL2772_ALS_GAIN_TRIM_MAX 4000 | ||
| 109 | |||
| 110 | /* Device family members */ | ||
| 111 | enum { | ||
| 112 | tsl2571, | ||
| 113 | tsl2671, | ||
| 114 | tmd2671, | ||
| 115 | tsl2771, | ||
| 116 | tmd2771, | ||
| 117 | tsl2572, | ||
| 118 | tsl2672, | ||
| 119 | tmd2672, | ||
| 120 | tsl2772, | ||
| 121 | tmd2772 | ||
| 122 | }; | ||
| 123 | |||
| 124 | enum { | ||
| 125 | TSL2772_CHIP_UNKNOWN = 0, | ||
| 126 | TSL2772_CHIP_WORKING = 1, | ||
| 127 | TSL2772_CHIP_SUSPENDED = 2 | ||
| 128 | }; | ||
| 129 | |||
| 130 | /* Per-device data */ | ||
| 131 | struct tsl2772_als_info { | ||
| 132 | u16 als_ch0; | ||
| 133 | u16 als_ch1; | ||
| 134 | u16 lux; | ||
| 135 | }; | ||
| 136 | |||
| 137 | struct tsl2772_chip_info { | ||
| 138 | int chan_table_elements; | ||
| 139 | struct iio_chan_spec channel_with_events[4]; | ||
| 140 | struct iio_chan_spec channel_without_events[4]; | ||
| 141 | const struct iio_info *info; | ||
| 142 | }; | ||
| 143 | |||
| 144 | struct tsl2772_chip { | ||
| 145 | kernel_ulong_t id; | ||
| 146 | struct mutex prox_mutex; | ||
| 147 | struct mutex als_mutex; | ||
| 148 | struct i2c_client *client; | ||
| 149 | u16 prox_data; | ||
| 150 | struct tsl2772_als_info als_cur_info; | ||
| 151 | struct tsl2772_settings settings; | ||
| 152 | struct tsl2772_platform_data *pdata; | ||
| 153 | int als_gain_time_scale; | ||
| 154 | int als_saturation; | ||
| 155 | int tsl2772_chip_status; | ||
| 156 | u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; | ||
| 157 | const struct tsl2772_chip_info *chip_info; | ||
| 158 | const struct iio_info *info; | ||
| 159 | s64 event_timestamp; | ||
| 160 | /* | ||
| 161 | * This structure is intentionally large to accommodate | ||
| 162 | * updates via sysfs. | ||
| 163 | * Sized to 9 = max 8 segments + 1 termination segment | ||
| 164 | */ | ||
| 165 | struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE]; | ||
| 166 | }; | ||
| 167 | |||
| 168 | /* | ||
| 169 | * Different devices require different coefficents, and these numbers were | ||
| 170 | * derived from the 'Lux Equation' section of the various device datasheets. | ||
| 171 | * All of these coefficients assume a Glass Attenuation (GA) factor of 1. | ||
| 172 | * The coefficients are multiplied by 1000 to avoid floating point operations. | ||
| 173 | * The two rows in each table correspond to the Lux1 and Lux2 equations from | ||
| 174 | * the datasheets. | ||
| 175 | */ | ||
| 176 | static const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { | ||
| 177 | { 53000, 106000 }, | ||
| 178 | { 31800, 53000 }, | ||
| 179 | { 0, 0 }, | ||
| 180 | }; | ||
| 181 | |||
| 182 | static const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { | ||
| 183 | { 24000, 48000 }, | ||
| 184 | { 14400, 24000 }, | ||
| 185 | { 0, 0 }, | ||
| 186 | }; | ||
| 187 | |||
| 188 | static const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { | ||
| 189 | { 60000, 112200 }, | ||
| 190 | { 37800, 60000 }, | ||
| 191 | { 0, 0 }, | ||
| 192 | }; | ||
| 193 | |||
| 194 | static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { | ||
| 195 | { 20000, 35000 }, | ||
| 196 | { 12600, 20000 }, | ||
| 197 | { 0, 0 }, | ||
| 198 | }; | ||
| 199 | |||
| 200 | static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { | ||
| 201 | [tsl2571] = tsl2x71_lux_table, | ||
| 202 | [tsl2671] = tsl2x71_lux_table, | ||
| 203 | [tmd2671] = tmd2x71_lux_table, | ||
| 204 | [tsl2771] = tsl2x71_lux_table, | ||
| 205 | [tmd2771] = tmd2x71_lux_table, | ||
| 206 | [tsl2572] = tsl2x72_lux_table, | ||
| 207 | [tsl2672] = tsl2x72_lux_table, | ||
| 208 | [tmd2672] = tmd2x72_lux_table, | ||
| 209 | [tsl2772] = tsl2x72_lux_table, | ||
| 210 | [tmd2772] = tmd2x72_lux_table, | ||
| 211 | }; | ||
| 212 | |||
| 213 | static const struct tsl2772_settings tsl2772_default_settings = { | ||
| 214 | .als_time = 255, /* 2.72 / 2.73 ms */ | ||
| 215 | .als_gain = 0, | ||
| 216 | .prox_time = 255, /* 2.72 / 2.73 ms */ | ||
| 217 | .prox_gain = 0, | ||
| 218 | .wait_time = 255, | ||
| 219 | .als_prox_config = 0, | ||
| 220 | .als_gain_trim = 1000, | ||
| 221 | .als_cal_target = 150, | ||
| 222 | .als_persistence = 1, | ||
| 223 | .als_interrupt_en = false, | ||
| 224 | .als_thresh_low = 200, | ||
| 225 | .als_thresh_high = 256, | ||
| 226 | .prox_persistence = 1, | ||
| 227 | .prox_interrupt_en = false, | ||
| 228 | .prox_thres_low = 0, | ||
| 229 | .prox_thres_high = 512, | ||
| 230 | .prox_max_samples_cal = 30, | ||
| 231 | .prox_pulse_count = 8, | ||
| 232 | .prox_diode = TSL2772_DIODE1, | ||
| 233 | .prox_power = TSL2772_100_mA | ||
| 234 | }; | ||
| 235 | |||
| 236 | static const s16 tsl2772_als_gain[] = { | ||
| 237 | 1, | ||
| 238 | 8, | ||
| 239 | 16, | ||
| 240 | 120 | ||
| 241 | }; | ||
| 242 | |||
| 243 | static const s16 tsl2772_prox_gain[] = { | ||
| 244 | 1, | ||
| 245 | 2, | ||
| 246 | 4, | ||
| 247 | 8 | ||
| 248 | }; | ||
| 249 | |||
| 250 | static const int tsl2772_int_time_avail[][6] = { | ||
| 251 | [tsl2571] = { 0, 2720, 0, 2720, 0, 696000 }, | ||
| 252 | [tsl2671] = { 0, 2720, 0, 2720, 0, 696000 }, | ||
| 253 | [tmd2671] = { 0, 2720, 0, 2720, 0, 696000 }, | ||
| 254 | [tsl2771] = { 0, 2720, 0, 2720, 0, 696000 }, | ||
| 255 | [tmd2771] = { 0, 2720, 0, 2720, 0, 696000 }, | ||
| 256 | [tsl2572] = { 0, 2730, 0, 2730, 0, 699000 }, | ||
| 257 | [tsl2672] = { 0, 2730, 0, 2730, 0, 699000 }, | ||
| 258 | [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, | ||
| 259 | [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, | ||
| 260 | [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, | ||
| 261 | }; | ||
| 262 | |||
| 263 | static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; | ||
| 264 | |||
| 265 | static int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 }; | ||
| 266 | |||
| 267 | /* Channel variations */ | ||
| 268 | enum { | ||
| 269 | ALS, | ||
| 270 | PRX, | ||
| 271 | ALSPRX, | ||
| 272 | PRX2, | ||
| 273 | ALSPRX2, | ||
| 274 | }; | ||
| 275 | |||
| 276 | static const u8 device_channel_config[] = { | ||
| 277 | [tsl2571] = ALS, | ||
| 278 | [tsl2671] = PRX, | ||
| 279 | [tmd2671] = PRX, | ||
| 280 | [tsl2771] = ALSPRX, | ||
| 281 | [tmd2771] = ALSPRX, | ||
| 282 | [tsl2572] = ALS, | ||
| 283 | [tsl2672] = PRX2, | ||
| 284 | [tmd2672] = PRX2, | ||
| 285 | [tsl2772] = ALSPRX2, | ||
| 286 | [tmd2772] = ALSPRX2 | ||
| 287 | }; | ||
| 288 | |||
| 289 | static int tsl2772_read_status(struct tsl2772_chip *chip) | ||
| 290 | { | ||
| 291 | int ret; | ||
| 292 | |||
| 293 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 294 | TSL2772_CMD_REG | TSL2772_STATUS); | ||
| 295 | if (ret < 0) | ||
| 296 | dev_err(&chip->client->dev, | ||
| 297 | "%s: failed to read STATUS register: %d\n", __func__, | ||
| 298 | ret); | ||
| 299 | |||
| 300 | return ret; | ||
| 301 | } | ||
| 302 | |||
| 303 | static int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data) | ||
| 304 | { | ||
| 305 | int ret; | ||
| 306 | |||
| 307 | ret = i2c_smbus_write_byte_data(chip->client, | ||
| 308 | TSL2772_CMD_REG | TSL2772_CNTRL, data); | ||
| 309 | if (ret < 0) { | ||
| 310 | dev_err(&chip->client->dev, | ||
| 311 | "%s: failed to write to control register %x: %d\n", | ||
| 312 | __func__, data, ret); | ||
| 313 | } | ||
| 314 | |||
| 315 | return ret; | ||
| 316 | } | ||
| 317 | |||
| 318 | static int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, | ||
| 319 | int upper_reg) | ||
| 320 | { | ||
| 321 | u8 buf[2]; | ||
| 322 | int ret; | ||
| 323 | |||
| 324 | ret = i2c_smbus_write_byte(chip->client, | ||
| 325 | TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO | | ||
| 326 | lower_reg); | ||
| 327 | if (ret < 0) { | ||
| 328 | dev_err(&chip->client->dev, | ||
| 329 | "%s: failed to enable auto increment protocol: %d\n", | ||
| 330 | __func__, ret); | ||
| 331 | return ret; | ||
| 332 | } | ||
| 333 | |||
| 334 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 335 | TSL2772_CMD_REG | lower_reg); | ||
| 336 | if (ret < 0) { | ||
| 337 | dev_err(&chip->client->dev, | ||
| 338 | "%s: failed to read from register %x: %d\n", __func__, | ||
| 339 | lower_reg, ret); | ||
| 340 | return ret; | ||
| 341 | } | ||
| 342 | buf[0] = ret; | ||
| 343 | |||
| 344 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 345 | TSL2772_CMD_REG | upper_reg); | ||
| 346 | if (ret < 0) { | ||
| 347 | dev_err(&chip->client->dev, | ||
| 348 | "%s: failed to read from register %x: %d\n", __func__, | ||
| 349 | upper_reg, ret); | ||
| 350 | return ret; | ||
| 351 | } | ||
| 352 | buf[1] = ret; | ||
| 353 | |||
| 354 | ret = i2c_smbus_write_byte(chip->client, | ||
| 355 | TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO | | ||
| 356 | lower_reg); | ||
| 357 | if (ret < 0) { | ||
| 358 | dev_err(&chip->client->dev, | ||
| 359 | "%s: failed to enable repeated byte protocol: %d\n", | ||
| 360 | __func__, ret); | ||
| 361 | return ret; | ||
| 362 | } | ||
| 363 | |||
| 364 | return le16_to_cpup((const __le16 *)&buf[0]); | ||
| 365 | } | ||
| 366 | |||
| 367 | /** | ||
| 368 | * tsl2772_get_lux() - Reads and calculates current lux value. | ||
| 369 | * @indio_dev: pointer to IIO device | ||
| 370 | * | ||
| 371 | * The raw ch0 and ch1 values of the ambient light sensed in the last | ||
| 372 | * integration cycle are read from the device. The raw values are multiplied | ||
| 373 | * by a device-specific scale factor, and divided by the integration time and | ||
| 374 | * device gain. The code supports multiple lux equations through the lux table | ||
| 375 | * coefficients. A lux gain trim is applied to each lux equation, and then the | ||
| 376 | * maximum lux within the interval 0..65535 is selected. | ||
| 377 | */ | ||
| 378 | static int tsl2772_get_lux(struct iio_dev *indio_dev) | ||
| 379 | { | ||
| 380 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 381 | struct tsl2772_lux *p; | ||
| 382 | int max_lux, ret; | ||
| 383 | bool overflow; | ||
| 384 | |||
| 385 | mutex_lock(&chip->als_mutex); | ||
| 386 | |||
| 387 | if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) { | ||
| 388 | dev_err(&chip->client->dev, "%s: device is not enabled\n", | ||
| 389 | __func__); | ||
| 390 | ret = -EBUSY; | ||
| 391 | goto out_unlock; | ||
| 392 | } | ||
| 393 | |||
| 394 | ret = tsl2772_read_status(chip); | ||
| 395 | if (ret < 0) | ||
| 396 | goto out_unlock; | ||
| 397 | |||
| 398 | if (!(ret & TSL2772_STA_ADC_VALID)) { | ||
| 399 | dev_err(&chip->client->dev, | ||
| 400 | "%s: data not valid yet\n", __func__); | ||
| 401 | ret = chip->als_cur_info.lux; /* return LAST VALUE */ | ||
| 402 | goto out_unlock; | ||
| 403 | } | ||
| 404 | |||
| 405 | ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO, | ||
| 406 | TSL2772_ALS_CHAN0HI); | ||
| 407 | if (ret < 0) | ||
| 408 | goto out_unlock; | ||
| 409 | chip->als_cur_info.als_ch0 = ret; | ||
| 410 | |||
| 411 | ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO, | ||
| 412 | TSL2772_ALS_CHAN1HI); | ||
| 413 | if (ret < 0) | ||
| 414 | goto out_unlock; | ||
| 415 | chip->als_cur_info.als_ch1 = ret; | ||
| 416 | |||
| 417 | if (chip->als_cur_info.als_ch0 >= chip->als_saturation) { | ||
| 418 | max_lux = TSL2772_LUX_CALC_OVER_FLOW; | ||
| 419 | goto update_struct_with_max_lux; | ||
| 420 | } | ||
| 421 | |||
| 422 | if (!chip->als_cur_info.als_ch0) { | ||
| 423 | /* have no data, so return LAST VALUE */ | ||
| 424 | ret = chip->als_cur_info.lux; | ||
| 425 | goto out_unlock; | ||
| 426 | } | ||
| 427 | |||
| 428 | max_lux = 0; | ||
| 429 | overflow = false; | ||
| 430 | for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0; | ||
| 431 | p++) { | ||
| 432 | int lux; | ||
| 433 | |||
| 434 | lux = ((chip->als_cur_info.als_ch0 * p->ch0) - | ||
| 435 | (chip->als_cur_info.als_ch1 * p->ch1)) / | ||
| 436 | chip->als_gain_time_scale; | ||
| 437 | |||
| 438 | /* | ||
| 439 | * The als_gain_trim can have a value within the range 250..4000 | ||
| 440 | * and is a multiplier for the lux. A trim of 1000 makes no | ||
| 441 | * changes to the lux, less than 1000 scales it down, and | ||
| 442 | * greater than 1000 scales it up. | ||
| 443 | */ | ||
| 444 | lux = (lux * chip->settings.als_gain_trim) / 1000; | ||
| 445 | |||
| 446 | if (lux > TSL2772_LUX_CALC_OVER_FLOW) { | ||
| 447 | overflow = true; | ||
| 448 | continue; | ||
| 449 | } | ||
| 450 | |||
| 451 | max_lux = max(max_lux, lux); | ||
| 452 | } | ||
| 453 | |||
| 454 | if (overflow && max_lux == 0) | ||
| 455 | max_lux = TSL2772_LUX_CALC_OVER_FLOW; | ||
| 456 | |||
| 457 | update_struct_with_max_lux: | ||
| 458 | chip->als_cur_info.lux = max_lux; | ||
| 459 | ret = max_lux; | ||
| 460 | |||
| 461 | out_unlock: | ||
| 462 | mutex_unlock(&chip->als_mutex); | ||
| 463 | |||
| 464 | return ret; | ||
| 465 | } | ||
| 466 | |||
| 467 | /** | ||
| 468 | * tsl2772_get_prox() - Reads proximity data registers and updates | ||
| 469 | * chip->prox_data. | ||
| 470 | * | ||
| 471 | * @indio_dev: pointer to IIO device | ||
| 472 | */ | ||
| 473 | static int tsl2772_get_prox(struct iio_dev *indio_dev) | ||
| 474 | { | ||
| 475 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 476 | int ret; | ||
| 477 | |||
| 478 | mutex_lock(&chip->prox_mutex); | ||
| 479 | |||
| 480 | ret = tsl2772_read_status(chip); | ||
| 481 | if (ret < 0) | ||
| 482 | goto prox_poll_err; | ||
| 483 | |||
| 484 | switch (chip->id) { | ||
| 485 | case tsl2571: | ||
| 486 | case tsl2671: | ||
| 487 | case tmd2671: | ||
| 488 | case tsl2771: | ||
| 489 | case tmd2771: | ||
| 490 | if (!(ret & TSL2772_STA_ADC_VALID)) { | ||
| 491 | ret = -EINVAL; | ||
| 492 | goto prox_poll_err; | ||
| 493 | } | ||
| 494 | break; | ||
| 495 | case tsl2572: | ||
| 496 | case tsl2672: | ||
| 497 | case tmd2672: | ||
| 498 | case tsl2772: | ||
| 499 | case tmd2772: | ||
| 500 | if (!(ret & TSL2772_STA_PRX_VALID)) { | ||
| 501 | ret = -EINVAL; | ||
| 502 | goto prox_poll_err; | ||
| 503 | } | ||
| 504 | break; | ||
| 505 | } | ||
| 506 | |||
| 507 | ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); | ||
| 508 | if (ret < 0) | ||
| 509 | goto prox_poll_err; | ||
| 510 | chip->prox_data = ret; | ||
| 511 | |||
| 512 | prox_poll_err: | ||
| 513 | mutex_unlock(&chip->prox_mutex); | ||
| 514 | |||
| 515 | return ret; | ||
| 516 | } | ||
| 517 | |||
| 518 | /** | ||
| 519 | * tsl2772_defaults() - Populates the device nominal operating parameters | ||
| 520 | * with those provided by a 'platform' data struct or | ||
| 521 | * with prefined defaults. | ||
| 522 | * | ||
| 523 | * @chip: pointer to device structure. | ||
| 524 | */ | ||
| 525 | static void tsl2772_defaults(struct tsl2772_chip *chip) | ||
| 526 | { | ||
| 527 | /* If Operational settings defined elsewhere.. */ | ||
| 528 | if (chip->pdata && chip->pdata->platform_default_settings) | ||
| 529 | memcpy(&chip->settings, chip->pdata->platform_default_settings, | ||
| 530 | sizeof(tsl2772_default_settings)); | ||
| 531 | else | ||
| 532 | memcpy(&chip->settings, &tsl2772_default_settings, | ||
| 533 | sizeof(tsl2772_default_settings)); | ||
| 534 | |||
| 535 | /* Load up the proper lux table. */ | ||
| 536 | if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0) | ||
| 537 | memcpy(chip->tsl2772_device_lux, | ||
| 538 | chip->pdata->platform_lux_table, | ||
| 539 | sizeof(chip->pdata->platform_lux_table)); | ||
| 540 | else | ||
| 541 | memcpy(chip->tsl2772_device_lux, | ||
| 542 | tsl2772_default_lux_table_group[chip->id], | ||
| 543 | TSL2772_DEFAULT_TABLE_BYTES); | ||
| 544 | } | ||
| 545 | |||
| 546 | /** | ||
| 547 | * tsl2772_als_calibrate() - Obtain single reading and calculate | ||
| 548 | * the als_gain_trim. | ||
| 549 | * | ||
| 550 | * @indio_dev: pointer to IIO device | ||
| 551 | */ | ||
| 552 | static int tsl2772_als_calibrate(struct iio_dev *indio_dev) | ||
| 553 | { | ||
| 554 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 555 | int ret, lux_val; | ||
| 556 | |||
| 557 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 558 | TSL2772_CMD_REG | TSL2772_CNTRL); | ||
| 559 | if (ret < 0) { | ||
| 560 | dev_err(&chip->client->dev, | ||
| 561 | "%s: failed to read from the CNTRL register\n", | ||
| 562 | __func__); | ||
| 563 | return ret; | ||
| 564 | } | ||
| 565 | |||
| 566 | if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) | ||
| 567 | != (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) { | ||
| 568 | dev_err(&chip->client->dev, | ||
| 569 | "%s: Device is not powered on and/or ADC is not enabled\n", | ||
| 570 | __func__); | ||
| 571 | return -EINVAL; | ||
| 572 | } else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) { | ||
| 573 | dev_err(&chip->client->dev, | ||
| 574 | "%s: The two ADC channels have not completed an integration cycle\n", | ||
| 575 | __func__); | ||
| 576 | return -ENODATA; | ||
| 577 | } | ||
| 578 | |||
| 579 | lux_val = tsl2772_get_lux(indio_dev); | ||
| 580 | if (lux_val < 0) { | ||
| 581 | dev_err(&chip->client->dev, | ||
| 582 | "%s: failed to get lux\n", __func__); | ||
| 583 | return lux_val; | ||
| 584 | } | ||
| 585 | |||
| 586 | ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / | ||
| 587 | lux_val; | ||
| 588 | if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) | ||
| 589 | return -ERANGE; | ||
| 590 | |||
| 591 | chip->settings.als_gain_trim = ret; | ||
| 592 | |||
| 593 | return ret; | ||
| 594 | } | ||
| 595 | |||
| 596 | static int tsl2772_chip_on(struct iio_dev *indio_dev) | ||
| 597 | { | ||
| 598 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 599 | int ret, i, als_count, als_time_us; | ||
| 600 | u8 *dev_reg, reg_val; | ||
| 601 | |||
| 602 | /* Non calculated parameters */ | ||
| 603 | chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time; | ||
| 604 | chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time; | ||
| 605 | chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time; | ||
| 606 | chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] = | ||
| 607 | chip->settings.als_prox_config; | ||
| 608 | |||
| 609 | chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] = | ||
| 610 | (chip->settings.als_thresh_low) & 0xFF; | ||
| 611 | chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] = | ||
| 612 | (chip->settings.als_thresh_low >> 8) & 0xFF; | ||
| 613 | chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] = | ||
| 614 | (chip->settings.als_thresh_high) & 0xFF; | ||
| 615 | chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] = | ||
| 616 | (chip->settings.als_thresh_high >> 8) & 0xFF; | ||
| 617 | chip->tsl2772_config[TSL2772_PERSISTENCE] = | ||
| 618 | (chip->settings.prox_persistence & 0xFF) << 4 | | ||
| 619 | (chip->settings.als_persistence & 0xFF); | ||
| 620 | |||
| 621 | chip->tsl2772_config[TSL2772_PRX_COUNT] = | ||
| 622 | chip->settings.prox_pulse_count; | ||
| 623 | chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] = | ||
| 624 | (chip->settings.prox_thres_low) & 0xFF; | ||
| 625 | chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] = | ||
| 626 | (chip->settings.prox_thres_low >> 8) & 0xFF; | ||
| 627 | chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] = | ||
| 628 | (chip->settings.prox_thres_high) & 0xFF; | ||
| 629 | chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] = | ||
| 630 | (chip->settings.prox_thres_high >> 8) & 0xFF; | ||
| 631 | |||
| 632 | /* and make sure we're not already on */ | ||
| 633 | if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { | ||
| 634 | /* if forcing a register update - turn off, then on */ | ||
| 635 | dev_info(&chip->client->dev, "device is already enabled\n"); | ||
| 636 | return -EINVAL; | ||
| 637 | } | ||
| 638 | |||
| 639 | /* Set the gain based on tsl2772_settings struct */ | ||
| 640 | chip->tsl2772_config[TSL2772_GAIN] = | ||
| 641 | (chip->settings.als_gain & 0xFF) | | ||
| 642 | ((chip->settings.prox_gain & 0xFF) << 2) | | ||
| 643 | (chip->settings.prox_diode << 4) | | ||
| 644 | (chip->settings.prox_power << 6); | ||
| 645 | |||
| 646 | /* set chip time scaling and saturation */ | ||
| 647 | als_count = 256 - chip->settings.als_time; | ||
| 648 | als_time_us = als_count * tsl2772_int_time_avail[chip->id][3]; | ||
| 649 | chip->als_saturation = als_count * 768; /* 75% of full scale */ | ||
| 650 | chip->als_gain_time_scale = als_time_us * | ||
| 651 | tsl2772_als_gain[chip->settings.als_gain]; | ||
| 652 | |||
| 653 | /* | ||
| 654 | * TSL2772 Specific power-on / adc enable sequence | ||
| 655 | * Power on the device 1st. | ||
| 656 | */ | ||
| 657 | ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); | ||
| 658 | if (ret < 0) | ||
| 659 | return ret; | ||
| 660 | |||
| 661 | /* | ||
| 662 | * Use the following shadow copy for our delay before enabling ADC. | ||
| 663 | * Write all the registers. | ||
| 664 | */ | ||
| 665 | for (i = 0, dev_reg = chip->tsl2772_config; | ||
| 666 | i < TSL2772_MAX_CONFIG_REG; i++) { | ||
| 667 | int reg = TSL2772_CMD_REG + i; | ||
| 668 | |||
| 669 | ret = i2c_smbus_write_byte_data(chip->client, reg, | ||
| 670 | *dev_reg++); | ||
| 671 | if (ret < 0) { | ||
| 672 | dev_err(&chip->client->dev, | ||
| 673 | "%s: failed to write to register %x: %d\n", | ||
| 674 | __func__, reg, ret); | ||
| 675 | return ret; | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | /* Power-on settling time */ | ||
| 680 | usleep_range(3000, 3500); | ||
| 681 | |||
| 682 | reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL | | ||
| 683 | TSL2772_CNTL_PROX_DET_ENBL; | ||
| 684 | if (chip->settings.als_interrupt_en) | ||
| 685 | reg_val |= TSL2772_CNTL_ALS_INT_ENBL; | ||
| 686 | if (chip->settings.prox_interrupt_en) | ||
| 687 | reg_val |= TSL2772_CNTL_PROX_INT_ENBL; | ||
| 688 | |||
| 689 | ret = tsl2772_write_control_reg(chip, reg_val); | ||
| 690 | if (ret < 0) | ||
| 691 | return ret; | ||
| 692 | |||
| 693 | ret = i2c_smbus_write_byte(chip->client, | ||
| 694 | TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | | ||
| 695 | TSL2772_CMD_PROXALS_INT_CLR); | ||
| 696 | if (ret < 0) { | ||
| 697 | dev_err(&chip->client->dev, | ||
| 698 | "%s: failed to clear interrupt status: %d\n", | ||
| 699 | __func__, ret); | ||
| 700 | return ret; | ||
| 701 | } | ||
| 702 | |||
| 703 | chip->tsl2772_chip_status = TSL2772_CHIP_WORKING; | ||
| 704 | |||
| 705 | return ret; | ||
| 706 | } | ||
| 707 | |||
| 708 | static int tsl2772_chip_off(struct iio_dev *indio_dev) | ||
| 709 | { | ||
| 710 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 711 | |||
| 712 | /* turn device off */ | ||
| 713 | chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED; | ||
| 714 | return tsl2772_write_control_reg(chip, 0x00); | ||
| 715 | } | ||
| 716 | |||
| 717 | /** | ||
| 718 | * tsl2772_invoke_change - power cycle the device to implement the user | ||
| 719 | * parameters | ||
| 720 | * @indio_dev: pointer to IIO device | ||
| 721 | * | ||
| 722 | * Obtain and lock both ALS and PROX resources, determine and save device state | ||
| 723 | * (On/Off), cycle device to implement updated parameter, put device back into | ||
| 724 | * proper state, and unlock resource. | ||
| 725 | */ | ||
| 726 | static int tsl2772_invoke_change(struct iio_dev *indio_dev) | ||
| 727 | { | ||
| 728 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 729 | int device_status = chip->tsl2772_chip_status; | ||
| 730 | int ret; | ||
| 731 | |||
| 732 | mutex_lock(&chip->als_mutex); | ||
| 733 | mutex_lock(&chip->prox_mutex); | ||
| 734 | |||
| 735 | if (device_status == TSL2772_CHIP_WORKING) { | ||
| 736 | ret = tsl2772_chip_off(indio_dev); | ||
| 737 | if (ret < 0) | ||
| 738 | goto unlock; | ||
| 739 | } | ||
| 740 | |||
| 741 | ret = tsl2772_chip_on(indio_dev); | ||
| 742 | |||
| 743 | unlock: | ||
| 744 | mutex_unlock(&chip->prox_mutex); | ||
| 745 | mutex_unlock(&chip->als_mutex); | ||
| 746 | |||
| 747 | return ret; | ||
| 748 | } | ||
| 749 | |||
| 750 | static int tsl2772_prox_cal(struct iio_dev *indio_dev) | ||
| 751 | { | ||
| 752 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 753 | int prox_history[MAX_SAMPLES_CAL + 1]; | ||
| 754 | int i, ret, mean, max, sample_sum; | ||
| 755 | |||
| 756 | if (chip->settings.prox_max_samples_cal < 1 || | ||
| 757 | chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) | ||
| 758 | return -EINVAL; | ||
| 759 | |||
| 760 | for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { | ||
| 761 | usleep_range(15000, 17500); | ||
| 762 | ret = tsl2772_get_prox(indio_dev); | ||
| 763 | if (ret < 0) | ||
| 764 | return ret; | ||
| 765 | |||
| 766 | prox_history[i] = chip->prox_data; | ||
| 767 | } | ||
| 768 | |||
| 769 | sample_sum = 0; | ||
| 770 | max = INT_MIN; | ||
| 771 | for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { | ||
| 772 | sample_sum += prox_history[i]; | ||
| 773 | max = max(max, prox_history[i]); | ||
| 774 | } | ||
| 775 | mean = sample_sum / chip->settings.prox_max_samples_cal; | ||
| 776 | |||
| 777 | chip->settings.prox_thres_high = (max << 1) - mean; | ||
| 778 | |||
| 779 | return tsl2772_invoke_change(indio_dev); | ||
| 780 | } | ||
| 781 | |||
| 782 | static int tsl2772_read_avail(struct iio_dev *indio_dev, | ||
| 783 | struct iio_chan_spec const *chan, | ||
| 784 | const int **vals, int *type, int *length, | ||
| 785 | long mask) | ||
| 786 | { | ||
| 787 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 788 | |||
| 789 | switch (mask) { | ||
| 790 | case IIO_CHAN_INFO_CALIBSCALE: | ||
| 791 | if (chan->type == IIO_INTENSITY) { | ||
| 792 | *length = ARRAY_SIZE(tsl2772_int_calibscale_avail); | ||
| 793 | *vals = tsl2772_int_calibscale_avail; | ||
| 794 | } else { | ||
| 795 | *length = ARRAY_SIZE(tsl2772_prox_calibscale_avail); | ||
| 796 | *vals = tsl2772_prox_calibscale_avail; | ||
| 797 | } | ||
| 798 | *type = IIO_VAL_INT; | ||
| 799 | return IIO_AVAIL_LIST; | ||
| 800 | case IIO_CHAN_INFO_INT_TIME: | ||
| 801 | *length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]); | ||
| 802 | *vals = tsl2772_int_time_avail[chip->id]; | ||
| 803 | *type = IIO_VAL_INT_PLUS_MICRO; | ||
| 804 | return IIO_AVAIL_RANGE; | ||
| 805 | } | ||
| 806 | |||
| 807 | return -EINVAL; | ||
| 808 | } | ||
| 809 | |||
| 810 | static ssize_t in_illuminance0_target_input_show(struct device *dev, | ||
| 811 | struct device_attribute *attr, | ||
| 812 | char *buf) | ||
| 813 | { | ||
| 814 | struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); | ||
| 815 | |||
| 816 | return snprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); | ||
| 817 | } | ||
| 818 | |||
| 819 | static ssize_t in_illuminance0_target_input_store(struct device *dev, | ||
| 820 | struct device_attribute *attr, | ||
| 821 | const char *buf, size_t len) | ||
| 822 | { | ||
| 823 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 824 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 825 | u16 value; | ||
| 826 | int ret; | ||
| 827 | |||
| 828 | if (kstrtou16(buf, 0, &value)) | ||
| 829 | return -EINVAL; | ||
| 830 | |||
| 831 | chip->settings.als_cal_target = value; | ||
| 832 | ret = tsl2772_invoke_change(indio_dev); | ||
| 833 | if (ret < 0) | ||
| 834 | return ret; | ||
| 835 | |||
| 836 | return len; | ||
| 837 | } | ||
| 838 | |||
| 839 | static ssize_t in_illuminance0_calibrate_store(struct device *dev, | ||
| 840 | struct device_attribute *attr, | ||
| 841 | const char *buf, size_t len) | ||
| 842 | { | ||
| 843 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 844 | bool value; | ||
| 845 | int ret; | ||
| 846 | |||
| 847 | if (kstrtobool(buf, &value) || !value) | ||
| 848 | return -EINVAL; | ||
| 849 | |||
| 850 | ret = tsl2772_als_calibrate(indio_dev); | ||
| 851 | if (ret < 0) | ||
| 852 | return ret; | ||
| 853 | |||
| 854 | ret = tsl2772_invoke_change(indio_dev); | ||
| 855 | if (ret < 0) | ||
| 856 | return ret; | ||
| 857 | |||
| 858 | return len; | ||
| 859 | } | ||
| 860 | |||
| 861 | static ssize_t in_illuminance0_lux_table_show(struct device *dev, | ||
| 862 | struct device_attribute *attr, | ||
| 863 | char *buf) | ||
| 864 | { | ||
| 865 | struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); | ||
| 866 | int i = 0; | ||
| 867 | int offset = 0; | ||
| 868 | |||
| 869 | while (i < TSL2772_MAX_LUX_TABLE_SIZE) { | ||
| 870 | offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,", | ||
| 871 | chip->tsl2772_device_lux[i].ch0, | ||
| 872 | chip->tsl2772_device_lux[i].ch1); | ||
| 873 | if (chip->tsl2772_device_lux[i].ch0 == 0) { | ||
| 874 | /* | ||
| 875 | * We just printed the first "0" entry. | ||
| 876 | * Now get rid of the extra "," and break. | ||
| 877 | */ | ||
| 878 | offset--; | ||
| 879 | break; | ||
| 880 | } | ||
| 881 | i++; | ||
| 882 | } | ||
| 883 | |||
| 884 | offset += snprintf(buf + offset, PAGE_SIZE, "\n"); | ||
| 885 | return offset; | ||
| 886 | } | ||
| 887 | |||
| 888 | static ssize_t in_illuminance0_lux_table_store(struct device *dev, | ||
| 889 | struct device_attribute *attr, | ||
| 890 | const char *buf, size_t len) | ||
| 891 | { | ||
| 892 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 893 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 894 | int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1]; | ||
| 895 | int n, ret; | ||
| 896 | |||
| 897 | get_options(buf, ARRAY_SIZE(value), value); | ||
| 898 | |||
| 899 | /* | ||
| 900 | * We now have an array of ints starting at value[1], and | ||
| 901 | * enumerated by value[0]. | ||
| 902 | * We expect each group of two ints to be one table entry, | ||
| 903 | * and the last table entry is all 0. | ||
| 904 | */ | ||
| 905 | n = value[0]; | ||
| 906 | if ((n % 2) || n < 4 || | ||
| 907 | n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) | ||
| 908 | return -EINVAL; | ||
| 909 | |||
| 910 | if ((value[(n - 1)] | value[n]) != 0) | ||
| 911 | return -EINVAL; | ||
| 912 | |||
| 913 | if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { | ||
| 914 | ret = tsl2772_chip_off(indio_dev); | ||
| 915 | if (ret < 0) | ||
| 916 | return ret; | ||
| 917 | } | ||
| 918 | |||
| 919 | /* Zero out the table */ | ||
| 920 | memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux)); | ||
| 921 | memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4)); | ||
| 922 | |||
| 923 | ret = tsl2772_invoke_change(indio_dev); | ||
| 924 | if (ret < 0) | ||
| 925 | return ret; | ||
| 926 | |||
| 927 | return len; | ||
| 928 | } | ||
| 929 | |||
| 930 | static ssize_t in_proximity0_calibrate_store(struct device *dev, | ||
| 931 | struct device_attribute *attr, | ||
| 932 | const char *buf, size_t len) | ||
| 933 | { | ||
| 934 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 935 | bool value; | ||
| 936 | int ret; | ||
| 937 | |||
| 938 | if (kstrtobool(buf, &value) || !value) | ||
| 939 | return -EINVAL; | ||
| 940 | |||
| 941 | ret = tsl2772_prox_cal(indio_dev); | ||
| 942 | if (ret < 0) | ||
| 943 | return ret; | ||
| 944 | |||
| 945 | ret = tsl2772_invoke_change(indio_dev); | ||
| 946 | if (ret < 0) | ||
| 947 | return ret; | ||
| 948 | |||
| 949 | return len; | ||
| 950 | } | ||
| 951 | |||
| 952 | static int tsl2772_read_interrupt_config(struct iio_dev *indio_dev, | ||
| 953 | const struct iio_chan_spec *chan, | ||
| 954 | enum iio_event_type type, | ||
| 955 | enum iio_event_direction dir) | ||
| 956 | { | ||
| 957 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 958 | |||
| 959 | if (chan->type == IIO_INTENSITY) | ||
| 960 | return chip->settings.als_interrupt_en; | ||
| 961 | else | ||
| 962 | return chip->settings.prox_interrupt_en; | ||
| 963 | } | ||
| 964 | |||
| 965 | static int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, | ||
| 966 | const struct iio_chan_spec *chan, | ||
| 967 | enum iio_event_type type, | ||
| 968 | enum iio_event_direction dir, | ||
| 969 | int val) | ||
| 970 | { | ||
| 971 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 972 | |||
| 973 | if (chan->type == IIO_INTENSITY) | ||
| 974 | chip->settings.als_interrupt_en = val ? true : false; | ||
| 975 | else | ||
| 976 | chip->settings.prox_interrupt_en = val ? true : false; | ||
| 977 | |||
| 978 | return tsl2772_invoke_change(indio_dev); | ||
| 979 | } | ||
| 980 | |||
| 981 | static int tsl2772_write_event_value(struct iio_dev *indio_dev, | ||
| 982 | const struct iio_chan_spec *chan, | ||
| 983 | enum iio_event_type type, | ||
| 984 | enum iio_event_direction dir, | ||
| 985 | enum iio_event_info info, | ||
| 986 | int val, int val2) | ||
| 987 | { | ||
| 988 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 989 | int ret = -EINVAL, count, persistence; | ||
| 990 | u8 time; | ||
| 991 | |||
| 992 | switch (info) { | ||
| 993 | case IIO_EV_INFO_VALUE: | ||
| 994 | if (chan->type == IIO_INTENSITY) { | ||
| 995 | switch (dir) { | ||
| 996 | case IIO_EV_DIR_RISING: | ||
| 997 | chip->settings.als_thresh_high = val; | ||
| 998 | ret = 0; | ||
| 999 | break; | ||
| 1000 | case IIO_EV_DIR_FALLING: | ||
| 1001 | chip->settings.als_thresh_low = val; | ||
| 1002 | ret = 0; | ||
| 1003 | break; | ||
| 1004 | default: | ||
| 1005 | break; | ||
| 1006 | } | ||
| 1007 | } else { | ||
| 1008 | switch (dir) { | ||
| 1009 | case IIO_EV_DIR_RISING: | ||
| 1010 | chip->settings.prox_thres_high = val; | ||
| 1011 | ret = 0; | ||
| 1012 | break; | ||
| 1013 | case IIO_EV_DIR_FALLING: | ||
| 1014 | chip->settings.prox_thres_low = val; | ||
| 1015 | ret = 0; | ||
| 1016 | break; | ||
| 1017 | default: | ||
| 1018 | break; | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | break; | ||
| 1022 | case IIO_EV_INFO_PERIOD: | ||
| 1023 | if (chan->type == IIO_INTENSITY) | ||
| 1024 | time = chip->settings.als_time; | ||
| 1025 | else | ||
| 1026 | time = chip->settings.prox_time; | ||
| 1027 | |||
| 1028 | count = 256 - time; | ||
| 1029 | persistence = ((val * 1000000) + val2) / | ||
| 1030 | (count * tsl2772_int_time_avail[chip->id][3]); | ||
| 1031 | |||
| 1032 | if (chan->type == IIO_INTENSITY) { | ||
| 1033 | /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ | ||
| 1034 | if (persistence > 3) | ||
| 1035 | persistence = (persistence / 5) + 3; | ||
| 1036 | |||
| 1037 | chip->settings.als_persistence = persistence; | ||
| 1038 | } else { | ||
| 1039 | chip->settings.prox_persistence = persistence; | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | ret = 0; | ||
| 1043 | break; | ||
| 1044 | default: | ||
| 1045 | break; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | if (ret < 0) | ||
| 1049 | return ret; | ||
| 1050 | |||
| 1051 | return tsl2772_invoke_change(indio_dev); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | static int tsl2772_read_event_value(struct iio_dev *indio_dev, | ||
| 1055 | const struct iio_chan_spec *chan, | ||
| 1056 | enum iio_event_type type, | ||
| 1057 | enum iio_event_direction dir, | ||
| 1058 | enum iio_event_info info, | ||
| 1059 | int *val, int *val2) | ||
| 1060 | { | ||
| 1061 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 1062 | int filter_delay, persistence; | ||
| 1063 | u8 time; | ||
| 1064 | |||
| 1065 | switch (info) { | ||
| 1066 | case IIO_EV_INFO_VALUE: | ||
| 1067 | if (chan->type == IIO_INTENSITY) { | ||
| 1068 | switch (dir) { | ||
| 1069 | case IIO_EV_DIR_RISING: | ||
| 1070 | *val = chip->settings.als_thresh_high; | ||
| 1071 | return IIO_VAL_INT; | ||
| 1072 | case IIO_EV_DIR_FALLING: | ||
| 1073 | *val = chip->settings.als_thresh_low; | ||
| 1074 | return IIO_VAL_INT; | ||
| 1075 | default: | ||
| 1076 | return -EINVAL; | ||
| 1077 | } | ||
| 1078 | } else { | ||
| 1079 | switch (dir) { | ||
| 1080 | case IIO_EV_DIR_RISING: | ||
| 1081 | *val = chip->settings.prox_thres_high; | ||
| 1082 | return IIO_VAL_INT; | ||
| 1083 | case IIO_EV_DIR_FALLING: | ||
| 1084 | *val = chip->settings.prox_thres_low; | ||
| 1085 | return IIO_VAL_INT; | ||
| 1086 | default: | ||
| 1087 | return -EINVAL; | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | break; | ||
| 1091 | case IIO_EV_INFO_PERIOD: | ||
| 1092 | if (chan->type == IIO_INTENSITY) { | ||
| 1093 | time = chip->settings.als_time; | ||
| 1094 | persistence = chip->settings.als_persistence; | ||
| 1095 | |||
| 1096 | /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ | ||
| 1097 | if (persistence > 3) | ||
| 1098 | persistence = (persistence - 3) * 5; | ||
| 1099 | } else { | ||
| 1100 | time = chip->settings.prox_time; | ||
| 1101 | persistence = chip->settings.prox_persistence; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | filter_delay = persistence * (256 - time) * | ||
| 1105 | tsl2772_int_time_avail[chip->id][3]; | ||
| 1106 | |||
| 1107 | *val = filter_delay / 1000000; | ||
| 1108 | *val2 = filter_delay % 1000000; | ||
| 1109 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 1110 | default: | ||
| 1111 | return -EINVAL; | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | static int tsl2772_read_raw(struct iio_dev *indio_dev, | ||
| 1116 | struct iio_chan_spec const *chan, | ||
| 1117 | int *val, | ||
| 1118 | int *val2, | ||
| 1119 | long mask) | ||
| 1120 | { | ||
| 1121 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 1122 | |||
| 1123 | switch (mask) { | ||
| 1124 | case IIO_CHAN_INFO_PROCESSED: | ||
| 1125 | switch (chan->type) { | ||
| 1126 | case IIO_LIGHT: | ||
| 1127 | tsl2772_get_lux(indio_dev); | ||
| 1128 | *val = chip->als_cur_info.lux; | ||
| 1129 | return IIO_VAL_INT; | ||
| 1130 | default: | ||
| 1131 | return -EINVAL; | ||
| 1132 | } | ||
| 1133 | case IIO_CHAN_INFO_RAW: | ||
| 1134 | switch (chan->type) { | ||
| 1135 | case IIO_INTENSITY: | ||
| 1136 | tsl2772_get_lux(indio_dev); | ||
| 1137 | if (chan->channel == 0) | ||
| 1138 | *val = chip->als_cur_info.als_ch0; | ||
| 1139 | else | ||
| 1140 | *val = chip->als_cur_info.als_ch1; | ||
| 1141 | return IIO_VAL_INT; | ||
| 1142 | case IIO_PROXIMITY: | ||
| 1143 | tsl2772_get_prox(indio_dev); | ||
| 1144 | *val = chip->prox_data; | ||
| 1145 | return IIO_VAL_INT; | ||
| 1146 | default: | ||
| 1147 | return -EINVAL; | ||
| 1148 | } | ||
| 1149 | break; | ||
| 1150 | case IIO_CHAN_INFO_CALIBSCALE: | ||
| 1151 | if (chan->type == IIO_LIGHT) | ||
| 1152 | *val = tsl2772_als_gain[chip->settings.als_gain]; | ||
| 1153 | else | ||
| 1154 | *val = tsl2772_prox_gain[chip->settings.prox_gain]; | ||
| 1155 | return IIO_VAL_INT; | ||
| 1156 | case IIO_CHAN_INFO_CALIBBIAS: | ||
| 1157 | *val = chip->settings.als_gain_trim; | ||
| 1158 | return IIO_VAL_INT; | ||
| 1159 | case IIO_CHAN_INFO_INT_TIME: | ||
| 1160 | *val = 0; | ||
| 1161 | *val2 = (256 - chip->settings.als_time) * | ||
| 1162 | tsl2772_int_time_avail[chip->id][3]; | ||
| 1163 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 1164 | default: | ||
| 1165 | return -EINVAL; | ||
| 1166 | } | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | static int tsl2772_write_raw(struct iio_dev *indio_dev, | ||
| 1170 | struct iio_chan_spec const *chan, | ||
| 1171 | int val, | ||
| 1172 | int val2, | ||
| 1173 | long mask) | ||
| 1174 | { | ||
| 1175 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 1176 | |||
| 1177 | switch (mask) { | ||
| 1178 | case IIO_CHAN_INFO_CALIBSCALE: | ||
| 1179 | if (chan->type == IIO_INTENSITY) { | ||
| 1180 | switch (val) { | ||
| 1181 | case 1: | ||
| 1182 | chip->settings.als_gain = 0; | ||
| 1183 | break; | ||
| 1184 | case 8: | ||
| 1185 | chip->settings.als_gain = 1; | ||
| 1186 | break; | ||
| 1187 | case 16: | ||
| 1188 | chip->settings.als_gain = 2; | ||
| 1189 | break; | ||
| 1190 | case 120: | ||
| 1191 | chip->settings.als_gain = 3; | ||
| 1192 | break; | ||
| 1193 | default: | ||
| 1194 | return -EINVAL; | ||
| 1195 | } | ||
| 1196 | } else { | ||
| 1197 | switch (val) { | ||
| 1198 | case 1: | ||
| 1199 | chip->settings.prox_gain = 0; | ||
| 1200 | break; | ||
| 1201 | case 2: | ||
| 1202 | chip->settings.prox_gain = 1; | ||
| 1203 | break; | ||
| 1204 | case 4: | ||
| 1205 | chip->settings.prox_gain = 2; | ||
| 1206 | break; | ||
| 1207 | case 8: | ||
| 1208 | chip->settings.prox_gain = 3; | ||
| 1209 | break; | ||
| 1210 | default: | ||
| 1211 | return -EINVAL; | ||
| 1212 | } | ||
| 1213 | } | ||
| 1214 | break; | ||
| 1215 | case IIO_CHAN_INFO_CALIBBIAS: | ||
| 1216 | if (val < TSL2772_ALS_GAIN_TRIM_MIN || | ||
| 1217 | val > TSL2772_ALS_GAIN_TRIM_MAX) | ||
| 1218 | return -EINVAL; | ||
| 1219 | |||
| 1220 | chip->settings.als_gain_trim = val; | ||
| 1221 | break; | ||
| 1222 | case IIO_CHAN_INFO_INT_TIME: | ||
| 1223 | if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] || | ||
| 1224 | val2 > tsl2772_int_time_avail[chip->id][5]) | ||
| 1225 | return -EINVAL; | ||
| 1226 | |||
| 1227 | chip->settings.als_time = 256 - | ||
| 1228 | (val2 / tsl2772_int_time_avail[chip->id][3]); | ||
| 1229 | break; | ||
| 1230 | default: | ||
| 1231 | return -EINVAL; | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | return tsl2772_invoke_change(indio_dev); | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | static DEVICE_ATTR_RW(in_illuminance0_target_input); | ||
| 1238 | |||
| 1239 | static DEVICE_ATTR_WO(in_illuminance0_calibrate); | ||
| 1240 | |||
| 1241 | static DEVICE_ATTR_WO(in_proximity0_calibrate); | ||
| 1242 | |||
| 1243 | static DEVICE_ATTR_RW(in_illuminance0_lux_table); | ||
| 1244 | |||
| 1245 | /* Use the default register values to identify the Taos device */ | ||
| 1246 | static int tsl2772_device_id_verif(int id, int target) | ||
| 1247 | { | ||
| 1248 | switch (target) { | ||
| 1249 | case tsl2571: | ||
| 1250 | case tsl2671: | ||
| 1251 | case tsl2771: | ||
| 1252 | return (id & 0xf0) == TRITON_ID; | ||
| 1253 | case tmd2671: | ||
| 1254 | case tmd2771: | ||
| 1255 | return (id & 0xf0) == HALIBUT_ID; | ||
| 1256 | case tsl2572: | ||
| 1257 | case tsl2672: | ||
| 1258 | case tmd2672: | ||
| 1259 | case tsl2772: | ||
| 1260 | case tmd2772: | ||
| 1261 | return (id & 0xf0) == SWORDFISH_ID; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | return -EINVAL; | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | static irqreturn_t tsl2772_event_handler(int irq, void *private) | ||
| 1268 | { | ||
| 1269 | struct iio_dev *indio_dev = private; | ||
| 1270 | struct tsl2772_chip *chip = iio_priv(indio_dev); | ||
| 1271 | s64 timestamp = iio_get_time_ns(indio_dev); | ||
| 1272 | int ret; | ||
| 1273 | |||
| 1274 | ret = tsl2772_read_status(chip); | ||
| 1275 | if (ret < 0) | ||
| 1276 | return IRQ_HANDLED; | ||
| 1277 | |||
| 1278 | /* What type of interrupt do we need to process */ | ||
| 1279 | if (ret & TSL2772_STA_PRX_INTR) { | ||
| 1280 | iio_push_event(indio_dev, | ||
| 1281 | IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, | ||
| 1282 | 0, | ||
| 1283 | IIO_EV_TYPE_THRESH, | ||
| 1284 | IIO_EV_DIR_EITHER), | ||
| 1285 | timestamp); | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | if (ret & TSL2772_STA_ALS_INTR) { | ||
| 1289 | iio_push_event(indio_dev, | ||
| 1290 | IIO_UNMOD_EVENT_CODE(IIO_LIGHT, | ||
| 1291 | 0, | ||
| 1292 | IIO_EV_TYPE_THRESH, | ||
| 1293 | IIO_EV_DIR_EITHER), | ||
| 1294 | timestamp); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | ret = i2c_smbus_write_byte(chip->client, | ||
| 1298 | TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | | ||
| 1299 | TSL2772_CMD_PROXALS_INT_CLR); | ||
| 1300 | if (ret < 0) | ||
| 1301 | dev_err(&chip->client->dev, | ||
| 1302 | "%s: failed to clear interrupt status: %d\n", | ||
| 1303 | __func__, ret); | ||
| 1304 | |||
| 1305 | return IRQ_HANDLED; | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | static struct attribute *tsl2772_ALS_device_attrs[] = { | ||
| 1309 | &dev_attr_in_illuminance0_target_input.attr, | ||
| 1310 | &dev_attr_in_illuminance0_calibrate.attr, | ||
| 1311 | &dev_attr_in_illuminance0_lux_table.attr, | ||
| 1312 | NULL | ||
| 1313 | }; | ||
| 1314 | |||
| 1315 | static struct attribute *tsl2772_PRX_device_attrs[] = { | ||
| 1316 | &dev_attr_in_proximity0_calibrate.attr, | ||
| 1317 | NULL | ||
| 1318 | }; | ||
| 1319 | |||
| 1320 | static struct attribute *tsl2772_ALSPRX_device_attrs[] = { | ||
| 1321 | &dev_attr_in_illuminance0_target_input.attr, | ||
| 1322 | &dev_attr_in_illuminance0_calibrate.attr, | ||
| 1323 | &dev_attr_in_illuminance0_lux_table.attr, | ||
| 1324 | NULL | ||
| 1325 | }; | ||
| 1326 | |||
| 1327 | static struct attribute *tsl2772_PRX2_device_attrs[] = { | ||
| 1328 | &dev_attr_in_proximity0_calibrate.attr, | ||
| 1329 | NULL | ||
| 1330 | }; | ||
| 1331 | |||
| 1332 | static struct attribute *tsl2772_ALSPRX2_device_attrs[] = { | ||
| 1333 | &dev_attr_in_illuminance0_target_input.attr, | ||
| 1334 | &dev_attr_in_illuminance0_calibrate.attr, | ||
| 1335 | &dev_attr_in_illuminance0_lux_table.attr, | ||
| 1336 | &dev_attr_in_proximity0_calibrate.attr, | ||
| 1337 | NULL | ||
| 1338 | }; | ||
| 1339 | |||
| 1340 | static const struct attribute_group tsl2772_device_attr_group_tbl[] = { | ||
| 1341 | [ALS] = { | ||
| 1342 | .attrs = tsl2772_ALS_device_attrs, | ||
| 1343 | }, | ||
| 1344 | [PRX] = { | ||
| 1345 | .attrs = tsl2772_PRX_device_attrs, | ||
| 1346 | }, | ||
| 1347 | [ALSPRX] = { | ||
| 1348 | .attrs = tsl2772_ALSPRX_device_attrs, | ||
| 1349 | }, | ||
| 1350 | [PRX2] = { | ||
| 1351 | .attrs = tsl2772_PRX2_device_attrs, | ||
| 1352 | }, | ||
| 1353 | [ALSPRX2] = { | ||
| 1354 | .attrs = tsl2772_ALSPRX2_device_attrs, | ||
| 1355 | }, | ||
| 1356 | }; | ||
| 1357 | |||
| 1358 | #define TSL2772_DEVICE_INFO(type)[type] = \ | ||
| 1359 | { \ | ||
| 1360 | .attrs = &tsl2772_device_attr_group_tbl[type], \ | ||
| 1361 | .read_raw = &tsl2772_read_raw, \ | ||
| 1362 | .read_avail = &tsl2772_read_avail, \ | ||
| 1363 | .write_raw = &tsl2772_write_raw, \ | ||
| 1364 | .read_event_value = &tsl2772_read_event_value, \ | ||
| 1365 | .write_event_value = &tsl2772_write_event_value, \ | ||
| 1366 | .read_event_config = &tsl2772_read_interrupt_config, \ | ||
| 1367 | .write_event_config = &tsl2772_write_interrupt_config, \ | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | static const struct iio_info tsl2772_device_info[] = { | ||
| 1371 | TSL2772_DEVICE_INFO(ALS), | ||
| 1372 | TSL2772_DEVICE_INFO(PRX), | ||
| 1373 | TSL2772_DEVICE_INFO(ALSPRX), | ||
| 1374 | TSL2772_DEVICE_INFO(PRX2), | ||
| 1375 | TSL2772_DEVICE_INFO(ALSPRX2), | ||
| 1376 | }; | ||
| 1377 | |||
| 1378 | static const struct iio_event_spec tsl2772_events[] = { | ||
| 1379 | { | ||
| 1380 | .type = IIO_EV_TYPE_THRESH, | ||
| 1381 | .dir = IIO_EV_DIR_RISING, | ||
| 1382 | .mask_separate = BIT(IIO_EV_INFO_VALUE), | ||
| 1383 | }, { | ||
| 1384 | .type = IIO_EV_TYPE_THRESH, | ||
| 1385 | .dir = IIO_EV_DIR_FALLING, | ||
| 1386 | .mask_separate = BIT(IIO_EV_INFO_VALUE), | ||
| 1387 | }, { | ||
| 1388 | .type = IIO_EV_TYPE_THRESH, | ||
| 1389 | .dir = IIO_EV_DIR_EITHER, | ||
| 1390 | .mask_separate = BIT(IIO_EV_INFO_PERIOD) | | ||
| 1391 | BIT(IIO_EV_INFO_ENABLE), | ||
| 1392 | }, | ||
| 1393 | }; | ||
| 1394 | |||
| 1395 | static const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = { | ||
| 1396 | [ALS] = { | ||
| 1397 | .channel_with_events = { | ||
| 1398 | { | ||
| 1399 | .type = IIO_LIGHT, | ||
| 1400 | .indexed = 1, | ||
| 1401 | .channel = 0, | ||
| 1402 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 1403 | }, { | ||
| 1404 | .type = IIO_INTENSITY, | ||
| 1405 | .indexed = 1, | ||
| 1406 | .channel = 0, | ||
| 1407 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1408 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1409 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1410 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1411 | .info_mask_separate_available = | ||
| 1412 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1413 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1414 | .event_spec = tsl2772_events, | ||
| 1415 | .num_event_specs = ARRAY_SIZE(tsl2772_events), | ||
| 1416 | }, { | ||
| 1417 | .type = IIO_INTENSITY, | ||
| 1418 | .indexed = 1, | ||
| 1419 | .channel = 1, | ||
| 1420 | }, | ||
| 1421 | }, | ||
| 1422 | .channel_without_events = { | ||
| 1423 | { | ||
| 1424 | .type = IIO_LIGHT, | ||
| 1425 | .indexed = 1, | ||
| 1426 | .channel = 0, | ||
| 1427 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 1428 | }, { | ||
| 1429 | .type = IIO_INTENSITY, | ||
| 1430 | .indexed = 1, | ||
| 1431 | .channel = 0, | ||
| 1432 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1433 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1434 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1435 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1436 | .info_mask_separate_available = | ||
| 1437 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1438 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1439 | }, { | ||
| 1440 | .type = IIO_INTENSITY, | ||
| 1441 | .indexed = 1, | ||
| 1442 | .channel = 1, | ||
| 1443 | }, | ||
| 1444 | }, | ||
| 1445 | .chan_table_elements = 3, | ||
| 1446 | .info = &tsl2772_device_info[ALS], | ||
| 1447 | }, | ||
| 1448 | [PRX] = { | ||
| 1449 | .channel_with_events = { | ||
| 1450 | { | ||
| 1451 | .type = IIO_PROXIMITY, | ||
| 1452 | .indexed = 1, | ||
| 1453 | .channel = 0, | ||
| 1454 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1455 | .event_spec = tsl2772_events, | ||
| 1456 | .num_event_specs = ARRAY_SIZE(tsl2772_events), | ||
| 1457 | }, | ||
| 1458 | }, | ||
| 1459 | .channel_without_events = { | ||
| 1460 | { | ||
| 1461 | .type = IIO_PROXIMITY, | ||
| 1462 | .indexed = 1, | ||
| 1463 | .channel = 0, | ||
| 1464 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1465 | }, | ||
| 1466 | }, | ||
| 1467 | .chan_table_elements = 1, | ||
| 1468 | .info = &tsl2772_device_info[PRX], | ||
| 1469 | }, | ||
| 1470 | [ALSPRX] = { | ||
| 1471 | .channel_with_events = { | ||
| 1472 | { | ||
| 1473 | .type = IIO_LIGHT, | ||
| 1474 | .indexed = 1, | ||
| 1475 | .channel = 0, | ||
| 1476 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 1477 | }, { | ||
| 1478 | .type = IIO_INTENSITY, | ||
| 1479 | .indexed = 1, | ||
| 1480 | .channel = 0, | ||
| 1481 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1482 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1483 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1484 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1485 | .info_mask_separate_available = | ||
| 1486 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1487 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1488 | .event_spec = tsl2772_events, | ||
| 1489 | .num_event_specs = ARRAY_SIZE(tsl2772_events), | ||
| 1490 | }, { | ||
| 1491 | .type = IIO_INTENSITY, | ||
| 1492 | .indexed = 1, | ||
| 1493 | .channel = 1, | ||
| 1494 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1495 | }, { | ||
| 1496 | .type = IIO_PROXIMITY, | ||
| 1497 | .indexed = 1, | ||
| 1498 | .channel = 0, | ||
| 1499 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1500 | .event_spec = tsl2772_events, | ||
| 1501 | .num_event_specs = ARRAY_SIZE(tsl2772_events), | ||
| 1502 | }, | ||
| 1503 | }, | ||
| 1504 | .channel_without_events = { | ||
| 1505 | { | ||
| 1506 | .type = IIO_LIGHT, | ||
| 1507 | .indexed = 1, | ||
| 1508 | .channel = 0, | ||
| 1509 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 1510 | }, { | ||
| 1511 | .type = IIO_INTENSITY, | ||
| 1512 | .indexed = 1, | ||
| 1513 | .channel = 0, | ||
| 1514 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1515 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1516 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1517 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1518 | .info_mask_separate_available = | ||
| 1519 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1520 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1521 | }, { | ||
| 1522 | .type = IIO_INTENSITY, | ||
| 1523 | .indexed = 1, | ||
| 1524 | .channel = 1, | ||
| 1525 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1526 | }, { | ||
| 1527 | .type = IIO_PROXIMITY, | ||
| 1528 | .indexed = 1, | ||
| 1529 | .channel = 0, | ||
| 1530 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1531 | }, | ||
| 1532 | }, | ||
| 1533 | .chan_table_elements = 4, | ||
| 1534 | .info = &tsl2772_device_info[ALSPRX], | ||
| 1535 | }, | ||
| 1536 | [PRX2] = { | ||
| 1537 | .channel_with_events = { | ||
| 1538 | { | ||
| 1539 | .type = IIO_PROXIMITY, | ||
| 1540 | .indexed = 1, | ||
| 1541 | .channel = 0, | ||
| 1542 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1543 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1544 | .info_mask_separate_available = | ||
| 1545 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1546 | .event_spec = tsl2772_events, | ||
| 1547 | .num_event_specs = ARRAY_SIZE(tsl2772_events), | ||
| 1548 | }, | ||
| 1549 | }, | ||
| 1550 | .channel_without_events = { | ||
| 1551 | { | ||
| 1552 | .type = IIO_PROXIMITY, | ||
| 1553 | .indexed = 1, | ||
| 1554 | .channel = 0, | ||
| 1555 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1556 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1557 | .info_mask_separate_available = | ||
| 1558 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1559 | }, | ||
| 1560 | }, | ||
| 1561 | .chan_table_elements = 1, | ||
| 1562 | .info = &tsl2772_device_info[PRX2], | ||
| 1563 | }, | ||
| 1564 | [ALSPRX2] = { | ||
| 1565 | .channel_with_events = { | ||
| 1566 | { | ||
| 1567 | .type = IIO_LIGHT, | ||
| 1568 | .indexed = 1, | ||
| 1569 | .channel = 0, | ||
| 1570 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 1571 | }, { | ||
| 1572 | .type = IIO_INTENSITY, | ||
| 1573 | .indexed = 1, | ||
| 1574 | .channel = 0, | ||
| 1575 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1576 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1577 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1578 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1579 | .info_mask_separate_available = | ||
| 1580 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1581 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1582 | .event_spec = tsl2772_events, | ||
| 1583 | .num_event_specs = ARRAY_SIZE(tsl2772_events), | ||
| 1584 | }, { | ||
| 1585 | .type = IIO_INTENSITY, | ||
| 1586 | .indexed = 1, | ||
| 1587 | .channel = 1, | ||
| 1588 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1589 | }, { | ||
| 1590 | .type = IIO_PROXIMITY, | ||
| 1591 | .indexed = 1, | ||
| 1592 | .channel = 0, | ||
| 1593 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1594 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1595 | .info_mask_separate_available = | ||
| 1596 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1597 | .event_spec = tsl2772_events, | ||
| 1598 | .num_event_specs = ARRAY_SIZE(tsl2772_events), | ||
| 1599 | }, | ||
| 1600 | }, | ||
| 1601 | .channel_without_events = { | ||
| 1602 | { | ||
| 1603 | .type = IIO_LIGHT, | ||
| 1604 | .indexed = 1, | ||
| 1605 | .channel = 0, | ||
| 1606 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 1607 | }, { | ||
| 1608 | .type = IIO_INTENSITY, | ||
| 1609 | .indexed = 1, | ||
| 1610 | .channel = 0, | ||
| 1611 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1612 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1613 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1614 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1615 | .info_mask_separate_available = | ||
| 1616 | BIT(IIO_CHAN_INFO_INT_TIME) | | ||
| 1617 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1618 | }, { | ||
| 1619 | .type = IIO_INTENSITY, | ||
| 1620 | .indexed = 1, | ||
| 1621 | .channel = 1, | ||
| 1622 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1623 | }, { | ||
| 1624 | .type = IIO_PROXIMITY, | ||
| 1625 | .indexed = 1, | ||
| 1626 | .channel = 0, | ||
| 1627 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1628 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1629 | .info_mask_separate_available = | ||
| 1630 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1631 | }, | ||
| 1632 | }, | ||
| 1633 | .chan_table_elements = 4, | ||
| 1634 | .info = &tsl2772_device_info[ALSPRX2], | ||
| 1635 | }, | ||
| 1636 | }; | ||
| 1637 | |||
| 1638 | static int tsl2772_probe(struct i2c_client *clientp, | ||
| 1639 | const struct i2c_device_id *id) | ||
| 1640 | { | ||
| 1641 | struct iio_dev *indio_dev; | ||
| 1642 | struct tsl2772_chip *chip; | ||
| 1643 | int ret; | ||
| 1644 | |||
| 1645 | indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); | ||
| 1646 | if (!indio_dev) | ||
| 1647 | return -ENOMEM; | ||
| 1648 | |||
| 1649 | chip = iio_priv(indio_dev); | ||
| 1650 | chip->client = clientp; | ||
| 1651 | i2c_set_clientdata(clientp, indio_dev); | ||
| 1652 | |||
| 1653 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 1654 | TSL2772_CMD_REG | TSL2772_CHIPID); | ||
| 1655 | if (ret < 0) | ||
| 1656 | return ret; | ||
| 1657 | |||
| 1658 | if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) { | ||
| 1659 | dev_info(&chip->client->dev, | ||
| 1660 | "%s: i2c device found does not match expected id\n", | ||
| 1661 | __func__); | ||
| 1662 | return -EINVAL; | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); | ||
| 1666 | if (ret < 0) { | ||
| 1667 | dev_err(&clientp->dev, | ||
| 1668 | "%s: Failed to write to CMD register: %d\n", | ||
| 1669 | __func__, ret); | ||
| 1670 | return ret; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | mutex_init(&chip->als_mutex); | ||
| 1674 | mutex_init(&chip->prox_mutex); | ||
| 1675 | |||
| 1676 | chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN; | ||
| 1677 | chip->pdata = dev_get_platdata(&clientp->dev); | ||
| 1678 | chip->id = id->driver_data; | ||
| 1679 | chip->chip_info = | ||
| 1680 | &tsl2772_chip_info_tbl[device_channel_config[id->driver_data]]; | ||
| 1681 | |||
| 1682 | indio_dev->info = chip->chip_info->info; | ||
| 1683 | indio_dev->dev.parent = &clientp->dev; | ||
| 1684 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 1685 | indio_dev->name = chip->client->name; | ||
| 1686 | indio_dev->num_channels = chip->chip_info->chan_table_elements; | ||
| 1687 | |||
| 1688 | if (clientp->irq) { | ||
| 1689 | indio_dev->channels = chip->chip_info->channel_with_events; | ||
| 1690 | |||
| 1691 | ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, | ||
| 1692 | NULL, | ||
| 1693 | &tsl2772_event_handler, | ||
| 1694 | IRQF_TRIGGER_FALLING | | ||
| 1695 | IRQF_ONESHOT, | ||
| 1696 | "TSL2772_event", | ||
| 1697 | indio_dev); | ||
| 1698 | if (ret) { | ||
| 1699 | dev_err(&clientp->dev, | ||
| 1700 | "%s: irq request failed\n", __func__); | ||
| 1701 | return ret; | ||
| 1702 | } | ||
| 1703 | } else { | ||
| 1704 | indio_dev->channels = chip->chip_info->channel_without_events; | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | tsl2772_defaults(chip); | ||
| 1708 | ret = tsl2772_chip_on(indio_dev); | ||
| 1709 | if (ret < 0) | ||
| 1710 | return ret; | ||
| 1711 | |||
| 1712 | ret = iio_device_register(indio_dev); | ||
| 1713 | if (ret) { | ||
| 1714 | tsl2772_chip_off(indio_dev); | ||
| 1715 | dev_err(&clientp->dev, | ||
| 1716 | "%s: iio registration failed\n", __func__); | ||
| 1717 | return ret; | ||
| 1718 | } | ||
| 1719 | |||
| 1720 | return 0; | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | static int tsl2772_suspend(struct device *dev) | ||
| 1724 | { | ||
| 1725 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 1726 | |||
| 1727 | return tsl2772_chip_off(indio_dev); | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | static int tsl2772_resume(struct device *dev) | ||
| 1731 | { | ||
| 1732 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 1733 | |||
| 1734 | return tsl2772_chip_on(indio_dev); | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | static int tsl2772_remove(struct i2c_client *client) | ||
| 1738 | { | ||
| 1739 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
| 1740 | |||
| 1741 | tsl2772_chip_off(indio_dev); | ||
| 1742 | |||
| 1743 | iio_device_unregister(indio_dev); | ||
| 1744 | |||
| 1745 | return 0; | ||
| 1746 | } | ||
| 1747 | |||
| 1748 | static const struct i2c_device_id tsl2772_idtable[] = { | ||
| 1749 | { "tsl2571", tsl2571 }, | ||
| 1750 | { "tsl2671", tsl2671 }, | ||
| 1751 | { "tmd2671", tmd2671 }, | ||
| 1752 | { "tsl2771", tsl2771 }, | ||
| 1753 | { "tmd2771", tmd2771 }, | ||
| 1754 | { "tsl2572", tsl2572 }, | ||
| 1755 | { "tsl2672", tsl2672 }, | ||
| 1756 | { "tmd2672", tmd2672 }, | ||
| 1757 | { "tsl2772", tsl2772 }, | ||
| 1758 | { "tmd2772", tmd2772 }, | ||
| 1759 | {} | ||
| 1760 | }; | ||
| 1761 | |||
| 1762 | MODULE_DEVICE_TABLE(i2c, tsl2772_idtable); | ||
| 1763 | |||
| 1764 | static const struct of_device_id tsl2772_of_match[] = { | ||
| 1765 | { .compatible = "amstaos,tsl2571" }, | ||
| 1766 | { .compatible = "amstaos,tsl2671" }, | ||
| 1767 | { .compatible = "amstaos,tmd2671" }, | ||
| 1768 | { .compatible = "amstaos,tsl2771" }, | ||
| 1769 | { .compatible = "amstaos,tmd2771" }, | ||
| 1770 | { .compatible = "amstaos,tsl2572" }, | ||
| 1771 | { .compatible = "amstaos,tsl2672" }, | ||
| 1772 | { .compatible = "amstaos,tmd2672" }, | ||
| 1773 | { .compatible = "amstaos,tsl2772" }, | ||
| 1774 | { .compatible = "amstaos,tmd2772" }, | ||
| 1775 | {} | ||
| 1776 | }; | ||
| 1777 | MODULE_DEVICE_TABLE(of, tsl2772_of_match); | ||
| 1778 | |||
| 1779 | static const struct dev_pm_ops tsl2772_pm_ops = { | ||
| 1780 | .suspend = tsl2772_suspend, | ||
| 1781 | .resume = tsl2772_resume, | ||
| 1782 | }; | ||
| 1783 | |||
| 1784 | static struct i2c_driver tsl2772_driver = { | ||
| 1785 | .driver = { | ||
| 1786 | .name = "tsl2772", | ||
| 1787 | .of_match_table = tsl2772_of_match, | ||
| 1788 | .pm = &tsl2772_pm_ops, | ||
| 1789 | }, | ||
| 1790 | .id_table = tsl2772_idtable, | ||
| 1791 | .probe = tsl2772_probe, | ||
| 1792 | .remove = tsl2772_remove, | ||
| 1793 | }; | ||
| 1794 | |||
| 1795 | module_i2c_driver(tsl2772_driver); | ||
| 1796 | |||
| 1797 | MODULE_AUTHOR("J. August Brenner <Jon.Brenner@ams.com>"); | ||
| 1798 | MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>"); | ||
| 1799 | MODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver"); | ||
| 1800 | MODULE_LICENSE("GPL"); | ||
| @@ -26,6 +26,7 @@ · 25 unmodified lines | |||
| 26 | #define MAG3110_OUT_Y 0x03 | 26 | #define MAG3110_OUT_Y 0x03 |
| 27 | #define MAG3110_OUT_Z 0x05 | 27 | #define MAG3110_OUT_Z 0x05 |
| 28 | #define MAG3110_WHO_AM_I 0x07 | 28 | #define MAG3110_WHO_AM_I 0x07 |
| 29 | #define MAG3110_SYSMOD 0x08 | ||
| 29 | #define MAG3110_OFF_X 0x09 /* MSB first */ | 30 | #define MAG3110_OFF_X 0x09 /* MSB first */ |
| 30 | #define MAG3110_OFF_Y 0x0b | 31 | #define MAG3110_OFF_Y 0x0b |
| 31 | #define MAG3110_OFF_Z 0x0d | 32 | #define MAG3110_OFF_Z 0x0d |
| @@ -39,6 +40,8 @@ · 7 unmodified lines | |||
| 39 | #define MAG3110_CTRL_DR_SHIFT 5 | 40 | #define MAG3110_CTRL_DR_SHIFT 5 |
| 40 | #define MAG3110_CTRL_DR_DEFAULT 0 | 41 | #define MAG3110_CTRL_DR_DEFAULT 0 |
| 41 | 42 | ||
| 43 | #define MAG3110_SYSMOD_MODE_MASK GENMASK(1, 0) | ||
| 44 | |||
| 42 | #define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */ | 45 | #define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */ |
| 43 | #define MAG3110_CTRL_AC BIT(0) /* continuous measurements */ | 46 | #define MAG3110_CTRL_AC BIT(0) /* continuous measurements */ |
| 44 | 47 | ||
| @@ -52,17 +55,20 @@ struct mag3110_data { · 7 unmodified lines | |||
| 52 | struct i2c_client *client; | 55 | struct i2c_client *client; |
| 53 | struct mutex lock; | 56 | struct mutex lock; |
| 54 | u8 ctrl_reg1; | 57 | u8 ctrl_reg1; |
| 58 | int sleep_val; | ||
| 55 | }; | 59 | }; |
| 56 | 60 | ||
| 57 | static int mag3110_request(struct mag3110_data *data) | 61 | static int mag3110_request(struct mag3110_data *data) |
| 58 | { | 62 | { |
| 59 | int ret, tries = 150; | 63 | int ret, tries = 150; |
| 60 | 64 | ||
| 61 | /* trigger measurement */ | 65 | if ((data->ctrl_reg1 & MAG3110_CTRL_AC) == 0) { |
| 62 | ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, | 66 | /* trigger measurement */ |
| 63 | data->ctrl_reg1 | MAG3110_CTRL_TM); | 67 | ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, |
| 64 | if (ret < 0) | 68 | data->ctrl_reg1 | MAG3110_CTRL_TM); |
| 65 | return ret; | 69 | if (ret < 0) |
| 70 | return ret; | ||
| 71 | } | ||
| 66 | 72 | ||
| 67 | while (tries-- > 0) { | 73 | while (tries-- > 0) { |
| 68 | ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS); | 74 | ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS); |
| @@ -71,7 +77,11 @@ static int mag3110_request(struct mag3110_data *data) · 2 unmodified lines | |||
| 71 | /* wait for data ready */ | 77 | /* wait for data ready */ |
| 72 | if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY) | 78 | if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY) |
| 73 | break; | 79 | break; |
| 74 | msleep(20); | 80 | |
| 81 | if (data->sleep_val <= 20) | ||
| 82 | usleep_range(data->sleep_val * 250, data->sleep_val * 500); | ||
| 83 | else | ||
| 84 | msleep(20); | ||
| 75 | } | 85 | } |
| 76 | 86 | ||
| 77 | if (tries < 0) { | 87 | if (tries < 0) { |
| @@ -144,6 +154,117 @@ static int mag3110_get_samp_freq_index(struct mag3110_data *data, · 66 unmodified lines | |||
| 144 | val2); | 154 | val2); |
| 145 | } | 155 | } |
| 146 | 156 | ||
| 157 | static int mag3110_calculate_sleep(struct mag3110_data *data) | ||
| 158 | { | ||
| 159 | int ret, i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT; | ||
| 160 | |||
| 161 | if (mag3110_samp_freq[i][0] > 0) | ||
| 162 | ret = 1000 / mag3110_samp_freq[i][0]; | ||
| 163 | else | ||
| 164 | ret = 1000; | ||
| 165 | |||
| 166 | return ret == 0 ? 1 : ret; | ||
| 167 | } | ||
| 168 | |||
| 169 | static int mag3110_standby(struct mag3110_data *data) | ||
| 170 | { | ||
| 171 | return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, | ||
| 172 | data->ctrl_reg1 & ~MAG3110_CTRL_AC); | ||
| 173 | } | ||
| 174 | |||
| 175 | static int mag3110_wait_standby(struct mag3110_data *data) | ||
| 176 | { | ||
| 177 | int ret, tries = 30; | ||
| 178 | |||
| 179 | /* | ||
| 180 | * Takes up to 1/ODR to come out of active mode into stby | ||
| 181 | * Longest expected period is 12.5seconds. | ||
| 182 | * We'll sleep for 500ms between checks | ||
| 183 | */ | ||
| 184 | while (tries-- > 0) { | ||
| 185 | ret = i2c_smbus_read_byte_data(data->client, MAG3110_SYSMOD); | ||
| 186 | if (ret < 0) { | ||
| 187 | dev_err(&data->client->dev, "i2c error\n"); | ||
| 188 | return ret; | ||
| 189 | } | ||
| 190 | /* wait for standby */ | ||
| 191 | if ((ret & MAG3110_SYSMOD_MODE_MASK) == 0) | ||
| 192 | break; | ||
| 193 | |||
| 194 | msleep_interruptible(500); | ||
| 195 | } | ||
| 196 | |||
| 197 | if (tries < 0) { | ||
| 198 | dev_err(&data->client->dev, "device not entering standby mode\n"); | ||
| 199 | return -EIO; | ||
| 200 | } | ||
| 201 | |||
| 202 | return 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | static int mag3110_active(struct mag3110_data *data) | ||
| 206 | { | ||
| 207 | return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, | ||
| 208 | data->ctrl_reg1); | ||
| 209 | } | ||
| 210 | |||
| 211 | /* returns >0 if active, 0 if in standby and <0 on error */ | ||
| 212 | static int mag3110_is_active(struct mag3110_data *data) | ||
| 213 | { | ||
| 214 | int reg; | ||
| 215 | |||
| 216 | reg = i2c_smbus_read_byte_data(data->client, MAG3110_CTRL_REG1); | ||
| 217 | if (reg < 0) | ||
| 218 | return reg; | ||
| 219 | |||
| 220 | return reg & MAG3110_CTRL_AC; | ||
| 221 | } | ||
| 222 | |||
| 223 | static int mag3110_change_config(struct mag3110_data *data, u8 reg, u8 val) | ||
| 224 | { | ||
| 225 | int ret; | ||
| 226 | int is_active; | ||
| 227 | |||
| 228 | mutex_lock(&data->lock); | ||
| 229 | |||
| 230 | is_active = mag3110_is_active(data); | ||
| 231 | if (is_active < 0) { | ||
| 232 | ret = is_active; | ||
| 233 | goto fail; | ||
| 234 | } | ||
| 235 | |||
| 236 | /* config can only be changed when in standby */ | ||
| 237 | if (is_active > 0) { | ||
| 238 | ret = mag3110_standby(data); | ||
| 239 | if (ret < 0) | ||
| 240 | goto fail; | ||
| 241 | } | ||
| 242 | |||
| 243 | /* | ||
| 244 | * After coming out of active we must wait for the part | ||
| 245 | * to transition to STBY. This can take up to 1 /ODR to occur | ||
| 246 | */ | ||
| 247 | ret = mag3110_wait_standby(data); | ||
| 248 | if (ret < 0) | ||
| 249 | goto fail; | ||
| 250 | |||
| 251 | ret = i2c_smbus_write_byte_data(data->client, reg, val); | ||
| 252 | if (ret < 0) | ||
| 253 | goto fail; | ||
| 254 | |||
| 255 | if (is_active > 0) { | ||
| 256 | ret = mag3110_active(data); | ||
| 257 | if (ret < 0) | ||
| 258 | goto fail; | ||
| 259 | } | ||
| 260 | |||
| 261 | ret = 0; | ||
| 262 | fail: | ||
| 263 | mutex_unlock(&data->lock); | ||
| 264 | |||
| 265 | return ret; | ||
| 266 | } | ||
| 267 | |||
| 147 | static int mag3110_read_raw(struct iio_dev *indio_dev, | 268 | static int mag3110_read_raw(struct iio_dev *indio_dev, |
| 148 | struct iio_chan_spec const *chan, | 269 | struct iio_chan_spec const *chan, |
| 149 | int *val, int *val2, long mask) | 270 | int *val, int *val2, long mask) |
| @@ -235,11 +356,15 @@ static int mag3110_write_raw(struct iio_dev *indio_dev, · 85 unmodified lines | |||
| 235 | ret = -EINVAL; | 356 | ret = -EINVAL; |
| 236 | break; | 357 | break; |
| 237 | } | 358 | } |
| 238 | 359 | data->ctrl_reg1 &= 0xff & ~MAG3110_CTRL_DR_MASK | |
| 239 | data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK; | 360 | & ~MAG3110_CTRL_AC; |
| 240 | data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; | 361 | data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; |
| 241 | ret = i2c_smbus_write_byte_data(data->client, | 362 | data->sleep_val = mag3110_calculate_sleep(data); |
| 242 | MAG3110_CTRL_REG1, data->ctrl_reg1); | 363 | if (data->sleep_val < 40) |
| 364 | data->ctrl_reg1 |= MAG3110_CTRL_AC; | ||
| 365 | |||
| 366 | ret = mag3110_change_config(data, MAG3110_CTRL_REG1, | ||
| 367 | data->ctrl_reg1); | ||
| 243 | break; | 368 | break; |
| 244 | case IIO_CHAN_INFO_CALIBBIAS: | 369 | case IIO_CHAN_INFO_CALIBBIAS: |
| 245 | if (val < -10000 || val > 10000) { | 370 | if (val < -10000 || val > 10000) { |
| @@ -337,12 +462,6 @@ static const struct iio_info mag3110_info = { · 91 unmodified lines | |||
| 337 | 462 | ||
| 338 | static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0}; | 463 | static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0}; |
| 339 | 464 | ||
| 340 | static int mag3110_standby(struct mag3110_data *data) | ||
| 341 | { | ||
| 342 | return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, | ||
| 343 | data->ctrl_reg1 & ~MAG3110_CTRL_AC); | ||
| 344 | } | ||
| 345 | |||
| 346 | static int mag3110_probe(struct i2c_client *client, | 465 | static int mag3110_probe(struct i2c_client *client, |
| 347 | const struct i2c_device_id *id) | 466 | const struct i2c_device_id *id) |
| 348 | { | 467 | { |
| @@ -374,8 +493,11 @@ static int mag3110_probe(struct i2c_client *client, · 25 unmodified lines | |||
| 374 | indio_dev->available_scan_masks = mag3110_scan_masks; | 493 | indio_dev->available_scan_masks = mag3110_scan_masks; |
| 375 | 494 | ||
| 376 | data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT; | 495 | data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT; |
| 377 | ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG1, | 496 | data->sleep_val = mag3110_calculate_sleep(data); |
| 378 | data->ctrl_reg1); | 497 | if (data->sleep_val < 40) |
| 498 | data->ctrl_reg1 |= MAG3110_CTRL_AC; | ||
| 499 | |||
| 500 | ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1); | ||
| 379 | if (ret < 0) | 501 | if (ret < 0) |
| 380 | return ret; | 502 | return ret; |
| 381 | 503 | ||
| @@ -99,6 +99,23 @@ static const struct iio_info mcp4018_info = { · 98 unmodified lines | |||
| 99 | .write_raw = mcp4018_write_raw, | 99 | .write_raw = mcp4018_write_raw, |
| 100 | }; | 100 | }; |
| 101 | 101 | ||
| 102 | static const struct i2c_device_id mcp4018_id[] = { | ||
| 103 | { "mcp4017-502", MCP4018_502 }, | ||
| 104 | { "mcp4017-103", MCP4018_103 }, | ||
| 105 | { "mcp4017-503", MCP4018_503 }, | ||
| 106 | { "mcp4017-104", MCP4018_104 }, | ||
| 107 | { "mcp4018-502", MCP4018_502 }, | ||
| 108 | { "mcp4018-103", MCP4018_103 }, | ||
| 109 | { "mcp4018-503", MCP4018_503 }, | ||
| 110 | { "mcp4018-104", MCP4018_104 }, | ||
| 111 | { "mcp4019-502", MCP4018_502 }, | ||
| 112 | { "mcp4019-103", MCP4018_103 }, | ||
| 113 | { "mcp4019-503", MCP4018_503 }, | ||
| 114 | { "mcp4019-104", MCP4018_104 }, | ||
| 115 | {} | ||
| 116 | }; | ||
| 117 | MODULE_DEVICE_TABLE(i2c, mcp4018_id); | ||
| 118 | |||
| 102 | #ifdef CONFIG_OF | 119 | #ifdef CONFIG_OF |
| 103 | 120 | ||
| 104 | #define MCP4018_COMPATIBLE(of_compatible, cfg) { \ | 121 | #define MCP4018_COMPATIBLE(of_compatible, cfg) { \ |
| @@ -125,8 +142,7 @@ MODULE_DEVICE_TABLE(of, mcp4018_of_match); · 20 unmodified lines | |||
| 125 | 142 | ||
| 126 | #endif | 143 | #endif |
| 127 | 144 | ||
| 128 | static int mcp4018_probe(struct i2c_client *client, | 145 | static int mcp4018_probe(struct i2c_client *client) |
| 129 | const struct i2c_device_id *id) | ||
| 130 | { | 146 | { |
| 131 | struct device *dev = &client->dev; | 147 | struct device *dev = &client->dev; |
| 132 | struct mcp4018_data *data; | 148 | struct mcp4018_data *data; |
| @@ -150,7 +166,7 @@ static int mcp4018_probe(struct i2c_client *client, · 17 unmodified lines | |||
| 150 | if (match) | 166 | if (match) |
| 151 | data->cfg = of_device_get_match_data(dev); | 167 | data->cfg = of_device_get_match_data(dev); |
| 152 | else | 168 | else |
| 153 | data->cfg = &mcp4018_cfg[id->driver_data]; | 169 | data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data]; |
| 154 | 170 | ||
| 155 | indio_dev->dev.parent = dev; | 171 | indio_dev->dev.parent = dev; |
| 156 | indio_dev->info = &mcp4018_info; | 172 | indio_dev->info = &mcp4018_info; |
| @@ -161,29 +177,12 @@ static int mcp4018_probe(struct i2c_client *client, · 4 unmodified lines | |||
| 161 | return devm_iio_device_register(dev, indio_dev); | 177 | return devm_iio_device_register(dev, indio_dev); |
| 162 | } | 178 | } |
| 163 | 179 | ||
| 164 | static const struct i2c_device_id mcp4018_id[] = { | ||
| 165 | { "mcp4017-502", MCP4018_502 }, | ||
| 166 | { "mcp4017-103", MCP4018_103 }, | ||
| 167 | { "mcp4017-503", MCP4018_503 }, | ||
| 168 | { "mcp4017-104", MCP4018_104 }, | ||
| 169 | { "mcp4018-502", MCP4018_502 }, | ||
| 170 | { "mcp4018-103", MCP4018_103 }, | ||
| 171 | { "mcp4018-503", MCP4018_503 }, | ||
| 172 | { "mcp4018-104", MCP4018_104 }, | ||
| 173 | { "mcp4019-502", MCP4018_502 }, | ||
| 174 | { "mcp4019-103", MCP4018_103 }, | ||
| 175 | { "mcp4019-503", MCP4018_503 }, | ||
| 176 | { "mcp4019-104", MCP4018_104 }, | ||
| 177 | {} | ||
| 178 | }; | ||
| 179 | MODULE_DEVICE_TABLE(i2c, mcp4018_id); | ||
| 180 | |||
| 181 | static struct i2c_driver mcp4018_driver = { | 180 | static struct i2c_driver mcp4018_driver = { |
| 182 | .driver = { | 181 | .driver = { |
| 183 | .name = "mcp4018", | 182 | .name = "mcp4018", |
| 184 | .of_match_table = of_match_ptr(mcp4018_of_match), | 183 | .of_match_table = of_match_ptr(mcp4018_of_match), |
| 185 | }, | 184 | }, |
| 186 | .probe = mcp4018_probe, | 185 | .probe_new = mcp4018_probe, |
| 187 | .id_table = mcp4018_id, | 186 | .id_table = mcp4018_id, |
| 188 | }; | 187 | }; |
| 189 | 188 | ||
| @@ -209,6 +209,75 @@ static const struct iio_info mcp4531_info = { · 208 unmodified lines | |||
| 209 | .write_raw = mcp4531_write_raw, | 209 | .write_raw = mcp4531_write_raw, |
| 210 | }; | 210 | }; |
| 211 | 211 | ||
| 212 | static const struct i2c_device_id mcp4531_id[] = { | ||
| 213 | { "mcp4531-502", MCP453x_502 }, | ||
| 214 | { "mcp4531-103", MCP453x_103 }, | ||
| 215 | { "mcp4531-503", MCP453x_503 }, | ||
| 216 | { "mcp4531-104", MCP453x_104 }, | ||
| 217 | { "mcp4532-502", MCP453x_502 }, | ||
| 218 | { "mcp4532-103", MCP453x_103 }, | ||
| 219 | { "mcp4532-503", MCP453x_503 }, | ||
| 220 | { "mcp4532-104", MCP453x_104 }, | ||
| 221 | { "mcp4541-502", MCP454x_502 }, | ||
| 222 | { "mcp4541-103", MCP454x_103 }, | ||
| 223 | { "mcp4541-503", MCP454x_503 }, | ||
| 224 | { "mcp4541-104", MCP454x_104 }, | ||
| 225 | { "mcp4542-502", MCP454x_502 }, | ||
| 226 | { "mcp4542-103", MCP454x_103 }, | ||
| 227 | { "mcp4542-503", MCP454x_503 }, | ||
| 228 | { "mcp4542-104", MCP454x_104 }, | ||
| 229 | { "mcp4551-502", MCP455x_502 }, | ||
| 230 | { "mcp4551-103", MCP455x_103 }, | ||
| 231 | { "mcp4551-503", MCP455x_503 }, | ||
| 232 | { "mcp4551-104", MCP455x_104 }, | ||
| 233 | { "mcp4552-502", MCP455x_502 }, | ||
| 234 | { "mcp4552-103", MCP455x_103 }, | ||
| 235 | { "mcp4552-503", MCP455x_503 }, | ||
| 236 | { "mcp4552-104", MCP455x_104 }, | ||
| 237 | { "mcp4561-502", MCP456x_502 }, | ||
| 238 | { "mcp4561-103", MCP456x_103 }, | ||
| 239 | { "mcp4561-503", MCP456x_503 }, | ||
| 240 | { "mcp4561-104", MCP456x_104 }, | ||
| 241 | { "mcp4562-502", MCP456x_502 }, | ||
| 242 | { "mcp4562-103", MCP456x_103 }, | ||
| 243 | { "mcp4562-503", MCP456x_503 }, | ||
| 244 | { "mcp4562-104", MCP456x_104 }, | ||
| 245 | { "mcp4631-502", MCP463x_502 }, | ||
| 246 | { "mcp4631-103", MCP463x_103 }, | ||
| 247 | { "mcp4631-503", MCP463x_503 }, | ||
| 248 | { "mcp4631-104", MCP463x_104 }, | ||
| 249 | { "mcp4632-502", MCP463x_502 }, | ||
| 250 | { "mcp4632-103", MCP463x_103 }, | ||
| 251 | { "mcp4632-503", MCP463x_503 }, | ||
| 252 | { "mcp4632-104", MCP463x_104 }, | ||
| 253 | { "mcp4641-502", MCP464x_502 }, | ||
| 254 | { "mcp4641-103", MCP464x_103 }, | ||
| 255 | { "mcp4641-503", MCP464x_503 }, | ||
| 256 | { "mcp4641-104", MCP464x_104 }, | ||
| 257 | { "mcp4642-502", MCP464x_502 }, | ||
| 258 | { "mcp4642-103", MCP464x_103 }, | ||
| 259 | { "mcp4642-503", MCP464x_503 }, | ||
| 260 | { "mcp4642-104", MCP464x_104 }, | ||
| 261 | { "mcp4651-502", MCP465x_502 }, | ||
| 262 | { "mcp4651-103", MCP465x_103 }, | ||
| 263 | { "mcp4651-503", MCP465x_503 }, | ||
| 264 | { "mcp4651-104", MCP465x_104 }, | ||
| 265 | { "mcp4652-502", MCP465x_502 }, | ||
| 266 | { "mcp4652-103", MCP465x_103 }, | ||
| 267 | { "mcp4652-503", MCP465x_503 }, | ||
| 268 | { "mcp4652-104", MCP465x_104 }, | ||
| 269 | { "mcp4661-502", MCP466x_502 }, | ||
| 270 | { "mcp4661-103", MCP466x_103 }, | ||
| 271 | { "mcp4661-503", MCP466x_503 }, | ||
| 272 | { "mcp4661-104", MCP466x_104 }, | ||
| 273 | { "mcp4662-502", MCP466x_502 }, | ||
| 274 | { "mcp4662-103", MCP466x_103 }, | ||
| 275 | { "mcp4662-503", MCP466x_503 }, | ||
| 276 | { "mcp4662-104", MCP466x_104 }, | ||
| 277 | {} | ||
| 278 | }; | ||
| 279 | MODULE_DEVICE_TABLE(i2c, mcp4531_id); | ||
| 280 | |||
| 212 | #ifdef CONFIG_OF | 281 | #ifdef CONFIG_OF |
| 213 | 282 | ||
| 214 | #define MCP4531_COMPATIBLE(of_compatible, cfg) { \ | 283 | #define MCP4531_COMPATIBLE(of_compatible, cfg) { \ |
| @@ -286,8 +355,7 @@ static const struct of_device_id mcp4531_of_match[] = { · 71 unmodified lines | |||
| 286 | MODULE_DEVICE_TABLE(of, mcp4531_of_match); | 355 | MODULE_DEVICE_TABLE(of, mcp4531_of_match); |
| 287 | #endif | 356 | #endif |
| 288 | 357 | ||
| 289 | static int mcp4531_probe(struct i2c_client *client, | 358 | static int mcp4531_probe(struct i2c_client *client) |
| 290 | const struct i2c_device_id *id) | ||
| 291 | { | 359 | { |
| 292 | struct device *dev = &client->dev; | 360 | struct device *dev = &client->dev; |
| 293 | struct mcp4531_data *data; | 361 | struct mcp4531_data *data; |
| @@ -311,7 +379,7 @@ static int mcp4531_probe(struct i2c_client *client, · 17 unmodified lines | |||
| 311 | if (match) | 379 | if (match) |
| 312 | data->cfg = of_device_get_match_data(dev); | 380 | data->cfg = of_device_get_match_data(dev); |
| 313 | else | 381 | else |
| 314 | data->cfg = &mcp4531_cfg[id->driver_data]; | 382 | data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data]; |
| 315 | 383 | ||
| 316 | indio_dev->dev.parent = dev; | 384 | indio_dev->dev.parent = dev; |
| 317 | indio_dev->info = &mcp4531_info; | 385 | indio_dev->info = &mcp4531_info; |
| @@ -322,81 +390,12 @@ static int mcp4531_probe(struct i2c_client *client, · 4 unmodified lines | |||
| 322 | return devm_iio_device_register(dev, indio_dev); | 390 | return devm_iio_device_register(dev, indio_dev); |
| 323 | } | 391 | } |
| 324 | 392 | ||
| 325 | static const struct i2c_device_id mcp4531_id[] = { | ||
| 326 | { "mcp4531-502", MCP453x_502 }, | ||
| 327 | { "mcp4531-103", MCP453x_103 }, | ||
| 328 | { "mcp4531-503", MCP453x_503 }, | ||
| 329 | { "mcp4531-104", MCP453x_104 }, | ||
| 330 | { "mcp4532-502", MCP453x_502 }, | ||
| 331 | { "mcp4532-103", MCP453x_103 }, | ||
| 332 | { "mcp4532-503", MCP453x_503 }, | ||
| 333 | { "mcp4532-104", MCP453x_104 }, | ||
| 334 | { "mcp4541-502", MCP454x_502 }, | ||
| 335 | { "mcp4541-103", MCP454x_103 }, | ||
| 336 | { "mcp4541-503", MCP454x_503 }, | ||
| 337 | { "mcp4541-104", MCP454x_104 }, | ||
| 338 | { "mcp4542-502", MCP454x_502 }, | ||
| 339 | { "mcp4542-103", MCP454x_103 }, | ||
| 340 | { "mcp4542-503", MCP454x_503 }, | ||
| 341 | { "mcp4542-104", MCP454x_104 }, | ||
| 342 | { "mcp4551-502", MCP455x_502 }, | ||
| 343 | { "mcp4551-103", MCP455x_103 }, | ||
| 344 | { "mcp4551-503", MCP455x_503 }, | ||
| 345 | { "mcp4551-104", MCP455x_104 }, | ||
| 346 | { "mcp4552-502", MCP455x_502 }, | ||
| 347 | { "mcp4552-103", MCP455x_103 }, | ||
| 348 | { "mcp4552-503", MCP455x_503 }, | ||
| 349 | { "mcp4552-104", MCP455x_104 }, | ||
| 350 | { "mcp4561-502", MCP456x_502 }, | ||
| 351 | { "mcp4561-103", MCP456x_103 }, | ||
| 352 | { "mcp4561-503", MCP456x_503 }, | ||
| 353 | { "mcp4561-104", MCP456x_104 }, | ||
| 354 | { "mcp4562-502", MCP456x_502 }, | ||
| 355 | { "mcp4562-103", MCP456x_103 }, | ||
| 356 | { "mcp4562-503", MCP456x_503 }, | ||
| 357 | { "mcp4562-104", MCP456x_104 }, | ||
| 358 | { "mcp4631-502", MCP463x_502 }, | ||
| 359 | { "mcp4631-103", MCP463x_103 }, | ||
| 360 | { "mcp4631-503", MCP463x_503 }, | ||
| 361 | { "mcp4631-104", MCP463x_104 }, | ||
| 362 | { "mcp4632-502", MCP463x_502 }, | ||
| 363 | { "mcp4632-103", MCP463x_103 }, | ||
| 364 | { "mcp4632-503", MCP463x_503 }, | ||
| 365 | { "mcp4632-104", MCP463x_104 }, | ||
| 366 | { "mcp4641-502", MCP464x_502 }, | ||
| 367 | { "mcp4641-103", MCP464x_103 }, | ||
| 368 | { "mcp4641-503", MCP464x_503 }, | ||
| 369 | { "mcp4641-104", MCP464x_104 }, | ||
| 370 | { "mcp4642-502", MCP464x_502 }, | ||
| 371 | { "mcp4642-103", MCP464x_103 }, | ||
| 372 | { "mcp4642-503", MCP464x_503 }, | ||
| 373 | { "mcp4642-104", MCP464x_104 }, | ||
| 374 | { "mcp4651-502", MCP465x_502 }, | ||
| 375 | { "mcp4651-103", MCP465x_103 }, | ||
| 376 | { "mcp4651-503", MCP465x_503 }, | ||
| 377 | { "mcp4651-104", MCP465x_104 }, | ||
| 378 | { "mcp4652-502", MCP465x_502 }, | ||
| 379 | { "mcp4652-103", MCP465x_103 }, | ||
| 380 | { "mcp4652-503", MCP465x_503 }, | ||
| 381 | { "mcp4652-104", MCP465x_104 }, | ||
| 382 | { "mcp4661-502", MCP466x_502 }, | ||
| 383 | { "mcp4661-103", MCP466x_103 }, | ||
| 384 | { "mcp4661-503", MCP466x_503 }, | ||
| 385 | { "mcp4661-104", MCP466x_104 }, | ||
| 386 | { "mcp4662-502", MCP466x_502 }, | ||
| 387 | { "mcp4662-103", MCP466x_103 }, | ||
| 388 | { "mcp4662-503", MCP466x_503 }, | ||
| 389 | { "mcp4662-104", MCP466x_104 }, | ||
| 390 | {} | ||
| 391 | }; | ||
| 392 | MODULE_DEVICE_TABLE(i2c, mcp4531_id); | ||
| 393 | |||
| 394 | static struct i2c_driver mcp4531_driver = { | 393 | static struct i2c_driver mcp4531_driver = { |
| 395 | .driver = { | 394 | .driver = { |
| 396 | .name = "mcp4531", | 395 | .name = "mcp4531", |
| 397 | .of_match_table = of_match_ptr(mcp4531_of_match), | 396 | .of_match_table = of_match_ptr(mcp4531_of_match), |
| 398 | }, | 397 | }, |
| 399 | .probe = mcp4531_probe, | 398 | .probe_new = mcp4531_probe, |
| 400 | .id_table = mcp4531_id, | 399 | .id_table = mcp4531_id, |
| 401 | }; | 400 | }; |
| 402 | 401 | ||
| @@ -411,12 +411,14 @@ static int lmp91000_remove(struct i2c_client *client) · 410 unmodified lines | |||
| 411 | 411 | ||
| 412 | static const struct of_device_id lmp91000_of_match[] = { | 412 | static const struct of_device_id lmp91000_of_match[] = { |
| 413 | { .compatible = "ti,lmp91000", }, | 413 | { .compatible = "ti,lmp91000", }, |
| 414 | { .compatible = "ti,lmp91002", }, | ||
| 414 | { }, | 415 | { }, |
| 415 | }; | 416 | }; |
| 416 | MODULE_DEVICE_TABLE(of, lmp91000_of_match); | 417 | MODULE_DEVICE_TABLE(of, lmp91000_of_match); |
| 417 | 418 | ||
| 418 | static const struct i2c_device_id lmp91000_id[] = { | 419 | static const struct i2c_device_id lmp91000_id[] = { |
| 419 | { "lmp91000", 0 }, | 420 | { "lmp91000", 0 }, |
| 421 | { "lmp91002", 0 }, | ||
| 420 | {} | 422 | {} |
| 421 | }; | 423 | }; |
| 422 | MODULE_DEVICE_TABLE(i2c, lmp91000_id); | 424 | MODULE_DEVICE_TABLE(i2c, lmp91000_id); |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | |||
| 16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
| 17 | #include <linux/device.h> | 17 | #include <linux/device.h> |
| 18 | #include <linux/iio/buffer.h> | 18 | #include <linux/iio/buffer.h> |
| 19 | #include <linux/iio/common/cros_ec_sensors_core.h> | ||
| 19 | #include <linux/iio/iio.h> | 20 | #include <linux/iio/iio.h> |
| 20 | #include <linux/iio/kfifo_buf.h> | 21 | #include <linux/iio/kfifo_buf.h> |
| 21 | #include <linux/iio/trigger.h> | 22 | #include <linux/iio/trigger.h> |
| @@ -28,8 +29,6 @@ · 6 unmodified lines | |||
| 28 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
| 29 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
| 30 | 31 | ||
| 31 | #include "../common/cros_ec_sensors/cros_ec_sensors_core.h" | ||
| 32 | |||
| 33 | /* | 32 | /* |
| 34 | * One channel for pressure, the other for timestamp. | 33 | * One channel for pressure, the other for timestamp. |
| 35 | */ | 34 | */ |
| @@ -0,0 +1,17 @@ | |||
| 1 | # | ||
| 2 | # Resolver/Synchro drivers | ||
| 3 | # | ||
| 4 | menu "Resolver to digital converters" | ||
| 5 | |||
| 6 | config AD2S1200 | ||
| 7 | tristate "Analog Devices ad2s1200/ad2s1205 driver" | ||
| 8 | depends on SPI | ||
| 9 | depends on GPIOLIB || COMPILE_TEST | ||
| 10 | help | ||
| 11 | Say yes here to build support for Analog Devices spi resolver | ||
| 12 | to digital converters, ad2s1200 and ad2s1205, provides direct access | ||
| 13 | via sysfs. | ||
| 14 | |||
| 15 | To compile this driver as a module, choose M here: the | ||
| 16 | module will be called ad2s1200. | ||
| 17 | endmenu | ||
| @@ -0,0 +1,5 @@ | |||
| 1 | # | ||
| 2 | # Makefile for Resolver/Synchro drivers | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_AD2S1200) += ad2s1200.o | ||
| @@ -2,43 +2,49 @@ · 1 unmodified line | |||
| 2 | * ad2s1200.c simple support for the ADI Resolver to Digital Converters: | 2 | * ad2s1200.c simple support for the ADI Resolver to Digital Converters: |
| 3 | * AD2S1200/1205 | 3 | * AD2S1200/1205 |
| 4 | * | 4 | * |
| 5 | * Copyright (c) 2018-2018 David Veenstra <davidjulianveenstra@gmail.com> | ||
| 5 | * Copyright (c) 2010-2010 Analog Devices Inc. | 6 | * Copyright (c) 2010-2010 Analog Devices Inc. |
| 6 | * | 7 | * |
| 7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
| 9 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
| 10 | * | ||
| 11 | */ | 11 | */ |
| 12 | #include <linux/types.h> | 12 | |
| 13 | #include <linux/mutex.h> | 13 | #include <linux/bitops.h> |
| 14 | #include <linux/device.h> | ||
| 15 | #include <linux/spi/spi.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/sysfs.h> | ||
| 18 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
| 15 | #include <linux/device.h> | ||
| 19 | #include <linux/gpio.h> | 16 | #include <linux/gpio.h> |
| 17 | #include <linux/gpio/consumer.h> | ||
| 20 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 21 | #include <linux/bitops.h> | 19 | #include <linux/mutex.h> |
| 20 | #include <linux/spi/spi.h> | ||
| 21 | #include <linux/sysfs.h> | ||
| 22 | #include <linux/types.h> | ||
| 22 | 23 | ||
| 23 | #include <linux/iio/iio.h> | 24 | #include <linux/iio/iio.h> |
| 24 | #include <linux/iio/sysfs.h> | 25 | #include <linux/iio/sysfs.h> |
| 25 | 26 | ||
| 26 | #define DRV_NAME "ad2s1200" | 27 | #define DRV_NAME "ad2s1200" |
| 27 | 28 | ||
| 28 | /* input pin sample and rdvel is controlled by driver */ | ||
| 29 | #define AD2S1200_PN 2 | ||
| 30 | |||
| 31 | /* input clock on serial interface */ | 29 | /* input clock on serial interface */ |
| 32 | #define AD2S1200_HZ 8192000 | 30 | #define AD2S1200_HZ 8192000 |
| 33 | /* clock period in nano second */ | 31 | /* clock period in nano second */ |
| 34 | #define AD2S1200_TSCLK (1000000000 / AD2S1200_HZ) | 32 | #define AD2S1200_TSCLK (1000000000 / AD2S1200_HZ) |
| 35 | 33 | ||
| 34 | /** | ||
| 35 | * struct ad2s1200_state - driver instance specific data. | ||
| 36 | * @lock: protects both the GPIO pins and the rx buffer. | ||
| 37 | * @sdev: spi device. | ||
| 38 | * @sample: GPIO pin SAMPLE. | ||
| 39 | * @rdvel: GPIO pin RDVEL. | ||
| 40 | * @rx: buffer for spi transfers. | ||
| 41 | */ | ||
| 36 | struct ad2s1200_state { | 42 | struct ad2s1200_state { |
| 37 | struct mutex lock; | 43 | struct mutex lock; |
| 38 | struct spi_device *sdev; | 44 | struct spi_device *sdev; |
| 39 | int sample; | 45 | struct gpio_desc *sample; |
| 40 | int rdvel; | 46 | struct gpio_desc *rdvel; |
| 41 | u8 rx[2] ____cacheline_aligned; | 47 | __be16 rx ____cacheline_aligned; |
| 42 | }; | 48 | }; |
| 43 | 49 | ||
| 44 | static int ad2s1200_read_raw(struct iio_dev *indio_dev, | 50 | static int ad2s1200_read_raw(struct iio_dev *indio_dev, |
| @@ -47,39 +53,63 @@ static int ad2s1200_read_raw(struct iio_dev *indio_dev, · 2 unmodified lines | |||
| 47 | int *val2, | 53 | int *val2, |
| 48 | long m) | 54 | long m) |
| 49 | { | 55 | { |
| 50 | int ret = 0; | ||
| 51 | s16 vel; | ||
| 52 | struct ad2s1200_state *st = iio_priv(indio_dev); | 56 | struct ad2s1200_state *st = iio_priv(indio_dev); |
| 57 | int ret; | ||
| 58 | |||
| 59 | switch (m) { | ||
| 60 | case IIO_CHAN_INFO_SCALE: | ||
| 61 | switch (chan->type) { | ||
| 62 | case IIO_ANGL: | ||
| 63 | /* 2 * Pi / (2^12 - 1) ~= 0.001534355 */ | ||
| 64 | *val = 0; | ||
| 65 | *val2 = 1534355; | ||
| 66 | return IIO_VAL_INT_PLUS_NANO; | ||
| 67 | case IIO_ANGL_VEL: | ||
| 68 | /* 2 * Pi ~= 6.283185 */ | ||
| 69 | *val = 6; | ||
| 70 | *val2 = 283185; | ||
| 71 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 72 | default: | ||
| 73 | return -EINVAL; | ||
| 74 | } | ||
| 75 | break; | ||
| 76 | case IIO_CHAN_INFO_RAW: | ||
| 77 | mutex_lock(&st->lock); | ||
| 78 | gpiod_set_value(st->sample, 0); | ||
| 79 | |||
| 80 | /* delay (6 * AD2S1200_TSCLK + 20) nano seconds */ | ||
| 81 | udelay(1); | ||
| 82 | gpiod_set_value(st->sample, 1); | ||
| 83 | gpiod_set_value(st->rdvel, !!(chan->type == IIO_ANGL)); | ||
| 84 | |||
| 85 | ret = spi_read(st->sdev, &st->rx, 2); | ||
| 86 | if (ret < 0) { | ||
| 87 | mutex_unlock(&st->lock); | ||
| 88 | return ret; | ||
| 89 | } | ||
| 53 | 90 | ||
| 54 | mutex_lock(&st->lock); | 91 | switch (chan->type) { |
| 55 | gpio_set_value(st->sample, 0); | 92 | case IIO_ANGL: |
| 56 | /* delay (6 * AD2S1200_TSCLK + 20) nano seconds */ | 93 | *val = be16_to_cpup(&st->rx) >> 4; |
| 57 | udelay(1); | 94 | break; |
| 58 | gpio_set_value(st->sample, 1); | 95 | case IIO_ANGL_VEL: |
| 59 | gpio_set_value(st->rdvel, !!(chan->type == IIO_ANGL)); | 96 | *val = sign_extend32(be16_to_cpup(&st->rx) >> 4, 11); |
| 60 | ret = spi_read(st->sdev, st->rx, 2); | 97 | break; |
| 61 | if (ret < 0) { | 98 | default: |
| 99 | mutex_unlock(&st->lock); | ||
| 100 | return -EINVAL; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* delay (2 * AD2S1200_TSCLK + 20) ns for sample pulse */ | ||
| 104 | udelay(1); | ||
| 62 | mutex_unlock(&st->lock); | 105 | mutex_unlock(&st->lock); |
| 63 | return ret; | ||
| 64 | } | ||
| 65 | 106 | ||
| 66 | switch (chan->type) { | 107 | return IIO_VAL_INT; |
| 67 | case IIO_ANGL: | ||
| 68 | *val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4); | ||
| 69 | break; | ||
| 70 | case IIO_ANGL_VEL: | ||
| 71 | vel = (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4); | ||
| 72 | vel = sign_extend32(vel, 11); | ||
| 73 | *val = vel; | ||
| 74 | break; | ||
| 75 | default: | 108 | default: |
| 76 | mutex_unlock(&st->lock); | 109 | break; |
| 77 | return -EINVAL; | ||
| 78 | } | 110 | } |
| 79 | /* delay (2 * AD2S1200_TSCLK + 20) ns for sample pulse */ | 111 | |
| 80 | udelay(1); | 112 | return -EINVAL; |
| 81 | mutex_unlock(&st->lock); | ||
| 82 | return IIO_VAL_INT; | ||
| 83 | } | 113 | } |
| 84 | 114 | ||
| 85 | static const struct iio_chan_spec ad2s1200_channels[] = { | 115 | static const struct iio_chan_spec ad2s1200_channels[] = { |
| @@ -88,11 +118,13 @@ static const struct iio_chan_spec ad2s1200_channels[] = { · 2 unmodified lines | |||
| 88 | .indexed = 1, | 118 | .indexed = 1, |
| 89 | .channel = 0, | 119 | .channel = 0, |
| 90 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | 120 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
| 121 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), | ||
| 91 | }, { | 122 | }, { |
| 92 | .type = IIO_ANGL_VEL, | 123 | .type = IIO_ANGL_VEL, |
| 93 | .indexed = 1, | 124 | .indexed = 1, |
| 94 | .channel = 0, | 125 | .channel = 0, |
| 95 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | 126 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
| 127 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), | ||
| 96 | } | 128 | } |
| 97 | }; | 129 | }; |
| 98 | 130 | ||
| @@ -104,27 +136,30 @@ static int ad2s1200_probe(struct spi_device *spi) · 5 unmodified lines | |||
| 104 | { | 136 | { |
| 105 | struct ad2s1200_state *st; | 137 | struct ad2s1200_state *st; |
| 106 | struct iio_dev *indio_dev; | 138 | struct iio_dev *indio_dev; |
| 107 | int pn, ret = 0; | 139 | int ret; |
| 108 | unsigned short *pins = spi->dev.platform_data; | 140 | |
| 109 | |||
| 110 | for (pn = 0; pn < AD2S1200_PN; pn++) { | ||
| 111 | ret = devm_gpio_request_one(&spi->dev, pins[pn], GPIOF_DIR_OUT, | ||
| 112 | DRV_NAME); | ||
| 113 | if (ret) { | ||
| 114 | dev_err(&spi->dev, "request gpio pin %d failed\n", | ||
| 115 | pins[pn]); | ||
| 116 | return ret; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | 141 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
| 120 | if (!indio_dev) | 142 | if (!indio_dev) |
| 121 | return -ENOMEM; | 143 | return -ENOMEM; |
| 144 | |||
| 122 | spi_set_drvdata(spi, indio_dev); | 145 | spi_set_drvdata(spi, indio_dev); |
| 123 | st = iio_priv(indio_dev); | 146 | st = iio_priv(indio_dev); |
| 124 | mutex_init(&st->lock); | 147 | mutex_init(&st->lock); |
| 125 | st->sdev = spi; | 148 | st->sdev = spi; |
| 126 | st->sample = pins[0]; | 149 | |
| 127 | st->rdvel = pins[1]; | 150 | st->sample = devm_gpiod_get(&spi->dev, "adi,sample", GPIOD_OUT_LOW); |
| 151 | if (IS_ERR(st->sample)) { | ||
| 152 | dev_err(&spi->dev, "Failed to claim SAMPLE gpio: err=%ld\n", | ||
| 153 | PTR_ERR(st->sample)); | ||
| 154 | return PTR_ERR(st->sample); | ||
| 155 | } | ||
| 156 | |||
| 157 | st->rdvel = devm_gpiod_get(&spi->dev, "adi,rdvel", GPIOD_OUT_LOW); | ||
| 158 | if (IS_ERR(st->rdvel)) { | ||
| 159 | dev_err(&spi->dev, "Failed to claim RDVEL gpio: err=%ld\n", | ||
| 160 | PTR_ERR(st->rdvel)); | ||
| 161 | return PTR_ERR(st->rdvel); | ||
| 162 | } | ||
| 128 | 163 | ||
| 129 | indio_dev->dev.parent = &spi->dev; | 164 | indio_dev->dev.parent = &spi->dev; |
| 130 | indio_dev->info = &ad2s1200_info; | 165 | indio_dev->info = &ad2s1200_info; |
| @@ -133,17 +168,25 @@ static int ad2s1200_probe(struct spi_device *spi) · 2 unmodified lines | |||
| 133 | indio_dev->num_channels = ARRAY_SIZE(ad2s1200_channels); | 168 | indio_dev->num_channels = ARRAY_SIZE(ad2s1200_channels); |
| 134 | indio_dev->name = spi_get_device_id(spi)->name; | 169 | indio_dev->name = spi_get_device_id(spi)->name; |
| 135 | 170 | ||
| 136 | ret = devm_iio_device_register(&spi->dev, indio_dev); | ||
| 137 | if (ret) | ||
| 138 | return ret; | ||
| 139 | |||
| 140 | spi->max_speed_hz = AD2S1200_HZ; | 171 | spi->max_speed_hz = AD2S1200_HZ; |
| 141 | spi->mode = SPI_MODE_3; | 172 | spi->mode = SPI_MODE_3; |
| 142 | spi_setup(spi); | 173 | ret = spi_setup(spi); |
| 143 | 174 | ||
| 144 | return 0; | 175 | if (ret < 0) { |
| 176 | dev_err(&spi->dev, "spi_setup failed!\n"); | ||
| 177 | return ret; | ||
| 178 | } | ||
| 179 | |||
| 180 | return devm_iio_device_register(&spi->dev, indio_dev); | ||
| 145 | } | 181 | } |
| 146 | 182 | ||
| 183 | static const struct of_device_id ad2s1200_of_match[] = { | ||
| 184 | { .compatible = "adi,ad2s1200", }, | ||
| 185 | { .compatible = "adi,ad2s1205", }, | ||
| 186 | { } | ||
| 187 | }; | ||
| 188 | MODULE_DEVICE_TABLE(of, ad2s1200_of_match); | ||
| 189 | |||
| 147 | static const struct spi_device_id ad2s1200_id[] = { | 190 | static const struct spi_device_id ad2s1200_id[] = { |
| 148 | { "ad2s1200" }, | 191 | { "ad2s1200" }, |
| 149 | { "ad2s1205" }, | 192 | { "ad2s1205" }, |
| @@ -154,12 +197,14 @@ MODULE_DEVICE_TABLE(spi, ad2s1200_id); · 4 unmodified lines | |||
| 154 | static struct spi_driver ad2s1200_driver = { | 197 | static struct spi_driver ad2s1200_driver = { |
| 155 | .driver = { | 198 | .driver = { |
| 156 | .name = DRV_NAME, | 199 | .name = DRV_NAME, |
| 200 | .of_match_table = of_match_ptr(ad2s1200_of_match), | ||
| 157 | }, | 201 | }, |
| 158 | .probe = ad2s1200_probe, | 202 | .probe = ad2s1200_probe, |
| 159 | .id_table = ad2s1200_id, | 203 | .id_table = ad2s1200_id, |
| 160 | }; | 204 | }; |
| 161 | module_spi_driver(ad2s1200_driver); | 205 | module_spi_driver(ad2s1200_driver); |
| 162 | 206 | ||
| 207 | MODULE_AUTHOR("David Veenstra <davidjulianveenstra@gmail.com>"); | ||
| 163 | MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); | 208 | MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); |
| 164 | MODULE_DESCRIPTION("Analog Devices AD2S1200/1205 Resolver to Digital SPI driver"); | 209 | MODULE_DESCRIPTION("Analog Devices AD2S1200/1205 Resolver to Digital SPI driver"); |
| 165 | MODULE_LICENSE("GPL v2"); | 210 | MODULE_LICENSE("GPL v2"); |
| @@ -24,10 +24,6 @@ menuconfig STAGING · 23 unmodified lines | |||
| 24 | 24 | ||
| 25 | if STAGING | 25 | if STAGING |
| 26 | 26 | ||
| 27 | source "drivers/staging/ipx/Kconfig" | ||
| 28 | |||
| 29 | source "drivers/staging/ncpfs/Kconfig" | ||
| 30 | |||
| 31 | source "drivers/staging/wlan-ng/Kconfig" | 27 | source "drivers/staging/wlan-ng/Kconfig" |
| 32 | 28 | ||
| 33 | source "drivers/staging/comedi/Kconfig" | 29 | source "drivers/staging/comedi/Kconfig" |
| @@ -84,8 +80,6 @@ source "drivers/staging/netlogic/Kconfig" · 50 unmodified lines | |||
| 84 | 80 | ||
| 85 | source "drivers/staging/mt29f_spinand/Kconfig" | 81 | source "drivers/staging/mt29f_spinand/Kconfig" |
| 86 | 82 | ||
| 87 | source "drivers/staging/lustre/Kconfig" | ||
| 88 | |||
| 89 | source "drivers/staging/dgnc/Kconfig" | 83 | source "drivers/staging/dgnc/Kconfig" |
| 90 | 84 | ||
| 91 | source "drivers/staging/gs_fpgaboot/Kconfig" | 85 | source "drivers/staging/gs_fpgaboot/Kconfig" |
| @@ -3,8 +3,6 @@ · 2 unmodified lines | |||
| 3 | 3 | ||
| 4 | obj-y += media/ | 4 | obj-y += media/ |
| 5 | obj-y += typec/ | 5 | obj-y += typec/ |
| 6 | obj-$(CONFIG_IPX) += ipx/ | ||
| 7 | obj-$(CONFIG_NCP_FS) += ncpfs/ | ||
| 8 | obj-$(CONFIG_PRISM2_USB) += wlan-ng/ | 6 | obj-$(CONFIG_PRISM2_USB) += wlan-ng/ |
| 9 | obj-$(CONFIG_COMEDI) += comedi/ | 7 | obj-$(CONFIG_COMEDI) += comedi/ |
| 10 | obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ | 8 | obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ |
| @@ -32,7 +30,6 @@ obj-$(CONFIG_STAGING_BOARD) += board/ · 21 unmodified lines | |||
| 32 | obj-$(CONFIG_LTE_GDM724X) += gdm724x/ | 30 | obj-$(CONFIG_LTE_GDM724X) += gdm724x/ |
| 33 | obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ | 31 | obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ |
| 34 | obj-$(CONFIG_GOLDFISH) += goldfish/ | 32 | obj-$(CONFIG_GOLDFISH) += goldfish/ |
| 35 | obj-$(CONFIG_LNET) += lustre/ | ||
| 36 | obj-$(CONFIG_DGNC) += dgnc/ | 33 | obj-$(CONFIG_DGNC) += dgnc/ |
| 37 | obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ | 34 | obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ |
| 38 | obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ | 35 | obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ |
| @@ -6,7 +6,7 @@ config ASHMEM · 5 unmodified lines | |||
| 6 | bool "Enable the Anonymous Shared Memory Subsystem" | 6 | bool "Enable the Anonymous Shared Memory Subsystem" |
| 7 | default n | 7 | default n |
| 8 | depends on SHMEM | 8 | depends on SHMEM |
| 9 | ---help--- | 9 | help |
| 10 | The ashmem subsystem is a new shared memory allocator, similar to | 10 | The ashmem subsystem is a new shared memory allocator, similar to |
| 11 | POSIX SHM but with different behavior and sporting a simpler | 11 | POSIX SHM but with different behavior and sporting a simpler |
| 12 | file-based API. | 12 | file-based API. |
| @@ -14,6 +14,15 @@ config ASHMEM · 1 unmodified line | |||
| 14 | It is, in theory, a good memory allocator for low-memory devices, | 14 | It is, in theory, a good memory allocator for low-memory devices, |
| 15 | because it can discard shared memory units when under memory pressure. | 15 | because it can discard shared memory units when under memory pressure. |
| 16 | 16 | ||
| 17 | config ANDROID_VSOC | ||
| 18 | tristate "Android Virtual SoC support" | ||
| 19 | default n | ||
| 20 | depends on PCI_MSI | ||
| 21 | help | ||
| 22 | This option adds support for the Virtual SoC driver needed to boot | ||
| 23 | a 'cuttlefish' Android image inside QEmu. The driver interacts with | ||
| 24 | a QEmu ivshmem device. If built as a module, it will be called vsoc. | ||
| 25 | |||
| 17 | source "drivers/staging/android/ion/Kconfig" | 26 | source "drivers/staging/android/ion/Kconfig" |
| 18 | 27 | ||
| 19 | endif # if ANDROID | 28 | endif # if ANDROID |
| @@ -3,3 +3,4 @@ ccflags-y += -I$(src) # needed for trace events · 2 unmodified lines | |||
| 3 | obj-y += ion/ | 3 | obj-y += ion/ |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_ASHMEM) += ashmem.o | 5 | obj-$(CONFIG_ASHMEM) += ashmem.o |
| 6 | obj-$(CONFIG_ANDROID_VSOC) += vsoc.o | ||
| @@ -11,5 +11,14 @@ ion/ · 10 unmodified lines | |||
| 11 | - Split /dev/ion up into multiple nodes (e.g. /dev/ion/heap0) | 11 | - Split /dev/ion up into multiple nodes (e.g. /dev/ion/heap0) |
| 12 | - Better test framework (integration with VGEM was suggested) | 12 | - Better test framework (integration with VGEM was suggested) |
| 13 | 13 | ||
| 14 | vsoc.c, uapi/vsoc_shm.h | ||
| 15 | - The current driver uses the same wait queue for all of the futexes in a | ||
| 16 | region. This will cause false wakeups in regions with a large number of | ||
| 17 | waiting threads. We should eventually use multiple queues and select the | ||
| 18 | queue based on the region. | ||
| 19 | - Add debugfs support for examining the permissions of regions. | ||
| 20 | - Remove VSOC_WAIT_FOR_INCOMING_INTERRUPT ioctl. This functionality has been | ||
| 21 | superseded by the futex and is there for legacy reasons. | ||
| 22 | |||
| 14 | Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc: | 23 | Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc: |
| 15 | Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com> | 24 | Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com> |
| @@ -1,13 +1,9 @@ | |||
| 1 | // SPDX-License-Identifier: (GPL-2.0 OR Apache-2.0) | 1 | /* SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * include/linux/ashmem.h | 3 | * include/linux/ashmem.h |
| 4 | * | 4 | * |
| 5 | * Copyright 2008 Google Inc. | 5 | * Copyright 2008 Google Inc. |
| 6 | * Author: Robert Love | 6 | * Author: Robert Love |
| 7 | * | ||
| 8 | * This file is dual licensed. It may be redistributed and/or modified | ||
| 9 | * under the terms of the Apache 2.0 License OR version 2 of the GNU | ||
| 10 | * General Public License. | ||
| 11 | */ | 7 | */ |
| 12 | 8 | ||
| 13 | #ifndef _LINUX_ASHMEM_H | 9 | #ifndef _LINUX_ASHMEM_H |
| @@ -3,7 +3,7 @@ menuconfig ION · 2 unmodified lines | |||
| 3 | depends on HAVE_MEMBLOCK && HAS_DMA && MMU | 3 | depends on HAVE_MEMBLOCK && HAS_DMA && MMU |
| 4 | select GENERIC_ALLOCATOR | 4 | select GENERIC_ALLOCATOR |
| 5 | select DMA_SHARED_BUFFER | 5 | select DMA_SHARED_BUFFER |
| 6 | ---help--- | 6 | help |
| 7 | Choose this option to enable the ION Memory Manager, | 7 | Choose this option to enable the ION Memory Manager, |
| 8 | used by Android to efficiently allocate buffers | 8 | used by Android to efficiently allocate buffers |
| 9 | from userspace that can be shared between drivers. | 9 | from userspace that can be shared between drivers. |
| @@ -74,6 +74,8 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, · 73 unmodified lines | |||
| 74 | 74 | ||
| 75 | buffer->heap = heap; | 75 | buffer->heap = heap; |
| 76 | buffer->flags = flags; | 76 | buffer->flags = flags; |
| 77 | buffer->dev = dev; | ||
| 78 | buffer->size = len; | ||
| 77 | 79 | ||
| 78 | ret = heap->ops->allocate(heap, buffer, len, flags); | 80 | ret = heap->ops->allocate(heap, buffer, len, flags); |
| 79 | 81 | ||
| @@ -93,11 +95,6 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, · 13 unmodified lines | |||
| 93 | goto err1; | 95 | goto err1; |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | buffer->dev = dev; | ||
| 97 | buffer->size = len; | ||
| 98 | |||
| 99 | buffer->dev = dev; | ||
| 100 | buffer->size = len; | ||
| 101 | INIT_LIST_HEAD(&buffer->attachments); | 98 | INIT_LIST_HEAD(&buffer->attachments); |
| 102 | mutex_init(&buffer->lock); | 99 | mutex_init(&buffer->lock); |
| 103 | mutex_lock(&dev->buffer_lock); | 100 | mutex_lock(&dev->buffer_lock); |
| @@ -114,8 +111,11 @@ err2: · 10 unmodified lines | |||
| 114 | 111 | ||
| 115 | void ion_buffer_destroy(struct ion_buffer *buffer) | 112 | void ion_buffer_destroy(struct ion_buffer *buffer) |
| 116 | { | 113 | { |
| 117 | if (WARN_ON(buffer->kmap_cnt > 0)) | 114 | if (buffer->kmap_cnt > 0) { |
| 115 | pr_warn_once("%s: buffer still mapped in the kernel\n", | ||
| 116 | __func__); | ||
| 118 | buffer->heap->ops->unmap_kernel(buffer->heap, buffer); | 117 | buffer->heap->ops->unmap_kernel(buffer->heap, buffer); |
| 118 | } | ||
| 119 | buffer->heap->ops->free(buffer); | 119 | buffer->heap->ops->free(buffer); |
| 120 | kfree(buffer); | 120 | kfree(buffer); |
| 121 | } | 121 | } |
| @@ -318,6 +318,7 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, · 196 unmodified lines | |||
| 318 | struct ion_buffer *buffer = dmabuf->priv; | 318 | struct ion_buffer *buffer = dmabuf->priv; |
| 319 | void *vaddr; | 319 | void *vaddr; |
| 320 | struct ion_dma_buf_attachment *a; | 320 | struct ion_dma_buf_attachment *a; |
| 321 | int ret = 0; | ||
| 321 | 322 | ||
| 322 | /* | 323 | /* |
| 323 | * TODO: Move this elsewhere because we don't always need a vaddr | 324 | * TODO: Move this elsewhere because we don't always need a vaddr |
| @@ -325,6 +326,10 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, · 1 unmodified line | |||
| 325 | if (buffer->heap->ops->map_kernel) { | 326 | if (buffer->heap->ops->map_kernel) { |
| 326 | mutex_lock(&buffer->lock); | 327 | mutex_lock(&buffer->lock); |
| 327 | vaddr = ion_buffer_kmap_get(buffer); | 328 | vaddr = ion_buffer_kmap_get(buffer); |
| 329 | if (IS_ERR(vaddr)) { | ||
| 330 | ret = PTR_ERR(vaddr); | ||
| 331 | goto unlock; | ||
| 332 | } | ||
| 328 | mutex_unlock(&buffer->lock); | 333 | mutex_unlock(&buffer->lock); |
| 329 | } | 334 | } |
| 330 | 335 | ||
| @@ -333,9 +338,10 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, · 2 unmodified lines | |||
| 333 | dma_sync_sg_for_cpu(a->dev, a->table->sgl, a->table->nents, | 338 | dma_sync_sg_for_cpu(a->dev, a->table->sgl, a->table->nents, |
| 334 | direction); | 339 | direction); |
| 335 | } | 340 | } |
| 336 | mutex_unlock(&buffer->lock); | ||
| 337 | 341 | ||
| 338 | return 0; | 342 | unlock: |
| 343 | mutex_unlock(&buffer->lock); | ||
| 344 | return ret; | ||
| 339 | } | 345 | } |
| 340 | 346 | ||
| 341 | static int ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, | 347 | static int ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * drivers/staging/android/ion/ion.h | 3 | * drivers/staging/android/ion/ion.h |
| 4 | * | 4 | * |
| @@ -32,6 +32,9 @@ static void ion_page_pool_add(struct ion_page_pool *pool, struct page *page) · 31 unmodified lines | |||
| 32 | list_add_tail(&page->lru, &pool->low_items); | 32 | list_add_tail(&page->lru, &pool->low_items); |
| 33 | pool->low_count++; | 33 | pool->low_count++; |
| 34 | } | 34 | } |
| 35 | |||
| 36 | mod_node_page_state(page_pgdat(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, | ||
| 37 | (1 << (PAGE_SHIFT + pool->order))); | ||
| 35 | mutex_unlock(&pool->mutex); | 38 | mutex_unlock(&pool->mutex); |
| 36 | } | 39 | } |
| 37 | 40 | ||
| @@ -50,6 +53,8 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) · 12 unmodified lines | |||
| 50 | } | 53 | } |
| 51 | 54 | ||
| 52 | list_del(&page->lru); | 55 | list_del(&page->lru); |
| 56 | mod_node_page_state(page_pgdat(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, | ||
| 57 | -(1 << (PAGE_SHIFT + pool->order))); | ||
| 53 | return page; | 58 | return page; |
| 54 | } | 59 | } |
| 55 | 60 | ||
| @@ -1,13 +1,9 @@ | |||
| 1 | // SPDX-License-Identifier: (GPL-2.0 OR Apache-2.0) | 1 | /* SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * drivers/staging/android/uapi/ashmem.h | 3 | * drivers/staging/android/uapi/ashmem.h |
| 4 | * | 4 | * |
| 5 | * Copyright 2008 Google Inc. | 5 | * Copyright 2008 Google Inc. |
| 6 | * Author: Robert Love | 6 | * Author: Robert Love |
| 7 | * | ||
| 8 | * This file is dual licensed. It may be redistributed and/or modified | ||
| 9 | * under the terms of the Apache 2.0 License OR version 2 of the GNU | ||
| 10 | * General Public License. | ||
| 11 | */ | 7 | */ |
| 12 | 8 | ||
| 13 | #ifndef _UAPI_LINUX_ASHMEM_H | 9 | #ifndef _UAPI_LINUX_ASHMEM_H |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * drivers/staging/android/uapi/ion.h | 3 | * drivers/staging/android/uapi/ion.h |
| 4 | * | 4 | * |
| @@ -0,0 +1,295 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2017 Google, Inc. | ||
| 4 | * | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef _UAPI_LINUX_VSOC_SHM_H | ||
| 8 | #define _UAPI_LINUX_VSOC_SHM_H | ||
| 9 | |||
| 10 | #include <linux/types.h> | ||
| 11 | |||
| 12 | /** | ||
| 13 | * A permission is a token that permits a receiver to read and/or write an area | ||
| 14 | * of memory within a Vsoc region. | ||
| 15 | * | ||
| 16 | * An fd_scoped permission grants both read and write access, and can be | ||
| 17 | * attached to a file description (see open(2)). | ||
| 18 | * Ownership of the area can then be shared by passing a file descriptor | ||
| 19 | * among processes. | ||
| 20 | * | ||
| 21 | * begin_offset and end_offset define the area of memory that is controlled by | ||
| 22 | * the permission. owner_offset points to a word, also in shared memory, that | ||
| 23 | * controls ownership of the area. | ||
| 24 | * | ||
| 25 | * ownership of the region expires when the associated file description is | ||
| 26 | * released. | ||
| 27 | * | ||
| 28 | * At most one permission can be attached to each file description. | ||
| 29 | * | ||
| 30 | * This is useful when implementing HALs like gralloc that scope and pass | ||
| 31 | * ownership of shared resources via file descriptors. | ||
| 32 | * | ||
| 33 | * The caller is responsibe for doing any fencing. | ||
| 34 | * | ||
| 35 | * The calling process will normally identify a currently free area of | ||
| 36 | * memory. It will construct a proposed fd_scoped_permission_arg structure: | ||
| 37 | * | ||
| 38 | * begin_offset and end_offset describe the area being claimed | ||
| 39 | * | ||
| 40 | * owner_offset points to the location in shared memory that indicates the | ||
| 41 | * owner of the area. | ||
| 42 | * | ||
| 43 | * owned_value is the value that will be stored in owner_offset iff the | ||
| 44 | * permission can be granted. It must be different than VSOC_REGION_FREE. | ||
| 45 | * | ||
| 46 | * Two fd_scoped_permission structures are compatible if they vary only by | ||
| 47 | * their owned_value fields. | ||
| 48 | * | ||
| 49 | * The driver ensures that, for any group of simultaneous callers proposing | ||
| 50 | * compatible fd_scoped_permissions, it will accept exactly one of the | ||
| 51 | * propopsals. The other callers will get a failure with errno of EAGAIN. | ||
| 52 | * | ||
| 53 | * A process receiving a file descriptor can identify the region being | ||
| 54 | * granted using the VSOC_GET_FD_SCOPED_PERMISSION ioctl. | ||
| 55 | */ | ||
| 56 | struct fd_scoped_permission { | ||
| 57 | __u32 begin_offset; | ||
| 58 | __u32 end_offset; | ||
| 59 | __u32 owner_offset; | ||
| 60 | __u32 owned_value; | ||
| 61 | }; | ||
| 62 | |||
| 63 | /* | ||
| 64 | * This value represents a free area of memory. The driver expects to see this | ||
| 65 | * value at owner_offset when creating a permission otherwise it will not do it, | ||
| 66 | * and will write this value back once the permission is no longer needed. | ||
| 67 | */ | ||
| 68 | #define VSOC_REGION_FREE ((__u32)0) | ||
| 69 | |||
| 70 | /** | ||
| 71 | * ioctl argument for VSOC_CREATE_FD_SCOPE_PERMISSION | ||
| 72 | */ | ||
| 73 | struct fd_scoped_permission_arg { | ||
| 74 | struct fd_scoped_permission perm; | ||
| 75 | __s32 managed_region_fd; | ||
| 76 | }; | ||
| 77 | |||
| 78 | #define VSOC_NODE_FREE ((__u32)0) | ||
| 79 | |||
| 80 | /* | ||
| 81 | * Describes a signal table in shared memory. Each non-zero entry in the | ||
| 82 | * table indicates that the receiver should signal the futex at the given | ||
| 83 | * offset. Offsets are relative to the region, not the shared memory window. | ||
| 84 | * | ||
| 85 | * interrupt_signalled_offset is used to reliably signal interrupts across the | ||
| 86 | * vmm boundary. There are two roles: transmitter and receiver. For example, | ||
| 87 | * in the host_to_guest_signal_table the host is the transmitter and the | ||
| 88 | * guest is the receiver. The protocol is as follows: | ||
| 89 | * | ||
| 90 | * 1. The transmitter should convert the offset of the futex to an offset | ||
| 91 | * in the signal table [0, (1 << num_nodes_lg2)) | ||
| 92 | * The transmitter can choose any appropriate hashing algorithm, including | ||
| 93 | * hash = futex_offset & ((1 << num_nodes_lg2) - 1) | ||
| 94 | * | ||
| 95 | * 3. The transmitter should atomically compare and swap futex_offset with 0 | ||
| 96 | * at hash. There are 3 possible outcomes | ||
| 97 | * a. The swap fails because the futex_offset is already in the table. | ||
| 98 | * The transmitter should stop. | ||
| 99 | * b. Some other offset is in the table. This is a hash collision. The | ||
| 100 | * transmitter should move to another table slot and try again. One | ||
| 101 | * possible algorithm: | ||
| 102 | * hash = (hash + 1) & ((1 << num_nodes_lg2) - 1) | ||
| 103 | * c. The swap worked. Continue below. | ||
| 104 | * | ||
| 105 | * 3. The transmitter atomically swaps 1 with the value at the | ||
| 106 | * interrupt_signalled_offset. There are two outcomes: | ||
| 107 | * a. The prior value was 1. In this case an interrupt has already been | ||
| 108 | * posted. The transmitter is done. | ||
| 109 | * b. The prior value was 0, indicating that the receiver may be sleeping. | ||
| 110 | * The transmitter will issue an interrupt. | ||
| 111 | * | ||
| 112 | * 4. On waking the receiver immediately exchanges a 0 with the | ||
| 113 | * interrupt_signalled_offset. If it receives a 0 then this a spurious | ||
| 114 | * interrupt. That may occasionally happen in the current protocol, but | ||
| 115 | * should be rare. | ||
| 116 | * | ||
| 117 | * 5. The receiver scans the signal table by atomicaly exchanging 0 at each | ||
| 118 | * location. If a non-zero offset is returned from the exchange the | ||
| 119 | * receiver wakes all sleepers at the given offset: | ||
| 120 | * futex((int*)(region_base + old_value), FUTEX_WAKE, MAX_INT); | ||
| 121 | * | ||
| 122 | * 6. The receiver thread then does a conditional wait, waking immediately | ||
| 123 | * if the value at interrupt_signalled_offset is non-zero. This catches cases | ||
| 124 | * here additional signals were posted while the table was being scanned. | ||
| 125 | * On the guest the wait is handled via the VSOC_WAIT_FOR_INCOMING_INTERRUPT | ||
| 126 | * ioctl. | ||
| 127 | */ | ||
| 128 | struct vsoc_signal_table_layout { | ||
| 129 | /* log_2(Number of signal table entries) */ | ||
| 130 | __u32 num_nodes_lg2; | ||
| 131 | /* | ||
| 132 | * Offset to the first signal table entry relative to the start of the | ||
| 133 | * region | ||
| 134 | */ | ||
| 135 | __u32 futex_uaddr_table_offset; | ||
| 136 | /* | ||
| 137 | * Offset to an atomic_t / atomic uint32_t. A non-zero value indicates | ||
| 138 | * that one or more offsets are currently posted in the table. | ||
| 139 | * semi-unique access to an entry in the table | ||
| 140 | */ | ||
| 141 | __u32 interrupt_signalled_offset; | ||
| 142 | }; | ||
| 143 | |||
| 144 | #define VSOC_REGION_WHOLE ((__s32)0) | ||
| 145 | #define VSOC_DEVICE_NAME_SZ 16 | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Each HAL would (usually) talk to a single device region | ||
| 149 | * Mulitple entities care about these regions: | ||
| 150 | * - The ivshmem_server will populate the regions in shared memory | ||
| 151 | * - The guest kernel will read the region, create minor device nodes, and | ||
| 152 | * allow interested parties to register for FUTEX_WAKE events in the region | ||
| 153 | * - HALs will access via the minor device nodes published by the guest kernel | ||
| 154 | * - Host side processes will access the region via the ivshmem_server: | ||
| 155 | * 1. Pass name to ivshmem_server at a UNIX socket | ||
| 156 | * 2. ivshmemserver will reply with 2 fds: | ||
| 157 | * - host->guest doorbell fd | ||
| 158 | * - guest->host doorbell fd | ||
| 159 | * - fd for the shared memory region | ||
| 160 | * - region offset | ||
| 161 | * 3. Start a futex receiver thread on the doorbell fd pointed at the | ||
| 162 | * signal_nodes | ||
| 163 | */ | ||
| 164 | struct vsoc_device_region { | ||
| 165 | __u16 current_version; | ||
| 166 | __u16 min_compatible_version; | ||
| 167 | __u32 region_begin_offset; | ||
| 168 | __u32 region_end_offset; | ||
| 169 | __u32 offset_of_region_data; | ||
| 170 | struct vsoc_signal_table_layout guest_to_host_signal_table; | ||
| 171 | struct vsoc_signal_table_layout host_to_guest_signal_table; | ||
| 172 | /* Name of the device. Must always be terminated with a '\0', so | ||
| 173 | * the longest supported device name is 15 characters. | ||
| 174 | */ | ||
| 175 | char device_name[VSOC_DEVICE_NAME_SZ]; | ||
| 176 | /* There are two ways that permissions to access regions are handled: | ||
| 177 | * - When subdivided_by is VSOC_REGION_WHOLE, any process that can | ||
| 178 | * open the device node for the region gains complete access to it. | ||
| 179 | * - When subdivided is set processes that open the region cannot | ||
| 180 | * access it. Access to a sub-region must be established by invoking | ||
| 181 | * the VSOC_CREATE_FD_SCOPE_PERMISSION ioctl on the region | ||
| 182 | * referenced in subdivided_by, providing a fileinstance | ||
| 183 | * (represented by a fd) opened on this region. | ||
| 184 | */ | ||
| 185 | __u32 managed_by; | ||
| 186 | }; | ||
| 187 | |||
| 188 | /* | ||
| 189 | * The vsoc layout descriptor. | ||
| 190 | * The first 4K should be reserved for the shm header and region descriptors. | ||
| 191 | * The regions should be page aligned. | ||
| 192 | */ | ||
| 193 | |||
| 194 | struct vsoc_shm_layout_descriptor { | ||
| 195 | __u16 major_version; | ||
| 196 | __u16 minor_version; | ||
| 197 | |||
| 198 | /* size of the shm. This may be redundant but nice to have */ | ||
| 199 | __u32 size; | ||
| 200 | |||
| 201 | /* number of shared memory regions */ | ||
| 202 | __u32 region_count; | ||
| 203 | |||
| 204 | /* The offset to the start of region descriptors */ | ||
| 205 | __u32 vsoc_region_desc_offset; | ||
| 206 | }; | ||
| 207 | |||
| 208 | /* | ||
| 209 | * This specifies the current version that should be stored in | ||
| 210 | * vsoc_shm_layout_descriptor.major_version and | ||
| 211 | * vsoc_shm_layout_descriptor.minor_version. | ||
| 212 | * It should be updated only if the vsoc_device_region and | ||
| 213 | * vsoc_shm_layout_descriptor structures have changed. | ||
| 214 | * Versioning within each region is transferred | ||
| 215 | * via the min_compatible_version and current_version fields in | ||
| 216 | * vsoc_device_region. The driver does not consult these fields: they are left | ||
| 217 | * for the HALs and host processes and will change independently of the layout | ||
| 218 | * version. | ||
| 219 | */ | ||
| 220 | #define CURRENT_VSOC_LAYOUT_MAJOR_VERSION 2 | ||
| 221 | #define CURRENT_VSOC_LAYOUT_MINOR_VERSION 0 | ||
| 222 | |||
| 223 | #define VSOC_CREATE_FD_SCOPED_PERMISSION \ | ||
| 224 | _IOW(0xF5, 0, struct fd_scoped_permission) | ||
| 225 | #define VSOC_GET_FD_SCOPED_PERMISSION _IOR(0xF5, 1, struct fd_scoped_permission) | ||
| 226 | |||
| 227 | /* | ||
| 228 | * This is used to signal the host to scan the guest_to_host_signal_table | ||
| 229 | * for new futexes to wake. This sends an interrupt if one is not already | ||
| 230 | * in flight. | ||
| 231 | */ | ||
| 232 | #define VSOC_MAYBE_SEND_INTERRUPT_TO_HOST _IO(0xF5, 2) | ||
| 233 | |||
| 234 | /* | ||
| 235 | * When this returns the guest will scan host_to_guest_signal_table to | ||
| 236 | * check for new futexes to wake. | ||
| 237 | */ | ||
| 238 | /* TODO(ghartman): Consider moving this to the bottom half */ | ||
| 239 | #define VSOC_WAIT_FOR_INCOMING_INTERRUPT _IO(0xF5, 3) | ||
| 240 | |||
| 241 | /* | ||
| 242 | * Guest HALs will use this to retrieve the region description after | ||
| 243 | * opening their device node. | ||
| 244 | */ | ||
| 245 | #define VSOC_DESCRIBE_REGION _IOR(0xF5, 4, struct vsoc_device_region) | ||
| 246 | |||
| 247 | /* | ||
| 248 | * Wake any threads that may be waiting for a host interrupt on this region. | ||
| 249 | * This is mostly used during shutdown. | ||
| 250 | */ | ||
| 251 | #define VSOC_SELF_INTERRUPT _IO(0xF5, 5) | ||
| 252 | |||
| 253 | /* | ||
| 254 | * This is used to signal the host to scan the guest_to_host_signal_table | ||
| 255 | * for new futexes to wake. This sends an interrupt unconditionally. | ||
| 256 | */ | ||
| 257 | #define VSOC_SEND_INTERRUPT_TO_HOST _IO(0xF5, 6) | ||
| 258 | |||
| 259 | enum wait_types { | ||
| 260 | VSOC_WAIT_UNDEFINED = 0, | ||
| 261 | VSOC_WAIT_IF_EQUAL = 1, | ||
| 262 | VSOC_WAIT_IF_EQUAL_TIMEOUT = 2 | ||
| 263 | }; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * Wait for a condition to be true | ||
| 267 | * | ||
| 268 | * Note, this is sized and aligned so the 32 bit and 64 bit layouts are | ||
| 269 | * identical. | ||
| 270 | */ | ||
| 271 | struct vsoc_cond_wait { | ||
| 272 | /* Input: Offset of the 32 bit word to check */ | ||
| 273 | __u32 offset; | ||
| 274 | /* Input: Value that will be compared with the offset */ | ||
| 275 | __u32 value; | ||
| 276 | /* Monotonic time to wake at in seconds */ | ||
| 277 | __u64 wake_time_sec; | ||
| 278 | /* Input: Monotonic time to wait in nanoseconds */ | ||
| 279 | __u32 wake_time_nsec; | ||
| 280 | /* Input: Type of wait */ | ||
| 281 | __u32 wait_type; | ||
| 282 | /* Output: Number of times the thread woke before returning. */ | ||
| 283 | __u32 wakes; | ||
| 284 | /* Ensure that we're 8-byte aligned and 8 byte length for 32/64 bit | ||
| 285 | * compatibility. | ||
| 286 | */ | ||
| 287 | __u32 reserved_1; | ||
| 288 | }; | ||
| 289 | |||
| 290 | #define VSOC_COND_WAIT _IOWR(0xF5, 7, struct vsoc_cond_wait) | ||
| 291 | |||
| 292 | /* Wake any local threads waiting at the offset given in arg */ | ||
| 293 | #define VSOC_COND_WAKE _IO(0xF5, 8) | ||
| 294 | |||
| 295 | #endif /* _UAPI_LINUX_VSOC_SHM_H */ | ||
| @@ -0,0 +1,1152 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * drivers/android/staging/vsoc.c | ||
| 4 | * | ||
| 5 | * Android Virtual System on a Chip (VSoC) driver | ||
| 6 | * | ||
| 7 | * Copyright (C) 2017 Google, Inc. | ||
| 8 | * | ||
| 9 | * Author: ghartman@google.com | ||
| 10 | * | ||
| 11 | * Based on drivers/char/kvm_ivshmem.c - driver for KVM Inter-VM shared memory | ||
| 12 | * Copyright 2009 Cam Macdonell <cam@cs.ualberta.ca> | ||
| 13 | * | ||
| 14 | * Based on cirrusfb.c and 8139cp.c: | ||
| 15 | * Copyright 1999-2001 Jeff Garzik | ||
| 16 | * Copyright 2001-2004 Jeff Garzik | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/dma-mapping.h> | ||
| 20 | #include <linux/freezer.h> | ||
| 21 | #include <linux/futex.h> | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/mutex.h> | ||
| 26 | #include <linux/pci.h> | ||
| 27 | #include <linux/proc_fs.h> | ||
| 28 | #include <linux/sched.h> | ||
| 29 | #include <linux/syscalls.h> | ||
| 30 | #include <linux/uaccess.h> | ||
| 31 | #include <linux/interrupt.h> | ||
| 32 | #include <linux/mutex.h> | ||
| 33 | #include <linux/cdev.h> | ||
| 34 | #include <linux/file.h> | ||
| 35 | #include "uapi/vsoc_shm.h" | ||
| 36 | |||
| 37 | #define VSOC_DEV_NAME "vsoc" | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Description of the ivshmem-doorbell PCI device used by QEmu. These | ||
| 41 | * constants follow docs/specs/ivshmem-spec.txt, which can be found in | ||
| 42 | * the QEmu repository. This was last reconciled with the version that | ||
| 43 | * came out with 2.8 | ||
| 44 | */ | ||
| 45 | |||
| 46 | /* | ||
| 47 | * These constants are determined KVM Inter-VM shared memory device | ||
| 48 | * register offsets | ||
| 49 | */ | ||
| 50 | enum { | ||
| 51 | INTR_MASK = 0x00, /* Interrupt Mask */ | ||
| 52 | INTR_STATUS = 0x04, /* Interrupt Status */ | ||
| 53 | IV_POSITION = 0x08, /* VM ID */ | ||
| 54 | DOORBELL = 0x0c, /* Doorbell */ | ||
| 55 | }; | ||
| 56 | |||
| 57 | static const int REGISTER_BAR; /* Equal to 0 */ | ||
| 58 | static const int MAX_REGISTER_BAR_LEN = 0x100; | ||
| 59 | /* | ||
| 60 | * The MSI-x BAR is not used directly. | ||
| 61 | * | ||
| 62 | * static const int MSI_X_BAR = 1; | ||
| 63 | */ | ||
| 64 | static const int SHARED_MEMORY_BAR = 2; | ||
| 65 | |||
| 66 | struct vsoc_region_data { | ||
| 67 | char name[VSOC_DEVICE_NAME_SZ + 1]; | ||
| 68 | wait_queue_head_t interrupt_wait_queue; | ||
| 69 | /* TODO(b/73664181): Use multiple futex wait queues */ | ||
| 70 | wait_queue_head_t futex_wait_queue; | ||
| 71 | /* Flag indicating that an interrupt has been signalled by the host. */ | ||
| 72 | atomic_t *incoming_signalled; | ||
| 73 | /* Flag indicating the guest has signalled the host. */ | ||
| 74 | atomic_t *outgoing_signalled; | ||
| 75 | bool irq_requested; | ||
| 76 | bool device_created; | ||
| 77 | }; | ||
| 78 | |||
| 79 | struct vsoc_device { | ||
| 80 | /* Kernel virtual address of REGISTER_BAR. */ | ||
| 81 | void __iomem *regs; | ||
| 82 | /* Physical address of SHARED_MEMORY_BAR. */ | ||
| 83 | phys_addr_t shm_phys_start; | ||
| 84 | /* Kernel virtual address of SHARED_MEMORY_BAR. */ | ||
| 85 | void __iomem *kernel_mapped_shm; | ||
| 86 | /* Size of the entire shared memory window in bytes. */ | ||
| 87 | size_t shm_size; | ||
| 88 | /* | ||
| 89 | * Pointer to the virtual address of the shared memory layout structure. | ||
| 90 | * This is probably identical to kernel_mapped_shm, but saving this | ||
| 91 | * here saves a lot of annoying casts. | ||
| 92 | */ | ||
| 93 | struct vsoc_shm_layout_descriptor *layout; | ||
| 94 | /* | ||
| 95 | * Points to a table of region descriptors in the kernel's virtual | ||
| 96 | * address space. Calculated from | ||
| 97 | * vsoc_shm_layout_descriptor.vsoc_region_desc_offset | ||
| 98 | */ | ||
| 99 | struct vsoc_device_region *regions; | ||
| 100 | /* Head of a list of permissions that have been granted. */ | ||
| 101 | struct list_head permissions; | ||
| 102 | struct pci_dev *dev; | ||
| 103 | /* Per-region (and therefore per-interrupt) information. */ | ||
| 104 | struct vsoc_region_data *regions_data; | ||
| 105 | /* | ||
| 106 | * Table of msi-x entries. This has to be separated from struct | ||
| 107 | * vsoc_region_data because the kernel deals with them as an array. | ||
| 108 | */ | ||
| 109 | struct msix_entry *msix_entries; | ||
| 110 | /* Mutex that protectes the permission list */ | ||
| 111 | struct mutex mtx; | ||
| 112 | /* Major number assigned by the kernel */ | ||
| 113 | int major; | ||
| 114 | /* Character device assigned by the kernel */ | ||
| 115 | struct cdev cdev; | ||
| 116 | /* Device class assigned by the kernel */ | ||
| 117 | struct class *class; | ||
| 118 | /* | ||
| 119 | * Flags that indicate what we've initialized. These are used to do an | ||
| 120 | * orderly cleanup of the device. | ||
| 121 | */ | ||
| 122 | bool enabled_device; | ||
| 123 | bool requested_regions; | ||
| 124 | bool cdev_added; | ||
| 125 | bool class_added; | ||
| 126 | bool msix_enabled; | ||
| 127 | }; | ||
| 128 | |||
| 129 | static struct vsoc_device vsoc_dev; | ||
| 130 | |||
| 131 | /* | ||
| 132 | * TODO(ghartman): Add a /sys filesystem entry that summarizes the permissions. | ||
| 133 | */ | ||
| 134 | |||
| 135 | struct fd_scoped_permission_node { | ||
| 136 | struct fd_scoped_permission permission; | ||
| 137 | struct list_head list; | ||
| 138 | }; | ||
| 139 | |||
| 140 | struct vsoc_private_data { | ||
| 141 | struct fd_scoped_permission_node *fd_scoped_permission_node; | ||
| 142 | }; | ||
| 143 | |||
| 144 | static long vsoc_ioctl(struct file *, unsigned int, unsigned long); | ||
| 145 | static int vsoc_mmap(struct file *, struct vm_area_struct *); | ||
| 146 | static int vsoc_open(struct inode *, struct file *); | ||
| 147 | static int vsoc_release(struct inode *, struct file *); | ||
| 148 | static ssize_t vsoc_read(struct file *, char __user *, size_t, loff_t *); | ||
| 149 | static ssize_t vsoc_write(struct file *, const char __user *, size_t, loff_t *); | ||
| 150 | static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin); | ||
| 151 | static int | ||
| 152 | do_create_fd_scoped_permission(struct vsoc_device_region *region_p, | ||
| 153 | struct fd_scoped_permission_node *np, | ||
| 154 | struct fd_scoped_permission_arg __user *arg); | ||
| 155 | static void | ||
| 156 | do_destroy_fd_scoped_permission(struct vsoc_device_region *owner_region_p, | ||
| 157 | struct fd_scoped_permission *perm); | ||
| 158 | static long do_vsoc_describe_region(struct file *, | ||
| 159 | struct vsoc_device_region __user *); | ||
| 160 | static ssize_t vsoc_get_area(struct file *filp, __u32 *perm_off); | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Validate arguments on entry points to the driver. | ||
| 164 | */ | ||
| 165 | inline int vsoc_validate_inode(struct inode *inode) | ||
| 166 | { | ||
| 167 | if (iminor(inode) >= vsoc_dev.layout->region_count) { | ||
| 168 | dev_err(&vsoc_dev.dev->dev, | ||
| 169 | "describe_region: invalid region %d\n", iminor(inode)); | ||
| 170 | return -ENODEV; | ||
| 171 | } | ||
| 172 | return 0; | ||
| 173 | } | ||
| 174 | |||
| 175 | inline int vsoc_validate_filep(struct file *filp) | ||
| 176 | { | ||
| 177 | int ret = vsoc_validate_inode(file_inode(filp)); | ||
| 178 | |||
| 179 | if (ret) | ||
| 180 | return ret; | ||
| 181 | if (!filp->private_data) { | ||
| 182 | dev_err(&vsoc_dev.dev->dev, | ||
| 183 | "No private data on fd, region %d\n", | ||
| 184 | iminor(file_inode(filp))); | ||
| 185 | return -EBADFD; | ||
| 186 | } | ||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | /* Converts from shared memory offset to virtual address */ | ||
| 191 | static inline void *shm_off_to_virtual_addr(__u32 offset) | ||
| 192 | { | ||
| 193 | return (void __force *)vsoc_dev.kernel_mapped_shm + offset; | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Converts from shared memory offset to physical address */ | ||
| 197 | static inline phys_addr_t shm_off_to_phys_addr(__u32 offset) | ||
| 198 | { | ||
| 199 | return vsoc_dev.shm_phys_start + offset; | ||
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * Convenience functions to obtain the region from the inode or file. | ||
| 204 | * Dangerous to call before validating the inode/file. | ||
| 205 | */ | ||
| 206 | static | ||
| 207 | inline struct vsoc_device_region *vsoc_region_from_inode(struct inode *inode) | ||
| 208 | { | ||
| 209 | return &vsoc_dev.regions[iminor(inode)]; | ||
| 210 | } | ||
| 211 | |||
| 212 | static | ||
| 213 | inline struct vsoc_device_region *vsoc_region_from_filep(struct file *inode) | ||
| 214 | { | ||
| 215 | return vsoc_region_from_inode(file_inode(inode)); | ||
| 216 | } | ||
| 217 | |||
| 218 | static inline uint32_t vsoc_device_region_size(struct vsoc_device_region *r) | ||
| 219 | { | ||
| 220 | return r->region_end_offset - r->region_begin_offset; | ||
| 221 | } | ||
| 222 | |||
| 223 | static const struct file_operations vsoc_ops = { | ||
| 224 | .owner = THIS_MODULE, | ||
| 225 | .open = vsoc_open, | ||
| 226 | .mmap = vsoc_mmap, | ||
| 227 | .read = vsoc_read, | ||
| 228 | .unlocked_ioctl = vsoc_ioctl, | ||
| 229 | .compat_ioctl = vsoc_ioctl, | ||
| 230 | .write = vsoc_write, | ||
| 231 | .llseek = vsoc_lseek, | ||
| 232 | .release = vsoc_release, | ||
| 233 | }; | ||
| 234 | |||
| 235 | static struct pci_device_id vsoc_id_table[] = { | ||
| 236 | {0x1af4, 0x1110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | ||
| 237 | {0}, | ||
| 238 | }; | ||
| 239 | |||
| 240 | MODULE_DEVICE_TABLE(pci, vsoc_id_table); | ||
| 241 | |||
| 242 | static void vsoc_remove_device(struct pci_dev *pdev); | ||
| 243 | static int vsoc_probe_device(struct pci_dev *pdev, | ||
| 244 | const struct pci_device_id *ent); | ||
| 245 | |||
| 246 | static struct pci_driver vsoc_pci_driver = { | ||
| 247 | .name = "vsoc", | ||
| 248 | .id_table = vsoc_id_table, | ||
| 249 | .probe = vsoc_probe_device, | ||
| 250 | .remove = vsoc_remove_device, | ||
| 251 | }; | ||
| 252 | |||
| 253 | static int | ||
| 254 | do_create_fd_scoped_permission(struct vsoc_device_region *region_p, | ||
| 255 | struct fd_scoped_permission_node *np, | ||
| 256 | struct fd_scoped_permission_arg __user *arg) | ||
| 257 | { | ||
| 258 | struct file *managed_filp; | ||
| 259 | s32 managed_fd; | ||
| 260 | atomic_t *owner_ptr = NULL; | ||
| 261 | struct vsoc_device_region *managed_region_p; | ||
| 262 | |||
| 263 | if (copy_from_user(&np->permission, &arg->perm, sizeof(*np)) || | ||
| 264 | copy_from_user(&managed_fd, | ||
| 265 | &arg->managed_region_fd, sizeof(managed_fd))) { | ||
| 266 | return -EFAULT; | ||
| 267 | } | ||
| 268 | managed_filp = fdget(managed_fd).file; | ||
| 269 | /* Check that it's a valid fd, */ | ||
| 270 | if (!managed_filp || vsoc_validate_filep(managed_filp)) | ||
| 271 | return -EPERM; | ||
| 272 | /* EEXIST if the given fd already has a permission. */ | ||
| 273 | if (((struct vsoc_private_data *)managed_filp->private_data)-> | ||
| 274 | fd_scoped_permission_node) | ||
| 275 | return -EEXIST; | ||
| 276 | managed_region_p = vsoc_region_from_filep(managed_filp); | ||
| 277 | /* Check that the provided region is managed by this one */ | ||
| 278 | if (&vsoc_dev.regions[managed_region_p->managed_by] != region_p) | ||
| 279 | return -EPERM; | ||
| 280 | /* The area must be well formed and have non-zero size */ | ||
| 281 | if (np->permission.begin_offset >= np->permission.end_offset) | ||
| 282 | return -EINVAL; | ||
| 283 | /* The area must fit in the memory window */ | ||
| 284 | if (np->permission.end_offset > | ||
| 285 | vsoc_device_region_size(managed_region_p)) | ||
| 286 | return -ERANGE; | ||
| 287 | /* The area must be in the region data section */ | ||
| 288 | if (np->permission.begin_offset < | ||
| 289 | managed_region_p->offset_of_region_data) | ||
| 290 | return -ERANGE; | ||
| 291 | /* The area must be page aligned */ | ||
| 292 | if (!PAGE_ALIGNED(np->permission.begin_offset) || | ||
| 293 | !PAGE_ALIGNED(np->permission.end_offset)) | ||
| 294 | return -EINVAL; | ||
| 295 | /* Owner offset must be naturally aligned in the window */ | ||
| 296 | if (np->permission.owner_offset & | ||
| 297 | (sizeof(np->permission.owner_offset) - 1)) | ||
| 298 | return -EINVAL; | ||
| 299 | /* The owner flag must reside in the owner memory */ | ||
| 300 | if (np->permission.owner_offset + sizeof(np->permission.owner_offset) > | ||
| 301 | vsoc_device_region_size(region_p)) | ||
| 302 | return -ERANGE; | ||
| 303 | /* The owner flag must reside in the data section */ | ||
| 304 | if (np->permission.owner_offset < region_p->offset_of_region_data) | ||
| 305 | return -EINVAL; | ||
| 306 | /* The owner value must change to claim the memory */ | ||
| 307 | if (np->permission.owned_value == VSOC_REGION_FREE) | ||
| 308 | return -EINVAL; | ||
| 309 | owner_ptr = | ||
| 310 | (atomic_t *)shm_off_to_virtual_addr(region_p->region_begin_offset + | ||
| 311 | np->permission.owner_offset); | ||
| 312 | /* We've already verified that this is in the shared memory window, so | ||
| 313 | * it should be safe to write to this address. | ||
| 314 | */ | ||
| 315 | if (atomic_cmpxchg(owner_ptr, | ||
| 316 | VSOC_REGION_FREE, | ||
| 317 | np->permission.owned_value) != VSOC_REGION_FREE) { | ||
| 318 | return -EBUSY; | ||
| 319 | } | ||
| 320 | ((struct vsoc_private_data *)managed_filp->private_data)-> | ||
| 321 | fd_scoped_permission_node = np; | ||
| 322 | /* The file offset needs to be adjusted if the calling | ||
| 323 | * process did any read/write operations on the fd | ||
| 324 | * before creating the permission. | ||
| 325 | */ | ||
| 326 | if (managed_filp->f_pos) { | ||
| 327 | if (managed_filp->f_pos > np->permission.end_offset) { | ||
| 328 | /* If the offset is beyond the permission end, set it | ||
| 329 | * to the end. | ||
| 330 | */ | ||
| 331 | managed_filp->f_pos = np->permission.end_offset; | ||
| 332 | } else { | ||
| 333 | /* If the offset is within the permission interval | ||
| 334 | * keep it there otherwise reset it to zero. | ||
| 335 | */ | ||
| 336 | if (managed_filp->f_pos < np->permission.begin_offset) { | ||
| 337 | managed_filp->f_pos = 0; | ||
| 338 | } else { | ||
| 339 | managed_filp->f_pos -= | ||
| 340 | np->permission.begin_offset; | ||
| 341 | } | ||
| 342 | } | ||
| 343 | } | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | static void | ||
| 348 | do_destroy_fd_scoped_permission_node(struct vsoc_device_region *owner_region_p, | ||
| 349 | struct fd_scoped_permission_node *node) | ||
| 350 | { | ||
| 351 | if (node) { | ||
| 352 | do_destroy_fd_scoped_permission(owner_region_p, | ||
| 353 | &node->permission); | ||
| 354 | mutex_lock(&vsoc_dev.mtx); | ||
| 355 | list_del(&node->list); | ||
| 356 | mutex_unlock(&vsoc_dev.mtx); | ||
| 357 | kfree(node); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | static void | ||
| 362 | do_destroy_fd_scoped_permission(struct vsoc_device_region *owner_region_p, | ||
| 363 | struct fd_scoped_permission *perm) | ||
| 364 | { | ||
| 365 | atomic_t *owner_ptr = NULL; | ||
| 366 | int prev = 0; | ||
| 367 | |||
| 368 | if (!perm) | ||
| 369 | return; | ||
| 370 | owner_ptr = (atomic_t *)shm_off_to_virtual_addr | ||
| 371 | (owner_region_p->region_begin_offset + perm->owner_offset); | ||
| 372 | prev = atomic_xchg(owner_ptr, VSOC_REGION_FREE); | ||
| 373 | if (prev != perm->owned_value) | ||
| 374 | dev_err(&vsoc_dev.dev->dev, | ||
| 375 | "%x-%x: owner (%s) %x: expected to be %x was %x", | ||
| 376 | perm->begin_offset, perm->end_offset, | ||
| 377 | owner_region_p->device_name, perm->owner_offset, | ||
| 378 | perm->owned_value, prev); | ||
| 379 | } | ||
| 380 | |||
| 381 | static long do_vsoc_describe_region(struct file *filp, | ||
| 382 | struct vsoc_device_region __user *dest) | ||
| 383 | { | ||
| 384 | struct vsoc_device_region *region_p; | ||
| 385 | int retval = vsoc_validate_filep(filp); | ||
| 386 | |||
| 387 | if (retval) | ||
| 388 | return retval; | ||
| 389 | region_p = vsoc_region_from_filep(filp); | ||
| 390 | if (copy_to_user(dest, region_p, sizeof(*region_p))) | ||
| 391 | return -EFAULT; | ||
| 392 | return 0; | ||
| 393 | } | ||
| 394 | |||
| 395 | /** | ||
| 396 | * Implements the inner logic of cond_wait. Copies to and from userspace are | ||
| 397 | * done in the helper function below. | ||
| 398 | */ | ||
| 399 | static int handle_vsoc_cond_wait(struct file *filp, struct vsoc_cond_wait *arg) | ||
| 400 | { | ||
| 401 | DEFINE_WAIT(wait); | ||
| 402 | u32 region_number = iminor(file_inode(filp)); | ||
| 403 | struct vsoc_region_data *data = vsoc_dev.regions_data + region_number; | ||
| 404 | struct hrtimer_sleeper timeout, *to = NULL; | ||
| 405 | int ret = 0; | ||
| 406 | struct vsoc_device_region *region_p = vsoc_region_from_filep(filp); | ||
| 407 | atomic_t *address = NULL; | ||
| 408 | struct timespec ts; | ||
| 409 | |||
| 410 | /* Ensure that the offset is aligned */ | ||
| 411 | if (arg->offset & (sizeof(uint32_t) - 1)) | ||
| 412 | return -EADDRNOTAVAIL; | ||
| 413 | /* Ensure that the offset is within shared memory */ | ||
| 414 | if (((uint64_t)arg->offset) + region_p->region_begin_offset + | ||
| 415 | sizeof(uint32_t) > region_p->region_end_offset) | ||
| 416 | return -E2BIG; | ||
| 417 | address = shm_off_to_virtual_addr(region_p->region_begin_offset + | ||
| 418 | arg->offset); | ||
| 419 | |||
| 420 | /* Ensure that the type of wait is valid */ | ||
| 421 | switch (arg->wait_type) { | ||
| 422 | case VSOC_WAIT_IF_EQUAL: | ||
| 423 | break; | ||
| 424 | case VSOC_WAIT_IF_EQUAL_TIMEOUT: | ||
| 425 | to = &timeout; | ||
| 426 | break; | ||
| 427 | default: | ||
| 428 | return -EINVAL; | ||
| 429 | } | ||
| 430 | |||
| 431 | if (to) { | ||
| 432 | /* Copy the user-supplied timesec into the kernel structure. | ||
| 433 | * We do things this way to flatten differences between 32 bit | ||
| 434 | * and 64 bit timespecs. | ||
| 435 | */ | ||
| 436 | ts.tv_sec = arg->wake_time_sec; | ||
| 437 | ts.tv_nsec = arg->wake_time_nsec; | ||
| 438 | |||
| 439 | if (!timespec_valid(&ts)) | ||
| 440 | return -EINVAL; | ||
| 441 | hrtimer_init_on_stack(&to->timer, CLOCK_MONOTONIC, | ||
| 442 | HRTIMER_MODE_ABS); | ||
| 443 | hrtimer_set_expires_range_ns(&to->timer, timespec_to_ktime(ts), | ||
| 444 | current->timer_slack_ns); | ||
| 445 | |||
| 446 | hrtimer_init_sleeper(to, current); | ||
| 447 | } | ||
| 448 | |||
| 449 | while (1) { | ||
| 450 | prepare_to_wait(&data->futex_wait_queue, &wait, | ||
| 451 | TASK_INTERRUPTIBLE); | ||
| 452 | /* | ||
| 453 | * Check the sentinel value after prepare_to_wait. If the value | ||
| 454 | * changes after this check the writer will call signal, | ||
| 455 | * changing the task state from INTERRUPTIBLE to RUNNING. That | ||
| 456 | * will ensure that schedule() will eventually schedule this | ||
| 457 | * task. | ||
| 458 | */ | ||
| 459 | if (atomic_read(address) != arg->value) { | ||
| 460 | ret = 0; | ||
| 461 | break; | ||
| 462 | } | ||
| 463 | if (to) { | ||
| 464 | hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS); | ||
| 465 | if (likely(to->task)) | ||
| 466 | freezable_schedule(); | ||
| 467 | hrtimer_cancel(&to->timer); | ||
| 468 | if (!to->task) { | ||
| 469 | ret = -ETIMEDOUT; | ||
| 470 | break; | ||
| 471 | } | ||
| 472 | } else { | ||
| 473 | freezable_schedule(); | ||
| 474 | } | ||
| 475 | /* Count the number of times that we woke up. This is useful | ||
| 476 | * for unit testing. | ||
| 477 | */ | ||
| 478 | ++arg->wakes; | ||
| 479 | if (signal_pending(current)) { | ||
| 480 | ret = -EINTR; | ||
| 481 | break; | ||
| 482 | } | ||
| 483 | } | ||
| 484 | finish_wait(&data->futex_wait_queue, &wait); | ||
| 485 | if (to) | ||
| 486 | destroy_hrtimer_on_stack(&to->timer); | ||
| 487 | return ret; | ||
| 488 | } | ||
| 489 | |||
| 490 | /** | ||
| 491 | * Handles the details of copying from/to userspace to ensure that the copies | ||
| 492 | * happen on all of the return paths of cond_wait. | ||
| 493 | */ | ||
| 494 | static int do_vsoc_cond_wait(struct file *filp, | ||
| 495 | struct vsoc_cond_wait __user *untrusted_in) | ||
| 496 | { | ||
| 497 | struct vsoc_cond_wait arg; | ||
| 498 | int rval = 0; | ||
| 499 | |||
| 500 | if (copy_from_user(&arg, untrusted_in, sizeof(arg))) | ||
| 501 | return -EFAULT; | ||
| 502 | /* wakes is an out parameter. Initialize it to something sensible. */ | ||
| 503 | arg.wakes = 0; | ||
| 504 | rval = handle_vsoc_cond_wait(filp, &arg); | ||
| 505 | if (copy_to_user(untrusted_in, &arg, sizeof(arg))) | ||
| 506 | return -EFAULT; | ||
| 507 | return rval; | ||
| 508 | } | ||
| 509 | |||
| 510 | static int do_vsoc_cond_wake(struct file *filp, uint32_t offset) | ||
| 511 | { | ||
| 512 | struct vsoc_device_region *region_p = vsoc_region_from_filep(filp); | ||
| 513 | u32 region_number = iminor(file_inode(filp)); | ||
| 514 | struct vsoc_region_data *data = vsoc_dev.regions_data + region_number; | ||
| 515 | /* Ensure that the offset is aligned */ | ||
| 516 | if (offset & (sizeof(uint32_t) - 1)) | ||
| 517 | return -EADDRNOTAVAIL; | ||
| 518 | /* Ensure that the offset is within shared memory */ | ||
| 519 | if (((uint64_t)offset) + region_p->region_begin_offset + | ||
| 520 | sizeof(uint32_t) > region_p->region_end_offset) | ||
| 521 | return -E2BIG; | ||
| 522 | /* | ||
| 523 | * TODO(b/73664181): Use multiple futex wait queues. | ||
| 524 | * We need to wake every sleeper when the condition changes. Typically | ||
| 525 | * only a single thread will be waiting on the condition, but there | ||
| 526 | * are exceptions. The worst case is about 10 threads. | ||
| 527 | */ | ||
| 528 | wake_up_interruptible_all(&data->futex_wait_queue); | ||
| 529 | return 0; | ||
| 530 | } | ||
| 531 | |||
| 532 | static long vsoc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | ||
| 533 | { | ||
| 534 | int rv = 0; | ||
| 535 | struct vsoc_device_region *region_p; | ||
| 536 | u32 reg_num; | ||
| 537 | struct vsoc_region_data *reg_data; | ||
| 538 | int retval = vsoc_validate_filep(filp); | ||
| 539 | |||
| 540 | if (retval) | ||
| 541 | return retval; | ||
| 542 | region_p = vsoc_region_from_filep(filp); | ||
| 543 | reg_num = iminor(file_inode(filp)); | ||
| 544 | reg_data = vsoc_dev.regions_data + reg_num; | ||
| 545 | switch (cmd) { | ||
| 546 | case VSOC_CREATE_FD_SCOPED_PERMISSION: | ||
| 547 | { | ||
| 548 | struct fd_scoped_permission_node *node = NULL; | ||
| 549 | |||
| 550 | node = kzalloc(sizeof(*node), GFP_KERNEL); | ||
| 551 | /* We can't allocate memory for the permission */ | ||
| 552 | if (!node) | ||
| 553 | return -ENOMEM; | ||
| 554 | INIT_LIST_HEAD(&node->list); | ||
| 555 | rv = do_create_fd_scoped_permission | ||
| 556 | (region_p, | ||
| 557 | node, | ||
| 558 | (struct fd_scoped_permission_arg __user *)arg); | ||
| 559 | if (!rv) { | ||
| 560 | mutex_lock(&vsoc_dev.mtx); | ||
| 561 | list_add(&node->list, &vsoc_dev.permissions); | ||
| 562 | mutex_unlock(&vsoc_dev.mtx); | ||
| 563 | } else { | ||
| 564 | kfree(node); | ||
| 565 | return rv; | ||
| 566 | } | ||
| 567 | } | ||
| 568 | break; | ||
| 569 | |||
| 570 | case VSOC_GET_FD_SCOPED_PERMISSION: | ||
| 571 | { | ||
| 572 | struct fd_scoped_permission_node *node = | ||
| 573 | ((struct vsoc_private_data *)filp->private_data)-> | ||
| 574 | fd_scoped_permission_node; | ||
| 575 | if (!node) | ||
| 576 | return -ENOENT; | ||
| 577 | if (copy_to_user | ||
| 578 | ((struct fd_scoped_permission __user *)arg, | ||
| 579 | &node->permission, sizeof(node->permission))) | ||
| 580 | return -EFAULT; | ||
| 581 | } | ||
| 582 | break; | ||
| 583 | |||
| 584 | case VSOC_MAYBE_SEND_INTERRUPT_TO_HOST: | ||
| 585 | if (!atomic_xchg(reg_data->outgoing_signalled, 1)) { | ||
| 586 | writel(reg_num, vsoc_dev.regs + DOORBELL); | ||
| 587 | return 0; | ||
| 588 | } else { | ||
| 589 | return -EBUSY; | ||
| 590 | } | ||
| 591 | break; | ||
| 592 | |||
| 593 | case VSOC_SEND_INTERRUPT_TO_HOST: | ||
| 594 | writel(reg_num, vsoc_dev.regs + DOORBELL); | ||
| 595 | return 0; | ||
| 596 | case VSOC_WAIT_FOR_INCOMING_INTERRUPT: | ||
| 597 | wait_event_interruptible | ||
| 598 | (reg_data->interrupt_wait_queue, | ||
| 599 | (atomic_read(reg_data->incoming_signalled) != 0)); | ||
| 600 | break; | ||
| 601 | |||
| 602 | case VSOC_DESCRIBE_REGION: | ||
| 603 | return do_vsoc_describe_region | ||
| 604 | (filp, | ||
| 605 | (struct vsoc_device_region __user *)arg); | ||
| 606 | |||
| 607 | case VSOC_SELF_INTERRUPT: | ||
| 608 | atomic_set(reg_data->incoming_signalled, 1); | ||
| 609 | wake_up_interruptible(®_data->interrupt_wait_queue); | ||
| 610 | break; | ||
| 611 | |||
| 612 | case VSOC_COND_WAIT: | ||
| 613 | return do_vsoc_cond_wait(filp, | ||
| 614 | (struct vsoc_cond_wait __user *)arg); | ||
| 615 | case VSOC_COND_WAKE: | ||
| 616 | return do_vsoc_cond_wake(filp, arg); | ||
| 617 | |||
| 618 | default: | ||
| 619 | return -EINVAL; | ||
| 620 | } | ||
| 621 | return 0; | ||
| 622 | } | ||
| 623 | |||
| 624 | static ssize_t vsoc_read(struct file *filp, char __user *buffer, size_t len, | ||
| 625 | loff_t *poffset) | ||
| 626 | { | ||
| 627 | __u32 area_off; | ||
| 628 | const void *area_p; | ||
| 629 | ssize_t area_len; | ||
| 630 | int retval = vsoc_validate_filep(filp); | ||
| 631 | |||
| 632 | if (retval) | ||
| 633 | return retval; | ||
| 634 | area_len = vsoc_get_area(filp, &area_off); | ||
| 635 | area_p = shm_off_to_virtual_addr(area_off); | ||
| 636 | area_p += *poffset; | ||
| 637 | area_len -= *poffset; | ||
| 638 | if (area_len <= 0) | ||
| 639 | return 0; | ||
| 640 | if (area_len < len) | ||
| 641 | len = area_len; | ||
| 642 | if (copy_to_user(buffer, area_p, len)) | ||
| 643 | return -EFAULT; | ||
| 644 | *poffset += len; | ||
| 645 | return len; | ||
| 646 | } | ||
| 647 | |||
| 648 | static loff_t vsoc_lseek(struct file *filp, loff_t offset, int origin) | ||
| 649 | { | ||
| 650 | ssize_t area_len = 0; | ||
| 651 | int retval = vsoc_validate_filep(filp); | ||
| 652 | |||
| 653 | if (retval) | ||
| 654 | return retval; | ||
| 655 | area_len = vsoc_get_area(filp, NULL); | ||
| 656 | switch (origin) { | ||
| 657 | case SEEK_SET: | ||
| 658 | break; | ||
| 659 | |||
| 660 | case SEEK_CUR: | ||
| 661 | if (offset > 0 && offset + filp->f_pos < 0) | ||
| 662 | return -EOVERFLOW; | ||
| 663 | offset += filp->f_pos; | ||
| 664 | break; | ||
| 665 | |||
| 666 | case SEEK_END: | ||
| 667 | if (offset > 0 && offset + area_len < 0) | ||
| 668 | return -EOVERFLOW; | ||
| 669 | offset += area_len; | ||
| 670 | break; | ||
| 671 | |||
| 672 | case SEEK_DATA: | ||
| 673 | if (offset >= area_len) | ||
| 674 | return -EINVAL; | ||
| 675 | if (offset < 0) | ||
| 676 | offset = 0; | ||
| 677 | break; | ||
| 678 | |||
| 679 | case SEEK_HOLE: | ||
| 680 | /* Next hole is always the end of the region, unless offset is | ||
| 681 | * beyond that | ||
| 682 | */ | ||
| 683 | if (offset < area_len) | ||
| 684 | offset = area_len; | ||
| 685 | break; | ||
| 686 | |||
| 687 | default: | ||
| 688 | return -EINVAL; | ||
| 689 | } | ||
| 690 | |||
| 691 | if (offset < 0 || offset > area_len) | ||
| 692 | return -EINVAL; | ||
| 693 | filp->f_pos = offset; | ||
| 694 | |||
| 695 | return offset; | ||
| 696 | } | ||
| 697 | |||
| 698 | static ssize_t vsoc_write(struct file *filp, const char __user *buffer, | ||
| 699 | size_t len, loff_t *poffset) | ||
| 700 | { | ||
| 701 | __u32 area_off; | ||
| 702 | void *area_p; | ||
| 703 | ssize_t area_len; | ||
| 704 | int retval = vsoc_validate_filep(filp); | ||
| 705 | |||
| 706 | if (retval) | ||
| 707 | return retval; | ||
| 708 | area_len = vsoc_get_area(filp, &area_off); | ||
| 709 | area_p = shm_off_to_virtual_addr(area_off); | ||
| 710 | area_p += *poffset; | ||
| 711 | area_len -= *poffset; | ||
| 712 | if (area_len <= 0) | ||
| 713 | return 0; | ||
| 714 | if (area_len < len) | ||
| 715 | len = area_len; | ||
| 716 | if (copy_from_user(area_p, buffer, len)) | ||
| 717 | return -EFAULT; | ||
| 718 | *poffset += len; | ||
| 719 | return len; | ||
| 720 | } | ||
| 721 | |||
| 722 | static irqreturn_t vsoc_interrupt(int irq, void *region_data_v) | ||
| 723 | { | ||
| 724 | struct vsoc_region_data *region_data = | ||
| 725 | (struct vsoc_region_data *)region_data_v; | ||
| 726 | int reg_num = region_data - vsoc_dev.regions_data; | ||
| 727 | |||
| 728 | if (unlikely(!region_data)) | ||
| 729 | return IRQ_NONE; | ||
| 730 | |||
| 731 | if (unlikely(reg_num < 0 || | ||
| 732 | reg_num >= vsoc_dev.layout->region_count)) { | ||
| 733 | dev_err(&vsoc_dev.dev->dev, | ||
| 734 | "invalid irq @%p reg_num=0x%04x\n", | ||
| 735 | region_data, reg_num); | ||
| 736 | return IRQ_NONE; | ||
| 737 | } | ||
| 738 | if (unlikely(vsoc_dev.regions_data + reg_num != region_data)) { | ||
| 739 | dev_err(&vsoc_dev.dev->dev, | ||
| 740 | "irq not aligned @%p reg_num=0x%04x\n", | ||
| 741 | region_data, reg_num); | ||
| 742 | return IRQ_NONE; | ||
| 743 | } | ||
| 744 | wake_up_interruptible(®ion_data->interrupt_wait_queue); | ||
| 745 | return IRQ_HANDLED; | ||
| 746 | } | ||
| 747 | |||
| 748 | static int vsoc_probe_device(struct pci_dev *pdev, | ||
| 749 | const struct pci_device_id *ent) | ||
| 750 | { | ||
| 751 | int result; | ||
| 752 | int i; | ||
| 753 | resource_size_t reg_size; | ||
| 754 | dev_t devt; | ||
| 755 | |||
| 756 | vsoc_dev.dev = pdev; | ||
| 757 | result = pci_enable_device(pdev); | ||
| 758 | if (result) { | ||
| 759 | dev_err(&pdev->dev, | ||
| 760 | "pci_enable_device failed %s: error %d\n", | ||
| 761 | pci_name(pdev), result); | ||
| 762 | return result; | ||
| 763 | } | ||
| 764 | vsoc_dev.enabled_device = true; | ||
| 765 | result = pci_request_regions(pdev, "vsoc"); | ||
| 766 | if (result < 0) { | ||
| 767 | dev_err(&pdev->dev, "pci_request_regions failed\n"); | ||
| 768 | vsoc_remove_device(pdev); | ||
| 769 | return -EBUSY; | ||
| 770 | } | ||
| 771 | vsoc_dev.requested_regions = true; | ||
| 772 | /* Set up the control registers in BAR 0 */ | ||
| 773 | reg_size = pci_resource_len(pdev, REGISTER_BAR); | ||
| 774 | if (reg_size > MAX_REGISTER_BAR_LEN) | ||
| 775 | vsoc_dev.regs = | ||
| 776 | pci_iomap(pdev, REGISTER_BAR, MAX_REGISTER_BAR_LEN); | ||
| 777 | else | ||
| 778 | vsoc_dev.regs = pci_iomap(pdev, REGISTER_BAR, reg_size); | ||
| 779 | |||
| 780 | if (!vsoc_dev.regs) { | ||
| 781 | dev_err(&pdev->dev, | ||
| 782 | "cannot map registers of size %zu\n", | ||
| 783 | (size_t)reg_size); | ||
| 784 | vsoc_remove_device(pdev); | ||
| 785 | return -EBUSY; | ||
| 786 | } | ||
| 787 | |||
| 788 | /* Map the shared memory in BAR 2 */ | ||
| 789 | vsoc_dev.shm_phys_start = pci_resource_start(pdev, SHARED_MEMORY_BAR); | ||
| 790 | vsoc_dev.shm_size = pci_resource_len(pdev, SHARED_MEMORY_BAR); | ||
| 791 | |||
| 792 | dev_info(&pdev->dev, "shared memory @ DMA %pa size=0x%zx\n", | ||
| 793 | &vsoc_dev.shm_phys_start, vsoc_dev.shm_size); | ||
| 794 | vsoc_dev.kernel_mapped_shm = pci_iomap_wc(pdev, SHARED_MEMORY_BAR, 0); | ||
| 795 | if (!vsoc_dev.kernel_mapped_shm) { | ||
| 796 | dev_err(&vsoc_dev.dev->dev, "cannot iomap region\n"); | ||
| 797 | vsoc_remove_device(pdev); | ||
| 798 | return -EBUSY; | ||
| 799 | } | ||
| 800 | |||
| 801 | vsoc_dev.layout = (struct vsoc_shm_layout_descriptor __force *) | ||
| 802 | vsoc_dev.kernel_mapped_shm; | ||
| 803 | dev_info(&pdev->dev, "major_version: %d\n", | ||
| 804 | vsoc_dev.layout->major_version); | ||
| 805 | dev_info(&pdev->dev, "minor_version: %d\n", | ||
| 806 | vsoc_dev.layout->minor_version); | ||
| 807 | dev_info(&pdev->dev, "size: 0x%x\n", vsoc_dev.layout->size); | ||
| 808 | dev_info(&pdev->dev, "regions: %d\n", vsoc_dev.layout->region_count); | ||
| 809 | if (vsoc_dev.layout->major_version != | ||
| 810 | CURRENT_VSOC_LAYOUT_MAJOR_VERSION) { | ||
| 811 | dev_err(&vsoc_dev.dev->dev, | ||
| 812 | "driver supports only major_version %d\n", | ||
| 813 | CURRENT_VSOC_LAYOUT_MAJOR_VERSION); | ||
| 814 | vsoc_remove_device(pdev); | ||
| 815 | return -EBUSY; | ||
| 816 | } | ||
| 817 | result = alloc_chrdev_region(&devt, 0, vsoc_dev.layout->region_count, | ||
| 818 | VSOC_DEV_NAME); | ||
| 819 | if (result) { | ||
| 820 | dev_err(&vsoc_dev.dev->dev, "alloc_chrdev_region failed\n"); | ||
| 821 | vsoc_remove_device(pdev); | ||
| 822 | return -EBUSY; | ||
| 823 | } | ||
| 824 | vsoc_dev.major = MAJOR(devt); | ||
| 825 | cdev_init(&vsoc_dev.cdev, &vsoc_ops); | ||
| 826 | vsoc_dev.cdev.owner = THIS_MODULE; | ||
| 827 | result = cdev_add(&vsoc_dev.cdev, devt, vsoc_dev.layout->region_count); | ||
| 828 | if (result) { | ||
| 829 | dev_err(&vsoc_dev.dev->dev, "cdev_add error\n"); | ||
| 830 | vsoc_remove_device(pdev); | ||
| 831 | return -EBUSY; | ||
| 832 | } | ||
| 833 | vsoc_dev.cdev_added = true; | ||
| 834 | vsoc_dev.class = class_create(THIS_MODULE, VSOC_DEV_NAME); | ||
| 835 | if (IS_ERR(vsoc_dev.class)) { | ||
| 836 | dev_err(&vsoc_dev.dev->dev, "class_create failed\n"); | ||
| 837 | vsoc_remove_device(pdev); | ||
| 838 | return PTR_ERR(vsoc_dev.class); | ||
| 839 | } | ||
| 840 | vsoc_dev.class_added = true; | ||
| 841 | vsoc_dev.regions = (struct vsoc_device_region __force *) | ||
| 842 | ((void *)vsoc_dev.layout + | ||
| 843 | vsoc_dev.layout->vsoc_region_desc_offset); | ||
| 844 | vsoc_dev.msix_entries = | ||
| 845 | kcalloc(vsoc_dev.layout->region_count, | ||
| 846 | sizeof(vsoc_dev.msix_entries[0]), GFP_KERNEL); | ||
| 847 | if (!vsoc_dev.msix_entries) { | ||
| 848 | dev_err(&vsoc_dev.dev->dev, | ||
| 849 | "unable to allocate msix_entries\n"); | ||
| 850 | vsoc_remove_device(pdev); | ||
| 851 | return -ENOSPC; | ||
| 852 | } | ||
| 853 | vsoc_dev.regions_data = | ||
| 854 | kcalloc(vsoc_dev.layout->region_count, | ||
| 855 | sizeof(vsoc_dev.regions_data[0]), GFP_KERNEL); | ||
| 856 | if (!vsoc_dev.regions_data) { | ||
| 857 | dev_err(&vsoc_dev.dev->dev, | ||
| 858 | "unable to allocate regions' data\n"); | ||
| 859 | vsoc_remove_device(pdev); | ||
| 860 | return -ENOSPC; | ||
| 861 | } | ||
| 862 | for (i = 0; i < vsoc_dev.layout->region_count; ++i) | ||
| 863 | vsoc_dev.msix_entries[i].entry = i; | ||
| 864 | |||
| 865 | result = pci_enable_msix_exact(vsoc_dev.dev, vsoc_dev.msix_entries, | ||
| 866 | vsoc_dev.layout->region_count); | ||
| 867 | if (result) { | ||
| 868 | dev_info(&pdev->dev, "pci_enable_msix failed: %d\n", result); | ||
| 869 | vsoc_remove_device(pdev); | ||
| 870 | return -ENOSPC; | ||
| 871 | } | ||
| 872 | /* Check that all regions are well formed */ | ||
| 873 | for (i = 0; i < vsoc_dev.layout->region_count; ++i) { | ||
| 874 | const struct vsoc_device_region *region = vsoc_dev.regions + i; | ||
| 875 | |||
| 876 | if (!PAGE_ALIGNED(region->region_begin_offset) || | ||
| 877 | !PAGE_ALIGNED(region->region_end_offset)) { | ||
| 878 | dev_err(&vsoc_dev.dev->dev, | ||
| 879 | "region %d not aligned (%x:%x)", i, | ||
| 880 | region->region_begin_offset, | ||
| 881 | region->region_end_offset); | ||
| 882 | vsoc_remove_device(pdev); | ||
| 883 | return -EFAULT; | ||
| 884 | } | ||
| 885 | if (region->region_begin_offset >= region->region_end_offset || | ||
| 886 | region->region_end_offset > vsoc_dev.shm_size) { | ||
| 887 | dev_err(&vsoc_dev.dev->dev, | ||
| 888 | "region %d offsets are wrong: %x %x %zx", | ||
| 889 | i, region->region_begin_offset, | ||
| 890 | region->region_end_offset, vsoc_dev.shm_size); | ||
| 891 | vsoc_remove_device(pdev); | ||
| 892 | return -EFAULT; | ||
| 893 | } | ||
| 894 | if (region->managed_by >= vsoc_dev.layout->region_count) { | ||
| 895 | dev_err(&vsoc_dev.dev->dev, | ||
| 896 | "region %d has invalid owner: %u", | ||
| 897 | i, region->managed_by); | ||
| 898 | vsoc_remove_device(pdev); | ||
| 899 | return -EFAULT; | ||
| 900 | } | ||
| 901 | } | ||
| 902 | vsoc_dev.msix_enabled = true; | ||
| 903 | for (i = 0; i < vsoc_dev.layout->region_count; ++i) { | ||
| 904 | const struct vsoc_device_region *region = vsoc_dev.regions + i; | ||
| 905 | size_t name_sz = sizeof(vsoc_dev.regions_data[i].name) - 1; | ||
| 906 | const struct vsoc_signal_table_layout *h_to_g_signal_table = | ||
| 907 | ®ion->host_to_guest_signal_table; | ||
| 908 | const struct vsoc_signal_table_layout *g_to_h_signal_table = | ||
| 909 | ®ion->guest_to_host_signal_table; | ||
| 910 | |||
| 911 | vsoc_dev.regions_data[i].name[name_sz] = '\0'; | ||
| 912 | memcpy(vsoc_dev.regions_data[i].name, region->device_name, | ||
| 913 | name_sz); | ||
| 914 | dev_info(&pdev->dev, "region %d name=%s\n", | ||
| 915 | i, vsoc_dev.regions_data[i].name); | ||
| 916 | init_waitqueue_head | ||
| 917 | (&vsoc_dev.regions_data[i].interrupt_wait_queue); | ||
| 918 | init_waitqueue_head(&vsoc_dev.regions_data[i].futex_wait_queue); | ||
| 919 | vsoc_dev.regions_data[i].incoming_signalled = | ||
| 920 | shm_off_to_virtual_addr(region->region_begin_offset) + | ||
| 921 | h_to_g_signal_table->interrupt_signalled_offset; | ||
| 922 | vsoc_dev.regions_data[i].outgoing_signalled = | ||
| 923 | shm_off_to_virtual_addr(region->region_begin_offset) + | ||
| 924 | g_to_h_signal_table->interrupt_signalled_offset; | ||
| 925 | result = request_irq(vsoc_dev.msix_entries[i].vector, | ||
| 926 | vsoc_interrupt, 0, | ||
| 927 | vsoc_dev.regions_data[i].name, | ||
| 928 | vsoc_dev.regions_data + i); | ||
| 929 | if (result) { | ||
| 930 | dev_info(&pdev->dev, | ||
| 931 | "request_irq failed irq=%d vector=%d\n", | ||
| 932 | i, vsoc_dev.msix_entries[i].vector); | ||
| 933 | vsoc_remove_device(pdev); | ||
| 934 | return -ENOSPC; | ||
| 935 | } | ||
| 936 | vsoc_dev.regions_data[i].irq_requested = true; | ||
| 937 | if (!device_create(vsoc_dev.class, NULL, | ||
| 938 | MKDEV(vsoc_dev.major, i), | ||
| 939 | NULL, vsoc_dev.regions_data[i].name)) { | ||
| 940 | dev_err(&vsoc_dev.dev->dev, "device_create failed\n"); | ||
| 941 | vsoc_remove_device(pdev); | ||
| 942 | return -EBUSY; | ||
| 943 | } | ||
| 944 | vsoc_dev.regions_data[i].device_created = true; | ||
| 945 | } | ||
| 946 | return 0; | ||
| 947 | } | ||
| 948 | |||
| 949 | /* | ||
| 950 | * This should undo all of the allocations in the probe function in reverse | ||
| 951 | * order. | ||
| 952 | * | ||
| 953 | * Notes: | ||
| 954 | * | ||
| 955 | * The device may have been partially initialized, so double check | ||
| 956 | * that the allocations happened. | ||
| 957 | * | ||
| 958 | * This function may be called multiple times, so mark resources as freed | ||
| 959 | * as they are deallocated. | ||
| 960 | */ | ||
| 961 | static void vsoc_remove_device(struct pci_dev *pdev) | ||
| 962 | { | ||
| 963 | int i; | ||
| 964 | /* | ||
| 965 | * pdev is the first thing to be set on probe and the last thing | ||
| 966 | * to be cleared here. If it's NULL then there is no cleanup. | ||
| 967 | */ | ||
| 968 | if (!pdev || !vsoc_dev.dev) | ||
| 969 | return; | ||
| 970 | dev_info(&pdev->dev, "remove_device\n"); | ||
| 971 | if (vsoc_dev.regions_data) { | ||
| 972 | for (i = 0; i < vsoc_dev.layout->region_count; ++i) { | ||
| 973 | if (vsoc_dev.regions_data[i].device_created) { | ||
| 974 | device_destroy(vsoc_dev.class, | ||
| 975 | MKDEV(vsoc_dev.major, i)); | ||
| 976 | vsoc_dev.regions_data[i].device_created = false; | ||
| 977 | } | ||
| 978 | if (vsoc_dev.regions_data[i].irq_requested) | ||
| 979 | free_irq(vsoc_dev.msix_entries[i].vector, NULL); | ||
| 980 | vsoc_dev.regions_data[i].irq_requested = false; | ||
| 981 | } | ||
| 982 | kfree(vsoc_dev.regions_data); | ||
| 983 | vsoc_dev.regions_data = NULL; | ||
| 984 | } | ||
| 985 | if (vsoc_dev.msix_enabled) { | ||
| 986 | pci_disable_msix(pdev); | ||
| 987 | vsoc_dev.msix_enabled = false; | ||
| 988 | } | ||
| 989 | kfree(vsoc_dev.msix_entries); | ||
| 990 | vsoc_dev.msix_entries = NULL; | ||
| 991 | vsoc_dev.regions = NULL; | ||
| 992 | if (vsoc_dev.class_added) { | ||
| 993 | class_destroy(vsoc_dev.class); | ||
| 994 | vsoc_dev.class_added = false; | ||
| 995 | } | ||
| 996 | if (vsoc_dev.cdev_added) { | ||
| 997 | cdev_del(&vsoc_dev.cdev); | ||
| 998 | vsoc_dev.cdev_added = false; | ||
| 999 | } | ||
| 1000 | if (vsoc_dev.major && vsoc_dev.layout) { | ||
| 1001 | unregister_chrdev_region(MKDEV(vsoc_dev.major, 0), | ||
| 1002 | vsoc_dev.layout->region_count); | ||
| 1003 | vsoc_dev.major = 0; | ||
| 1004 | } | ||
| 1005 | vsoc_dev.layout = NULL; | ||
| 1006 | if (vsoc_dev.kernel_mapped_shm) { | ||
| 1007 | pci_iounmap(pdev, vsoc_dev.kernel_mapped_shm); | ||
| 1008 | vsoc_dev.kernel_mapped_shm = NULL; | ||
| 1009 | } | ||
| 1010 | if (vsoc_dev.regs) { | ||
| 1011 | pci_iounmap(pdev, vsoc_dev.regs); | ||
| 1012 | vsoc_dev.regs = NULL; | ||
| 1013 | } | ||
| 1014 | if (vsoc_dev.requested_regions) { | ||
| 1015 | pci_release_regions(pdev); | ||
| 1016 | vsoc_dev.requested_regions = false; | ||
| 1017 | } | ||
| 1018 | if (vsoc_dev.enabled_device) { | ||
| 1019 | pci_disable_device(pdev); | ||
| 1020 | vsoc_dev.enabled_device = false; | ||
| 1021 | } | ||
| 1022 | /* Do this last: it indicates that the device is not initialized. */ | ||
| 1023 | vsoc_dev.dev = NULL; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | static void __exit vsoc_cleanup_module(void) | ||
| 1027 | { | ||
| 1028 | vsoc_remove_device(vsoc_dev.dev); | ||
| 1029 | pci_unregister_driver(&vsoc_pci_driver); | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | static int __init vsoc_init_module(void) | ||
| 1033 | { | ||
| 1034 | int err = -ENOMEM; | ||
| 1035 | |||
| 1036 | INIT_LIST_HEAD(&vsoc_dev.permissions); | ||
| 1037 | mutex_init(&vsoc_dev.mtx); | ||
| 1038 | |||
| 1039 | err = pci_register_driver(&vsoc_pci_driver); | ||
| 1040 | if (err < 0) | ||
| 1041 | return err; | ||
| 1042 | return 0; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | static int vsoc_open(struct inode *inode, struct file *filp) | ||
| 1046 | { | ||
| 1047 | /* Can't use vsoc_validate_filep because filp is still incomplete */ | ||
| 1048 | int ret = vsoc_validate_inode(inode); | ||
| 1049 | |||
| 1050 | if (ret) | ||
| 1051 | return ret; | ||
| 1052 | filp->private_data = | ||
| 1053 | kzalloc(sizeof(struct vsoc_private_data), GFP_KERNEL); | ||
| 1054 | if (!filp->private_data) | ||
| 1055 | return -ENOMEM; | ||
| 1056 | return 0; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | static int vsoc_release(struct inode *inode, struct file *filp) | ||
| 1060 | { | ||
| 1061 | struct vsoc_private_data *private_data = NULL; | ||
| 1062 | struct fd_scoped_permission_node *node = NULL; | ||
| 1063 | struct vsoc_device_region *owner_region_p = NULL; | ||
| 1064 | int retval = vsoc_validate_filep(filp); | ||
| 1065 | |||
| 1066 | if (retval) | ||
| 1067 | return retval; | ||
| 1068 | private_data = (struct vsoc_private_data *)filp->private_data; | ||
| 1069 | if (!private_data) | ||
| 1070 | return 0; | ||
| 1071 | |||
| 1072 | node = private_data->fd_scoped_permission_node; | ||
| 1073 | if (node) { | ||
| 1074 | owner_region_p = vsoc_region_from_inode(inode); | ||
| 1075 | if (owner_region_p->managed_by != VSOC_REGION_WHOLE) { | ||
| 1076 | owner_region_p = | ||
| 1077 | &vsoc_dev.regions[owner_region_p->managed_by]; | ||
| 1078 | } | ||
| 1079 | do_destroy_fd_scoped_permission_node(owner_region_p, node); | ||
| 1080 | private_data->fd_scoped_permission_node = NULL; | ||
| 1081 | } | ||
| 1082 | kfree(private_data); | ||
| 1083 | filp->private_data = NULL; | ||
| 1084 | |||
| 1085 | return 0; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | /* | ||
| 1089 | * Returns the device relative offset and length of the area specified by the | ||
| 1090 | * fd scoped permission. If there is no fd scoped permission set, a default | ||
| 1091 | * permission covering the entire region is assumed, unless the region is owned | ||
| 1092 | * by another one, in which case the default is a permission with zero size. | ||
| 1093 | */ | ||
| 1094 | static ssize_t vsoc_get_area(struct file *filp, __u32 *area_offset) | ||
| 1095 | { | ||
| 1096 | __u32 off = 0; | ||
| 1097 | ssize_t length = 0; | ||
| 1098 | struct vsoc_device_region *region_p; | ||
| 1099 | struct fd_scoped_permission *perm; | ||
| 1100 | |||
| 1101 | region_p = vsoc_region_from_filep(filp); | ||
| 1102 | off = region_p->region_begin_offset; | ||
| 1103 | perm = &((struct vsoc_private_data *)filp->private_data)-> | ||
| 1104 | fd_scoped_permission_node->permission; | ||
| 1105 | if (perm) { | ||
| 1106 | off += perm->begin_offset; | ||
| 1107 | length = perm->end_offset - perm->begin_offset; | ||
| 1108 | } else if (region_p->managed_by == VSOC_REGION_WHOLE) { | ||
| 1109 | /* No permission set and the regions is not owned by another, | ||
| 1110 | * default to full region access. | ||
| 1111 | */ | ||
| 1112 | length = vsoc_device_region_size(region_p); | ||
| 1113 | } else { | ||
| 1114 | /* return zero length, access is denied. */ | ||
| 1115 | length = 0; | ||
| 1116 | } | ||
| 1117 | if (area_offset) | ||
| 1118 | *area_offset = off; | ||
| 1119 | return length; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | static int vsoc_mmap(struct file *filp, struct vm_area_struct *vma) | ||
| 1123 | { | ||
| 1124 | unsigned long len = vma->vm_end - vma->vm_start; | ||
| 1125 | __u32 area_off; | ||
| 1126 | phys_addr_t mem_off; | ||
| 1127 | ssize_t area_len; | ||
| 1128 | int retval = vsoc_validate_filep(filp); | ||
| 1129 | |||
| 1130 | if (retval) | ||
| 1131 | return retval; | ||
| 1132 | area_len = vsoc_get_area(filp, &area_off); | ||
| 1133 | /* Add the requested offset */ | ||
| 1134 | area_off += (vma->vm_pgoff << PAGE_SHIFT); | ||
| 1135 | area_len -= (vma->vm_pgoff << PAGE_SHIFT); | ||
| 1136 | if (area_len < len) | ||
| 1137 | return -EINVAL; | ||
| 1138 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
| 1139 | mem_off = shm_off_to_phys_addr(area_off); | ||
| 1140 | if (io_remap_pfn_range(vma, vma->vm_start, mem_off >> PAGE_SHIFT, | ||
| 1141 | len, vma->vm_page_prot)) | ||
| 1142 | return -EAGAIN; | ||
| 1143 | return 0; | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | module_init(vsoc_init_module); | ||
| 1147 | module_exit(vsoc_cleanup_module); | ||
| 1148 | |||
| 1149 | MODULE_LICENSE("GPL"); | ||
| 1150 | MODULE_AUTHOR("Greg Hartman <ghartman@google.com>"); | ||
| 1151 | MODULE_DESCRIPTION("VSoC interpretation of QEmu's ivshmem device"); | ||
| 1152 | MODULE_VERSION("1.0"); | ||
| @@ -1,3 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Staging board support for Armadillo 800 eva. | 3 | * Staging board support for Armadillo 800 eva. |
| 3 | * Enable not-yet-DT-capable devices here. | 4 | * Enable not-yet-DT-capable devices here. |
| @@ -6,15 +7,6 @@ · 2 unmodified lines | |||
| 6 | * | 7 | * |
| 7 | * Copyright (C) 2012 Renesas Solutions Corp. | 8 | * Copyright (C) 2012 Renesas Solutions Corp. |
| 8 | * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 9 | * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; version 2 of the License. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | */ | 10 | */ |
| 19 | 11 | ||
| 20 | #include <linux/dma-mapping.h> | 12 | #include <linux/dma-mapping.h> |
| @@ -1,10 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (C) 2014 Magnus Damm | 3 | * Copyright (C) 2014 Magnus Damm |
| 3 | * Copyright (C) 2015 Glider bvba | 4 | * Copyright (C) 2015 Glider bvba |
| 4 | * | ||
| 5 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 6 | * License. See the file "COPYING" in the main directory of this archive | ||
| 7 | * for more details. | ||
| 8 | */ | 5 | */ |
| 9 | 6 | ||
| 10 | #define pr_fmt(fmt) "board_staging: " fmt | 7 | #define pr_fmt(fmt) "board_staging: " fmt |
| @@ -34,7 +34,7 @@ · 33 unmodified lines | |||
| 34 | struct comedi32_chaninfo_struct { | 34 | struct comedi32_chaninfo_struct { |
| 35 | unsigned int subdev; | 35 | unsigned int subdev; |
| 36 | compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */ | 36 | compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */ |
| 37 | compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */ | 37 | compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */ |
| 38 | compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */ | 38 | compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */ |
| 39 | unsigned int unused[4]; | 39 | unsigned int unused[4]; |
| 40 | }; | 40 | }; |
| @@ -57,16 +57,16 @@ struct comedi32_cmd_struct { · 16 unmodified lines | |||
| 57 | unsigned int scan_end_arg; | 57 | unsigned int scan_end_arg; |
| 58 | unsigned int stop_src; | 58 | unsigned int stop_src; |
| 59 | unsigned int stop_arg; | 59 | unsigned int stop_arg; |
| 60 | compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */ | 60 | compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */ |
| 61 | unsigned int chanlist_len; | 61 | unsigned int chanlist_len; |
| 62 | compat_uptr_t data; /* 32-bit 'short *' */ | 62 | compat_uptr_t data; /* 32-bit 'short *' */ |
| 63 | unsigned int data_len; | 63 | unsigned int data_len; |
| 64 | }; | 64 | }; |
| 65 | 65 | ||
| 66 | struct comedi32_insn_struct { | 66 | struct comedi32_insn_struct { |
| 67 | unsigned int insn; | 67 | unsigned int insn; |
| 68 | unsigned int n; | 68 | unsigned int n; |
| 69 | compat_uptr_t data; /* 32-bit 'unsigned int *' */ | 69 | compat_uptr_t data; /* 32-bit 'unsigned int *' */ |
| 70 | unsigned int subdev; | 70 | unsigned int subdev; |
| 71 | unsigned int chanspec; | 71 | unsigned int chanspec; |
| 72 | unsigned int unused[3]; | 72 | unsigned int unused[3]; |
| @@ -74,7 +74,7 @@ struct comedi32_insn_struct { · 1 unmodified line | |||
| 74 | 74 | ||
| 75 | struct comedi32_insnlist_struct { | 75 | struct comedi32_insnlist_struct { |
| 76 | unsigned int n_insns; | 76 | unsigned int n_insns; |
| 77 | compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ | 77 | compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ |
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | /* Handle translated ioctl. */ | 80 | /* Handle translated ioctl. */ |
| @@ -194,7 +194,7 @@ static int get_compat_cmd(struct comedi_cmd __user *cmd, · 113 unmodified lines | |||
| 194 | err |= __put_user(temp.uint, &cmd->stop_arg); | 194 | err |= __put_user(temp.uint, &cmd->stop_arg); |
| 195 | err |= __get_user(temp.uptr, &cmd32->chanlist); | 195 | err |= __get_user(temp.uptr, &cmd32->chanlist); |
| 196 | err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr), | 196 | err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr), |
| 197 | &cmd->chanlist); | 197 | &cmd->chanlist); |
| 198 | err |= __get_user(temp.uint, &cmd32->chanlist_len); | 198 | err |= __get_user(temp.uint, &cmd32->chanlist_len); |
| 199 | err |= __put_user(temp.uint, &cmd->chanlist_len); | 199 | err |= __put_user(temp.uint, &cmd->chanlist_len); |
| 200 | err |= __get_user(temp.uptr, &cmd32->data); | 200 | err |= __get_user(temp.uptr, &cmd32->data); |
| @@ -1,6 +1,5 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | /* | 2 | /* comedi_usb.h |
| 3 | * comedi_usb.h | ||
| 4 | * header file for USB Comedi drivers | 3 | * header file for USB Comedi drivers |
| 5 | * | 4 | * |
| 6 | * COMEDI - Linux Control and Measurement Device Interface | 5 | * COMEDI - Linux Control and Measurement Device Interface |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | /* | 2 | /* |
| 3 | * comedidev.h | 3 | * comedidev.h |
| 4 | * header file for kernel-only structures, variables, and constants | 4 | * header file for kernel-only structures, variables, and constants |
| @@ -2268,14 +2268,14 @@ static inline unsigned int dma_transfer_size(struct comedi_device *dev) · 2267 unmodified lines | |||
| 2268 | } | 2268 | } |
| 2269 | 2269 | ||
| 2270 | static u32 ai_convert_counter_6xxx(const struct comedi_device *dev, | 2270 | static u32 ai_convert_counter_6xxx(const struct comedi_device *dev, |
| 2271 | const struct comedi_cmd *cmd) | 2271 | const struct comedi_cmd *cmd) |
| 2272 | { | 2272 | { |
| 2273 | /* supposed to load counter with desired divisor minus 3 */ | 2273 | /* supposed to load counter with desired divisor minus 3 */ |
| 2274 | return cmd->convert_arg / TIMER_BASE - 3; | 2274 | return cmd->convert_arg / TIMER_BASE - 3; |
| 2275 | } | 2275 | } |
| 2276 | 2276 | ||
| 2277 | static u32 ai_scan_counter_6xxx(struct comedi_device *dev, | 2277 | static u32 ai_scan_counter_6xxx(struct comedi_device *dev, |
| 2278 | struct comedi_cmd *cmd) | 2278 | struct comedi_cmd *cmd) |
| 2279 | { | 2279 | { |
| 2280 | u32 count; | 2280 | u32 count; |
| 2281 | 2281 | ||
| @@ -2296,7 +2296,7 @@ static u32 ai_scan_counter_6xxx(struct comedi_device *dev, · 14 unmodified lines | |||
| 2296 | } | 2296 | } |
| 2297 | 2297 | ||
| 2298 | static u32 ai_convert_counter_4020(struct comedi_device *dev, | 2298 | static u32 ai_convert_counter_4020(struct comedi_device *dev, |
| 2299 | struct comedi_cmd *cmd) | 2299 | struct comedi_cmd *cmd) |
| 2300 | { | 2300 | { |
| 2301 | struct pcidas64_private *devpriv = dev->private; | 2301 | struct pcidas64_private *devpriv = dev->private; |
| 2302 | unsigned int divisor; | 2302 | unsigned int divisor; |
| @@ -1,6 +1,6 @@ | |||
| 1 | config USB_EMXX | 1 | config USB_EMXX |
| 2 | tristate "EMXX USB Function Device Controller" | 2 | tristate "EMXX USB Function Device Controller" |
| 3 | depends on USB_GADGET && (ARCH_SHMOBILE || (ARM && COMPILE_TEST)) | 3 | depends on USB_GADGET && (ARCH_RENESAS || (ARM && COMPILE_TEST)) |
| 4 | help | 4 | help |
| 5 | The Emma Mobile series of SoCs from Renesas Electronics and | 5 | The Emma Mobile series of SoCs from Renesas Electronics and |
| 6 | former NEC Electronics include USB Function hardware. | 6 | former NEC Electronics include USB Function hardware. |
| @@ -1,4 +1,6 @@ | |||
| 1 | * add clock framework support (platform device with CCF needs special care) | 1 | * add clock framework support (platform device with CCF needs special care) |
| 2 | * break out board-specific VBUS GPIO to work with multiplatform | 2 | * break out board-specific VBUS GPIO to work with multiplatform |
| 3 | * convert VBUS GPIO to use GPIO descriptors from <linux/gpio/consumer.h> | ||
| 4 | and stop using the old GPIO API | ||
| 3 | * DT bindings | 5 | * DT bindings |
| 4 | * move driver into drivers/usb/gadget/ | 6 | * move driver into drivers/usb/gadget/ |
| @@ -0,0 +1,4 @@ | |||
| 1 | * convert all uses of the old GPIO API from <linux/gpio.h> to the | ||
| 2 | GPIO descriptor API in <linux/gpio/consumer.h> and look up GPIO | ||
| 3 | lines from device tree, ACPI or board files, board files should | ||
| 4 | use <linux/gpio/machine.h> | ||
| @@ -22,10 +22,13 @@ void func(struct fbtft_par *par, int len, ...) \ · 21 unmodified lines | |||
| 22 | if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { \ | 22 | if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { \ |
| 23 | va_start(args, len); \ | 23 | va_start(args, len); \ |
| 24 | for (i = 0; i < len; i++) { \ | 24 | for (i = 0; i < len; i++) { \ |
| 25 | buf[i] = modifier((data_type)va_arg(args, unsigned int)); \ | 25 | buf[i] = modifier((data_type)va_arg(args, \ |
| 26 | unsigned int)); \ | ||
| 26 | } \ | 27 | } \ |
| 27 | va_end(args); \ | 28 | va_end(args); \ |
| 28 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, buffer_type, buf, len, "%s: ", __func__); \ | 29 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, \ |
| 30 | par->info->device, buffer_type, buf, len, \ | ||
| 31 | "%s: ", __func__); \ | ||
| 29 | } \ | 32 | } \ |
| 30 | \ | 33 | \ |
| 31 | va_start(args, len); \ | 34 | va_start(args, len); \ |
| @@ -37,7 +40,8 @@ void func(struct fbtft_par *par, int len, ...) \ · 5 unmodified lines | |||
| 37 | } \ | 40 | } \ |
| 38 | \ | 41 | \ |
| 39 | *buf = modifier((data_type)va_arg(args, unsigned int)); \ | 42 | *buf = modifier((data_type)va_arg(args, unsigned int)); \ |
| 40 | ret = fbtft_write_buf_dc(par, par->buf, sizeof(data_type) + offset, 0); \ | 43 | ret = fbtft_write_buf_dc(par, par->buf, sizeof(data_type) + offset, \ |
| 44 | 0); \ | ||
| 41 | if (ret < 0) \ | 45 | if (ret < 0) \ |
| 42 | goto out; \ | 46 | goto out; \ |
| 43 | len--; \ | 47 | len--; \ |
| @@ -48,7 +52,8 @@ void func(struct fbtft_par *par, int len, ...) \ · 4 unmodified lines | |||
| 48 | if (len) { \ | 52 | if (len) { \ |
| 49 | i = len; \ | 53 | i = len; \ |
| 50 | while (i--) \ | 54 | while (i--) \ |
| 51 | *buf++ = modifier((data_type)va_arg(args, unsigned int)); \ | 55 | *buf++ = modifier((data_type)va_arg(args, \ |
| 56 | unsigned int)); \ | ||
| 52 | fbtft_write_buf_dc(par, par->buf, \ | 57 | fbtft_write_buf_dc(par, par->buf, \ |
| 53 | len * (sizeof(data_type) + offset), 1); \ | 58 | len * (sizeof(data_type) + offset), 1); \ |
| 54 | } \ | 59 | } \ |
| @@ -24,3 +24,11 @@ config FSL_DPAA2_ETHSW · 23 unmodified lines | |||
| 24 | ---help--- | 24 | ---help--- |
| 25 | Driver for Freescale DPAA2 Ethernet Switch. Select | 25 | Driver for Freescale DPAA2 Ethernet Switch. Select |
| 26 | BRIDGE to have support for bridge tools. | 26 | BRIDGE to have support for bridge tools. |
| 27 | |||
| 28 | config FSL_DPAA2_PTP_CLOCK | ||
| 29 | tristate "Freescale DPAA2 PTP Clock" | ||
| 30 | depends on FSL_DPAA2_ETH && POSIX_TIMERS | ||
| 31 | select PTP_1588_CLOCK | ||
| 32 | help | ||
| 33 | This driver adds support for using the DPAA2 1588 timer module | ||
| 34 | as a PTP clock. | ||
| @@ -2,5 +2,6 @@ · 1 unmodified line | |||
| 2 | # Freescale DataPath Acceleration Architecture Gen2 (DPAA2) drivers | 2 | # Freescale DataPath Acceleration Architecture Gen2 (DPAA2) drivers |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_FSL_DPAA2_ETH) += ethernet/ | 5 | obj-$(CONFIG_FSL_DPAA2_ETH) += ethernet/ |
| 6 | obj-$(CONFIG_FSL_DPAA2_ETHSW) += ethsw/ | 6 | obj-$(CONFIG_FSL_DPAA2_ETHSW) += ethsw/ |
| 7 | obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK) += rtc/ | ||
| @@ -38,8 +38,11 @@ · 37 unmodified lines | |||
| 38 | #include <linux/msi.h> | 38 | #include <linux/msi.h> |
| 39 | #include <linux/kthread.h> | 39 | #include <linux/kthread.h> |
| 40 | #include <linux/iommu.h> | 40 | #include <linux/iommu.h> |
| 41 | 41 | #include <linux/net_tstamp.h> | |
| 42 | #include <linux/fsl/mc.h> | 42 | #include <linux/fsl/mc.h> |
| 43 | |||
| 44 | #include <net/sock.h> | ||
| 45 | |||
| 43 | #include "dpaa2-eth.h" | 46 | #include "dpaa2-eth.h" |
| 44 | 47 | ||
| 45 | /* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files | 48 | /* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files |
| @@ -275,6 +278,18 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, · 229 unmodified lines | |||
| 275 | 278 | ||
| 276 | prefetch(skb->data); | 279 | prefetch(skb->data); |
| 277 | 280 | ||
| 281 | /* Get the timestamp value */ | ||
| 282 | if (priv->rx_tstamp) { | ||
| 283 | struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); | ||
| 284 | __le64 *ts = dpaa2_get_ts(vaddr, false); | ||
| 285 | u64 ns; | ||
| 286 | |||
| 287 | memset(shhwtstamps, 0, sizeof(*shhwtstamps)); | ||
| 288 | |||
| 289 | ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); | ||
| 290 | shhwtstamps->hwtstamp = ns_to_ktime(ns); | ||
| 291 | } | ||
| 292 | |||
| 278 | /* Check if we need to validate the L4 csum */ | 293 | /* Check if we need to validate the L4 csum */ |
| 279 | if (likely(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV)) { | 294 | if (likely(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV)) { |
| 280 | status = le32_to_cpu(fas->status); | 295 | status = le32_to_cpu(fas->status); |
| @@ -334,6 +349,28 @@ static int consume_frames(struct dpaa2_eth_channel *ch) · 53 unmodified lines | |||
| 334 | return cleaned; | 349 | return cleaned; |
| 335 | } | 350 | } |
| 336 | 351 | ||
| 352 | /* Configure the egress frame annotation for timestamp update */ | ||
| 353 | static void enable_tx_tstamp(struct dpaa2_fd *fd, void *buf_start) | ||
| 354 | { | ||
| 355 | struct dpaa2_faead *faead; | ||
| 356 | u32 ctrl, frc; | ||
| 357 | |||
| 358 | /* Mark the egress frame annotation area as valid */ | ||
| 359 | frc = dpaa2_fd_get_frc(fd); | ||
| 360 | dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV); | ||
| 361 | |||
| 362 | /* Set hardware annotation size */ | ||
| 363 | ctrl = dpaa2_fd_get_ctrl(fd); | ||
| 364 | dpaa2_fd_set_ctrl(fd, ctrl | DPAA2_FD_CTRL_ASAL); | ||
| 365 | |||
| 366 | /* enable UPD (update prepanded data) bit in FAEAD field of | ||
| 367 | * hardware frame annotation area | ||
| 368 | */ | ||
| 369 | ctrl = DPAA2_FAEAD_A2V | DPAA2_FAEAD_UPDV | DPAA2_FAEAD_UPD; | ||
| 370 | faead = dpaa2_get_faead(buf_start, true); | ||
| 371 | faead->ctrl = cpu_to_le32(ctrl); | ||
| 372 | } | ||
| 373 | |||
| 337 | /* Create a frame descriptor based on a fragmented skb */ | 374 | /* Create a frame descriptor based on a fragmented skb */ |
| 338 | static int build_sg_fd(struct dpaa2_eth_priv *priv, | 375 | static int build_sg_fd(struct dpaa2_eth_priv *priv, |
| 339 | struct sk_buff *skb, | 376 | struct sk_buff *skb, |
| @@ -420,6 +457,9 @@ static int build_sg_fd(struct dpaa2_eth_priv *priv, · 80 unmodified lines | |||
| 420 | dpaa2_fd_set_len(fd, skb->len); | 457 | dpaa2_fd_set_len(fd, skb->len); |
| 421 | dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_PTA | DPAA2_FD_CTRL_PTV1); | 458 | dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_PTA | DPAA2_FD_CTRL_PTV1); |
| 422 | 459 | ||
| 460 | if (priv->tx_tstamp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) | ||
| 461 | enable_tx_tstamp(fd, sgt_buf); | ||
| 462 | |||
| 423 | return 0; | 463 | return 0; |
| 424 | 464 | ||
| 425 | dma_map_single_failed: | 465 | dma_map_single_failed: |
| @@ -470,6 +510,9 @@ static int build_single_fd(struct dpaa2_eth_priv *priv, · 44 unmodified lines | |||
| 470 | dpaa2_fd_set_format(fd, dpaa2_fd_single); | 510 | dpaa2_fd_set_format(fd, dpaa2_fd_single); |
| 471 | dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_PTA | DPAA2_FD_CTRL_PTV1); | 511 | dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_PTA | DPAA2_FD_CTRL_PTV1); |
| 472 | 512 | ||
| 513 | if (priv->tx_tstamp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) | ||
| 514 | enable_tx_tstamp(fd, buffer_start); | ||
| 515 | |||
| 473 | return 0; | 516 | return 0; |
| 474 | } | 517 | } |
| 475 | 518 | ||
| @@ -520,6 +563,19 @@ static void free_tx_fd(const struct dpaa2_eth_priv *priv, · 44 unmodified lines | |||
| 520 | return; | 563 | return; |
| 521 | } | 564 | } |
| 522 | 565 | ||
| 566 | /* Get the timestamp value */ | ||
| 567 | if (priv->tx_tstamp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { | ||
| 568 | struct skb_shared_hwtstamps shhwtstamps; | ||
| 569 | __le64 *ts = dpaa2_get_ts(skbh, true); | ||
| 570 | u64 ns; | ||
| 571 | |||
| 572 | memset(&shhwtstamps, 0, sizeof(shhwtstamps)); | ||
| 573 | |||
| 574 | ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); | ||
| 575 | shhwtstamps.hwtstamp = ns_to_ktime(ns); | ||
| 576 | skb_tstamp_tx(skb, &shhwtstamps); | ||
| 577 | } | ||
| 578 | |||
| 523 | /* Free SGT buffer allocated on tx */ | 579 | /* Free SGT buffer allocated on tx */ |
| 524 | if (fd_format != dpaa2_fd_single) | 580 | if (fd_format != dpaa2_fd_single) |
| 525 | skb_free_frag(skbh); | 581 | skb_free_frag(skbh); |
| @@ -552,6 +608,10 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev) · 26 unmodified lines | |||
| 552 | goto err_alloc_headroom; | 608 | goto err_alloc_headroom; |
| 553 | } | 609 | } |
| 554 | percpu_extras->tx_reallocs++; | 610 | percpu_extras->tx_reallocs++; |
| 611 | |||
| 612 | if (skb->sk) | ||
| 613 | skb_set_owner_w(ns, skb->sk); | ||
| 614 | |||
| 555 | dev_kfree_skb(skb); | 615 | dev_kfree_skb(skb); |
| 556 | skb = ns; | 616 | skb = ns; |
| 557 | } | 617 | } |
| @@ -1365,6 +1425,45 @@ static int dpaa2_eth_set_features(struct net_device *net_dev, · 807 unmodified lines | |||
| 1365 | return 0; | 1425 | return 0; |
| 1366 | } | 1426 | } |
| 1367 | 1427 | ||
| 1428 | static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | ||
| 1429 | { | ||
| 1430 | struct dpaa2_eth_priv *priv = netdev_priv(dev); | ||
| 1431 | struct hwtstamp_config config; | ||
| 1432 | |||
| 1433 | if (copy_from_user(&config, rq->ifr_data, sizeof(config))) | ||
| 1434 | return -EFAULT; | ||
| 1435 | |||
| 1436 | switch (config.tx_type) { | ||
| 1437 | case HWTSTAMP_TX_OFF: | ||
| 1438 | priv->tx_tstamp = false; | ||
| 1439 | break; | ||
| 1440 | case HWTSTAMP_TX_ON: | ||
| 1441 | priv->tx_tstamp = true; | ||
| 1442 | break; | ||
| 1443 | default: | ||
| 1444 | return -ERANGE; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | if (config.rx_filter == HWTSTAMP_FILTER_NONE) { | ||
| 1448 | priv->rx_tstamp = false; | ||
| 1449 | } else { | ||
| 1450 | priv->rx_tstamp = true; | ||
| 1451 | /* TS is set for all frame types, not only those requested */ | ||
| 1452 | config.rx_filter = HWTSTAMP_FILTER_ALL; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? | ||
| 1456 | -EFAULT : 0; | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | ||
| 1460 | { | ||
| 1461 | if (cmd == SIOCSHWTSTAMP) | ||
| 1462 | return dpaa2_eth_ts_ioctl(dev, rq, cmd); | ||
| 1463 | |||
| 1464 | return -EINVAL; | ||
| 1465 | } | ||
| 1466 | |||
| 1368 | static const struct net_device_ops dpaa2_eth_ops = { | 1467 | static const struct net_device_ops dpaa2_eth_ops = { |
| 1369 | .ndo_open = dpaa2_eth_open, | 1468 | .ndo_open = dpaa2_eth_open, |
| 1370 | .ndo_start_xmit = dpaa2_eth_tx, | 1469 | .ndo_start_xmit = dpaa2_eth_tx, |
| @@ -1375,6 +1474,7 @@ static const struct net_device_ops dpaa2_eth_ops = { · 4 unmodified lines | |||
| 1375 | .ndo_change_mtu = dpaa2_eth_change_mtu, | 1474 | .ndo_change_mtu = dpaa2_eth_change_mtu, |
| 1376 | .ndo_set_rx_mode = dpaa2_eth_set_rx_mode, | 1475 | .ndo_set_rx_mode = dpaa2_eth_set_rx_mode, |
| 1377 | .ndo_set_features = dpaa2_eth_set_features, | 1476 | .ndo_set_features = dpaa2_eth_set_features, |
| 1477 | .ndo_do_ioctl = dpaa2_eth_ioctl, | ||
| 1378 | }; | 1478 | }; |
| 1379 | 1479 | ||
| 1380 | static void cdan_cb(struct dpaa2_io_notification_ctx *ctx) | 1480 | static void cdan_cb(struct dpaa2_io_notification_ctx *ctx) |
| @@ -1770,7 +1870,9 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv) · 389 unmodified lines | |||
| 1770 | 1870 | ||
| 1771 | /* tx buffer */ | 1871 | /* tx buffer */ |
| 1772 | buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; | 1872 | buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; |
| 1773 | buf_layout.options = DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE; | 1873 | buf_layout.pass_timestamp = true; |
| 1874 | buf_layout.options = DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE | | ||
| 1875 | DPNI_BUF_LAYOUT_OPT_TIMESTAMP; | ||
| 1774 | err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, | 1876 | err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, |
| 1775 | DPNI_QUEUE_TX, &buf_layout); | 1877 | DPNI_QUEUE_TX, &buf_layout); |
| 1776 | if (err) { | 1878 | if (err) { |
| @@ -1779,7 +1881,7 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv) · 2 unmodified lines | |||
| 1779 | } | 1881 | } |
| 1780 | 1882 | ||
| 1781 | /* tx-confirm buffer */ | 1883 | /* tx-confirm buffer */ |
| 1782 | buf_layout.options = 0; | 1884 | buf_layout.options = DPNI_BUF_LAYOUT_OPT_TIMESTAMP; |
| 1783 | err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, | 1885 | err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, |
| 1784 | DPNI_QUEUE_TX_CONFIRM, &buf_layout); | 1886 | DPNI_QUEUE_TX_CONFIRM, &buf_layout); |
| 1785 | if (err) { | 1887 | if (err) { |
| @@ -1810,7 +1912,8 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv) · 24 unmodified lines | |||
| 1810 | buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | | 1912 | buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | |
| 1811 | DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | | 1913 | DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | |
| 1812 | DPNI_BUF_LAYOUT_OPT_DATA_ALIGN | | 1914 | DPNI_BUF_LAYOUT_OPT_DATA_ALIGN | |
| 1813 | DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM; | 1915 | DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM | |
| 1916 | DPNI_BUF_LAYOUT_OPT_TIMESTAMP; | ||
| 1814 | err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, | 1917 | err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, |
| 1815 | DPNI_QUEUE_RX, &buf_layout); | 1918 | DPNI_QUEUE_RX, &buf_layout); |
| 1816 | if (err) { | 1919 | if (err) { |
| @@ -88,8 +88,12 @@ · 87 unmodified lines | |||
| 88 | #define DPAA2_ETH_SKB_SIZE \ | 88 | #define DPAA2_ETH_SKB_SIZE \ |
| 89 | (DPAA2_ETH_RX_BUF_SIZE + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) | 89 | (DPAA2_ETH_RX_BUF_SIZE + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) |
| 90 | 90 | ||
| 91 | /* Hardware annotation area in RX buffers */ | 91 | /* Hardware annotation area in RX/TX buffers */ |
| 92 | #define DPAA2_ETH_RX_HWA_SIZE 64 | 92 | #define DPAA2_ETH_RX_HWA_SIZE 64 |
| 93 | #define DPAA2_ETH_TX_HWA_SIZE 128 | ||
| 94 | |||
| 95 | /* PTP nominal frequency 1GHz */ | ||
| 96 | #define DPAA2_PTP_CLK_PERIOD_NS 1 | ||
| 93 | 97 | ||
| 94 | /* Due to a limitation in WRIOP 1.0.0, the RX buffer data must be aligned | 98 | /* Due to a limitation in WRIOP 1.0.0, the RX buffer data must be aligned |
| 95 | * to 256B. For newer revisions, the requirement is only for 64B alignment | 99 | * to 256B. For newer revisions, the requirement is only for 64B alignment |
| @@ -135,6 +139,7 @@ struct dpaa2_eth_swa { · 39 unmodified lines | |||
| 135 | /* Annotation bits in FD CTRL */ | 139 | /* Annotation bits in FD CTRL */ |
| 136 | #define DPAA2_FD_CTRL_PTA 0x00800000 | 140 | #define DPAA2_FD_CTRL_PTA 0x00800000 |
| 137 | #define DPAA2_FD_CTRL_PTV1 0x00400000 | 141 | #define DPAA2_FD_CTRL_PTV1 0x00400000 |
| 142 | #define DPAA2_FD_CTRL_ASAL 0x00020000 /* ASAL = 128B */ | ||
| 138 | 143 | ||
| 139 | /* Frame annotation status */ | 144 | /* Frame annotation status */ |
| 140 | struct dpaa2_fas { | 145 | struct dpaa2_fas { |
| @@ -150,6 +155,23 @@ struct dpaa2_fas { · 9 unmodified lines | |||
| 150 | #define DPAA2_FAS_OFFSET 0 | 155 | #define DPAA2_FAS_OFFSET 0 |
| 151 | #define DPAA2_FAS_SIZE (sizeof(struct dpaa2_fas)) | 156 | #define DPAA2_FAS_SIZE (sizeof(struct dpaa2_fas)) |
| 152 | 157 | ||
| 158 | /* Timestamp is located in the next 8 bytes of the buffer's | ||
| 159 | * hardware annotation area | ||
| 160 | */ | ||
| 161 | #define DPAA2_TS_OFFSET 0x8 | ||
| 162 | |||
| 163 | /* Frame annotation egress action descriptor */ | ||
| 164 | #define DPAA2_FAEAD_OFFSET 0x58 | ||
| 165 | |||
| 166 | struct dpaa2_faead { | ||
| 167 | __le32 conf_fqid; | ||
| 168 | __le32 ctrl; | ||
| 169 | }; | ||
| 170 | |||
| 171 | #define DPAA2_FAEAD_A2V 0x20000000 | ||
| 172 | #define DPAA2_FAEAD_UPDV 0x00001000 | ||
| 173 | #define DPAA2_FAEAD_UPD 0x00000010 | ||
| 174 | |||
| 153 | /* Accessors for the hardware annotation fields that we use */ | 175 | /* Accessors for the hardware annotation fields that we use */ |
| 154 | static inline void *dpaa2_get_hwa(void *buf_addr, bool swa) | 176 | static inline void *dpaa2_get_hwa(void *buf_addr, bool swa) |
| 155 | { | 177 | { |
| @@ -161,6 +183,16 @@ static inline struct dpaa2_fas *dpaa2_get_fas(void *buf_addr, bool swa) · 5 unmodified lines | |||
| 161 | return dpaa2_get_hwa(buf_addr, swa) + DPAA2_FAS_OFFSET; | 183 | return dpaa2_get_hwa(buf_addr, swa) + DPAA2_FAS_OFFSET; |
| 162 | } | 184 | } |
| 163 | 185 | ||
| 186 | static inline __le64 *dpaa2_get_ts(void *buf_addr, bool swa) | ||
| 187 | { | ||
| 188 | return dpaa2_get_hwa(buf_addr, swa) + DPAA2_TS_OFFSET; | ||
| 189 | } | ||
| 190 | |||
| 191 | static inline struct dpaa2_faead *dpaa2_get_faead(void *buf_addr, bool swa) | ||
| 192 | { | ||
| 193 | return dpaa2_get_hwa(buf_addr, swa) + DPAA2_FAEAD_OFFSET; | ||
| 194 | } | ||
| 195 | |||
| 164 | /* Error and status bits in the frame annotation status word */ | 196 | /* Error and status bits in the frame annotation status word */ |
| 165 | /* Debug frame, otherwise supposed to be discarded */ | 197 | /* Debug frame, otherwise supposed to be discarded */ |
| 166 | #define DPAA2_FAS_DISC 0x80000000 | 198 | #define DPAA2_FAS_DISC 0x80000000 |
| @@ -319,6 +351,9 @@ struct dpaa2_eth_priv { · 152 unmodified lines | |||
| 319 | u16 bpid; | 351 | u16 bpid; |
| 320 | struct iommu_domain *iommu_domain; | 352 | struct iommu_domain *iommu_domain; |
| 321 | 353 | ||
| 354 | bool tx_tstamp; /* Tx timestamping enabled */ | ||
| 355 | bool rx_tstamp; /* Rx timestamping enabled */ | ||
| 356 | |||
| 322 | u16 tx_qdid; | 357 | u16 tx_qdid; |
| 323 | u16 rx_buf_align; | 358 | u16 rx_buf_align; |
| 324 | struct fsl_mc_io *mc_io; | 359 | struct fsl_mc_io *mc_io; |
| @@ -355,6 +390,7 @@ struct dpaa2_eth_priv { · 30 unmodified lines | |||
| 355 | 390 | ||
| 356 | extern const struct ethtool_ops dpaa2_ethtool_ops; | 391 | extern const struct ethtool_ops dpaa2_ethtool_ops; |
| 357 | extern const char dpaa2_eth_drv_version[]; | 392 | extern const char dpaa2_eth_drv_version[]; |
| 393 | extern int dpaa2_phc_index; | ||
| 358 | 394 | ||
| 359 | static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv, | 395 | static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv, |
| 360 | u16 ver_major, u16 ver_minor) | 396 | u16 ver_major, u16 ver_minor) |
| @@ -377,10 +413,19 @@ static inline · 16 unmodified lines | |||
| 377 | unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv, | 413 | unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv, |
| 378 | struct sk_buff *skb) | 414 | struct sk_buff *skb) |
| 379 | { | 415 | { |
| 416 | unsigned int headroom = DPAA2_ETH_SWA_SIZE; | ||
| 417 | |||
| 418 | /* For non-linear skbs we have no headroom requirement, as we build a | ||
| 419 | * SG frame with a newly allocated SGT buffer | ||
| 420 | */ | ||
| 380 | if (skb_is_nonlinear(skb)) | 421 | if (skb_is_nonlinear(skb)) |
| 381 | return 0; | 422 | return 0; |
| 382 | 423 | ||
| 383 | return DPAA2_ETH_SWA_SIZE; | 424 | /* If we have Tx timestamping, need 128B hardware annotation */ |
| 425 | if (priv->tx_tstamp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) | ||
| 426 | headroom += DPAA2_ETH_TX_HWA_SIZE; | ||
| 427 | |||
| 428 | return headroom; | ||
| 384 | } | 429 | } |
| 385 | 430 | ||
| 386 | /* Extra headroom space requested to hardware, in order to make sure there's | 431 | /* Extra headroom space requested to hardware, in order to make sure there's |
| @@ -30,6 +30,8 @@ · 29 unmodified lines | |||
| 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | */ | 31 | */ |
| 32 | 32 | ||
| 33 | #include <linux/net_tstamp.h> | ||
| 34 | |||
| 33 | #include "dpni.h" /* DPNI_LINK_OPT_* */ | 35 | #include "dpni.h" /* DPNI_LINK_OPT_* */ |
| 34 | #include "dpaa2-eth.h" | 36 | #include "dpaa2-eth.h" |
| 35 | 37 | ||
| @@ -274,6 +276,26 @@ static int dpaa2_eth_get_rxnfc(struct net_device *net_dev, · 238 unmodified lines | |||
| 274 | return 0; | 276 | return 0; |
| 275 | } | 277 | } |
| 276 | 278 | ||
| 279 | int dpaa2_phc_index = -1; | ||
| 280 | EXPORT_SYMBOL(dpaa2_phc_index); | ||
| 281 | |||
| 282 | static int dpaa2_eth_get_ts_info(struct net_device *dev, | ||
| 283 | struct ethtool_ts_info *info) | ||
| 284 | { | ||
| 285 | info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | | ||
| 286 | SOF_TIMESTAMPING_RX_HARDWARE | | ||
| 287 | SOF_TIMESTAMPING_RAW_HARDWARE; | ||
| 288 | |||
| 289 | info->phc_index = dpaa2_phc_index; | ||
| 290 | |||
| 291 | info->tx_types = (1 << HWTSTAMP_TX_OFF) | | ||
| 292 | (1 << HWTSTAMP_TX_ON); | ||
| 293 | |||
| 294 | info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | | ||
| 295 | (1 << HWTSTAMP_FILTER_ALL); | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 277 | const struct ethtool_ops dpaa2_ethtool_ops = { | 299 | const struct ethtool_ops dpaa2_ethtool_ops = { |
| 278 | .get_drvinfo = dpaa2_eth_get_drvinfo, | 300 | .get_drvinfo = dpaa2_eth_get_drvinfo, |
| 279 | .get_link = ethtool_op_get_link, | 301 | .get_link = ethtool_op_get_link, |
| @@ -283,4 +305,5 @@ const struct ethtool_ops dpaa2_ethtool_ops = { · 3 unmodified lines | |||
| 283 | .get_ethtool_stats = dpaa2_eth_get_ethtool_stats, | 305 | .get_ethtool_stats = dpaa2_eth_get_ethtool_stats, |
| 284 | .get_strings = dpaa2_eth_get_strings, | 306 | .get_strings = dpaa2_eth_get_strings, |
| 285 | .get_rxnfc = dpaa2_eth_get_rxnfc, | 307 | .get_rxnfc = dpaa2_eth_get_rxnfc, |
| 308 | .get_ts_info = dpaa2_eth_get_ts_info, | ||
| 286 | }; | 309 | }; |
| @@ -49,6 +49,8 @@ · 48 unmodified lines | |||
| 49 | #define DPSW_CMDID_IF_SET_FLOODING DPSW_CMD_ID(0x047) | 49 | #define DPSW_CMDID_IF_SET_FLOODING DPSW_CMD_ID(0x047) |
| 50 | #define DPSW_CMDID_IF_SET_BROADCAST DPSW_CMD_ID(0x048) | 50 | #define DPSW_CMDID_IF_SET_BROADCAST DPSW_CMD_ID(0x048) |
| 51 | 51 | ||
| 52 | #define DPSW_CMDID_IF_GET_TCI DPSW_CMD_ID(0x04A) | ||
| 53 | |||
| 52 | #define DPSW_CMDID_IF_SET_LINK_CFG DPSW_CMD_ID(0x04C) | 54 | #define DPSW_CMDID_IF_SET_LINK_CFG DPSW_CMD_ID(0x04C) |
| 53 | 55 | ||
| 54 | #define DPSW_CMDID_VLAN_ADD DPSW_CMD_ID(0x060) | 56 | #define DPSW_CMDID_VLAN_ADD DPSW_CMD_ID(0x060) |
| @@ -206,6 +208,17 @@ struct dpsw_cmd_if_set_tci { · 151 unmodified lines | |||
| 206 | __le16 conf; | 208 | __le16 conf; |
| 207 | }; | 209 | }; |
| 208 | 210 | ||
| 211 | struct dpsw_cmd_if_get_tci { | ||
| 212 | __le16 if_id; | ||
| 213 | }; | ||
| 214 | |||
| 215 | struct dpsw_rsp_if_get_tci { | ||
| 216 | __le16 pad; | ||
| 217 | __le16 vlan_id; | ||
| 218 | u8 dei; | ||
| 219 | u8 pcp; | ||
| 220 | }; | ||
| 221 | |||
| 209 | #define DPSW_STATE_SHIFT 0 | 222 | #define DPSW_STATE_SHIFT 0 |
| 210 | #define DPSW_STATE_SIZE 4 | 223 | #define DPSW_STATE_SIZE 4 |
| 211 | 224 | ||
| @@ -528,6 +528,48 @@ int dpsw_if_set_tci(struct fsl_mc_io *mc_io, · 527 unmodified lines | |||
| 528 | return mc_send_command(mc_io, &cmd); | 528 | return mc_send_command(mc_io, &cmd); |
| 529 | } | 529 | } |
| 530 | 530 | ||
| 531 | /** | ||
| 532 | * dpsw_if_get_tci() - Get default VLAN Tag Control Information (TCI) | ||
| 533 | * @mc_io: Pointer to MC portal's I/O object | ||
| 534 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 535 | * @token: Token of DPSW object | ||
| 536 | * @if_id: Interface Identifier | ||
| 537 | * @cfg: Tag Control Information Configuration | ||
| 538 | * | ||
| 539 | * Return: Completion status. '0' on Success; Error code otherwise. | ||
| 540 | */ | ||
| 541 | int dpsw_if_get_tci(struct fsl_mc_io *mc_io, | ||
| 542 | u32 cmd_flags, | ||
| 543 | u16 token, | ||
| 544 | u16 if_id, | ||
| 545 | struct dpsw_tci_cfg *cfg) | ||
| 546 | { | ||
| 547 | struct fsl_mc_command cmd = { 0 }; | ||
| 548 | struct dpsw_cmd_if_get_tci *cmd_params; | ||
| 549 | struct dpsw_rsp_if_get_tci *rsp_params; | ||
| 550 | int err; | ||
| 551 | |||
| 552 | /* prepare command */ | ||
| 553 | cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_GET_TCI, | ||
| 554 | cmd_flags, | ||
| 555 | token); | ||
| 556 | cmd_params = (struct dpsw_cmd_if_get_tci *)cmd.params; | ||
| 557 | cmd_params->if_id = cpu_to_le16(if_id); | ||
| 558 | |||
| 559 | /* send command to mc*/ | ||
| 560 | err = mc_send_command(mc_io, &cmd); | ||
| 561 | if (err) | ||
| 562 | return err; | ||
| 563 | |||
| 564 | /* retrieve response parameters */ | ||
| 565 | rsp_params = (struct dpsw_rsp_if_get_tci *)cmd.params; | ||
| 566 | cfg->pcp = rsp_params->pcp; | ||
| 567 | cfg->dei = rsp_params->dei; | ||
| 568 | cfg->vlan_id = le16_to_cpu(rsp_params->vlan_id); | ||
| 569 | |||
| 570 | return 0; | ||
| 571 | } | ||
| 572 | |||
| 531 | /** | 573 | /** |
| 532 | * dpsw_if_set_stp() - Function sets Spanning Tree Protocol (STP) state. | 574 | * dpsw_if_set_stp() - Function sets Spanning Tree Protocol (STP) state. |
| 533 | * @mc_io: Pointer to MC portal's I/O object | 575 | * @mc_io: Pointer to MC portal's I/O object |
| @@ -306,6 +306,12 @@ int dpsw_if_set_tci(struct fsl_mc_io *mc_io, · 305 unmodified lines | |||
| 306 | u16 if_id, | 306 | u16 if_id, |
| 307 | const struct dpsw_tci_cfg *cfg); | 307 | const struct dpsw_tci_cfg *cfg); |
| 308 | 308 | ||
| 309 | int dpsw_if_get_tci(struct fsl_mc_io *mc_io, | ||
| 310 | u32 cmd_flags, | ||
| 311 | u16 token, | ||
| 312 | u16 if_id, | ||
| 313 | struct dpsw_tci_cfg *cfg); | ||
| 314 | |||
| 309 | /** | 315 | /** |
| 310 | * enum dpsw_stp_state - Spanning Tree Protocol (STP) states | 316 | * enum dpsw_stp_state - Spanning Tree Protocol (STP) states |
| 311 | * @DPSW_STP_STATE_BLOCKING: Blocking state | 317 | * @DPSW_STP_STATE_BLOCKING: Blocking state |
| @@ -50,14 +50,23 @@ static int ethsw_add_vlan(struct ethsw_core *ethsw, u16 vid) · 49 unmodified lines | |||
| 50 | return 0; | 50 | return 0; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | static int ethsw_port_set_tci(struct ethsw_port_priv *port_priv, | 53 | static int ethsw_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvid) |
| 54 | struct dpsw_tci_cfg *tci_cfg) | ||
| 55 | { | 54 | { |
| 56 | struct ethsw_core *ethsw = port_priv->ethsw_data; | 55 | struct ethsw_core *ethsw = port_priv->ethsw_data; |
| 57 | struct net_device *netdev = port_priv->netdev; | 56 | struct net_device *netdev = port_priv->netdev; |
| 57 | struct dpsw_tci_cfg tci_cfg = { 0 }; | ||
| 58 | bool is_oper; | 58 | bool is_oper; |
| 59 | int err, ret; | 59 | int err, ret; |
| 60 | 60 | ||
| 61 | err = dpsw_if_get_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, | ||
| 62 | port_priv->idx, &tci_cfg); | ||
| 63 | if (err) { | ||
| 64 | netdev_err(netdev, "dpsw_if_get_tci err %d\n", err); | ||
| 65 | return err; | ||
| 66 | } | ||
| 67 | |||
| 68 | tci_cfg.vlan_id = pvid; | ||
| 69 | |||
| 61 | /* Interface needs to be down to change PVID */ | 70 | /* Interface needs to be down to change PVID */ |
| 62 | is_oper = netif_oper_up(netdev); | 71 | is_oper = netif_oper_up(netdev); |
| 63 | if (is_oper) { | 72 | if (is_oper) { |
| @@ -71,17 +80,16 @@ static int ethsw_port_set_tci(struct ethsw_port_priv *port_priv, · 7 unmodified lines | |||
| 71 | } | 80 | } |
| 72 | 81 | ||
| 73 | err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, | 82 | err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, |
| 74 | port_priv->idx, tci_cfg); | 83 | port_priv->idx, &tci_cfg); |
| 75 | if (err) { | 84 | if (err) { |
| 76 | netdev_err(netdev, "dpsw_if_set_tci err %d\n", err); | 85 | netdev_err(netdev, "dpsw_if_set_tci err %d\n", err); |
| 77 | goto set_tci_error; | 86 | goto set_tci_error; |
| 78 | } | 87 | } |
| 79 | 88 | ||
| 80 | /* Delete previous PVID info and mark the new one */ | 89 | /* Delete previous PVID info and mark the new one */ |
| 81 | if (port_priv->pvid) | 90 | port_priv->vlans[port_priv->pvid] &= ~ETHSW_VLAN_PVID; |
| 82 | port_priv->vlans[port_priv->pvid] &= ~ETHSW_VLAN_PVID; | 91 | port_priv->vlans[pvid] |= ETHSW_VLAN_PVID; |
| 83 | port_priv->vlans[tci_cfg->vlan_id] |= ETHSW_VLAN_PVID; | 92 | port_priv->pvid = pvid; |
| 84 | port_priv->pvid = tci_cfg->vlan_id; | ||
| 85 | 93 | ||
| 86 | set_tci_error: | 94 | set_tci_error: |
| 87 | if (is_oper) { | 95 | if (is_oper) { |
| @@ -133,13 +141,7 @@ static int ethsw_port_add_vlan(struct ethsw_port_priv *port_priv, · 45 unmodified lines | |||
| 133 | } | 141 | } |
| 134 | 142 | ||
| 135 | if (flags & BRIDGE_VLAN_INFO_PVID) { | 143 | if (flags & BRIDGE_VLAN_INFO_PVID) { |
| 136 | struct dpsw_tci_cfg tci_cfg = { | 144 | err = ethsw_port_set_pvid(port_priv, vid); |
| 137 | .pcp = 0, | ||
| 138 | .dei = 0, | ||
| 139 | .vlan_id = vid, | ||
| 140 | }; | ||
| 141 | |||
| 142 | err = ethsw_port_set_tci(port_priv, &tci_cfg); | ||
| 143 | if (err) | 145 | if (err) |
| 144 | return err; | 146 | return err; |
| 145 | } | 147 | } |
| @@ -616,10 +618,8 @@ static void ethsw_teardown_irqs(struct fsl_mc_device *sw_dev) · 470 unmodified lines | |||
| 616 | { | 618 | { |
| 617 | struct device *dev = &sw_dev->dev; | 619 | struct device *dev = &sw_dev->dev; |
| 618 | struct ethsw_core *ethsw = dev_get_drvdata(dev); | 620 | struct ethsw_core *ethsw = dev_get_drvdata(dev); |
| 619 | struct fsl_mc_device_irq *irq; | ||
| 620 | int err; | 621 | int err; |
| 621 | 622 | ||
| 622 | irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF]; | ||
| 623 | err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle, | 623 | err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle, |
| 624 | DPSW_IRQ_INDEX_IF, 0); | 624 | DPSW_IRQ_INDEX_IF, 0); |
| 625 | if (err) | 625 | if (err) |
| @@ -822,9 +822,7 @@ static int ethsw_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid) · 196 unmodified lines | |||
| 822 | return -ENOENT; | 822 | return -ENOENT; |
| 823 | 823 | ||
| 824 | if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) { | 824 | if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) { |
| 825 | struct dpsw_tci_cfg tci_cfg = { 0 }; | 825 | err = ethsw_port_set_pvid(port_priv, 0); |
| 826 | |||
| 827 | err = ethsw_port_set_tci(port_priv, &tci_cfg); | ||
| 828 | if (err) | 826 | if (err) |
| 829 | return err; | 827 | return err; |
| 830 | } | 828 | } |
| @@ -1260,7 +1258,6 @@ static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port) · 429 unmodified lines | |||
| 1260 | const char def_mcast[ETH_ALEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01}; | 1258 | const char def_mcast[ETH_ALEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01}; |
| 1261 | struct net_device *netdev = port_priv->netdev; | 1259 | struct net_device *netdev = port_priv->netdev; |
| 1262 | struct ethsw_core *ethsw = port_priv->ethsw_data; | 1260 | struct ethsw_core *ethsw = port_priv->ethsw_data; |
| 1263 | struct dpsw_tci_cfg tci_cfg = {0}; | ||
| 1264 | struct dpsw_vlan_if_cfg vcfg; | 1261 | struct dpsw_vlan_if_cfg vcfg; |
| 1265 | int err; | 1262 | int err; |
| 1266 | 1263 | ||
| @@ -1278,7 +1275,7 @@ static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port) · 11 unmodified lines | |||
| 1278 | return err; | 1275 | return err; |
| 1279 | } | 1276 | } |
| 1280 | 1277 | ||
| 1281 | err = ethsw_port_set_tci(port_priv, &tci_cfg); | 1278 | err = ethsw_port_set_pvid(port_priv, 0); |
| 1282 | if (err) | 1279 | if (err) |
| 1283 | return err; | 1280 | return err; |
| 1284 | 1281 | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the Freescale DPAA2 PTP clock | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK) += dpaa2-rtc.o | ||
| 6 | |||
| 7 | dpaa2-rtc-objs := rtc.o dprtc.o | ||
| @@ -0,0 +1,137 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright 2013-2016 Freescale Semiconductor Inc. | ||
| 4 | * Copyright 2016-2018 NXP | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef _FSL_DPRTC_CMD_H | ||
| 8 | #define _FSL_DPRTC_CMD_H | ||
| 9 | |||
| 10 | /* DPRTC Version */ | ||
| 11 | #define DPRTC_VER_MAJOR 2 | ||
| 12 | #define DPRTC_VER_MINOR 0 | ||
| 13 | |||
| 14 | /* Command versioning */ | ||
| 15 | #define DPRTC_CMD_BASE_VERSION 1 | ||
| 16 | #define DPRTC_CMD_ID_OFFSET 4 | ||
| 17 | |||
| 18 | #define DPRTC_CMD(id) (((id) << DPRTC_CMD_ID_OFFSET) | DPRTC_CMD_BASE_VERSION) | ||
| 19 | |||
| 20 | /* Command IDs */ | ||
| 21 | #define DPRTC_CMDID_CLOSE DPRTC_CMD(0x800) | ||
| 22 | #define DPRTC_CMDID_OPEN DPRTC_CMD(0x810) | ||
| 23 | #define DPRTC_CMDID_CREATE DPRTC_CMD(0x910) | ||
| 24 | #define DPRTC_CMDID_DESTROY DPRTC_CMD(0x990) | ||
| 25 | #define DPRTC_CMDID_GET_API_VERSION DPRTC_CMD(0xa10) | ||
| 26 | |||
| 27 | #define DPRTC_CMDID_ENABLE DPRTC_CMD(0x002) | ||
| 28 | #define DPRTC_CMDID_DISABLE DPRTC_CMD(0x003) | ||
| 29 | #define DPRTC_CMDID_GET_ATTR DPRTC_CMD(0x004) | ||
| 30 | #define DPRTC_CMDID_RESET DPRTC_CMD(0x005) | ||
| 31 | #define DPRTC_CMDID_IS_ENABLED DPRTC_CMD(0x006) | ||
| 32 | |||
| 33 | #define DPRTC_CMDID_SET_IRQ_ENABLE DPRTC_CMD(0x012) | ||
| 34 | #define DPRTC_CMDID_GET_IRQ_ENABLE DPRTC_CMD(0x013) | ||
| 35 | #define DPRTC_CMDID_SET_IRQ_MASK DPRTC_CMD(0x014) | ||
| 36 | #define DPRTC_CMDID_GET_IRQ_MASK DPRTC_CMD(0x015) | ||
| 37 | #define DPRTC_CMDID_GET_IRQ_STATUS DPRTC_CMD(0x016) | ||
| 38 | #define DPRTC_CMDID_CLEAR_IRQ_STATUS DPRTC_CMD(0x017) | ||
| 39 | |||
| 40 | #define DPRTC_CMDID_SET_CLOCK_OFFSET DPRTC_CMD(0x1d0) | ||
| 41 | #define DPRTC_CMDID_SET_FREQ_COMPENSATION DPRTC_CMD(0x1d1) | ||
| 42 | #define DPRTC_CMDID_GET_FREQ_COMPENSATION DPRTC_CMD(0x1d2) | ||
| 43 | #define DPRTC_CMDID_GET_TIME DPRTC_CMD(0x1d3) | ||
| 44 | #define DPRTC_CMDID_SET_TIME DPRTC_CMD(0x1d4) | ||
| 45 | #define DPRTC_CMDID_SET_ALARM DPRTC_CMD(0x1d5) | ||
| 46 | #define DPRTC_CMDID_SET_PERIODIC_PULSE DPRTC_CMD(0x1d6) | ||
| 47 | #define DPRTC_CMDID_CLEAR_PERIODIC_PULSE DPRTC_CMD(0x1d7) | ||
| 48 | #define DPRTC_CMDID_SET_EXT_TRIGGER DPRTC_CMD(0x1d8) | ||
| 49 | #define DPRTC_CMDID_CLEAR_EXT_TRIGGER DPRTC_CMD(0x1d9) | ||
| 50 | #define DPRTC_CMDID_GET_EXT_TRIGGER_TIMESTAMP DPRTC_CMD(0x1dA) | ||
| 51 | |||
| 52 | /* Macros for accessing command fields smaller than 1byte */ | ||
| 53 | #define DPRTC_MASK(field) \ | ||
| 54 | GENMASK(DPRTC_##field##_SHIFT + DPRTC_##field##_SIZE - 1, \ | ||
| 55 | DPRTC_##field##_SHIFT) | ||
| 56 | #define dprtc_get_field(var, field) \ | ||
| 57 | (((var) & DPRTC_MASK(field)) >> DPRTC_##field##_SHIFT) | ||
| 58 | |||
| 59 | #pragma pack(push, 1) | ||
| 60 | struct dprtc_cmd_open { | ||
| 61 | __le32 dprtc_id; | ||
| 62 | }; | ||
| 63 | |||
| 64 | struct dprtc_cmd_destroy { | ||
| 65 | __le32 object_id; | ||
| 66 | }; | ||
| 67 | |||
| 68 | #define DPRTC_ENABLE_SHIFT 0 | ||
| 69 | #define DPRTC_ENABLE_SIZE 1 | ||
| 70 | |||
| 71 | struct dprtc_rsp_is_enabled { | ||
| 72 | u8 en; | ||
| 73 | }; | ||
| 74 | |||
| 75 | struct dprtc_cmd_get_irq { | ||
| 76 | __le32 pad; | ||
| 77 | u8 irq_index; | ||
| 78 | }; | ||
| 79 | |||
| 80 | struct dprtc_cmd_set_irq_enable { | ||
| 81 | u8 en; | ||
| 82 | u8 pad[3]; | ||
| 83 | u8 irq_index; | ||
| 84 | }; | ||
| 85 | |||
| 86 | struct dprtc_rsp_get_irq_enable { | ||
| 87 | u8 en; | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct dprtc_cmd_set_irq_mask { | ||
| 91 | __le32 mask; | ||
| 92 | u8 irq_index; | ||
| 93 | }; | ||
| 94 | |||
| 95 | struct dprtc_rsp_get_irq_mask { | ||
| 96 | __le32 mask; | ||
| 97 | }; | ||
| 98 | |||
| 99 | struct dprtc_cmd_get_irq_status { | ||
| 100 | __le32 status; | ||
| 101 | u8 irq_index; | ||
| 102 | }; | ||
| 103 | |||
| 104 | struct dprtc_rsp_get_irq_status { | ||
| 105 | __le32 status; | ||
| 106 | }; | ||
| 107 | |||
| 108 | struct dprtc_cmd_clear_irq_status { | ||
| 109 | __le32 status; | ||
| 110 | u8 irq_index; | ||
| 111 | }; | ||
| 112 | |||
| 113 | struct dprtc_rsp_get_attributes { | ||
| 114 | __le32 pad; | ||
| 115 | __le32 id; | ||
| 116 | }; | ||
| 117 | |||
| 118 | struct dprtc_cmd_set_clock_offset { | ||
| 119 | __le64 offset; | ||
| 120 | }; | ||
| 121 | |||
| 122 | struct dprtc_get_freq_compensation { | ||
| 123 | __le32 freq_compensation; | ||
| 124 | }; | ||
| 125 | |||
| 126 | struct dprtc_time { | ||
| 127 | __le64 time; | ||
| 128 | }; | ||
| 129 | |||
| 130 | struct dprtc_rsp_get_api_version { | ||
| 131 | __le16 major; | ||
| 132 | __le16 minor; | ||
| 133 | }; | ||
| 134 | |||
| 135 | #pragma pack(pop) | ||
| 136 | |||
| 137 | #endif /* _FSL_DPRTC_CMD_H */ | ||
| @@ -0,0 +1,701 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright 2013-2016 Freescale Semiconductor Inc. | ||
| 4 | * Copyright 2016-2018 NXP | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/fsl/mc.h> | ||
| 8 | |||
| 9 | #include "dprtc.h" | ||
| 10 | #include "dprtc-cmd.h" | ||
| 11 | |||
| 12 | /** | ||
| 13 | * dprtc_open() - Open a control session for the specified object. | ||
| 14 | * @mc_io: Pointer to MC portal's I/O object | ||
| 15 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 16 | * @dprtc_id: DPRTC unique ID | ||
| 17 | * @token: Returned token; use in subsequent API calls | ||
| 18 | * | ||
| 19 | * This function can be used to open a control session for an | ||
| 20 | * already created object; an object may have been declared in | ||
| 21 | * the DPL or by calling the dprtc_create function. | ||
| 22 | * This function returns a unique authentication token, | ||
| 23 | * associated with the specific object ID and the specific MC | ||
| 24 | * portal; this token must be used in all subsequent commands for | ||
| 25 | * this specific object | ||
| 26 | * | ||
| 27 | * Return: '0' on Success; Error code otherwise. | ||
| 28 | */ | ||
| 29 | int dprtc_open(struct fsl_mc_io *mc_io, | ||
| 30 | u32 cmd_flags, | ||
| 31 | int dprtc_id, | ||
| 32 | u16 *token) | ||
| 33 | { | ||
| 34 | struct dprtc_cmd_open *cmd_params; | ||
| 35 | struct fsl_mc_command cmd = { 0 }; | ||
| 36 | int err; | ||
| 37 | |||
| 38 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_OPEN, | ||
| 39 | cmd_flags, | ||
| 40 | 0); | ||
| 41 | cmd_params = (struct dprtc_cmd_open *)cmd.params; | ||
| 42 | cmd_params->dprtc_id = cpu_to_le32(dprtc_id); | ||
| 43 | |||
| 44 | err = mc_send_command(mc_io, &cmd); | ||
| 45 | if (err) | ||
| 46 | return err; | ||
| 47 | |||
| 48 | *token = mc_cmd_hdr_read_token(&cmd); | ||
| 49 | |||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * dprtc_close() - Close the control session of the object | ||
| 55 | * @mc_io: Pointer to MC portal's I/O object | ||
| 56 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 57 | * @token: Token of DPRTC object | ||
| 58 | * | ||
| 59 | * After this function is called, no further operations are | ||
| 60 | * allowed on the object without opening a new control session. | ||
| 61 | * | ||
| 62 | * Return: '0' on Success; Error code otherwise. | ||
| 63 | */ | ||
| 64 | int dprtc_close(struct fsl_mc_io *mc_io, | ||
| 65 | u32 cmd_flags, | ||
| 66 | u16 token) | ||
| 67 | { | ||
| 68 | struct fsl_mc_command cmd = { 0 }; | ||
| 69 | |||
| 70 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_CLOSE, cmd_flags, | ||
| 71 | token); | ||
| 72 | |||
| 73 | return mc_send_command(mc_io, &cmd); | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * dprtc_create() - Create the DPRTC object. | ||
| 78 | * @mc_io: Pointer to MC portal's I/O object | ||
| 79 | * @dprc_token: Parent container token; '0' for default container | ||
| 80 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 81 | * @cfg: Configuration structure | ||
| 82 | * @obj_id: Returned object id | ||
| 83 | * | ||
| 84 | * Create the DPRTC object, allocate required resources and | ||
| 85 | * perform required initialization. | ||
| 86 | * | ||
| 87 | * The function accepts an authentication token of a parent | ||
| 88 | * container that this object should be assigned to. The token | ||
| 89 | * can be '0' so the object will be assigned to the default container. | ||
| 90 | * The newly created object can be opened with the returned | ||
| 91 | * object id and using the container's associated tokens and MC portals. | ||
| 92 | * | ||
| 93 | * Return: '0' on Success; Error code otherwise. | ||
| 94 | */ | ||
| 95 | int dprtc_create(struct fsl_mc_io *mc_io, | ||
| 96 | u16 dprc_token, | ||
| 97 | u32 cmd_flags, | ||
| 98 | const struct dprtc_cfg *cfg, | ||
| 99 | u32 *obj_id) | ||
| 100 | { | ||
| 101 | struct fsl_mc_command cmd = { 0 }; | ||
| 102 | int err; | ||
| 103 | |||
| 104 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_CREATE, | ||
| 105 | cmd_flags, | ||
| 106 | dprc_token); | ||
| 107 | |||
| 108 | err = mc_send_command(mc_io, &cmd); | ||
| 109 | if (err) | ||
| 110 | return err; | ||
| 111 | |||
| 112 | *obj_id = mc_cmd_read_object_id(&cmd); | ||
| 113 | |||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * dprtc_destroy() - Destroy the DPRTC object and release all its resources. | ||
| 119 | * @mc_io: Pointer to MC portal's I/O object | ||
| 120 | * @dprc_token: Parent container token; '0' for default container | ||
| 121 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 122 | * @object_id: The object id; it must be a valid id within the container that | ||
| 123 | * created this object; | ||
| 124 | * | ||
| 125 | * The function accepts the authentication token of the parent container that | ||
| 126 | * created the object (not the one that currently owns the object). The object | ||
| 127 | * is searched within parent using the provided 'object_id'. | ||
| 128 | * All tokens to the object must be closed before calling destroy. | ||
| 129 | * | ||
| 130 | * Return: '0' on Success; error code otherwise. | ||
| 131 | */ | ||
| 132 | int dprtc_destroy(struct fsl_mc_io *mc_io, | ||
| 133 | u16 dprc_token, | ||
| 134 | u32 cmd_flags, | ||
| 135 | u32 object_id) | ||
| 136 | { | ||
| 137 | struct dprtc_cmd_destroy *cmd_params; | ||
| 138 | struct fsl_mc_command cmd = { 0 }; | ||
| 139 | |||
| 140 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_DESTROY, | ||
| 141 | cmd_flags, | ||
| 142 | dprc_token); | ||
| 143 | cmd_params = (struct dprtc_cmd_destroy *)cmd.params; | ||
| 144 | cmd_params->object_id = cpu_to_le32(object_id); | ||
| 145 | |||
| 146 | return mc_send_command(mc_io, &cmd); | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * dprtc_enable() - Enable the DPRTC. | ||
| 151 | * @mc_io: Pointer to MC portal's I/O object | ||
| 152 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 153 | * @token: Token of DPRTC object | ||
| 154 | * | ||
| 155 | * Return: '0' on Success; Error code otherwise. | ||
| 156 | */ | ||
| 157 | int dprtc_enable(struct fsl_mc_io *mc_io, | ||
| 158 | u32 cmd_flags, | ||
| 159 | u16 token) | ||
| 160 | { | ||
| 161 | struct fsl_mc_command cmd = { 0 }; | ||
| 162 | |||
| 163 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_ENABLE, cmd_flags, | ||
| 164 | token); | ||
| 165 | |||
| 166 | return mc_send_command(mc_io, &cmd); | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * dprtc_disable() - Disable the DPRTC. | ||
| 171 | * @mc_io: Pointer to MC portal's I/O object | ||
| 172 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 173 | * @token: Token of DPRTC object | ||
| 174 | * | ||
| 175 | * Return: '0' on Success; Error code otherwise. | ||
| 176 | */ | ||
| 177 | int dprtc_disable(struct fsl_mc_io *mc_io, | ||
| 178 | u32 cmd_flags, | ||
| 179 | u16 token) | ||
| 180 | { | ||
| 181 | struct fsl_mc_command cmd = { 0 }; | ||
| 182 | |||
| 183 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_DISABLE, | ||
| 184 | cmd_flags, | ||
| 185 | token); | ||
| 186 | |||
| 187 | return mc_send_command(mc_io, &cmd); | ||
| 188 | } | ||
| 189 | |||
| 190 | /** | ||
| 191 | * dprtc_is_enabled() - Check if the DPRTC is enabled. | ||
| 192 | * @mc_io: Pointer to MC portal's I/O object | ||
| 193 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 194 | * @token: Token of DPRTC object | ||
| 195 | * @en: Returns '1' if object is enabled; '0' otherwise | ||
| 196 | * | ||
| 197 | * Return: '0' on Success; Error code otherwise. | ||
| 198 | */ | ||
| 199 | int dprtc_is_enabled(struct fsl_mc_io *mc_io, | ||
| 200 | u32 cmd_flags, | ||
| 201 | u16 token, | ||
| 202 | int *en) | ||
| 203 | { | ||
| 204 | struct dprtc_rsp_is_enabled *rsp_params; | ||
| 205 | struct fsl_mc_command cmd = { 0 }; | ||
| 206 | int err; | ||
| 207 | |||
| 208 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_IS_ENABLED, cmd_flags, | ||
| 209 | token); | ||
| 210 | |||
| 211 | err = mc_send_command(mc_io, &cmd); | ||
| 212 | if (err) | ||
| 213 | return err; | ||
| 214 | |||
| 215 | rsp_params = (struct dprtc_rsp_is_enabled *)cmd.params; | ||
| 216 | *en = dprtc_get_field(rsp_params->en, ENABLE); | ||
| 217 | |||
| 218 | return 0; | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * dprtc_reset() - Reset the DPRTC, returns the object to initial state. | ||
| 223 | * @mc_io: Pointer to MC portal's I/O object | ||
| 224 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 225 | * @token: Token of DPRTC object | ||
| 226 | * | ||
| 227 | * Return: '0' on Success; Error code otherwise. | ||
| 228 | */ | ||
| 229 | int dprtc_reset(struct fsl_mc_io *mc_io, | ||
| 230 | u32 cmd_flags, | ||
| 231 | u16 token) | ||
| 232 | { | ||
| 233 | struct fsl_mc_command cmd = { 0 }; | ||
| 234 | |||
| 235 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_RESET, | ||
| 236 | cmd_flags, | ||
| 237 | token); | ||
| 238 | |||
| 239 | return mc_send_command(mc_io, &cmd); | ||
| 240 | } | ||
| 241 | |||
| 242 | /** | ||
| 243 | * dprtc_set_irq_enable() - Set overall interrupt state. | ||
| 244 | * @mc_io: Pointer to MC portal's I/O object | ||
| 245 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 246 | * @token: Token of DPRTC object | ||
| 247 | * @irq_index: The interrupt index to configure | ||
| 248 | * @en: Interrupt state - enable = 1, disable = 0 | ||
| 249 | * | ||
| 250 | * Allows GPP software to control when interrupts are generated. | ||
| 251 | * Each interrupt can have up to 32 causes. The enable/disable control's the | ||
| 252 | * overall interrupt state. if the interrupt is disabled no causes will cause | ||
| 253 | * an interrupt. | ||
| 254 | * | ||
| 255 | * Return: '0' on Success; Error code otherwise. | ||
| 256 | */ | ||
| 257 | int dprtc_set_irq_enable(struct fsl_mc_io *mc_io, | ||
| 258 | u32 cmd_flags, | ||
| 259 | u16 token, | ||
| 260 | u8 irq_index, | ||
| 261 | u8 en) | ||
| 262 | { | ||
| 263 | struct dprtc_cmd_set_irq_enable *cmd_params; | ||
| 264 | struct fsl_mc_command cmd = { 0 }; | ||
| 265 | |||
| 266 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_SET_IRQ_ENABLE, | ||
| 267 | cmd_flags, | ||
| 268 | token); | ||
| 269 | cmd_params = (struct dprtc_cmd_set_irq_enable *)cmd.params; | ||
| 270 | cmd_params->irq_index = irq_index; | ||
| 271 | cmd_params->en = en; | ||
| 272 | |||
| 273 | return mc_send_command(mc_io, &cmd); | ||
| 274 | } | ||
| 275 | |||
| 276 | /** | ||
| 277 | * dprtc_get_irq_enable() - Get overall interrupt state | ||
| 278 | * @mc_io: Pointer to MC portal's I/O object | ||
| 279 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 280 | * @token: Token of DPRTC object | ||
| 281 | * @irq_index: The interrupt index to configure | ||
| 282 | * @en: Returned interrupt state - enable = 1, disable = 0 | ||
| 283 | * | ||
| 284 | * Return: '0' on Success; Error code otherwise. | ||
| 285 | */ | ||
| 286 | int dprtc_get_irq_enable(struct fsl_mc_io *mc_io, | ||
| 287 | u32 cmd_flags, | ||
| 288 | u16 token, | ||
| 289 | u8 irq_index, | ||
| 290 | u8 *en) | ||
| 291 | { | ||
| 292 | struct dprtc_rsp_get_irq_enable *rsp_params; | ||
| 293 | struct dprtc_cmd_get_irq *cmd_params; | ||
| 294 | struct fsl_mc_command cmd = { 0 }; | ||
| 295 | int err; | ||
| 296 | |||
| 297 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_GET_IRQ_ENABLE, | ||
| 298 | cmd_flags, | ||
| 299 | token); | ||
| 300 | cmd_params = (struct dprtc_cmd_get_irq *)cmd.params; | ||
| 301 | cmd_params->irq_index = irq_index; | ||
| 302 | |||
| 303 | err = mc_send_command(mc_io, &cmd); | ||
| 304 | if (err) | ||
| 305 | return err; | ||
| 306 | |||
| 307 | rsp_params = (struct dprtc_rsp_get_irq_enable *)cmd.params; | ||
| 308 | *en = rsp_params->en; | ||
| 309 | |||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | /** | ||
| 314 | * dprtc_set_irq_mask() - Set interrupt mask. | ||
| 315 | * @mc_io: Pointer to MC portal's I/O object | ||
| 316 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 317 | * @token: Token of DPRTC object | ||
| 318 | * @irq_index: The interrupt index to configure | ||
| 319 | * @mask: Event mask to trigger interrupt; | ||
| 320 | * each bit: | ||
| 321 | * 0 = ignore event | ||
| 322 | * 1 = consider event for asserting IRQ | ||
| 323 | * | ||
| 324 | * Every interrupt can have up to 32 causes and the interrupt model supports | ||
| 325 | * masking/unmasking each cause independently | ||
| 326 | * | ||
| 327 | * Return: '0' on Success; Error code otherwise. | ||
| 328 | */ | ||
| 329 | int dprtc_set_irq_mask(struct fsl_mc_io *mc_io, | ||
| 330 | u32 cmd_flags, | ||
| 331 | u16 token, | ||
| 332 | u8 irq_index, | ||
| 333 | u32 mask) | ||
| 334 | { | ||
| 335 | struct dprtc_cmd_set_irq_mask *cmd_params; | ||
| 336 | struct fsl_mc_command cmd = { 0 }; | ||
| 337 | |||
| 338 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_SET_IRQ_MASK, | ||
| 339 | cmd_flags, | ||
| 340 | token); | ||
| 341 | cmd_params = (struct dprtc_cmd_set_irq_mask *)cmd.params; | ||
| 342 | cmd_params->mask = cpu_to_le32(mask); | ||
| 343 | cmd_params->irq_index = irq_index; | ||
| 344 | |||
| 345 | return mc_send_command(mc_io, &cmd); | ||
| 346 | } | ||
| 347 | |||
| 348 | /** | ||
| 349 | * dprtc_get_irq_mask() - Get interrupt mask. | ||
| 350 | * @mc_io: Pointer to MC portal's I/O object | ||
| 351 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 352 | * @token: Token of DPRTC object | ||
| 353 | * @irq_index: The interrupt index to configure | ||
| 354 | * @mask: Returned event mask to trigger interrupt | ||
| 355 | * | ||
| 356 | * Every interrupt can have up to 32 causes and the interrupt model supports | ||
| 357 | * masking/unmasking each cause independently | ||
| 358 | * | ||
| 359 | * Return: '0' on Success; Error code otherwise. | ||
| 360 | */ | ||
| 361 | int dprtc_get_irq_mask(struct fsl_mc_io *mc_io, | ||
| 362 | u32 cmd_flags, | ||
| 363 | u16 token, | ||
| 364 | u8 irq_index, | ||
| 365 | u32 *mask) | ||
| 366 | { | ||
| 367 | struct dprtc_rsp_get_irq_mask *rsp_params; | ||
| 368 | struct dprtc_cmd_get_irq *cmd_params; | ||
| 369 | struct fsl_mc_command cmd = { 0 }; | ||
| 370 | int err; | ||
| 371 | |||
| 372 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_GET_IRQ_MASK, | ||
| 373 | cmd_flags, | ||
| 374 | token); | ||
| 375 | cmd_params = (struct dprtc_cmd_get_irq *)cmd.params; | ||
| 376 | cmd_params->irq_index = irq_index; | ||
| 377 | |||
| 378 | err = mc_send_command(mc_io, &cmd); | ||
| 379 | if (err) | ||
| 380 | return err; | ||
| 381 | |||
| 382 | rsp_params = (struct dprtc_rsp_get_irq_mask *)cmd.params; | ||
| 383 | *mask = le32_to_cpu(rsp_params->mask); | ||
| 384 | |||
| 385 | return 0; | ||
| 386 | } | ||
| 387 | |||
| 388 | /** | ||
| 389 | * dprtc_get_irq_status() - Get the current status of any pending interrupts. | ||
| 390 | * | ||
| 391 | * @mc_io: Pointer to MC portal's I/O object | ||
| 392 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 393 | * @token: Token of DPRTC object | ||
| 394 | * @irq_index: The interrupt index to configure | ||
| 395 | * @status: Returned interrupts status - one bit per cause: | ||
| 396 | * 0 = no interrupt pending | ||
| 397 | * 1 = interrupt pending | ||
| 398 | * | ||
| 399 | * Return: '0' on Success; Error code otherwise. | ||
| 400 | */ | ||
| 401 | int dprtc_get_irq_status(struct fsl_mc_io *mc_io, | ||
| 402 | u32 cmd_flags, | ||
| 403 | u16 token, | ||
| 404 | u8 irq_index, | ||
| 405 | u32 *status) | ||
| 406 | { | ||
| 407 | struct dprtc_cmd_get_irq_status *cmd_params; | ||
| 408 | struct dprtc_rsp_get_irq_status *rsp_params; | ||
| 409 | struct fsl_mc_command cmd = { 0 }; | ||
| 410 | int err; | ||
| 411 | |||
| 412 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_GET_IRQ_STATUS, | ||
| 413 | cmd_flags, | ||
| 414 | token); | ||
| 415 | cmd_params = (struct dprtc_cmd_get_irq_status *)cmd.params; | ||
| 416 | cmd_params->status = cpu_to_le32(*status); | ||
| 417 | cmd_params->irq_index = irq_index; | ||
| 418 | |||
| 419 | err = mc_send_command(mc_io, &cmd); | ||
| 420 | if (err) | ||
| 421 | return err; | ||
| 422 | |||
| 423 | rsp_params = (struct dprtc_rsp_get_irq_status *)cmd.params; | ||
| 424 | *status = le32_to_cpu(rsp_params->status); | ||
| 425 | |||
| 426 | return 0; | ||
| 427 | } | ||
| 428 | |||
| 429 | /** | ||
| 430 | * dprtc_clear_irq_status() - Clear a pending interrupt's status | ||
| 431 | * | ||
| 432 | * @mc_io: Pointer to MC portal's I/O object | ||
| 433 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 434 | * @token: Token of DPRTC object | ||
| 435 | * @irq_index: The interrupt index to configure | ||
| 436 | * @status: Bits to clear (W1C) - one bit per cause: | ||
| 437 | * 0 = don't change | ||
| 438 | * 1 = clear status bit | ||
| 439 | * | ||
| 440 | * Return: '0' on Success; Error code otherwise. | ||
| 441 | */ | ||
| 442 | int dprtc_clear_irq_status(struct fsl_mc_io *mc_io, | ||
| 443 | u32 cmd_flags, | ||
| 444 | u16 token, | ||
| 445 | u8 irq_index, | ||
| 446 | u32 status) | ||
| 447 | { | ||
| 448 | struct dprtc_cmd_clear_irq_status *cmd_params; | ||
| 449 | struct fsl_mc_command cmd = { 0 }; | ||
| 450 | |||
| 451 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_CLEAR_IRQ_STATUS, | ||
| 452 | cmd_flags, | ||
| 453 | token); | ||
| 454 | cmd_params = (struct dprtc_cmd_clear_irq_status *)cmd.params; | ||
| 455 | cmd_params->irq_index = irq_index; | ||
| 456 | cmd_params->status = cpu_to_le32(status); | ||
| 457 | |||
| 458 | return mc_send_command(mc_io, &cmd); | ||
| 459 | } | ||
| 460 | |||
| 461 | /** | ||
| 462 | * dprtc_get_attributes - Retrieve DPRTC attributes. | ||
| 463 | * | ||
| 464 | * @mc_io: Pointer to MC portal's I/O object | ||
| 465 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 466 | * @token: Token of DPRTC object | ||
| 467 | * @attr: Returned object's attributes | ||
| 468 | * | ||
| 469 | * Return: '0' on Success; Error code otherwise. | ||
| 470 | */ | ||
| 471 | int dprtc_get_attributes(struct fsl_mc_io *mc_io, | ||
| 472 | u32 cmd_flags, | ||
| 473 | u16 token, | ||
| 474 | struct dprtc_attr *attr) | ||
| 475 | { | ||
| 476 | struct dprtc_rsp_get_attributes *rsp_params; | ||
| 477 | struct fsl_mc_command cmd = { 0 }; | ||
| 478 | int err; | ||
| 479 | |||
| 480 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_GET_ATTR, | ||
| 481 | cmd_flags, | ||
| 482 | token); | ||
| 483 | |||
| 484 | err = mc_send_command(mc_io, &cmd); | ||
| 485 | if (err) | ||
| 486 | return err; | ||
| 487 | |||
| 488 | rsp_params = (struct dprtc_rsp_get_attributes *)cmd.params; | ||
| 489 | attr->id = le32_to_cpu(rsp_params->id); | ||
| 490 | |||
| 491 | return 0; | ||
| 492 | } | ||
| 493 | |||
| 494 | /** | ||
| 495 | * dprtc_set_clock_offset() - Sets the clock's offset | ||
| 496 | * (usually relative to another clock). | ||
| 497 | * | ||
| 498 | * @mc_io: Pointer to MC portal's I/O object | ||
| 499 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 500 | * @token: Token of DPRTC object | ||
| 501 | * @offset: New clock offset (in nanoseconds). | ||
| 502 | * | ||
| 503 | * Return: '0' on Success; Error code otherwise. | ||
| 504 | */ | ||
| 505 | int dprtc_set_clock_offset(struct fsl_mc_io *mc_io, | ||
| 506 | u32 cmd_flags, | ||
| 507 | u16 token, | ||
| 508 | int64_t offset) | ||
| 509 | { | ||
| 510 | struct dprtc_cmd_set_clock_offset *cmd_params; | ||
| 511 | struct fsl_mc_command cmd = { 0 }; | ||
| 512 | |||
| 513 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_SET_CLOCK_OFFSET, | ||
| 514 | cmd_flags, | ||
| 515 | token); | ||
| 516 | cmd_params = (struct dprtc_cmd_set_clock_offset *)cmd.params; | ||
| 517 | cmd_params->offset = cpu_to_le64(offset); | ||
| 518 | |||
| 519 | return mc_send_command(mc_io, &cmd); | ||
| 520 | } | ||
| 521 | |||
| 522 | /** | ||
| 523 | * dprtc_set_freq_compensation() - Sets a new frequency compensation value. | ||
| 524 | * | ||
| 525 | * @mc_io: Pointer to MC portal's I/O object | ||
| 526 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 527 | * @token: Token of DPRTC object | ||
| 528 | * @freq_compensation: The new frequency compensation value to set. | ||
| 529 | * | ||
| 530 | * Return: '0' on Success; Error code otherwise. | ||
| 531 | */ | ||
| 532 | int dprtc_set_freq_compensation(struct fsl_mc_io *mc_io, | ||
| 533 | u32 cmd_flags, | ||
| 534 | u16 token, | ||
| 535 | u32 freq_compensation) | ||
| 536 | { | ||
| 537 | struct dprtc_get_freq_compensation *cmd_params; | ||
| 538 | struct fsl_mc_command cmd = { 0 }; | ||
| 539 | |||
| 540 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_SET_FREQ_COMPENSATION, | ||
| 541 | cmd_flags, | ||
| 542 | token); | ||
| 543 | cmd_params = (struct dprtc_get_freq_compensation *)cmd.params; | ||
| 544 | cmd_params->freq_compensation = cpu_to_le32(freq_compensation); | ||
| 545 | |||
| 546 | return mc_send_command(mc_io, &cmd); | ||
| 547 | } | ||
| 548 | |||
| 549 | /** | ||
| 550 | * dprtc_get_freq_compensation() - Retrieves the frequency compensation value | ||
| 551 | * | ||
| 552 | * @mc_io: Pointer to MC portal's I/O object | ||
| 553 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 554 | * @token: Token of DPRTC object | ||
| 555 | * @freq_compensation: Frequency compensation value | ||
| 556 | * | ||
| 557 | * Return: '0' on Success; Error code otherwise. | ||
| 558 | */ | ||
| 559 | int dprtc_get_freq_compensation(struct fsl_mc_io *mc_io, | ||
| 560 | u32 cmd_flags, | ||
| 561 | u16 token, | ||
| 562 | u32 *freq_compensation) | ||
| 563 | { | ||
| 564 | struct dprtc_get_freq_compensation *rsp_params; | ||
| 565 | struct fsl_mc_command cmd = { 0 }; | ||
| 566 | int err; | ||
| 567 | |||
| 568 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_GET_FREQ_COMPENSATION, | ||
| 569 | cmd_flags, | ||
| 570 | token); | ||
| 571 | |||
| 572 | err = mc_send_command(mc_io, &cmd); | ||
| 573 | if (err) | ||
| 574 | return err; | ||
| 575 | |||
| 576 | rsp_params = (struct dprtc_get_freq_compensation *)cmd.params; | ||
| 577 | *freq_compensation = le32_to_cpu(rsp_params->freq_compensation); | ||
| 578 | |||
| 579 | return 0; | ||
| 580 | } | ||
| 581 | |||
| 582 | /** | ||
| 583 | * dprtc_get_time() - Returns the current RTC time. | ||
| 584 | * | ||
| 585 | * @mc_io: Pointer to MC portal's I/O object | ||
| 586 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 587 | * @token: Token of DPRTC object | ||
| 588 | * @time: Current RTC time. | ||
| 589 | * | ||
| 590 | * Return: '0' on Success; Error code otherwise. | ||
| 591 | */ | ||
| 592 | int dprtc_get_time(struct fsl_mc_io *mc_io, | ||
| 593 | u32 cmd_flags, | ||
| 594 | u16 token, | ||
| 595 | uint64_t *time) | ||
| 596 | { | ||
| 597 | struct dprtc_time *rsp_params; | ||
| 598 | struct fsl_mc_command cmd = { 0 }; | ||
| 599 | int err; | ||
| 600 | |||
| 601 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_GET_TIME, | ||
| 602 | cmd_flags, | ||
| 603 | token); | ||
| 604 | |||
| 605 | err = mc_send_command(mc_io, &cmd); | ||
| 606 | if (err) | ||
| 607 | return err; | ||
| 608 | |||
| 609 | rsp_params = (struct dprtc_time *)cmd.params; | ||
| 610 | *time = le64_to_cpu(rsp_params->time); | ||
| 611 | |||
| 612 | return 0; | ||
| 613 | } | ||
| 614 | |||
| 615 | /** | ||
| 616 | * dprtc_set_time() - Updates current RTC time. | ||
| 617 | * | ||
| 618 | * @mc_io: Pointer to MC portal's I/O object | ||
| 619 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 620 | * @token: Token of DPRTC object | ||
| 621 | * @time: New RTC time. | ||
| 622 | * | ||
| 623 | * Return: '0' on Success; Error code otherwise. | ||
| 624 | */ | ||
| 625 | int dprtc_set_time(struct fsl_mc_io *mc_io, | ||
| 626 | u32 cmd_flags, | ||
| 627 | u16 token, | ||
| 628 | uint64_t time) | ||
| 629 | { | ||
| 630 | struct dprtc_time *cmd_params; | ||
| 631 | struct fsl_mc_command cmd = { 0 }; | ||
| 632 | |||
| 633 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_SET_TIME, | ||
| 634 | cmd_flags, | ||
| 635 | token); | ||
| 636 | cmd_params = (struct dprtc_time *)cmd.params; | ||
| 637 | cmd_params->time = cpu_to_le64(time); | ||
| 638 | |||
| 639 | return mc_send_command(mc_io, &cmd); | ||
| 640 | } | ||
| 641 | |||
| 642 | /** | ||
| 643 | * dprtc_set_alarm() - Defines and sets alarm. | ||
| 644 | * | ||
| 645 | * @mc_io: Pointer to MC portal's I/O object | ||
| 646 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 647 | * @token: Token of DPRTC object | ||
| 648 | * @time: In nanoseconds, the time when the alarm | ||
| 649 | * should go off - must be a multiple of | ||
| 650 | * 1 microsecond | ||
| 651 | * | ||
| 652 | * Return: '0' on Success; Error code otherwise. | ||
| 653 | */ | ||
| 654 | int dprtc_set_alarm(struct fsl_mc_io *mc_io, | ||
| 655 | u32 cmd_flags, | ||
| 656 | u16 token, uint64_t time) | ||
| 657 | { | ||
| 658 | struct dprtc_time *cmd_params; | ||
| 659 | struct fsl_mc_command cmd = { 0 }; | ||
| 660 | |||
| 661 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_SET_ALARM, | ||
| 662 | cmd_flags, | ||
| 663 | token); | ||
| 664 | cmd_params = (struct dprtc_time *)cmd.params; | ||
| 665 | cmd_params->time = cpu_to_le64(time); | ||
| 666 | |||
| 667 | return mc_send_command(mc_io, &cmd); | ||
| 668 | } | ||
| 669 | |||
| 670 | /** | ||
| 671 | * dprtc_get_api_version() - Get Data Path Real Time Counter API version | ||
| 672 | * @mc_io: Pointer to MC portal's I/O object | ||
| 673 | * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' | ||
| 674 | * @major_ver: Major version of data path real time counter API | ||
| 675 | * @minor_ver: Minor version of data path real time counter API | ||
| 676 | * | ||
| 677 | * Return: '0' on Success; Error code otherwise. | ||
| 678 | */ | ||
| 679 | int dprtc_get_api_version(struct fsl_mc_io *mc_io, | ||
| 680 | u32 cmd_flags, | ||
| 681 | u16 *major_ver, | ||
| 682 | u16 *minor_ver) | ||
| 683 | { | ||
| 684 | struct dprtc_rsp_get_api_version *rsp_params; | ||
| 685 | struct fsl_mc_command cmd = { 0 }; | ||
| 686 | int err; | ||
| 687 | |||
| 688 | cmd.header = mc_encode_cmd_header(DPRTC_CMDID_GET_API_VERSION, | ||
| 689 | cmd_flags, | ||
| 690 | 0); | ||
| 691 | |||
| 692 | err = mc_send_command(mc_io, &cmd); | ||
| 693 | if (err) | ||
| 694 | return err; | ||
| 695 | |||
| 696 | rsp_params = (struct dprtc_rsp_get_api_version *)cmd.params; | ||
| 697 | *major_ver = le16_to_cpu(rsp_params->major); | ||
| 698 | *minor_ver = le16_to_cpu(rsp_params->minor); | ||
| 699 | |||
| 700 | return 0; | ||
| 701 | } | ||
| @@ -0,0 +1,164 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright 2013-2016 Freescale Semiconductor Inc. | ||
| 4 | * Copyright 2016-2018 NXP | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef __FSL_DPRTC_H | ||
| 8 | #define __FSL_DPRTC_H | ||
| 9 | |||
| 10 | /* Data Path Real Time Counter API | ||
| 11 | * Contains initialization APIs and runtime control APIs for RTC | ||
| 12 | */ | ||
| 13 | |||
| 14 | struct fsl_mc_io; | ||
| 15 | |||
| 16 | /** | ||
| 17 | * Number of irq's | ||
| 18 | */ | ||
| 19 | #define DPRTC_MAX_IRQ_NUM 1 | ||
| 20 | #define DPRTC_IRQ_INDEX 0 | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Interrupt event masks: | ||
| 24 | */ | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Interrupt event mask indicating alarm event had occurred | ||
| 28 | */ | ||
| 29 | #define DPRTC_EVENT_ALARM 0x40000000 | ||
| 30 | /** | ||
| 31 | * Interrupt event mask indicating periodic pulse event had occurred | ||
| 32 | */ | ||
| 33 | #define DPRTC_EVENT_PPS 0x08000000 | ||
| 34 | |||
| 35 | int dprtc_open(struct fsl_mc_io *mc_io, | ||
| 36 | u32 cmd_flags, | ||
| 37 | int dprtc_id, | ||
| 38 | u16 *token); | ||
| 39 | |||
| 40 | int dprtc_close(struct fsl_mc_io *mc_io, | ||
| 41 | u32 cmd_flags, | ||
| 42 | u16 token); | ||
| 43 | |||
| 44 | /** | ||
| 45 | * struct dprtc_cfg - Structure representing DPRTC configuration | ||
| 46 | * @options: place holder | ||
| 47 | */ | ||
| 48 | struct dprtc_cfg { | ||
| 49 | u32 options; | ||
| 50 | }; | ||
| 51 | |||
| 52 | int dprtc_create(struct fsl_mc_io *mc_io, | ||
| 53 | u16 dprc_token, | ||
| 54 | u32 cmd_flags, | ||
| 55 | const struct dprtc_cfg *cfg, | ||
| 56 | u32 *obj_id); | ||
| 57 | |||
| 58 | int dprtc_destroy(struct fsl_mc_io *mc_io, | ||
| 59 | u16 dprc_token, | ||
| 60 | u32 cmd_flags, | ||
| 61 | u32 object_id); | ||
| 62 | |||
| 63 | int dprtc_enable(struct fsl_mc_io *mc_io, | ||
| 64 | u32 cmd_flags, | ||
| 65 | u16 token); | ||
| 66 | |||
| 67 | int dprtc_disable(struct fsl_mc_io *mc_io, | ||
| 68 | u32 cmd_flags, | ||
| 69 | u16 token); | ||
| 70 | |||
| 71 | int dprtc_is_enabled(struct fsl_mc_io *mc_io, | ||
| 72 | u32 cmd_flags, | ||
| 73 | u16 token, | ||
| 74 | int *en); | ||
| 75 | |||
| 76 | int dprtc_reset(struct fsl_mc_io *mc_io, | ||
| 77 | u32 cmd_flags, | ||
| 78 | u16 token); | ||
| 79 | |||
| 80 | int dprtc_set_clock_offset(struct fsl_mc_io *mc_io, | ||
| 81 | u32 cmd_flags, | ||
| 82 | u16 token, | ||
| 83 | int64_t offset); | ||
| 84 | |||
| 85 | int dprtc_set_freq_compensation(struct fsl_mc_io *mc_io, | ||
| 86 | u32 cmd_flags, | ||
| 87 | u16 token, | ||
| 88 | u32 freq_compensation); | ||
| 89 | |||
| 90 | int dprtc_get_freq_compensation(struct fsl_mc_io *mc_io, | ||
| 91 | u32 cmd_flags, | ||
| 92 | u16 token, | ||
| 93 | u32 *freq_compensation); | ||
| 94 | |||
| 95 | int dprtc_get_time(struct fsl_mc_io *mc_io, | ||
| 96 | u32 cmd_flags, | ||
| 97 | u16 token, | ||
| 98 | uint64_t *time); | ||
| 99 | |||
| 100 | int dprtc_set_time(struct fsl_mc_io *mc_io, | ||
| 101 | u32 cmd_flags, | ||
| 102 | u16 token, | ||
| 103 | uint64_t time); | ||
| 104 | |||
| 105 | int dprtc_set_alarm(struct fsl_mc_io *mc_io, | ||
| 106 | u32 cmd_flags, | ||
| 107 | u16 token, | ||
| 108 | uint64_t time); | ||
| 109 | |||
| 110 | int dprtc_set_irq_enable(struct fsl_mc_io *mc_io, | ||
| 111 | u32 cmd_flags, | ||
| 112 | u16 token, | ||
| 113 | u8 irq_index, | ||
| 114 | u8 en); | ||
| 115 | |||
| 116 | int dprtc_get_irq_enable(struct fsl_mc_io *mc_io, | ||
| 117 | u32 cmd_flags, | ||
| 118 | u16 token, | ||
| 119 | u8 irq_index, | ||
| 120 | u8 *en); | ||
| 121 | |||
| 122 | int dprtc_set_irq_mask(struct fsl_mc_io *mc_io, | ||
| 123 | u32 cmd_flags, | ||
| 124 | u16 token, | ||
| 125 | u8 irq_index, | ||
| 126 | u32 mask); | ||
| 127 | |||
| 128 | int dprtc_get_irq_mask(struct fsl_mc_io *mc_io, | ||
| 129 | u32 cmd_flags, | ||
| 130 | u16 token, | ||
| 131 | u8 irq_index, | ||
| 132 | u32 *mask); | ||
| 133 | |||
| 134 | int dprtc_get_irq_status(struct fsl_mc_io *mc_io, | ||
| 135 | u32 cmd_flags, | ||
| 136 | u16 token, | ||
| 137 | u8 irq_index, | ||
| 138 | u32 *status); | ||
| 139 | |||
| 140 | int dprtc_clear_irq_status(struct fsl_mc_io *mc_io, | ||
| 141 | u32 cmd_flags, | ||
| 142 | u16 token, | ||
| 143 | u8 irq_index, | ||
| 144 | u32 status); | ||
| 145 | |||
| 146 | /** | ||
| 147 | * struct dprtc_attr - Structure representing DPRTC attributes | ||
| 148 | * @id: DPRTC object ID | ||
| 149 | */ | ||
| 150 | struct dprtc_attr { | ||
| 151 | int id; | ||
| 152 | }; | ||
| 153 | |||
| 154 | int dprtc_get_attributes(struct fsl_mc_io *mc_io, | ||
| 155 | u32 cmd_flags, | ||
| 156 | u16 token, | ||
| 157 | struct dprtc_attr *attr); | ||
| 158 | |||
| 159 | int dprtc_get_api_version(struct fsl_mc_io *mc_io, | ||
| 160 | u32 cmd_flags, | ||
| 161 | u16 *major_ver, | ||
| 162 | u16 *minor_ver); | ||
| 163 | |||
| 164 | #endif /* __FSL_DPRTC_H */ | ||
| @@ -0,0 +1,229 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright 2013-2016 Freescale Semiconductor Inc. | ||
| 4 | * Copyright 2016-2018 NXP | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/module.h> | ||
| 8 | #include <linux/slab.h> | ||
| 9 | #include <linux/ptp_clock_kernel.h> | ||
| 10 | #include <linux/fsl/mc.h> | ||
| 11 | |||
| 12 | #include "rtc.h" | ||
| 13 | |||
| 14 | struct ptp_dpaa2_priv { | ||
| 15 | struct fsl_mc_device *rtc_mc_dev; | ||
| 16 | struct ptp_clock *clock; | ||
| 17 | struct ptp_clock_info caps; | ||
| 18 | u32 freq_comp; | ||
| 19 | }; | ||
| 20 | |||
| 21 | /* PTP clock operations */ | ||
| 22 | static int ptp_dpaa2_adjfreq(struct ptp_clock_info *ptp, s32 ppb) | ||
| 23 | { | ||
| 24 | struct ptp_dpaa2_priv *ptp_dpaa2 = | ||
| 25 | container_of(ptp, struct ptp_dpaa2_priv, caps); | ||
| 26 | struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev; | ||
| 27 | struct device *dev = &mc_dev->dev; | ||
| 28 | u64 adj; | ||
| 29 | u32 diff, tmr_add; | ||
| 30 | int neg_adj = 0; | ||
| 31 | int err = 0; | ||
| 32 | |||
| 33 | if (ppb < 0) { | ||
| 34 | neg_adj = 1; | ||
| 35 | ppb = -ppb; | ||
| 36 | } | ||
| 37 | |||
| 38 | tmr_add = ptp_dpaa2->freq_comp; | ||
| 39 | adj = tmr_add; | ||
| 40 | adj *= ppb; | ||
| 41 | diff = div_u64(adj, 1000000000ULL); | ||
| 42 | |||
| 43 | tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; | ||
| 44 | |||
| 45 | err = dprtc_set_freq_compensation(mc_dev->mc_io, 0, | ||
| 46 | mc_dev->mc_handle, tmr_add); | ||
| 47 | if (err) | ||
| 48 | dev_err(dev, "dprtc_set_freq_compensation err %d\n", err); | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | static int ptp_dpaa2_adjtime(struct ptp_clock_info *ptp, s64 delta) | ||
| 53 | { | ||
| 54 | struct ptp_dpaa2_priv *ptp_dpaa2 = | ||
| 55 | container_of(ptp, struct ptp_dpaa2_priv, caps); | ||
| 56 | struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev; | ||
| 57 | struct device *dev = &mc_dev->dev; | ||
| 58 | s64 now; | ||
| 59 | int err = 0; | ||
| 60 | |||
| 61 | err = dprtc_get_time(mc_dev->mc_io, 0, mc_dev->mc_handle, &now); | ||
| 62 | if (err) { | ||
| 63 | dev_err(dev, "dprtc_get_time err %d\n", err); | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | now += delta; | ||
| 68 | |||
| 69 | err = dprtc_set_time(mc_dev->mc_io, 0, mc_dev->mc_handle, now); | ||
| 70 | if (err) { | ||
| 71 | dev_err(dev, "dprtc_set_time err %d\n", err); | ||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | static int ptp_dpaa2_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) | ||
| 78 | { | ||
| 79 | struct ptp_dpaa2_priv *ptp_dpaa2 = | ||
| 80 | container_of(ptp, struct ptp_dpaa2_priv, caps); | ||
| 81 | struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev; | ||
| 82 | struct device *dev = &mc_dev->dev; | ||
| 83 | u64 ns; | ||
| 84 | u32 remainder; | ||
| 85 | int err = 0; | ||
| 86 | |||
| 87 | err = dprtc_get_time(mc_dev->mc_io, 0, mc_dev->mc_handle, &ns); | ||
| 88 | if (err) { | ||
| 89 | dev_err(dev, "dprtc_get_time err %d\n", err); | ||
| 90 | return 0; | ||
| 91 | } | ||
| 92 | |||
| 93 | ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder); | ||
| 94 | ts->tv_nsec = remainder; | ||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int ptp_dpaa2_settime(struct ptp_clock_info *ptp, | ||
| 99 | const struct timespec64 *ts) | ||
| 100 | { | ||
| 101 | struct ptp_dpaa2_priv *ptp_dpaa2 = | ||
| 102 | container_of(ptp, struct ptp_dpaa2_priv, caps); | ||
| 103 | struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev; | ||
| 104 | struct device *dev = &mc_dev->dev; | ||
| 105 | u64 ns; | ||
| 106 | int err = 0; | ||
| 107 | |||
| 108 | ns = ts->tv_sec * 1000000000ULL; | ||
| 109 | ns += ts->tv_nsec; | ||
| 110 | |||
| 111 | err = dprtc_set_time(mc_dev->mc_io, 0, mc_dev->mc_handle, ns); | ||
| 112 | if (err) | ||
| 113 | dev_err(dev, "dprtc_set_time err %d\n", err); | ||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | static struct ptp_clock_info ptp_dpaa2_caps = { | ||
| 118 | .owner = THIS_MODULE, | ||
| 119 | .name = "DPAA2 PTP Clock", | ||
| 120 | .max_adj = 512000, | ||
| 121 | .n_alarm = 2, | ||
| 122 | .n_ext_ts = 2, | ||
| 123 | .n_per_out = 3, | ||
| 124 | .n_pins = 0, | ||
| 125 | .pps = 1, | ||
| 126 | .adjfreq = ptp_dpaa2_adjfreq, | ||
| 127 | .adjtime = ptp_dpaa2_adjtime, | ||
| 128 | .gettime64 = ptp_dpaa2_gettime, | ||
| 129 | .settime64 = ptp_dpaa2_settime, | ||
| 130 | }; | ||
| 131 | |||
| 132 | static int rtc_probe(struct fsl_mc_device *mc_dev) | ||
| 133 | { | ||
| 134 | struct device *dev = &mc_dev->dev; | ||
| 135 | struct ptp_dpaa2_priv *ptp_dpaa2; | ||
| 136 | u32 tmr_add = 0; | ||
| 137 | int err; | ||
| 138 | |||
| 139 | ptp_dpaa2 = kzalloc(sizeof(*ptp_dpaa2), GFP_KERNEL); | ||
| 140 | if (!ptp_dpaa2) | ||
| 141 | return -ENOMEM; | ||
| 142 | |||
| 143 | err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io); | ||
| 144 | if (err) { | ||
| 145 | dev_err(dev, "fsl_mc_portal_allocate err %d\n", err); | ||
| 146 | goto err_exit; | ||
| 147 | } | ||
| 148 | |||
| 149 | err = dprtc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, | ||
| 150 | &mc_dev->mc_handle); | ||
| 151 | if (err) { | ||
| 152 | dev_err(dev, "dprtc_open err %d\n", err); | ||
| 153 | goto err_free_mcp; | ||
| 154 | } | ||
| 155 | |||
| 156 | ptp_dpaa2->rtc_mc_dev = mc_dev; | ||
| 157 | |||
| 158 | err = dprtc_get_freq_compensation(mc_dev->mc_io, 0, | ||
| 159 | mc_dev->mc_handle, &tmr_add); | ||
| 160 | if (err) { | ||
| 161 | dev_err(dev, "dprtc_get_freq_compensation err %d\n", err); | ||
| 162 | goto err_close; | ||
| 163 | } | ||
| 164 | |||
| 165 | ptp_dpaa2->freq_comp = tmr_add; | ||
| 166 | ptp_dpaa2->caps = ptp_dpaa2_caps; | ||
| 167 | |||
| 168 | ptp_dpaa2->clock = ptp_clock_register(&ptp_dpaa2->caps, dev); | ||
| 169 | if (IS_ERR(ptp_dpaa2->clock)) { | ||
| 170 | err = PTR_ERR(ptp_dpaa2->clock); | ||
| 171 | goto err_close; | ||
| 172 | } | ||
| 173 | |||
| 174 | dpaa2_phc_index = ptp_clock_index(ptp_dpaa2->clock); | ||
| 175 | |||
| 176 | dev_set_drvdata(dev, ptp_dpaa2); | ||
| 177 | |||
| 178 | return 0; | ||
| 179 | |||
| 180 | err_close: | ||
| 181 | dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); | ||
| 182 | err_free_mcp: | ||
| 183 | fsl_mc_portal_free(mc_dev->mc_io); | ||
| 184 | err_exit: | ||
| 185 | kfree(ptp_dpaa2); | ||
| 186 | dev_set_drvdata(dev, NULL); | ||
| 187 | return err; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int rtc_remove(struct fsl_mc_device *mc_dev) | ||
| 191 | { | ||
| 192 | struct ptp_dpaa2_priv *ptp_dpaa2; | ||
| 193 | struct device *dev = &mc_dev->dev; | ||
| 194 | |||
| 195 | ptp_dpaa2 = dev_get_drvdata(dev); | ||
| 196 | ptp_clock_unregister(ptp_dpaa2->clock); | ||
| 197 | |||
| 198 | dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); | ||
| 199 | fsl_mc_portal_free(mc_dev->mc_io); | ||
| 200 | |||
| 201 | kfree(ptp_dpaa2); | ||
| 202 | dev_set_drvdata(dev, NULL); | ||
| 203 | |||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | static const struct fsl_mc_device_id rtc_match_id_table[] = { | ||
| 208 | { | ||
| 209 | .vendor = FSL_MC_VENDOR_FREESCALE, | ||
| 210 | .obj_type = "dprtc", | ||
| 211 | }, | ||
| 212 | {} | ||
| 213 | }; | ||
| 214 | MODULE_DEVICE_TABLE(fslmc, rtc_match_id_table); | ||
| 215 | |||
| 216 | static struct fsl_mc_driver rtc_drv = { | ||
| 217 | .driver = { | ||
| 218 | .name = KBUILD_MODNAME, | ||
| 219 | .owner = THIS_MODULE, | ||
| 220 | }, | ||
| 221 | .probe = rtc_probe, | ||
| 222 | .remove = rtc_remove, | ||
| 223 | .match_id_table = rtc_match_id_table, | ||
| 224 | }; | ||
| 225 | |||
| 226 | module_fsl_mc_driver(rtc_drv); | ||
| 227 | |||
| 228 | MODULE_LICENSE("GPL v2"); | ||
| 229 | MODULE_DESCRIPTION("DPAA2 PTP Clock Driver"); | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright 2018 NXP | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef __RTC_H | ||
| 7 | #define __RTC_H | ||
| 8 | |||
| 9 | #include "dprtc.h" | ||
| 10 | #include "dprtc-cmd.h" | ||
| 11 | |||
| 12 | extern int dpaa2_phc_index; | ||
| 13 | |||
| 14 | #endif | ||
| @@ -13,6 +13,7 @@ · 12 unmodified lines | |||
| 13 | #include <linux/msi.h> | 13 | #include <linux/msi.h> |
| 14 | #include <linux/dma-mapping.h> | 14 | #include <linux/dma-mapping.h> |
| 15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
| 16 | #include <linux/io.h> | ||
| 16 | 17 | ||
| 17 | #include <linux/fsl/mc.h> | 18 | #include <linux/fsl/mc.h> |
| 18 | #include "../../include/dpaa2-io.h" | 19 | #include "../../include/dpaa2-io.h" |
| @@ -100,7 +101,7 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev) · 81 unmodified lines | |||
| 100 | if (err) { | 101 | if (err) { |
| 101 | dev_dbg(dev, "MC portal allocation failed\n"); | 102 | dev_dbg(dev, "MC portal allocation failed\n"); |
| 102 | err = -EPROBE_DEFER; | 103 | err = -EPROBE_DEFER; |
| 103 | goto err_mcportal; | 104 | goto err_priv_alloc; |
| 104 | } | 105 | } |
| 105 | 106 | ||
| 106 | err = dpio_open(dpio_dev->mc_io, 0, dpio_dev->obj_desc.id, | 107 | err = dpio_open(dpio_dev->mc_io, 0, dpio_dev->obj_desc.id, |
| @@ -146,10 +147,22 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev) · 39 unmodified lines | |||
| 146 | * Set the CENA regs to be the cache inhibited area of the portal to | 147 | * Set the CENA regs to be the cache inhibited area of the portal to |
| 147 | * avoid coherency issues if a user migrates to another core. | 148 | * avoid coherency issues if a user migrates to another core. |
| 148 | */ | 149 | */ |
| 149 | desc.regs_cena = ioremap_wc(dpio_dev->regions[1].start, | 150 | desc.regs_cena = devm_memremap(dev, dpio_dev->regions[1].start, |
| 150 | resource_size(&dpio_dev->regions[1])); | 151 | resource_size(&dpio_dev->regions[1]), |
| 151 | desc.regs_cinh = ioremap(dpio_dev->regions[1].start, | 152 | MEMREMAP_WC); |
| 152 | resource_size(&dpio_dev->regions[1])); | 153 | if (IS_ERR(desc.regs_cena)) { |
| 154 | dev_err(dev, "devm_memremap failed\n"); | ||
| 155 | err = PTR_ERR(desc.regs_cena); | ||
| 156 | goto err_allocate_irqs; | ||
| 157 | } | ||
| 158 | |||
| 159 | desc.regs_cinh = devm_ioremap(dev, dpio_dev->regions[1].start, | ||
| 160 | resource_size(&dpio_dev->regions[1])); | ||
| 161 | if (!desc.regs_cinh) { | ||
| 162 | err = -ENOMEM; | ||
| 163 | dev_err(dev, "devm_ioremap failed\n"); | ||
| 164 | goto err_allocate_irqs; | ||
| 165 | } | ||
| 153 | 166 | ||
| 154 | err = fsl_mc_allocate_irqs(dpio_dev); | 167 | err = fsl_mc_allocate_irqs(dpio_dev); |
| 155 | if (err) { | 168 | if (err) { |
| @@ -164,6 +177,7 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev) · 8 unmodified lines | |||
| 164 | priv->io = dpaa2_io_create(&desc); | 177 | priv->io = dpaa2_io_create(&desc); |
| 165 | if (!priv->io) { | 178 | if (!priv->io) { |
| 166 | dev_err(dev, "dpaa2_io_create failed\n"); | 179 | dev_err(dev, "dpaa2_io_create failed\n"); |
| 180 | err = -ENOMEM; | ||
| 167 | goto err_dpaa2_io_create; | 181 | goto err_dpaa2_io_create; |
| 168 | } | 182 | } |
| 169 | 183 | ||
| @@ -185,8 +199,6 @@ err_get_attr: · 15 unmodified lines | |||
| 185 | dpio_close(dpio_dev->mc_io, 0, dpio_dev->mc_handle); | 199 | dpio_close(dpio_dev->mc_io, 0, dpio_dev->mc_handle); |
| 186 | err_open: | 200 | err_open: |
| 187 | fsl_mc_portal_free(dpio_dev->mc_io); | 201 | fsl_mc_portal_free(dpio_dev->mc_io); |
| 188 | err_mcportal: | ||
| 189 | dev_set_drvdata(dev, NULL); | ||
| 190 | err_priv_alloc: | 202 | err_priv_alloc: |
| 191 | return err; | 203 | return err; |
| 192 | } | 204 | } |
| @@ -230,8 +242,6 @@ static int dpaa2_dpio_remove(struct fsl_mc_device *dpio_dev) · 37 unmodified lines | |||
| 230 | 242 | ||
| 231 | fsl_mc_portal_free(dpio_dev->mc_io); | 243 | fsl_mc_portal_free(dpio_dev->mc_io); |
| 232 | 244 | ||
| 233 | dev_set_drvdata(dev, NULL); | ||
| 234 | |||
| 235 | return 0; | 245 | return 0; |
| 236 | 246 | ||
| 237 | err_open: | 247 | err_open: |
| @@ -15,7 +15,7 @@ struct qbman_swp; · 14 unmodified lines | |||
| 15 | /* qbman software portal descriptor structure */ | 15 | /* qbman software portal descriptor structure */ |
| 16 | struct qbman_swp_desc { | 16 | struct qbman_swp_desc { |
| 17 | void *cena_bar; /* Cache-enabled portal base address */ | 17 | void *cena_bar; /* Cache-enabled portal base address */ |
| 18 | void *cinh_bar; /* Cache-inhibited portal base address */ | 18 | void __iomem *cinh_bar; /* Cache-inhibited portal base address */ |
| 19 | u32 qman_version; | 19 | u32 qman_version; |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| @@ -102,7 +102,7 @@ struct qbman_release_desc { · 80 unmodified lines | |||
| 102 | /* portal data structure */ | 102 | /* portal data structure */ |
| 103 | struct qbman_swp { | 103 | struct qbman_swp { |
| 104 | const struct qbman_swp_desc *desc; | 104 | const struct qbman_swp_desc *desc; |
| 105 | void __iomem *addr_cena; | 105 | void *addr_cena; |
| 106 | void __iomem *addr_cinh; | 106 | void __iomem *addr_cinh; |
| 107 | 107 | ||
| 108 | /* Management commands */ | 108 | /* Management commands */ |
| @@ -52,7 +52,7 @@ struct dpaa2_io_desc { · 51 unmodified lines | |||
| 52 | int has_8prio; | 52 | int has_8prio; |
| 53 | int cpu; | 53 | int cpu; |
| 54 | void *regs_cena; | 54 | void *regs_cena; |
| 55 | void *regs_cinh; | 55 | void __iomem *regs_cinh; |
| 56 | int dpio_id; | 56 | int dpio_id; |
| 57 | u32 qman_version; | 57 | u32 qman_version; |
| 58 | }; | 58 | }; |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
| 15 | #include "gdm_endian.h" | 5 | #include "gdm_endian.h" |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef __GDM_ENDIAN_H__ | 4 | #ifndef __GDM_ENDIAN_H__ |
| 15 | #define __GDM_ENDIAN_H__ | 5 | #define __GDM_ENDIAN_H__ |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 15 | 5 | ||
| @@ -408,7 +398,7 @@ static s32 gdm_lte_tx_nic_type(struct net_device *dev, struct sk_buff *skb) · 392 unmodified lines | |||
| 408 | return nic_type; | 398 | return nic_type; |
| 409 | } | 399 | } |
| 410 | 400 | ||
| 411 | static int gdm_lte_tx(struct sk_buff *skb, struct net_device *dev) | 401 | static netdev_tx_t gdm_lte_tx(struct sk_buff *skb, struct net_device *dev) |
| 412 | { | 402 | { |
| 413 | struct nic *nic = netdev_priv(dev); | 403 | struct nic *nic = netdev_priv(dev); |
| 414 | u32 nic_type; | 404 | u32 nic_type; |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef _GDM_LTE_H_ | 4 | #ifndef _GDM_LTE_H_ |
| 15 | #define _GDM_LTE_H_ | 5 | #define _GDM_LTE_H_ |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 15 | 5 | ||
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef _GDM_MUX_H_ | 4 | #ifndef _GDM_MUX_H_ |
| 15 | #define _GDM_MUX_H_ | 5 | #define _GDM_MUX_H_ |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 15 | 5 | ||
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef _GDM_TTY_H_ | 4 | #ifndef _GDM_TTY_H_ |
| 15 | #define _GDM_TTY_H_ | 5 | #define _GDM_TTY_H_ |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 15 | 5 | ||
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef _GDM_USB_H_ | 4 | #ifndef _GDM_USB_H_ |
| 15 | #define _GDM_USB_H_ | 5 | #define _GDM_USB_H_ |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef _HCI_H_ | 4 | #ifndef _HCI_H_ |
| 15 | #define _HCI_H_ | 5 | #define _HCI_H_ |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef _HCI_PACKET_H_ | 4 | #ifndef _HCI_PACKET_H_ |
| 15 | #define _HCI_PACKET_H_ | 5 | #define _HCI_PACKET_H_ |
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 15 | 5 | ||
| @@ -1,15 +1,5 @@ | |||
| 1 | /* | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | 2 | /* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | 3 | ||
| 14 | #ifndef _NETLINK_K_H | 4 | #ifndef _NETLINK_K_H |
| 15 | #define _NETLINK_K_H | 5 | #define _NETLINK_K_H |
| @@ -1,13 +1,6 @@ | |||
| 1 | config GOLDFISH_AUDIO | 1 | config GOLDFISH_AUDIO |
| 2 | tristate "Goldfish AVD Audio Device" | 2 | tristate "Goldfish AVD Audio Device" |
| 3 | depends on GOLDFISH | 3 | depends on GOLDFISH |
| 4 | ---help--- | 4 | help |
| 5 | Emulated audio channel for the Goldfish Android Virtual Device | 5 | Emulated audio channel for the Goldfish Android Virtual Device |
| 6 | 6 | ||
| 7 | config MTD_GOLDFISH_NAND | ||
| 8 | tristate "Goldfish NAND device" | ||
| 9 | depends on GOLDFISH | ||
| 10 | depends on MTD | ||
| 11 | help | ||
| 12 | Drives the emulated NAND flash device on the Google Goldfish | ||
| 13 | Android virtual device. | ||
| @@ -3,4 +3,3 @@ · 2 unmodified lines | |||
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o | 5 | obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o |
| 6 | obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o | ||
| @@ -1,441 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * drivers/mtd/devices/goldfish_nand.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007 Google, Inc. | ||
| 5 | * Copyright (C) 2012 Intel, Inc. | ||
| 6 | * Copyright (C) 2013 Intel, Inc. | ||
| 7 | * | ||
| 8 | * This software is licensed under the terms of the GNU General Public | ||
| 9 | * License version 2, as published by the Free Software Foundation, and | ||
| 10 | * may be copied, distributed, and modified under those terms. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/io.h> | ||
| 20 | #include <linux/device.h> | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | #include <linux/ioport.h> | ||
| 24 | #include <linux/vmalloc.h> | ||
| 25 | #include <linux/mtd/mtd.h> | ||
| 26 | #include <linux/platform_device.h> | ||
| 27 | #include <linux/mutex.h> | ||
| 28 | #include <linux/goldfish.h> | ||
| 29 | #include <asm/div64.h> | ||
| 30 | #include <linux/dma-mapping.h> | ||
| 31 | |||
| 32 | #include "goldfish_nand_reg.h" | ||
| 33 | |||
| 34 | struct goldfish_nand { | ||
| 35 | /* lock protects access to the device registers */ | ||
| 36 | struct mutex lock; | ||
| 37 | unsigned char __iomem *base; | ||
| 38 | struct cmd_params *cmd_params; | ||
| 39 | size_t mtd_count; | ||
| 40 | struct mtd_info mtd[0]; | ||
| 41 | }; | ||
| 42 | |||
| 43 | static u32 goldfish_nand_cmd_with_params(struct mtd_info *mtd, | ||
| 44 | enum nand_cmd cmd, u64 addr, u32 len, | ||
| 45 | void *ptr, u32 *rv) | ||
| 46 | { | ||
| 47 | u32 cmdp; | ||
| 48 | struct goldfish_nand *nand = mtd->priv; | ||
| 49 | struct cmd_params *cps = nand->cmd_params; | ||
| 50 | unsigned char __iomem *base = nand->base; | ||
| 51 | |||
| 52 | if (!cps) | ||
| 53 | return -1; | ||
| 54 | |||
| 55 | switch (cmd) { | ||
| 56 | case NAND_CMD_ERASE: | ||
| 57 | cmdp = NAND_CMD_ERASE_WITH_PARAMS; | ||
| 58 | break; | ||
| 59 | case NAND_CMD_READ: | ||
| 60 | cmdp = NAND_CMD_READ_WITH_PARAMS; | ||
| 61 | break; | ||
| 62 | case NAND_CMD_WRITE: | ||
| 63 | cmdp = NAND_CMD_WRITE_WITH_PARAMS; | ||
| 64 | break; | ||
| 65 | default: | ||
| 66 | return -1; | ||
| 67 | } | ||
| 68 | cps->dev = mtd - nand->mtd; | ||
| 69 | cps->addr_high = (u32)(addr >> 32); | ||
| 70 | cps->addr_low = (u32)addr; | ||
| 71 | cps->transfer_size = len; | ||
| 72 | cps->data = (unsigned long)ptr; | ||
| 73 | writel(cmdp, base + NAND_COMMAND); | ||
| 74 | *rv = cps->result; | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | static u32 goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd, | ||
| 79 | u64 addr, u32 len, void *ptr) | ||
| 80 | { | ||
| 81 | struct goldfish_nand *nand = mtd->priv; | ||
| 82 | u32 rv; | ||
| 83 | unsigned char __iomem *base = nand->base; | ||
| 84 | |||
| 85 | mutex_lock(&nand->lock); | ||
| 86 | if (goldfish_nand_cmd_with_params(mtd, cmd, addr, len, ptr, &rv)) { | ||
| 87 | writel(mtd - nand->mtd, base + NAND_DEV); | ||
| 88 | writel((u32)(addr >> 32), base + NAND_ADDR_HIGH); | ||
| 89 | writel((u32)addr, base + NAND_ADDR_LOW); | ||
| 90 | writel(len, base + NAND_TRANSFER_SIZE); | ||
| 91 | gf_write_ptr(ptr, base + NAND_DATA, base + NAND_DATA_HIGH); | ||
| 92 | writel(cmd, base + NAND_COMMAND); | ||
| 93 | rv = readl(base + NAND_RESULT); | ||
| 94 | } | ||
| 95 | mutex_unlock(&nand->lock); | ||
| 96 | return rv; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
| 100 | { | ||
| 101 | loff_t ofs = instr->addr; | ||
| 102 | u32 len = instr->len; | ||
| 103 | s32 rem; | ||
| 104 | |||
| 105 | if (ofs + len > mtd->size) | ||
| 106 | goto invalid_arg; | ||
| 107 | ofs = div_s64_rem(ofs, mtd->writesize, &rem); | ||
| 108 | if (rem) | ||
| 109 | goto invalid_arg; | ||
| 110 | ofs *= (mtd->writesize + mtd->oobsize); | ||
| 111 | |||
| 112 | if (len % mtd->writesize) | ||
| 113 | goto invalid_arg; | ||
| 114 | len = len / mtd->writesize * (mtd->writesize + mtd->oobsize); | ||
| 115 | |||
| 116 | if (goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) { | ||
| 117 | pr_err("%s: erase failed, start %llx, len %x, dev_size %llx, erase_size %x\n", | ||
| 118 | __func__, ofs, len, mtd->size, mtd->erasesize); | ||
| 119 | return -EIO; | ||
| 120 | } | ||
| 121 | |||
| 122 | return 0; | ||
| 123 | |||
| 124 | invalid_arg: | ||
| 125 | pr_err("%s: invalid erase, start %llx, len %x, dev_size %llx, erase_size %x\n", | ||
| 126 | __func__, ofs, len, mtd->size, mtd->erasesize); | ||
| 127 | return -EINVAL; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs, | ||
| 131 | struct mtd_oob_ops *ops) | ||
| 132 | { | ||
| 133 | s32 rem; | ||
| 134 | |||
| 135 | if (ofs + ops->len > mtd->size) | ||
| 136 | goto invalid_arg; | ||
| 137 | if (ops->datbuf && ops->len && ops->len != mtd->writesize) | ||
| 138 | goto invalid_arg; | ||
| 139 | if (ops->ooblen + ops->ooboffs > mtd->oobsize) | ||
| 140 | goto invalid_arg; | ||
| 141 | |||
| 142 | ofs = div_s64_rem(ofs, mtd->writesize, &rem); | ||
| 143 | if (rem) | ||
| 144 | goto invalid_arg; | ||
| 145 | ofs *= (mtd->writesize + mtd->oobsize); | ||
| 146 | |||
| 147 | if (ops->datbuf) | ||
| 148 | ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs, | ||
| 149 | ops->len, ops->datbuf); | ||
| 150 | ofs += mtd->writesize + ops->ooboffs; | ||
| 151 | if (ops->oobbuf) | ||
| 152 | ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs, | ||
| 153 | ops->ooblen, ops->oobbuf); | ||
| 154 | return 0; | ||
| 155 | |||
| 156 | invalid_arg: | ||
| 157 | pr_err("%s: invalid read, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n", | ||
| 158 | __func__, ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize); | ||
| 159 | return -EINVAL; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs, | ||
| 163 | struct mtd_oob_ops *ops) | ||
| 164 | { | ||
| 165 | s32 rem; | ||
| 166 | |||
| 167 | if (ofs + ops->len > mtd->size) | ||
| 168 | goto invalid_arg; | ||
| 169 | if (ops->len && ops->len != mtd->writesize) | ||
| 170 | goto invalid_arg; | ||
| 171 | if (ops->ooblen + ops->ooboffs > mtd->oobsize) | ||
| 172 | goto invalid_arg; | ||
| 173 | |||
| 174 | ofs = div_s64_rem(ofs, mtd->writesize, &rem); | ||
| 175 | if (rem) | ||
| 176 | goto invalid_arg; | ||
| 177 | ofs *= (mtd->writesize + mtd->oobsize); | ||
| 178 | |||
| 179 | if (ops->datbuf) | ||
| 180 | ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs, | ||
| 181 | ops->len, ops->datbuf); | ||
| 182 | ofs += mtd->writesize + ops->ooboffs; | ||
| 183 | if (ops->oobbuf) | ||
| 184 | ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs, | ||
| 185 | ops->ooblen, ops->oobbuf); | ||
| 186 | return 0; | ||
| 187 | |||
| 188 | invalid_arg: | ||
| 189 | pr_err("%s: invalid write, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n", | ||
| 190 | __func__, ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize); | ||
| 191 | return -EINVAL; | ||
| 192 | } | ||
| 193 | |||
| 194 | static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
| 195 | size_t *retlen, u_char *buf) | ||
| 196 | { | ||
| 197 | s32 rem; | ||
| 198 | |||
| 199 | if (from + len > mtd->size) | ||
| 200 | goto invalid_arg; | ||
| 201 | |||
| 202 | from = div_s64_rem(from, mtd->writesize, &rem); | ||
| 203 | if (rem) | ||
| 204 | goto invalid_arg; | ||
| 205 | from *= (mtd->writesize + mtd->oobsize); | ||
| 206 | |||
| 207 | *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf); | ||
| 208 | return 0; | ||
| 209 | |||
| 210 | invalid_arg: | ||
| 211 | pr_err("%s: invalid read, start %llx, len %zx, dev_size %llx, write_size %x\n", | ||
| 212 | __func__, from, len, mtd->size, mtd->writesize); | ||
| 213 | return -EINVAL; | ||
| 214 | } | ||
| 215 | |||
| 216 | static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
| 217 | size_t *retlen, const u_char *buf) | ||
| 218 | { | ||
| 219 | s32 rem; | ||
| 220 | |||
| 221 | if (to + len > mtd->size) | ||
| 222 | goto invalid_arg; | ||
| 223 | |||
| 224 | to = div_s64_rem(to, mtd->writesize, &rem); | ||
| 225 | if (rem) | ||
| 226 | goto invalid_arg; | ||
| 227 | to *= (mtd->writesize + mtd->oobsize); | ||
| 228 | |||
| 229 | *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf); | ||
| 230 | return 0; | ||
| 231 | |||
| 232 | invalid_arg: | ||
| 233 | pr_err("%s: invalid write, start %llx, len %zx, dev_size %llx, write_size %x\n", | ||
| 234 | __func__, to, len, mtd->size, mtd->writesize); | ||
| 235 | return -EINVAL; | ||
| 236 | } | ||
| 237 | |||
| 238 | static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) | ||
| 239 | { | ||
| 240 | s32 rem; | ||
| 241 | |||
| 242 | if (ofs >= mtd->size) | ||
| 243 | goto invalid_arg; | ||
| 244 | |||
| 245 | ofs = div_s64_rem(ofs, mtd->writesize, &rem); | ||
| 246 | if (rem) | ||
| 247 | goto invalid_arg; | ||
| 248 | ofs *= mtd->erasesize / mtd->writesize; | ||
| 249 | ofs *= (mtd->writesize + mtd->oobsize); | ||
| 250 | |||
| 251 | return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL); | ||
| 252 | |||
| 253 | invalid_arg: | ||
| 254 | pr_err("%s: invalid arg, ofs %llx, dev_size %llx, write_size %x\n", | ||
| 255 | __func__, ofs, mtd->size, mtd->writesize); | ||
| 256 | return -EINVAL; | ||
| 257 | } | ||
| 258 | |||
| 259 | static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) | ||
| 260 | { | ||
| 261 | s32 rem; | ||
| 262 | |||
| 263 | if (ofs >= mtd->size) | ||
| 264 | goto invalid_arg; | ||
| 265 | |||
| 266 | ofs = div_s64_rem(ofs, mtd->writesize, &rem); | ||
| 267 | if (rem) | ||
| 268 | goto invalid_arg; | ||
| 269 | ofs *= mtd->erasesize / mtd->writesize; | ||
| 270 | ofs *= (mtd->writesize + mtd->oobsize); | ||
| 271 | |||
| 272 | if (goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1) | ||
| 273 | return -EIO; | ||
| 274 | return 0; | ||
| 275 | |||
| 276 | invalid_arg: | ||
| 277 | pr_err("%s: invalid arg, ofs %llx, dev_size %llx, write_size %x\n", | ||
| 278 | __func__, ofs, mtd->size, mtd->writesize); | ||
| 279 | return -EINVAL; | ||
| 280 | } | ||
| 281 | |||
| 282 | static int nand_setup_cmd_params(struct platform_device *pdev, | ||
| 283 | struct goldfish_nand *nand) | ||
| 284 | { | ||
| 285 | dma_addr_t dma_handle; | ||
| 286 | unsigned char __iomem *base = nand->base; | ||
| 287 | |||
| 288 | nand->cmd_params = dmam_alloc_coherent(&pdev->dev, | ||
| 289 | sizeof(struct cmd_params), | ||
| 290 | &dma_handle, GFP_KERNEL); | ||
| 291 | if (!nand->cmd_params) { | ||
| 292 | dev_err(&pdev->dev, "allocate buffer failed\n"); | ||
| 293 | return -ENOMEM; | ||
| 294 | } | ||
| 295 | writel((u32)((u64)dma_handle >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH); | ||
| 296 | writel((u32)dma_handle, base + NAND_CMD_PARAMS_ADDR_LOW); | ||
| 297 | return 0; | ||
| 298 | } | ||
| 299 | |||
| 300 | static int goldfish_nand_init_device(struct platform_device *pdev, | ||
| 301 | struct goldfish_nand *nand, int id) | ||
| 302 | { | ||
| 303 | u32 name_len; | ||
| 304 | u32 result; | ||
| 305 | u32 flags; | ||
| 306 | unsigned char __iomem *base = nand->base; | ||
| 307 | struct mtd_info *mtd = &nand->mtd[id]; | ||
| 308 | char *name; | ||
| 309 | |||
| 310 | mutex_lock(&nand->lock); | ||
| 311 | writel(id, base + NAND_DEV); | ||
| 312 | flags = readl(base + NAND_DEV_FLAGS); | ||
| 313 | name_len = readl(base + NAND_DEV_NAME_LEN); | ||
| 314 | mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE); | ||
| 315 | mtd->size = readl(base + NAND_DEV_SIZE_LOW); | ||
| 316 | mtd->size |= (u64)readl(base + NAND_DEV_SIZE_HIGH) << 32; | ||
| 317 | mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE); | ||
| 318 | mtd->oobavail = mtd->oobsize; | ||
| 319 | mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) / | ||
| 320 | (mtd->writesize + mtd->oobsize) * mtd->writesize; | ||
| 321 | mtd->size = div_s64(mtd->size, mtd->writesize + mtd->oobsize); | ||
| 322 | mtd->size *= mtd->writesize; | ||
| 323 | dev_dbg(&pdev->dev, | ||
| 324 | "goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n", | ||
| 325 | id, mtd->size, mtd->writesize, | ||
| 326 | mtd->oobsize, mtd->erasesize); | ||
| 327 | mutex_unlock(&nand->lock); | ||
| 328 | |||
| 329 | mtd->priv = nand; | ||
| 330 | |||
| 331 | name = devm_kzalloc(&pdev->dev, name_len + 1, GFP_KERNEL); | ||
| 332 | if (!name) | ||
| 333 | return -ENOMEM; | ||
| 334 | mtd->name = name; | ||
| 335 | |||
| 336 | result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, | ||
| 337 | name); | ||
| 338 | if (result != name_len) { | ||
| 339 | dev_err(&pdev->dev, | ||
| 340 | "%s: failed to get dev name %d != %d\n", | ||
| 341 | __func__, result, name_len); | ||
| 342 | return -ENODEV; | ||
| 343 | } | ||
| 344 | ((char *)mtd->name)[name_len] = '\0'; | ||
| 345 | |||
| 346 | /* Setup the MTD structure */ | ||
| 347 | mtd->type = MTD_NANDFLASH; | ||
| 348 | mtd->flags = MTD_CAP_NANDFLASH; | ||
| 349 | if (flags & NAND_DEV_FLAG_READ_ONLY) | ||
| 350 | mtd->flags &= ~MTD_WRITEABLE; | ||
| 351 | if (flags & NAND_DEV_FLAG_CMD_PARAMS_CAP) | ||
| 352 | nand_setup_cmd_params(pdev, nand); | ||
| 353 | |||
| 354 | mtd->owner = THIS_MODULE; | ||
| 355 | mtd->_erase = goldfish_nand_erase; | ||
| 356 | mtd->_read = goldfish_nand_read; | ||
| 357 | mtd->_write = goldfish_nand_write; | ||
| 358 | mtd->_read_oob = goldfish_nand_read_oob; | ||
| 359 | mtd->_write_oob = goldfish_nand_write_oob; | ||
| 360 | mtd->_block_isbad = goldfish_nand_block_isbad; | ||
| 361 | mtd->_block_markbad = goldfish_nand_block_markbad; | ||
| 362 | |||
| 363 | if (mtd_device_register(mtd, NULL, 0)) | ||
| 364 | return -EIO; | ||
| 365 | |||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | |||
| 369 | static int goldfish_nand_probe(struct platform_device *pdev) | ||
| 370 | { | ||
| 371 | u32 num_dev; | ||
| 372 | int i; | ||
| 373 | int err; | ||
| 374 | u32 num_dev_working; | ||
| 375 | u32 version; | ||
| 376 | struct resource *r; | ||
| 377 | struct goldfish_nand *nand; | ||
| 378 | unsigned char __iomem *base; | ||
| 379 | |||
| 380 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 381 | if (!r) | ||
| 382 | return -ENODEV; | ||
| 383 | |||
| 384 | base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); | ||
| 385 | if (!base) | ||
| 386 | return -ENOMEM; | ||
| 387 | |||
| 388 | version = readl(base + NAND_VERSION); | ||
| 389 | if (version != NAND_VERSION_CURRENT) { | ||
| 390 | dev_err(&pdev->dev, | ||
| 391 | "goldfish_nand_init: version mismatch, got %d, expected %d\n", | ||
| 392 | version, NAND_VERSION_CURRENT); | ||
| 393 | return -ENODEV; | ||
| 394 | } | ||
| 395 | num_dev = readl(base + NAND_NUM_DEV); | ||
| 396 | if (num_dev == 0) | ||
| 397 | return -ENODEV; | ||
| 398 | |||
| 399 | nand = devm_kzalloc(&pdev->dev, sizeof(*nand) + | ||
| 400 | sizeof(struct mtd_info) * num_dev, GFP_KERNEL); | ||
| 401 | if (!nand) | ||
| 402 | return -ENOMEM; | ||
| 403 | |||
| 404 | mutex_init(&nand->lock); | ||
| 405 | nand->base = base; | ||
| 406 | nand->mtd_count = num_dev; | ||
| 407 | platform_set_drvdata(pdev, nand); | ||
| 408 | |||
| 409 | num_dev_working = 0; | ||
| 410 | for (i = 0; i < num_dev; i++) { | ||
| 411 | err = goldfish_nand_init_device(pdev, nand, i); | ||
| 412 | if (err == 0) | ||
| 413 | num_dev_working++; | ||
| 414 | } | ||
| 415 | if (num_dev_working == 0) | ||
| 416 | return -ENODEV; | ||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | |||
| 420 | static int goldfish_nand_remove(struct platform_device *pdev) | ||
| 421 | { | ||
| 422 | struct goldfish_nand *nand = platform_get_drvdata(pdev); | ||
| 423 | int i; | ||
| 424 | |||
| 425 | for (i = 0; i < nand->mtd_count; i++) { | ||
| 426 | if (nand->mtd[i].name) | ||
| 427 | mtd_device_unregister(&nand->mtd[i]); | ||
| 428 | } | ||
| 429 | return 0; | ||
| 430 | } | ||
| 431 | |||
| 432 | static struct platform_driver goldfish_nand_driver = { | ||
| 433 | .probe = goldfish_nand_probe, | ||
| 434 | .remove = goldfish_nand_remove, | ||
| 435 | .driver = { | ||
| 436 | .name = "goldfish_nand" | ||
| 437 | } | ||
| 438 | }; | ||
| 439 | |||
| 440 | module_platform_driver(goldfish_nand_driver); | ||
| 441 | MODULE_LICENSE("GPL"); | ||
| @@ -1,76 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * drivers/mtd/devices/goldfish_nand_reg.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007 Google, Inc. | ||
| 5 | * | ||
| 6 | * This software is licensed under the terms of the GNU General Public | ||
| 7 | * License version 2, as published by the Free Software Foundation, and | ||
| 8 | * may be copied, distributed, and modified under those terms. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef GOLDFISH_NAND_REG_H | ||
| 18 | #define GOLDFISH_NAND_REG_H | ||
| 19 | |||
| 20 | enum nand_cmd { | ||
| 21 | /* Write device name for NAND_DEV to NAND_DATA (vaddr) */ | ||
| 22 | NAND_CMD_GET_DEV_NAME, | ||
| 23 | NAND_CMD_READ, | ||
| 24 | NAND_CMD_WRITE, | ||
| 25 | NAND_CMD_ERASE, | ||
| 26 | /* NAND_RESULT is 1 if block is bad, 0 if it is not */ | ||
| 27 | NAND_CMD_BLOCK_BAD_GET, | ||
| 28 | NAND_CMD_BLOCK_BAD_SET, | ||
| 29 | NAND_CMD_READ_WITH_PARAMS, | ||
| 30 | NAND_CMD_WRITE_WITH_PARAMS, | ||
| 31 | NAND_CMD_ERASE_WITH_PARAMS | ||
| 32 | }; | ||
| 33 | |||
| 34 | enum nand_dev_flags { | ||
| 35 | NAND_DEV_FLAG_READ_ONLY = 0x00000001, | ||
| 36 | NAND_DEV_FLAG_CMD_PARAMS_CAP = 0x00000002, | ||
| 37 | }; | ||
| 38 | |||
| 39 | #define NAND_VERSION_CURRENT (1) | ||
| 40 | |||
| 41 | enum nand_reg { | ||
| 42 | /* Global */ | ||
| 43 | NAND_VERSION = 0x000, | ||
| 44 | NAND_NUM_DEV = 0x004, | ||
| 45 | NAND_DEV = 0x008, | ||
| 46 | |||
| 47 | /* Dev info */ | ||
| 48 | NAND_DEV_FLAGS = 0x010, | ||
| 49 | NAND_DEV_NAME_LEN = 0x014, | ||
| 50 | NAND_DEV_PAGE_SIZE = 0x018, | ||
| 51 | NAND_DEV_EXTRA_SIZE = 0x01c, | ||
| 52 | NAND_DEV_ERASE_SIZE = 0x020, | ||
| 53 | NAND_DEV_SIZE_LOW = 0x028, | ||
| 54 | NAND_DEV_SIZE_HIGH = 0x02c, | ||
| 55 | |||
| 56 | /* Command */ | ||
| 57 | NAND_RESULT = 0x040, | ||
| 58 | NAND_COMMAND = 0x044, | ||
| 59 | NAND_DATA = 0x048, | ||
| 60 | NAND_DATA_HIGH = 0x100, | ||
| 61 | NAND_TRANSFER_SIZE = 0x04c, | ||
| 62 | NAND_ADDR_LOW = 0x050, | ||
| 63 | NAND_ADDR_HIGH = 0x054, | ||
| 64 | NAND_CMD_PARAMS_ADDR_LOW = 0x058, | ||
| 65 | NAND_CMD_PARAMS_ADDR_HIGH = 0x05c, | ||
| 66 | }; | ||
| 67 | |||
| 68 | struct cmd_params { | ||
| 69 | u32 dev; | ||
| 70 | u32 addr_low; | ||
| 71 | u32 addr_high; | ||
| 72 | u32 transfer_size; | ||
| 73 | unsigned long data; | ||
| 74 | u32 result; | ||
| 75 | }; | ||
| 76 | #endif | ||
| @@ -0,0 +1,5 @@ | |||
| 1 | * Convert all uses of the old GPIO API from <linux/gpio.h> to the | ||
| 2 | GPIO descriptor API in <linux/gpio/consumer.h> and look up GPIO | ||
| 3 | lines from device tree or ACPI. | ||
| 4 | * Convert the GPIO driver to use the GPIO irqchip library | ||
| 5 | GPIOLIB_IRQCHIP instead of reimplementing the same. | ||
| @@ -315,8 +315,7 @@ static ssize_t state_store(struct device *dev, · 314 unmodified lines | |||
| 315 | struct device_attribute *attr, | 315 | struct device_attribute *attr, |
| 316 | const char *buf, size_t count) | 316 | const char *buf, size_t count) |
| 317 | { | 317 | { |
| 318 | struct platform_device *pdev = to_platform_device(dev); | 318 | struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); |
| 319 | struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); | ||
| 320 | int ret = 0; | 319 | int ret = 0; |
| 321 | 320 | ||
| 322 | mutex_lock(&arche_pdata->platform_state_mutex); | 321 | mutex_lock(&arche_pdata->platform_state_mutex); |
| @@ -448,7 +447,7 @@ static int arche_platform_probe(struct platform_device *pdev) · 125 unmodified lines | |||
| 448 | arche_pdata->svc_reset_gpio = of_get_named_gpio(np, | 447 | arche_pdata->svc_reset_gpio = of_get_named_gpio(np, |
| 449 | "svc,reset-gpio", | 448 | "svc,reset-gpio", |
| 450 | 0); | 449 | 0); |
| 451 | if (arche_pdata->svc_reset_gpio < 0) { | 450 | if (!gpio_is_valid(arche_pdata->svc_reset_gpio)) { |
| 452 | dev_err(dev, "failed to get reset-gpio\n"); | 451 | dev_err(dev, "failed to get reset-gpio\n"); |
| 453 | return arche_pdata->svc_reset_gpio; | 452 | return arche_pdata->svc_reset_gpio; |
| 454 | } | 453 | } |
| @@ -468,7 +467,7 @@ static int arche_platform_probe(struct platform_device *pdev) · 13 unmodified lines | |||
| 468 | arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, | 467 | arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, |
| 469 | "svc,sysboot-gpio", | 468 | "svc,sysboot-gpio", |
| 470 | 0); | 469 | 0); |
| 471 | if (arche_pdata->svc_sysboot_gpio < 0) { | 470 | if (!gpio_is_valid(arche_pdata->svc_sysboot_gpio)) { |
| 472 | dev_err(dev, "failed to get sysboot gpio\n"); | 471 | dev_err(dev, "failed to get sysboot gpio\n"); |
| 473 | return arche_pdata->svc_sysboot_gpio; | 472 | return arche_pdata->svc_sysboot_gpio; |
| 474 | } | 473 | } |
| @@ -487,7 +486,7 @@ static int arche_platform_probe(struct platform_device *pdev) · 12 unmodified lines | |||
| 487 | arche_pdata->svc_refclk_req = of_get_named_gpio(np, | 486 | arche_pdata->svc_refclk_req = of_get_named_gpio(np, |
| 488 | "svc,refclk-req-gpio", | 487 | "svc,refclk-req-gpio", |
| 489 | 0); | 488 | 0); |
| 490 | if (arche_pdata->svc_refclk_req < 0) { | 489 | if (!gpio_is_valid(arche_pdata->svc_refclk_req)) { |
| 491 | dev_err(dev, "failed to get svc clock-req gpio\n"); | 490 | dev_err(dev, "failed to get svc clock-req gpio\n"); |
| 492 | return arche_pdata->svc_refclk_req; | 491 | return arche_pdata->svc_refclk_req; |
| 493 | } | 492 | } |
| @@ -23,7 +23,10 @@ enum { · 22 unmodified lines | |||
| 23 | NUM_CODEC_DAIS, | 23 | NUM_CODEC_DAIS, |
| 24 | }; | 24 | }; |
| 25 | 25 | ||
| 26 | /* device_type should be same as defined in audio.h (Android media layer) */ | 26 | /* |
| 27 | * device_type should be same as defined in audio.h | ||
| 28 | * (Android media layer) | ||
| 29 | */ | ||
| 27 | enum { | 30 | enum { |
| 28 | GBAUDIO_DEVICE_NONE = 0x0, | 31 | GBAUDIO_DEVICE_NONE = 0x0, |
| 29 | /* reserved bits */ | 32 | /* reserved bits */ |
| @@ -996,7 +996,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, · 995 unmodified lines | |||
| 996 | 996 | ||
| 997 | ret = gbaudio_validate_kcontrol_count(w); | 997 | ret = gbaudio_validate_kcontrol_count(w); |
| 998 | if (ret) { | 998 | if (ret) { |
| 999 | dev_err(module->dev, "Inavlid kcontrol count=%d for %s\n", | 999 | dev_err(module->dev, "Invalid kcontrol count=%d for %s\n", |
| 1000 | w->ncontrols, w->name); | 1000 | w->ncontrols, w->name); |
| 1001 | return ret; | 1001 | return ret; |
| 1002 | } | 1002 | } |
| @@ -1174,11 +1174,6 @@ static int gb_camera_debugfs_init(struct gb_camera *gcam) · 1173 unmodified lines | |||
| 1174 | gcam->bundle->id); | 1174 | gcam->bundle->id); |
| 1175 | 1175 | ||
| 1176 | gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); | 1176 | gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); |
| 1177 | if (IS_ERR(gcam->debugfs.root)) { | ||
| 1178 | gcam_err(gcam, "debugfs root create failed (%ld)\n", | ||
| 1179 | PTR_ERR(gcam->debugfs.root)); | ||
| 1180 | return PTR_ERR(gcam->debugfs.root); | ||
| 1181 | } | ||
| 1182 | 1177 | ||
| 1183 | gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * | 1178 | gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * |
| 1184 | GB_CAMERA_DEBUGFS_BUFFER_MAX); | 1179 | GB_CAMERA_DEBUGFS_BUFFER_MAX); |
| @@ -1188,18 +1183,12 @@ static int gb_camera_debugfs_init(struct gb_camera *gcam) · 3 unmodified lines | |||
| 1188 | for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { | 1183 | for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { |
| 1189 | const struct gb_camera_debugfs_entry *entry = | 1184 | const struct gb_camera_debugfs_entry *entry = |
| 1190 | &gb_camera_debugfs_entries[i]; | 1185 | &gb_camera_debugfs_entries[i]; |
| 1191 | struct dentry *dentry; | ||
| 1192 | 1186 | ||
| 1193 | gcam->debugfs.buffers[i].length = 0; | 1187 | gcam->debugfs.buffers[i].length = 0; |
| 1194 | 1188 | ||
| 1195 | dentry = debugfs_create_file(entry->name, entry->mask, | 1189 | debugfs_create_file(entry->name, entry->mask, |
| 1196 | gcam->debugfs.root, gcam, | 1190 | gcam->debugfs.root, gcam, |
| 1197 | &gb_camera_debugfs_ops); | 1191 | &gb_camera_debugfs_ops); |
| 1198 | if (IS_ERR(dentry)) { | ||
| 1199 | gcam_err(gcam, | ||
| 1200 | "debugfs operation %s create failed (%ld)\n", | ||
| 1201 | entry->name, PTR_ERR(dentry)); | ||
| 1202 | return PTR_ERR(dentry); | ||
| 1203 | } | 1192 | } |
| 1204 | } | 1193 | } |
| 1205 | 1194 | ||
| @@ -1137,7 +1137,6 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) · 1136 unmodified lines | |||
| 1137 | struct gb_svc *svc = gb_connection_get_data(op->connection); | 1137 | struct gb_svc *svc = gb_connection_get_data(op->connection); |
| 1138 | struct gb_message *request = op->request; | 1138 | struct gb_message *request = op->request; |
| 1139 | struct gb_svc_intf_reset_request *reset; | 1139 | struct gb_svc_intf_reset_request *reset; |
| 1140 | u8 intf_id; | ||
| 1141 | 1140 | ||
| 1142 | if (request->payload_size < sizeof(*reset)) { | 1141 | if (request->payload_size < sizeof(*reset)) { |
| 1143 | dev_warn(&svc->dev, "short reset request received (%zu < %zu)\n", | 1142 | dev_warn(&svc->dev, "short reset request received (%zu < %zu)\n", |
| @@ -1146,8 +1145,6 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op) · 2 unmodified lines | |||
| 1146 | } | 1145 | } |
| 1147 | reset = request->payload; | 1146 | reset = request->payload; |
| 1148 | 1147 | ||
| 1149 | intf_id = reset->intf_id; | ||
| 1150 | |||
| 1151 | /* FIXME Reset the interface here */ | 1148 | /* FIXME Reset the interface here */ |
| 1152 | 1149 | ||
| 1153 | return 0; | 1150 | return 0; |
| @@ -11,7 +11,6 @@ source "drivers/staging/iio/cdc/Kconfig" · 10 unmodified lines | |||
| 11 | source "drivers/staging/iio/frequency/Kconfig" | 11 | source "drivers/staging/iio/frequency/Kconfig" |
| 12 | source "drivers/staging/iio/gyro/Kconfig" | 12 | source "drivers/staging/iio/gyro/Kconfig" |
| 13 | source "drivers/staging/iio/impedance-analyzer/Kconfig" | 13 | source "drivers/staging/iio/impedance-analyzer/Kconfig" |
| 14 | source "drivers/staging/iio/light/Kconfig" | ||
| 15 | source "drivers/staging/iio/meter/Kconfig" | 14 | source "drivers/staging/iio/meter/Kconfig" |
| 16 | source "drivers/staging/iio/resolver/Kconfig" | 15 | source "drivers/staging/iio/resolver/Kconfig" |
| 17 | 16 | ||
| @@ -10,6 +10,5 @@ obj-y += cdc/ · 9 unmodified lines | |||
| 10 | obj-y += frequency/ | 10 | obj-y += frequency/ |
| 11 | obj-y += gyro/ | 11 | obj-y += gyro/ |
| 12 | obj-y += impedance-analyzer/ | 12 | obj-y += impedance-analyzer/ |
| 13 | obj-y += light/ | ||
| 14 | obj-y += meter/ | 13 | obj-y += meter/ |
| 15 | obj-y += resolver/ | 14 | obj-y += resolver/ |
| @@ -1,4 +1,11 @@ | |||
| 1 | 2016 10/09 | 1 | 2018-04-15 |
| 2 | |||
| 3 | All affected drivers: | ||
| 4 | Convert all uses of the old GPIO API from <linux/gpio.h> to the | ||
| 5 | GPIO descriptor API in <linux/gpio/consumer.h> and look up GPIO | ||
| 6 | lines from device tree, ACPI or board files, board files should | ||
| 7 | use <linux/gpio/machine.h>. | ||
| 8 | |||
| 2 | 9 | ||
| 3 | ADI Drivers: | 10 | ADI Drivers: |
| 4 | CC the device-drivers-devel@blackfin.uclinux.org mailing list when | 11 | CC the device-drivers-devel@blackfin.uclinux.org mailing list when |
| @@ -3,18 +3,6 @@ · 2 unmodified lines | |||
| 3 | # | 3 | # |
| 4 | menu "Accelerometers" | 4 | menu "Accelerometers" |
| 5 | 5 | ||
| 6 | config ADIS16201 | ||
| 7 | tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer" | ||
| 8 | depends on SPI | ||
| 9 | select IIO_ADIS_LIB | ||
| 10 | select IIO_ADIS_LIB_BUFFER if IIO_BUFFER | ||
| 11 | help | ||
| 12 | Say Y here to build support for Analog Devices adis16201 dual-axis | ||
| 13 | digital inclinometer and accelerometer. | ||
| 14 | |||
| 15 | To compile this driver as a module, say M here: the module will | ||
| 16 | be called adis16201. | ||
| 17 | |||
| 18 | config ADIS16203 | 6 | config ADIS16203 |
| 19 | tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer" | 7 | tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer" |
| 20 | depends on SPI | 8 | depends on SPI |
| @@ -27,18 +15,6 @@ config ADIS16203 · 6 unmodified lines | |||
| 27 | To compile this driver as a module, say M here: the module will be | 15 | To compile this driver as a module, say M here: the module will be |
| 28 | called adis16203. | 16 | called adis16203. |
| 29 | 17 | ||
| 30 | config ADIS16209 | ||
| 31 | tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer" | ||
| 32 | depends on SPI | ||
| 33 | select IIO_ADIS_LIB | ||
| 34 | select IIO_ADIS_LIB_BUFFER if IIO_BUFFER | ||
| 35 | help | ||
| 36 | Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer | ||
| 37 | and accelerometer. | ||
| 38 | |||
| 39 | To compile this driver as a module, say M here: the module will be | ||
| 40 | called adis16209. | ||
| 41 | |||
| 42 | config ADIS16240 | 18 | config ADIS16240 |
| 43 | tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder" | 19 | tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder" |
| 44 | depends on SPI | 20 | depends on SPI |
| @@ -2,7 +2,5 @@ · 1 unmodified line | |||
| 2 | # Makefile for industrial I/O accelerometer drivers | 2 | # Makefile for industrial I/O accelerometer drivers |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_ADIS16201) += adis16201.o | ||
| 6 | obj-$(CONFIG_ADIS16203) += adis16203.o | 5 | obj-$(CONFIG_ADIS16203) += adis16203.o |
| 7 | obj-$(CONFIG_ADIS16209) += adis16209.o | ||
| 8 | obj-$(CONFIG_ADIS16240) += adis16240.o | 6 | obj-$(CONFIG_ADIS16240) += adis16240.o |
| @@ -1,385 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer | ||
| 3 | * | ||
| 4 | * Copyright 2010 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2 or later. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/device.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/mutex.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | #include <linux/spi/spi.h> | ||
| 16 | #include <linux/sysfs.h> | ||
| 17 | |||
| 18 | #include <linux/iio/iio.h> | ||
| 19 | #include <linux/iio/sysfs.h> | ||
| 20 | #include <linux/iio/buffer.h> | ||
| 21 | #include <linux/iio/imu/adis.h> | ||
| 22 | |||
| 23 | #define ADIS16201_STARTUP_DELAY 220 /* ms */ | ||
| 24 | |||
| 25 | /* Flash memory write count */ | ||
| 26 | #define ADIS16201_FLASH_CNT 0x00 | ||
| 27 | |||
| 28 | /* Output, power supply */ | ||
| 29 | #define ADIS16201_SUPPLY_OUT 0x02 | ||
| 30 | |||
| 31 | /* Output, x-axis accelerometer */ | ||
| 32 | #define ADIS16201_XACCL_OUT 0x04 | ||
| 33 | |||
| 34 | /* Output, y-axis accelerometer */ | ||
| 35 | #define ADIS16201_YACCL_OUT 0x06 | ||
| 36 | |||
| 37 | /* Output, auxiliary ADC input */ | ||
| 38 | #define ADIS16201_AUX_ADC 0x08 | ||
| 39 | |||
| 40 | /* Output, temperature */ | ||
| 41 | #define ADIS16201_TEMP_OUT 0x0A | ||
| 42 | |||
| 43 | /* Output, x-axis inclination */ | ||
| 44 | #define ADIS16201_XINCL_OUT 0x0C | ||
| 45 | |||
| 46 | /* Output, y-axis inclination */ | ||
| 47 | #define ADIS16201_YINCL_OUT 0x0E | ||
| 48 | |||
| 49 | /* Calibration, x-axis acceleration offset */ | ||
| 50 | #define ADIS16201_XACCL_OFFS 0x10 | ||
| 51 | |||
| 52 | /* Calibration, y-axis acceleration offset */ | ||
| 53 | #define ADIS16201_YACCL_OFFS 0x12 | ||
| 54 | |||
| 55 | /* x-axis acceleration scale factor */ | ||
| 56 | #define ADIS16201_XACCL_SCALE 0x14 | ||
| 57 | |||
| 58 | /* y-axis acceleration scale factor */ | ||
| 59 | #define ADIS16201_YACCL_SCALE 0x16 | ||
| 60 | |||
| 61 | /* Calibration, x-axis inclination offset */ | ||
| 62 | #define ADIS16201_XINCL_OFFS 0x18 | ||
| 63 | |||
| 64 | /* Calibration, y-axis inclination offset */ | ||
| 65 | #define ADIS16201_YINCL_OFFS 0x1A | ||
| 66 | |||
| 67 | /* x-axis inclination scale factor */ | ||
| 68 | #define ADIS16201_XINCL_SCALE 0x1C | ||
| 69 | |||
| 70 | /* y-axis inclination scale factor */ | ||
| 71 | #define ADIS16201_YINCL_SCALE 0x1E | ||
| 72 | |||
| 73 | /* Alarm 1 amplitude threshold */ | ||
| 74 | #define ADIS16201_ALM_MAG1 0x20 | ||
| 75 | |||
| 76 | /* Alarm 2 amplitude threshold */ | ||
| 77 | #define ADIS16201_ALM_MAG2 0x22 | ||
| 78 | |||
| 79 | /* Alarm 1, sample period */ | ||
| 80 | #define ADIS16201_ALM_SMPL1 0x24 | ||
| 81 | |||
| 82 | /* Alarm 2, sample period */ | ||
| 83 | #define ADIS16201_ALM_SMPL2 0x26 | ||
| 84 | |||
| 85 | /* Alarm control */ | ||
| 86 | #define ADIS16201_ALM_CTRL 0x28 | ||
| 87 | |||
| 88 | /* Auxiliary DAC data */ | ||
| 89 | #define ADIS16201_AUX_DAC 0x30 | ||
| 90 | |||
| 91 | /* General-purpose digital input/output control */ | ||
| 92 | #define ADIS16201_GPIO_CTRL 0x32 | ||
| 93 | |||
| 94 | /* Miscellaneous control */ | ||
| 95 | #define ADIS16201_MSC_CTRL 0x34 | ||
| 96 | |||
| 97 | /* Internal sample period (rate) control */ | ||
| 98 | #define ADIS16201_SMPL_PRD 0x36 | ||
| 99 | |||
| 100 | /* Operation, filter configuration */ | ||
| 101 | #define ADIS16201_AVG_CNT 0x38 | ||
| 102 | |||
| 103 | /* Operation, sleep mode control */ | ||
| 104 | #define ADIS16201_SLP_CNT 0x3A | ||
| 105 | |||
| 106 | /* Diagnostics, system status register */ | ||
| 107 | #define ADIS16201_DIAG_STAT 0x3C | ||
| 108 | |||
| 109 | /* Operation, system command register */ | ||
| 110 | #define ADIS16201_GLOB_CMD 0x3E | ||
| 111 | |||
| 112 | /* MSC_CTRL */ | ||
| 113 | |||
| 114 | /* Self-test enable */ | ||
| 115 | #define ADIS16201_MSC_CTRL_SELF_TEST_EN BIT(8) | ||
| 116 | |||
| 117 | /* Data-ready enable: 1 = enabled, 0 = disabled */ | ||
| 118 | #define ADIS16201_MSC_CTRL_DATA_RDY_EN BIT(2) | ||
| 119 | |||
| 120 | /* Data-ready polarity: 1 = active high, 0 = active low */ | ||
| 121 | #define ADIS16201_MSC_CTRL_ACTIVE_HIGH BIT(1) | ||
| 122 | |||
| 123 | /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ | ||
| 124 | #define ADIS16201_MSC_CTRL_DATA_RDY_DIO1 BIT(0) | ||
| 125 | |||
| 126 | /* DIAG_STAT */ | ||
| 127 | |||
| 128 | /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ | ||
| 129 | #define ADIS16201_DIAG_STAT_ALARM2 BIT(9) | ||
| 130 | |||
| 131 | /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ | ||
| 132 | #define ADIS16201_DIAG_STAT_ALARM1 BIT(8) | ||
| 133 | |||
| 134 | /* SPI communications failure */ | ||
| 135 | #define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 | ||
| 136 | |||
| 137 | /* Flash update failure */ | ||
| 138 | #define ADIS16201_DIAG_STAT_FLASH_UPT_BIT 2 | ||
| 139 | |||
| 140 | /* Power supply above 3.625 V */ | ||
| 141 | #define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 | ||
| 142 | |||
| 143 | /* Power supply below 3.15 V */ | ||
| 144 | #define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 | ||
| 145 | |||
| 146 | /* GLOB_CMD */ | ||
| 147 | |||
| 148 | #define ADIS16201_GLOB_CMD_SW_RESET BIT(7) | ||
| 149 | #define ADIS16201_GLOB_CMD_FACTORY_CAL BIT(1) | ||
| 150 | |||
| 151 | #define ADIS16201_ERROR_ACTIVE BIT(14) | ||
| 152 | |||
| 153 | enum adis16201_scan { | ||
| 154 | ADIS16201_SCAN_ACC_X, | ||
| 155 | ADIS16201_SCAN_ACC_Y, | ||
| 156 | ADIS16201_SCAN_INCLI_X, | ||
| 157 | ADIS16201_SCAN_INCLI_Y, | ||
| 158 | ADIS16201_SCAN_SUPPLY, | ||
| 159 | ADIS16201_SCAN_AUX_ADC, | ||
| 160 | ADIS16201_SCAN_TEMP, | ||
| 161 | }; | ||
| 162 | |||
| 163 | static const u8 adis16201_addresses[] = { | ||
| 164 | [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS, | ||
| 165 | [ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS, | ||
| 166 | [ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS, | ||
| 167 | [ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS, | ||
| 168 | }; | ||
| 169 | |||
| 170 | static int adis16201_read_raw(struct iio_dev *indio_dev, | ||
| 171 | struct iio_chan_spec const *chan, | ||
| 172 | int *val, int *val2, | ||
| 173 | long mask) | ||
| 174 | { | ||
| 175 | struct adis *st = iio_priv(indio_dev); | ||
| 176 | int ret; | ||
| 177 | int bits; | ||
| 178 | u8 addr; | ||
| 179 | s16 val16; | ||
| 180 | |||
| 181 | switch (mask) { | ||
| 182 | case IIO_CHAN_INFO_RAW: | ||
| 183 | return adis_single_conversion(indio_dev, chan, | ||
| 184 | ADIS16201_ERROR_ACTIVE, val); | ||
| 185 | case IIO_CHAN_INFO_SCALE: | ||
| 186 | switch (chan->type) { | ||
| 187 | case IIO_VOLTAGE: | ||
| 188 | if (chan->channel == 0) { | ||
| 189 | *val = 1; | ||
| 190 | *val2 = 220000; /* 1.22 mV */ | ||
| 191 | } else { | ||
| 192 | *val = 0; | ||
| 193 | *val2 = 610000; /* 0.610 mV */ | ||
| 194 | } | ||
| 195 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 196 | case IIO_TEMP: | ||
| 197 | *val = -470; /* 0.47 C */ | ||
| 198 | *val2 = 0; | ||
| 199 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 200 | case IIO_ACCEL: | ||
| 201 | *val = 0; | ||
| 202 | *val2 = IIO_G_TO_M_S_2(462400); /* 0.4624 mg */ | ||
| 203 | return IIO_VAL_INT_PLUS_NANO; | ||
| 204 | case IIO_INCLI: | ||
| 205 | *val = 0; | ||
| 206 | *val2 = 100000; /* 0.1 degree */ | ||
| 207 | return IIO_VAL_INT_PLUS_MICRO; | ||
| 208 | default: | ||
| 209 | return -EINVAL; | ||
| 210 | } | ||
| 211 | break; | ||
| 212 | case IIO_CHAN_INFO_OFFSET: | ||
| 213 | *val = 25000 / -470 - 1278; /* 25 C = 1278 */ | ||
| 214 | return IIO_VAL_INT; | ||
| 215 | case IIO_CHAN_INFO_CALIBBIAS: | ||
| 216 | switch (chan->type) { | ||
| 217 | case IIO_ACCEL: | ||
| 218 | bits = 12; | ||
| 219 | break; | ||
| 220 | case IIO_INCLI: | ||
| 221 | bits = 9; | ||
| 222 | break; | ||
| 223 | default: | ||
| 224 | return -EINVAL; | ||
| 225 | } | ||
| 226 | addr = adis16201_addresses[chan->scan_index]; | ||
| 227 | ret = adis_read_reg_16(st, addr, &val16); | ||
| 228 | if (ret) | ||
| 229 | return ret; | ||
| 230 | val16 &= (1 << bits) - 1; | ||
| 231 | val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); | ||
| 232 | *val = val16; | ||
| 233 | return IIO_VAL_INT; | ||
| 234 | } | ||
| 235 | |||
| 236 | return -EINVAL; | ||
| 237 | } | ||
| 238 | |||
| 239 | static int adis16201_write_raw(struct iio_dev *indio_dev, | ||
| 240 | struct iio_chan_spec const *chan, | ||
| 241 | int val, | ||
| 242 | int val2, | ||
| 243 | long mask) | ||
| 244 | { | ||
| 245 | struct adis *st = iio_priv(indio_dev); | ||
| 246 | int bits; | ||
| 247 | s16 val16; | ||
| 248 | u8 addr; | ||
| 249 | |||
| 250 | switch (mask) { | ||
| 251 | case IIO_CHAN_INFO_CALIBBIAS: | ||
| 252 | switch (chan->type) { | ||
| 253 | case IIO_ACCEL: | ||
| 254 | bits = 12; | ||
| 255 | break; | ||
| 256 | case IIO_INCLI: | ||
| 257 | bits = 9; | ||
| 258 | break; | ||
| 259 | default: | ||
| 260 | return -EINVAL; | ||
| 261 | } | ||
| 262 | val16 = val & ((1 << bits) - 1); | ||
| 263 | addr = adis16201_addresses[chan->scan_index]; | ||
| 264 | return adis_write_reg_16(st, addr, val16); | ||
| 265 | } | ||
| 266 | |||
| 267 | return -EINVAL; | ||
| 268 | } | ||
| 269 | |||
| 270 | static const struct iio_chan_spec adis16201_channels[] = { | ||
| 271 | ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 0, 12), | ||
| 272 | ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 0, 12), | ||
| 273 | ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X, | ||
| 274 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 275 | ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y, | ||
| 276 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 277 | ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 0, 12), | ||
| 278 | ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X, | ||
| 279 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 280 | ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y, | ||
| 281 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), | ||
| 282 | IIO_CHAN_SOFT_TIMESTAMP(7) | ||
| 283 | }; | ||
| 284 | |||
| 285 | static const struct iio_info adis16201_info = { | ||
| 286 | .read_raw = adis16201_read_raw, | ||
| 287 | .write_raw = adis16201_write_raw, | ||
| 288 | .update_scan_mode = adis_update_scan_mode, | ||
| 289 | }; | ||
| 290 | |||
| 291 | static const char * const adis16201_status_error_msgs[] = { | ||
| 292 | [ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", | ||
| 293 | [ADIS16201_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", | ||
| 294 | [ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", | ||
| 295 | [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", | ||
| 296 | }; | ||
| 297 | |||
| 298 | static const struct adis_data adis16201_data = { | ||
| 299 | .read_delay = 20, | ||
| 300 | .msc_ctrl_reg = ADIS16201_MSC_CTRL, | ||
| 301 | .glob_cmd_reg = ADIS16201_GLOB_CMD, | ||
| 302 | .diag_stat_reg = ADIS16201_DIAG_STAT, | ||
| 303 | |||
| 304 | .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, | ||
| 305 | .self_test_no_autoclear = true, | ||
| 306 | .startup_delay = ADIS16201_STARTUP_DELAY, | ||
| 307 | |||
| 308 | .status_error_msgs = adis16201_status_error_msgs, | ||
| 309 | .status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) | | ||
| 310 | BIT(ADIS16201_DIAG_STAT_FLASH_UPT_BIT) | | ||
| 311 | BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) | | ||
| 312 | BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT), | ||
| 313 | }; | ||
| 314 | |||
| 315 | static int adis16201_probe(struct spi_device *spi) | ||
| 316 | { | ||
| 317 | int ret; | ||
| 318 | struct adis *st; | ||
| 319 | struct iio_dev *indio_dev; | ||
| 320 | |||
| 321 | /* setup the industrialio driver allocated elements */ | ||
| 322 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | ||
| 323 | if (!indio_dev) | ||
| 324 | return -ENOMEM; | ||
| 325 | |||
| 326 | st = iio_priv(indio_dev); | ||
| 327 | /* this is only used for removal purposes */ | ||
| 328 | spi_set_drvdata(spi, indio_dev); | ||
| 329 | |||
| 330 | indio_dev->name = spi->dev.driver->name; | ||
| 331 | indio_dev->dev.parent = &spi->dev; | ||
| 332 | indio_dev->info = &adis16201_info; | ||
| 333 | |||
| 334 | indio_dev->channels = adis16201_channels; | ||
| 335 | indio_dev->num_channels = ARRAY_SIZE(adis16201_channels); | ||
| 336 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 337 | |||
| 338 | ret = adis_init(st, indio_dev, spi, &adis16201_data); | ||
| 339 | if (ret) | ||
| 340 | return ret; | ||
| 341 | |||
| 342 | ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); | ||
| 343 | if (ret) | ||
| 344 | return ret; | ||
| 345 | |||
| 346 | /* Get the device into a sane initial state */ | ||
| 347 | ret = adis_initial_startup(st); | ||
| 348 | if (ret) | ||
| 349 | goto error_cleanup_buffer_trigger; | ||
| 350 | |||
| 351 | ret = iio_device_register(indio_dev); | ||
| 352 | if (ret < 0) | ||
| 353 | goto error_cleanup_buffer_trigger; | ||
| 354 | |||
| 355 | return 0; | ||
| 356 | |||
| 357 | error_cleanup_buffer_trigger: | ||
| 358 | adis_cleanup_buffer_and_trigger(st, indio_dev); | ||
| 359 | return ret; | ||
| 360 | } | ||
| 361 | |||
| 362 | static int adis16201_remove(struct spi_device *spi) | ||
| 363 | { | ||
| 364 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
| 365 | struct adis *st = iio_priv(indio_dev); | ||
| 366 | |||
| 367 | iio_device_unregister(indio_dev); | ||
| 368 | adis_cleanup_buffer_and_trigger(st, indio_dev); | ||
| 369 | |||
| 370 | return 0; | ||
| 371 | } | ||
| 372 | |||
| 373 | static struct spi_driver adis16201_driver = { | ||
| 374 | .driver = { | ||
| 375 | .name = "adis16201", | ||
| 376 | }, | ||
| 377 | .probe = adis16201_probe, | ||
| 378 | .remove = adis16201_remove, | ||
| 379 | }; | ||
| 380 | module_spi_driver(adis16201_driver); | ||
| 381 | |||
| 382 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | ||
| 383 | MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"); | ||
| 384 | MODULE_LICENSE("GPL v2"); | ||
| 385 | MODULE_ALIAS("spi:adis16201"); | ||
| @@ -18,8 +18,7 @@ · 17 unmodified lines | |||
| 18 | static int ad7606_par16_read_block(struct device *dev, | 18 | static int ad7606_par16_read_block(struct device *dev, |
| 19 | int count, void *buf) | 19 | int count, void *buf) |
| 20 | { | 20 | { |
| 21 | struct platform_device *pdev = to_platform_device(dev); | 21 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 22 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 23 | struct ad7606_state *st = iio_priv(indio_dev); | 22 | struct ad7606_state *st = iio_priv(indio_dev); |
| 24 | 23 | ||
| 25 | insw((unsigned long)st->base_address, buf, count); | 24 | insw((unsigned long)st->base_address, buf, count); |
| @@ -34,8 +33,7 @@ static const struct ad7606_bus_ops ad7606_par16_bops = { · 8 unmodified lines | |||
| 34 | static int ad7606_par8_read_block(struct device *dev, | 33 | static int ad7606_par8_read_block(struct device *dev, |
| 35 | int count, void *buf) | 34 | int count, void *buf) |
| 36 | { | 35 | { |
| 37 | struct platform_device *pdev = to_platform_device(dev); | 36 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| 38 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 39 | struct ad7606_state *st = iio_priv(indio_dev); | 37 | struct ad7606_state *st = iio_priv(indio_dev); |
| 40 | 38 | ||
| 41 | insb((unsigned long)st->base_address, buf, count * 2); | 39 | insb((unsigned long)st->base_address, buf, count * 2); |
| @@ -128,7 +128,7 @@ static const struct ad_sigma_delta_info ad7780_sigma_delta_info = { · 127 unmodified lines | |||
| 128 | }; | 128 | }; |
| 129 | 129 | ||
| 130 | #define AD7780_CHANNEL(bits, wordsize) \ | 130 | #define AD7780_CHANNEL(bits, wordsize) \ |
| 131 | AD_SD_CHANNEL(1, 0, 0, bits, 32, wordsize - bits) | 131 | AD_SD_CHANNEL_NO_SAMP_FREQ(1, 0, 0, bits, 32, wordsize - bits) |
| 132 | 132 | ||
| 133 | static const struct ad7780_chip_info ad7780_chip_info_tbl[] = { | 133 | static const struct ad7780_chip_info ad7780_chip_info_tbl[] = { |
| 134 | [ID_AD7170] = { | 134 | [ID_AD7170] = { |
| @@ -6,15 +6,15 @@ · 5 unmodified lines | |||
| 6 | * Licensed under the GPL-2. | 6 | * Licensed under the GPL-2. |
| 7 | */ | 7 | */ |
| 8 | 8 | ||
| 9 | #include <linux/interrupt.h> | 9 | #include <linux/delay.h> |
| 10 | #include <linux/device.h> | 10 | #include <linux/device.h> |
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/sysfs.h> | ||
| 14 | #include <linux/i2c.h> | 11 | #include <linux/i2c.h> |
| 15 | #include <linux/delay.h> | 12 | #include <linux/interrupt.h> |
| 13 | #include <linux/kernel.h> | ||
| 16 | #include <linux/module.h> | 14 | #include <linux/module.h> |
| 15 | #include <linux/slab.h> | ||
| 17 | #include <linux/stat.h> | 16 | #include <linux/stat.h> |
| 17 | #include <linux/sysfs.h> | ||
| 18 | 18 | ||
| 19 | #include <linux/iio/iio.h> | 19 | #include <linux/iio/iio.h> |
| 20 | #include <linux/iio/sysfs.h> | 20 | #include <linux/iio/sysfs.h> |
| @@ -220,8 +220,8 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, · 199 unmodified lines | |||
| 220 | struct iio_chan_spec const *chan) | 220 | struct iio_chan_spec const *chan) |
| 221 | { | 221 | { |
| 222 | struct ad7746_chip_info *chip = iio_priv(indio_dev); | 222 | struct ad7746_chip_info *chip = iio_priv(indio_dev); |
| 223 | int ret, delay, idx; | ||
| 224 | u8 vt_setup, cap_setup; | 223 | u8 vt_setup, cap_setup; |
| 224 | int ret, delay, idx; | ||
| 225 | 225 | ||
| 226 | switch (chan->type) { | 226 | switch (chan->type) { |
| 227 | case IIO_CAPACITANCE: | 227 | case IIO_CAPACITANCE: |
| @@ -289,8 +289,8 @@ static inline ssize_t ad7746_start_calib(struct device *dev, · 61 unmodified lines | |||
| 289 | { | 289 | { |
| 290 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 290 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 291 | struct ad7746_chip_info *chip = iio_priv(indio_dev); | 291 | struct ad7746_chip_info *chip = iio_priv(indio_dev); |
| 292 | bool doit; | ||
| 293 | int ret, timeout = 10; | 292 | int ret, timeout = 10; |
| 293 | bool doit; | ||
| 294 | 294 | ||
| 295 | ret = strtobool(buf, &doit); | 295 | ret = strtobool(buf, &doit); |
| 296 | if (ret < 0) | 296 | if (ret < 0) |
| @@ -410,8 +410,7 @@ static struct attribute *ad7746_attributes[] = { · 113 unmodified lines | |||
| 410 | &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, | 410 | &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, |
| 411 | &iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr, | 411 | &iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr, |
| 412 | &iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr, | 412 | &iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr, |
| 413 | &iio_const_attr_in_capacitance_sampling_frequency_available. | 413 | &iio_const_attr_in_capacitance_sampling_frequency_available.dev_attr.attr, |
| 414 | dev_attr.attr, | ||
| 415 | NULL, | 414 | NULL, |
| 416 | }; | 415 | }; |
| 417 | 416 | ||
| @@ -451,26 +450,26 @@ static int ad7746_write_raw(struct iio_dev *indio_dev, · 33 unmodified lines | |||
| 451 | goto out; | 450 | goto out; |
| 452 | } | 451 | } |
| 453 | 452 | ||
| 454 | ret = i2c_smbus_write_word_data(chip->client, reg, swab16(val)); | 453 | ret = i2c_smbus_write_word_swapped(chip->client, reg, val); |
| 455 | if (ret < 0) | 454 | if (ret < 0) |
| 456 | goto out; | 455 | goto out; |
| 457 | 456 | ||
| 458 | ret = 0; | 457 | ret = 0; |
| 459 | break; | 458 | break; |
| 460 | case IIO_CHAN_INFO_CALIBBIAS: | 459 | case IIO_CHAN_INFO_CALIBBIAS: |
| 461 | if ((val < 0) | (val > 0xFFFF)) { | 460 | if (val < 0 || val > 0xFFFF) { |
| 462 | ret = -EINVAL; | 461 | ret = -EINVAL; |
| 463 | goto out; | 462 | goto out; |
| 464 | } | 463 | } |
| 465 | ret = i2c_smbus_write_word_data(chip->client, | 464 | ret = i2c_smbus_write_word_swapped(chip->client, |
| 466 | AD7746_REG_CAP_OFFH, swab16(val)); | 465 | AD7746_REG_CAP_OFFH, val); |
| 467 | if (ret < 0) | 466 | if (ret < 0) |
| 468 | goto out; | 467 | goto out; |
| 469 | 468 | ||
| 470 | ret = 0; | 469 | ret = 0; |
| 471 | break; | 470 | break; |
| 472 | case IIO_CHAN_INFO_OFFSET: | 471 | case IIO_CHAN_INFO_OFFSET: |
| 473 | if ((val < 0) | (val > 43008000)) { /* 21pF */ | 472 | if (val < 0 || val > 43008000) { /* 21pF */ |
| 474 | ret = -EINVAL; | 473 | ret = -EINVAL; |
| 475 | goto out; | 474 | goto out; |
| 476 | } | 475 | } |
| @@ -556,7 +555,8 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, · 79 unmodified lines | |||
| 556 | /* Now read the actual register */ | 555 | /* Now read the actual register */ |
| 557 | 556 | ||
| 558 | ret = i2c_smbus_read_i2c_block_data(chip->client, | 557 | ret = i2c_smbus_read_i2c_block_data(chip->client, |
| 559 | chan->address >> 8, 3, &chip->data.d8[1]); | 558 | chan->address >> 8, 3, |
| 559 | &chip->data.d8[1]); | ||
| 560 | 560 | ||
| 561 | if (ret < 0) | 561 | if (ret < 0) |
| 562 | goto out; | 562 | goto out; |
| @@ -594,27 +594,27 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, · 31 unmodified lines | |||
| 594 | goto out; | 594 | goto out; |
| 595 | } | 595 | } |
| 596 | 596 | ||
| 597 | ret = i2c_smbus_read_word_data(chip->client, reg); | 597 | ret = i2c_smbus_read_word_swapped(chip->client, reg); |
| 598 | if (ret < 0) | 598 | if (ret < 0) |
| 599 | goto out; | 599 | goto out; |
| 600 | /* 1 + gain_val / 2^16 */ | 600 | /* 1 + gain_val / 2^16 */ |
| 601 | *val = 1; | 601 | *val = 1; |
| 602 | *val2 = (15625 * swab16(ret)) / 1024; | 602 | *val2 = (15625 * ret) / 1024; |
| 603 | 603 | ||
| 604 | ret = IIO_VAL_INT_PLUS_MICRO; | 604 | ret = IIO_VAL_INT_PLUS_MICRO; |
| 605 | break; | 605 | break; |
| 606 | case IIO_CHAN_INFO_CALIBBIAS: | 606 | case IIO_CHAN_INFO_CALIBBIAS: |
| 607 | ret = i2c_smbus_read_word_data(chip->client, | 607 | ret = i2c_smbus_read_word_swapped(chip->client, |
| 608 | AD7746_REG_CAP_OFFH); | 608 | AD7746_REG_CAP_OFFH); |
| 609 | if (ret < 0) | 609 | if (ret < 0) |
| 610 | goto out; | 610 | goto out; |
| 611 | *val = swab16(ret); | 611 | *val = ret; |
| 612 | 612 | ||
| 613 | ret = IIO_VAL_INT; | 613 | ret = IIO_VAL_INT; |
| 614 | break; | 614 | break; |
| 615 | case IIO_CHAN_INFO_OFFSET: | 615 | case IIO_CHAN_INFO_OFFSET: |
| 616 | *val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel] | 616 | *val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel] |
| 617 | [chan->differential]) * 338646; | 617 | [chan->differential]) * 338646; |
| 618 | 618 | ||
| 619 | ret = IIO_VAL_INT; | 619 | ret = IIO_VAL_INT; |
| 620 | break; | 620 | break; |
| @@ -680,8 +680,8 @@ static int ad7746_probe(struct i2c_client *client, · 59 unmodified lines | |||
| 680 | struct ad7746_platform_data *pdata = client->dev.platform_data; | 680 | struct ad7746_platform_data *pdata = client->dev.platform_data; |
| 681 | struct ad7746_chip_info *chip; | 681 | struct ad7746_chip_info *chip; |
| 682 | struct iio_dev *indio_dev; | 682 | struct iio_dev *indio_dev; |
| 683 | int ret = 0; | ||
| 684 | unsigned char regval = 0; | 683 | unsigned char regval = 0; |
| 684 | int ret = 0; | ||
| 685 | 685 | ||
| 686 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); | 686 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); |
| 687 | if (!indio_dev) | 687 | if (!indio_dev) |
| @@ -1,14 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Light sensors | ||
| 3 | # | ||
| 4 | menu "Light sensors" | ||
| 5 | |||
| 6 | config TSL2x7x | ||
| 7 | tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" | ||
| 8 | depends on I2C | ||
| 9 | help | ||
| 10 | Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, | ||
| 11 | tmd2672, tsl2772, tmd2772 devices. | ||
| 12 | Provides iio_events and direct access via sysfs. | ||
| 13 | |||
| 14 | endmenu | ||
| @@ -1,5 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Makefile for industrial I/O Light sensors | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_TSL2x7x) += tsl2x7x.o | ||
| @@ -1,1889 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Device driver for monitoring ambient light intensity in (lux) | ||
| 3 | * and proximity detection (prox) within the TAOS TSL2X7X family of devices. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2012, TAOS Corporation. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 15 | * more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/errno.h> | ||
| 20 | #include <linux/i2c.h> | ||
| 21 | #include <linux/interrupt.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/mutex.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | #include <linux/iio/events.h> | ||
| 27 | #include <linux/iio/iio.h> | ||
| 28 | #include <linux/iio/sysfs.h> | ||
| 29 | #include "tsl2x7x.h" | ||
| 30 | |||
| 31 | /* Cal defs*/ | ||
| 32 | #define PROX_STAT_CAL 0 | ||
| 33 | #define PROX_STAT_SAMP 1 | ||
| 34 | #define MAX_SAMPLES_CAL 200 | ||
| 35 | |||
| 36 | /* TSL2X7X Device ID */ | ||
| 37 | #define TRITON_ID 0x00 | ||
| 38 | #define SWORDFISH_ID 0x30 | ||
| 39 | #define HALIBUT_ID 0x20 | ||
| 40 | |||
| 41 | /* Lux calculation constants */ | ||
| 42 | #define TSL2X7X_LUX_CALC_OVER_FLOW 65535 | ||
| 43 | |||
| 44 | /* TAOS Register definitions - note: | ||
| 45 | * depending on device, some of these register are not used and the | ||
| 46 | * register address is benign. | ||
| 47 | */ | ||
| 48 | /* 2X7X register offsets */ | ||
| 49 | #define TSL2X7X_MAX_CONFIG_REG 16 | ||
| 50 | |||
| 51 | /* Device Registers and Masks */ | ||
| 52 | #define TSL2X7X_CNTRL 0x00 | ||
| 53 | #define TSL2X7X_ALS_TIME 0X01 | ||
| 54 | #define TSL2X7X_PRX_TIME 0x02 | ||
| 55 | #define TSL2X7X_WAIT_TIME 0x03 | ||
| 56 | #define TSL2X7X_ALS_MINTHRESHLO 0X04 | ||
| 57 | #define TSL2X7X_ALS_MINTHRESHHI 0X05 | ||
| 58 | #define TSL2X7X_ALS_MAXTHRESHLO 0X06 | ||
| 59 | #define TSL2X7X_ALS_MAXTHRESHHI 0X07 | ||
| 60 | #define TSL2X7X_PRX_MINTHRESHLO 0X08 | ||
| 61 | #define TSL2X7X_PRX_MINTHRESHHI 0X09 | ||
| 62 | #define TSL2X7X_PRX_MAXTHRESHLO 0X0A | ||
| 63 | #define TSL2X7X_PRX_MAXTHRESHHI 0X0B | ||
| 64 | #define TSL2X7X_PERSISTENCE 0x0C | ||
| 65 | #define TSL2X7X_PRX_CONFIG 0x0D | ||
| 66 | #define TSL2X7X_PRX_COUNT 0x0E | ||
| 67 | #define TSL2X7X_GAIN 0x0F | ||
| 68 | #define TSL2X7X_NOTUSED 0x10 | ||
| 69 | #define TSL2X7X_REVID 0x11 | ||
| 70 | #define TSL2X7X_CHIPID 0x12 | ||
| 71 | #define TSL2X7X_STATUS 0x13 | ||
| 72 | #define TSL2X7X_ALS_CHAN0LO 0x14 | ||
| 73 | #define TSL2X7X_ALS_CHAN0HI 0x15 | ||
| 74 | #define TSL2X7X_ALS_CHAN1LO 0x16 | ||
| 75 | #define TSL2X7X_ALS_CHAN1HI 0x17 | ||
| 76 | #define TSL2X7X_PRX_LO 0x18 | ||
| 77 | #define TSL2X7X_PRX_HI 0x19 | ||
| 78 | |||
| 79 | /* tsl2X7X cmd reg masks */ | ||
| 80 | #define TSL2X7X_CMD_REG 0x80 | ||
| 81 | #define TSL2X7X_CMD_SPL_FN 0x60 | ||
| 82 | |||
| 83 | #define TSL2X7X_CMD_PROX_INT_CLR 0X05 | ||
| 84 | #define TSL2X7X_CMD_ALS_INT_CLR 0x06 | ||
| 85 | #define TSL2X7X_CMD_PROXALS_INT_CLR 0X07 | ||
| 86 | |||
| 87 | /* tsl2X7X cntrl reg masks */ | ||
| 88 | #define TSL2X7X_CNTL_ADC_ENBL 0x02 | ||
| 89 | #define TSL2X7X_CNTL_PWR_ON 0x01 | ||
| 90 | |||
| 91 | /* tsl2X7X status reg masks */ | ||
| 92 | #define TSL2X7X_STA_ADC_VALID 0x01 | ||
| 93 | #define TSL2X7X_STA_PRX_VALID 0x02 | ||
| 94 | #define TSL2X7X_STA_ADC_PRX_VALID (TSL2X7X_STA_ADC_VALID | \ | ||
| 95 | TSL2X7X_STA_PRX_VALID) | ||
| 96 | #define TSL2X7X_STA_ALS_INTR 0x10 | ||
| 97 | #define TSL2X7X_STA_PRX_INTR 0x20 | ||
| 98 | |||
| 99 | /* tsl2X7X cntrl reg masks */ | ||
| 100 | #define TSL2X7X_CNTL_REG_CLEAR 0x00 | ||
| 101 | #define TSL2X7X_CNTL_PROX_INT_ENBL 0X20 | ||
| 102 | #define TSL2X7X_CNTL_ALS_INT_ENBL 0X10 | ||
| 103 | #define TSL2X7X_CNTL_WAIT_TMR_ENBL 0X08 | ||
| 104 | #define TSL2X7X_CNTL_PROX_DET_ENBL 0X04 | ||
| 105 | #define TSL2X7X_CNTL_PWRON 0x01 | ||
| 106 | #define TSL2X7X_CNTL_ALSPON_ENBL 0x03 | ||
| 107 | #define TSL2X7X_CNTL_INTALSPON_ENBL 0x13 | ||
| 108 | #define TSL2X7X_CNTL_PROXPON_ENBL 0x0F | ||
| 109 | #define TSL2X7X_CNTL_INTPROXPON_ENBL 0x2F | ||
| 110 | |||
| 111 | /*Prox diode to use */ | ||
| 112 | #define TSL2X7X_DIODE0 0x01 | ||
| 113 | #define TSL2X7X_DIODE1 0x02 | ||
| 114 | #define TSL2X7X_DIODE_BOTH 0x03 | ||
| 115 | |||
| 116 | /* LED Power */ | ||
| 117 | #define TSL2X7X_100_mA 0x00 | ||
| 118 | #define TSL2X7X_50_mA 0x01 | ||
| 119 | #define TSL2X7X_25_mA 0x02 | ||
| 120 | #define TSL2X7X_13_mA 0x03 | ||
| 121 | #define TSL2X7X_MAX_TIMER_CNT 0xFF | ||
| 122 | |||
| 123 | #define TSL2X7X_MIN_ITIME 3 | ||
| 124 | |||
| 125 | /* TAOS txx2x7x Device family members */ | ||
| 126 | enum { | ||
| 127 | tsl2571, | ||
| 128 | tsl2671, | ||
| 129 | tmd2671, | ||
| 130 | tsl2771, | ||
| 131 | tmd2771, | ||
| 132 | tsl2572, | ||
| 133 | tsl2672, | ||
| 134 | tmd2672, | ||
| 135 | tsl2772, | ||
| 136 | tmd2772 | ||
| 137 | }; | ||
| 138 | |||
| 139 | enum { | ||
| 140 | TSL2X7X_CHIP_UNKNOWN = 0, | ||
| 141 | TSL2X7X_CHIP_WORKING = 1, | ||
| 142 | TSL2X7X_CHIP_SUSPENDED = 2 | ||
| 143 | }; | ||
| 144 | |||
| 145 | /* Per-device data */ | ||
| 146 | struct tsl2x7x_als_info { | ||
| 147 | u16 als_ch0; | ||
| 148 | u16 als_ch1; | ||
| 149 | u16 lux; | ||
| 150 | }; | ||
| 151 | |||
| 152 | struct tsl2x7x_prox_stat { | ||
| 153 | int min; | ||
| 154 | int max; | ||
| 155 | int mean; | ||
| 156 | unsigned long stddev; | ||
| 157 | }; | ||
| 158 | |||
| 159 | struct tsl2x7x_chip_info { | ||
| 160 | int chan_table_elements; | ||
| 161 | struct iio_chan_spec channel[4]; | ||
| 162 | const struct iio_info *info; | ||
| 163 | }; | ||
| 164 | |||
| 165 | struct tsl2X7X_chip { | ||
| 166 | kernel_ulong_t id; | ||
| 167 | struct mutex prox_mutex; | ||
| 168 | struct mutex als_mutex; | ||
| 169 | struct i2c_client *client; | ||
| 170 | u16 prox_data; | ||
| 171 | struct tsl2x7x_als_info als_cur_info; | ||
| 172 | struct tsl2x7x_settings settings; | ||
| 173 | struct tsl2X7X_platform_data *pdata; | ||
| 174 | int als_time_scale; | ||
| 175 | int als_saturation; | ||
| 176 | int tsl2x7x_chip_status; | ||
| 177 | u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG]; | ||
| 178 | const struct tsl2x7x_chip_info *chip_info; | ||
| 179 | const struct iio_info *info; | ||
| 180 | s64 event_timestamp; | ||
| 181 | /* | ||
| 182 | * This structure is intentionally large to accommodate | ||
| 183 | * updates via sysfs. | ||
| 184 | * Sized to 9 = max 8 segments + 1 termination segment | ||
| 185 | */ | ||
| 186 | struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE]; | ||
| 187 | }; | ||
| 188 | |||
| 189 | /* Different devices require different coefficents */ | ||
| 190 | static const struct tsl2x7x_lux tsl2x71_lux_table[TSL2X7X_DEF_LUX_TABLE_SZ] = { | ||
| 191 | { 14461, 611, 1211 }, | ||
| 192 | { 18540, 352, 623 }, | ||
| 193 | { 0, 0, 0 }, | ||
| 194 | }; | ||
| 195 | |||
| 196 | static const struct tsl2x7x_lux tmd2x71_lux_table[TSL2X7X_DEF_LUX_TABLE_SZ] = { | ||
| 197 | { 11635, 115, 256 }, | ||
| 198 | { 15536, 87, 179 }, | ||
| 199 | { 0, 0, 0 }, | ||
| 200 | }; | ||
| 201 | |||
| 202 | static const struct tsl2x7x_lux tsl2x72_lux_table[TSL2X7X_DEF_LUX_TABLE_SZ] = { | ||
| 203 | { 14013, 466, 917 }, | ||
| 204 | { 18222, 310, 552 }, | ||
| 205 | { 0, 0, 0 }, | ||
| 206 | }; | ||
| 207 | |||
| 208 | static const struct tsl2x7x_lux tmd2x72_lux_table[TSL2X7X_DEF_LUX_TABLE_SZ] = { | ||
| 209 | { 13218, 130, 262 }, | ||
| 210 | { 17592, 92, 169 }, | ||
| 211 | { 0, 0, 0 }, | ||
| 212 | }; | ||
| 213 | |||
| 214 | static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = { | ||
| 215 | [tsl2571] = tsl2x71_lux_table, | ||
| 216 | [tsl2671] = tsl2x71_lux_table, | ||
| 217 | [tmd2671] = tmd2x71_lux_table, | ||
| 218 | [tsl2771] = tsl2x71_lux_table, | ||
| 219 | [tmd2771] = tmd2x71_lux_table, | ||
| 220 | [tsl2572] = tsl2x72_lux_table, | ||
| 221 | [tsl2672] = tsl2x72_lux_table, | ||
| 222 | [tmd2672] = tmd2x72_lux_table, | ||
| 223 | [tsl2772] = tsl2x72_lux_table, | ||
| 224 | [tmd2772] = tmd2x72_lux_table, | ||
| 225 | }; | ||
| 226 | |||
| 227 | static const struct tsl2x7x_settings tsl2x7x_default_settings = { | ||
| 228 | .als_time = 219, /* 101 ms */ | ||
| 229 | .als_gain = 0, | ||
| 230 | .prx_time = 254, /* 5.4 ms */ | ||
| 231 | .prox_gain = 0, | ||
| 232 | .wait_time = 245, | ||
| 233 | .prox_config = 0, | ||
| 234 | .als_gain_trim = 1000, | ||
| 235 | .als_cal_target = 150, | ||
| 236 | .als_thresh_low = 200, | ||
| 237 | .als_thresh_high = 256, | ||
| 238 | .persistence = 255, | ||
| 239 | .interrupts_en = 0, | ||
| 240 | .prox_thres_low = 0, | ||
| 241 | .prox_thres_high = 512, | ||
| 242 | .prox_max_samples_cal = 30, | ||
| 243 | .prox_pulse_count = 8, | ||
| 244 | .prox_diode = TSL2X7X_DIODE1, | ||
| 245 | .prox_power = TSL2X7X_100_mA | ||
| 246 | }; | ||
| 247 | |||
| 248 | static const s16 tsl2x7x_als_gain[] = { | ||
| 249 | 1, | ||
| 250 | 8, | ||
| 251 | 16, | ||
| 252 | 120 | ||
| 253 | }; | ||
| 254 | |||
| 255 | static const s16 tsl2x7x_prx_gain[] = { | ||
| 256 | 1, | ||
| 257 | 2, | ||
| 258 | 4, | ||
| 259 | 8 | ||
| 260 | }; | ||
| 261 | |||
| 262 | /* Channel variations */ | ||
| 263 | enum { | ||
| 264 | ALS, | ||
| 265 | PRX, | ||
| 266 | ALSPRX, | ||
| 267 | PRX2, | ||
| 268 | ALSPRX2, | ||
| 269 | }; | ||
| 270 | |||
| 271 | static const u8 device_channel_config[] = { | ||
| 272 | ALS, | ||
| 273 | PRX, | ||
| 274 | PRX, | ||
| 275 | ALSPRX, | ||
| 276 | ALSPRX, | ||
| 277 | ALS, | ||
| 278 | PRX2, | ||
| 279 | PRX2, | ||
| 280 | ALSPRX2, | ||
| 281 | ALSPRX2 | ||
| 282 | }; | ||
| 283 | |||
| 284 | static int tsl2x7x_clear_interrupts(struct tsl2X7X_chip *chip, int reg) | ||
| 285 | { | ||
| 286 | int ret; | ||
| 287 | |||
| 288 | ret = i2c_smbus_write_byte(chip->client, | ||
| 289 | TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | reg); | ||
| 290 | if (ret < 0) | ||
| 291 | dev_err(&chip->client->dev, | ||
| 292 | "%s: failed to clear interrupt status %x: %d\n", | ||
| 293 | __func__, reg, ret); | ||
| 294 | |||
| 295 | return ret; | ||
| 296 | } | ||
| 297 | |||
| 298 | static int tsl2x7x_read_status(struct tsl2X7X_chip *chip) | ||
| 299 | { | ||
| 300 | int ret; | ||
| 301 | |||
| 302 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 303 | TSL2X7X_CMD_REG | TSL2X7X_STATUS); | ||
| 304 | if (ret < 0) | ||
| 305 | dev_err(&chip->client->dev, | ||
| 306 | "%s: failed to read STATUS register: %d\n", __func__, | ||
| 307 | ret); | ||
| 308 | |||
| 309 | return ret; | ||
| 310 | } | ||
| 311 | |||
| 312 | static int tsl2x7x_write_control_reg(struct tsl2X7X_chip *chip, u8 data) | ||
| 313 | { | ||
| 314 | int ret; | ||
| 315 | |||
| 316 | ret = i2c_smbus_write_byte_data(chip->client, | ||
| 317 | TSL2X7X_CMD_REG | TSL2X7X_CNTRL, data); | ||
| 318 | if (ret < 0) { | ||
| 319 | dev_err(&chip->client->dev, | ||
| 320 | "%s: failed to write to control register %x: %d\n", | ||
| 321 | __func__, data, ret); | ||
| 322 | } | ||
| 323 | |||
| 324 | return ret; | ||
| 325 | } | ||
| 326 | |||
| 327 | /** | ||
| 328 | * tsl2x7x_get_lux() - Reads and calculates current lux value. | ||
| 329 | * @indio_dev: pointer to IIO device | ||
| 330 | * | ||
| 331 | * The raw ch0 and ch1 values of the ambient light sensed in the last | ||
| 332 | * integration cycle are read from the device. | ||
| 333 | * Time scale factor array values are adjusted based on the integration time. | ||
| 334 | * The raw values are multiplied by a scale factor, and device gain is obtained | ||
| 335 | * using gain index. Limit checks are done next, then the ratio of a multiple | ||
| 336 | * of ch1 value, to the ch0 value, is calculated. Array tsl2x7x_device_lux[] | ||
| 337 | * is then scanned to find the first ratio value that is just above the ratio | ||
| 338 | * we just calculated. The ch0 and ch1 multiplier constants in the array are | ||
| 339 | * then used along with the time scale factor array values, to calculate the | ||
| 340 | * lux. | ||
| 341 | */ | ||
| 342 | static int tsl2x7x_get_lux(struct iio_dev *indio_dev) | ||
| 343 | { | ||
| 344 | u16 ch0, ch1; /* separated ch0/ch1 data from device */ | ||
| 345 | u32 lux; /* raw lux calculated from device data */ | ||
| 346 | u64 lux64; | ||
| 347 | u32 ratio; | ||
| 348 | u8 buf[4]; | ||
| 349 | struct tsl2x7x_lux *p; | ||
| 350 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 351 | int i, ret; | ||
| 352 | u32 ch0lux = 0; | ||
| 353 | u32 ch1lux = 0; | ||
| 354 | |||
| 355 | mutex_lock(&chip->als_mutex); | ||
| 356 | |||
| 357 | if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) { | ||
| 358 | /* device is not enabled */ | ||
| 359 | dev_err(&chip->client->dev, "%s: device is not enabled\n", | ||
| 360 | __func__); | ||
| 361 | ret = -EBUSY; | ||
| 362 | goto out_unlock; | ||
| 363 | } | ||
| 364 | |||
| 365 | ret = tsl2x7x_read_status(chip); | ||
| 366 | if (ret < 0) | ||
| 367 | goto out_unlock; | ||
| 368 | |||
| 369 | /* is data new & valid */ | ||
| 370 | if (!(ret & TSL2X7X_STA_ADC_VALID)) { | ||
| 371 | dev_err(&chip->client->dev, | ||
| 372 | "%s: data not valid yet\n", __func__); | ||
| 373 | ret = chip->als_cur_info.lux; /* return LAST VALUE */ | ||
| 374 | goto out_unlock; | ||
| 375 | } | ||
| 376 | |||
| 377 | for (i = 0; i < 4; i++) { | ||
| 378 | int reg = TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i); | ||
| 379 | |||
| 380 | ret = i2c_smbus_read_byte_data(chip->client, reg); | ||
| 381 | if (ret < 0) { | ||
| 382 | dev_err(&chip->client->dev, | ||
| 383 | "failed to read. err=%x\n", ret); | ||
| 384 | goto out_unlock; | ||
| 385 | } | ||
| 386 | |||
| 387 | buf[i] = ret; | ||
| 388 | } | ||
| 389 | |||
| 390 | ret = tsl2x7x_clear_interrupts(chip, TSL2X7X_CMD_ALS_INT_CLR); | ||
| 391 | if (ret < 0) | ||
| 392 | goto out_unlock; | ||
| 393 | |||
| 394 | /* extract ALS/lux data */ | ||
| 395 | ch0 = le16_to_cpup((const __le16 *)&buf[0]); | ||
| 396 | ch1 = le16_to_cpup((const __le16 *)&buf[2]); | ||
| 397 | |||
| 398 | chip->als_cur_info.als_ch0 = ch0; | ||
| 399 | chip->als_cur_info.als_ch1 = ch1; | ||
| 400 | |||
| 401 | if (ch0 >= chip->als_saturation || ch1 >= chip->als_saturation) { | ||
| 402 | lux = TSL2X7X_LUX_CALC_OVER_FLOW; | ||
| 403 | goto return_max; | ||
| 404 | } | ||
| 405 | |||
| 406 | if (!ch0) { | ||
| 407 | /* have no data, so return LAST VALUE */ | ||
| 408 | ret = chip->als_cur_info.lux; | ||
| 409 | goto out_unlock; | ||
| 410 | } | ||
| 411 | /* calculate ratio */ | ||
| 412 | ratio = (ch1 << 15) / ch0; | ||
| 413 | /* convert to unscaled lux using the pointer to the table */ | ||
| 414 | p = (struct tsl2x7x_lux *)chip->tsl2x7x_device_lux; | ||
| 415 | while (p->ratio != 0 && p->ratio < ratio) | ||
| 416 | p++; | ||
| 417 | |||
| 418 | if (p->ratio == 0) { | ||
| 419 | lux = 0; | ||
| 420 | } else { | ||
| 421 | lux = DIV_ROUND_UP(ch0 * p->ch0, | ||
| 422 | tsl2x7x_als_gain[chip->settings.als_gain]) - | ||
| 423 | DIV_ROUND_UP(ch1 * p->ch1, | ||
| 424 | tsl2x7x_als_gain[chip->settings.als_gain]); | ||
| 425 | } | ||
| 426 | |||
| 427 | /* note: lux is 31 bit max at this point */ | ||
| 428 | if (ch1lux > ch0lux) { | ||
| 429 | dev_dbg(&chip->client->dev, "ch1lux > ch0lux-return last value\n"); | ||
| 430 | ret = chip->als_cur_info.lux; | ||
| 431 | goto out_unlock; | ||
| 432 | } | ||
| 433 | |||
| 434 | /* adjust for active time scale */ | ||
| 435 | if (chip->als_time_scale == 0) | ||
| 436 | lux = 0; | ||
| 437 | else | ||
| 438 | lux = (lux + (chip->als_time_scale >> 1)) / | ||
| 439 | chip->als_time_scale; | ||
| 440 | |||
| 441 | /* adjust for active gain scale | ||
| 442 | * The tsl2x7x_device_lux tables have a factor of 256 built-in. | ||
| 443 | * User-specified gain provides a multiplier. | ||
| 444 | * Apply user-specified gain before shifting right to retain precision. | ||
| 445 | * Use 64 bits to avoid overflow on multiplication. | ||
| 446 | * Then go back to 32 bits before division to avoid using div_u64(). | ||
| 447 | */ | ||
| 448 | |||
| 449 | lux64 = lux; | ||
| 450 | lux64 = lux64 * chip->settings.als_gain_trim; | ||
| 451 | lux64 >>= 8; | ||
| 452 | lux = lux64; | ||
| 453 | lux = (lux + 500) / 1000; | ||
| 454 | |||
| 455 | if (lux > TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */ | ||
| 456 | lux = TSL2X7X_LUX_CALC_OVER_FLOW; | ||
| 457 | |||
| 458 | /* Update the structure with the latest lux. */ | ||
| 459 | return_max: | ||
| 460 | chip->als_cur_info.lux = lux; | ||
| 461 | ret = lux; | ||
| 462 | |||
| 463 | out_unlock: | ||
| 464 | mutex_unlock(&chip->als_mutex); | ||
| 465 | |||
| 466 | return ret; | ||
| 467 | } | ||
| 468 | |||
| 469 | /** | ||
| 470 | * tsl2x7x_get_prox() - Reads proximity data registers and updates | ||
| 471 | * chip->prox_data. | ||
| 472 | * | ||
| 473 | * @indio_dev: pointer to IIO device | ||
| 474 | */ | ||
| 475 | static int tsl2x7x_get_prox(struct iio_dev *indio_dev) | ||
| 476 | { | ||
| 477 | int i; | ||
| 478 | int ret; | ||
| 479 | u8 chdata[2]; | ||
| 480 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 481 | |||
| 482 | mutex_lock(&chip->prox_mutex); | ||
| 483 | |||
| 484 | ret = tsl2x7x_read_status(chip); | ||
| 485 | if (ret < 0) | ||
| 486 | goto prox_poll_err; | ||
| 487 | |||
| 488 | switch (chip->id) { | ||
| 489 | case tsl2571: | ||
| 490 | case tsl2671: | ||
| 491 | case tmd2671: | ||
| 492 | case tsl2771: | ||
| 493 | case tmd2771: | ||
| 494 | if (!(ret & TSL2X7X_STA_ADC_VALID)) { | ||
| 495 | ret = -EINVAL; | ||
| 496 | goto prox_poll_err; | ||
| 497 | } | ||
| 498 | break; | ||
| 499 | case tsl2572: | ||
| 500 | case tsl2672: | ||
| 501 | case tmd2672: | ||
| 502 | case tsl2772: | ||
| 503 | case tmd2772: | ||
| 504 | if (!(ret & TSL2X7X_STA_PRX_VALID)) { | ||
| 505 | ret = -EINVAL; | ||
| 506 | goto prox_poll_err; | ||
| 507 | } | ||
| 508 | break; | ||
| 509 | } | ||
| 510 | |||
| 511 | for (i = 0; i < 2; i++) { | ||
| 512 | int reg = TSL2X7X_CMD_REG | (TSL2X7X_PRX_LO + i); | ||
| 513 | |||
| 514 | ret = i2c_smbus_read_byte_data(chip->client, reg); | ||
| 515 | if (ret < 0) | ||
| 516 | goto prox_poll_err; | ||
| 517 | |||
| 518 | chdata[i] = ret; | ||
| 519 | } | ||
| 520 | |||
| 521 | chip->prox_data = le16_to_cpup((const __le16 *)&chdata[0]); | ||
| 522 | ret = chip->prox_data; | ||
| 523 | |||
| 524 | prox_poll_err: | ||
| 525 | mutex_unlock(&chip->prox_mutex); | ||
| 526 | |||
| 527 | return ret; | ||
| 528 | } | ||
| 529 | |||
| 530 | /** | ||
| 531 | * tsl2x7x_defaults() - Populates the device nominal operating parameters | ||
| 532 | * with those provided by a 'platform' data struct or | ||
| 533 | * with prefined defaults. | ||
| 534 | * | ||
| 535 | * @chip: pointer to device structure. | ||
| 536 | */ | ||
| 537 | static void tsl2x7x_defaults(struct tsl2X7X_chip *chip) | ||
| 538 | { | ||
| 539 | /* If Operational settings defined elsewhere.. */ | ||
| 540 | if (chip->pdata && chip->pdata->platform_default_settings) | ||
| 541 | memcpy(&chip->settings, chip->pdata->platform_default_settings, | ||
| 542 | sizeof(tsl2x7x_default_settings)); | ||
| 543 | else | ||
| 544 | memcpy(&chip->settings, &tsl2x7x_default_settings, | ||
| 545 | sizeof(tsl2x7x_default_settings)); | ||
| 546 | |||
| 547 | /* Load up the proper lux table. */ | ||
| 548 | if (chip->pdata && chip->pdata->platform_lux_table[0].ratio != 0) | ||
| 549 | memcpy(chip->tsl2x7x_device_lux, | ||
| 550 | chip->pdata->platform_lux_table, | ||
| 551 | sizeof(chip->pdata->platform_lux_table)); | ||
| 552 | else | ||
| 553 | memcpy(chip->tsl2x7x_device_lux, | ||
| 554 | tsl2x7x_default_lux_table_group[chip->id], | ||
| 555 | TSL2X7X_DEFAULT_TABLE_BYTES); | ||
| 556 | } | ||
| 557 | |||
| 558 | /** | ||
| 559 | * tsl2x7x_als_calibrate() - Obtain single reading and calculate | ||
| 560 | * the als_gain_trim. | ||
| 561 | * | ||
| 562 | * @indio_dev: pointer to IIO device | ||
| 563 | */ | ||
| 564 | static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev) | ||
| 565 | { | ||
| 566 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 567 | int ret, lux_val; | ||
| 568 | |||
| 569 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 570 | TSL2X7X_CMD_REG | TSL2X7X_CNTRL); | ||
| 571 | if (ret < 0) { | ||
| 572 | dev_err(&chip->client->dev, | ||
| 573 | "%s: failed to read from the CNTRL register\n", | ||
| 574 | __func__); | ||
| 575 | return ret; | ||
| 576 | } | ||
| 577 | |||
| 578 | if ((ret & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) | ||
| 579 | != (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) { | ||
| 580 | dev_err(&chip->client->dev, | ||
| 581 | "%s: Device is not powered on and/or ADC is not enabled\n", | ||
| 582 | __func__); | ||
| 583 | return -EINVAL; | ||
| 584 | } else if ((ret & TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) { | ||
| 585 | dev_err(&chip->client->dev, | ||
| 586 | "%s: The two ADC channels have not completed an integration cycle\n", | ||
| 587 | __func__); | ||
| 588 | return -ENODATA; | ||
| 589 | } | ||
| 590 | |||
| 591 | lux_val = tsl2x7x_get_lux(indio_dev); | ||
| 592 | if (lux_val < 0) { | ||
| 593 | dev_err(&chip->client->dev, | ||
| 594 | "%s: failed to get lux\n", __func__); | ||
| 595 | return lux_val; | ||
| 596 | } | ||
| 597 | |||
| 598 | ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / | ||
| 599 | lux_val; | ||
| 600 | if (ret < 250 || ret > 4000) | ||
| 601 | return -ERANGE; | ||
| 602 | |||
| 603 | chip->settings.als_gain_trim = ret; | ||
| 604 | dev_info(&chip->client->dev, | ||
| 605 | "%s als_calibrate completed\n", chip->client->name); | ||
| 606 | |||
| 607 | return ret; | ||
| 608 | } | ||
| 609 | |||
| 610 | static int tsl2x7x_chip_on(struct iio_dev *indio_dev) | ||
| 611 | { | ||
| 612 | int i; | ||
| 613 | int ret = 0; | ||
| 614 | u8 *dev_reg; | ||
| 615 | int als_count; | ||
| 616 | int als_time; | ||
| 617 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 618 | u8 reg_val = 0; | ||
| 619 | |||
| 620 | /* Non calculated parameters */ | ||
| 621 | chip->tsl2x7x_config[TSL2X7X_PRX_TIME] = chip->settings.prx_time; | ||
| 622 | chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] = chip->settings.wait_time; | ||
| 623 | chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] = chip->settings.prox_config; | ||
| 624 | |||
| 625 | chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] = | ||
| 626 | (chip->settings.als_thresh_low) & 0xFF; | ||
| 627 | chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] = | ||
| 628 | (chip->settings.als_thresh_low >> 8) & 0xFF; | ||
| 629 | chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] = | ||
| 630 | (chip->settings.als_thresh_high) & 0xFF; | ||
| 631 | chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] = | ||
| 632 | (chip->settings.als_thresh_high >> 8) & 0xFF; | ||
| 633 | chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] = chip->settings.persistence; | ||
| 634 | |||
| 635 | chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] = | ||
| 636 | chip->settings.prox_pulse_count; | ||
| 637 | chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] = | ||
| 638 | (chip->settings.prox_thres_low) & 0xFF; | ||
| 639 | chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHHI] = | ||
| 640 | (chip->settings.prox_thres_low >> 8) & 0xFF; | ||
| 641 | chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] = | ||
| 642 | (chip->settings.prox_thres_high) & 0xFF; | ||
| 643 | chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHHI] = | ||
| 644 | (chip->settings.prox_thres_high >> 8) & 0xFF; | ||
| 645 | |||
| 646 | /* and make sure we're not already on */ | ||
| 647 | if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { | ||
| 648 | /* if forcing a register update - turn off, then on */ | ||
| 649 | dev_info(&chip->client->dev, "device is already enabled\n"); | ||
| 650 | return -EINVAL; | ||
| 651 | } | ||
| 652 | |||
| 653 | /* determine als integration register */ | ||
| 654 | als_count = (chip->settings.als_time * 100 + 135) / 270; | ||
| 655 | if (!als_count) | ||
| 656 | als_count = 1; /* ensure at least one cycle */ | ||
| 657 | |||
| 658 | /* convert back to time (encompasses overrides) */ | ||
| 659 | als_time = (als_count * 27 + 5) / 10; | ||
| 660 | chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count; | ||
| 661 | |||
| 662 | /* Set the gain based on tsl2x7x_settings struct */ | ||
| 663 | chip->tsl2x7x_config[TSL2X7X_GAIN] = | ||
| 664 | (chip->settings.als_gain & 0xFF) | | ||
| 665 | ((chip->settings.prox_gain & 0xFF) << 2) | | ||
| 666 | (chip->settings.prox_diode << 4) | | ||
| 667 | (chip->settings.prox_power << 6); | ||
| 668 | |||
| 669 | /* set chip struct re scaling and saturation */ | ||
| 670 | chip->als_saturation = als_count * 922; /* 90% of full scale */ | ||
| 671 | chip->als_time_scale = (als_time + 25) / 50; | ||
| 672 | |||
| 673 | /* | ||
| 674 | * TSL2X7X Specific power-on / adc enable sequence | ||
| 675 | * Power on the device 1st. | ||
| 676 | */ | ||
| 677 | ret = tsl2x7x_write_control_reg(chip, TSL2X7X_CNTL_PWR_ON); | ||
| 678 | if (ret < 0) | ||
| 679 | return ret; | ||
| 680 | |||
| 681 | /* | ||
| 682 | * Use the following shadow copy for our delay before enabling ADC. | ||
| 683 | * Write all the registers. | ||
| 684 | */ | ||
| 685 | for (i = 0, dev_reg = chip->tsl2x7x_config; | ||
| 686 | i < TSL2X7X_MAX_CONFIG_REG; i++) { | ||
| 687 | ret = i2c_smbus_write_byte_data(chip->client, | ||
| 688 | TSL2X7X_CMD_REG + i, | ||
| 689 | *dev_reg++); | ||
| 690 | if (ret < 0) { | ||
| 691 | dev_err(&chip->client->dev, | ||
| 692 | "failed on write to reg %d.\n", i); | ||
| 693 | return ret; | ||
| 694 | } | ||
| 695 | } | ||
| 696 | |||
| 697 | /* Power-on settling time */ | ||
| 698 | usleep_range(3000, 3500); | ||
| 699 | |||
| 700 | /* | ||
| 701 | * NOW enable the ADC | ||
| 702 | * initialize the desired mode of operation | ||
| 703 | */ | ||
| 704 | ret = tsl2x7x_write_control_reg(chip, | ||
| 705 | TSL2X7X_CNTL_PWR_ON | | ||
| 706 | TSL2X7X_CNTL_ADC_ENBL | | ||
| 707 | TSL2X7X_CNTL_PROX_DET_ENBL); | ||
| 708 | if (ret < 0) | ||
| 709 | return ret; | ||
| 710 | |||
| 711 | chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING; | ||
| 712 | |||
| 713 | if (chip->settings.interrupts_en != 0) { | ||
| 714 | dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n"); | ||
| 715 | |||
| 716 | reg_val = TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL; | ||
| 717 | if (chip->settings.interrupts_en == 0x20 || | ||
| 718 | chip->settings.interrupts_en == 0x30) | ||
| 719 | reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL; | ||
| 720 | |||
| 721 | reg_val |= chip->settings.interrupts_en; | ||
| 722 | ret = tsl2x7x_write_control_reg(chip, reg_val); | ||
| 723 | if (ret < 0) | ||
| 724 | return ret; | ||
| 725 | |||
| 726 | ret = tsl2x7x_clear_interrupts(chip, | ||
| 727 | TSL2X7X_CMD_PROXALS_INT_CLR); | ||
| 728 | if (ret < 0) | ||
| 729 | return ret; | ||
| 730 | } | ||
| 731 | |||
| 732 | return ret; | ||
| 733 | } | ||
| 734 | |||
| 735 | static int tsl2x7x_chip_off(struct iio_dev *indio_dev) | ||
| 736 | { | ||
| 737 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 738 | |||
| 739 | /* turn device off */ | ||
| 740 | chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; | ||
| 741 | return tsl2x7x_write_control_reg(chip, 0x00); | ||
| 742 | } | ||
| 743 | |||
| 744 | /** | ||
| 745 | * tsl2x7x_invoke_change | ||
| 746 | * @indio_dev: pointer to IIO device | ||
| 747 | * | ||
| 748 | * Obtain and lock both ALS and PROX resources, | ||
| 749 | * determine and save device state (On/Off), | ||
| 750 | * cycle device to implement updated parameter, | ||
| 751 | * put device back into proper state, and unlock | ||
| 752 | * resource. | ||
| 753 | */ | ||
| 754 | static int tsl2x7x_invoke_change(struct iio_dev *indio_dev) | ||
| 755 | { | ||
| 756 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 757 | int device_status = chip->tsl2x7x_chip_status; | ||
| 758 | int ret; | ||
| 759 | |||
| 760 | mutex_lock(&chip->als_mutex); | ||
| 761 | mutex_lock(&chip->prox_mutex); | ||
| 762 | |||
| 763 | if (device_status == TSL2X7X_CHIP_WORKING) { | ||
| 764 | ret = tsl2x7x_chip_off(indio_dev); | ||
| 765 | if (ret < 0) | ||
| 766 | goto unlock; | ||
| 767 | } | ||
| 768 | |||
| 769 | ret = tsl2x7x_chip_on(indio_dev); | ||
| 770 | |||
| 771 | unlock: | ||
| 772 | mutex_unlock(&chip->prox_mutex); | ||
| 773 | mutex_unlock(&chip->als_mutex); | ||
| 774 | |||
| 775 | return ret; | ||
| 776 | } | ||
| 777 | |||
| 778 | static void tsl2x7x_prox_calculate(int *data, int length, | ||
| 779 | struct tsl2x7x_prox_stat *stat) | ||
| 780 | { | ||
| 781 | int i; | ||
| 782 | int sample_sum; | ||
| 783 | int tmp; | ||
| 784 | |||
| 785 | if (!length) | ||
| 786 | length = 1; | ||
| 787 | |||
| 788 | sample_sum = 0; | ||
| 789 | stat->min = INT_MAX; | ||
| 790 | stat->max = INT_MIN; | ||
| 791 | for (i = 0; i < length; i++) { | ||
| 792 | sample_sum += data[i]; | ||
| 793 | stat->min = min(stat->min, data[i]); | ||
| 794 | stat->max = max(stat->max, data[i]); | ||
| 795 | } | ||
| 796 | |||
| 797 | stat->mean = sample_sum / length; | ||
| 798 | sample_sum = 0; | ||
| 799 | for (i = 0; i < length; i++) { | ||
| 800 | tmp = data[i] - stat->mean; | ||
| 801 | sample_sum += tmp * tmp; | ||
| 802 | } | ||
| 803 | stat->stddev = int_sqrt((long)sample_sum / length); | ||
| 804 | } | ||
| 805 | |||
| 806 | /** | ||
| 807 | * tsl2x7x_prox_cal() - Calculates std. and sets thresholds. | ||
| 808 | * @indio_dev: pointer to IIO device | ||
| 809 | * | ||
| 810 | * Calculates a standard deviation based on the samples, | ||
| 811 | * and sets the threshold accordingly. | ||
| 812 | */ | ||
| 813 | static int tsl2x7x_prox_cal(struct iio_dev *indio_dev) | ||
| 814 | { | ||
| 815 | int prox_history[MAX_SAMPLES_CAL + 1]; | ||
| 816 | int i, ret; | ||
| 817 | struct tsl2x7x_prox_stat prox_stat_data[2]; | ||
| 818 | struct tsl2x7x_prox_stat *cal; | ||
| 819 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 820 | u8 tmp_irq_settings; | ||
| 821 | u8 current_state = chip->tsl2x7x_chip_status; | ||
| 822 | |||
| 823 | if (chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) { | ||
| 824 | dev_err(&chip->client->dev, | ||
| 825 | "max prox samples cal is too big: %d\n", | ||
| 826 | chip->settings.prox_max_samples_cal); | ||
| 827 | chip->settings.prox_max_samples_cal = MAX_SAMPLES_CAL; | ||
| 828 | } | ||
| 829 | |||
| 830 | /* have to stop to change settings */ | ||
| 831 | ret = tsl2x7x_chip_off(indio_dev); | ||
| 832 | if (ret < 0) | ||
| 833 | return ret; | ||
| 834 | |||
| 835 | /* Enable proximity detection save just in case prox not wanted yet*/ | ||
| 836 | tmp_irq_settings = chip->settings.interrupts_en; | ||
| 837 | chip->settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL; | ||
| 838 | |||
| 839 | /*turn on device if not already on*/ | ||
| 840 | ret = tsl2x7x_chip_on(indio_dev); | ||
| 841 | if (ret < 0) | ||
| 842 | return ret; | ||
| 843 | |||
| 844 | /*gather the samples*/ | ||
| 845 | for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { | ||
| 846 | usleep_range(15000, 17500); | ||
| 847 | ret = tsl2x7x_get_prox(indio_dev); | ||
| 848 | if (ret < 0) | ||
| 849 | return ret; | ||
| 850 | prox_history[i] = chip->prox_data; | ||
| 851 | dev_info(&chip->client->dev, "2 i=%d prox data= %d\n", | ||
| 852 | i, chip->prox_data); | ||
| 853 | } | ||
| 854 | |||
| 855 | ret = tsl2x7x_chip_off(indio_dev); | ||
| 856 | if (ret < 0) | ||
| 857 | return ret; | ||
| 858 | cal = &prox_stat_data[PROX_STAT_CAL]; | ||
| 859 | tsl2x7x_prox_calculate(prox_history, | ||
| 860 | chip->settings.prox_max_samples_cal, cal); | ||
| 861 | chip->settings.prox_thres_high = (cal->max << 1) - cal->mean; | ||
| 862 | |||
| 863 | dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n", | ||
| 864 | cal->min, cal->mean, cal->max); | ||
| 865 | dev_info(&chip->client->dev, | ||
| 866 | "%s proximity threshold set to %d\n", | ||
| 867 | chip->client->name, chip->settings.prox_thres_high); | ||
| 868 | |||
| 869 | /* back to the way they were */ | ||
| 870 | chip->settings.interrupts_en = tmp_irq_settings; | ||
| 871 | if (current_state == TSL2X7X_CHIP_WORKING) { | ||
| 872 | ret = tsl2x7x_chip_on(indio_dev); | ||
| 873 | if (ret < 0) | ||
| 874 | return ret; | ||
| 875 | } | ||
| 876 | |||
| 877 | return 0; | ||
| 878 | } | ||
| 879 | |||
| 880 | static ssize_t | ||
| 881 | in_illuminance0_calibscale_available_show(struct device *dev, | ||
| 882 | struct device_attribute *attr, | ||
| 883 | char *buf) | ||
| 884 | { | ||
| 885 | struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); | ||
| 886 | |||
| 887 | switch (chip->id) { | ||
| 888 | case tsl2571: | ||
| 889 | case tsl2671: | ||
| 890 | case tmd2671: | ||
| 891 | case tsl2771: | ||
| 892 | case tmd2771: | ||
| 893 | return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128"); | ||
| 894 | } | ||
| 895 | |||
| 896 | return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120"); | ||
| 897 | } | ||
| 898 | |||
| 899 | static IIO_CONST_ATTR(in_proximity0_calibscale_available, "1 2 4 8"); | ||
| 900 | |||
| 901 | static IIO_CONST_ATTR(in_illuminance0_integration_time_available, | ||
| 902 | ".00272 - .696"); | ||
| 903 | |||
| 904 | static ssize_t in_illuminance0_target_input_show(struct device *dev, | ||
| 905 | struct device_attribute *attr, | ||
| 906 | char *buf) | ||
| 907 | { | ||
| 908 | struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); | ||
| 909 | |||
| 910 | return snprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); | ||
| 911 | } | ||
| 912 | |||
| 913 | static ssize_t in_illuminance0_target_input_store(struct device *dev, | ||
| 914 | struct device_attribute *attr, | ||
| 915 | const char *buf, size_t len) | ||
| 916 | { | ||
| 917 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 918 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 919 | unsigned long value; | ||
| 920 | int ret; | ||
| 921 | |||
| 922 | if (kstrtoul(buf, 0, &value)) | ||
| 923 | return -EINVAL; | ||
| 924 | |||
| 925 | if (value) | ||
| 926 | chip->settings.als_cal_target = value; | ||
| 927 | |||
| 928 | ret = tsl2x7x_invoke_change(indio_dev); | ||
| 929 | if (ret < 0) | ||
| 930 | return ret; | ||
| 931 | |||
| 932 | return len; | ||
| 933 | } | ||
| 934 | |||
| 935 | static ssize_t in_illuminance0_calibrate_store(struct device *dev, | ||
| 936 | struct device_attribute *attr, | ||
| 937 | const char *buf, size_t len) | ||
| 938 | { | ||
| 939 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 940 | bool value; | ||
| 941 | int ret; | ||
| 942 | |||
| 943 | if (strtobool(buf, &value)) | ||
| 944 | return -EINVAL; | ||
| 945 | |||
| 946 | if (value) { | ||
| 947 | ret = tsl2x7x_als_calibrate(indio_dev); | ||
| 948 | if (ret < 0) | ||
| 949 | return ret; | ||
| 950 | } | ||
| 951 | |||
| 952 | ret = tsl2x7x_invoke_change(indio_dev); | ||
| 953 | if (ret < 0) | ||
| 954 | return ret; | ||
| 955 | |||
| 956 | return len; | ||
| 957 | } | ||
| 958 | |||
| 959 | static ssize_t in_illuminance0_lux_table_show(struct device *dev, | ||
| 960 | struct device_attribute *attr, | ||
| 961 | char *buf) | ||
| 962 | { | ||
| 963 | struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev)); | ||
| 964 | int i = 0; | ||
| 965 | int offset = 0; | ||
| 966 | |||
| 967 | while (i < TSL2X7X_MAX_LUX_TABLE_SIZE) { | ||
| 968 | offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,%u,", | ||
| 969 | chip->tsl2x7x_device_lux[i].ratio, | ||
| 970 | chip->tsl2x7x_device_lux[i].ch0, | ||
| 971 | chip->tsl2x7x_device_lux[i].ch1); | ||
| 972 | if (chip->tsl2x7x_device_lux[i].ratio == 0) { | ||
| 973 | /* | ||
| 974 | * We just printed the first "0" entry. | ||
| 975 | * Now get rid of the extra "," and break. | ||
| 976 | */ | ||
| 977 | offset--; | ||
| 978 | break; | ||
| 979 | } | ||
| 980 | i++; | ||
| 981 | } | ||
| 982 | |||
| 983 | offset += snprintf(buf + offset, PAGE_SIZE, "\n"); | ||
| 984 | return offset; | ||
| 985 | } | ||
| 986 | |||
| 987 | static ssize_t in_illuminance0_lux_table_store(struct device *dev, | ||
| 988 | struct device_attribute *attr, | ||
| 989 | const char *buf, size_t len) | ||
| 990 | { | ||
| 991 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 992 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 993 | int value[ARRAY_SIZE(chip->tsl2x7x_device_lux) * 3 + 1]; | ||
| 994 | int n, ret; | ||
| 995 | |||
| 996 | get_options(buf, ARRAY_SIZE(value), value); | ||
| 997 | |||
| 998 | /* We now have an array of ints starting at value[1], and | ||
| 999 | * enumerated by value[0]. | ||
| 1000 | * We expect each group of three ints is one table entry, | ||
| 1001 | * and the last table entry is all 0. | ||
| 1002 | */ | ||
| 1003 | n = value[0]; | ||
| 1004 | if ((n % 3) || n < 6 || | ||
| 1005 | n > ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) { | ||
| 1006 | dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n); | ||
| 1007 | return -EINVAL; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) { | ||
| 1011 | dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n); | ||
| 1012 | return -EINVAL; | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { | ||
| 1016 | ret = tsl2x7x_chip_off(indio_dev); | ||
| 1017 | if (ret < 0) | ||
| 1018 | return ret; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | /* Zero out the table */ | ||
| 1022 | memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux)); | ||
| 1023 | memcpy(chip->tsl2x7x_device_lux, &value[1], (value[0] * 4)); | ||
| 1024 | |||
| 1025 | ret = tsl2x7x_invoke_change(indio_dev); | ||
| 1026 | if (ret < 0) | ||
| 1027 | return ret; | ||
| 1028 | |||
| 1029 | return len; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | static ssize_t in_proximity0_calibrate_store(struct device *dev, | ||
| 1033 | struct device_attribute *attr, | ||
| 1034 | const char *buf, size_t len) | ||
| 1035 | { | ||
| 1036 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 1037 | bool value; | ||
| 1038 | int ret; | ||
| 1039 | |||
| 1040 | if (strtobool(buf, &value)) | ||
| 1041 | return -EINVAL; | ||
| 1042 | |||
| 1043 | if (value) { | ||
| 1044 | ret = tsl2x7x_prox_cal(indio_dev); | ||
| 1045 | if (ret < 0) | ||
| 1046 | return ret; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | ret = tsl2x7x_invoke_change(indio_dev); | ||
| 1050 | if (ret < 0) | ||
| 1051 | return ret; | ||
| 1052 | |||
| 1053 | return len; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev, | ||
| 1057 | const struct iio_chan_spec *chan, | ||
| 1058 | enum iio_event_type type, | ||
| 1059 | enum iio_event_direction dir) | ||
| 1060 | { | ||
| 1061 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1062 | int ret; | ||
| 1063 | |||
| 1064 | if (chan->type == IIO_INTENSITY) | ||
| 1065 | ret = !!(chip->settings.interrupts_en & 0x10); | ||
| 1066 | else | ||
| 1067 | ret = !!(chip->settings.interrupts_en & 0x20); | ||
| 1068 | |||
| 1069 | return ret; | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev, | ||
| 1073 | const struct iio_chan_spec *chan, | ||
| 1074 | enum iio_event_type type, | ||
| 1075 | enum iio_event_direction dir, | ||
| 1076 | int val) | ||
| 1077 | { | ||
| 1078 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1079 | int ret; | ||
| 1080 | |||
| 1081 | if (chan->type == IIO_INTENSITY) { | ||
| 1082 | if (val) | ||
| 1083 | chip->settings.interrupts_en |= 0x10; | ||
| 1084 | else | ||
| 1085 | chip->settings.interrupts_en &= 0x20; | ||
| 1086 | } else { | ||
| 1087 | if (val) | ||
| 1088 | chip->settings.interrupts_en |= 0x20; | ||
| 1089 | else | ||
| 1090 | chip->settings.interrupts_en &= 0x10; | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | ret = tsl2x7x_invoke_change(indio_dev); | ||
| 1094 | if (ret < 0) | ||
| 1095 | return ret; | ||
| 1096 | |||
| 1097 | return 0; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | static int tsl2x7x_write_event_value(struct iio_dev *indio_dev, | ||
| 1101 | const struct iio_chan_spec *chan, | ||
| 1102 | enum iio_event_type type, | ||
| 1103 | enum iio_event_direction dir, | ||
| 1104 | enum iio_event_info info, | ||
| 1105 | int val, int val2) | ||
| 1106 | { | ||
| 1107 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1108 | int ret = -EINVAL, y, z, filter_delay; | ||
| 1109 | u8 time; | ||
| 1110 | |||
| 1111 | switch (info) { | ||
| 1112 | case IIO_EV_INFO_VALUE: | ||
| 1113 | if (chan->type == IIO_INTENSITY) { | ||
| 1114 | switch (dir) { | ||
| 1115 | case IIO_EV_DIR_RISING: | ||
| 1116 | chip->settings.als_thresh_high = val; | ||
| 1117 | ret = 0; | ||
| 1118 | break; | ||
| 1119 | case IIO_EV_DIR_FALLING: | ||
| 1120 | chip->settings.als_thresh_low = val; | ||
| 1121 | ret = 0; | ||
| 1122 | break; | ||
| 1123 | default: | ||
| 1124 | break; | ||
| 1125 | } | ||
| 1126 | } else { | ||
| 1127 | switch (dir) { | ||
| 1128 | case IIO_EV_DIR_RISING: | ||
| 1129 | chip->settings.prox_thres_high = val; | ||
| 1130 | ret = 0; | ||
| 1131 | break; | ||
| 1132 | case IIO_EV_DIR_FALLING: | ||
| 1133 | chip->settings.prox_thres_low = val; | ||
| 1134 | ret = 0; | ||
| 1135 | break; | ||
| 1136 | default: | ||
| 1137 | break; | ||
| 1138 | } | ||
| 1139 | } | ||
| 1140 | break; | ||
| 1141 | case IIO_EV_INFO_PERIOD: | ||
| 1142 | if (chan->type == IIO_INTENSITY) | ||
| 1143 | time = chip->settings.als_time; | ||
| 1144 | else | ||
| 1145 | time = chip->settings.prx_time; | ||
| 1146 | |||
| 1147 | y = (TSL2X7X_MAX_TIMER_CNT - time) + 1; | ||
| 1148 | z = y * TSL2X7X_MIN_ITIME; | ||
| 1149 | |||
| 1150 | filter_delay = DIV_ROUND_UP((val * 1000) + val2, z); | ||
| 1151 | |||
| 1152 | if (chan->type == IIO_INTENSITY) { | ||
| 1153 | chip->settings.persistence &= 0xF0; | ||
| 1154 | chip->settings.persistence |= | ||
| 1155 | (filter_delay & 0x0F); | ||
| 1156 | dev_info(&chip->client->dev, "%s: ALS persistence = %d", | ||
| 1157 | __func__, filter_delay); | ||
| 1158 | } else { | ||
| 1159 | chip->settings.persistence &= 0x0F; | ||
| 1160 | chip->settings.persistence |= | ||
| 1161 | ((filter_delay << 4) & 0xF0); | ||
| 1162 | dev_info(&chip->client->dev, | ||
| 1163 | "%s: Proximity persistence = %d", | ||
| 1164 | __func__, filter_delay); | ||
| 1165 | } | ||
| 1166 | ret = 0; | ||
| 1167 | break; | ||
| 1168 | default: | ||
| 1169 | break; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | if (ret < 0) | ||
| 1173 | return ret; | ||
| 1174 | |||
| 1175 | return tsl2x7x_invoke_change(indio_dev); | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | static int tsl2x7x_read_event_value(struct iio_dev *indio_dev, | ||
| 1179 | const struct iio_chan_spec *chan, | ||
| 1180 | enum iio_event_type type, | ||
| 1181 | enum iio_event_direction dir, | ||
| 1182 | enum iio_event_info info, | ||
| 1183 | int *val, int *val2) | ||
| 1184 | { | ||
| 1185 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1186 | int ret = -EINVAL, filter_delay, mult; | ||
| 1187 | u8 time; | ||
| 1188 | |||
| 1189 | switch (info) { | ||
| 1190 | case IIO_EV_INFO_VALUE: | ||
| 1191 | if (chan->type == IIO_INTENSITY) { | ||
| 1192 | switch (dir) { | ||
| 1193 | case IIO_EV_DIR_RISING: | ||
| 1194 | *val = chip->settings.als_thresh_high; | ||
| 1195 | ret = IIO_VAL_INT; | ||
| 1196 | break; | ||
| 1197 | case IIO_EV_DIR_FALLING: | ||
| 1198 | *val = chip->settings.als_thresh_low; | ||
| 1199 | ret = IIO_VAL_INT; | ||
| 1200 | break; | ||
| 1201 | default: | ||
| 1202 | break; | ||
| 1203 | } | ||
| 1204 | } else { | ||
| 1205 | switch (dir) { | ||
| 1206 | case IIO_EV_DIR_RISING: | ||
| 1207 | *val = chip->settings.prox_thres_high; | ||
| 1208 | ret = IIO_VAL_INT; | ||
| 1209 | break; | ||
| 1210 | case IIO_EV_DIR_FALLING: | ||
| 1211 | *val = chip->settings.prox_thres_low; | ||
| 1212 | ret = IIO_VAL_INT; | ||
| 1213 | break; | ||
| 1214 | default: | ||
| 1215 | break; | ||
| 1216 | } | ||
| 1217 | } | ||
| 1218 | break; | ||
| 1219 | case IIO_EV_INFO_PERIOD: | ||
| 1220 | if (chan->type == IIO_INTENSITY) { | ||
| 1221 | time = chip->settings.als_time; | ||
| 1222 | mult = chip->settings.persistence & 0x0F; | ||
| 1223 | } else { | ||
| 1224 | time = chip->settings.prx_time; | ||
| 1225 | mult = (chip->settings.persistence & 0xF0) >> 4; | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | /* Determine integration time */ | ||
| 1229 | *val = (TSL2X7X_MAX_TIMER_CNT - time) + 1; | ||
| 1230 | *val2 = *val * TSL2X7X_MIN_ITIME; | ||
| 1231 | filter_delay = *val2 * mult; | ||
| 1232 | *val = filter_delay / 1000; | ||
| 1233 | *val2 = filter_delay % 1000; | ||
| 1234 | ret = IIO_VAL_INT_PLUS_MICRO; | ||
| 1235 | break; | ||
| 1236 | default: | ||
| 1237 | break; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | return ret; | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | static int tsl2x7x_read_raw(struct iio_dev *indio_dev, | ||
| 1244 | struct iio_chan_spec const *chan, | ||
| 1245 | int *val, | ||
| 1246 | int *val2, | ||
| 1247 | long mask) | ||
| 1248 | { | ||
| 1249 | int ret = -EINVAL; | ||
| 1250 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1251 | |||
| 1252 | switch (mask) { | ||
| 1253 | case IIO_CHAN_INFO_PROCESSED: | ||
| 1254 | switch (chan->type) { | ||
| 1255 | case IIO_LIGHT: | ||
| 1256 | tsl2x7x_get_lux(indio_dev); | ||
| 1257 | *val = chip->als_cur_info.lux; | ||
| 1258 | ret = IIO_VAL_INT; | ||
| 1259 | break; | ||
| 1260 | default: | ||
| 1261 | return -EINVAL; | ||
| 1262 | } | ||
| 1263 | break; | ||
| 1264 | case IIO_CHAN_INFO_RAW: | ||
| 1265 | switch (chan->type) { | ||
| 1266 | case IIO_INTENSITY: | ||
| 1267 | tsl2x7x_get_lux(indio_dev); | ||
| 1268 | if (chan->channel == 0) | ||
| 1269 | *val = chip->als_cur_info.als_ch0; | ||
| 1270 | else | ||
| 1271 | *val = chip->als_cur_info.als_ch1; | ||
| 1272 | ret = IIO_VAL_INT; | ||
| 1273 | break; | ||
| 1274 | case IIO_PROXIMITY: | ||
| 1275 | tsl2x7x_get_prox(indio_dev); | ||
| 1276 | *val = chip->prox_data; | ||
| 1277 | ret = IIO_VAL_INT; | ||
| 1278 | break; | ||
| 1279 | default: | ||
| 1280 | return -EINVAL; | ||
| 1281 | } | ||
| 1282 | break; | ||
| 1283 | case IIO_CHAN_INFO_CALIBSCALE: | ||
| 1284 | if (chan->type == IIO_LIGHT) | ||
| 1285 | *val = tsl2x7x_als_gain[chip->settings.als_gain]; | ||
| 1286 | else | ||
| 1287 | *val = tsl2x7x_prx_gain[chip->settings.prox_gain]; | ||
| 1288 | ret = IIO_VAL_INT; | ||
| 1289 | break; | ||
| 1290 | case IIO_CHAN_INFO_CALIBBIAS: | ||
| 1291 | *val = chip->settings.als_gain_trim; | ||
| 1292 | ret = IIO_VAL_INT; | ||
| 1293 | break; | ||
| 1294 | case IIO_CHAN_INFO_INT_TIME: | ||
| 1295 | *val = (TSL2X7X_MAX_TIMER_CNT - chip->settings.als_time) + 1; | ||
| 1296 | *val2 = ((*val * TSL2X7X_MIN_ITIME) % 1000) / 1000; | ||
| 1297 | ret = IIO_VAL_INT_PLUS_MICRO; | ||
| 1298 | break; | ||
| 1299 | default: | ||
| 1300 | ret = -EINVAL; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | return ret; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | static int tsl2x7x_write_raw(struct iio_dev *indio_dev, | ||
| 1307 | struct iio_chan_spec const *chan, | ||
| 1308 | int val, | ||
| 1309 | int val2, | ||
| 1310 | long mask) | ||
| 1311 | { | ||
| 1312 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1313 | |||
| 1314 | switch (mask) { | ||
| 1315 | case IIO_CHAN_INFO_CALIBSCALE: | ||
| 1316 | if (chan->type == IIO_INTENSITY) { | ||
| 1317 | switch (val) { | ||
| 1318 | case 1: | ||
| 1319 | chip->settings.als_gain = 0; | ||
| 1320 | break; | ||
| 1321 | case 8: | ||
| 1322 | chip->settings.als_gain = 1; | ||
| 1323 | break; | ||
| 1324 | case 16: | ||
| 1325 | chip->settings.als_gain = 2; | ||
| 1326 | break; | ||
| 1327 | case 120: | ||
| 1328 | switch (chip->id) { | ||
| 1329 | case tsl2572: | ||
| 1330 | case tsl2672: | ||
| 1331 | case tmd2672: | ||
| 1332 | case tsl2772: | ||
| 1333 | case tmd2772: | ||
| 1334 | return -EINVAL; | ||
| 1335 | } | ||
| 1336 | chip->settings.als_gain = 3; | ||
| 1337 | break; | ||
| 1338 | case 128: | ||
| 1339 | switch (chip->id) { | ||
| 1340 | case tsl2571: | ||
| 1341 | case tsl2671: | ||
| 1342 | case tmd2671: | ||
| 1343 | case tsl2771: | ||
| 1344 | case tmd2771: | ||
| 1345 | return -EINVAL; | ||
| 1346 | } | ||
| 1347 | chip->settings.als_gain = 3; | ||
| 1348 | break; | ||
| 1349 | default: | ||
| 1350 | return -EINVAL; | ||
| 1351 | } | ||
| 1352 | } else { | ||
| 1353 | switch (val) { | ||
| 1354 | case 1: | ||
| 1355 | chip->settings.prox_gain = 0; | ||
| 1356 | break; | ||
| 1357 | case 2: | ||
| 1358 | chip->settings.prox_gain = 1; | ||
| 1359 | break; | ||
| 1360 | case 4: | ||
| 1361 | chip->settings.prox_gain = 2; | ||
| 1362 | break; | ||
| 1363 | case 8: | ||
| 1364 | chip->settings.prox_gain = 3; | ||
| 1365 | break; | ||
| 1366 | default: | ||
| 1367 | return -EINVAL; | ||
| 1368 | } | ||
| 1369 | } | ||
| 1370 | break; | ||
| 1371 | case IIO_CHAN_INFO_CALIBBIAS: | ||
| 1372 | chip->settings.als_gain_trim = val; | ||
| 1373 | break; | ||
| 1374 | case IIO_CHAN_INFO_INT_TIME: | ||
| 1375 | chip->settings.als_time = | ||
| 1376 | TSL2X7X_MAX_TIMER_CNT - (val2 / TSL2X7X_MIN_ITIME); | ||
| 1377 | |||
| 1378 | dev_info(&chip->client->dev, "%s: als time = %d", | ||
| 1379 | __func__, chip->settings.als_time); | ||
| 1380 | break; | ||
| 1381 | default: | ||
| 1382 | return -EINVAL; | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | return tsl2x7x_invoke_change(indio_dev); | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | static DEVICE_ATTR_RO(in_illuminance0_calibscale_available); | ||
| 1389 | |||
| 1390 | static DEVICE_ATTR_RW(in_illuminance0_target_input); | ||
| 1391 | |||
| 1392 | static DEVICE_ATTR_WO(in_illuminance0_calibrate); | ||
| 1393 | |||
| 1394 | static DEVICE_ATTR_WO(in_proximity0_calibrate); | ||
| 1395 | |||
| 1396 | static DEVICE_ATTR_RW(in_illuminance0_lux_table); | ||
| 1397 | |||
| 1398 | /* Use the default register values to identify the Taos device */ | ||
| 1399 | static int tsl2x7x_device_id(int *id, int target) | ||
| 1400 | { | ||
| 1401 | switch (target) { | ||
| 1402 | case tsl2571: | ||
| 1403 | case tsl2671: | ||
| 1404 | case tsl2771: | ||
| 1405 | return (*id & 0xf0) == TRITON_ID; | ||
| 1406 | case tmd2671: | ||
| 1407 | case tmd2771: | ||
| 1408 | return (*id & 0xf0) == HALIBUT_ID; | ||
| 1409 | case tsl2572: | ||
| 1410 | case tsl2672: | ||
| 1411 | case tmd2672: | ||
| 1412 | case tsl2772: | ||
| 1413 | case tmd2772: | ||
| 1414 | return (*id & 0xf0) == SWORDFISH_ID; | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | return -EINVAL; | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | static irqreturn_t tsl2x7x_event_handler(int irq, void *private) | ||
| 1421 | { | ||
| 1422 | struct iio_dev *indio_dev = private; | ||
| 1423 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1424 | s64 timestamp = iio_get_time_ns(indio_dev); | ||
| 1425 | int ret; | ||
| 1426 | |||
| 1427 | ret = tsl2x7x_read_status(chip); | ||
| 1428 | if (ret < 0) | ||
| 1429 | return ret; | ||
| 1430 | |||
| 1431 | /* What type of interrupt do we need to process */ | ||
| 1432 | if (ret & TSL2X7X_STA_PRX_INTR) { | ||
| 1433 | tsl2x7x_get_prox(indio_dev); /* freshen data for ABI */ | ||
| 1434 | iio_push_event(indio_dev, | ||
| 1435 | IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, | ||
| 1436 | 0, | ||
| 1437 | IIO_EV_TYPE_THRESH, | ||
| 1438 | IIO_EV_DIR_EITHER), | ||
| 1439 | timestamp); | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | if (ret & TSL2X7X_STA_ALS_INTR) { | ||
| 1443 | tsl2x7x_get_lux(indio_dev); /* freshen data for ABI */ | ||
| 1444 | iio_push_event(indio_dev, | ||
| 1445 | IIO_UNMOD_EVENT_CODE(IIO_LIGHT, | ||
| 1446 | 0, | ||
| 1447 | IIO_EV_TYPE_THRESH, | ||
| 1448 | IIO_EV_DIR_EITHER), | ||
| 1449 | timestamp); | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | ret = tsl2x7x_clear_interrupts(chip, TSL2X7X_CMD_PROXALS_INT_CLR); | ||
| 1453 | if (ret < 0) | ||
| 1454 | return ret; | ||
| 1455 | |||
| 1456 | return IRQ_HANDLED; | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | static struct attribute *tsl2x7x_ALS_device_attrs[] = { | ||
| 1460 | &dev_attr_in_illuminance0_calibscale_available.attr, | ||
| 1461 | &iio_const_attr_in_illuminance0_integration_time_available | ||
| 1462 | .dev_attr.attr, | ||
| 1463 | &dev_attr_in_illuminance0_target_input.attr, | ||
| 1464 | &dev_attr_in_illuminance0_calibrate.attr, | ||
| 1465 | &dev_attr_in_illuminance0_lux_table.attr, | ||
| 1466 | NULL | ||
| 1467 | }; | ||
| 1468 | |||
| 1469 | static struct attribute *tsl2x7x_PRX_device_attrs[] = { | ||
| 1470 | &dev_attr_in_proximity0_calibrate.attr, | ||
| 1471 | NULL | ||
| 1472 | }; | ||
| 1473 | |||
| 1474 | static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = { | ||
| 1475 | &dev_attr_in_illuminance0_calibscale_available.attr, | ||
| 1476 | &iio_const_attr_in_illuminance0_integration_time_available | ||
| 1477 | .dev_attr.attr, | ||
| 1478 | &dev_attr_in_illuminance0_target_input.attr, | ||
| 1479 | &dev_attr_in_illuminance0_calibrate.attr, | ||
| 1480 | &dev_attr_in_illuminance0_lux_table.attr, | ||
| 1481 | NULL | ||
| 1482 | }; | ||
| 1483 | |||
| 1484 | static struct attribute *tsl2x7x_PRX2_device_attrs[] = { | ||
| 1485 | &dev_attr_in_proximity0_calibrate.attr, | ||
| 1486 | &iio_const_attr_in_proximity0_calibscale_available.dev_attr.attr, | ||
| 1487 | NULL | ||
| 1488 | }; | ||
| 1489 | |||
| 1490 | static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = { | ||
| 1491 | &dev_attr_in_illuminance0_calibscale_available.attr, | ||
| 1492 | &iio_const_attr_in_illuminance0_integration_time_available | ||
| 1493 | .dev_attr.attr, | ||
| 1494 | &dev_attr_in_illuminance0_target_input.attr, | ||
| 1495 | &dev_attr_in_illuminance0_calibrate.attr, | ||
| 1496 | &dev_attr_in_illuminance0_lux_table.attr, | ||
| 1497 | &dev_attr_in_proximity0_calibrate.attr, | ||
| 1498 | &iio_const_attr_in_proximity0_calibscale_available.dev_attr.attr, | ||
| 1499 | NULL | ||
| 1500 | }; | ||
| 1501 | |||
| 1502 | static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = { | ||
| 1503 | [ALS] = { | ||
| 1504 | .attrs = tsl2x7x_ALS_device_attrs, | ||
| 1505 | }, | ||
| 1506 | [PRX] = { | ||
| 1507 | .attrs = tsl2x7x_PRX_device_attrs, | ||
| 1508 | }, | ||
| 1509 | [ALSPRX] = { | ||
| 1510 | .attrs = tsl2x7x_ALSPRX_device_attrs, | ||
| 1511 | }, | ||
| 1512 | [PRX2] = { | ||
| 1513 | .attrs = tsl2x7x_PRX2_device_attrs, | ||
| 1514 | }, | ||
| 1515 | [ALSPRX2] = { | ||
| 1516 | .attrs = tsl2x7x_ALSPRX2_device_attrs, | ||
| 1517 | }, | ||
| 1518 | }; | ||
| 1519 | |||
| 1520 | static const struct iio_info tsl2X7X_device_info[] = { | ||
| 1521 | [ALS] = { | ||
| 1522 | .attrs = &tsl2X7X_device_attr_group_tbl[ALS], | ||
| 1523 | .read_raw = &tsl2x7x_read_raw, | ||
| 1524 | .write_raw = &tsl2x7x_write_raw, | ||
| 1525 | .read_event_value = &tsl2x7x_read_event_value, | ||
| 1526 | .write_event_value = &tsl2x7x_write_event_value, | ||
| 1527 | .read_event_config = &tsl2x7x_read_interrupt_config, | ||
| 1528 | .write_event_config = &tsl2x7x_write_interrupt_config, | ||
| 1529 | }, | ||
| 1530 | [PRX] = { | ||
| 1531 | .attrs = &tsl2X7X_device_attr_group_tbl[PRX], | ||
| 1532 | .read_raw = &tsl2x7x_read_raw, | ||
| 1533 | .write_raw = &tsl2x7x_write_raw, | ||
| 1534 | .read_event_value = &tsl2x7x_read_event_value, | ||
| 1535 | .write_event_value = &tsl2x7x_write_event_value, | ||
| 1536 | .read_event_config = &tsl2x7x_read_interrupt_config, | ||
| 1537 | .write_event_config = &tsl2x7x_write_interrupt_config, | ||
| 1538 | }, | ||
| 1539 | [ALSPRX] = { | ||
| 1540 | .attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX], | ||
| 1541 | .read_raw = &tsl2x7x_read_raw, | ||
| 1542 | .write_raw = &tsl2x7x_write_raw, | ||
| 1543 | .read_event_value = &tsl2x7x_read_event_value, | ||
| 1544 | .write_event_value = &tsl2x7x_write_event_value, | ||
| 1545 | .read_event_config = &tsl2x7x_read_interrupt_config, | ||
| 1546 | .write_event_config = &tsl2x7x_write_interrupt_config, | ||
| 1547 | }, | ||
| 1548 | [PRX2] = { | ||
| 1549 | .attrs = &tsl2X7X_device_attr_group_tbl[PRX2], | ||
| 1550 | .read_raw = &tsl2x7x_read_raw, | ||
| 1551 | .write_raw = &tsl2x7x_write_raw, | ||
| 1552 | .read_event_value = &tsl2x7x_read_event_value, | ||
| 1553 | .write_event_value = &tsl2x7x_write_event_value, | ||
| 1554 | .read_event_config = &tsl2x7x_read_interrupt_config, | ||
| 1555 | .write_event_config = &tsl2x7x_write_interrupt_config, | ||
| 1556 | }, | ||
| 1557 | [ALSPRX2] = { | ||
| 1558 | .attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX2], | ||
| 1559 | .read_raw = &tsl2x7x_read_raw, | ||
| 1560 | .write_raw = &tsl2x7x_write_raw, | ||
| 1561 | .read_event_value = &tsl2x7x_read_event_value, | ||
| 1562 | .write_event_value = &tsl2x7x_write_event_value, | ||
| 1563 | .read_event_config = &tsl2x7x_read_interrupt_config, | ||
| 1564 | .write_event_config = &tsl2x7x_write_interrupt_config, | ||
| 1565 | }, | ||
| 1566 | }; | ||
| 1567 | |||
| 1568 | static const struct iio_event_spec tsl2x7x_events[] = { | ||
| 1569 | { | ||
| 1570 | .type = IIO_EV_TYPE_THRESH, | ||
| 1571 | .dir = IIO_EV_DIR_RISING, | ||
| 1572 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
| 1573 | BIT(IIO_EV_INFO_ENABLE), | ||
| 1574 | }, { | ||
| 1575 | .type = IIO_EV_TYPE_THRESH, | ||
| 1576 | .dir = IIO_EV_DIR_FALLING, | ||
| 1577 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | | ||
| 1578 | BIT(IIO_EV_INFO_ENABLE), | ||
| 1579 | }, { | ||
| 1580 | .type = IIO_EV_TYPE_THRESH, | ||
| 1581 | .dir = IIO_EV_DIR_EITHER, | ||
| 1582 | .mask_separate = BIT(IIO_EV_INFO_PERIOD), | ||
| 1583 | }, | ||
| 1584 | }; | ||
| 1585 | |||
| 1586 | static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = { | ||
| 1587 | [ALS] = { | ||
| 1588 | .channel = { | ||
| 1589 | { | ||
| 1590 | .type = IIO_LIGHT, | ||
| 1591 | .indexed = 1, | ||
| 1592 | .channel = 0, | ||
| 1593 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | | ||
| 1594 | BIT(IIO_CHAN_INFO_INT_TIME), | ||
| 1595 | }, { | ||
| 1596 | .type = IIO_INTENSITY, | ||
| 1597 | .indexed = 1, | ||
| 1598 | .channel = 0, | ||
| 1599 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1600 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1601 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1602 | .event_spec = tsl2x7x_events, | ||
| 1603 | .num_event_specs = ARRAY_SIZE(tsl2x7x_events), | ||
| 1604 | }, { | ||
| 1605 | .type = IIO_INTENSITY, | ||
| 1606 | .indexed = 1, | ||
| 1607 | .channel = 1, | ||
| 1608 | }, | ||
| 1609 | }, | ||
| 1610 | .chan_table_elements = 3, | ||
| 1611 | .info = &tsl2X7X_device_info[ALS], | ||
| 1612 | }, | ||
| 1613 | [PRX] = { | ||
| 1614 | .channel = { | ||
| 1615 | { | ||
| 1616 | .type = IIO_PROXIMITY, | ||
| 1617 | .indexed = 1, | ||
| 1618 | .channel = 0, | ||
| 1619 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1620 | .event_spec = tsl2x7x_events, | ||
| 1621 | .num_event_specs = ARRAY_SIZE(tsl2x7x_events), | ||
| 1622 | }, | ||
| 1623 | }, | ||
| 1624 | .chan_table_elements = 1, | ||
| 1625 | .info = &tsl2X7X_device_info[PRX], | ||
| 1626 | }, | ||
| 1627 | [ALSPRX] = { | ||
| 1628 | .channel = { | ||
| 1629 | { | ||
| 1630 | .type = IIO_LIGHT, | ||
| 1631 | .indexed = 1, | ||
| 1632 | .channel = 0, | ||
| 1633 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | | ||
| 1634 | BIT(IIO_CHAN_INFO_INT_TIME), | ||
| 1635 | }, { | ||
| 1636 | .type = IIO_INTENSITY, | ||
| 1637 | .indexed = 1, | ||
| 1638 | .channel = 0, | ||
| 1639 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1640 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1641 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1642 | .event_spec = tsl2x7x_events, | ||
| 1643 | .num_event_specs = ARRAY_SIZE(tsl2x7x_events), | ||
| 1644 | }, { | ||
| 1645 | .type = IIO_INTENSITY, | ||
| 1646 | .indexed = 1, | ||
| 1647 | .channel = 1, | ||
| 1648 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1649 | }, { | ||
| 1650 | .type = IIO_PROXIMITY, | ||
| 1651 | .indexed = 1, | ||
| 1652 | .channel = 0, | ||
| 1653 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1654 | .event_spec = tsl2x7x_events, | ||
| 1655 | .num_event_specs = ARRAY_SIZE(tsl2x7x_events), | ||
| 1656 | }, | ||
| 1657 | }, | ||
| 1658 | .chan_table_elements = 4, | ||
| 1659 | .info = &tsl2X7X_device_info[ALSPRX], | ||
| 1660 | }, | ||
| 1661 | [PRX2] = { | ||
| 1662 | .channel = { | ||
| 1663 | { | ||
| 1664 | .type = IIO_PROXIMITY, | ||
| 1665 | .indexed = 1, | ||
| 1666 | .channel = 0, | ||
| 1667 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1668 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1669 | .event_spec = tsl2x7x_events, | ||
| 1670 | .num_event_specs = ARRAY_SIZE(tsl2x7x_events), | ||
| 1671 | }, | ||
| 1672 | }, | ||
| 1673 | .chan_table_elements = 1, | ||
| 1674 | .info = &tsl2X7X_device_info[PRX2], | ||
| 1675 | }, | ||
| 1676 | [ALSPRX2] = { | ||
| 1677 | .channel = { | ||
| 1678 | { | ||
| 1679 | .type = IIO_LIGHT, | ||
| 1680 | .indexed = 1, | ||
| 1681 | .channel = 0, | ||
| 1682 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | | ||
| 1683 | BIT(IIO_CHAN_INFO_INT_TIME), | ||
| 1684 | }, { | ||
| 1685 | .type = IIO_INTENSITY, | ||
| 1686 | .indexed = 1, | ||
| 1687 | .channel = 0, | ||
| 1688 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1689 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
| 1690 | BIT(IIO_CHAN_INFO_CALIBBIAS), | ||
| 1691 | .event_spec = tsl2x7x_events, | ||
| 1692 | .num_event_specs = ARRAY_SIZE(tsl2x7x_events), | ||
| 1693 | }, { | ||
| 1694 | .type = IIO_INTENSITY, | ||
| 1695 | .indexed = 1, | ||
| 1696 | .channel = 1, | ||
| 1697 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
| 1698 | }, { | ||
| 1699 | .type = IIO_PROXIMITY, | ||
| 1700 | .indexed = 1, | ||
| 1701 | .channel = 0, | ||
| 1702 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | ||
| 1703 | BIT(IIO_CHAN_INFO_CALIBSCALE), | ||
| 1704 | .event_spec = tsl2x7x_events, | ||
| 1705 | .num_event_specs = ARRAY_SIZE(tsl2x7x_events), | ||
| 1706 | }, | ||
| 1707 | }, | ||
| 1708 | .chan_table_elements = 4, | ||
| 1709 | .info = &tsl2X7X_device_info[ALSPRX2], | ||
| 1710 | }, | ||
| 1711 | }; | ||
| 1712 | |||
| 1713 | static int tsl2x7x_probe(struct i2c_client *clientp, | ||
| 1714 | const struct i2c_device_id *id) | ||
| 1715 | { | ||
| 1716 | int ret; | ||
| 1717 | struct iio_dev *indio_dev; | ||
| 1718 | struct tsl2X7X_chip *chip; | ||
| 1719 | |||
| 1720 | indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); | ||
| 1721 | if (!indio_dev) | ||
| 1722 | return -ENOMEM; | ||
| 1723 | |||
| 1724 | chip = iio_priv(indio_dev); | ||
| 1725 | chip->client = clientp; | ||
| 1726 | i2c_set_clientdata(clientp, indio_dev); | ||
| 1727 | |||
| 1728 | ret = i2c_smbus_read_byte_data(chip->client, | ||
| 1729 | TSL2X7X_CMD_REG | TSL2X7X_CHIPID); | ||
| 1730 | if (ret < 0) | ||
| 1731 | return ret; | ||
| 1732 | |||
| 1733 | if ((!tsl2x7x_device_id(&ret, id->driver_data)) || | ||
| 1734 | (tsl2x7x_device_id(&ret, id->driver_data) == -EINVAL)) { | ||
| 1735 | dev_info(&chip->client->dev, | ||
| 1736 | "%s: i2c device found does not match expected id\n", | ||
| 1737 | __func__); | ||
| 1738 | return -EINVAL; | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | ret = i2c_smbus_write_byte(clientp, TSL2X7X_CMD_REG | TSL2X7X_CNTRL); | ||
| 1742 | if (ret < 0) { | ||
| 1743 | dev_err(&clientp->dev, "write to cmd reg failed. err = %d\n", | ||
| 1744 | ret); | ||
| 1745 | return ret; | ||
| 1746 | } | ||
| 1747 | |||
| 1748 | /* | ||
| 1749 | * ALS and PROX functions can be invoked via user space poll | ||
| 1750 | * or H/W interrupt. If busy return last sample. | ||
| 1751 | */ | ||
| 1752 | mutex_init(&chip->als_mutex); | ||
| 1753 | mutex_init(&chip->prox_mutex); | ||
| 1754 | |||
| 1755 | chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN; | ||
| 1756 | chip->pdata = dev_get_platdata(&clientp->dev); | ||
| 1757 | chip->id = id->driver_data; | ||
| 1758 | chip->chip_info = | ||
| 1759 | &tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]]; | ||
| 1760 | |||
| 1761 | indio_dev->info = chip->chip_info->info; | ||
| 1762 | indio_dev->dev.parent = &clientp->dev; | ||
| 1763 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 1764 | indio_dev->name = chip->client->name; | ||
| 1765 | indio_dev->channels = chip->chip_info->channel; | ||
| 1766 | indio_dev->num_channels = chip->chip_info->chan_table_elements; | ||
| 1767 | |||
| 1768 | if (clientp->irq) { | ||
| 1769 | ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, | ||
| 1770 | NULL, | ||
| 1771 | &tsl2x7x_event_handler, | ||
| 1772 | IRQF_TRIGGER_RISING | | ||
| 1773 | IRQF_ONESHOT, | ||
| 1774 | "TSL2X7X_event", | ||
| 1775 | indio_dev); | ||
| 1776 | if (ret) { | ||
| 1777 | dev_err(&clientp->dev, | ||
| 1778 | "%s: irq request failed", __func__); | ||
| 1779 | return ret; | ||
| 1780 | } | ||
| 1781 | } | ||
| 1782 | |||
| 1783 | /* Load up the defaults */ | ||
| 1784 | tsl2x7x_defaults(chip); | ||
| 1785 | /* Make sure the chip is on */ | ||
| 1786 | tsl2x7x_chip_on(indio_dev); | ||
| 1787 | |||
| 1788 | ret = iio_device_register(indio_dev); | ||
| 1789 | if (ret) { | ||
| 1790 | dev_err(&clientp->dev, | ||
| 1791 | "%s: iio registration failed\n", __func__); | ||
| 1792 | return ret; | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | dev_info(&clientp->dev, "%s Light sensor found.\n", id->name); | ||
| 1796 | |||
| 1797 | return 0; | ||
| 1798 | } | ||
| 1799 | |||
| 1800 | static int tsl2x7x_suspend(struct device *dev) | ||
| 1801 | { | ||
| 1802 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 1803 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1804 | int ret = 0; | ||
| 1805 | |||
| 1806 | if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { | ||
| 1807 | ret = tsl2x7x_chip_off(indio_dev); | ||
| 1808 | chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | return ret; | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | static int tsl2x7x_resume(struct device *dev) | ||
| 1815 | { | ||
| 1816 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
| 1817 | struct tsl2X7X_chip *chip = iio_priv(indio_dev); | ||
| 1818 | int ret = 0; | ||
| 1819 | |||
| 1820 | if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED) | ||
| 1821 | ret = tsl2x7x_chip_on(indio_dev); | ||
| 1822 | |||
| 1823 | return ret; | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | static int tsl2x7x_remove(struct i2c_client *client) | ||
| 1827 | { | ||
| 1828 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
| 1829 | |||
| 1830 | tsl2x7x_chip_off(indio_dev); | ||
| 1831 | |||
| 1832 | iio_device_unregister(indio_dev); | ||
| 1833 | |||
| 1834 | return 0; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | static const struct i2c_device_id tsl2x7x_idtable[] = { | ||
| 1838 | { "tsl2571", tsl2571 }, | ||
| 1839 | { "tsl2671", tsl2671 }, | ||
| 1840 | { "tmd2671", tmd2671 }, | ||
| 1841 | { "tsl2771", tsl2771 }, | ||
| 1842 | { "tmd2771", tmd2771 }, | ||
| 1843 | { "tsl2572", tsl2572 }, | ||
| 1844 | { "tsl2672", tsl2672 }, | ||
| 1845 | { "tmd2672", tmd2672 }, | ||
| 1846 | { "tsl2772", tsl2772 }, | ||
| 1847 | { "tmd2772", tmd2772 }, | ||
| 1848 | {} | ||
| 1849 | }; | ||
| 1850 | |||
| 1851 | MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable); | ||
| 1852 | |||
| 1853 | static const struct of_device_id tsl2x7x_of_match[] = { | ||
| 1854 | { .compatible = "amstaos,tsl2571" }, | ||
| 1855 | { .compatible = "amstaos,tsl2671" }, | ||
| 1856 | { .compatible = "amstaos,tmd2671" }, | ||
| 1857 | { .compatible = "amstaos,tsl2771" }, | ||
| 1858 | { .compatible = "amstaos,tmd2771" }, | ||
| 1859 | { .compatible = "amstaos,tsl2572" }, | ||
| 1860 | { .compatible = "amstaos,tsl2672" }, | ||
| 1861 | { .compatible = "amstaos,tmd2672" }, | ||
| 1862 | { .compatible = "amstaos,tsl2772" }, | ||
| 1863 | { .compatible = "amstaos,tmd2772" }, | ||
| 1864 | {} | ||
| 1865 | }; | ||
| 1866 | MODULE_DEVICE_TABLE(of, tsl2x7x_of_match); | ||
| 1867 | |||
| 1868 | static const struct dev_pm_ops tsl2x7x_pm_ops = { | ||
| 1869 | .suspend = tsl2x7x_suspend, | ||
| 1870 | .resume = tsl2x7x_resume, | ||
| 1871 | }; | ||
| 1872 | |||
| 1873 | /* Driver definition */ | ||
| 1874 | static struct i2c_driver tsl2x7x_driver = { | ||
| 1875 | .driver = { | ||
| 1876 | .name = "tsl2x7x", | ||
| 1877 | .of_match_table = tsl2x7x_of_match, | ||
| 1878 | .pm = &tsl2x7x_pm_ops, | ||
| 1879 | }, | ||
| 1880 | .id_table = tsl2x7x_idtable, | ||
| 1881 | .probe = tsl2x7x_probe, | ||
| 1882 | .remove = tsl2x7x_remove, | ||
| 1883 | }; | ||
| 1884 | |||
| 1885 | module_i2c_driver(tsl2x7x_driver); | ||
| 1886 | |||
| 1887 | MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>"); | ||
| 1888 | MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver"); | ||
| 1889 | MODULE_LICENSE("GPL"); | ||
| @@ -1,99 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Device driver for monitoring ambient light intensity (lux) | ||
| 3 | * and proximity (prox) within the TAOS TSL2X7X family of devices. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2012, TAOS Corporation. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 15 | * more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License along | ||
| 18 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifndef __TSL2X7X_H | ||
| 23 | #define __TSL2X7X_H | ||
| 24 | |||
| 25 | struct tsl2x7x_lux { | ||
| 26 | unsigned int ratio; | ||
| 27 | unsigned int ch0; | ||
| 28 | unsigned int ch1; | ||
| 29 | }; | ||
| 30 | |||
| 31 | /* Max number of segments allowable in LUX table */ | ||
| 32 | #define TSL2X7X_MAX_LUX_TABLE_SIZE 9 | ||
| 33 | /* The default LUX tables all have 3 elements. */ | ||
| 34 | #define TSL2X7X_DEF_LUX_TABLE_SZ 3 | ||
| 35 | #define TSL2X7X_DEFAULT_TABLE_BYTES (sizeof(struct tsl2x7x_lux) * \ | ||
| 36 | TSL2X7X_DEF_LUX_TABLE_SZ) | ||
| 37 | |||
| 38 | /** | ||
| 39 | * struct tsl2x7x_default_settings - power on defaults unless | ||
| 40 | * overridden by platform data. | ||
| 41 | * @als_time: ALS Integration time - multiple of 50mS | ||
| 42 | * @als_gain: Index into the ALS gain table. | ||
| 43 | * @als_gain_trim: default gain trim to account for | ||
| 44 | * aperture effects. | ||
| 45 | * @wait_time: Time between PRX and ALS cycles | ||
| 46 | * in 2.7 periods | ||
| 47 | * @prx_time: 5.2ms prox integration time - | ||
| 48 | * decrease in 2.7ms periods | ||
| 49 | * @prx_gain: Proximity gain index | ||
| 50 | * @prox_config: Prox configuration filters. | ||
| 51 | * @als_cal_target: Known external ALS reading for | ||
| 52 | * calibration. | ||
| 53 | * @interrupts_en: Enable/Disable - 0x00 = none, 0x10 = als, | ||
| 54 | * 0x20 = prx, 0x30 = bth | ||
| 55 | * @persistence: H/W Filters, Number of 'out of limits' | ||
| 56 | * ADC readings PRX/ALS. | ||
| 57 | * @als_thresh_low: CH0 'low' count to trigger interrupt. | ||
| 58 | * @als_thresh_high: CH0 'high' count to trigger interrupt. | ||
| 59 | * @prox_thres_low: Low threshold proximity detection. | ||
| 60 | * @prox_thres_high: High threshold proximity detection | ||
| 61 | * @prox_pulse_count: Number if proximity emitter pulses | ||
| 62 | * @prox_max_samples_cal: Used for prox cal. | ||
| 63 | */ | ||
| 64 | struct tsl2x7x_settings { | ||
| 65 | int als_time; | ||
| 66 | int als_gain; | ||
| 67 | int als_gain_trim; | ||
| 68 | int wait_time; | ||
| 69 | int prx_time; | ||
| 70 | int prox_gain; | ||
| 71 | int prox_config; | ||
| 72 | int als_cal_target; | ||
| 73 | u8 interrupts_en; | ||
| 74 | u8 persistence; | ||
| 75 | int als_thresh_low; | ||
| 76 | int als_thresh_high; | ||
| 77 | int prox_thres_low; | ||
| 78 | int prox_thres_high; | ||
| 79 | int prox_pulse_count; | ||
| 80 | int prox_max_samples_cal; | ||
| 81 | int prox_diode; | ||
| 82 | int prox_power; | ||
| 83 | }; | ||
| 84 | |||
| 85 | /** | ||
| 86 | * struct tsl2X7X_platform_data - Platform callback, glass and defaults | ||
| 87 | * @platform_power: Suspend/resume platform callback | ||
| 88 | * @power_on: Power on callback | ||
| 89 | * @power_off: Power off callback | ||
| 90 | * @platform_lux_table: Device specific glass coefficents | ||
| 91 | * @platform_default_settings: Device specific power on defaults | ||
| 92 | * | ||
| 93 | */ | ||
| 94 | struct tsl2X7X_platform_data { | ||
| 95 | struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE]; | ||
| 96 | struct tsl2x7x_settings *platform_default_settings; | ||
| 97 | }; | ||
| 98 | |||
| 99 | #endif /* __TSL2X7X_H */ | ||
| @@ -3,48 +3,6 @@ · 2 unmodified lines | |||
| 3 | # | 3 | # |
| 4 | menu "Active energy metering IC" | 4 | menu "Active energy metering IC" |
| 5 | 5 | ||
| 6 | config ADE7753 | ||
| 7 | tristate "Analog Devices ADE7753/6 Single-Phase Multifunction Metering IC Driver" | ||
| 8 | depends on SPI | ||
| 9 | help | ||
| 10 | Say yes here to build support for Analog Devices ADE7753 Single-Phase Multifunction | ||
| 11 | Metering IC with di/dt Sensor Interface. | ||
| 12 | |||
| 13 | To compile this driver as a module, choose M here: the | ||
| 14 | module will be called ade7753. | ||
| 15 | |||
| 16 | config ADE7754 | ||
| 17 | tristate "Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver" | ||
| 18 | depends on SPI | ||
| 19 | help | ||
| 20 | Say yes here to build support for Analog Devices ADE7754 Polyphase | ||
| 21 | Multifunction Energy Metering IC Driver. | ||
| 22 | |||
| 23 | To compile this driver as a module, choose M here: the | ||
| 24 | module will be called ade7754. | ||
| 25 | |||
| 26 | config ADE7758 | ||
| 27 | tristate "Analog Devices ADE7758 Poly Phase Multifunction Energy Metering IC Driver" | ||
| 28 | depends on SPI | ||
| 29 | select IIO_TRIGGER if IIO_BUFFER | ||
| 30 | select IIO_KFIFO_BUF if IIO_BUFFER | ||
| 31 | help | ||
| 32 | Say yes here to build support for Analog Devices ADE7758 Polyphase | ||
| 33 | Multifunction Energy Metering IC with Per Phase Information Driver. | ||
| 34 | |||
| 35 | To compile this driver as a module, choose M here: the | ||
| 36 | module will be called ade7758. | ||
| 37 | |||
| 38 | config ADE7759 | ||
| 39 | tristate "Analog Devices ADE7759 Active Energy Metering IC Driver" | ||
| 40 | depends on SPI | ||
| 41 | help | ||
| 42 | Say yes here to build support for Analog Devices ADE7758 Active Energy | ||
| 43 | Metering IC with di/dt Sensor Interface. | ||
| 44 | |||
| 45 | To compile this driver as a module, choose M here: the | ||
| 46 | module will be called ade7759. | ||
| 47 | |||
| 48 | config ADE7854 | 6 | config ADE7854 |
| 49 | tristate "Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver" | 7 | tristate "Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver" |
| 50 | depends on SPI || I2C | 8 | depends on SPI || I2C |
| @@ -3,14 +3,6 @@ · 2 unmodified lines | |||
| 3 | # Makefile for metering ic drivers | 3 | # Makefile for metering ic drivers |
| 4 | # | 4 | # |
| 5 | 5 | ||
| 6 | obj-$(CONFIG_ADE7753) += ade7753.o | ||
| 7 | obj-$(CONFIG_ADE7754) += ade7754.o | ||
| 8 | |||
| 9 | ade7758-y := ade7758_core.o | ||
| 10 | ade7758-$(CONFIG_IIO_BUFFER) += ade7758_ring.o ade7758_trigger.o | ||
| 11 | obj-$(CONFIG_ADE7758) += ade7758.o | ||
| 12 | |||
| 13 | obj-$(CONFIG_ADE7759) += ade7759.o | ||
| 14 | obj-$(CONFIG_ADE7854) += ade7854.o | 6 | obj-$(CONFIG_ADE7854) += ade7854.o |
| 15 | obj-$(CONFIG_ADE7854_I2C) += ade7854-i2c.o | 7 | obj-$(CONFIG_ADE7854_I2C) += ade7854-i2c.o |
| 16 | obj-$(CONFIG_ADE7854_SPI) += ade7854-spi.o | 8 | obj-$(CONFIG_ADE7854_SPI) += ade7854-spi.o |
| @@ -1,630 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADE7753 Single-Phase Multifunction Metering IC with di/dt Sensor Interface | ||
| 3 | * | ||
| 4 | * Copyright 2010 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2 or later. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/device.h> | ||
| 11 | #include <linux/interrupt.h> | ||
| 12 | #include <linux/irq.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/list.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/mutex.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/sysfs.h> | ||
| 19 | #include <linux/iio/iio.h> | ||
| 20 | #include <linux/iio/sysfs.h> | ||
| 21 | #include <linux/spi/spi.h> | ||
| 22 | #include "meter.h" | ||
| 23 | |||
| 24 | #define ADE7753_WAVEFORM 0x01 | ||
| 25 | #define ADE7753_AENERGY 0x02 | ||
| 26 | #define ADE7753_RAENERGY 0x03 | ||
| 27 | #define ADE7753_LAENERGY 0x04 | ||
| 28 | #define ADE7753_VAENERGY 0x05 | ||
| 29 | #define ADE7753_RVAENERGY 0x06 | ||
| 30 | #define ADE7753_LVAENERGY 0x07 | ||
| 31 | #define ADE7753_LVARENERGY 0x08 | ||
| 32 | #define ADE7753_MODE 0x09 | ||
| 33 | #define ADE7753_IRQEN 0x0A | ||
| 34 | #define ADE7753_STATUS 0x0B | ||
| 35 | #define ADE7753_RSTSTATUS 0x0C | ||
| 36 | #define ADE7753_CH1OS 0x0D | ||
| 37 | #define ADE7753_CH2OS 0x0E | ||
| 38 | #define ADE7753_GAIN 0x0F | ||
| 39 | #define ADE7753_PHCAL 0x10 | ||
| 40 | #define ADE7753_APOS 0x11 | ||
| 41 | #define ADE7753_WGAIN 0x12 | ||
| 42 | #define ADE7753_WDIV 0x13 | ||
| 43 | #define ADE7753_CFNUM 0x14 | ||
| 44 | #define ADE7753_CFDEN 0x15 | ||
| 45 | #define ADE7753_IRMS 0x16 | ||
| 46 | #define ADE7753_VRMS 0x17 | ||
| 47 | #define ADE7753_IRMSOS 0x18 | ||
| 48 | #define ADE7753_VRMSOS 0x19 | ||
| 49 | #define ADE7753_VAGAIN 0x1A | ||
| 50 | #define ADE7753_VADIV 0x1B | ||
| 51 | #define ADE7753_LINECYC 0x1C | ||
| 52 | #define ADE7753_ZXTOUT 0x1D | ||
| 53 | #define ADE7753_SAGCYC 0x1E | ||
| 54 | #define ADE7753_SAGLVL 0x1F | ||
| 55 | #define ADE7753_IPKLVL 0x20 | ||
| 56 | #define ADE7753_VPKLVL 0x21 | ||
| 57 | #define ADE7753_IPEAK 0x22 | ||
| 58 | #define ADE7753_RSTIPEAK 0x23 | ||
| 59 | #define ADE7753_VPEAK 0x24 | ||
| 60 | #define ADE7753_RSTVPEAK 0x25 | ||
| 61 | #define ADE7753_TEMP 0x26 | ||
| 62 | #define ADE7753_PERIOD 0x27 | ||
| 63 | #define ADE7753_TMODE 0x3D | ||
| 64 | #define ADE7753_CHKSUM 0x3E | ||
| 65 | #define ADE7753_DIEREV 0x3F | ||
| 66 | |||
| 67 | #define ADE7753_READ_REG(a) a | ||
| 68 | #define ADE7753_WRITE_REG(a) ((a) | 0x80) | ||
| 69 | |||
| 70 | #define ADE7753_MAX_TX 4 | ||
| 71 | #define ADE7753_MAX_RX 4 | ||
| 72 | #define ADE7753_STARTUP_DELAY 1000 | ||
| 73 | |||
| 74 | #define ADE7753_SPI_SLOW (u32)(300 * 1000) | ||
| 75 | #define ADE7753_SPI_BURST (u32)(1000 * 1000) | ||
| 76 | #define ADE7753_SPI_FAST (u32)(2000 * 1000) | ||
| 77 | |||
| 78 | /** | ||
| 79 | * struct ade7753_state - device instance specific data | ||
| 80 | * @us: actual spi_device | ||
| 81 | * @tx: transmit buffer | ||
| 82 | * @rx: receive buffer | ||
| 83 | * @buf_lock: mutex to protect tx, rx and write frequency | ||
| 84 | **/ | ||
| 85 | struct ade7753_state { | ||
| 86 | struct spi_device *us; | ||
| 87 | struct mutex buf_lock; | ||
| 88 | u8 tx[ADE7753_MAX_TX] ____cacheline_aligned; | ||
| 89 | u8 rx[ADE7753_MAX_RX]; | ||
| 90 | }; | ||
| 91 | |||
| 92 | static int ade7753_spi_write_reg_8(struct device *dev, | ||
| 93 | u8 reg_address, | ||
| 94 | u8 val) | ||
| 95 | { | ||
| 96 | int ret; | ||
| 97 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 98 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 99 | |||
| 100 | mutex_lock(&st->buf_lock); | ||
| 101 | st->tx[0] = ADE7753_WRITE_REG(reg_address); | ||
| 102 | st->tx[1] = val; | ||
| 103 | |||
| 104 | ret = spi_write(st->us, st->tx, 2); | ||
| 105 | mutex_unlock(&st->buf_lock); | ||
| 106 | |||
| 107 | return ret; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int __ade7753_spi_write_reg_16(struct device *dev, u8 reg_address, | ||
| 111 | u16 value) | ||
| 112 | { | ||
| 113 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 114 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 115 | |||
| 116 | st->tx[0] = ADE7753_WRITE_REG(reg_address); | ||
| 117 | st->tx[1] = (value >> 8) & 0xFF; | ||
| 118 | st->tx[2] = value & 0xFF; | ||
| 119 | |||
| 120 | return spi_write(st->us, st->tx, 3); | ||
| 121 | } | ||
| 122 | |||
| 123 | static int ade7753_spi_write_reg_16(struct device *dev, u8 reg_address, | ||
| 124 | u16 value) | ||
| 125 | { | ||
| 126 | int ret; | ||
| 127 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 128 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 129 | |||
| 130 | mutex_lock(&st->buf_lock); | ||
| 131 | ret = __ade7753_spi_write_reg_16(dev, reg_address, value); | ||
| 132 | mutex_unlock(&st->buf_lock); | ||
| 133 | |||
| 134 | return ret; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int ade7753_spi_read_reg_8(struct device *dev, | ||
| 138 | u8 reg_address, | ||
| 139 | u8 *val) | ||
| 140 | { | ||
| 141 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 142 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 143 | ssize_t ret; | ||
| 144 | |||
| 145 | ret = spi_w8r8(st->us, ADE7753_READ_REG(reg_address)); | ||
| 146 | if (ret < 0) { | ||
| 147 | dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X", | ||
| 148 | reg_address); | ||
| 149 | return ret; | ||
| 150 | } | ||
| 151 | *val = ret; | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int ade7753_spi_read_reg_16(struct device *dev, | ||
| 157 | u8 reg_address, | ||
| 158 | u16 *val) | ||
| 159 | { | ||
| 160 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 161 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 162 | ssize_t ret; | ||
| 163 | |||
| 164 | ret = spi_w8r16be(st->us, ADE7753_READ_REG(reg_address)); | ||
| 165 | if (ret < 0) { | ||
| 166 | dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", | ||
| 167 | reg_address); | ||
| 168 | return ret; | ||
| 169 | } | ||
| 170 | |||
| 171 | *val = ret; | ||
| 172 | |||
| 173 | return 0; | ||
| 174 | } | ||
| 175 | |||
| 176 | static int ade7753_spi_read_reg_24(struct device *dev, | ||
| 177 | u8 reg_address, | ||
| 178 | u32 *val) | ||
| 179 | { | ||
| 180 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 181 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 182 | int ret; | ||
| 183 | struct spi_transfer xfers[] = { | ||
| 184 | { | ||
| 185 | .tx_buf = st->tx, | ||
| 186 | .bits_per_word = 8, | ||
| 187 | .len = 1, | ||
| 188 | }, { | ||
| 189 | .rx_buf = st->tx, | ||
| 190 | .bits_per_word = 8, | ||
| 191 | .len = 3, | ||
| 192 | } | ||
| 193 | }; | ||
| 194 | |||
| 195 | mutex_lock(&st->buf_lock); | ||
| 196 | st->tx[0] = ADE7753_READ_REG(reg_address); | ||
| 197 | |||
| 198 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 199 | if (ret) { | ||
| 200 | dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X", | ||
| 201 | reg_address); | ||
| 202 | goto error_ret; | ||
| 203 | } | ||
| 204 | *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; | ||
| 205 | |||
| 206 | error_ret: | ||
| 207 | mutex_unlock(&st->buf_lock); | ||
| 208 | return ret; | ||
| 209 | } | ||
| 210 | |||
| 211 | static ssize_t ade7753_read_8bit(struct device *dev, | ||
| 212 | struct device_attribute *attr, | ||
| 213 | char *buf) | ||
| 214 | { | ||
| 215 | int ret; | ||
| 216 | u8 val; | ||
| 217 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 218 | |||
| 219 | ret = ade7753_spi_read_reg_8(dev, this_attr->address, &val); | ||
| 220 | if (ret) | ||
| 221 | return ret; | ||
| 222 | |||
| 223 | return sprintf(buf, "%u\n", val); | ||
| 224 | } | ||
| 225 | |||
| 226 | static ssize_t ade7753_read_16bit(struct device *dev, | ||
| 227 | struct device_attribute *attr, | ||
| 228 | char *buf) | ||
| 229 | { | ||
| 230 | int ret; | ||
| 231 | u16 val; | ||
| 232 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 233 | |||
| 234 | ret = ade7753_spi_read_reg_16(dev, this_attr->address, &val); | ||
| 235 | if (ret) | ||
| 236 | return ret; | ||
| 237 | |||
| 238 | return sprintf(buf, "%u\n", val); | ||
| 239 | } | ||
| 240 | |||
| 241 | static ssize_t ade7753_read_24bit(struct device *dev, | ||
| 242 | struct device_attribute *attr, | ||
| 243 | char *buf) | ||
| 244 | { | ||
| 245 | int ret; | ||
| 246 | u32 val; | ||
| 247 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 248 | |||
| 249 | ret = ade7753_spi_read_reg_24(dev, this_attr->address, &val); | ||
| 250 | if (ret) | ||
| 251 | return ret; | ||
| 252 | |||
| 253 | return sprintf(buf, "%u\n", val); | ||
| 254 | } | ||
| 255 | |||
| 256 | static ssize_t ade7753_write_8bit(struct device *dev, | ||
| 257 | struct device_attribute *attr, | ||
| 258 | const char *buf, | ||
| 259 | size_t len) | ||
| 260 | { | ||
| 261 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 262 | int ret; | ||
| 263 | u8 val; | ||
| 264 | |||
| 265 | ret = kstrtou8(buf, 10, &val); | ||
| 266 | if (ret) | ||
| 267 | goto error_ret; | ||
| 268 | ret = ade7753_spi_write_reg_8(dev, this_attr->address, val); | ||
| 269 | |||
| 270 | error_ret: | ||
| 271 | return ret ? ret : len; | ||
| 272 | } | ||
| 273 | |||
| 274 | static ssize_t ade7753_write_16bit(struct device *dev, | ||
| 275 | struct device_attribute *attr, | ||
| 276 | const char *buf, | ||
| 277 | size_t len) | ||
| 278 | { | ||
| 279 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 280 | int ret; | ||
| 281 | u16 val; | ||
| 282 | |||
| 283 | ret = kstrtou16(buf, 10, &val); | ||
| 284 | if (ret) | ||
| 285 | goto error_ret; | ||
| 286 | ret = ade7753_spi_write_reg_16(dev, this_attr->address, val); | ||
| 287 | |||
| 288 | error_ret: | ||
| 289 | return ret ? ret : len; | ||
| 290 | } | ||
| 291 | |||
| 292 | static int ade7753_reset(struct device *dev) | ||
| 293 | { | ||
| 294 | u16 val; | ||
| 295 | int ret; | ||
| 296 | |||
| 297 | ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val); | ||
| 298 | if (ret) | ||
| 299 | return ret; | ||
| 300 | |||
| 301 | val |= BIT(6); /* Software Chip Reset */ | ||
| 302 | |||
| 303 | return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val); | ||
| 304 | } | ||
| 305 | |||
| 306 | static IIO_DEV_ATTR_AENERGY(ade7753_read_24bit, ADE7753_AENERGY); | ||
| 307 | static IIO_DEV_ATTR_LAENERGY(ade7753_read_24bit, ADE7753_LAENERGY); | ||
| 308 | static IIO_DEV_ATTR_VAENERGY(ade7753_read_24bit, ADE7753_VAENERGY); | ||
| 309 | static IIO_DEV_ATTR_LVAENERGY(ade7753_read_24bit, ADE7753_LVAENERGY); | ||
| 310 | static IIO_DEV_ATTR_CFDEN(0644, | ||
| 311 | ade7753_read_16bit, | ||
| 312 | ade7753_write_16bit, | ||
| 313 | ADE7753_CFDEN); | ||
| 314 | static IIO_DEV_ATTR_CFNUM(0644, | ||
| 315 | ade7753_read_8bit, | ||
| 316 | ade7753_write_8bit, | ||
| 317 | ADE7753_CFNUM); | ||
| 318 | static IIO_DEV_ATTR_CHKSUM(ade7753_read_8bit, ADE7753_CHKSUM); | ||
| 319 | static IIO_DEV_ATTR_PHCAL(0644, | ||
| 320 | ade7753_read_16bit, | ||
| 321 | ade7753_write_16bit, | ||
| 322 | ADE7753_PHCAL); | ||
| 323 | static IIO_DEV_ATTR_APOS(0644, | ||
| 324 | ade7753_read_16bit, | ||
| 325 | ade7753_write_16bit, | ||
| 326 | ADE7753_APOS); | ||
| 327 | static IIO_DEV_ATTR_SAGCYC(0644, | ||
| 328 | ade7753_read_8bit, | ||
| 329 | ade7753_write_8bit, | ||
| 330 | ADE7753_SAGCYC); | ||
| 331 | static IIO_DEV_ATTR_SAGLVL(0644, | ||
| 332 | ade7753_read_8bit, | ||
| 333 | ade7753_write_8bit, | ||
| 334 | ADE7753_SAGLVL); | ||
| 335 | static IIO_DEV_ATTR_LINECYC(0644, | ||
| 336 | ade7753_read_8bit, | ||
| 337 | ade7753_write_8bit, | ||
| 338 | ADE7753_LINECYC); | ||
| 339 | static IIO_DEV_ATTR_WDIV(0644, | ||
| 340 | ade7753_read_8bit, | ||
| 341 | ade7753_write_8bit, | ||
| 342 | ADE7753_WDIV); | ||
| 343 | static IIO_DEV_ATTR_IRMS(0644, | ||
| 344 | ade7753_read_24bit, | ||
| 345 | NULL, | ||
| 346 | ADE7753_IRMS); | ||
| 347 | static IIO_DEV_ATTR_VRMS(0444, | ||
| 348 | ade7753_read_24bit, | ||
| 349 | NULL, | ||
| 350 | ADE7753_VRMS); | ||
| 351 | static IIO_DEV_ATTR_IRMSOS(0644, | ||
| 352 | ade7753_read_16bit, | ||
| 353 | ade7753_write_16bit, | ||
| 354 | ADE7753_IRMSOS); | ||
| 355 | static IIO_DEV_ATTR_VRMSOS(0644, | ||
| 356 | ade7753_read_16bit, | ||
| 357 | ade7753_write_16bit, | ||
| 358 | ADE7753_VRMSOS); | ||
| 359 | static IIO_DEV_ATTR_WGAIN(0644, | ||
| 360 | ade7753_read_16bit, | ||
| 361 | ade7753_write_16bit, | ||
| 362 | ADE7753_WGAIN); | ||
| 363 | static IIO_DEV_ATTR_VAGAIN(0644, | ||
| 364 | ade7753_read_16bit, | ||
| 365 | ade7753_write_16bit, | ||
| 366 | ADE7753_VAGAIN); | ||
| 367 | static IIO_DEV_ATTR_PGA_GAIN(0644, | ||
| 368 | ade7753_read_16bit, | ||
| 369 | ade7753_write_16bit, | ||
| 370 | ADE7753_GAIN); | ||
| 371 | static IIO_DEV_ATTR_IPKLVL(0644, | ||
| 372 | ade7753_read_8bit, | ||
| 373 | ade7753_write_8bit, | ||
| 374 | ADE7753_IPKLVL); | ||
| 375 | static IIO_DEV_ATTR_VPKLVL(0644, | ||
| 376 | ade7753_read_8bit, | ||
| 377 | ade7753_write_8bit, | ||
| 378 | ADE7753_VPKLVL); | ||
| 379 | static IIO_DEV_ATTR_IPEAK(0444, | ||
| 380 | ade7753_read_24bit, | ||
| 381 | NULL, | ||
| 382 | ADE7753_IPEAK); | ||
| 383 | static IIO_DEV_ATTR_VPEAK(0444, | ||
| 384 | ade7753_read_24bit, | ||
| 385 | NULL, | ||
| 386 | ADE7753_VPEAK); | ||
| 387 | static IIO_DEV_ATTR_VPERIOD(0444, | ||
| 388 | ade7753_read_16bit, | ||
| 389 | NULL, | ||
| 390 | ADE7753_PERIOD); | ||
| 391 | |||
| 392 | static IIO_DEVICE_ATTR(choff_1, 0644, | ||
| 393 | ade7753_read_8bit, | ||
| 394 | ade7753_write_8bit, | ||
| 395 | ADE7753_CH1OS); | ||
| 396 | |||
| 397 | static IIO_DEVICE_ATTR(choff_2, 0644, | ||
| 398 | ade7753_read_8bit, | ||
| 399 | ade7753_write_8bit, | ||
| 400 | ADE7753_CH2OS); | ||
| 401 | |||
| 402 | static int ade7753_set_irq(struct device *dev, bool enable) | ||
| 403 | { | ||
| 404 | int ret; | ||
| 405 | u8 irqen; | ||
| 406 | |||
| 407 | ret = ade7753_spi_read_reg_8(dev, ADE7753_IRQEN, &irqen); | ||
| 408 | if (ret) | ||
| 409 | goto error_ret; | ||
| 410 | |||
| 411 | if (enable) | ||
| 412 | irqen |= BIT(3); /* Enables an interrupt when a data is | ||
| 413 | * present in the waveform register | ||
| 414 | */ | ||
| 415 | else | ||
| 416 | irqen &= ~BIT(3); | ||
| 417 | |||
| 418 | ret = ade7753_spi_write_reg_8(dev, ADE7753_IRQEN, irqen); | ||
| 419 | |||
| 420 | error_ret: | ||
| 421 | return ret; | ||
| 422 | } | ||
| 423 | |||
| 424 | /* Power down the device */ | ||
| 425 | static int ade7753_stop_device(struct device *dev) | ||
| 426 | { | ||
| 427 | u16 val; | ||
| 428 | int ret; | ||
| 429 | |||
| 430 | ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val); | ||
| 431 | if (ret) | ||
| 432 | return ret; | ||
| 433 | |||
| 434 | val |= BIT(4); /* AD converters can be turned off */ | ||
| 435 | |||
| 436 | return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val); | ||
| 437 | } | ||
| 438 | |||
| 439 | static int ade7753_initial_setup(struct iio_dev *indio_dev) | ||
| 440 | { | ||
| 441 | int ret; | ||
| 442 | struct device *dev = &indio_dev->dev; | ||
| 443 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 444 | |||
| 445 | /* use low spi speed for init */ | ||
| 446 | st->us->mode = SPI_MODE_3; | ||
| 447 | spi_setup(st->us); | ||
| 448 | |||
| 449 | /* Disable IRQ */ | ||
| 450 | ret = ade7753_set_irq(dev, false); | ||
| 451 | if (ret) { | ||
| 452 | dev_err(dev, "disable irq failed"); | ||
| 453 | goto err_ret; | ||
| 454 | } | ||
| 455 | |||
| 456 | ade7753_reset(dev); | ||
| 457 | usleep_range(ADE7753_STARTUP_DELAY, ADE7753_STARTUP_DELAY + 100); | ||
| 458 | |||
| 459 | err_ret: | ||
| 460 | return ret; | ||
| 461 | } | ||
| 462 | |||
| 463 | static ssize_t ade7753_read_frequency(struct device *dev, | ||
| 464 | struct device_attribute *attr, | ||
| 465 | char *buf) | ||
| 466 | { | ||
| 467 | int ret; | ||
| 468 | u16 t; | ||
| 469 | int sps; | ||
| 470 | |||
| 471 | ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, &t); | ||
| 472 | if (ret) | ||
| 473 | return ret; | ||
| 474 | |||
| 475 | t = (t >> 11) & 0x3; | ||
| 476 | sps = 27900 / (1 + t); | ||
| 477 | |||
| 478 | return sprintf(buf, "%d\n", sps); | ||
| 479 | } | ||
| 480 | |||
| 481 | static ssize_t ade7753_write_frequency(struct device *dev, | ||
| 482 | struct device_attribute *attr, | ||
| 483 | const char *buf, | ||
| 484 | size_t len) | ||
| 485 | { | ||
| 486 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 487 | struct ade7753_state *st = iio_priv(indio_dev); | ||
| 488 | u16 val; | ||
| 489 | int ret; | ||
| 490 | u16 reg, t; | ||
| 491 | |||
| 492 | ret = kstrtou16(buf, 10, &val); | ||
| 493 | if (ret) | ||
| 494 | return ret; | ||
| 495 | if (!val) | ||
| 496 | return -EINVAL; | ||
| 497 | |||
| 498 | mutex_lock(&st->buf_lock); | ||
| 499 | |||
| 500 | t = 27900 / val; | ||
| 501 | if (t > 0) | ||
| 502 | t--; | ||
| 503 | |||
| 504 | if (t > 1) | ||
| 505 | st->us->max_speed_hz = ADE7753_SPI_SLOW; | ||
| 506 | else | ||
| 507 | st->us->max_speed_hz = ADE7753_SPI_FAST; | ||
| 508 | |||
| 509 | ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, ®); | ||
| 510 | if (ret) | ||
| 511 | goto out; | ||
| 512 | |||
| 513 | reg &= ~(3 << 11); | ||
| 514 | reg |= t << 11; | ||
| 515 | |||
| 516 | ret = __ade7753_spi_write_reg_16(dev, ADE7753_MODE, reg); | ||
| 517 | |||
| 518 | out: | ||
| 519 | mutex_unlock(&st->buf_lock); | ||
| 520 | |||
| 521 | return ret ? ret : len; | ||
| 522 | } | ||
| 523 | |||
| 524 | static IIO_DEV_ATTR_TEMP_RAW(ade7753_read_8bit); | ||
| 525 | static IIO_CONST_ATTR(in_temp_offset, "-25 C"); | ||
| 526 | static IIO_CONST_ATTR(in_temp_scale, "0.67 C"); | ||
| 527 | |||
| 528 | static IIO_DEV_ATTR_SAMP_FREQ(0644, | ||
| 529 | ade7753_read_frequency, | ||
| 530 | ade7753_write_frequency); | ||
| 531 | |||
| 532 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500"); | ||
| 533 | |||
| 534 | static struct attribute *ade7753_attributes[] = { | ||
| 535 | &iio_dev_attr_in_temp_raw.dev_attr.attr, | ||
| 536 | &iio_const_attr_in_temp_offset.dev_attr.attr, | ||
| 537 | &iio_const_attr_in_temp_scale.dev_attr.attr, | ||
| 538 | &iio_dev_attr_sampling_frequency.dev_attr.attr, | ||
| 539 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, | ||
| 540 | &iio_dev_attr_phcal.dev_attr.attr, | ||
| 541 | &iio_dev_attr_cfden.dev_attr.attr, | ||
| 542 | &iio_dev_attr_aenergy.dev_attr.attr, | ||
| 543 | &iio_dev_attr_laenergy.dev_attr.attr, | ||
| 544 | &iio_dev_attr_vaenergy.dev_attr.attr, | ||
| 545 | &iio_dev_attr_lvaenergy.dev_attr.attr, | ||
| 546 | &iio_dev_attr_cfnum.dev_attr.attr, | ||
| 547 | &iio_dev_attr_apos.dev_attr.attr, | ||
| 548 | &iio_dev_attr_sagcyc.dev_attr.attr, | ||
| 549 | &iio_dev_attr_saglvl.dev_attr.attr, | ||
| 550 | &iio_dev_attr_linecyc.dev_attr.attr, | ||
| 551 | &iio_dev_attr_chksum.dev_attr.attr, | ||
| 552 | &iio_dev_attr_pga_gain.dev_attr.attr, | ||
| 553 | &iio_dev_attr_wgain.dev_attr.attr, | ||
| 554 | &iio_dev_attr_choff_1.dev_attr.attr, | ||
| 555 | &iio_dev_attr_choff_2.dev_attr.attr, | ||
| 556 | &iio_dev_attr_wdiv.dev_attr.attr, | ||
| 557 | &iio_dev_attr_irms.dev_attr.attr, | ||
| 558 | &iio_dev_attr_vrms.dev_attr.attr, | ||
| 559 | &iio_dev_attr_irmsos.dev_attr.attr, | ||
| 560 | &iio_dev_attr_vrmsos.dev_attr.attr, | ||
| 561 | &iio_dev_attr_vagain.dev_attr.attr, | ||
| 562 | &iio_dev_attr_ipklvl.dev_attr.attr, | ||
| 563 | &iio_dev_attr_vpklvl.dev_attr.attr, | ||
| 564 | &iio_dev_attr_ipeak.dev_attr.attr, | ||
| 565 | &iio_dev_attr_vpeak.dev_attr.attr, | ||
| 566 | &iio_dev_attr_vperiod.dev_attr.attr, | ||
| 567 | NULL, | ||
| 568 | }; | ||
| 569 | |||
| 570 | static const struct attribute_group ade7753_attribute_group = { | ||
| 571 | .attrs = ade7753_attributes, | ||
| 572 | }; | ||
| 573 | |||
| 574 | static const struct iio_info ade7753_info = { | ||
| 575 | .attrs = &ade7753_attribute_group, | ||
| 576 | }; | ||
| 577 | |||
| 578 | static int ade7753_probe(struct spi_device *spi) | ||
| 579 | { | ||
| 580 | int ret; | ||
| 581 | struct ade7753_state *st; | ||
| 582 | struct iio_dev *indio_dev; | ||
| 583 | |||
| 584 | /* setup the industrialio driver allocated elements */ | ||
| 585 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | ||
| 586 | if (!indio_dev) | ||
| 587 | return -ENOMEM; | ||
| 588 | /* this is only used for removal purposes */ | ||
| 589 | spi_set_drvdata(spi, indio_dev); | ||
| 590 | |||
| 591 | st = iio_priv(indio_dev); | ||
| 592 | st->us = spi; | ||
| 593 | mutex_init(&st->buf_lock); | ||
| 594 | |||
| 595 | indio_dev->name = spi->dev.driver->name; | ||
| 596 | indio_dev->dev.parent = &spi->dev; | ||
| 597 | indio_dev->info = &ade7753_info; | ||
| 598 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 599 | |||
| 600 | /* Get the device into a sane initial state */ | ||
| 601 | ret = ade7753_initial_setup(indio_dev); | ||
| 602 | if (ret) | ||
| 603 | return ret; | ||
| 604 | |||
| 605 | return iio_device_register(indio_dev); | ||
| 606 | } | ||
| 607 | |||
| 608 | static int ade7753_remove(struct spi_device *spi) | ||
| 609 | { | ||
| 610 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
| 611 | |||
| 612 | iio_device_unregister(indio_dev); | ||
| 613 | ade7753_stop_device(&indio_dev->dev); | ||
| 614 | |||
| 615 | return 0; | ||
| 616 | } | ||
| 617 | |||
| 618 | static struct spi_driver ade7753_driver = { | ||
| 619 | .driver = { | ||
| 620 | .name = "ade7753", | ||
| 621 | }, | ||
| 622 | .probe = ade7753_probe, | ||
| 623 | .remove = ade7753_remove, | ||
| 624 | }; | ||
| 625 | module_spi_driver(ade7753_driver); | ||
| 626 | |||
| 627 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | ||
| 628 | MODULE_DESCRIPTION("Analog Devices ADE7753/6 Single-Phase Multifunction Meter"); | ||
| 629 | MODULE_LICENSE("GPL v2"); | ||
| 630 | MODULE_ALIAS("spi:ade7753"); | ||
| @@ -1,664 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADE7754 Polyphase Multifunction Energy Metering IC Driver | ||
| 3 | * | ||
| 4 | * Copyright 2010 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2 or later. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/device.h> | ||
| 11 | #include <linux/interrupt.h> | ||
| 12 | #include <linux/irq.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/list.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/mutex.h> | ||
| 17 | #include <linux/spi/spi.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/sysfs.h> | ||
| 20 | #include <linux/iio/iio.h> | ||
| 21 | #include <linux/iio/sysfs.h> | ||
| 22 | #include "meter.h" | ||
| 23 | |||
| 24 | #define ADE7754_AENERGY 0x01 | ||
| 25 | #define ADE7754_RAENERGY 0x02 | ||
| 26 | #define ADE7754_LAENERGY 0x03 | ||
| 27 | #define ADE7754_VAENERGY 0x04 | ||
| 28 | #define ADE7754_RVAENERGY 0x05 | ||
| 29 | #define ADE7754_LVAENERGY 0x06 | ||
| 30 | #define ADE7754_PERIOD 0x07 | ||
| 31 | #define ADE7754_TEMP 0x08 | ||
| 32 | #define ADE7754_WFORM 0x09 | ||
| 33 | #define ADE7754_OPMODE 0x0A | ||
| 34 | #define ADE7754_MMODE 0x0B | ||
| 35 | #define ADE7754_WAVMODE 0x0C | ||
| 36 | #define ADE7754_WATMODE 0x0D | ||
| 37 | #define ADE7754_VAMODE 0x0E | ||
| 38 | #define ADE7754_IRQEN 0x0F | ||
| 39 | #define ADE7754_STATUS 0x10 | ||
| 40 | #define ADE7754_RSTATUS 0x11 | ||
| 41 | #define ADE7754_ZXTOUT 0x12 | ||
| 42 | #define ADE7754_LINCYC 0x13 | ||
| 43 | #define ADE7754_SAGCYC 0x14 | ||
| 44 | #define ADE7754_SAGLVL 0x15 | ||
| 45 | #define ADE7754_VPEAK 0x16 | ||
| 46 | #define ADE7754_IPEAK 0x17 | ||
| 47 | #define ADE7754_GAIN 0x18 | ||
| 48 | #define ADE7754_AWG 0x19 | ||
| 49 | #define ADE7754_BWG 0x1A | ||
| 50 | #define ADE7754_CWG 0x1B | ||
| 51 | #define ADE7754_AVAG 0x1C | ||
| 52 | #define ADE7754_BVAG 0x1D | ||
| 53 | #define ADE7754_CVAG 0x1E | ||
| 54 | #define ADE7754_APHCAL 0x1F | ||
| 55 | #define ADE7754_BPHCAL 0x20 | ||
| 56 | #define ADE7754_CPHCAL 0x21 | ||
| 57 | #define ADE7754_AAPOS 0x22 | ||
| 58 | #define ADE7754_BAPOS 0x23 | ||
| 59 | #define ADE7754_CAPOS 0x24 | ||
| 60 | #define ADE7754_CFNUM 0x25 | ||
| 61 | #define ADE7754_CFDEN 0x26 | ||
| 62 | #define ADE7754_WDIV 0x27 | ||
| 63 | #define ADE7754_VADIV 0x28 | ||
| 64 | #define ADE7754_AIRMS 0x29 | ||
| 65 | #define ADE7754_BIRMS 0x2A | ||
| 66 | #define ADE7754_CIRMS 0x2B | ||
| 67 | #define ADE7754_AVRMS 0x2C | ||
| 68 | #define ADE7754_BVRMS 0x2D | ||
| 69 | #define ADE7754_CVRMS 0x2E | ||
| 70 | #define ADE7754_AIRMSOS 0x2F | ||
| 71 | #define ADE7754_BIRMSOS 0x30 | ||
| 72 | #define ADE7754_CIRMSOS 0x31 | ||
| 73 | #define ADE7754_AVRMSOS 0x32 | ||
| 74 | #define ADE7754_BVRMSOS 0x33 | ||
| 75 | #define ADE7754_CVRMSOS 0x34 | ||
| 76 | #define ADE7754_AAPGAIN 0x35 | ||
| 77 | #define ADE7754_BAPGAIN 0x36 | ||
| 78 | #define ADE7754_CAPGAIN 0x37 | ||
| 79 | #define ADE7754_AVGAIN 0x38 | ||
| 80 | #define ADE7754_BVGAIN 0x39 | ||
| 81 | #define ADE7754_CVGAIN 0x3A | ||
| 82 | #define ADE7754_CHKSUM 0x3E | ||
| 83 | #define ADE7754_VERSION 0x3F | ||
| 84 | |||
| 85 | #define ADE7754_READ_REG(a) a | ||
| 86 | #define ADE7754_WRITE_REG(a) ((a) | 0x80) | ||
| 87 | |||
| 88 | #define ADE7754_MAX_TX 4 | ||
| 89 | #define ADE7754_MAX_RX 4 | ||
| 90 | #define ADE7754_STARTUP_DELAY 1000 | ||
| 91 | |||
| 92 | #define ADE7754_SPI_SLOW (u32)(300 * 1000) | ||
| 93 | #define ADE7754_SPI_BURST (u32)(1000 * 1000) | ||
| 94 | #define ADE7754_SPI_FAST (u32)(2000 * 1000) | ||
| 95 | |||
| 96 | /** | ||
| 97 | * struct ade7754_state - device instance specific data | ||
| 98 | * @us: actual spi_device | ||
| 99 | * @buf_lock: mutex to protect tx, rx and write frequency | ||
| 100 | * @tx: transmit buffer | ||
| 101 | * @rx: receive buffer | ||
| 102 | **/ | ||
| 103 | struct ade7754_state { | ||
| 104 | struct spi_device *us; | ||
| 105 | struct mutex buf_lock; | ||
| 106 | u8 tx[ADE7754_MAX_TX] ____cacheline_aligned; | ||
| 107 | u8 rx[ADE7754_MAX_RX]; | ||
| 108 | }; | ||
| 109 | |||
| 110 | /* Unlocked version of ade7754_spi_write_reg_8 function */ | ||
| 111 | static int __ade7754_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) | ||
| 112 | { | ||
| 113 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 114 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 115 | |||
| 116 | st->tx[0] = ADE7754_WRITE_REG(reg_address); | ||
| 117 | st->tx[1] = val; | ||
| 118 | return spi_write(st->us, st->tx, 2); | ||
| 119 | } | ||
| 120 | |||
| 121 | static int ade7754_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) | ||
| 122 | { | ||
| 123 | int ret; | ||
| 124 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 125 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 126 | |||
| 127 | mutex_lock(&st->buf_lock); | ||
| 128 | ret = __ade7754_spi_write_reg_8(dev, reg_address, val); | ||
| 129 | mutex_unlock(&st->buf_lock); | ||
| 130 | |||
| 131 | return ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | static int ade7754_spi_write_reg_16(struct device *dev, | ||
| 135 | u8 reg_address, u16 val) | ||
| 136 | { | ||
| 137 | int ret; | ||
| 138 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 139 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 140 | |||
| 141 | mutex_lock(&st->buf_lock); | ||
| 142 | st->tx[0] = ADE7754_WRITE_REG(reg_address); | ||
| 143 | st->tx[1] = (val >> 8) & 0xFF; | ||
| 144 | st->tx[2] = val & 0xFF; | ||
| 145 | ret = spi_write(st->us, st->tx, 3); | ||
| 146 | mutex_unlock(&st->buf_lock); | ||
| 147 | |||
| 148 | return ret; | ||
| 149 | } | ||
| 150 | |||
| 151 | static int ade7754_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val) | ||
| 152 | { | ||
| 153 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 154 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 155 | int ret; | ||
| 156 | |||
| 157 | ret = spi_w8r8(st->us, ADE7754_READ_REG(reg_address)); | ||
| 158 | if (ret < 0) { | ||
| 159 | dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X", | ||
| 160 | reg_address); | ||
| 161 | return ret; | ||
| 162 | } | ||
| 163 | *val = ret; | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int ade7754_spi_read_reg_16(struct device *dev, | ||
| 169 | u8 reg_address, u16 *val) | ||
| 170 | { | ||
| 171 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 172 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 173 | int ret; | ||
| 174 | |||
| 175 | ret = spi_w8r16be(st->us, ADE7754_READ_REG(reg_address)); | ||
| 176 | if (ret < 0) { | ||
| 177 | dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", | ||
| 178 | reg_address); | ||
| 179 | return ret; | ||
| 180 | } | ||
| 181 | |||
| 182 | *val = ret; | ||
| 183 | |||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | static int ade7754_spi_read_reg_24(struct device *dev, | ||
| 188 | u8 reg_address, u32 *val) | ||
| 189 | { | ||
| 190 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 191 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 192 | int ret; | ||
| 193 | struct spi_transfer xfers[] = { | ||
| 194 | { | ||
| 195 | .tx_buf = st->tx, | ||
| 196 | .rx_buf = st->rx, | ||
| 197 | .bits_per_word = 8, | ||
| 198 | .len = 4, | ||
| 199 | }, | ||
| 200 | }; | ||
| 201 | |||
| 202 | mutex_lock(&st->buf_lock); | ||
| 203 | st->tx[0] = ADE7754_READ_REG(reg_address); | ||
| 204 | st->tx[1] = 0; | ||
| 205 | st->tx[2] = 0; | ||
| 206 | st->tx[3] = 0; | ||
| 207 | |||
| 208 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 209 | if (ret) { | ||
| 210 | dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X", | ||
| 211 | reg_address); | ||
| 212 | goto error_ret; | ||
| 213 | } | ||
| 214 | *val = (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3]; | ||
| 215 | |||
| 216 | error_ret: | ||
| 217 | mutex_unlock(&st->buf_lock); | ||
| 218 | return ret; | ||
| 219 | } | ||
| 220 | |||
| 221 | static ssize_t ade7754_read_8bit(struct device *dev, | ||
| 222 | struct device_attribute *attr, | ||
| 223 | char *buf) | ||
| 224 | { | ||
| 225 | int ret; | ||
| 226 | u8 val = 0; | ||
| 227 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 228 | |||
| 229 | ret = ade7754_spi_read_reg_8(dev, this_attr->address, &val); | ||
| 230 | if (ret) | ||
| 231 | return ret; | ||
| 232 | |||
| 233 | return sprintf(buf, "%u\n", val); | ||
| 234 | } | ||
| 235 | |||
| 236 | static ssize_t ade7754_read_16bit(struct device *dev, | ||
| 237 | struct device_attribute *attr, | ||
| 238 | char *buf) | ||
| 239 | { | ||
| 240 | int ret; | ||
| 241 | u16 val = 0; | ||
| 242 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 243 | |||
| 244 | ret = ade7754_spi_read_reg_16(dev, this_attr->address, &val); | ||
| 245 | if (ret) | ||
| 246 | return ret; | ||
| 247 | |||
| 248 | return sprintf(buf, "%u\n", val); | ||
| 249 | } | ||
| 250 | |||
| 251 | static ssize_t ade7754_read_24bit(struct device *dev, | ||
| 252 | struct device_attribute *attr, | ||
| 253 | char *buf) | ||
| 254 | { | ||
| 255 | int ret; | ||
| 256 | u32 val = 0; | ||
| 257 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 258 | |||
| 259 | ret = ade7754_spi_read_reg_24(dev, this_attr->address, &val); | ||
| 260 | if (ret) | ||
| 261 | return ret; | ||
| 262 | |||
| 263 | return sprintf(buf, "%u\n", val & 0xFFFFFF); | ||
| 264 | } | ||
| 265 | |||
| 266 | static ssize_t ade7754_write_8bit(struct device *dev, | ||
| 267 | struct device_attribute *attr, | ||
| 268 | const char *buf, | ||
| 269 | size_t len) | ||
| 270 | { | ||
| 271 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 272 | int ret; | ||
| 273 | u8 val; | ||
| 274 | |||
| 275 | ret = kstrtou8(buf, 10, &val); | ||
| 276 | if (ret) | ||
| 277 | goto error_ret; | ||
| 278 | ret = ade7754_spi_write_reg_8(dev, this_attr->address, val); | ||
| 279 | |||
| 280 | error_ret: | ||
| 281 | return ret ? ret : len; | ||
| 282 | } | ||
| 283 | |||
| 284 | static ssize_t ade7754_write_16bit(struct device *dev, | ||
| 285 | struct device_attribute *attr, | ||
| 286 | const char *buf, | ||
| 287 | size_t len) | ||
| 288 | { | ||
| 289 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 290 | int ret; | ||
| 291 | u16 val; | ||
| 292 | |||
| 293 | ret = kstrtou16(buf, 10, &val); | ||
| 294 | if (ret) | ||
| 295 | goto error_ret; | ||
| 296 | ret = ade7754_spi_write_reg_16(dev, this_attr->address, val); | ||
| 297 | |||
| 298 | error_ret: | ||
| 299 | return ret ? ret : len; | ||
| 300 | } | ||
| 301 | |||
| 302 | static int ade7754_reset(struct device *dev) | ||
| 303 | { | ||
| 304 | int ret; | ||
| 305 | u8 val; | ||
| 306 | |||
| 307 | ret = ade7754_spi_read_reg_8(dev, ADE7754_OPMODE, &val); | ||
| 308 | if (ret < 0) | ||
| 309 | return ret; | ||
| 310 | |||
| 311 | val |= BIT(6); /* Software Chip Reset */ | ||
| 312 | return ade7754_spi_write_reg_8(dev, ADE7754_OPMODE, val); | ||
| 313 | } | ||
| 314 | |||
| 315 | static IIO_DEV_ATTR_AENERGY(ade7754_read_24bit, ADE7754_AENERGY); | ||
| 316 | static IIO_DEV_ATTR_LAENERGY(ade7754_read_24bit, ADE7754_LAENERGY); | ||
| 317 | static IIO_DEV_ATTR_VAENERGY(ade7754_read_24bit, ADE7754_VAENERGY); | ||
| 318 | static IIO_DEV_ATTR_LVAENERGY(ade7754_read_24bit, ADE7754_LVAENERGY); | ||
| 319 | static IIO_DEV_ATTR_VPEAK(0644, | ||
| 320 | ade7754_read_8bit, | ||
| 321 | ade7754_write_8bit, | ||
| 322 | ADE7754_VPEAK); | ||
| 323 | static IIO_DEV_ATTR_IPEAK(0644, | ||
| 324 | ade7754_read_8bit, | ||
| 325 | ade7754_write_8bit, | ||
| 326 | ADE7754_VPEAK); | ||
| 327 | static IIO_DEV_ATTR_APHCAL(0644, | ||
| 328 | ade7754_read_8bit, | ||
| 329 | ade7754_write_8bit, | ||
| 330 | ADE7754_APHCAL); | ||
| 331 | static IIO_DEV_ATTR_BPHCAL(0644, | ||
| 332 | ade7754_read_8bit, | ||
| 333 | ade7754_write_8bit, | ||
| 334 | ADE7754_BPHCAL); | ||
| 335 | static IIO_DEV_ATTR_CPHCAL(0644, | ||
| 336 | ade7754_read_8bit, | ||
| 337 | ade7754_write_8bit, | ||
| 338 | ADE7754_CPHCAL); | ||
| 339 | static IIO_DEV_ATTR_AAPOS(0644, | ||
| 340 | ade7754_read_16bit, | ||
| 341 | ade7754_write_16bit, | ||
| 342 | ADE7754_AAPOS); | ||
| 343 | static IIO_DEV_ATTR_BAPOS(0644, | ||
| 344 | ade7754_read_16bit, | ||
| 345 | ade7754_write_16bit, | ||
| 346 | ADE7754_BAPOS); | ||
| 347 | static IIO_DEV_ATTR_CAPOS(0644, | ||
| 348 | ade7754_read_16bit, | ||
| 349 | ade7754_write_16bit, | ||
| 350 | ADE7754_CAPOS); | ||
| 351 | static IIO_DEV_ATTR_WDIV(0644, | ||
| 352 | ade7754_read_8bit, | ||
| 353 | ade7754_write_8bit, | ||
| 354 | ADE7754_WDIV); | ||
| 355 | static IIO_DEV_ATTR_VADIV(0644, | ||
| 356 | ade7754_read_8bit, | ||
| 357 | ade7754_write_8bit, | ||
| 358 | ADE7754_VADIV); | ||
| 359 | static IIO_DEV_ATTR_CFNUM(0644, | ||
| 360 | ade7754_read_16bit, | ||
| 361 | ade7754_write_16bit, | ||
| 362 | ADE7754_CFNUM); | ||
| 363 | static IIO_DEV_ATTR_CFDEN(0644, | ||
| 364 | ade7754_read_16bit, | ||
| 365 | ade7754_write_16bit, | ||
| 366 | ADE7754_CFDEN); | ||
| 367 | static IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(0644, | ||
| 368 | ade7754_read_16bit, | ||
| 369 | ade7754_write_16bit, | ||
| 370 | ADE7754_AAPGAIN); | ||
| 371 | static IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(0644, | ||
| 372 | ade7754_read_16bit, | ||
| 373 | ade7754_write_16bit, | ||
| 374 | ADE7754_BAPGAIN); | ||
| 375 | static IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(0644, | ||
| 376 | ade7754_read_16bit, | ||
| 377 | ade7754_write_16bit, | ||
| 378 | ADE7754_CAPGAIN); | ||
| 379 | static IIO_DEV_ATTR_AIRMS(0444, | ||
| 380 | ade7754_read_24bit, | ||
| 381 | NULL, | ||
| 382 | ADE7754_AIRMS); | ||
| 383 | static IIO_DEV_ATTR_BIRMS(0444, | ||
| 384 | ade7754_read_24bit, | ||
| 385 | NULL, | ||
| 386 | ADE7754_BIRMS); | ||
| 387 | static IIO_DEV_ATTR_CIRMS(0444, | ||
| 388 | ade7754_read_24bit, | ||
| 389 | NULL, | ||
| 390 | ADE7754_CIRMS); | ||
| 391 | static IIO_DEV_ATTR_AVRMS(0444, | ||
| 392 | ade7754_read_24bit, | ||
| 393 | NULL, | ||
| 394 | ADE7754_AVRMS); | ||
| 395 | static IIO_DEV_ATTR_BVRMS(0444, | ||
| 396 | ade7754_read_24bit, | ||
| 397 | NULL, | ||
| 398 | ADE7754_BVRMS); | ||
| 399 | static IIO_DEV_ATTR_CVRMS(0444, | ||
| 400 | ade7754_read_24bit, | ||
| 401 | NULL, | ||
| 402 | ADE7754_CVRMS); | ||
| 403 | static IIO_DEV_ATTR_AIRMSOS(0444, | ||
| 404 | ade7754_read_16bit, | ||
| 405 | ade7754_write_16bit, | ||
| 406 | ADE7754_AIRMSOS); | ||
| 407 | static IIO_DEV_ATTR_BIRMSOS(0444, | ||
| 408 | ade7754_read_16bit, | ||
| 409 | ade7754_write_16bit, | ||
| 410 | ADE7754_BIRMSOS); | ||
| 411 | static IIO_DEV_ATTR_CIRMSOS(0444, | ||
| 412 | ade7754_read_16bit, | ||
| 413 | ade7754_write_16bit, | ||
| 414 | ADE7754_CIRMSOS); | ||
| 415 | static IIO_DEV_ATTR_AVRMSOS(0444, | ||
| 416 | ade7754_read_16bit, | ||
| 417 | ade7754_write_16bit, | ||
| 418 | ADE7754_AVRMSOS); | ||
| 419 | static IIO_DEV_ATTR_BVRMSOS(0444, | ||
| 420 | ade7754_read_16bit, | ||
| 421 | ade7754_write_16bit, | ||
| 422 | ADE7754_BVRMSOS); | ||
| 423 | static IIO_DEV_ATTR_CVRMSOS(0444, | ||
| 424 | ade7754_read_16bit, | ||
| 425 | ade7754_write_16bit, | ||
| 426 | ADE7754_CVRMSOS); | ||
| 427 | |||
| 428 | static int ade7754_set_irq(struct device *dev, bool enable) | ||
| 429 | { | ||
| 430 | int ret; | ||
| 431 | u16 irqen; | ||
| 432 | |||
| 433 | ret = ade7754_spi_read_reg_16(dev, ADE7754_IRQEN, &irqen); | ||
| 434 | if (ret) | ||
| 435 | return ret; | ||
| 436 | |||
| 437 | if (enable) | ||
| 438 | irqen |= BIT(14); /* Enables an interrupt when a data is | ||
| 439 | * present in the waveform register | ||
| 440 | */ | ||
| 441 | else | ||
| 442 | irqen &= ~BIT(14); | ||
| 443 | |||
| 444 | return ade7754_spi_write_reg_16(dev, ADE7754_IRQEN, irqen); | ||
| 445 | } | ||
| 446 | |||
| 447 | /* Power down the device */ | ||
| 448 | static int ade7754_stop_device(struct device *dev) | ||
| 449 | { | ||
| 450 | int ret; | ||
| 451 | u8 val; | ||
| 452 | |||
| 453 | ret = ade7754_spi_read_reg_8(dev, ADE7754_OPMODE, &val); | ||
| 454 | if (ret < 0) { | ||
| 455 | dev_err(dev, "unable to power down the device, error: %d", | ||
| 456 | ret); | ||
| 457 | return ret; | ||
| 458 | } | ||
| 459 | |||
| 460 | val |= 7 << 3; /* ADE7754 powered down */ | ||
| 461 | return ade7754_spi_write_reg_8(dev, ADE7754_OPMODE, val); | ||
| 462 | } | ||
| 463 | |||
| 464 | static int ade7754_initial_setup(struct iio_dev *indio_dev) | ||
| 465 | { | ||
| 466 | int ret; | ||
| 467 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 468 | struct device *dev = &indio_dev->dev; | ||
| 469 | |||
| 470 | /* use low spi speed for init */ | ||
| 471 | st->us->mode = SPI_MODE_3; | ||
| 472 | spi_setup(st->us); | ||
| 473 | |||
| 474 | /* Disable IRQ */ | ||
| 475 | ret = ade7754_set_irq(dev, false); | ||
| 476 | if (ret) { | ||
| 477 | dev_err(dev, "disable irq failed"); | ||
| 478 | goto err_ret; | ||
| 479 | } | ||
| 480 | |||
| 481 | ade7754_reset(dev); | ||
| 482 | usleep_range(ADE7754_STARTUP_DELAY, ADE7754_STARTUP_DELAY + 100); | ||
| 483 | |||
| 484 | err_ret: | ||
| 485 | return ret; | ||
| 486 | } | ||
| 487 | |||
| 488 | static ssize_t ade7754_read_frequency(struct device *dev, | ||
| 489 | struct device_attribute *attr, | ||
| 490 | char *buf) | ||
| 491 | { | ||
| 492 | int ret; | ||
| 493 | u8 t; | ||
| 494 | int sps; | ||
| 495 | |||
| 496 | ret = ade7754_spi_read_reg_8(dev, ADE7754_WAVMODE, &t); | ||
| 497 | if (ret) | ||
| 498 | return ret; | ||
| 499 | |||
| 500 | t = (t >> 3) & 0x3; | ||
| 501 | sps = 26000 / (1 + t); | ||
| 502 | |||
| 503 | return sprintf(buf, "%d\n", sps); | ||
| 504 | } | ||
| 505 | |||
| 506 | static ssize_t ade7754_write_frequency(struct device *dev, | ||
| 507 | struct device_attribute *attr, | ||
| 508 | const char *buf, | ||
| 509 | size_t len) | ||
| 510 | { | ||
| 511 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 512 | struct ade7754_state *st = iio_priv(indio_dev); | ||
| 513 | u16 val; | ||
| 514 | int ret; | ||
| 515 | u8 reg, t; | ||
| 516 | |||
| 517 | ret = kstrtou16(buf, 10, &val); | ||
| 518 | if (ret) | ||
| 519 | return ret; | ||
| 520 | if (!val) | ||
| 521 | return -EINVAL; | ||
| 522 | |||
| 523 | mutex_lock(&st->buf_lock); | ||
| 524 | |||
| 525 | t = 26000 / val; | ||
| 526 | if (t > 0) | ||
| 527 | t--; | ||
| 528 | |||
| 529 | if (t > 1) | ||
| 530 | st->us->max_speed_hz = ADE7754_SPI_SLOW; | ||
| 531 | else | ||
| 532 | st->us->max_speed_hz = ADE7754_SPI_FAST; | ||
| 533 | |||
| 534 | ret = ade7754_spi_read_reg_8(dev, ADE7754_WAVMODE, ®); | ||
| 535 | if (ret) | ||
| 536 | goto out; | ||
| 537 | |||
| 538 | reg &= ~(3 << 3); | ||
| 539 | reg |= t << 3; | ||
| 540 | |||
| 541 | ret = __ade7754_spi_write_reg_8(dev, ADE7754_WAVMODE, reg); | ||
| 542 | |||
| 543 | out: | ||
| 544 | mutex_unlock(&st->buf_lock); | ||
| 545 | |||
| 546 | return ret ? ret : len; | ||
| 547 | } | ||
| 548 | static IIO_DEV_ATTR_TEMP_RAW(ade7754_read_8bit); | ||
| 549 | static IIO_CONST_ATTR(in_temp_offset, "129 C"); | ||
| 550 | static IIO_CONST_ATTR(in_temp_scale, "4 C"); | ||
| 551 | |||
| 552 | static IIO_DEV_ATTR_SAMP_FREQ(0644, | ||
| 553 | ade7754_read_frequency, | ||
| 554 | ade7754_write_frequency); | ||
| 555 | |||
| 556 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26000 13000 65000 33000"); | ||
| 557 | |||
| 558 | static struct attribute *ade7754_attributes[] = { | ||
| 559 | &iio_dev_attr_in_temp_raw.dev_attr.attr, | ||
| 560 | &iio_const_attr_in_temp_offset.dev_attr.attr, | ||
| 561 | &iio_const_attr_in_temp_scale.dev_attr.attr, | ||
| 562 | &iio_dev_attr_sampling_frequency.dev_attr.attr, | ||
| 563 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, | ||
| 564 | &iio_dev_attr_aenergy.dev_attr.attr, | ||
| 565 | &iio_dev_attr_laenergy.dev_attr.attr, | ||
| 566 | &iio_dev_attr_vaenergy.dev_attr.attr, | ||
| 567 | &iio_dev_attr_lvaenergy.dev_attr.attr, | ||
| 568 | &iio_dev_attr_vpeak.dev_attr.attr, | ||
| 569 | &iio_dev_attr_ipeak.dev_attr.attr, | ||
| 570 | &iio_dev_attr_aphcal.dev_attr.attr, | ||
| 571 | &iio_dev_attr_bphcal.dev_attr.attr, | ||
| 572 | &iio_dev_attr_cphcal.dev_attr.attr, | ||
| 573 | &iio_dev_attr_aapos.dev_attr.attr, | ||
| 574 | &iio_dev_attr_bapos.dev_attr.attr, | ||
| 575 | &iio_dev_attr_capos.dev_attr.attr, | ||
| 576 | &iio_dev_attr_wdiv.dev_attr.attr, | ||
| 577 | &iio_dev_attr_vadiv.dev_attr.attr, | ||
| 578 | &iio_dev_attr_cfnum.dev_attr.attr, | ||
| 579 | &iio_dev_attr_cfden.dev_attr.attr, | ||
| 580 | &iio_dev_attr_active_power_a_gain.dev_attr.attr, | ||
| 581 | &iio_dev_attr_active_power_b_gain.dev_attr.attr, | ||
| 582 | &iio_dev_attr_active_power_c_gain.dev_attr.attr, | ||
| 583 | &iio_dev_attr_airms.dev_attr.attr, | ||
| 584 | &iio_dev_attr_birms.dev_attr.attr, | ||
| 585 | &iio_dev_attr_cirms.dev_attr.attr, | ||
| 586 | &iio_dev_attr_avrms.dev_attr.attr, | ||
| 587 | &iio_dev_attr_bvrms.dev_attr.attr, | ||
| 588 | &iio_dev_attr_cvrms.dev_attr.attr, | ||
| 589 | &iio_dev_attr_airmsos.dev_attr.attr, | ||
| 590 | &iio_dev_attr_birmsos.dev_attr.attr, | ||
| 591 | &iio_dev_attr_cirmsos.dev_attr.attr, | ||
| 592 | &iio_dev_attr_avrmsos.dev_attr.attr, | ||
| 593 | &iio_dev_attr_bvrmsos.dev_attr.attr, | ||
| 594 | &iio_dev_attr_cvrmsos.dev_attr.attr, | ||
| 595 | NULL, | ||
| 596 | }; | ||
| 597 | |||
| 598 | static const struct attribute_group ade7754_attribute_group = { | ||
| 599 | .attrs = ade7754_attributes, | ||
| 600 | }; | ||
| 601 | |||
| 602 | static const struct iio_info ade7754_info = { | ||
| 603 | .attrs = &ade7754_attribute_group, | ||
| 604 | }; | ||
| 605 | |||
| 606 | static int ade7754_probe(struct spi_device *spi) | ||
| 607 | { | ||
| 608 | int ret; | ||
| 609 | struct ade7754_state *st; | ||
| 610 | struct iio_dev *indio_dev; | ||
| 611 | |||
| 612 | /* setup the industrialio driver allocated elements */ | ||
| 613 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | ||
| 614 | if (!indio_dev) | ||
| 615 | return -ENOMEM; | ||
| 616 | /* this is only used for removal purposes */ | ||
| 617 | spi_set_drvdata(spi, indio_dev); | ||
| 618 | |||
| 619 | st = iio_priv(indio_dev); | ||
| 620 | st->us = spi; | ||
| 621 | mutex_init(&st->buf_lock); | ||
| 622 | |||
| 623 | indio_dev->name = spi->dev.driver->name; | ||
| 624 | indio_dev->dev.parent = &spi->dev; | ||
| 625 | indio_dev->info = &ade7754_info; | ||
| 626 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 627 | |||
| 628 | /* Get the device into a sane initial state */ | ||
| 629 | ret = ade7754_initial_setup(indio_dev); | ||
| 630 | if (ret) | ||
| 631 | goto powerdown_on_error; | ||
| 632 | ret = iio_device_register(indio_dev); | ||
| 633 | if (ret) | ||
| 634 | goto powerdown_on_error; | ||
| 635 | return ret; | ||
| 636 | |||
| 637 | powerdown_on_error: | ||
| 638 | ade7754_stop_device(&indio_dev->dev); | ||
| 639 | return ret; | ||
| 640 | } | ||
| 641 | |||
| 642 | static int ade7754_remove(struct spi_device *spi) | ||
| 643 | { | ||
| 644 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
| 645 | |||
| 646 | iio_device_unregister(indio_dev); | ||
| 647 | ade7754_stop_device(&indio_dev->dev); | ||
| 648 | |||
| 649 | return 0; | ||
| 650 | } | ||
| 651 | |||
| 652 | static struct spi_driver ade7754_driver = { | ||
| 653 | .driver = { | ||
| 654 | .name = "ade7754", | ||
| 655 | }, | ||
| 656 | .probe = ade7754_probe, | ||
| 657 | .remove = ade7754_remove, | ||
| 658 | }; | ||
| 659 | module_spi_driver(ade7754_driver); | ||
| 660 | |||
| 661 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | ||
| 662 | MODULE_DESCRIPTION("Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver"); | ||
| 663 | MODULE_LICENSE("GPL v2"); | ||
| 664 | MODULE_ALIAS("spi:ad7754"); | ||
| @@ -1,183 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADE7758 Poly Phase Multifunction Energy Metering IC driver | ||
| 3 | * | ||
| 4 | * Copyright 2010-2011 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef _ADE7758_H | ||
| 10 | #define _ADE7758_H | ||
| 11 | |||
| 12 | #define ADE7758_AWATTHR 0x01 | ||
| 13 | #define ADE7758_BWATTHR 0x02 | ||
| 14 | #define ADE7758_CWATTHR 0x03 | ||
| 15 | #define ADE7758_AVARHR 0x04 | ||
| 16 | #define ADE7758_BVARHR 0x05 | ||
| 17 | #define ADE7758_CVARHR 0x06 | ||
| 18 | #define ADE7758_AVAHR 0x07 | ||
| 19 | #define ADE7758_BVAHR 0x08 | ||
| 20 | #define ADE7758_CVAHR 0x09 | ||
| 21 | #define ADE7758_AIRMS 0x0A | ||
| 22 | #define ADE7758_BIRMS 0x0B | ||
| 23 | #define ADE7758_CIRMS 0x0C | ||
| 24 | #define ADE7758_AVRMS 0x0D | ||
| 25 | #define ADE7758_BVRMS 0x0E | ||
| 26 | #define ADE7758_CVRMS 0x0F | ||
| 27 | #define ADE7758_FREQ 0x10 | ||
| 28 | #define ADE7758_TEMP 0x11 | ||
| 29 | #define ADE7758_WFORM 0x12 | ||
| 30 | #define ADE7758_OPMODE 0x13 | ||
| 31 | #define ADE7758_MMODE 0x14 | ||
| 32 | #define ADE7758_WAVMODE 0x15 | ||
| 33 | #define ADE7758_COMPMODE 0x16 | ||
| 34 | #define ADE7758_LCYCMODE 0x17 | ||
| 35 | #define ADE7758_MASK 0x18 | ||
| 36 | #define ADE7758_STATUS 0x19 | ||
| 37 | #define ADE7758_RSTATUS 0x1A | ||
| 38 | #define ADE7758_ZXTOUT 0x1B | ||
| 39 | #define ADE7758_LINECYC 0x1C | ||
| 40 | #define ADE7758_SAGCYC 0x1D | ||
| 41 | #define ADE7758_SAGLVL 0x1E | ||
| 42 | #define ADE7758_VPINTLVL 0x1F | ||
| 43 | #define ADE7758_IPINTLVL 0x20 | ||
| 44 | #define ADE7758_VPEAK 0x21 | ||
| 45 | #define ADE7758_IPEAK 0x22 | ||
| 46 | #define ADE7758_GAIN 0x23 | ||
| 47 | #define ADE7758_AVRMSGAIN 0x24 | ||
| 48 | #define ADE7758_BVRMSGAIN 0x25 | ||
| 49 | #define ADE7758_CVRMSGAIN 0x26 | ||
| 50 | #define ADE7758_AIGAIN 0x27 | ||
| 51 | #define ADE7758_BIGAIN 0x28 | ||
| 52 | #define ADE7758_CIGAIN 0x29 | ||
| 53 | #define ADE7758_AWG 0x2A | ||
| 54 | #define ADE7758_BWG 0x2B | ||
| 55 | #define ADE7758_CWG 0x2C | ||
| 56 | #define ADE7758_AVARG 0x2D | ||
| 57 | #define ADE7758_BVARG 0x2E | ||
| 58 | #define ADE7758_CVARG 0x2F | ||
| 59 | #define ADE7758_AVAG 0x30 | ||
| 60 | #define ADE7758_BVAG 0x31 | ||
| 61 | #define ADE7758_CVAG 0x32 | ||
| 62 | #define ADE7758_AVRMSOS 0x33 | ||
| 63 | #define ADE7758_BVRMSOS 0x34 | ||
| 64 | #define ADE7758_CVRMSOS 0x35 | ||
| 65 | #define ADE7758_AIRMSOS 0x36 | ||
| 66 | #define ADE7758_BIRMSOS 0x37 | ||
| 67 | #define ADE7758_CIRMSOS 0x38 | ||
| 68 | #define ADE7758_AWAITOS 0x39 | ||
| 69 | #define ADE7758_BWAITOS 0x3A | ||
| 70 | #define ADE7758_CWAITOS 0x3B | ||
| 71 | #define ADE7758_AVAROS 0x3C | ||
| 72 | #define ADE7758_BVAROS 0x3D | ||
| 73 | #define ADE7758_CVAROS 0x3E | ||
| 74 | #define ADE7758_APHCAL 0x3F | ||
| 75 | #define ADE7758_BPHCAL 0x40 | ||
| 76 | #define ADE7758_CPHCAL 0x41 | ||
| 77 | #define ADE7758_WDIV 0x42 | ||
| 78 | #define ADE7758_VADIV 0x44 | ||
| 79 | #define ADE7758_VARDIV 0x43 | ||
| 80 | #define ADE7758_APCFNUM 0x45 | ||
| 81 | #define ADE7758_APCFDEN 0x46 | ||
| 82 | #define ADE7758_VARCFNUM 0x47 | ||
| 83 | #define ADE7758_VARCFDEN 0x48 | ||
| 84 | #define ADE7758_CHKSUM 0x7E | ||
| 85 | #define ADE7758_VERSION 0x7F | ||
| 86 | |||
| 87 | #define ADE7758_READ_REG(a) a | ||
| 88 | #define ADE7758_WRITE_REG(a) ((a) | 0x80) | ||
| 89 | |||
| 90 | #define ADE7758_MAX_TX 8 | ||
| 91 | #define ADE7758_MAX_RX 4 | ||
| 92 | #define ADE7758_STARTUP_DELAY 1000 | ||
| 93 | |||
| 94 | #define AD7758_NUM_WAVSEL 5 | ||
| 95 | #define AD7758_NUM_PHSEL 3 | ||
| 96 | #define AD7758_NUM_WAVESRC (AD7758_NUM_WAVSEL * AD7758_NUM_PHSEL) | ||
| 97 | |||
| 98 | #define AD7758_PHASE_A 0 | ||
| 99 | #define AD7758_PHASE_B 1 | ||
| 100 | #define AD7758_PHASE_C 2 | ||
| 101 | #define AD7758_CURRENT 0 | ||
| 102 | #define AD7758_VOLTAGE 1 | ||
| 103 | #define AD7758_ACT_PWR 2 | ||
| 104 | #define AD7758_REACT_PWR 3 | ||
| 105 | #define AD7758_APP_PWR 4 | ||
| 106 | #define AD7758_WT(p, w) (((w) << 2) | (p)) | ||
| 107 | |||
| 108 | /** | ||
| 109 | * struct ade7758_state - device instance specific data | ||
| 110 | * @us: actual spi_device | ||
| 111 | * @trig: data ready trigger registered with iio | ||
| 112 | * @tx: transmit buffer | ||
| 113 | * @rx: receive buffer | ||
| 114 | * @buf_lock: mutex to protect tx, rx, read and write frequency | ||
| 115 | **/ | ||
| 116 | struct ade7758_state { | ||
| 117 | struct spi_device *us; | ||
| 118 | struct iio_trigger *trig; | ||
| 119 | u8 *tx; | ||
| 120 | u8 *rx; | ||
| 121 | struct mutex buf_lock; | ||
| 122 | struct spi_transfer ring_xfer[4]; | ||
| 123 | struct spi_message ring_msg; | ||
| 124 | /* | ||
| 125 | * DMA (thus cache coherency maintenance) requires the | ||
| 126 | * transfer buffers to live in their own cache lines. | ||
| 127 | */ | ||
| 128 | unsigned char rx_buf[8] ____cacheline_aligned; | ||
| 129 | unsigned char tx_buf[8]; | ||
| 130 | |||
| 131 | }; | ||
| 132 | |||
| 133 | #ifdef CONFIG_IIO_BUFFER | ||
| 134 | /* At the moment triggers are only used for ring buffer | ||
| 135 | * filling. This may change! | ||
| 136 | */ | ||
| 137 | |||
| 138 | void ade7758_remove_trigger(struct iio_dev *indio_dev); | ||
| 139 | int ade7758_probe_trigger(struct iio_dev *indio_dev); | ||
| 140 | |||
| 141 | ssize_t ade7758_read_data_from_ring(struct device *dev, | ||
| 142 | struct device_attribute *attr, char *buf); | ||
| 143 | |||
| 144 | int ade7758_configure_ring(struct iio_dev *indio_dev); | ||
| 145 | void ade7758_unconfigure_ring(struct iio_dev *indio_dev); | ||
| 146 | |||
| 147 | int ade7758_set_irq(struct device *dev, bool enable); | ||
| 148 | |||
| 149 | int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val); | ||
| 150 | int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val); | ||
| 151 | |||
| 152 | #else /* CONFIG_IIO_BUFFER */ | ||
| 153 | |||
| 154 | static inline void ade7758_remove_trigger(struct iio_dev *indio_dev) | ||
| 155 | { | ||
| 156 | } | ||
| 157 | |||
| 158 | static inline int ade7758_probe_trigger(struct iio_dev *indio_dev) | ||
| 159 | { | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int ade7758_configure_ring(struct iio_dev *indio_dev) | ||
| 164 | { | ||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static inline void ade7758_unconfigure_ring(struct iio_dev *indio_dev) | ||
| 169 | { | ||
| 170 | } | ||
| 171 | |||
| 172 | static inline int ade7758_initialize_ring(struct iio_ring_buffer *ring) | ||
| 173 | { | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | static inline void ade7758_uninitialize_ring(struct iio_dev *indio_dev) | ||
| 178 | { | ||
| 179 | } | ||
| 180 | |||
| 181 | #endif /* CONFIG_IIO_BUFFER */ | ||
| 182 | |||
| 183 | #endif | ||
| @@ -1,955 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADE7758 Poly Phase Multifunction Energy Metering IC driver | ||
| 3 | * | ||
| 4 | * Copyright 2010-2011 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/interrupt.h> | ||
| 10 | #include <linux/irq.h> | ||
| 11 | #include <linux/delay.h> | ||
| 12 | #include <linux/mutex.h> | ||
| 13 | #include <linux/device.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/spi/spi.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/sysfs.h> | ||
| 18 | #include <linux/list.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | |||
| 21 | #include <linux/iio/iio.h> | ||
| 22 | #include <linux/iio/sysfs.h> | ||
| 23 | #include <linux/iio/buffer.h> | ||
| 24 | #include "meter.h" | ||
| 25 | #include "ade7758.h" | ||
| 26 | |||
| 27 | static int __ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) | ||
| 28 | { | ||
| 29 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 30 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 31 | |||
| 32 | st->tx[0] = ADE7758_WRITE_REG(reg_address); | ||
| 33 | st->tx[1] = val; | ||
| 34 | |||
| 35 | return spi_write(st->us, st->tx, 2); | ||
| 36 | } | ||
| 37 | |||
| 38 | int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) | ||
| 39 | { | ||
| 40 | int ret; | ||
| 41 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 42 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 43 | |||
| 44 | mutex_lock(&st->buf_lock); | ||
| 45 | ret = __ade7758_spi_write_reg_8(dev, reg_address, val); | ||
| 46 | mutex_unlock(&st->buf_lock); | ||
| 47 | |||
| 48 | return ret; | ||
| 49 | } | ||
| 50 | |||
| 51 | static int ade7758_spi_write_reg_16(struct device *dev, u8 reg_address, | ||
| 52 | u16 value) | ||
| 53 | { | ||
| 54 | int ret; | ||
| 55 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 56 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 57 | struct spi_transfer xfers[] = { | ||
| 58 | { | ||
| 59 | .tx_buf = st->tx, | ||
| 60 | .bits_per_word = 8, | ||
| 61 | .len = 3, | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | mutex_lock(&st->buf_lock); | ||
| 66 | st->tx[0] = ADE7758_WRITE_REG(reg_address); | ||
| 67 | st->tx[1] = (value >> 8) & 0xFF; | ||
| 68 | st->tx[2] = value & 0xFF; | ||
| 69 | |||
| 70 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 71 | mutex_unlock(&st->buf_lock); | ||
| 72 | |||
| 73 | return ret; | ||
| 74 | } | ||
| 75 | |||
| 76 | static int ade7758_spi_write_reg_24(struct device *dev, u8 reg_address, | ||
| 77 | u32 value) | ||
| 78 | { | ||
| 79 | int ret; | ||
| 80 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 81 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 82 | struct spi_transfer xfers[] = { | ||
| 83 | { | ||
| 84 | .tx_buf = st->tx, | ||
| 85 | .bits_per_word = 8, | ||
| 86 | .len = 4, | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | mutex_lock(&st->buf_lock); | ||
| 91 | st->tx[0] = ADE7758_WRITE_REG(reg_address); | ||
| 92 | st->tx[1] = (value >> 16) & 0xFF; | ||
| 93 | st->tx[2] = (value >> 8) & 0xFF; | ||
| 94 | st->tx[3] = value & 0xFF; | ||
| 95 | |||
| 96 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 97 | mutex_unlock(&st->buf_lock); | ||
| 98 | |||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | |||
| 102 | static int __ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val) | ||
| 103 | { | ||
| 104 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 105 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 106 | int ret; | ||
| 107 | struct spi_transfer xfers[] = { | ||
| 108 | { | ||
| 109 | .tx_buf = st->tx, | ||
| 110 | .bits_per_word = 8, | ||
| 111 | .len = 1, | ||
| 112 | .delay_usecs = 4, | ||
| 113 | }, | ||
| 114 | { | ||
| 115 | .tx_buf = &st->tx[1], | ||
| 116 | .rx_buf = st->rx, | ||
| 117 | .bits_per_word = 8, | ||
| 118 | .len = 1, | ||
| 119 | }, | ||
| 120 | }; | ||
| 121 | |||
| 122 | st->tx[0] = ADE7758_READ_REG(reg_address); | ||
| 123 | st->tx[1] = 0; | ||
| 124 | |||
| 125 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 126 | if (ret) { | ||
| 127 | dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X", | ||
| 128 | reg_address); | ||
| 129 | goto error_ret; | ||
| 130 | } | ||
| 131 | *val = st->rx[0]; | ||
| 132 | |||
| 133 | error_ret: | ||
| 134 | return ret; | ||
| 135 | } | ||
| 136 | |||
| 137 | int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val) | ||
| 138 | { | ||
| 139 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 140 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 141 | int ret; | ||
| 142 | |||
| 143 | mutex_lock(&st->buf_lock); | ||
| 144 | ret = __ade7758_spi_read_reg_8(dev, reg_address, val); | ||
| 145 | mutex_unlock(&st->buf_lock); | ||
| 146 | |||
| 147 | return ret; | ||
| 148 | } | ||
| 149 | |||
| 150 | static int ade7758_spi_read_reg_16(struct device *dev, u8 reg_address, | ||
| 151 | u16 *val) | ||
| 152 | { | ||
| 153 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 154 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 155 | int ret; | ||
| 156 | struct spi_transfer xfers[] = { | ||
| 157 | { | ||
| 158 | .tx_buf = st->tx, | ||
| 159 | .bits_per_word = 8, | ||
| 160 | .len = 1, | ||
| 161 | .delay_usecs = 4, | ||
| 162 | }, | ||
| 163 | { | ||
| 164 | .tx_buf = &st->tx[1], | ||
| 165 | .rx_buf = st->rx, | ||
| 166 | .bits_per_word = 8, | ||
| 167 | .len = 2, | ||
| 168 | }, | ||
| 169 | }; | ||
| 170 | |||
| 171 | mutex_lock(&st->buf_lock); | ||
| 172 | st->tx[0] = ADE7758_READ_REG(reg_address); | ||
| 173 | st->tx[1] = 0; | ||
| 174 | st->tx[2] = 0; | ||
| 175 | |||
| 176 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 177 | if (ret) { | ||
| 178 | dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", | ||
| 179 | reg_address); | ||
| 180 | goto error_ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | *val = (st->rx[0] << 8) | st->rx[1]; | ||
| 184 | |||
| 185 | error_ret: | ||
| 186 | mutex_unlock(&st->buf_lock); | ||
| 187 | return ret; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int ade7758_spi_read_reg_24(struct device *dev, u8 reg_address, | ||
| 191 | u32 *val) | ||
| 192 | { | ||
| 193 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 194 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 195 | int ret; | ||
| 196 | struct spi_transfer xfers[] = { | ||
| 197 | { | ||
| 198 | .tx_buf = st->tx, | ||
| 199 | .bits_per_word = 8, | ||
| 200 | .len = 1, | ||
| 201 | .delay_usecs = 4, | ||
| 202 | }, | ||
| 203 | { | ||
| 204 | .tx_buf = &st->tx[1], | ||
| 205 | .rx_buf = st->rx, | ||
| 206 | .bits_per_word = 8, | ||
| 207 | .len = 3, | ||
| 208 | }, | ||
| 209 | }; | ||
| 210 | |||
| 211 | mutex_lock(&st->buf_lock); | ||
| 212 | st->tx[0] = ADE7758_READ_REG(reg_address); | ||
| 213 | st->tx[1] = 0; | ||
| 214 | st->tx[2] = 0; | ||
| 215 | st->tx[3] = 0; | ||
| 216 | |||
| 217 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 218 | if (ret) { | ||
| 219 | dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X", | ||
| 220 | reg_address); | ||
| 221 | goto error_ret; | ||
| 222 | } | ||
| 223 | *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; | ||
| 224 | |||
| 225 | error_ret: | ||
| 226 | mutex_unlock(&st->buf_lock); | ||
| 227 | return ret; | ||
| 228 | } | ||
| 229 | |||
| 230 | static ssize_t ade7758_read_8bit(struct device *dev, | ||
| 231 | struct device_attribute *attr, char *buf) | ||
| 232 | { | ||
| 233 | int ret; | ||
| 234 | u8 val = 0; | ||
| 235 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 236 | |||
| 237 | ret = ade7758_spi_read_reg_8(dev, this_attr->address, &val); | ||
| 238 | if (ret) | ||
| 239 | return ret; | ||
| 240 | |||
| 241 | return sprintf(buf, "%u\n", val); | ||
| 242 | } | ||
| 243 | |||
| 244 | static ssize_t ade7758_read_16bit(struct device *dev, | ||
| 245 | struct device_attribute *attr, char *buf) | ||
| 246 | { | ||
| 247 | int ret; | ||
| 248 | u16 val = 0; | ||
| 249 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 250 | |||
| 251 | ret = ade7758_spi_read_reg_16(dev, this_attr->address, &val); | ||
| 252 | if (ret) | ||
| 253 | return ret; | ||
| 254 | |||
| 255 | return sprintf(buf, "%u\n", val); | ||
| 256 | } | ||
| 257 | |||
| 258 | static ssize_t ade7758_read_24bit(struct device *dev, | ||
| 259 | struct device_attribute *attr, char *buf) | ||
| 260 | { | ||
| 261 | int ret; | ||
| 262 | u32 val = 0; | ||
| 263 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 264 | |||
| 265 | ret = ade7758_spi_read_reg_24(dev, this_attr->address, &val); | ||
| 266 | if (ret) | ||
| 267 | return ret; | ||
| 268 | |||
| 269 | return sprintf(buf, "%u\n", val & 0xFFFFFF); | ||
| 270 | } | ||
| 271 | |||
| 272 | static ssize_t ade7758_write_8bit(struct device *dev, | ||
| 273 | struct device_attribute *attr, | ||
| 274 | const char *buf, size_t len) | ||
| 275 | { | ||
| 276 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 277 | int ret; | ||
| 278 | u8 val; | ||
| 279 | |||
| 280 | ret = kstrtou8(buf, 10, &val); | ||
| 281 | if (ret) | ||
| 282 | goto error_ret; | ||
| 283 | ret = ade7758_spi_write_reg_8(dev, this_attr->address, val); | ||
| 284 | |||
| 285 | error_ret: | ||
| 286 | return ret ? ret : len; | ||
| 287 | } | ||
| 288 | |||
| 289 | static ssize_t ade7758_write_16bit(struct device *dev, | ||
| 290 | struct device_attribute *attr, | ||
| 291 | const char *buf, size_t len) | ||
| 292 | { | ||
| 293 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 294 | int ret; | ||
| 295 | u16 val; | ||
| 296 | |||
| 297 | ret = kstrtou16(buf, 10, &val); | ||
| 298 | if (ret) | ||
| 299 | goto error_ret; | ||
| 300 | ret = ade7758_spi_write_reg_16(dev, this_attr->address, val); | ||
| 301 | |||
| 302 | error_ret: | ||
| 303 | return ret ? ret : len; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int ade7758_reset(struct device *dev) | ||
| 307 | { | ||
| 308 | int ret; | ||
| 309 | u8 val; | ||
| 310 | |||
| 311 | ret = ade7758_spi_read_reg_8(dev, ADE7758_OPMODE, &val); | ||
| 312 | if (ret < 0) { | ||
| 313 | dev_err(dev, "Failed to read opmode reg\n"); | ||
| 314 | return ret; | ||
| 315 | } | ||
| 316 | val |= BIT(6); /* Software Chip Reset */ | ||
| 317 | ret = ade7758_spi_write_reg_8(dev, ADE7758_OPMODE, val); | ||
| 318 | if (ret < 0) | ||
| 319 | dev_err(dev, "Failed to write opmode reg\n"); | ||
| 320 | return ret; | ||
| 321 | } | ||
| 322 | |||
| 323 | static IIO_DEV_ATTR_VPEAK(0644, | ||
| 324 | ade7758_read_8bit, | ||
| 325 | ade7758_write_8bit, | ||
| 326 | ADE7758_VPEAK); | ||
| 327 | static IIO_DEV_ATTR_IPEAK(0644, | ||
| 328 | ade7758_read_8bit, | ||
| 329 | ade7758_write_8bit, | ||
| 330 | ADE7758_VPEAK); | ||
| 331 | static IIO_DEV_ATTR_APHCAL(0644, | ||
| 332 | ade7758_read_8bit, | ||
| 333 | ade7758_write_8bit, | ||
| 334 | ADE7758_APHCAL); | ||
| 335 | static IIO_DEV_ATTR_BPHCAL(0644, | ||
| 336 | ade7758_read_8bit, | ||
| 337 | ade7758_write_8bit, | ||
| 338 | ADE7758_BPHCAL); | ||
| 339 | static IIO_DEV_ATTR_CPHCAL(0644, | ||
| 340 | ade7758_read_8bit, | ||
| 341 | ade7758_write_8bit, | ||
| 342 | ADE7758_CPHCAL); | ||
| 343 | static IIO_DEV_ATTR_WDIV(0644, | ||
| 344 | ade7758_read_8bit, | ||
| 345 | ade7758_write_8bit, | ||
| 346 | ADE7758_WDIV); | ||
| 347 | static IIO_DEV_ATTR_VADIV(0644, | ||
| 348 | ade7758_read_8bit, | ||
| 349 | ade7758_write_8bit, | ||
| 350 | ADE7758_VADIV); | ||
| 351 | static IIO_DEV_ATTR_AIRMS(0444, | ||
| 352 | ade7758_read_24bit, | ||
| 353 | NULL, | ||
| 354 | ADE7758_AIRMS); | ||
| 355 | static IIO_DEV_ATTR_BIRMS(0444, | ||
| 356 | ade7758_read_24bit, | ||
| 357 | NULL, | ||
| 358 | ADE7758_BIRMS); | ||
| 359 | static IIO_DEV_ATTR_CIRMS(0444, | ||
| 360 | ade7758_read_24bit, | ||
| 361 | NULL, | ||
| 362 | ADE7758_CIRMS); | ||
| 363 | static IIO_DEV_ATTR_AVRMS(0444, | ||
| 364 | ade7758_read_24bit, | ||
| 365 | NULL, | ||
| 366 | ADE7758_AVRMS); | ||
| 367 | static IIO_DEV_ATTR_BVRMS(0444, | ||
| 368 | ade7758_read_24bit, | ||
| 369 | NULL, | ||
| 370 | ADE7758_BVRMS); | ||
| 371 | static IIO_DEV_ATTR_CVRMS(0444, | ||
| 372 | ade7758_read_24bit, | ||
| 373 | NULL, | ||
| 374 | ADE7758_CVRMS); | ||
| 375 | static IIO_DEV_ATTR_AIRMSOS(0644, | ||
| 376 | ade7758_read_16bit, | ||
| 377 | ade7758_write_16bit, | ||
| 378 | ADE7758_AIRMSOS); | ||
| 379 | static IIO_DEV_ATTR_BIRMSOS(0644, | ||
| 380 | ade7758_read_16bit, | ||
| 381 | ade7758_write_16bit, | ||
| 382 | ADE7758_BIRMSOS); | ||
| 383 | static IIO_DEV_ATTR_CIRMSOS(0644, | ||
| 384 | ade7758_read_16bit, | ||
| 385 | ade7758_write_16bit, | ||
| 386 | ADE7758_CIRMSOS); | ||
| 387 | static IIO_DEV_ATTR_AVRMSOS(0644, | ||
| 388 | ade7758_read_16bit, | ||
| 389 | ade7758_write_16bit, | ||
| 390 | ADE7758_AVRMSOS); | ||
| 391 | static IIO_DEV_ATTR_BVRMSOS(0644, | ||
| 392 | ade7758_read_16bit, | ||
| 393 | ade7758_write_16bit, | ||
| 394 | ADE7758_BVRMSOS); | ||
| 395 | static IIO_DEV_ATTR_CVRMSOS(0644, | ||
| 396 | ade7758_read_16bit, | ||
| 397 | ade7758_write_16bit, | ||
| 398 | ADE7758_CVRMSOS); | ||
| 399 | static IIO_DEV_ATTR_AIGAIN(0644, | ||
| 400 | ade7758_read_16bit, | ||
| 401 | ade7758_write_16bit, | ||
| 402 | ADE7758_AIGAIN); | ||
| 403 | static IIO_DEV_ATTR_BIGAIN(0644, | ||
| 404 | ade7758_read_16bit, | ||
| 405 | ade7758_write_16bit, | ||
| 406 | ADE7758_BIGAIN); | ||
| 407 | static IIO_DEV_ATTR_CIGAIN(0644, | ||
| 408 | ade7758_read_16bit, | ||
| 409 | ade7758_write_16bit, | ||
| 410 | ADE7758_CIGAIN); | ||
| 411 | static IIO_DEV_ATTR_AVRMSGAIN(0644, | ||
| 412 | ade7758_read_16bit, | ||
| 413 | ade7758_write_16bit, | ||
| 414 | ADE7758_AVRMSGAIN); | ||
| 415 | static IIO_DEV_ATTR_BVRMSGAIN(0644, | ||
| 416 | ade7758_read_16bit, | ||
| 417 | ade7758_write_16bit, | ||
| 418 | ADE7758_BVRMSGAIN); | ||
| 419 | static IIO_DEV_ATTR_CVRMSGAIN(0644, | ||
| 420 | ade7758_read_16bit, | ||
| 421 | ade7758_write_16bit, | ||
| 422 | ADE7758_CVRMSGAIN); | ||
| 423 | |||
| 424 | int ade7758_set_irq(struct device *dev, bool enable) | ||
| 425 | { | ||
| 426 | int ret; | ||
| 427 | u32 irqen; | ||
| 428 | |||
| 429 | ret = ade7758_spi_read_reg_24(dev, ADE7758_MASK, &irqen); | ||
| 430 | if (ret) | ||
| 431 | return ret; | ||
| 432 | |||
| 433 | if (enable) | ||
| 434 | irqen |= BIT(16); /* Enables an interrupt when a data is | ||
| 435 | * present in the waveform register | ||
| 436 | */ | ||
| 437 | else | ||
| 438 | irqen &= ~BIT(16); | ||
| 439 | |||
| 440 | ret = ade7758_spi_write_reg_24(dev, ADE7758_MASK, irqen); | ||
| 441 | |||
| 442 | return ret; | ||
| 443 | } | ||
| 444 | |||
| 445 | /* Power down the device */ | ||
| 446 | static int ade7758_stop_device(struct device *dev) | ||
| 447 | { | ||
| 448 | int ret; | ||
| 449 | u8 val; | ||
| 450 | |||
| 451 | ret = ade7758_spi_read_reg_8(dev, ADE7758_OPMODE, &val); | ||
| 452 | if (ret < 0) { | ||
| 453 | dev_err(dev, "Failed to read opmode reg\n"); | ||
| 454 | return ret; | ||
| 455 | } | ||
| 456 | val |= 7 << 3; /* ADE7758 powered down */ | ||
| 457 | ret = ade7758_spi_write_reg_8(dev, ADE7758_OPMODE, val); | ||
| 458 | if (ret < 0) | ||
| 459 | dev_err(dev, "Failed to write opmode reg\n"); | ||
| 460 | return ret; | ||
| 461 | } | ||
| 462 | |||
| 463 | static int ade7758_initial_setup(struct iio_dev *indio_dev) | ||
| 464 | { | ||
| 465 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 466 | struct device *dev = &indio_dev->dev; | ||
| 467 | int ret; | ||
| 468 | |||
| 469 | /* use low spi speed for init */ | ||
| 470 | st->us->mode = SPI_MODE_1; | ||
| 471 | spi_setup(st->us); | ||
| 472 | |||
| 473 | /* Disable IRQ */ | ||
| 474 | ret = ade7758_set_irq(dev, false); | ||
| 475 | if (ret) { | ||
| 476 | dev_err(dev, "disable irq failed"); | ||
| 477 | goto err_ret; | ||
| 478 | } | ||
| 479 | |||
| 480 | ade7758_reset(dev); | ||
| 481 | usleep_range(ADE7758_STARTUP_DELAY, ADE7758_STARTUP_DELAY + 100); | ||
| 482 | |||
| 483 | err_ret: | ||
| 484 | return ret; | ||
| 485 | } | ||
| 486 | |||
| 487 | static int ade7758_read_samp_freq(struct device *dev, int *val) | ||
| 488 | { | ||
| 489 | int ret; | ||
| 490 | u8 t; | ||
| 491 | |||
| 492 | ret = ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, &t); | ||
| 493 | if (ret) | ||
| 494 | return ret; | ||
| 495 | |||
| 496 | t = (t >> 5) & 0x3; | ||
| 497 | *val = 26040 / (1 << t); | ||
| 498 | |||
| 499 | return 0; | ||
| 500 | } | ||
| 501 | |||
| 502 | static int ade7758_write_samp_freq(struct device *dev, int val) | ||
| 503 | { | ||
| 504 | int ret; | ||
| 505 | u8 reg, t; | ||
| 506 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 507 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 508 | |||
| 509 | switch (val) { | ||
| 510 | case 26040: | ||
| 511 | t = 0; | ||
| 512 | break; | ||
| 513 | case 13020: | ||
| 514 | t = 1; | ||
| 515 | break; | ||
| 516 | case 6510: | ||
| 517 | t = 2; | ||
| 518 | break; | ||
| 519 | case 3255: | ||
| 520 | t = 3; | ||
| 521 | break; | ||
| 522 | default: | ||
| 523 | return -EINVAL; | ||
| 524 | } | ||
| 525 | |||
| 526 | mutex_lock(&st->buf_lock); | ||
| 527 | |||
| 528 | ret = __ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, ®); | ||
| 529 | if (ret) | ||
| 530 | goto out; | ||
| 531 | |||
| 532 | reg &= ~(5 << 3); | ||
| 533 | reg |= t << 5; | ||
| 534 | |||
| 535 | ret = __ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg); | ||
| 536 | |||
| 537 | out: | ||
| 538 | mutex_unlock(&st->buf_lock); | ||
| 539 | |||
| 540 | return ret; | ||
| 541 | } | ||
| 542 | |||
| 543 | static int ade7758_read_raw(struct iio_dev *indio_dev, | ||
| 544 | struct iio_chan_spec const *chan, | ||
| 545 | int *val, | ||
| 546 | int *val2, | ||
| 547 | long mask) | ||
| 548 | { | ||
| 549 | int ret; | ||
| 550 | |||
| 551 | switch (mask) { | ||
| 552 | case IIO_CHAN_INFO_SAMP_FREQ: | ||
| 553 | |||
| 554 | ret = ade7758_read_samp_freq(&indio_dev->dev, val); | ||
| 555 | |||
| 556 | return ret; | ||
| 557 | default: | ||
| 558 | return -EINVAL; | ||
| 559 | } | ||
| 560 | |||
| 561 | return ret; | ||
| 562 | } | ||
| 563 | |||
| 564 | static int ade7758_write_raw(struct iio_dev *indio_dev, | ||
| 565 | struct iio_chan_spec const *chan, | ||
| 566 | int val, int val2, long mask) | ||
| 567 | { | ||
| 568 | int ret; | ||
| 569 | |||
| 570 | switch (mask) { | ||
| 571 | case IIO_CHAN_INFO_SAMP_FREQ: | ||
| 572 | if (val2) | ||
| 573 | return -EINVAL; | ||
| 574 | |||
| 575 | ret = ade7758_write_samp_freq(&indio_dev->dev, val); | ||
| 576 | |||
| 577 | return ret; | ||
| 578 | default: | ||
| 579 | return -EINVAL; | ||
| 580 | } | ||
| 581 | |||
| 582 | return ret; | ||
| 583 | } | ||
| 584 | |||
| 585 | static IIO_DEV_ATTR_TEMP_RAW(ade7758_read_8bit); | ||
| 586 | static IIO_CONST_ATTR(in_temp_offset, "129 C"); | ||
| 587 | static IIO_CONST_ATTR(in_temp_scale, "4 C"); | ||
| 588 | |||
| 589 | static IIO_DEV_ATTR_AWATTHR(ade7758_read_16bit, | ||
| 590 | ADE7758_AWATTHR); | ||
| 591 | static IIO_DEV_ATTR_BWATTHR(ade7758_read_16bit, | ||
| 592 | ADE7758_BWATTHR); | ||
| 593 | static IIO_DEV_ATTR_CWATTHR(ade7758_read_16bit, | ||
| 594 | ADE7758_CWATTHR); | ||
| 595 | static IIO_DEV_ATTR_AVARHR(ade7758_read_16bit, | ||
| 596 | ADE7758_AVARHR); | ||
| 597 | static IIO_DEV_ATTR_BVARHR(ade7758_read_16bit, | ||
| 598 | ADE7758_BVARHR); | ||
| 599 | static IIO_DEV_ATTR_CVARHR(ade7758_read_16bit, | ||
| 600 | ADE7758_CVARHR); | ||
| 601 | static IIO_DEV_ATTR_AVAHR(ade7758_read_16bit, | ||
| 602 | ADE7758_AVAHR); | ||
| 603 | static IIO_DEV_ATTR_BVAHR(ade7758_read_16bit, | ||
| 604 | ADE7758_BVAHR); | ||
| 605 | static IIO_DEV_ATTR_CVAHR(ade7758_read_16bit, | ||
| 606 | ADE7758_CVAHR); | ||
| 607 | |||
| 608 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26040 13020 6510 3255"); | ||
| 609 | |||
| 610 | static struct attribute *ade7758_attributes[] = { | ||
| 611 | &iio_dev_attr_in_temp_raw.dev_attr.attr, | ||
| 612 | &iio_const_attr_in_temp_offset.dev_attr.attr, | ||
| 613 | &iio_const_attr_in_temp_scale.dev_attr.attr, | ||
| 614 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, | ||
| 615 | &iio_dev_attr_awatthr.dev_attr.attr, | ||
| 616 | &iio_dev_attr_bwatthr.dev_attr.attr, | ||
| 617 | &iio_dev_attr_cwatthr.dev_attr.attr, | ||
| 618 | &iio_dev_attr_avarhr.dev_attr.attr, | ||
| 619 | &iio_dev_attr_bvarhr.dev_attr.attr, | ||
| 620 | &iio_dev_attr_cvarhr.dev_attr.attr, | ||
| 621 | &iio_dev_attr_avahr.dev_attr.attr, | ||
| 622 | &iio_dev_attr_bvahr.dev_attr.attr, | ||
| 623 | &iio_dev_attr_cvahr.dev_attr.attr, | ||
| 624 | &iio_dev_attr_vpeak.dev_attr.attr, | ||
| 625 | &iio_dev_attr_ipeak.dev_attr.attr, | ||
| 626 | &iio_dev_attr_aphcal.dev_attr.attr, | ||
| 627 | &iio_dev_attr_bphcal.dev_attr.attr, | ||
| 628 | &iio_dev_attr_cphcal.dev_attr.attr, | ||
| 629 | &iio_dev_attr_wdiv.dev_attr.attr, | ||
| 630 | &iio_dev_attr_vadiv.dev_attr.attr, | ||
| 631 | &iio_dev_attr_airms.dev_attr.attr, | ||
| 632 | &iio_dev_attr_birms.dev_attr.attr, | ||
| 633 | &iio_dev_attr_cirms.dev_attr.attr, | ||
| 634 | &iio_dev_attr_avrms.dev_attr.attr, | ||
| 635 | &iio_dev_attr_bvrms.dev_attr.attr, | ||
| 636 | &iio_dev_attr_cvrms.dev_attr.attr, | ||
| 637 | &iio_dev_attr_aigain.dev_attr.attr, | ||
| 638 | &iio_dev_attr_bigain.dev_attr.attr, | ||
| 639 | &iio_dev_attr_cigain.dev_attr.attr, | ||
| 640 | &iio_dev_attr_avrmsgain.dev_attr.attr, | ||
| 641 | &iio_dev_attr_bvrmsgain.dev_attr.attr, | ||
| 642 | &iio_dev_attr_cvrmsgain.dev_attr.attr, | ||
| 643 | &iio_dev_attr_airmsos.dev_attr.attr, | ||
| 644 | &iio_dev_attr_birmsos.dev_attr.attr, | ||
| 645 | &iio_dev_attr_cirmsos.dev_attr.attr, | ||
| 646 | &iio_dev_attr_avrmsos.dev_attr.attr, | ||
| 647 | &iio_dev_attr_bvrmsos.dev_attr.attr, | ||
| 648 | &iio_dev_attr_cvrmsos.dev_attr.attr, | ||
| 649 | NULL, | ||
| 650 | }; | ||
| 651 | |||
| 652 | static const struct attribute_group ade7758_attribute_group = { | ||
| 653 | .attrs = ade7758_attributes, | ||
| 654 | }; | ||
| 655 | |||
| 656 | static const struct iio_chan_spec ade7758_channels[] = { | ||
| 657 | { | ||
| 658 | .type = IIO_VOLTAGE, | ||
| 659 | .indexed = 1, | ||
| 660 | .channel = 0, | ||
| 661 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 662 | .address = AD7758_WT(AD7758_PHASE_A, AD7758_VOLTAGE), | ||
| 663 | .scan_index = 0, | ||
| 664 | .scan_type = { | ||
| 665 | .sign = 's', | ||
| 666 | .realbits = 24, | ||
| 667 | .storagebits = 32, | ||
| 668 | }, | ||
| 669 | }, { | ||
| 670 | .type = IIO_CURRENT, | ||
| 671 | .indexed = 1, | ||
| 672 | .channel = 0, | ||
| 673 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 674 | .address = AD7758_WT(AD7758_PHASE_A, AD7758_CURRENT), | ||
| 675 | .scan_index = 1, | ||
| 676 | .scan_type = { | ||
| 677 | .sign = 's', | ||
| 678 | .realbits = 24, | ||
| 679 | .storagebits = 32, | ||
| 680 | }, | ||
| 681 | }, { | ||
| 682 | .type = IIO_POWER, | ||
| 683 | .indexed = 1, | ||
| 684 | .channel = 0, | ||
| 685 | .extend_name = "apparent", | ||
| 686 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 687 | .address = AD7758_WT(AD7758_PHASE_A, AD7758_APP_PWR), | ||
| 688 | .scan_index = 2, | ||
| 689 | .scan_type = { | ||
| 690 | .sign = 's', | ||
| 691 | .realbits = 24, | ||
| 692 | .storagebits = 32, | ||
| 693 | }, | ||
| 694 | }, { | ||
| 695 | .type = IIO_POWER, | ||
| 696 | .indexed = 1, | ||
| 697 | .channel = 0, | ||
| 698 | .extend_name = "active", | ||
| 699 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 700 | .address = AD7758_WT(AD7758_PHASE_A, AD7758_ACT_PWR), | ||
| 701 | .scan_index = 3, | ||
| 702 | .scan_type = { | ||
| 703 | .sign = 's', | ||
| 704 | .realbits = 24, | ||
| 705 | .storagebits = 32, | ||
| 706 | }, | ||
| 707 | }, { | ||
| 708 | .type = IIO_POWER, | ||
| 709 | .indexed = 1, | ||
| 710 | .channel = 0, | ||
| 711 | .extend_name = "reactive", | ||
| 712 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 713 | .address = AD7758_WT(AD7758_PHASE_A, AD7758_REACT_PWR), | ||
| 714 | .scan_index = 4, | ||
| 715 | .scan_type = { | ||
| 716 | .sign = 's', | ||
| 717 | .realbits = 24, | ||
| 718 | .storagebits = 32, | ||
| 719 | }, | ||
| 720 | }, { | ||
| 721 | .type = IIO_VOLTAGE, | ||
| 722 | .indexed = 1, | ||
| 723 | .channel = 1, | ||
| 724 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 725 | .address = AD7758_WT(AD7758_PHASE_B, AD7758_VOLTAGE), | ||
| 726 | .scan_index = 5, | ||
| 727 | .scan_type = { | ||
| 728 | .sign = 's', | ||
| 729 | .realbits = 24, | ||
| 730 | .storagebits = 32, | ||
| 731 | }, | ||
| 732 | }, { | ||
| 733 | .type = IIO_CURRENT, | ||
| 734 | .indexed = 1, | ||
| 735 | .channel = 1, | ||
| 736 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 737 | .address = AD7758_WT(AD7758_PHASE_B, AD7758_CURRENT), | ||
| 738 | .scan_index = 6, | ||
| 739 | .scan_type = { | ||
| 740 | .sign = 's', | ||
| 741 | .realbits = 24, | ||
| 742 | .storagebits = 32, | ||
| 743 | }, | ||
| 744 | }, { | ||
| 745 | .type = IIO_POWER, | ||
| 746 | .indexed = 1, | ||
| 747 | .channel = 1, | ||
| 748 | .extend_name = "apparent", | ||
| 749 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 750 | .address = AD7758_WT(AD7758_PHASE_B, AD7758_APP_PWR), | ||
| 751 | .scan_index = 7, | ||
| 752 | .scan_type = { | ||
| 753 | .sign = 's', | ||
| 754 | .realbits = 24, | ||
| 755 | .storagebits = 32, | ||
| 756 | }, | ||
| 757 | }, { | ||
| 758 | .type = IIO_POWER, | ||
| 759 | .indexed = 1, | ||
| 760 | .channel = 1, | ||
| 761 | .extend_name = "active", | ||
| 762 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 763 | .address = AD7758_WT(AD7758_PHASE_B, AD7758_ACT_PWR), | ||
| 764 | .scan_index = 8, | ||
| 765 | .scan_type = { | ||
| 766 | .sign = 's', | ||
| 767 | .realbits = 24, | ||
| 768 | .storagebits = 32, | ||
| 769 | }, | ||
| 770 | }, { | ||
| 771 | .type = IIO_POWER, | ||
| 772 | .indexed = 1, | ||
| 773 | .channel = 1, | ||
| 774 | .extend_name = "reactive", | ||
| 775 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 776 | .address = AD7758_WT(AD7758_PHASE_B, AD7758_REACT_PWR), | ||
| 777 | .scan_index = 9, | ||
| 778 | .scan_type = { | ||
| 779 | .sign = 's', | ||
| 780 | .realbits = 24, | ||
| 781 | .storagebits = 32, | ||
| 782 | }, | ||
| 783 | }, { | ||
| 784 | .type = IIO_VOLTAGE, | ||
| 785 | .indexed = 1, | ||
| 786 | .channel = 2, | ||
| 787 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 788 | .address = AD7758_WT(AD7758_PHASE_C, AD7758_VOLTAGE), | ||
| 789 | .scan_index = 10, | ||
| 790 | .scan_type = { | ||
| 791 | .sign = 's', | ||
| 792 | .realbits = 24, | ||
| 793 | .storagebits = 32, | ||
| 794 | }, | ||
| 795 | }, { | ||
| 796 | .type = IIO_CURRENT, | ||
| 797 | .indexed = 1, | ||
| 798 | .channel = 2, | ||
| 799 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 800 | .address = AD7758_WT(AD7758_PHASE_C, AD7758_CURRENT), | ||
| 801 | .scan_index = 11, | ||
| 802 | .scan_type = { | ||
| 803 | .sign = 's', | ||
| 804 | .realbits = 24, | ||
| 805 | .storagebits = 32, | ||
| 806 | }, | ||
| 807 | }, { | ||
| 808 | .type = IIO_POWER, | ||
| 809 | .indexed = 1, | ||
| 810 | .channel = 2, | ||
| 811 | .extend_name = "apparent", | ||
| 812 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 813 | .address = AD7758_WT(AD7758_PHASE_C, AD7758_APP_PWR), | ||
| 814 | .scan_index = 12, | ||
| 815 | .scan_type = { | ||
| 816 | .sign = 's', | ||
| 817 | .realbits = 24, | ||
| 818 | .storagebits = 32, | ||
| 819 | }, | ||
| 820 | }, { | ||
| 821 | .type = IIO_POWER, | ||
| 822 | .indexed = 1, | ||
| 823 | .channel = 2, | ||
| 824 | .extend_name = "active", | ||
| 825 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 826 | .address = AD7758_WT(AD7758_PHASE_C, AD7758_ACT_PWR), | ||
| 827 | .scan_index = 13, | ||
| 828 | .scan_type = { | ||
| 829 | .sign = 's', | ||
| 830 | .realbits = 24, | ||
| 831 | .storagebits = 32, | ||
| 832 | }, | ||
| 833 | }, { | ||
| 834 | .type = IIO_POWER, | ||
| 835 | .indexed = 1, | ||
| 836 | .channel = 2, | ||
| 837 | .extend_name = "reactive", | ||
| 838 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), | ||
| 839 | .address = AD7758_WT(AD7758_PHASE_C, AD7758_REACT_PWR), | ||
| 840 | .scan_index = 14, | ||
| 841 | .scan_type = { | ||
| 842 | .sign = 's', | ||
| 843 | .realbits = 24, | ||
| 844 | .storagebits = 32, | ||
| 845 | }, | ||
| 846 | }, | ||
| 847 | IIO_CHAN_SOFT_TIMESTAMP(15), | ||
| 848 | }; | ||
| 849 | |||
| 850 | static const struct iio_info ade7758_info = { | ||
| 851 | .attrs = &ade7758_attribute_group, | ||
| 852 | .read_raw = &ade7758_read_raw, | ||
| 853 | .write_raw = &ade7758_write_raw, | ||
| 854 | }; | ||
| 855 | |||
| 856 | static int ade7758_probe(struct spi_device *spi) | ||
| 857 | { | ||
| 858 | int ret; | ||
| 859 | struct ade7758_state *st; | ||
| 860 | struct iio_dev *indio_dev; | ||
| 861 | |||
| 862 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | ||
| 863 | if (!indio_dev) | ||
| 864 | return -ENOMEM; | ||
| 865 | |||
| 866 | st = iio_priv(indio_dev); | ||
| 867 | /* this is only used for removal purposes */ | ||
| 868 | spi_set_drvdata(spi, indio_dev); | ||
| 869 | |||
| 870 | /* Allocate the comms buffers */ | ||
| 871 | st->rx = kcalloc(ADE7758_MAX_RX, sizeof(*st->rx), GFP_KERNEL); | ||
| 872 | if (!st->rx) | ||
| 873 | return -ENOMEM; | ||
| 874 | st->tx = kcalloc(ADE7758_MAX_TX, sizeof(*st->tx), GFP_KERNEL); | ||
| 875 | if (!st->tx) { | ||
| 876 | ret = -ENOMEM; | ||
| 877 | goto error_free_rx; | ||
| 878 | } | ||
| 879 | st->us = spi; | ||
| 880 | mutex_init(&st->buf_lock); | ||
| 881 | |||
| 882 | indio_dev->name = spi->dev.driver->name; | ||
| 883 | indio_dev->dev.parent = &spi->dev; | ||
| 884 | indio_dev->info = &ade7758_info; | ||
| 885 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 886 | indio_dev->channels = ade7758_channels; | ||
| 887 | indio_dev->num_channels = ARRAY_SIZE(ade7758_channels); | ||
| 888 | |||
| 889 | ret = ade7758_configure_ring(indio_dev); | ||
| 890 | if (ret) | ||
| 891 | goto error_free_tx; | ||
| 892 | |||
| 893 | /* Get the device into a sane initial state */ | ||
| 894 | ret = ade7758_initial_setup(indio_dev); | ||
| 895 | if (ret) | ||
| 896 | goto error_unreg_ring_funcs; | ||
| 897 | |||
| 898 | if (spi->irq) { | ||
| 899 | ret = ade7758_probe_trigger(indio_dev); | ||
| 900 | if (ret) | ||
| 901 | goto error_unreg_ring_funcs; | ||
| 902 | } | ||
| 903 | |||
| 904 | ret = iio_device_register(indio_dev); | ||
| 905 | if (ret) | ||
| 906 | goto error_remove_trigger; | ||
| 907 | |||
| 908 | return 0; | ||
| 909 | |||
| 910 | error_remove_trigger: | ||
| 911 | if (spi->irq) | ||
| 912 | ade7758_remove_trigger(indio_dev); | ||
| 913 | error_unreg_ring_funcs: | ||
| 914 | ade7758_unconfigure_ring(indio_dev); | ||
| 915 | error_free_tx: | ||
| 916 | kfree(st->tx); | ||
| 917 | error_free_rx: | ||
| 918 | kfree(st->rx); | ||
| 919 | return ret; | ||
| 920 | } | ||
| 921 | |||
| 922 | static int ade7758_remove(struct spi_device *spi) | ||
| 923 | { | ||
| 924 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
| 925 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 926 | |||
| 927 | iio_device_unregister(indio_dev); | ||
| 928 | ade7758_stop_device(&indio_dev->dev); | ||
| 929 | ade7758_remove_trigger(indio_dev); | ||
| 930 | ade7758_unconfigure_ring(indio_dev); | ||
| 931 | kfree(st->tx); | ||
| 932 | kfree(st->rx); | ||
| 933 | |||
| 934 | return 0; | ||
| 935 | } | ||
| 936 | |||
| 937 | static const struct spi_device_id ade7758_id[] = { | ||
| 938 | {"ade7758", 0}, | ||
| 939 | {} | ||
| 940 | }; | ||
| 941 | MODULE_DEVICE_TABLE(spi, ade7758_id); | ||
| 942 | |||
| 943 | static struct spi_driver ade7758_driver = { | ||
| 944 | .driver = { | ||
| 945 | .name = "ade7758", | ||
| 946 | }, | ||
| 947 | .probe = ade7758_probe, | ||
| 948 | .remove = ade7758_remove, | ||
| 949 | .id_table = ade7758_id, | ||
| 950 | }; | ||
| 951 | module_spi_driver(ade7758_driver); | ||
| 952 | |||
| 953 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | ||
| 954 | MODULE_DESCRIPTION("Analog Devices ADE7758 Polyphase Multifunction Energy Metering IC Driver"); | ||
| 955 | MODULE_LICENSE("GPL v2"); | ||
| @@ -1,177 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADE7758 Poly Phase Multifunction Energy Metering IC driver | ||
| 3 | * | ||
| 4 | * Copyright 2010-2011 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2. | ||
| 7 | */ | ||
| 8 | #include <linux/export.h> | ||
| 9 | #include <linux/interrupt.h> | ||
| 10 | #include <linux/kernel.h> | ||
| 11 | #include <linux/spi/spi.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <asm/unaligned.h> | ||
| 14 | |||
| 15 | #include <linux/iio/iio.h> | ||
| 16 | #include <linux/iio/buffer.h> | ||
| 17 | #include <linux/iio/kfifo_buf.h> | ||
| 18 | #include <linux/iio/trigger_consumer.h> | ||
| 19 | #include "ade7758.h" | ||
| 20 | |||
| 21 | /** | ||
| 22 | * ade7758_spi_read_burst() - read data registers | ||
| 23 | * @indio_dev: the IIO device | ||
| 24 | **/ | ||
| 25 | static int ade7758_spi_read_burst(struct iio_dev *indio_dev) | ||
| 26 | { | ||
| 27 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 28 | int ret; | ||
| 29 | |||
| 30 | ret = spi_sync(st->us, &st->ring_msg); | ||
| 31 | if (ret) | ||
| 32 | dev_err(&st->us->dev, "problem when reading WFORM value\n"); | ||
| 33 | |||
| 34 | return ret; | ||
| 35 | } | ||
| 36 | |||
| 37 | static int ade7758_write_waveform_type(struct device *dev, unsigned int type) | ||
| 38 | { | ||
| 39 | int ret; | ||
| 40 | u8 reg; | ||
| 41 | |||
| 42 | ret = ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, ®); | ||
| 43 | if (ret) | ||
| 44 | goto out; | ||
| 45 | |||
| 46 | reg &= ~0x1F; | ||
| 47 | reg |= type & 0x1F; | ||
| 48 | |||
| 49 | ret = ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg); | ||
| 50 | out: | ||
| 51 | return ret; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Whilst this makes a lot of calls to iio_sw_ring functions - it is too device | ||
| 55 | * specific to be rolled into the core. | ||
| 56 | */ | ||
| 57 | static irqreturn_t ade7758_trigger_handler(int irq, void *p) | ||
| 58 | { | ||
| 59 | struct iio_poll_func *pf = p; | ||
| 60 | struct iio_dev *indio_dev = pf->indio_dev; | ||
| 61 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 62 | s64 dat64[2]; | ||
| 63 | u32 *dat32 = (u32 *)dat64; | ||
| 64 | |||
| 65 | if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) | ||
| 66 | if (ade7758_spi_read_burst(indio_dev) >= 0) | ||
| 67 | *dat32 = get_unaligned_be32(&st->rx_buf[5]) & 0xFFFFFF; | ||
| 68 | |||
| 69 | iio_push_to_buffers_with_timestamp(indio_dev, dat64, pf->timestamp); | ||
| 70 | |||
| 71 | iio_trigger_notify_done(indio_dev->trig); | ||
| 72 | |||
| 73 | return IRQ_HANDLED; | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * ade7758_ring_preenable() setup the parameters of the ring before enabling | ||
| 78 | * | ||
| 79 | * The complex nature of the setting of the number of bytes per datum is due | ||
| 80 | * to this driver currently ensuring that the timestamp is stored at an 8 | ||
| 81 | * byte boundary. | ||
| 82 | **/ | ||
| 83 | static int ade7758_ring_preenable(struct iio_dev *indio_dev) | ||
| 84 | { | ||
| 85 | unsigned int channel; | ||
| 86 | |||
| 87 | if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) | ||
| 88 | return -EINVAL; | ||
| 89 | |||
| 90 | channel = find_first_bit(indio_dev->active_scan_mask, | ||
| 91 | indio_dev->masklength); | ||
| 92 | |||
| 93 | ade7758_write_waveform_type(&indio_dev->dev, | ||
| 94 | indio_dev->channels[channel].address); | ||
| 95 | |||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | static const struct iio_buffer_setup_ops ade7758_ring_setup_ops = { | ||
| 100 | .preenable = &ade7758_ring_preenable, | ||
| 101 | .postenable = &iio_triggered_buffer_postenable, | ||
| 102 | .predisable = &iio_triggered_buffer_predisable, | ||
| 103 | .validate_scan_mask = &iio_validate_scan_mask_onehot, | ||
| 104 | }; | ||
| 105 | |||
| 106 | void ade7758_unconfigure_ring(struct iio_dev *indio_dev) | ||
| 107 | { | ||
| 108 | iio_dealloc_pollfunc(indio_dev->pollfunc); | ||
| 109 | iio_kfifo_free(indio_dev->buffer); | ||
| 110 | } | ||
| 111 | |||
| 112 | int ade7758_configure_ring(struct iio_dev *indio_dev) | ||
| 113 | { | ||
| 114 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 115 | struct iio_buffer *buffer; | ||
| 116 | int ret = 0; | ||
| 117 | |||
| 118 | buffer = iio_kfifo_allocate(); | ||
| 119 | if (!buffer) | ||
| 120 | return -ENOMEM; | ||
| 121 | |||
| 122 | iio_device_attach_buffer(indio_dev, buffer); | ||
| 123 | |||
| 124 | indio_dev->setup_ops = &ade7758_ring_setup_ops; | ||
| 125 | |||
| 126 | indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time, | ||
| 127 | &ade7758_trigger_handler, | ||
| 128 | 0, | ||
| 129 | indio_dev, | ||
| 130 | "ade7759_consumer%d", | ||
| 131 | indio_dev->id); | ||
| 132 | if (!indio_dev->pollfunc) { | ||
| 133 | ret = -ENOMEM; | ||
| 134 | goto error_iio_kfifo_free; | ||
| 135 | } | ||
| 136 | |||
| 137 | indio_dev->modes |= INDIO_BUFFER_TRIGGERED; | ||
| 138 | |||
| 139 | st->tx_buf[0] = ADE7758_READ_REG(ADE7758_RSTATUS); | ||
| 140 | st->tx_buf[1] = 0; | ||
| 141 | st->tx_buf[2] = 0; | ||
| 142 | st->tx_buf[3] = 0; | ||
| 143 | st->tx_buf[4] = ADE7758_READ_REG(ADE7758_WFORM); | ||
| 144 | st->tx_buf[5] = 0; | ||
| 145 | st->tx_buf[6] = 0; | ||
| 146 | st->tx_buf[7] = 0; | ||
| 147 | |||
| 148 | /* build spi ring message */ | ||
| 149 | st->ring_xfer[0].tx_buf = &st->tx_buf[0]; | ||
| 150 | st->ring_xfer[0].len = 1; | ||
| 151 | st->ring_xfer[0].bits_per_word = 8; | ||
| 152 | st->ring_xfer[0].delay_usecs = 4; | ||
| 153 | st->ring_xfer[1].rx_buf = &st->rx_buf[1]; | ||
| 154 | st->ring_xfer[1].len = 3; | ||
| 155 | st->ring_xfer[1].bits_per_word = 8; | ||
| 156 | st->ring_xfer[1].cs_change = 1; | ||
| 157 | |||
| 158 | st->ring_xfer[2].tx_buf = &st->tx_buf[4]; | ||
| 159 | st->ring_xfer[2].len = 1; | ||
| 160 | st->ring_xfer[2].bits_per_word = 8; | ||
| 161 | st->ring_xfer[2].delay_usecs = 1; | ||
| 162 | st->ring_xfer[3].rx_buf = &st->rx_buf[5]; | ||
| 163 | st->ring_xfer[3].len = 3; | ||
| 164 | st->ring_xfer[3].bits_per_word = 8; | ||
| 165 | |||
| 166 | spi_message_init(&st->ring_msg); | ||
| 167 | spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); | ||
| 168 | spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg); | ||
| 169 | spi_message_add_tail(&st->ring_xfer[2], &st->ring_msg); | ||
| 170 | spi_message_add_tail(&st->ring_xfer[3], &st->ring_msg); | ||
| 171 | |||
| 172 | return 0; | ||
| 173 | |||
| 174 | error_iio_kfifo_free: | ||
| 175 | iio_kfifo_free(indio_dev->buffer); | ||
| 176 | return ret; | ||
| 177 | } | ||
| @@ -1,108 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADE7758 Poly Phase Multifunction Energy Metering IC driver | ||
| 3 | * | ||
| 4 | * Copyright 2010-2011 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/interrupt.h> | ||
| 10 | #include <linux/kernel.h> | ||
| 11 | #include <linux/spi/spi.h> | ||
| 12 | #include <linux/export.h> | ||
| 13 | |||
| 14 | #include <linux/iio/iio.h> | ||
| 15 | #include <linux/iio/trigger.h> | ||
| 16 | #include "ade7758.h" | ||
| 17 | |||
| 18 | /** | ||
| 19 | * ade7758_data_rdy_trig_poll() the event handler for the data rdy trig | ||
| 20 | **/ | ||
| 21 | static irqreturn_t ade7758_data_rdy_trig_poll(int irq, void *private) | ||
| 22 | { | ||
| 23 | disable_irq_nosync(irq); | ||
| 24 | iio_trigger_poll(private); | ||
| 25 | |||
| 26 | return IRQ_HANDLED; | ||
| 27 | } | ||
| 28 | |||
| 29 | /** | ||
| 30 | * ade7758_data_rdy_trigger_set_state() set datardy interrupt state | ||
| 31 | **/ | ||
| 32 | static int ade7758_data_rdy_trigger_set_state(struct iio_trigger *trig, | ||
| 33 | bool state) | ||
| 34 | { | ||
| 35 | struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); | ||
| 36 | |||
| 37 | dev_dbg(&indio_dev->dev, "(%d)\n", state); | ||
| 38 | return ade7758_set_irq(&indio_dev->dev, state); | ||
| 39 | } | ||
| 40 | |||
| 41 | /** | ||
| 42 | * ade7758_trig_try_reen() try renabling irq for data rdy trigger | ||
| 43 | * @trig: the datardy trigger | ||
| 44 | **/ | ||
| 45 | static int ade7758_trig_try_reen(struct iio_trigger *trig) | ||
| 46 | { | ||
| 47 | struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); | ||
| 48 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 49 | |||
| 50 | enable_irq(st->us->irq); | ||
| 51 | /* irq reenabled so success! */ | ||
| 52 | return 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | static const struct iio_trigger_ops ade7758_trigger_ops = { | ||
| 56 | .set_trigger_state = &ade7758_data_rdy_trigger_set_state, | ||
| 57 | .try_reenable = &ade7758_trig_try_reen, | ||
| 58 | }; | ||
| 59 | |||
| 60 | int ade7758_probe_trigger(struct iio_dev *indio_dev) | ||
| 61 | { | ||
| 62 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 63 | int ret; | ||
| 64 | |||
| 65 | st->trig = iio_trigger_alloc("%s-dev%d", | ||
| 66 | spi_get_device_id(st->us)->name, | ||
| 67 | indio_dev->id); | ||
| 68 | if (!st->trig) { | ||
| 69 | ret = -ENOMEM; | ||
| 70 | goto error_ret; | ||
| 71 | } | ||
| 72 | |||
| 73 | ret = request_irq(st->us->irq, | ||
| 74 | ade7758_data_rdy_trig_poll, | ||
| 75 | IRQF_TRIGGER_LOW, | ||
| 76 | spi_get_device_id(st->us)->name, | ||
| 77 | st->trig); | ||
| 78 | if (ret) | ||
| 79 | goto error_free_trig; | ||
| 80 | |||
| 81 | st->trig->dev.parent = &st->us->dev; | ||
| 82 | st->trig->ops = &ade7758_trigger_ops; | ||
| 83 | iio_trigger_set_drvdata(st->trig, indio_dev); | ||
| 84 | ret = iio_trigger_register(st->trig); | ||
| 85 | |||
| 86 | /* select default trigger */ | ||
| 87 | indio_dev->trig = iio_trigger_get(st->trig); | ||
| 88 | if (ret) | ||
| 89 | goto error_free_irq; | ||
| 90 | |||
| 91 | return 0; | ||
| 92 | |||
| 93 | error_free_irq: | ||
| 94 | free_irq(st->us->irq, st->trig); | ||
| 95 | error_free_trig: | ||
| 96 | iio_trigger_free(st->trig); | ||
| 97 | error_ret: | ||
| 98 | return ret; | ||
| 99 | } | ||
| 100 | |||
| 101 | void ade7758_remove_trigger(struct iio_dev *indio_dev) | ||
| 102 | { | ||
| 103 | struct ade7758_state *st = iio_priv(indio_dev); | ||
| 104 | |||
| 105 | iio_trigger_unregister(st->trig); | ||
| 106 | free_irq(st->us->irq, st->trig); | ||
| 107 | iio_trigger_free(st->trig); | ||
| 108 | } | ||
| @@ -1,558 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ADE7759 Active Energy Metering IC with di/dt Sensor Interface Driver | ||
| 3 | * | ||
| 4 | * Copyright 2010 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Licensed under the GPL-2 or later. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/interrupt.h> | ||
| 10 | #include <linux/irq.h> | ||
| 11 | #include <linux/delay.h> | ||
| 12 | #include <linux/mutex.h> | ||
| 13 | #include <linux/device.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/spi/spi.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/sysfs.h> | ||
| 18 | #include <linux/list.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | |||
| 21 | #include <linux/iio/iio.h> | ||
| 22 | #include <linux/iio/sysfs.h> | ||
| 23 | #include "meter.h" | ||
| 24 | |||
| 25 | #define ADE7759_WAVEFORM 0x01 | ||
| 26 | #define ADE7759_AENERGY 0x02 | ||
| 27 | #define ADE7759_RSTENERGY 0x03 | ||
| 28 | #define ADE7759_STATUS 0x04 | ||
| 29 | #define ADE7759_RSTSTATUS 0x05 | ||
| 30 | #define ADE7759_MODE 0x06 | ||
| 31 | #define ADE7759_CFDEN 0x07 | ||
| 32 | #define ADE7759_CH1OS 0x08 | ||
| 33 | #define ADE7759_CH2OS 0x09 | ||
| 34 | #define ADE7759_GAIN 0x0A | ||
| 35 | #define ADE7759_APGAIN 0x0B | ||
| 36 | #define ADE7759_PHCAL 0x0C | ||
| 37 | #define ADE7759_APOS 0x0D | ||
| 38 | #define ADE7759_ZXTOUT 0x0E | ||
| 39 | #define ADE7759_SAGCYC 0x0F | ||
| 40 | #define ADE7759_IRQEN 0x10 | ||
| 41 | #define ADE7759_SAGLVL 0x11 | ||
| 42 | #define ADE7759_TEMP 0x12 | ||
| 43 | #define ADE7759_LINECYC 0x13 | ||
| 44 | #define ADE7759_LENERGY 0x14 | ||
| 45 | #define ADE7759_CFNUM 0x15 | ||
| 46 | #define ADE7759_CHKSUM 0x1E | ||
| 47 | #define ADE7759_DIEREV 0x1F | ||
| 48 | |||
| 49 | #define ADE7759_READ_REG(a) a | ||
| 50 | #define ADE7759_WRITE_REG(a) ((a) | 0x80) | ||
| 51 | |||
| 52 | #define ADE7759_MAX_TX 6 | ||
| 53 | #define ADE7759_MAX_RX 6 | ||
| 54 | #define ADE7759_STARTUP_DELAY 1000 | ||
| 55 | |||
| 56 | #define ADE7759_SPI_SLOW (u32)(300 * 1000) | ||
| 57 | #define ADE7759_SPI_BURST (u32)(1000 * 1000) | ||
| 58 | #define ADE7759_SPI_FAST (u32)(2000 * 1000) | ||
| 59 | |||
| 60 | /** | ||
| 61 | * struct ade7759_state - device instance specific data | ||
| 62 | * @us: actual spi_device | ||
| 63 | * @buf_lock: mutex to protect tx and rx and write frequency | ||
| 64 | * @tx: transmit buffer | ||
| 65 | * @rx: receive buffer | ||
| 66 | **/ | ||
| 67 | struct ade7759_state { | ||
| 68 | struct spi_device *us; | ||
| 69 | struct mutex buf_lock; | ||
| 70 | u8 tx[ADE7759_MAX_TX] ____cacheline_aligned; | ||
| 71 | u8 rx[ADE7759_MAX_RX]; | ||
| 72 | }; | ||
| 73 | |||
| 74 | static int ade7759_spi_write_reg_8(struct device *dev, | ||
| 75 | u8 reg_address, | ||
| 76 | u8 val) | ||
| 77 | { | ||
| 78 | int ret; | ||
| 79 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 80 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 81 | |||
| 82 | mutex_lock(&st->buf_lock); | ||
| 83 | st->tx[0] = ADE7759_WRITE_REG(reg_address); | ||
| 84 | st->tx[1] = val; | ||
| 85 | |||
| 86 | ret = spi_write(st->us, st->tx, 2); | ||
| 87 | mutex_unlock(&st->buf_lock); | ||
| 88 | |||
| 89 | return ret; | ||
| 90 | } | ||
| 91 | |||
| 92 | /*Unlocked version of ade7759_spi_write_reg_16 function */ | ||
| 93 | static int __ade7759_spi_write_reg_16(struct device *dev, | ||
| 94 | u8 reg_address, | ||
| 95 | u16 value) | ||
| 96 | { | ||
| 97 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 98 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 99 | |||
| 100 | st->tx[0] = ADE7759_WRITE_REG(reg_address); | ||
| 101 | st->tx[1] = (value >> 8) & 0xFF; | ||
| 102 | st->tx[2] = value & 0xFF; | ||
| 103 | return spi_write(st->us, st->tx, 3); | ||
| 104 | } | ||
| 105 | |||
| 106 | static int ade7759_spi_write_reg_16(struct device *dev, | ||
| 107 | u8 reg_address, | ||
| 108 | u16 value) | ||
| 109 | { | ||
| 110 | int ret; | ||
| 111 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 112 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 113 | |||
| 114 | mutex_lock(&st->buf_lock); | ||
| 115 | ret = __ade7759_spi_write_reg_16(dev, reg_address, value); | ||
| 116 | mutex_unlock(&st->buf_lock); | ||
| 117 | |||
| 118 | return ret; | ||
| 119 | } | ||
| 120 | |||
| 121 | static int ade7759_spi_read_reg_8(struct device *dev, | ||
| 122 | u8 reg_address, | ||
| 123 | u8 *val) | ||
| 124 | { | ||
| 125 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 126 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 127 | int ret; | ||
| 128 | |||
| 129 | ret = spi_w8r8(st->us, ADE7759_READ_REG(reg_address)); | ||
| 130 | if (ret < 0) { | ||
| 131 | dev_err(&st->us->dev, | ||
| 132 | "problem when reading 8 bit register 0x%02X", | ||
| 133 | reg_address); | ||
| 134 | return ret; | ||
| 135 | } | ||
| 136 | *val = ret; | ||
| 137 | |||
| 138 | return 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | static int ade7759_spi_read_reg_16(struct device *dev, | ||
| 142 | u8 reg_address, | ||
| 143 | u16 *val) | ||
| 144 | { | ||
| 145 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 146 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 147 | int ret; | ||
| 148 | |||
| 149 | ret = spi_w8r16be(st->us, ADE7759_READ_REG(reg_address)); | ||
| 150 | if (ret < 0) { | ||
| 151 | dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X", | ||
| 152 | reg_address); | ||
| 153 | return ret; | ||
| 154 | } | ||
| 155 | |||
| 156 | *val = ret; | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static int ade7759_spi_read_reg_40(struct device *dev, | ||
| 162 | u8 reg_address, | ||
| 163 | u64 *val) | ||
| 164 | { | ||
| 165 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 166 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 167 | int ret; | ||
| 168 | struct spi_transfer xfers[] = { | ||
| 169 | { | ||
| 170 | .tx_buf = st->tx, | ||
| 171 | .rx_buf = st->rx, | ||
| 172 | .bits_per_word = 8, | ||
| 173 | .len = 6, | ||
| 174 | }, | ||
| 175 | }; | ||
| 176 | |||
| 177 | mutex_lock(&st->buf_lock); | ||
| 178 | st->tx[0] = ADE7759_READ_REG(reg_address); | ||
| 179 | memset(&st->tx[1], 0, 5); | ||
| 180 | |||
| 181 | ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); | ||
| 182 | if (ret) { | ||
| 183 | dev_err(&st->us->dev, | ||
| 184 | "problem when reading 40 bit register 0x%02X", | ||
| 185 | reg_address); | ||
| 186 | goto error_ret; | ||
| 187 | } | ||
| 188 | *val = ((u64)st->rx[1] << 32) | ((u64)st->rx[2] << 24) | | ||
| 189 | (st->rx[3] << 16) | (st->rx[4] << 8) | st->rx[5]; | ||
| 190 | |||
| 191 | error_ret: | ||
| 192 | mutex_unlock(&st->buf_lock); | ||
| 193 | return ret; | ||
| 194 | } | ||
| 195 | |||
| 196 | static ssize_t ade7759_read_8bit(struct device *dev, | ||
| 197 | struct device_attribute *attr, | ||
| 198 | char *buf) | ||
| 199 | { | ||
| 200 | int ret; | ||
| 201 | u8 val = 0; | ||
| 202 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 203 | |||
| 204 | ret = ade7759_spi_read_reg_8(dev, this_attr->address, &val); | ||
| 205 | if (ret) | ||
| 206 | return ret; | ||
| 207 | |||
| 208 | return sprintf(buf, "%u\n", val); | ||
| 209 | } | ||
| 210 | |||
| 211 | static ssize_t ade7759_read_16bit(struct device *dev, | ||
| 212 | struct device_attribute *attr, | ||
| 213 | char *buf) | ||
| 214 | { | ||
| 215 | int ret; | ||
| 216 | u16 val = 0; | ||
| 217 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 218 | |||
| 219 | ret = ade7759_spi_read_reg_16(dev, this_attr->address, &val); | ||
| 220 | if (ret) | ||
| 221 | return ret; | ||
| 222 | |||
| 223 | return sprintf(buf, "%u\n", val); | ||
| 224 | } | ||
| 225 | |||
| 226 | static ssize_t ade7759_read_40bit(struct device *dev, | ||
| 227 | struct device_attribute *attr, | ||
| 228 | char *buf) | ||
| 229 | { | ||
| 230 | int ret; | ||
| 231 | u64 val = 0; | ||
| 232 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 233 | |||
| 234 | ret = ade7759_spi_read_reg_40(dev, this_attr->address, &val); | ||
| 235 | if (ret) | ||
| 236 | return ret; | ||
| 237 | |||
| 238 | return sprintf(buf, "%llu\n", val); | ||
| 239 | } | ||
| 240 | |||
| 241 | static ssize_t ade7759_write_8bit(struct device *dev, | ||
| 242 | struct device_attribute *attr, | ||
| 243 | const char *buf, | ||
| 244 | size_t len) | ||
| 245 | { | ||
| 246 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 247 | int ret; | ||
| 248 | u8 val; | ||
| 249 | |||
| 250 | ret = kstrtou8(buf, 10, &val); | ||
| 251 | if (ret) | ||
| 252 | goto error_ret; | ||
| 253 | ret = ade7759_spi_write_reg_8(dev, this_attr->address, val); | ||
| 254 | |||
| 255 | error_ret: | ||
| 256 | return ret ? ret : len; | ||
| 257 | } | ||
| 258 | |||
| 259 | static ssize_t ade7759_write_16bit(struct device *dev, | ||
| 260 | struct device_attribute *attr, | ||
| 261 | const char *buf, | ||
| 262 | size_t len) | ||
| 263 | { | ||
| 264 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | ||
| 265 | int ret; | ||
| 266 | u16 val; | ||
| 267 | |||
| 268 | ret = kstrtou16(buf, 10, &val); | ||
| 269 | if (ret) | ||
| 270 | goto error_ret; | ||
| 271 | ret = ade7759_spi_write_reg_16(dev, this_attr->address, val); | ||
| 272 | |||
| 273 | error_ret: | ||
| 274 | return ret ? ret : len; | ||
| 275 | } | ||
| 276 | |||
| 277 | static int ade7759_reset(struct device *dev) | ||
| 278 | { | ||
| 279 | int ret; | ||
| 280 | u16 val; | ||
| 281 | |||
| 282 | ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val); | ||
| 283 | if (ret < 0) | ||
| 284 | return ret; | ||
| 285 | |||
| 286 | val |= BIT(6); /* Software Chip Reset */ | ||
| 287 | return ade7759_spi_write_reg_16(dev, | ||
| 288 | ADE7759_MODE, | ||
| 289 | val); | ||
| 290 | } | ||
| 291 | |||
| 292 | static IIO_DEV_ATTR_AENERGY(ade7759_read_40bit, ADE7759_AENERGY); | ||
| 293 | static IIO_DEV_ATTR_CFDEN(0644, | ||
| 294 | ade7759_read_16bit, | ||
| 295 | ade7759_write_16bit, | ||
| 296 | ADE7759_CFDEN); | ||
| 297 | static IIO_DEV_ATTR_CFNUM(0644, | ||
| 298 | ade7759_read_8bit, | ||
| 299 | ade7759_write_8bit, | ||
| 300 | ADE7759_CFNUM); | ||
| 301 | static IIO_DEV_ATTR_CHKSUM(ade7759_read_8bit, ADE7759_CHKSUM); | ||
| 302 | static IIO_DEV_ATTR_PHCAL(0644, | ||
| 303 | ade7759_read_16bit, | ||
| 304 | ade7759_write_16bit, | ||
| 305 | ADE7759_PHCAL); | ||
| 306 | static IIO_DEV_ATTR_APOS(0644, | ||
| 307 | ade7759_read_16bit, | ||
| 308 | ade7759_write_16bit, | ||
| 309 | ADE7759_APOS); | ||
| 310 | static IIO_DEV_ATTR_SAGCYC(0644, | ||
| 311 | ade7759_read_8bit, | ||
| 312 | ade7759_write_8bit, | ||
| 313 | ADE7759_SAGCYC); | ||
| 314 | static IIO_DEV_ATTR_SAGLVL(0644, | ||
| 315 | ade7759_read_8bit, | ||
| 316 | ade7759_write_8bit, | ||
| 317 | ADE7759_SAGLVL); | ||
| 318 | static IIO_DEV_ATTR_LINECYC(0644, | ||
| 319 | ade7759_read_8bit, | ||
| 320 | ade7759_write_8bit, | ||
| 321 | ADE7759_LINECYC); | ||
| 322 | static IIO_DEV_ATTR_LENERGY(ade7759_read_40bit, ADE7759_LENERGY); | ||
| 323 | static IIO_DEV_ATTR_PGA_GAIN(0644, | ||
| 324 | ade7759_read_8bit, | ||
| 325 | ade7759_write_8bit, | ||
| 326 | ADE7759_GAIN); | ||
| 327 | static IIO_DEV_ATTR_ACTIVE_POWER_GAIN(0644, | ||
| 328 | ade7759_read_16bit, | ||
| 329 | ade7759_write_16bit, | ||
| 330 | ADE7759_APGAIN); | ||
| 331 | |||
| 332 | static IIO_DEVICE_ATTR(choff_1, 0644, | ||
| 333 | ade7759_read_8bit, | ||
| 334 | ade7759_write_8bit, | ||
| 335 | ADE7759_CH1OS); | ||
| 336 | |||
| 337 | static IIO_DEVICE_ATTR(choff_2, 0644, | ||
| 338 | ade7759_read_8bit, | ||
| 339 | ade7759_write_8bit, | ||
| 340 | ADE7759_CH2OS); | ||
| 341 | |||
| 342 | static int ade7759_set_irq(struct device *dev, bool enable) | ||
| 343 | { | ||
| 344 | int ret; | ||
| 345 | u8 irqen; | ||
| 346 | |||
| 347 | ret = ade7759_spi_read_reg_8(dev, ADE7759_IRQEN, &irqen); | ||
| 348 | if (ret) | ||
| 349 | goto error_ret; | ||
| 350 | |||
| 351 | if (enable) | ||
| 352 | irqen |= BIT(3); /* Enables an interrupt when a data is | ||
| 353 | * present in the waveform register | ||
| 354 | */ | ||
| 355 | else | ||
| 356 | irqen &= ~BIT(3); | ||
| 357 | |||
| 358 | ret = ade7759_spi_write_reg_8(dev, ADE7759_IRQEN, irqen); | ||
| 359 | |||
| 360 | error_ret: | ||
| 361 | return ret; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* Power down the device */ | ||
| 365 | static int ade7759_stop_device(struct device *dev) | ||
| 366 | { | ||
| 367 | int ret; | ||
| 368 | u16 val; | ||
| 369 | |||
| 370 | ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val); | ||
| 371 | if (ret < 0) { | ||
| 372 | dev_err(dev, "unable to power down the device, error: %d\n", | ||
| 373 | ret); | ||
| 374 | return ret; | ||
| 375 | } | ||
| 376 | |||
| 377 | val |= BIT(4); /* AD converters can be turned off */ | ||
| 378 | |||
| 379 | return ade7759_spi_write_reg_16(dev, ADE7759_MODE, val); | ||
| 380 | } | ||
| 381 | |||
| 382 | static int ade7759_initial_setup(struct iio_dev *indio_dev) | ||
| 383 | { | ||
| 384 | int ret; | ||
| 385 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 386 | struct device *dev = &indio_dev->dev; | ||
| 387 | |||
| 388 | /* use low spi speed for init */ | ||
| 389 | st->us->mode = SPI_MODE_3; | ||
| 390 | spi_setup(st->us); | ||
| 391 | |||
| 392 | /* Disable IRQ */ | ||
| 393 | ret = ade7759_set_irq(dev, false); | ||
| 394 | if (ret) { | ||
| 395 | dev_err(dev, "disable irq failed"); | ||
| 396 | goto err_ret; | ||
| 397 | } | ||
| 398 | |||
| 399 | ade7759_reset(dev); | ||
| 400 | usleep_range(ADE7759_STARTUP_DELAY, ADE7759_STARTUP_DELAY + 100); | ||
| 401 | |||
| 402 | err_ret: | ||
| 403 | return ret; | ||
| 404 | } | ||
| 405 | |||
| 406 | static ssize_t ade7759_read_frequency(struct device *dev, | ||
| 407 | struct device_attribute *attr, | ||
| 408 | char *buf) | ||
| 409 | { | ||
| 410 | int ret; | ||
| 411 | u16 t; | ||
| 412 | int sps; | ||
| 413 | |||
| 414 | ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &t); | ||
| 415 | if (ret) | ||
| 416 | return ret; | ||
| 417 | |||
| 418 | t = (t >> 3) & 0x3; | ||
| 419 | sps = 27900 / (1 + t); | ||
| 420 | |||
| 421 | return sprintf(buf, "%d\n", sps); | ||
| 422 | } | ||
| 423 | |||
| 424 | static ssize_t ade7759_write_frequency(struct device *dev, | ||
| 425 | struct device_attribute *attr, | ||
| 426 | const char *buf, | ||
| 427 | size_t len) | ||
| 428 | { | ||
| 429 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 430 | struct ade7759_state *st = iio_priv(indio_dev); | ||
| 431 | u16 val; | ||
| 432 | int ret; | ||
| 433 | u16 reg, t; | ||
| 434 | |||
| 435 | ret = kstrtou16(buf, 10, &val); | ||
| 436 | if (ret) | ||
| 437 | return ret; | ||
| 438 | if (!val) | ||
| 439 | return -EINVAL; | ||
| 440 | |||
| 441 | mutex_lock(&st->buf_lock); | ||
| 442 | |||
| 443 | t = 27900 / val; | ||
| 444 | if (t > 0) | ||
| 445 | t--; | ||
| 446 | |||
| 447 | if (t > 1) | ||
| 448 | st->us->max_speed_hz = ADE7759_SPI_SLOW; | ||
| 449 | else | ||
| 450 | st->us->max_speed_hz = ADE7759_SPI_FAST; | ||
| 451 | |||
| 452 | ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, ®); | ||
| 453 | if (ret) | ||
| 454 | goto out; | ||
| 455 | |||
| 456 | reg &= ~(3 << 13); | ||
| 457 | reg |= t << 13; | ||
| 458 | |||
| 459 | ret = __ade7759_spi_write_reg_16(dev, ADE7759_MODE, reg); | ||
| 460 | |||
| 461 | out: | ||
| 462 | mutex_unlock(&st->buf_lock); | ||
| 463 | |||
| 464 | return ret ? ret : len; | ||
| 465 | } | ||
| 466 | static IIO_DEV_ATTR_TEMP_RAW(ade7759_read_8bit); | ||
| 467 | static IIO_CONST_ATTR(in_temp_offset, "70 C"); | ||
| 468 | static IIO_CONST_ATTR(in_temp_scale, "1 C"); | ||
| 469 | |||
| 470 | static IIO_DEV_ATTR_SAMP_FREQ(0644, | ||
| 471 | ade7759_read_frequency, | ||
| 472 | ade7759_write_frequency); | ||
| 473 | |||
| 474 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500"); | ||
| 475 | |||
| 476 | static struct attribute *ade7759_attributes[] = { | ||
| 477 | &iio_dev_attr_in_temp_raw.dev_attr.attr, | ||
| 478 | &iio_const_attr_in_temp_offset.dev_attr.attr, | ||
| 479 | &iio_const_attr_in_temp_scale.dev_attr.attr, | ||
| 480 | &iio_dev_attr_sampling_frequency.dev_attr.attr, | ||
| 481 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, | ||
| 482 | &iio_dev_attr_phcal.dev_attr.attr, | ||
| 483 | &iio_dev_attr_cfden.dev_attr.attr, | ||
| 484 | &iio_dev_attr_aenergy.dev_attr.attr, | ||
| 485 | &iio_dev_attr_cfnum.dev_attr.attr, | ||
| 486 | &iio_dev_attr_apos.dev_attr.attr, | ||
| 487 | &iio_dev_attr_sagcyc.dev_attr.attr, | ||
| 488 | &iio_dev_attr_saglvl.dev_attr.attr, | ||
| 489 | &iio_dev_attr_linecyc.dev_attr.attr, | ||
| 490 | &iio_dev_attr_lenergy.dev_attr.attr, | ||
| 491 | &iio_dev_attr_chksum.dev_attr.attr, | ||
| 492 | &iio_dev_attr_pga_gain.dev_attr.attr, | ||
| 493 | &iio_dev_attr_active_power_gain.dev_attr.attr, | ||
| 494 | &iio_dev_attr_choff_1.dev_attr.attr, | ||
| 495 | &iio_dev_attr_choff_2.dev_attr.attr, | ||
| 496 | NULL, | ||
| 497 | }; | ||
| 498 | |||
| 499 | static const struct attribute_group ade7759_attribute_group = { | ||
| 500 | .attrs = ade7759_attributes, | ||
| 501 | }; | ||
| 502 | |||
| 503 | static const struct iio_info ade7759_info = { | ||
| 504 | .attrs = &ade7759_attribute_group, | ||
| 505 | }; | ||
| 506 | |||
| 507 | static int ade7759_probe(struct spi_device *spi) | ||
| 508 | { | ||
| 509 | int ret; | ||
| 510 | struct ade7759_state *st; | ||
| 511 | struct iio_dev *indio_dev; | ||
| 512 | |||
| 513 | /* setup the industrialio driver allocated elements */ | ||
| 514 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | ||
| 515 | if (!indio_dev) | ||
| 516 | return -ENOMEM; | ||
| 517 | /* this is only used for removal purposes */ | ||
| 518 | spi_set_drvdata(spi, indio_dev); | ||
| 519 | |||
| 520 | st = iio_priv(indio_dev); | ||
| 521 | st->us = spi; | ||
| 522 | mutex_init(&st->buf_lock); | ||
| 523 | indio_dev->name = spi->dev.driver->name; | ||
| 524 | indio_dev->dev.parent = &spi->dev; | ||
| 525 | indio_dev->info = &ade7759_info; | ||
| 526 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 527 | |||
| 528 | /* Get the device into a sane initial state */ | ||
| 529 | ret = ade7759_initial_setup(indio_dev); | ||
| 530 | if (ret) | ||
| 531 | return ret; | ||
| 532 | |||
| 533 | return iio_device_register(indio_dev); | ||
| 534 | } | ||
| 535 | |||
| 536 | static int ade7759_remove(struct spi_device *spi) | ||
| 537 | { | ||
| 538 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
| 539 | |||
| 540 | iio_device_unregister(indio_dev); | ||
| 541 | ade7759_stop_device(&indio_dev->dev); | ||
| 542 | |||
| 543 | return 0; | ||
| 544 | } | ||
| 545 | |||
| 546 | static struct spi_driver ade7759_driver = { | ||
| 547 | .driver = { | ||
| 548 | .name = "ade7759", | ||
| 549 | }, | ||
| 550 | .probe = ade7759_probe, | ||
| 551 | .remove = ade7759_remove, | ||
| 552 | }; | ||
| 553 | module_spi_driver(ade7759_driver); | ||
| 554 | |||
| 555 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | ||
| 556 | MODULE_DESCRIPTION("Analog Devices ADE7759 Active Energy Metering IC Driver"); | ||
| 557 | MODULE_LICENSE("GPL v2"); | ||
| 558 | MODULE_ALIAS("spi:ad7759"); | ||
| @@ -15,91 +15,60 @@ · 14 unmodified lines | |||
| 15 | #include <linux/iio/iio.h> | 15 | #include <linux/iio/iio.h> |
| 16 | #include "ade7854.h" | 16 | #include "ade7854.h" |
| 17 | 17 | ||
| 18 | static int ade7854_i2c_write_reg_8(struct device *dev, | 18 | static int ade7854_i2c_write_reg(struct device *dev, |
| 19 | u16 reg_address, | 19 | u16 reg_address, |
| 20 | u8 val) | 20 | u32 val, |
| 21 | int bits) | ||
| 21 | { | 22 | { |
| 22 | int ret; | 23 | int ret; |
| 24 | int count; | ||
| 23 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 25 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 24 | struct ade7854_state *st = iio_priv(indio_dev); | 26 | struct ade7854_state *st = iio_priv(indio_dev); |
| 25 | 27 | ||
| 26 | mutex_lock(&st->buf_lock); | 28 | mutex_lock(&st->buf_lock); |
| 27 | st->tx[0] = (reg_address >> 8) & 0xFF; | 29 | st->tx[0] = (reg_address >> 8) & 0xFF; |
| 28 | st->tx[1] = reg_address & 0xFF; | 30 | st->tx[1] = reg_address & 0xFF; |
| 29 | st->tx[2] = val; | ||
| 30 | 31 | ||
| 31 | ret = i2c_master_send(st->i2c, st->tx, 3); | 32 | switch (bits) { |
| 33 | case 8: | ||
| 34 | st->tx[2] = val & 0xFF; | ||
| 35 | count = 3; | ||
| 36 | break; | ||
| 37 | case 16: | ||
| 38 | st->tx[2] = (val >> 8) & 0xFF; | ||
| 39 | st->tx[3] = val & 0xFF; | ||
| 40 | count = 4; | ||
| 41 | break; | ||
| 42 | case 24: | ||
| 43 | st->tx[2] = (val >> 16) & 0xFF; | ||
| 44 | st->tx[3] = (val >> 8) & 0xFF; | ||
| 45 | st->tx[4] = val & 0xFF; | ||
| 46 | count = 5; | ||
| 47 | break; | ||
| 48 | case 32: | ||
| 49 | st->tx[2] = (val >> 24) & 0xFF; | ||
| 50 | st->tx[3] = (val >> 16) & 0xFF; | ||
| 51 | st->tx[4] = (val >> 8) & 0xFF; | ||
| 52 | st->tx[5] = val & 0xFF; | ||
| 53 | count = 6; | ||
| 54 | break; | ||
| 55 | default: | ||
| 56 | ret = -EINVAL; | ||
| 57 | goto unlock; | ||
| 58 | } | ||
| 59 | |||
| 60 | ret = i2c_master_send(st->i2c, st->tx, count); | ||
| 61 | |||
| 62 | unlock: | ||
| 32 | mutex_unlock(&st->buf_lock); | 63 | mutex_unlock(&st->buf_lock); |
| 33 | 64 | ||
| 34 | return ret; | 65 | return ret < 0 ? ret : 0; |
| 35 | } | 66 | } |
| 36 | 67 | ||
| 37 | static int ade7854_i2c_write_reg_16(struct device *dev, | 68 | static int ade7854_i2c_read_reg(struct device *dev, |
| 38 | u16 reg_address, | 69 | u16 reg_address, |
| 39 | u16 val) | 70 | u32 *val, |
| 40 | { | 71 | int bits) |
| 41 | int ret; | ||
| 42 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 43 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 44 | |||
| 45 | mutex_lock(&st->buf_lock); | ||
| 46 | st->tx[0] = (reg_address >> 8) & 0xFF; | ||
| 47 | st->tx[1] = reg_address & 0xFF; | ||
| 48 | st->tx[2] = (val >> 8) & 0xFF; | ||
| 49 | st->tx[3] = val & 0xFF; | ||
| 50 | |||
| 51 | ret = i2c_master_send(st->i2c, st->tx, 4); | ||
| 52 | mutex_unlock(&st->buf_lock); | ||
| 53 | |||
| 54 | return ret; | ||
| 55 | } | ||
| 56 | |||
| 57 | static int ade7854_i2c_write_reg_24(struct device *dev, | ||
| 58 | u16 reg_address, | ||
| 59 | u32 val) | ||
| 60 | { | ||
| 61 | int ret; | ||
| 62 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 63 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 64 | |||
| 65 | mutex_lock(&st->buf_lock); | ||
| 66 | st->tx[0] = (reg_address >> 8) & 0xFF; | ||
| 67 | st->tx[1] = reg_address & 0xFF; | ||
| 68 | st->tx[2] = (val >> 16) & 0xFF; | ||
| 69 | st->tx[3] = (val >> 8) & 0xFF; | ||
| 70 | st->tx[4] = val & 0xFF; | ||
| 71 | |||
| 72 | ret = i2c_master_send(st->i2c, st->tx, 5); | ||
| 73 | mutex_unlock(&st->buf_lock); | ||
| 74 | |||
| 75 | return ret; | ||
| 76 | } | ||
| 77 | |||
| 78 | static int ade7854_i2c_write_reg_32(struct device *dev, | ||
| 79 | u16 reg_address, | ||
| 80 | u32 val) | ||
| 81 | { | ||
| 82 | int ret; | ||
| 83 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 84 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 85 | |||
| 86 | mutex_lock(&st->buf_lock); | ||
| 87 | st->tx[0] = (reg_address >> 8) & 0xFF; | ||
| 88 | st->tx[1] = reg_address & 0xFF; | ||
| 89 | st->tx[2] = (val >> 24) & 0xFF; | ||
| 90 | st->tx[3] = (val >> 16) & 0xFF; | ||
| 91 | st->tx[4] = (val >> 8) & 0xFF; | ||
| 92 | st->tx[5] = val & 0xFF; | ||
| 93 | |||
| 94 | ret = i2c_master_send(st->i2c, st->tx, 6); | ||
| 95 | mutex_unlock(&st->buf_lock); | ||
| 96 | |||
| 97 | return ret; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int ade7854_i2c_read_reg_8(struct device *dev, | ||
| 101 | u16 reg_address, | ||
| 102 | u8 *val) | ||
| 103 | { | 72 | { |
| 104 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 73 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 105 | struct ade7854_state *st = iio_priv(indio_dev); | 74 | struct ade7854_state *st = iio_priv(indio_dev); |
| @@ -110,94 +79,33 @@ static int ade7854_i2c_read_reg_8(struct device *dev, · 4 unmodified lines | |||
| 110 | st->tx[1] = reg_address & 0xFF; | 79 | st->tx[1] = reg_address & 0xFF; |
| 111 | 80 | ||
| 112 | ret = i2c_master_send(st->i2c, st->tx, 2); | 81 | ret = i2c_master_send(st->i2c, st->tx, 2); |
| 113 | if (ret) | 82 | if (ret < 0) |
| 114 | goto out; | 83 | goto unlock; |
| 115 | 84 | ||
| 116 | ret = i2c_master_recv(st->i2c, st->rx, 1); | 85 | ret = i2c_master_recv(st->i2c, st->rx, bits); |
| 117 | if (ret) | 86 | if (ret < 0) |
| 118 | goto out; | 87 | goto unlock; |
| 119 | 88 | ||
| 120 | *val = st->rx[0]; | 89 | switch (bits) { |
| 121 | out: | 90 | case 8: |
| 122 | mutex_unlock(&st->buf_lock); | 91 | *val = st->rx[0]; |
| 123 | return ret; | 92 | break; |
| 124 | } | 93 | case 16: |
| 125 | 94 | *val = (st->rx[0] << 8) | st->rx[1]; | |
| 126 | static int ade7854_i2c_read_reg_16(struct device *dev, | 95 | break; |
| 127 | u16 reg_address, | 96 | case 24: |
| 128 | u16 *val) | 97 | *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; |
| 129 | { | 98 | break; |
| 130 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 99 | case 32: |
| 131 | struct ade7854_state *st = iio_priv(indio_dev); | 100 | *val = (st->rx[0] << 24) | (st->rx[1] << 16) | |
| 132 | int ret; | 101 | (st->rx[2] << 8) | st->rx[3]; |
| 133 | 102 | break; | |
| 134 | mutex_lock(&st->buf_lock); | 103 | default: |
| 135 | st->tx[0] = (reg_address >> 8) & 0xFF; | 104 | ret = -EINVAL; |
| 136 | st->tx[1] = reg_address & 0xFF; | 105 | goto unlock; |
| 137 | 106 | } | |
| 138 | ret = i2c_master_send(st->i2c, st->tx, 2); | 107 | |
| 139 | if (ret) | 108 | unlock: |
| 140 | goto out; | ||
| 141 | |||
| 142 | ret = i2c_master_recv(st->i2c, st->rx, 2); | ||
| 143 | if (ret) | ||
| 144 | goto out; | ||
| 145 | |||
| 146 | *val = (st->rx[0] << 8) | st->rx[1]; | ||
| 147 | out: | ||
| 148 | mutex_unlock(&st->buf_lock); | ||
| 149 | return ret; | ||
| 150 | } | ||
| 151 | |||
| 152 | static int ade7854_i2c_read_reg_24(struct device *dev, | ||
| 153 | u16 reg_address, | ||
| 154 | u32 *val) | ||
| 155 | { | ||
| 156 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 157 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 158 | int ret; | ||
| 159 | |||
| 160 | mutex_lock(&st->buf_lock); | ||
| 161 | st->tx[0] = (reg_address >> 8) & 0xFF; | ||
| 162 | st->tx[1] = reg_address & 0xFF; | ||
| 163 | |||
| 164 | ret = i2c_master_send(st->i2c, st->tx, 2); | ||
| 165 | if (ret) | ||
| 166 | goto out; | ||
| 167 | |||
| 168 | ret = i2c_master_recv(st->i2c, st->rx, 3); | ||
| 169 | if (ret) | ||
| 170 | goto out; | ||
| 171 | |||
| 172 | *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; | ||
| 173 | out: | ||
| 174 | mutex_unlock(&st->buf_lock); | ||
| 175 | return ret; | ||
| 176 | } | ||
| 177 | |||
| 178 | static int ade7854_i2c_read_reg_32(struct device *dev, | ||
| 179 | u16 reg_address, | ||
| 180 | u32 *val) | ||
| 181 | { | ||
| 182 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 183 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 184 | int ret; | ||
| 185 | |||
| 186 | mutex_lock(&st->buf_lock); | ||
| 187 | st->tx[0] = (reg_address >> 8) & 0xFF; | ||
| 188 | st->tx[1] = reg_address & 0xFF; | ||
| 189 | |||
| 190 | ret = i2c_master_send(st->i2c, st->tx, 2); | ||
| 191 | if (ret) | ||
| 192 | goto out; | ||
| 193 | |||
| 194 | ret = i2c_master_recv(st->i2c, st->rx, 3); | ||
| 195 | if (ret) | ||
| 196 | goto out; | ||
| 197 | |||
| 198 | *val = (st->rx[0] << 24) | (st->rx[1] << 16) | | ||
| 199 | (st->rx[2] << 8) | st->rx[3]; | ||
| 200 | out: | ||
| 201 | mutex_unlock(&st->buf_lock); | 109 | mutex_unlock(&st->buf_lock); |
| 202 | return ret; | 110 | return ret; |
| 203 | } | 111 | } |
| @@ -213,14 +121,8 @@ static int ade7854_i2c_probe(struct i2c_client *client, · 9 unmodified lines | |||
| 213 | return -ENOMEM; | 121 | return -ENOMEM; |
| 214 | st = iio_priv(indio_dev); | 122 | st = iio_priv(indio_dev); |
| 215 | i2c_set_clientdata(client, indio_dev); | 123 | i2c_set_clientdata(client, indio_dev); |
| 216 | st->read_reg_8 = ade7854_i2c_read_reg_8; | 124 | st->read_reg = ade7854_i2c_read_reg; |
| 217 | st->read_reg_16 = ade7854_i2c_read_reg_16; | 125 | st->write_reg = ade7854_i2c_write_reg; |
| 218 | st->read_reg_24 = ade7854_i2c_read_reg_24; | ||
| 219 | st->read_reg_32 = ade7854_i2c_read_reg_32; | ||
| 220 | st->write_reg_8 = ade7854_i2c_write_reg_8; | ||
| 221 | st->write_reg_16 = ade7854_i2c_write_reg_16; | ||
| 222 | st->write_reg_24 = ade7854_i2c_write_reg_24; | ||
| 223 | st->write_reg_32 = ade7854_i2c_write_reg_32; | ||
| 224 | st->i2c = client; | 126 | st->i2c = client; |
| 225 | st->irq = client->irq; | 127 | st->irq = client->irq; |
| 226 | 128 | ||
| @@ -15,9 +15,10 @@ · 14 unmodified lines | |||
| 15 | #include <linux/iio/iio.h> | 15 | #include <linux/iio/iio.h> |
| 16 | #include "ade7854.h" | 16 | #include "ade7854.h" |
| 17 | 17 | ||
| 18 | static int ade7854_spi_write_reg_8(struct device *dev, | 18 | static int ade7854_spi_write_reg(struct device *dev, |
| 19 | u16 reg_address, | 19 | u16 reg_address, |
| 20 | u8 val) | 20 | u32 val, |
| 21 | int bits) | ||
| 21 | { | 22 | { |
| 22 | int ret; | 23 | int ret; |
| 23 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 24 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| @@ -32,173 +33,44 @@ static int ade7854_spi_write_reg_8(struct device *dev, · 8 unmodified lines | |||
| 32 | st->tx[0] = ADE7854_WRITE_REG; | 33 | st->tx[0] = ADE7854_WRITE_REG; |
| 33 | st->tx[1] = (reg_address >> 8) & 0xFF; | 34 | st->tx[1] = (reg_address >> 8) & 0xFF; |
| 34 | st->tx[2] = reg_address & 0xFF; | 35 | st->tx[2] = reg_address & 0xFF; |
| 35 | st->tx[3] = val & 0xFF; | 36 | switch (bits) { |
| 36 | 37 | case 8: | |
| 37 | ret = spi_sync_transfer(st->spi, &xfer, 1); | 38 | st->tx[3] = val & 0xFF; |
| 38 | mutex_unlock(&st->buf_lock); | 39 | break; |
| 39 | 40 | case 16: | |
| 40 | return ret; | 41 | xfer.len = 5; |
| 41 | } | 42 | st->tx[3] = (val >> 8) & 0xFF; |
| 42 | 43 | st->tx[4] = val & 0xFF; | |
| 43 | static int ade7854_spi_write_reg_16(struct device *dev, | 44 | break; |
| 44 | u16 reg_address, | 45 | case 24: |
| 45 | u16 val) | 46 | xfer.len = 6; |
| 46 | { | 47 | st->tx[3] = (val >> 16) & 0xFF; |
| 47 | int ret; | 48 | st->tx[4] = (val >> 8) & 0xFF; |
| 48 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 49 | st->tx[5] = val & 0xFF; |
| 49 | struct ade7854_state *st = iio_priv(indio_dev); | 50 | break; |
| 50 | struct spi_transfer xfer = { | 51 | case 32: |
| 51 | .tx_buf = st->tx, | 52 | xfer.len = 7; |
| 52 | .bits_per_word = 8, | 53 | st->tx[3] = (val >> 24) & 0xFF; |
| 53 | .len = 5, | 54 | st->tx[4] = (val >> 16) & 0xFF; |
| 54 | }; | 55 | st->tx[5] = (val >> 8) & 0xFF; |
| 55 | 56 | st->tx[6] = val & 0xFF; | |
| 56 | mutex_lock(&st->buf_lock); | 57 | break; |
| 57 | st->tx[0] = ADE7854_WRITE_REG; | 58 | default: |
| 58 | st->tx[1] = (reg_address >> 8) & 0xFF; | 59 | ret = -EINVAL; |
| 59 | st->tx[2] = reg_address & 0xFF; | 60 | goto unlock; |
| 60 | st->tx[3] = (val >> 8) & 0xFF; | ||
| 61 | st->tx[4] = val & 0xFF; | ||
| 62 | |||
| 63 | ret = spi_sync_transfer(st->spi, &xfer, 1); | ||
| 64 | mutex_unlock(&st->buf_lock); | ||
| 65 | |||
| 66 | return ret; | ||
| 67 | } | ||
| 68 | |||
| 69 | static int ade7854_spi_write_reg_24(struct device *dev, | ||
| 70 | u16 reg_address, | ||
| 71 | u32 val) | ||
| 72 | { | ||
| 73 | int ret; | ||
| 74 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 75 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 76 | struct spi_transfer xfer = { | ||
| 77 | .tx_buf = st->tx, | ||
| 78 | .bits_per_word = 8, | ||
| 79 | .len = 6, | ||
| 80 | }; | ||
| 81 | |||
| 82 | mutex_lock(&st->buf_lock); | ||
| 83 | st->tx[0] = ADE7854_WRITE_REG; | ||
| 84 | st->tx[1] = (reg_address >> 8) & 0xFF; | ||
| 85 | st->tx[2] = reg_address & 0xFF; | ||
| 86 | st->tx[3] = (val >> 16) & 0xFF; | ||
| 87 | st->tx[4] = (val >> 8) & 0xFF; | ||
| 88 | st->tx[5] = val & 0xFF; | ||
| 89 | |||
| 90 | ret = spi_sync_transfer(st->spi, &xfer, 1); | ||
| 91 | mutex_unlock(&st->buf_lock); | ||
| 92 | |||
| 93 | return ret; | ||
| 94 | } | ||
| 95 | |||
| 96 | static int ade7854_spi_write_reg_32(struct device *dev, | ||
| 97 | u16 reg_address, | ||
| 98 | u32 val) | ||
| 99 | { | ||
| 100 | int ret; | ||
| 101 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 102 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 103 | struct spi_transfer xfer = { | ||
| 104 | .tx_buf = st->tx, | ||
| 105 | .bits_per_word = 8, | ||
| 106 | .len = 7, | ||
| 107 | }; | ||
| 108 | |||
| 109 | mutex_lock(&st->buf_lock); | ||
| 110 | st->tx[0] = ADE7854_WRITE_REG; | ||
| 111 | st->tx[1] = (reg_address >> 8) & 0xFF; | ||
| 112 | st->tx[2] = reg_address & 0xFF; | ||
| 113 | st->tx[3] = (val >> 24) & 0xFF; | ||
| 114 | st->tx[4] = (val >> 16) & 0xFF; | ||
| 115 | st->tx[5] = (val >> 8) & 0xFF; | ||
| 116 | st->tx[6] = val & 0xFF; | ||
| 117 | |||
| 118 | ret = spi_sync_transfer(st->spi, &xfer, 1); | ||
| 119 | mutex_unlock(&st->buf_lock); | ||
| 120 | |||
| 121 | return ret; | ||
| 122 | } | ||
| 123 | |||
| 124 | static int ade7854_spi_read_reg_8(struct device *dev, | ||
| 125 | u16 reg_address, | ||
| 126 | u8 *val) | ||
| 127 | { | ||
| 128 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 129 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 130 | int ret; | ||
| 131 | struct spi_transfer xfers[] = { | ||
| 132 | { | ||
| 133 | .tx_buf = st->tx, | ||
| 134 | .bits_per_word = 8, | ||
| 135 | .len = 3, | ||
| 136 | }, { | ||
| 137 | .rx_buf = st->rx, | ||
| 138 | .bits_per_word = 8, | ||
| 139 | .len = 1, | ||
| 140 | } | ||
| 141 | }; | ||
| 142 | |||
| 143 | mutex_lock(&st->buf_lock); | ||
| 144 | |||
| 145 | st->tx[0] = ADE7854_READ_REG; | ||
| 146 | st->tx[1] = (reg_address >> 8) & 0xFF; | ||
| 147 | st->tx[2] = reg_address & 0xFF; | ||
| 148 | |||
| 149 | ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); | ||
| 150 | if (ret) { | ||
| 151 | dev_err(&st->spi->dev, "problem when reading 8 bit register 0x%02X", | ||
| 152 | reg_address); | ||
| 153 | goto error_ret; | ||
| 154 | } | 61 | } |
| 155 | *val = st->rx[0]; | ||
| 156 | 62 | ||
| 157 | error_ret: | 63 | ret = spi_sync_transfer(st->spi, &xfer, 1); |
| 64 | unlock: | ||
| 158 | mutex_unlock(&st->buf_lock); | 65 | mutex_unlock(&st->buf_lock); |
| 159 | return ret; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int ade7854_spi_read_reg_16(struct device *dev, | ||
| 163 | u16 reg_address, | ||
| 164 | u16 *val) | ||
| 165 | { | ||
| 166 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 167 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 168 | int ret; | ||
| 169 | struct spi_transfer xfers[] = { | ||
| 170 | { | ||
| 171 | .tx_buf = st->tx, | ||
| 172 | .bits_per_word = 8, | ||
| 173 | .len = 3, | ||
| 174 | }, { | ||
| 175 | .rx_buf = st->rx, | ||
| 176 | .bits_per_word = 8, | ||
| 177 | .len = 2, | ||
| 178 | } | ||
| 179 | }; | ||
| 180 | 66 | ||
| 181 | mutex_lock(&st->buf_lock); | ||
| 182 | st->tx[0] = ADE7854_READ_REG; | ||
| 183 | st->tx[1] = (reg_address >> 8) & 0xFF; | ||
| 184 | st->tx[2] = reg_address & 0xFF; | ||
| 185 | |||
| 186 | ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); | ||
| 187 | if (ret) { | ||
| 188 | dev_err(&st->spi->dev, "problem when reading 16 bit register 0x%02X", | ||
| 189 | reg_address); | ||
| 190 | goto error_ret; | ||
| 191 | } | ||
| 192 | *val = be16_to_cpup((const __be16 *)st->rx); | ||
| 193 | |||
| 194 | error_ret: | ||
| 195 | mutex_unlock(&st->buf_lock); | ||
| 196 | return ret; | 67 | return ret; |
| 197 | } | 68 | } |
| 198 | 69 | ||
| 199 | static int ade7854_spi_read_reg_24(struct device *dev, | 70 | static int ade7854_spi_read_reg(struct device *dev, |
| 200 | u16 reg_address, | 71 | u16 reg_address, |
| 201 | u32 *val) | 72 | u32 *val, |
| 73 | int bits) | ||
| 202 | { | 74 | { |
| 203 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 75 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 204 | struct ade7854_state *st = iio_priv(indio_dev); | 76 | struct ade7854_state *st = iio_priv(indio_dev); |
| @@ -211,7 +83,7 @@ static int ade7854_spi_read_reg_24(struct device *dev, · 6 unmodified lines | |||
| 211 | }, { | 83 | }, { |
| 212 | .rx_buf = st->rx, | 84 | .rx_buf = st->rx, |
| 213 | .bits_per_word = 8, | 85 | .bits_per_word = 8, |
| 214 | .len = 3, | 86 | .len = bits, |
| 215 | } | 87 | } |
| 216 | }; | 88 | }; |
| 217 | 89 | ||
| @@ -222,52 +94,28 @@ static int ade7854_spi_read_reg_24(struct device *dev, · 4 unmodified lines | |||
| 222 | st->tx[2] = reg_address & 0xFF; | 94 | st->tx[2] = reg_address & 0xFF; |
| 223 | 95 | ||
| 224 | ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); | 96 | ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); |
| 225 | if (ret) { | 97 | if (ret < 0) { |
| 226 | dev_err(&st->spi->dev, "problem when reading 24 bit register 0x%02X", | 98 | dev_err(&st->spi->dev, "problem when reading register 0x%02X", |
| 227 | reg_address); | 99 | reg_address); |
| 228 | goto error_ret; | 100 | goto unlock; |
| 229 | } | 101 | } |
| 230 | *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; | ||
| 231 | |||
| 232 | error_ret: | ||
| 233 | mutex_unlock(&st->buf_lock); | ||
| 234 | return ret; | ||
| 235 | } | ||
| 236 | |||
| 237 | static int ade7854_spi_read_reg_32(struct device *dev, | ||
| 238 | u16 reg_address, | ||
| 239 | u32 *val) | ||
| 240 | { | ||
| 241 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
| 242 | struct ade7854_state *st = iio_priv(indio_dev); | ||
| 243 | int ret; | ||
| 244 | struct spi_transfer xfers[] = { | ||
| 245 | { | ||
| 246 | .tx_buf = st->tx, | ||
| 247 | .bits_per_word = 8, | ||
| 248 | .len = 3, | ||
| 249 | }, { | ||
| 250 | .rx_buf = st->rx, | ||
| 251 | .bits_per_word = 8, | ||
| 252 | .len = 4, | ||
| 253 | } | ||
| 254 | }; | ||
| 255 | |||
| 256 | mutex_lock(&st->buf_lock); | ||
| 257 | 102 | ||
| 258 | st->tx[0] = ADE7854_READ_REG; | 103 | switch (bits) { |
| 259 | st->tx[1] = (reg_address >> 8) & 0xFF; | 104 | case 8: |
| 260 | st->tx[2] = reg_address & 0xFF; | 105 | *val = st->rx[0]; |
| 261 | 106 | break; | |
| 262 | ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); | 107 | case 16: |
| 263 | if (ret) { | 108 | *val = be16_to_cpup((const __be16 *)st->rx); |
| 264 | dev_err(&st->spi->dev, "problem when reading 32 bit register 0x%02X", | 109 | break; |
| 265 | reg_address); | 110 | case 24: |
| 266 | goto error_ret; | 111 | *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2]; |
| 112 | break; | ||
| 113 | case 32: | ||
| 114 | *val = be32_to_cpup((const __be32 *)st->rx); | ||
| 115 | break; | ||
| 267 | } | 116 | } |
| 268 | *val = be32_to_cpup((const __be32 *)st->rx); | ||
| 269 | 117 | ||
| 270 | error_ret: | 118 | unlock: |
| 271 | mutex_unlock(&st->buf_lock); | 119 | mutex_unlock(&st->buf_lock); |
| 272 | return ret; | 120 | return ret; |
| 273 | } | 121 | } |
| @@ -282,14 +130,8 @@ static int ade7854_spi_probe(struct spi_device *spi) · 8 unmodified lines | |||
| 282 | return -ENOMEM; | 130 | return -ENOMEM; |
| 283 | st = iio_priv(indio_dev); | 131 | st = iio_priv(indio_dev); |
| 284 | spi_set_drvdata(spi, indio_dev); | 132 | spi_set_drvdata(spi, indio_dev); |
| 285 | st->read_reg_8 = ade7854_spi_read_reg_8; | 133 | st->read_reg = ade7854_spi_read_reg; |
| 286 | st->read_reg_16 = ade7854_spi_read_reg_16; | 134 | st->write_reg = ade7854_spi_write_reg; |
| 287 | st->read_reg_24 = ade7854_spi_read_reg_24; | ||
| 288 | st->read_reg_32 = ade7854_spi_read_reg_32; | ||
| 289 | st->write_reg_8 = ade7854_spi_write_reg_8; | ||
| 290 | st->write_reg_16 = ade7854_spi_write_reg_16; | ||
| 291 | st->write_reg_24 = ade7854_spi_write_reg_24; | ||
| 292 | st->write_reg_32 = ade7854_spi_write_reg_32; | ||
| 293 | st->irq = spi->irq; | 135 | st->irq = spi->irq; |
| 294 | st->spi = spi; | 136 | st->spi = spi; |
| 295 | 137 | ||
| @@ -27,13 +27,13 @@ static ssize_t ade7854_read_8bit(struct device *dev, · 26 unmodified lines | |||
| 27 | char *buf) | 27 | char *buf) |
| 28 | { | 28 | { |
| 29 | int ret; | 29 | int ret; |
| 30 | u8 val = 0; | 30 | u32 val = 0; |
| 31 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 31 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 32 | struct ade7854_state *st = iio_priv(indio_dev); | 32 | struct ade7854_state *st = iio_priv(indio_dev); |
| 33 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | 33 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| 34 | 34 | ||
| 35 | ret = st->read_reg_8(dev, this_attr->address, &val); | 35 | ret = st->read_reg(dev, this_attr->address, &val, 8); |
| 36 | if (ret) | 36 | if (ret < 0) |
| 37 | return ret; | 37 | return ret; |
| 38 | 38 | ||
| 39 | return sprintf(buf, "%u\n", val); | 39 | return sprintf(buf, "%u\n", val); |
| @@ -44,13 +44,13 @@ static ssize_t ade7854_read_16bit(struct device *dev, · 4 unmodified lines | |||
| 44 | char *buf) | 44 | char *buf) |
| 45 | { | 45 | { |
| 46 | int ret; | 46 | int ret; |
| 47 | u16 val = 0; | 47 | u32 val = 0; |
| 48 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 48 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 49 | struct ade7854_state *st = iio_priv(indio_dev); | 49 | struct ade7854_state *st = iio_priv(indio_dev); |
| 50 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | 50 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| 51 | 51 | ||
| 52 | ret = st->read_reg_16(dev, this_attr->address, &val); | 52 | ret = st->read_reg(dev, this_attr->address, &val, 16); |
| 53 | if (ret) | 53 | if (ret < 0) |
| 54 | return ret; | 54 | return ret; |
| 55 | 55 | ||
| 56 | return sprintf(buf, "%u\n", val); | 56 | return sprintf(buf, "%u\n", val); |
| @@ -66,8 +66,8 @@ static ssize_t ade7854_read_24bit(struct device *dev, · 9 unmodified lines | |||
| 66 | struct ade7854_state *st = iio_priv(indio_dev); | 66 | struct ade7854_state *st = iio_priv(indio_dev); |
| 67 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | 67 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| 68 | 68 | ||
| 69 | ret = st->read_reg_24(dev, this_attr->address, &val); | 69 | ret = st->read_reg(dev, this_attr->address, &val, 24); |
| 70 | if (ret) | 70 | if (ret < 0) |
| 71 | return ret; | 71 | return ret; |
| 72 | 72 | ||
| 73 | return sprintf(buf, "%u\n", val); | 73 | return sprintf(buf, "%u\n", val); |
| @@ -83,8 +83,8 @@ static ssize_t ade7854_read_32bit(struct device *dev, · 9 unmodified lines | |||
| 83 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 83 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 84 | struct ade7854_state *st = iio_priv(indio_dev); | 84 | struct ade7854_state *st = iio_priv(indio_dev); |
| 85 | 85 | ||
| 86 | ret = st->read_reg_32(dev, this_attr->address, &val); | 86 | ret = st->read_reg(dev, this_attr->address, &val, 32); |
| 87 | if (ret) | 87 | if (ret < 0) |
| 88 | return ret; | 88 | return ret; |
| 89 | 89 | ||
| 90 | return sprintf(buf, "%u\n", val); | 90 | return sprintf(buf, "%u\n", val); |
| @@ -105,7 +105,7 @@ static ssize_t ade7854_write_8bit(struct device *dev, · 14 unmodified lines | |||
| 105 | ret = kstrtou8(buf, 10, &val); | 105 | ret = kstrtou8(buf, 10, &val); |
| 106 | if (ret) | 106 | if (ret) |
| 107 | goto error_ret; | 107 | goto error_ret; |
| 108 | ret = st->write_reg_8(dev, this_attr->address, val); | 108 | ret = st->write_reg(dev, this_attr->address, val, 8); |
| 109 | 109 | ||
| 110 | error_ret: | 110 | error_ret: |
| 111 | return ret ? ret : len; | 111 | return ret ? ret : len; |
| @@ -126,7 +126,7 @@ static ssize_t ade7854_write_16bit(struct device *dev, · 14 unmodified lines | |||
| 126 | ret = kstrtou16(buf, 10, &val); | 126 | ret = kstrtou16(buf, 10, &val); |
| 127 | if (ret) | 127 | if (ret) |
| 128 | goto error_ret; | 128 | goto error_ret; |
| 129 | ret = st->write_reg_16(dev, this_attr->address, val); | 129 | ret = st->write_reg(dev, this_attr->address, val, 16); |
| 130 | 130 | ||
| 131 | error_ret: | 131 | error_ret: |
| 132 | return ret ? ret : len; | 132 | return ret ? ret : len; |
| @@ -147,7 +147,7 @@ static ssize_t ade7854_write_24bit(struct device *dev, · 14 unmodified lines | |||
| 147 | ret = kstrtou32(buf, 10, &val); | 147 | ret = kstrtou32(buf, 10, &val); |
| 148 | if (ret) | 148 | if (ret) |
| 149 | goto error_ret; | 149 | goto error_ret; |
| 150 | ret = st->write_reg_24(dev, this_attr->address, val); | 150 | ret = st->write_reg(dev, this_attr->address, val, 24); |
| 151 | 151 | ||
| 152 | error_ret: | 152 | error_ret: |
| 153 | return ret ? ret : len; | 153 | return ret ? ret : len; |
| @@ -168,7 +168,7 @@ static ssize_t ade7854_write_32bit(struct device *dev, · 14 unmodified lines | |||
| 168 | ret = kstrtou32(buf, 10, &val); | 168 | ret = kstrtou32(buf, 10, &val); |
| 169 | if (ret) | 169 | if (ret) |
| 170 | goto error_ret; | 170 | goto error_ret; |
| 171 | ret = st->write_reg_32(dev, this_attr->address, val); | 171 | ret = st->write_reg(dev, this_attr->address, val, 32); |
| 172 | 172 | ||
| 173 | error_ret: | 173 | error_ret: |
| 174 | return ret ? ret : len; | 174 | return ret ? ret : len; |
| @@ -178,12 +178,12 @@ static int ade7854_reset(struct device *dev) · 3 unmodified lines | |||
| 178 | { | 178 | { |
| 179 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 179 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 180 | struct ade7854_state *st = iio_priv(indio_dev); | 180 | struct ade7854_state *st = iio_priv(indio_dev); |
| 181 | u16 val; | 181 | u32 val; |
| 182 | 182 | ||
| 183 | st->read_reg_16(dev, ADE7854_CONFIG, &val); | 183 | st->read_reg(dev, ADE7854_CONFIG, &val, 16); |
| 184 | val |= BIT(7); /* Software Chip Reset */ | 184 | val |= BIT(7); /* Software Chip Reset */ |
| 185 | 185 | ||
| 186 | return st->write_reg_16(dev, ADE7854_CONFIG, val); | 186 | return st->write_reg(dev, ADE7854_CONFIG, val, 16); |
| 187 | } | 187 | } |
| 188 | 188 | ||
| 189 | static IIO_DEV_ATTR_AIGAIN(0644, | 189 | static IIO_DEV_ATTR_AIGAIN(0644, |
| @@ -415,8 +415,8 @@ static int ade7854_set_irq(struct device *dev, bool enable) · 225 unmodified lines | |||
| 415 | int ret; | 415 | int ret; |
| 416 | u32 irqen; | 416 | u32 irqen; |
| 417 | 417 | ||
| 418 | ret = st->read_reg_32(dev, ADE7854_MASK0, &irqen); | 418 | ret = st->read_reg(dev, ADE7854_MASK0, &irqen, 32); |
| 419 | if (ret) | 419 | if (ret < 0) |
| 420 | return ret; | 420 | return ret; |
| 421 | 421 | ||
| 422 | if (enable) | 422 | if (enable) |
| @@ -426,7 +426,7 @@ static int ade7854_set_irq(struct device *dev, bool enable) · 3 unmodified lines | |||
| 426 | else | 426 | else |
| 427 | irqen &= ~BIT(17); | 427 | irqen &= ~BIT(17); |
| 428 | 428 | ||
| 429 | return st->write_reg_32(dev, ADE7854_MASK0, irqen); | 429 | return st->write_reg(dev, ADE7854_MASK0, irqen, 32); |
| 430 | } | 430 | } |
| 431 | 431 | ||
| 432 | static int ade7854_initial_setup(struct iio_dev *indio_dev) | 432 | static int ade7854_initial_setup(struct iio_dev *indio_dev) |
| @@ -145,7 +145,9 @@ · 144 unmodified lines | |||
| 145 | 145 | ||
| 146 | /** | 146 | /** |
| 147 | * struct ade7854_state - device instance specific data | 147 | * struct ade7854_state - device instance specific data |
| 148 | * @spi: actual spi_device | 148 | * @spi: actual spi_device |
| 149 | * @read_reg Wrapper function for I2C and SPI read | ||
| 150 | * @write_reg Wrapper function for I2C and SPI write | ||
| 149 | * @indio_dev: industrial I/O device structure | 151 | * @indio_dev: industrial I/O device structure |
| 150 | * @buf_lock: mutex to protect tx and rx | 152 | * @buf_lock: mutex to protect tx and rx |
| 151 | * @tx: transmit buffer | 153 | * @tx: transmit buffer |
| @@ -154,14 +156,10 @@ · 2 unmodified lines | |||
| 154 | struct ade7854_state { | 156 | struct ade7854_state { |
| 155 | struct spi_device *spi; | 157 | struct spi_device *spi; |
| 156 | struct i2c_client *i2c; | 158 | struct i2c_client *i2c; |
| 157 | int (*read_reg_8)(struct device *dev, u16 reg_address, u8 *val); | 159 | int (*read_reg)(struct device *dev, u16 reg_address, u32 *val, |
| 158 | int (*read_reg_16)(struct device *dev, u16 reg_address, u16 *val); | 160 | int bits); |
| 159 | int (*read_reg_24)(struct device *dev, u16 reg_address, u32 *val); | 161 | int (*write_reg)(struct device *dev, u16 reg_address, u32 val, |
| 160 | int (*read_reg_32)(struct device *dev, u16 reg_address, u32 *val); | 162 | int bits); |
| 161 | int (*write_reg_8)(struct device *dev, u16 reg_address, u8 val); | ||
| 162 | int (*write_reg_16)(struct device *dev, u16 reg_address, u16 val); | ||
| 163 | int (*write_reg_24)(struct device *dev, u16 reg_address, u32 val); | ||
| 164 | int (*write_reg_32)(struct device *dev, u16 reg_address, u32 val); | ||
| 165 | int irq; | 163 | int irq; |
| 166 | struct mutex buf_lock; | 164 | struct mutex buf_lock; |
| 167 | u8 tx[ADE7854_MAX_TX] ____cacheline_aligned; | 165 | u8 tx[ADE7854_MAX_TX] ____cacheline_aligned; |
| @@ -13,18 +13,6 @@ config AD2S90 · 12 unmodified lines | |||
| 13 | To compile this driver as a module, choose M here: the | 13 | To compile this driver as a module, choose M here: the |
| 14 | module will be called ad2s90. | 14 | module will be called ad2s90. |
| 15 | 15 | ||
| 16 | config AD2S1200 | ||
| 17 | tristate "Analog Devices ad2s1200/ad2s1205 driver" | ||
| 18 | depends on SPI | ||
| 19 | depends on GPIOLIB || COMPILE_TEST | ||
| 20 | help | ||
| 21 | Say yes here to build support for Analog Devices spi resolver | ||
| 22 | to digital converters, ad2s1200 and ad2s1205, provides direct access | ||
| 23 | via sysfs. | ||
| 24 | |||
| 25 | To compile this driver as a module, choose M here: the | ||
| 26 | module will be called ad2s1200. | ||
| 27 | |||
| 28 | config AD2S1210 | 16 | config AD2S1210 |
| 29 | tristate "Analog Devices ad2s1210 driver" | 17 | tristate "Analog Devices ad2s1210 driver" |
| 30 | depends on SPI | 18 | depends on SPI |
| @@ -3,5 +3,4 @@ · 2 unmodified lines | |||
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_AD2S90) += ad2s90.o | 5 | obj-$(CONFIG_AD2S90) += ad2s90.o |
| 6 | obj-$(CONFIG_AD2S1200) += ad2s1200.o | ||
| 7 | obj-$(CONFIG_AD2S1210) += ad2s1210.o | 6 | obj-$(CONFIG_AD2S1210) += ad2s1210.o |
| @@ -1,61 +0,0 @@ | |||
| 1 | # | ||
| 2 | # IPX configuration | ||
| 3 | # | ||
| 4 | config IPX | ||
| 5 | tristate "The IPX protocol" | ||
| 6 | depends on NET | ||
| 7 | select LLC | ||
| 8 | ---help--- | ||
| 9 | This is support for the Novell networking protocol, IPX, commonly | ||
| 10 | used for local networks of Windows machines. You need it if you | ||
| 11 | want to access Novell NetWare file or print servers using the Linux | ||
| 12 | Novell client ncpfs (available from | ||
| 13 | <ftp://platan.vc.cvut.cz/pub/linux/ncpfs/>) or from | ||
| 14 | within the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, | ||
| 15 | available from <http://www.tldp.org/docs.html#howto>). In order | ||
| 16 | to do the former, you'll also have to say Y to "NCP file system | ||
| 17 | support", below. | ||
| 18 | |||
| 19 | IPX is similar in scope to IP, while SPX, which runs on top of IPX, | ||
| 20 | is similar to TCP. | ||
| 21 | |||
| 22 | To turn your Linux box into a fully featured NetWare file server and | ||
| 23 | IPX router, say Y here and fetch either lwared from | ||
| 24 | <ftp://ibiblio.org/pub/Linux/system/network/daemons/> or | ||
| 25 | mars_nwe from <ftp://www.compu-art.de/mars_nwe/>. For more | ||
| 26 | information, read the IPX-HOWTO available from | ||
| 27 | <http://www.tldp.org/docs.html#howto>. | ||
| 28 | |||
| 29 | The IPX driver would enlarge your kernel by about 16 KB. To compile | ||
| 30 | this driver as a module, choose M here: the module will be called ipx. | ||
| 31 | Unless you want to integrate your Linux box with a local Novell | ||
| 32 | network, say N. | ||
| 33 | |||
| 34 | config IPX_INTERN | ||
| 35 | bool "IPX: Full internal IPX network" | ||
| 36 | depends on IPX | ||
| 37 | ---help--- | ||
| 38 | Every IPX network has an address that identifies it. Sometimes it is | ||
| 39 | useful to give an IPX "network" address to your Linux box as well | ||
| 40 | (for example if your box is acting as a file server for different | ||
| 41 | IPX networks: it will then be accessible from everywhere using the | ||
| 42 | same address). The way this is done is to create a virtual internal | ||
| 43 | "network" inside your box and to assign an IPX address to this | ||
| 44 | network. Say Y here if you want to do this; read the IPX-HOWTO at | ||
| 45 | <http://www.tldp.org/docs.html#howto> for details. | ||
| 46 | |||
| 47 | The full internal IPX network enables you to allocate sockets on | ||
| 48 | different virtual nodes of the internal network. This is done by | ||
| 49 | evaluating the field sipx_node of the socket address given to the | ||
| 50 | bind call. So applications should always initialize the node field | ||
| 51 | to 0 when binding a socket on the primary network. In this case the | ||
| 52 | socket is assigned the default node that has been given to the | ||
| 53 | kernel when the internal network was created. By enabling the full | ||
| 54 | internal IPX network the cross-forwarding of packets targeted at | ||
| 55 | 'special' sockets to sockets listening on the primary network is | ||
| 56 | disabled. This might break existing applications, especially RIP/SAP | ||
| 57 | daemons. A RIP/SAP daemon that works well with the full internal net | ||
| 58 | can be found on <ftp://ftp.gwdg.de/pub/linux/misc/ncpfs/>. | ||
| 59 | |||
| 60 | If you don't know what you are doing, say N. | ||
| 61 | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the Linux IPX layer. | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_IPX) += ipx.o | ||
| 6 | |||
| 7 | ipx-y := af_ipx.o ipx_route.o ipx_proc.o pe2.o | ||
| 8 | ipx-$(CONFIG_SYSCTL) += sysctl_net_ipx.o | ||
| @@ -1,4 +0,0 @@ | |||
| 1 | The ipx code will be removed soon from the kernel tree as it is old and | ||
| 2 | obsolete and broken. | ||
| 3 | |||
| 4 | Don't worry about fixing up anything here, it's not needed. | ||
| @@ -1,2082 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Implements an IPX socket layer. | ||
| 3 | * | ||
| 4 | * This code is derived from work by | ||
| 5 | * Ross Biro : Writing the original IP stack | ||
| 6 | * Fred Van Kempen : Tidying up the TCP/IP | ||
| 7 | * | ||
| 8 | * Many thanks go to Keith Baker, Institute For Industrial Information | ||
| 9 | * Technology Ltd, Swansea University for allowing me to work on this | ||
| 10 | * in my own time even though it was in some ways related to commercial | ||
| 11 | * work I am currently employed to do there. | ||
| 12 | * | ||
| 13 | * All the material in this file is subject to the Gnu license version 2. | ||
| 14 | * Neither Alan Cox nor the Swansea University Computer Society admit | ||
| 15 | * liability nor provide warranty for any of this software. This material | ||
| 16 | * is provided as is and at no charge. | ||
| 17 | * | ||
| 18 | * Portions Copyright (c) 2000-2003 Conectiva, Inc. <acme@conectiva.com.br> | ||
| 19 | * Neither Arnaldo Carvalho de Melo nor Conectiva, Inc. admit liability nor | ||
| 20 | * provide warranty for any of this software. This material is provided | ||
| 21 | * "AS-IS" and at no charge. | ||
| 22 | * | ||
| 23 | * Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com> | ||
| 24 | * Neither Greg Page nor Caldera, Inc. admit liability nor provide | ||
| 25 | * warranty for any of this software. This material is provided | ||
| 26 | * "AS-IS" and at no charge. | ||
| 27 | * | ||
| 28 | * See net/ipx/ChangeLog. | ||
| 29 | */ | ||
| 30 | |||
| 31 | #include <linux/capability.h> | ||
| 32 | #include <linux/errno.h> | ||
| 33 | #include <linux/if_arp.h> | ||
| 34 | #include <linux/if_ether.h> | ||
| 35 | #include <linux/init.h> | ||
| 36 | #include <linux/ipx.h> | ||
| 37 | #include <linux/kernel.h> | ||
| 38 | #include <linux/list.h> | ||
| 39 | #include <linux/module.h> | ||
| 40 | #include <linux/net.h> | ||
| 41 | #include <linux/netdevice.h> | ||
| 42 | #include <linux/uio.h> | ||
| 43 | #include <linux/slab.h> | ||
| 44 | #include <linux/skbuff.h> | ||
| 45 | #include <linux/socket.h> | ||
| 46 | #include <linux/sockios.h> | ||
| 47 | #include <linux/string.h> | ||
| 48 | #include <linux/types.h> | ||
| 49 | #include <linux/termios.h> | ||
| 50 | |||
| 51 | #include <net/ipx.h> | ||
| 52 | #include <net/p8022.h> | ||
| 53 | #include <net/psnap.h> | ||
| 54 | #include <net/sock.h> | ||
| 55 | #include <net/datalink.h> | ||
| 56 | #include <net/tcp_states.h> | ||
| 57 | #include <net/net_namespace.h> | ||
| 58 | |||
| 59 | #include <linux/uaccess.h> | ||
| 60 | |||
| 61 | /* Configuration Variables */ | ||
| 62 | static unsigned char ipxcfg_max_hops = 16; | ||
| 63 | static char ipxcfg_auto_select_primary; | ||
| 64 | static char ipxcfg_auto_create_interfaces; | ||
| 65 | int sysctl_ipx_pprop_broadcasting = 1; | ||
| 66 | |||
| 67 | /* Global Variables */ | ||
| 68 | static struct datalink_proto *p8022_datalink; | ||
| 69 | static struct datalink_proto *pEII_datalink; | ||
| 70 | static struct datalink_proto *p8023_datalink; | ||
| 71 | static struct datalink_proto *pSNAP_datalink; | ||
| 72 | |||
| 73 | static const struct proto_ops ipx_dgram_ops; | ||
| 74 | |||
| 75 | LIST_HEAD(ipx_interfaces); | ||
| 76 | DEFINE_SPINLOCK(ipx_interfaces_lock); | ||
| 77 | |||
| 78 | struct ipx_interface *ipx_primary_net; | ||
| 79 | struct ipx_interface *ipx_internal_net; | ||
| 80 | |||
| 81 | struct ipx_interface *ipx_interfaces_head(void) | ||
| 82 | { | ||
| 83 | struct ipx_interface *rc = NULL; | ||
| 84 | |||
| 85 | if (!list_empty(&ipx_interfaces)) | ||
| 86 | rc = list_entry(ipx_interfaces.next, | ||
| 87 | struct ipx_interface, node); | ||
| 88 | return rc; | ||
| 89 | } | ||
| 90 | |||
| 91 | static void ipxcfg_set_auto_select(char val) | ||
| 92 | { | ||
| 93 | ipxcfg_auto_select_primary = val; | ||
| 94 | if (val && !ipx_primary_net) | ||
| 95 | ipx_primary_net = ipx_interfaces_head(); | ||
| 96 | } | ||
| 97 | |||
| 98 | static int ipxcfg_get_config_data(struct ipx_config_data __user *arg) | ||
| 99 | { | ||
| 100 | struct ipx_config_data vals; | ||
| 101 | |||
| 102 | vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces; | ||
| 103 | vals.ipxcfg_auto_select_primary = ipxcfg_auto_select_primary; | ||
| 104 | |||
| 105 | return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | /* | ||
| 109 | * Note: Sockets may not be removed _during_ an interrupt or inet_bh | ||
| 110 | * handler using this technique. They can be added although we do not | ||
| 111 | * use this facility. | ||
| 112 | */ | ||
| 113 | |||
| 114 | static void ipx_remove_socket(struct sock *sk) | ||
| 115 | { | ||
| 116 | /* Determine interface with which socket is associated */ | ||
| 117 | struct ipx_interface *intrfc = ipx_sk(sk)->intrfc; | ||
| 118 | |||
| 119 | if (!intrfc) | ||
| 120 | goto out; | ||
| 121 | |||
| 122 | ipxitf_hold(intrfc); | ||
| 123 | spin_lock_bh(&intrfc->if_sklist_lock); | ||
| 124 | sk_del_node_init(sk); | ||
| 125 | spin_unlock_bh(&intrfc->if_sklist_lock); | ||
| 126 | ipxitf_put(intrfc); | ||
| 127 | out: | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | |||
| 131 | static void ipx_destroy_socket(struct sock *sk) | ||
| 132 | { | ||
| 133 | ipx_remove_socket(sk); | ||
| 134 | skb_queue_purge(&sk->sk_receive_queue); | ||
| 135 | sk_refcnt_debug_dec(sk); | ||
| 136 | } | ||
| 137 | |||
| 138 | /* | ||
| 139 | * The following code is used to support IPX Interfaces (IPXITF). An | ||
| 140 | * IPX interface is defined by a physical device and a frame type. | ||
| 141 | */ | ||
| 142 | |||
| 143 | /* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */ | ||
| 144 | |||
| 145 | static void ipxitf_clear_primary_net(void) | ||
| 146 | { | ||
| 147 | ipx_primary_net = NULL; | ||
| 148 | if (ipxcfg_auto_select_primary) | ||
| 149 | ipx_primary_net = ipx_interfaces_head(); | ||
| 150 | } | ||
| 151 | |||
| 152 | static struct ipx_interface *__ipxitf_find_using_phys(struct net_device *dev, | ||
| 153 | __be16 datalink) | ||
| 154 | { | ||
| 155 | struct ipx_interface *i; | ||
| 156 | |||
| 157 | list_for_each_entry(i, &ipx_interfaces, node) | ||
| 158 | if (i->if_dev == dev && i->if_dlink_type == datalink) | ||
| 159 | goto out; | ||
| 160 | i = NULL; | ||
| 161 | out: | ||
| 162 | return i; | ||
| 163 | } | ||
| 164 | |||
| 165 | static struct ipx_interface *ipxitf_find_using_phys(struct net_device *dev, | ||
| 166 | __be16 datalink) | ||
| 167 | { | ||
| 168 | struct ipx_interface *i; | ||
| 169 | |||
| 170 | spin_lock_bh(&ipx_interfaces_lock); | ||
| 171 | i = __ipxitf_find_using_phys(dev, datalink); | ||
| 172 | if (i) | ||
| 173 | ipxitf_hold(i); | ||
| 174 | spin_unlock_bh(&ipx_interfaces_lock); | ||
| 175 | return i; | ||
| 176 | } | ||
| 177 | |||
| 178 | struct ipx_interface *ipxitf_find_using_net(__be32 net) | ||
| 179 | { | ||
| 180 | struct ipx_interface *i; | ||
| 181 | |||
| 182 | spin_lock_bh(&ipx_interfaces_lock); | ||
| 183 | if (net) { | ||
| 184 | list_for_each_entry(i, &ipx_interfaces, node) | ||
| 185 | if (i->if_netnum == net) | ||
| 186 | goto hold; | ||
| 187 | i = NULL; | ||
| 188 | goto unlock; | ||
| 189 | } | ||
| 190 | |||
| 191 | i = ipx_primary_net; | ||
| 192 | if (i) | ||
| 193 | hold: | ||
| 194 | ipxitf_hold(i); | ||
| 195 | unlock: | ||
| 196 | spin_unlock_bh(&ipx_interfaces_lock); | ||
| 197 | return i; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* Sockets are bound to a particular IPX interface. */ | ||
| 201 | static void ipxitf_insert_socket(struct ipx_interface *intrfc, struct sock *sk) | ||
| 202 | { | ||
| 203 | ipxitf_hold(intrfc); | ||
| 204 | spin_lock_bh(&intrfc->if_sklist_lock); | ||
| 205 | ipx_sk(sk)->intrfc = intrfc; | ||
| 206 | sk_add_node(sk, &intrfc->if_sklist); | ||
| 207 | spin_unlock_bh(&intrfc->if_sklist_lock); | ||
| 208 | ipxitf_put(intrfc); | ||
| 209 | } | ||
| 210 | |||
| 211 | /* caller must hold intrfc->if_sklist_lock */ | ||
| 212 | static struct sock *__ipxitf_find_socket(struct ipx_interface *intrfc, | ||
| 213 | __be16 port) | ||
| 214 | { | ||
| 215 | struct sock *s; | ||
| 216 | |||
| 217 | sk_for_each(s, &intrfc->if_sklist) | ||
| 218 | if (ipx_sk(s)->port == port) | ||
| 219 | goto found; | ||
| 220 | s = NULL; | ||
| 221 | found: | ||
| 222 | return s; | ||
| 223 | } | ||
| 224 | |||
| 225 | /* caller must hold a reference to intrfc */ | ||
| 226 | static struct sock *ipxitf_find_socket(struct ipx_interface *intrfc, | ||
| 227 | __be16 port) | ||
| 228 | { | ||
| 229 | struct sock *s; | ||
| 230 | |||
| 231 | spin_lock_bh(&intrfc->if_sklist_lock); | ||
| 232 | s = __ipxitf_find_socket(intrfc, port); | ||
| 233 | if (s) | ||
| 234 | sock_hold(s); | ||
| 235 | spin_unlock_bh(&intrfc->if_sklist_lock); | ||
| 236 | |||
| 237 | return s; | ||
| 238 | } | ||
| 239 | |||
| 240 | #ifdef CONFIG_IPX_INTERN | ||
| 241 | static struct sock *ipxitf_find_internal_socket(struct ipx_interface *intrfc, | ||
| 242 | unsigned char *ipx_node, | ||
| 243 | __be16 port) | ||
| 244 | { | ||
| 245 | struct sock *s; | ||
| 246 | |||
| 247 | ipxitf_hold(intrfc); | ||
| 248 | spin_lock_bh(&intrfc->if_sklist_lock); | ||
| 249 | |||
| 250 | sk_for_each(s, &intrfc->if_sklist) { | ||
| 251 | struct ipx_sock *ipxs = ipx_sk(s); | ||
| 252 | |||
| 253 | if (ipxs->port == port && | ||
| 254 | !memcmp(ipx_node, ipxs->node, IPX_NODE_LEN)) | ||
| 255 | goto found; | ||
| 256 | } | ||
| 257 | s = NULL; | ||
| 258 | found: | ||
| 259 | spin_unlock_bh(&intrfc->if_sklist_lock); | ||
| 260 | ipxitf_put(intrfc); | ||
| 261 | return s; | ||
| 262 | } | ||
| 263 | #endif | ||
| 264 | |||
| 265 | static void __ipxitf_down(struct ipx_interface *intrfc) | ||
| 266 | { | ||
| 267 | struct sock *s; | ||
| 268 | struct hlist_node *t; | ||
| 269 | |||
| 270 | /* Delete all routes associated with this interface */ | ||
| 271 | ipxrtr_del_routes(intrfc); | ||
| 272 | |||
| 273 | spin_lock_bh(&intrfc->if_sklist_lock); | ||
| 274 | /* error sockets */ | ||
| 275 | sk_for_each_safe(s, t, &intrfc->if_sklist) { | ||
| 276 | struct ipx_sock *ipxs = ipx_sk(s); | ||
| 277 | |||
| 278 | s->sk_err = ENOLINK; | ||
| 279 | s->sk_error_report(s); | ||
| 280 | ipxs->intrfc = NULL; | ||
| 281 | ipxs->port = 0; | ||
| 282 | sock_set_flag(s, SOCK_ZAPPED); /* Indicates it is no longer bound */ | ||
| 283 | sk_del_node_init(s); | ||
| 284 | } | ||
| 285 | INIT_HLIST_HEAD(&intrfc->if_sklist); | ||
| 286 | spin_unlock_bh(&intrfc->if_sklist_lock); | ||
| 287 | |||
| 288 | /* remove this interface from list */ | ||
| 289 | list_del(&intrfc->node); | ||
| 290 | |||
| 291 | /* remove this interface from *special* networks */ | ||
| 292 | if (intrfc == ipx_primary_net) | ||
| 293 | ipxitf_clear_primary_net(); | ||
| 294 | if (intrfc == ipx_internal_net) | ||
| 295 | ipx_internal_net = NULL; | ||
| 296 | |||
| 297 | if (intrfc->if_dev) | ||
| 298 | dev_put(intrfc->if_dev); | ||
| 299 | kfree(intrfc); | ||
| 300 | } | ||
| 301 | |||
| 302 | void ipxitf_down(struct ipx_interface *intrfc) | ||
| 303 | { | ||
| 304 | spin_lock_bh(&ipx_interfaces_lock); | ||
| 305 | __ipxitf_down(intrfc); | ||
| 306 | spin_unlock_bh(&ipx_interfaces_lock); | ||
| 307 | } | ||
| 308 | |||
| 309 | static void __ipxitf_put(struct ipx_interface *intrfc) | ||
| 310 | { | ||
| 311 | if (refcount_dec_and_test(&intrfc->refcnt)) | ||
| 312 | __ipxitf_down(intrfc); | ||
| 313 | } | ||
| 314 | |||
| 315 | static int ipxitf_device_event(struct notifier_block *notifier, | ||
| 316 | unsigned long event, void *ptr) | ||
| 317 | { | ||
| 318 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||
| 319 | struct ipx_interface *i, *tmp; | ||
| 320 | |||
| 321 | if (!net_eq(dev_net(dev), &init_net)) | ||
| 322 | return NOTIFY_DONE; | ||
| 323 | |||
| 324 | if (event != NETDEV_DOWN && event != NETDEV_UP) | ||
| 325 | goto out; | ||
| 326 | |||
| 327 | spin_lock_bh(&ipx_interfaces_lock); | ||
| 328 | list_for_each_entry_safe(i, tmp, &ipx_interfaces, node) | ||
| 329 | if (i->if_dev == dev) { | ||
| 330 | if (event == NETDEV_UP) | ||
| 331 | ipxitf_hold(i); | ||
| 332 | else | ||
| 333 | __ipxitf_put(i); | ||
| 334 | } | ||
| 335 | spin_unlock_bh(&ipx_interfaces_lock); | ||
| 336 | out: | ||
| 337 | return NOTIFY_DONE; | ||
| 338 | } | ||
| 339 | |||
| 340 | |||
| 341 | static __exit void ipxitf_cleanup(void) | ||
| 342 | { | ||
| 343 | struct ipx_interface *i, *tmp; | ||
| 344 | |||
| 345 | spin_lock_bh(&ipx_interfaces_lock); | ||
| 346 | list_for_each_entry_safe(i, tmp, &ipx_interfaces, node) | ||
| 347 | __ipxitf_put(i); | ||
| 348 | spin_unlock_bh(&ipx_interfaces_lock); | ||
| 349 | } | ||
| 350 | |||
| 351 | static void ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb) | ||
| 352 | { | ||
| 353 | if (sock_queue_rcv_skb(sock, skb) < 0) | ||
| 354 | kfree_skb(skb); | ||
| 355 | } | ||
| 356 | |||
| 357 | /* | ||
| 358 | * On input skb->sk is NULL. Nobody is charged for the memory. | ||
| 359 | */ | ||
| 360 | |||
| 361 | /* caller must hold a reference to intrfc */ | ||
| 362 | |||
| 363 | #ifdef CONFIG_IPX_INTERN | ||
| 364 | static int ipxitf_demux_socket(struct ipx_interface *intrfc, | ||
| 365 | struct sk_buff *skb, int copy) | ||
| 366 | { | ||
| 367 | struct ipxhdr *ipx = ipx_hdr(skb); | ||
| 368 | int is_broadcast = !memcmp(ipx->ipx_dest.node, ipx_broadcast_node, | ||
| 369 | IPX_NODE_LEN); | ||
| 370 | struct sock *s; | ||
| 371 | int rc; | ||
| 372 | |||
| 373 | spin_lock_bh(&intrfc->if_sklist_lock); | ||
| 374 | |||
| 375 | sk_for_each(s, &intrfc->if_sklist) { | ||
| 376 | struct ipx_sock *ipxs = ipx_sk(s); | ||
| 377 | |||
| 378 | if (ipxs->port == ipx->ipx_dest.sock && | ||
| 379 | (is_broadcast || !memcmp(ipx->ipx_dest.node, | ||
| 380 | ipxs->node, IPX_NODE_LEN))) { | ||
| 381 | /* We found a socket to which to send */ | ||
| 382 | struct sk_buff *skb1; | ||
| 383 | |||
| 384 | if (copy) { | ||
| 385 | skb1 = skb_clone(skb, GFP_ATOMIC); | ||
| 386 | rc = -ENOMEM; | ||
| 387 | if (!skb1) | ||
| 388 | goto out; | ||
| 389 | } else { | ||
| 390 | skb1 = skb; | ||
| 391 | copy = 1; /* skb may only be used once */ | ||
| 392 | } | ||
| 393 | ipxitf_def_skb_handler(s, skb1); | ||
| 394 | |||
| 395 | /* On an external interface, one socket can listen */ | ||
| 396 | if (intrfc != ipx_internal_net) | ||
| 397 | break; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | /* skb was solely for us, and we did not make a copy, so free it. */ | ||
| 402 | if (!copy) | ||
| 403 | kfree_skb(skb); | ||
| 404 | |||
| 405 | rc = 0; | ||
| 406 | out: | ||
| 407 | spin_unlock_bh(&intrfc->if_sklist_lock); | ||
| 408 | return rc; | ||
| 409 | } | ||
| 410 | #else | ||
| 411 | static struct sock *ncp_connection_hack(struct ipx_interface *intrfc, | ||
| 412 | struct ipxhdr *ipx) | ||
| 413 | { | ||
| 414 | /* The packet's target is a NCP connection handler. We want to hand it | ||
| 415 | * to the correct socket directly within the kernel, so that the | ||
| 416 | * mars_nwe packet distribution process does not have to do it. Here we | ||
| 417 | * only care about NCP and BURST packets. | ||
| 418 | * | ||
| 419 | * You might call this a hack, but believe me, you do not want a | ||
| 420 | * complete NCP layer in the kernel, and this is VERY fast as well. */ | ||
| 421 | struct sock *sk = NULL; | ||
| 422 | int connection = 0; | ||
| 423 | u8 *ncphdr = (u8 *)(ipx + 1); | ||
| 424 | |||
| 425 | if (*ncphdr == 0x22 && *(ncphdr + 1) == 0x22) /* NCP request */ | ||
| 426 | connection = (((int) *(ncphdr + 5)) << 8) | (int) *(ncphdr + 3); | ||
| 427 | else if (*ncphdr == 0x77 && *(ncphdr + 1) == 0x77) /* BURST packet */ | ||
| 428 | connection = (((int) *(ncphdr + 9)) << 8) | (int) *(ncphdr + 8); | ||
| 429 | |||
| 430 | if (connection) { | ||
| 431 | /* Now we have to look for a special NCP connection handling | ||
| 432 | * socket. Only these sockets have ipx_ncp_conn != 0, set by | ||
| 433 | * SIOCIPXNCPCONN. */ | ||
| 434 | spin_lock_bh(&intrfc->if_sklist_lock); | ||
| 435 | sk_for_each(sk, &intrfc->if_sklist) | ||
| 436 | if (ipx_sk(sk)->ipx_ncp_conn == connection) { | ||
| 437 | sock_hold(sk); | ||
| 438 | goto found; | ||
| 439 | } | ||
| 440 | sk = NULL; | ||
| 441 | found: | ||
| 442 | spin_unlock_bh(&intrfc->if_sklist_lock); | ||
| 443 | } | ||
| 444 | return sk; | ||
| 445 | } | ||
| 446 | |||
| 447 | static int ipxitf_demux_socket(struct ipx_interface *intrfc, | ||
| 448 | struct sk_buff *skb, int copy) | ||
| 449 | { | ||
| 450 | struct ipxhdr *ipx = ipx_hdr(skb); | ||
| 451 | struct sock *sock1 = NULL, *sock2 = NULL; | ||
| 452 | struct sk_buff *skb1 = NULL, *skb2 = NULL; | ||
| 453 | int rc; | ||
| 454 | |||
| 455 | if (intrfc == ipx_primary_net && ntohs(ipx->ipx_dest.sock) == 0x451) | ||
| 456 | sock1 = ncp_connection_hack(intrfc, ipx); | ||
| 457 | if (!sock1) | ||
| 458 | /* No special socket found, forward the packet the normal way */ | ||
| 459 | sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock); | ||
| 460 | |||
| 461 | /* | ||
| 462 | * We need to check if there is a primary net and if | ||
| 463 | * this is addressed to one of the *SPECIAL* sockets because | ||
| 464 | * these need to be propagated to the primary net. | ||
| 465 | * The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and | ||
| 466 | * 0x456(Diagnostic). | ||
| 467 | */ | ||
| 468 | |||
| 469 | if (ipx_primary_net && intrfc != ipx_primary_net) { | ||
| 470 | const int dsock = ntohs(ipx->ipx_dest.sock); | ||
| 471 | |||
| 472 | if (dsock == 0x452 || dsock == 0x453 || dsock == 0x456) | ||
| 473 | /* The appropriate thing to do here is to dup the | ||
| 474 | * packet and route to the primary net interface via | ||
| 475 | * ipxitf_send; however, we'll cheat and just demux it | ||
| 476 | * here. */ | ||
| 477 | sock2 = ipxitf_find_socket(ipx_primary_net, | ||
| 478 | ipx->ipx_dest.sock); | ||
| 479 | } | ||
| 480 | |||
| 481 | /* | ||
| 482 | * If there is nothing to do return. The kfree will cancel any charging. | ||
| 483 | */ | ||
| 484 | rc = 0; | ||
| 485 | if (!sock1 && !sock2) { | ||
| 486 | if (!copy) | ||
| 487 | kfree_skb(skb); | ||
| 488 | goto out; | ||
| 489 | } | ||
| 490 | |||
| 491 | /* | ||
| 492 | * This next segment of code is a little awkward, but it sets it up | ||
| 493 | * so that the appropriate number of copies of the SKB are made and | ||
| 494 | * that skb1 and skb2 point to it (them) so that it (they) can be | ||
| 495 | * demuxed to sock1 and/or sock2. If we are unable to make enough | ||
| 496 | * copies, we do as much as is possible. | ||
| 497 | */ | ||
| 498 | |||
| 499 | if (copy) | ||
| 500 | skb1 = skb_clone(skb, GFP_ATOMIC); | ||
| 501 | else | ||
| 502 | skb1 = skb; | ||
| 503 | |||
| 504 | rc = -ENOMEM; | ||
| 505 | if (!skb1) | ||
| 506 | goto out_put; | ||
| 507 | |||
| 508 | /* Do we need 2 SKBs? */ | ||
| 509 | if (sock1 && sock2) | ||
| 510 | skb2 = skb_clone(skb1, GFP_ATOMIC); | ||
| 511 | else | ||
| 512 | skb2 = skb1; | ||
| 513 | |||
| 514 | if (sock1) | ||
| 515 | ipxitf_def_skb_handler(sock1, skb1); | ||
| 516 | |||
| 517 | if (!skb2) | ||
| 518 | goto out_put; | ||
| 519 | |||
| 520 | if (sock2) | ||
| 521 | ipxitf_def_skb_handler(sock2, skb2); | ||
| 522 | |||
| 523 | rc = 0; | ||
| 524 | out_put: | ||
| 525 | if (sock1) | ||
| 526 | sock_put(sock1); | ||
| 527 | if (sock2) | ||
| 528 | sock_put(sock2); | ||
| 529 | out: | ||
| 530 | return rc; | ||
| 531 | } | ||
| 532 | #endif /* CONFIG_IPX_INTERN */ | ||
| 533 | |||
| 534 | static struct sk_buff *ipxitf_adjust_skbuff(struct ipx_interface *intrfc, | ||
| 535 | struct sk_buff *skb) | ||
| 536 | { | ||
| 537 | struct sk_buff *skb2; | ||
| 538 | int in_offset = (unsigned char *)ipx_hdr(skb) - skb->head; | ||
| 539 | int out_offset = intrfc->if_ipx_offset; | ||
| 540 | int len; | ||
| 541 | |||
| 542 | /* Hopefully, most cases */ | ||
| 543 | if (in_offset >= out_offset) | ||
| 544 | return skb; | ||
| 545 | |||
| 546 | /* Need new SKB */ | ||
| 547 | len = skb->len + out_offset; | ||
| 548 | skb2 = alloc_skb(len, GFP_ATOMIC); | ||
| 549 | if (skb2) { | ||
| 550 | skb_reserve(skb2, out_offset); | ||
| 551 | skb_reset_network_header(skb2); | ||
| 552 | skb_reset_transport_header(skb2); | ||
| 553 | skb_put(skb2, skb->len); | ||
| 554 | memcpy(ipx_hdr(skb2), ipx_hdr(skb), skb->len); | ||
| 555 | memcpy(skb2->cb, skb->cb, sizeof(skb->cb)); | ||
| 556 | } | ||
| 557 | kfree_skb(skb); | ||
| 558 | return skb2; | ||
| 559 | } | ||
| 560 | |||
| 561 | /* caller must hold a reference to intrfc and the skb has to be unshared */ | ||
| 562 | int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node) | ||
| 563 | { | ||
| 564 | struct ipxhdr *ipx = ipx_hdr(skb); | ||
| 565 | struct net_device *dev = intrfc->if_dev; | ||
| 566 | struct datalink_proto *dl = intrfc->if_dlink; | ||
| 567 | char dest_node[IPX_NODE_LEN]; | ||
| 568 | int send_to_wire = 1; | ||
| 569 | int addr_len; | ||
| 570 | |||
| 571 | ipx->ipx_tctrl = IPX_SKB_CB(skb)->ipx_tctrl; | ||
| 572 | ipx->ipx_dest.net = IPX_SKB_CB(skb)->ipx_dest_net; | ||
| 573 | ipx->ipx_source.net = IPX_SKB_CB(skb)->ipx_source_net; | ||
| 574 | |||
| 575 | /* see if we need to include the netnum in the route list */ | ||
| 576 | if (IPX_SKB_CB(skb)->last_hop.index >= 0) { | ||
| 577 | __be32 *last_hop = (__be32 *)(((u8 *) skb->data) + | ||
| 578 | sizeof(struct ipxhdr) + | ||
| 579 | IPX_SKB_CB(skb)->last_hop.index * | ||
| 580 | sizeof(__be32)); | ||
| 581 | *last_hop = IPX_SKB_CB(skb)->last_hop.netnum; | ||
| 582 | IPX_SKB_CB(skb)->last_hop.index = -1; | ||
| 583 | } | ||
| 584 | |||
| 585 | /* | ||
| 586 | * We need to know how many skbuffs it will take to send out this | ||
| 587 | * packet to avoid unnecessary copies. | ||
| 588 | */ | ||
| 589 | |||
| 590 | if (!dl || !dev || dev->flags & IFF_LOOPBACK) | ||
| 591 | send_to_wire = 0; /* No non looped */ | ||
| 592 | |||
| 593 | /* | ||
| 594 | * See if this should be demuxed to sockets on this interface | ||
| 595 | * | ||
| 596 | * We want to ensure the original was eaten or that we only use | ||
| 597 | * up clones. | ||
| 598 | */ | ||
| 599 | |||
| 600 | if (ipx->ipx_dest.net == intrfc->if_netnum) { | ||
| 601 | /* | ||
| 602 | * To our own node, loop and free the original. | ||
| 603 | * The internal net will receive on all node address. | ||
| 604 | */ | ||
| 605 | if (intrfc == ipx_internal_net || | ||
| 606 | !memcmp(intrfc->if_node, node, IPX_NODE_LEN)) { | ||
| 607 | /* Don't charge sender */ | ||
| 608 | skb_orphan(skb); | ||
| 609 | |||
| 610 | /* Will charge receiver */ | ||
| 611 | return ipxitf_demux_socket(intrfc, skb, 0); | ||
| 612 | } | ||
| 613 | |||
| 614 | /* Broadcast, loop and possibly keep to send on. */ | ||
| 615 | if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN)) { | ||
| 616 | if (!send_to_wire) | ||
| 617 | skb_orphan(skb); | ||
| 618 | ipxitf_demux_socket(intrfc, skb, send_to_wire); | ||
| 619 | if (!send_to_wire) | ||
| 620 | goto out; | ||
| 621 | } | ||
| 622 | } | ||
| 623 | |||
| 624 | /* | ||
| 625 | * If the originating net is not equal to our net; this is routed | ||
| 626 | * We are still charging the sender. Which is right - the driver | ||
| 627 | * free will handle this fairly. | ||
| 628 | */ | ||
| 629 | if (ipx->ipx_source.net != intrfc->if_netnum) { | ||
| 630 | /* | ||
| 631 | * Unshare the buffer before modifying the count in | ||
| 632 | * case it's a flood or tcpdump | ||
| 633 | */ | ||
| 634 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
| 635 | if (!skb) | ||
| 636 | goto out; | ||
| 637 | if (++ipx->ipx_tctrl > ipxcfg_max_hops) | ||
| 638 | send_to_wire = 0; | ||
| 639 | } | ||
| 640 | |||
| 641 | if (!send_to_wire) { | ||
| 642 | kfree_skb(skb); | ||
| 643 | goto out; | ||
| 644 | } | ||
| 645 | |||
| 646 | /* Determine the appropriate hardware address */ | ||
| 647 | addr_len = dev->addr_len; | ||
| 648 | if (!memcmp(ipx_broadcast_node, node, IPX_NODE_LEN)) | ||
| 649 | memcpy(dest_node, dev->broadcast, addr_len); | ||
| 650 | else | ||
| 651 | memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len); | ||
| 652 | |||
| 653 | /* Make any compensation for differing physical/data link size */ | ||
| 654 | skb = ipxitf_adjust_skbuff(intrfc, skb); | ||
| 655 | if (!skb) | ||
| 656 | goto out; | ||
| 657 | |||
| 658 | /* set up data link and physical headers */ | ||
| 659 | skb->dev = dev; | ||
| 660 | skb->protocol = htons(ETH_P_IPX); | ||
| 661 | |||
| 662 | /* Send it out */ | ||
| 663 | dl->request(dl, skb, dest_node); | ||
| 664 | out: | ||
| 665 | return 0; | ||
| 666 | } | ||
| 667 | |||
1415 more lines aren’t shown here.
This diff is large: its 304 lines aren’t shown here.
This diff is large: its 294 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 41 lines aren’t shown here.
This diff is large: its 108 lines aren’t shown here.
This diff is large: its 1107 lines aren’t shown here.
This diff is large: its 165 lines aren’t shown here.
This diff is large: its 1933 lines aren’t shown here.
This diff is large: its 736 lines aren’t shown here.
This diff is large: its 479 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 2089 lines aren’t shown here.
This diff is large: its 81 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 4 lines aren’t shown here.
This diff is large: its 3 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 303 lines aren’t shown here.
This diff is large: its 86 lines aren’t shown here.
This diff is large: its 136 lines aren’t shown here.
This diff is large: its 296 lines aren’t shown here.
This diff is large: its 206 lines aren’t shown here.
This diff is large: its 168 lines aren’t shown here.
This diff is large: its 192 lines aren’t shown here.
This diff is large: its 867 lines aren’t shown here.
This diff is large: its 83 lines aren’t shown here.
This diff is large: its 201 lines aren’t shown here.
This diff is large: its 101 lines aren’t shown here.
This diff is large: its 80 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 79 lines aren’t shown here.
This diff is large: its 104 lines aren’t shown here.
This diff is large: its 213 lines aren’t shown here.
This diff is large: its 645 lines aren’t shown here.
This diff is large: its 667 lines aren’t shown here.
This diff is large: its 88 lines aren’t shown here.
This diff is large: its 150 lines aren’t shown here.
This diff is large: its 142 lines aren’t shown here.
This diff is large: its 150 lines aren’t shown here.
This diff is large: its 670 lines aren’t shown here.
This diff is large: its 135 lines aren’t shown here.
This diff is large: its 557 lines aren’t shown here.
This diff is large: its 120 lines aren’t shown here.
This diff is large: its 45 lines aren’t shown here.
This diff is large: its 262 lines aren’t shown here.
This diff is large: its 294 lines aren’t shown here.
This diff is large: its 73 lines aren’t shown here.
This diff is large: its 2689 lines aren’t shown here.
This diff is large: its 232 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 237 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 1326 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 47 lines aren’t shown here.
This diff is large: its 2 lines aren’t shown here.
This diff is large: its 2 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 2953 lines aren’t shown here.
This diff is large: its 1039 lines aren’t shown here.
This diff is large: its 3752 lines aren’t shown here.
This diff is large: its 288 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 2919 lines aren’t shown here.
This diff is large: its 706 lines aren’t shown here.
This diff is large: its 2593 lines aren’t shown here.
This diff is large: its 534 lines aren’t shown here.
This diff is large: its 185 lines aren’t shown here.
This diff is large: its 811 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 459 lines aren’t shown here.
This diff is large: its 143 lines aren’t shown here.
This diff is large: its 2065 lines aren’t shown here.
This diff is large: its 229 lines aren’t shown here.
This diff is large: its 153 lines aren’t shown here.
This diff is large: its 168 lines aren’t shown here.
This diff is large: its 557 lines aren’t shown here.
This diff is large: its 1080 lines aren’t shown here.
This diff is large: its 140 lines aren’t shown here.
This diff is large: its 444 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 146 lines aren’t shown here.
This diff is large: its 198 lines aren’t shown here.
This diff is large: its 258 lines aren’t shown here.
This diff is large: its 605 lines aren’t shown here.
This diff is large: its 1192 lines aren’t shown here.
This diff is large: its 264 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 502 lines aren’t shown here.
This diff is large: its 2308 lines aren’t shown here.
This diff is large: its 1235 lines aren’t shown here.
This diff is large: its 427 lines aren’t shown here.
This diff is large: its 464 lines aren’t shown here.
This diff is large: its 275 lines aren’t shown here.
This diff is large: its 2389 lines aren’t shown here.
This diff is large: its 626 lines aren’t shown here.
This diff is large: its 988 lines aren’t shown here.
This diff is large: its 587 lines aren’t shown here.
This diff is large: its 106 lines aren’t shown here.
This diff is large: its 224 lines aren’t shown here.
This diff is large: its 1024 lines aren’t shown here.
This diff is large: its 1259 lines aren’t shown here.
This diff is large: its 457 lines aren’t shown here.
This diff is large: its 1801 lines aren’t shown here.
This diff is large: its 910 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 527 lines aren’t shown here.
This diff is large: its 800 lines aren’t shown here.
This diff is large: its 1398 lines aren’t shown here.
This diff is large: its 144 lines aren’t shown here.
This diff is large: its 2102 lines aren’t shown here.
This diff is large: its 245 lines aren’t shown here.
This diff is large: its 1787 lines aren’t shown here.
This diff is large: its 166 lines aren’t shown here.
This diff is large: its 229 lines aren’t shown here.
This diff is large: its 1683 lines aren’t shown here.
This diff is large: its 296 lines aren’t shown here.
This diff is large: its 624 lines aren’t shown here.
This diff is large: its 245 lines aren’t shown here.
This diff is large: its 51 lines aren’t shown here.
This diff is large: its 46 lines aren’t shown here.
This diff is large: its 3 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 89 lines aren’t shown here.
This diff is large: its 441 lines aren’t shown here.
This diff is large: its 227 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 518 lines aren’t shown here.
This diff is large: its 172 lines aren’t shown here.
This diff is large: its 472 lines aren’t shown here.
This diff is large: its 156 lines aren’t shown here.
This diff is large: its 2464 lines aren’t shown here.
This diff is large: its 120 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 673 lines aren’t shown here.
This diff is large: its 1336 lines aren’t shown here.
This diff is large: its 179 lines aren’t shown here.
This diff is large: its 47 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 53 lines aren’t shown here.
This diff is large: its 154 lines aren’t shown here.
This diff is large: its 1355 lines aren’t shown here.
This diff is large: its 403 lines aren’t shown here.
This diff is large: its 199 lines aren’t shown here.
This diff is large: its 258 lines aren’t shown here.
This diff is large: its 677 lines aren’t shown here.
This diff is large: its 139 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 369 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 57 lines aren’t shown here.
This diff is large: its 125 lines aren’t shown here.
This diff is large: its 94 lines aren’t shown here.
This diff is large: its 175 lines aren’t shown here.
This diff is large: its 383 lines aren’t shown here.
This diff is large: its 230 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 2360 lines aren’t shown here.
This diff is large: its 719 lines aren’t shown here.
This diff is large: its 72 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 68 lines aren’t shown here.
This diff is large: its 308 lines aren’t shown here.
This diff is large: its 1071 lines aren’t shown here.
This diff is large: its 110 lines aren’t shown here.
This diff is large: its 1102 lines aren’t shown here.
This diff is large: its 154 lines aren’t shown here.
This diff is large: its 1608 lines aren’t shown here.
This diff is large: its 546 lines aren’t shown here.
This diff is large: its 201 lines aren’t shown here.
This diff is large: its 600 lines aren’t shown here.
This diff is large: its 75 lines aren’t shown here.
This diff is large: its 260 lines aren’t shown here.
This diff is large: its 496 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 343 lines aren’t shown here.
This diff is large: its 844 lines aren’t shown here.
This diff is large: its 2147 lines aren’t shown here.
This diff is large: its 1164 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 1024 lines aren’t shown here.
This diff is large: its 2081 lines aren’t shown here.
This diff is large: its 1370 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 301 lines aren’t shown here.
This diff is large: its 1707 lines aren’t shown here.
This diff is large: its 3601 lines aren’t shown here.
This diff is large: its 207 lines aren’t shown here.
This diff is large: its 294 lines aren’t shown here.
This diff is large: its 187 lines aren’t shown here.
This diff is large: its 1338 lines aren’t shown here.
This diff is large: its 2667 lines aren’t shown here.
This diff is large: its 479 lines aren’t shown here.
This diff is large: its 376 lines aren’t shown here.
This diff is large: its 1685 lines aren’t shown here.
This diff is large: its 1203 lines aren’t shown here.
This diff is large: its 241 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 1215 lines aren’t shown here.
This diff is large: its 642 lines aren’t shown here.
This diff is large: its 1578 lines aren’t shown here.
This diff is large: its 186 lines aren’t shown here.
This diff is large: its 160 lines aren’t shown here.
This diff is large: its 660 lines aren’t shown here.
This diff is large: its 322 lines aren’t shown here.
This diff is large: its 1375 lines aren’t shown here.
This diff is large: its 88 lines aren’t shown here.
This diff is large: its 306 lines aren’t shown here.
This diff is large: its 524 lines aren’t shown here.
This diff is large: its 639 lines aren’t shown here.
This diff is large: its 524 lines aren’t shown here.
This diff is large: its 97 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 83 lines aren’t shown here.
This diff is large: its 518 lines aren’t shown here.
This diff is large: its 165 lines aren’t shown here.
This diff is large: its 3132 lines aren’t shown here.
This diff is large: its 174 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 642 lines aren’t shown here.
This diff is large: its 385 lines aren’t shown here.
This diff is large: its 333 lines aren’t shown here.
This diff is large: its 284 lines aren’t shown here.
This diff is large: its 1024 lines aren’t shown here.
This diff is large: its 349 lines aren’t shown here.
This diff is large: its 108 lines aren’t shown here.
This diff is large: its 1449 lines aren’t shown here.
This diff is large: its 1626 lines aren’t shown here.
This diff is large: its 272 lines aren’t shown here.
This diff is large: its 401 lines aren’t shown here.
This diff is large: its 137 lines aren’t shown here.
This diff is large: its 587 lines aren’t shown here.
This diff is large: its 357 lines aren’t shown here.
This diff is large: its 148 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 181 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 300 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 232 lines aren’t shown here.
This diff is large: its 145 lines aren’t shown here.
This diff is large: its 498 lines aren’t shown here.
This diff is large: its 1203 lines aren’t shown here.
This diff is large: its 420 lines aren’t shown here.
This diff is large: its 2755 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 1845 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 96 lines aren’t shown here.
This diff is large: its 1153 lines aren’t shown here.
This diff is large: its 276 lines aren’t shown here.
This diff is large: its 1062 lines aren’t shown here.
This diff is large: its 1047 lines aren’t shown here.
This diff is large: its 536 lines aren’t shown here.
This diff is large: its 97 lines aren’t shown here.
This diff is large: its 1515 lines aren’t shown here.
This diff is large: its 239 lines aren’t shown here.
This diff is large: its 250 lines aren’t shown here.
This diff is large: its 532 lines aren’t shown here.
This diff is large: its 163 lines aren’t shown here.
This diff is large: its 524 lines aren’t shown here.
This diff is large: its 237 lines aren’t shown here.
This diff is large: its 80 lines aren’t shown here.
This diff is large: its 226 lines aren’t shown here.
This diff is large: its 413 lines aren’t shown here.
This diff is large: its 134 lines aren’t shown here.
This diff is large: its 1811 lines aren’t shown here.
This diff is large: its 2059 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 241 lines aren’t shown here.
This diff is large: its 215 lines aren’t shown here.
This diff is large: its 1560 lines aren’t shown here.
This diff is large: its 1245 lines aren’t shown here.
This diff is large: its 182 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 1725 lines aren’t shown here.
This diff is large: its 43 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 844 lines aren’t shown here.
This diff is large: its 3307 lines aren’t shown here.
This diff is large: its 684 lines aren’t shown here.
This diff is large: its 247 lines aren’t shown here.
This diff is large: its 237 lines aren’t shown here.
This diff is large: its 919 lines aren’t shown here.
This diff is large: its 1232 lines aren’t shown here.
This diff is large: its 475 lines aren’t shown here.
This diff is large: its 1095 lines aren’t shown here.
This diff is large: its 285 lines aren’t shown here.
This diff is large: its 2900 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 3270 lines aren’t shown here.
This diff is large: its 235 lines aren’t shown here.
This diff is large: its 382 lines aren’t shown here.
This diff is large: its 586 lines aren’t shown here.
This diff is large: its 1677 lines aren’t shown here.
This diff is large: its 2235 lines aren’t shown here.
This diff is large: its 341 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 1333 lines aren’t shown here.
This diff is large: its 771 lines aren’t shown here.
This diff is large: its 1613 lines aren’t shown here.
This diff is large: its 270 lines aren’t shown here.
This diff is large: its 2314 lines aren’t shown here.
This diff is large: its 73 lines aren’t shown here.
This diff is large: its 478 lines aren’t shown here.
This diff is large: its 372 lines aren’t shown here.
This diff is large: its 183 lines aren’t shown here.
This diff is large: its 913 lines aren’t shown here.
This diff is large: its 375 lines aren’t shown here.
This diff is large: its 2384 lines aren’t shown here.
This diff is large: its 573 lines aren’t shown here.
This diff is large: its 851 lines aren’t shown here.
This diff is large: its 191 lines aren’t shown here.
This diff is large: its 196 lines aren’t shown here.
This diff is large: its 460 lines aren’t shown here.
This diff is large: its 1024 lines aren’t shown here.
This diff is large: its 2805 lines aren’t shown here.
This diff is large: its 4211 lines aren’t shown here.
This diff is large: its 655 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 124 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 585 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 91 lines aren’t shown here.
This diff is large: its 284 lines aren’t shown here.
This diff is large: its 170 lines aren’t shown here.
This diff is large: its 136 lines aren’t shown here.
This diff is large: its 116 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 57 lines aren’t shown here.
This diff is large: its 116 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 123 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 393 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 104 lines aren’t shown here.
This diff is large: its 483 lines aren’t shown here.
This diff is large: its 143 lines aren’t shown here.
This diff is large: its 1180 lines aren’t shown here.
This diff is large: its 4941 lines aren’t shown here.
This diff is large: its 742 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 109 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 5 lines aren’t shown here.
This diff is large: its 1221 lines aren’t shown here.
This diff is large: its 264 lines aren’t shown here.
This diff is large: its 77 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 1068 lines aren’t shown here.
This diff is large: its 924 lines aren’t shown here.
This diff is large: its 126 lines aren’t shown here.
This diff is large: its 102 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 175 lines aren’t shown here.
This diff is large: its 1327 lines aren’t shown here.
This diff is large: its 216 lines aren’t shown here.
This diff is large: its 129 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 856 lines aren’t shown here.
This diff is large: its 183 lines aren’t shown here.
This diff is large: its 181 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 102 lines aren’t shown here.
This diff is large: its 358 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 286 lines aren’t shown here.
This diff is large: its 67 lines aren’t shown here.
This diff is large: its 83 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 114 lines aren’t shown here.
This diff is large: its 323 lines aren’t shown here.
This diff is large: its 117 lines aren’t shown here.
This diff is large: its 42 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 63 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 926 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 16 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 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 82 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
File renamed without changes.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 413 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 50 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 4 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 117 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 17 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 46 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 90 lines aren’t shown here.
This diff is large: its 71 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 599 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 565 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 266 lines aren’t shown here.
This diff is large: its 165 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 206 lines aren’t shown here.
This diff is large: its 14 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 296 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 208 lines aren’t shown here.
This diff is large: its 73 lines aren’t shown here.
This diff is large: its 973 lines aren’t shown here.
This diff is large: its 34 lines aren’t shown here.
This diff is large: its 188 lines aren’t shown here.
This diff is large: its 731 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 410 lines aren’t shown here.
This diff is large: its 251 lines aren’t shown here.
This diff is large: its 1548 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 150 lines aren’t shown here.
This diff is large: its 81 lines aren’t shown here.
This diff is large: its 66 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 95 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 6 lines aren’t shown here.
This diff is large: its 127 lines aren’t shown here.
This diff is large: its 99 lines aren’t shown here.
This diff is large: its 59 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 31 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 102 lines aren’t shown here.
This diff is large: its 203 lines aren’t shown here.
This diff is large: its 148 lines aren’t shown here.
This diff is large: its 73 lines aren’t shown here.
This diff is large: its 21 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 44 lines aren’t shown here.