Jonathan writes: Second set of IIO new device support, features and cleanups. There are also a couple of fixes that can wait for the coming merge window. Core new features * Support for phase channels (used in time of flight sensors amongst other things) * Support for deep UV light channel modifier. New Device Support * AD4758 DAC - New driver and dt bindings. * adxl345 - Support the adxl375 +-200g part which is register compatible. * isl29501 Time of flight sensor. - New driver * meson-saradc - Support the Meson8m2 Socs - right now this is just an ID, but there will be additional difference in future. * mpu6050 - New ID for 6515 variant. * si1133 UV sensor. - New driver * Spreadtrum SC27xx PMIC ADC - New driver and dt bindings. Features * adxl345 - Add calibration offset readback and writing. - Add sampling frequency control. Fixes and Cleanups * ad5933 - Use a macro for the channel definition to reduce duplication. * ad9523 - Replace use of core mlock with a local lock. Part of ongoing efforts to avoid confusing the purpose of mlock which is only about iio core state changes. - Fix displayed phase which was out by a factor of 10. * adxl345 - Add a link to the datasheet. - Rework the use of the address field in the chan_spec structures to allow addition of more per channel information. * adis imu - Mark switch fall throughs. * at91-sama5d2 - Fix some casting on big endian systems. * bmp280 - Drop some DT elements that aren't used and should mostly be done from userspace rather than in DT. * hx711 - add clock-frequency dt binding and resulting delay to deal with capacitance issue on some boards. - fix a spurious unit-address in the example. * ina2xx - Avoid a possible kthread_stop with a stale task_struct. * ltc2632 - Remove some unused local variables (assigned but value never used). * max1363 - Use device_get_match_data to remove some boilerplate. * mma8452 - Mark switch fall throughs. * sca3000 - Fix a missing return in a switch statement (a bad fallthrough previously!) * sigma-delta-modulator - Drop incorrect unit address from the DT example. * st_accel - Use device_get_match_data to drop some boiler plate. - Move to probe_new for i2c driver as second parameter not used. * st_sensors library - Use a strlcpy (safe in this case). * st_lsm6dsx - Add some error logging. * ti-ads7950 - SPDX - Allow simultaneous buffered and polled reads. Needed on a Lego Mindstorms EV3 where some channels are used for power supply monitoring at a very low rate. * ti-dac5571 - Remove an unused variable. * xadc - Drop some dead code.
Unified · Split
| @@ -1307,13 +1307,16 @@ What: /sys/.../iio:deviceX/in_intensityY_raw · 1306 unmodified lines | ||
| 1307 | 1307 | What: /sys/.../iio:deviceX/in_intensityY_ir_raw |
| 1308 | 1308 | What: /sys/.../iio:deviceX/in_intensityY_both_raw |
| 1309 | 1309 | What: /sys/.../iio:deviceX/in_intensityY_uv_raw |
| 1310 | +What: /sys/.../iio:deviceX/in_intensityY_duv_raw | |
| 1310 | 1311 | KernelVersion: 3.4 |
| 1311 | 1312 | Contact: linux-iio@vger.kernel.org |
| 1312 | 1313 | Description: |
| 1313 | 1314 | Unit-less light intensity. Modifiers both and ir indicate |
| 1314 | 1315 | that measurements contain visible and infrared light |
| 1315 | − components or just infrared light, respectively. Modifier uv indicates | |
| 1316 | − that measurements contain ultraviolet light components. | |
| 1316 | + components or just infrared light, respectively. Modifier | |
| 1317 | + uv indicates that measurements contain ultraviolet light | |
| 1318 | + components. Modifier duv indicates that measurements | |
| 1319 | + contain deep ultraviolet light components. | |
| 1317 | 1320 | |
| 1318 | 1321 | What: /sys/.../iio:deviceX/in_uvindex_input |
| 1319 | 1322 | KernelVersion: 4.6 |
| @@ -1675,3 +1678,10 @@ KernelVersion: 4.12 · 355 unmodified lines | ||
| 1675 | 1678 | Contact: linux-iio@vger.kernel.org |
| 1676 | 1679 | Description: |
| 1677 | 1680 | Raw counter device counters direction for channel Y. |
| 1681 | + | |
| 1682 | +What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw | |
| 1683 | +KernelVersion: 4.18 | |
| 1684 | +Contact: linux-iio@vger.kernel.org | |
| 1685 | +Description: | |
| 1686 | + Raw (unscaled) phase difference reading from channel Y | |
| 1687 | + that can be processed to radians. | |
| @@ -0,0 +1,47 @@ | ||
| 1 | +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_agc_gain | |
| 2 | +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_agc_gain_bias | |
| 3 | +KernelVersion: 4.18 | |
| 4 | +Contact: linux-iio@vger.kernel.org | |
| 5 | +Description: | |
| 6 | + This sensor has an automatic gain control (agc) loop | |
| 7 | + which sets the analog signal levels at an optimum | |
| 8 | + level by controlling programmable gain amplifiers. The | |
| 9 | + criteria for optimal gain is determined by the sensor. | |
| 10 | + | |
| 11 | + Return the actual gain value as an integer in [0; 65536] | |
| 12 | + range when read from. | |
| 13 | + | |
| 14 | + The agc gain read when measuring crosstalk shall be | |
| 15 | + written into in_proximity0_agc_gain_bias. | |
| 16 | + | |
| 17 | +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_temp_a | |
| 18 | +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_temp_b | |
| 19 | +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_light_a | |
| 20 | +What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_light_b | |
| 21 | +KernelVersion: 4.18 | |
| 22 | +Contact: linux-iio@vger.kernel.org | |
| 23 | +Description: | |
| 24 | + The sensor is able to perform correction of distance | |
| 25 | + measurements due to changing temperature and ambient | |
| 26 | + light conditions. It can be programmed to correct for | |
| 27 | + a second order error polynomial. | |
| 28 | + | |
| 29 | + Phase data has to be collected when temperature and | |
| 30 | + ambient light are modulated independently. | |
| 31 | + | |
| 32 | + Then a least squares curve fit to a second order | |
| 33 | + polynomial has to be generated from the data. The | |
| 34 | + resultant curves have the form ax^2 + bx + c. | |
| 35 | + | |
| 36 | + From those two curves, a and b coefficients shall be | |
| 37 | + stored in in_proximity0_calib_phase_temp_a and | |
| 38 | + in_proximity0_calib_phase_temp_b for temperature and | |
| 39 | + in in_proximity0_calib_phase_light_a and | |
| 40 | + in_proximity0_calib_phase_light_b for ambient light. | |
| 41 | + | |
| 42 | + Those values must be integer in [0; 8355840] range. | |
| 43 | + | |
| 44 | + Finally, the c constant is set by the sensor | |
| 45 | + internally. | |
| 46 | + | |
| 47 | + The value stored in sensor is displayed when read from. | |
| @@ -0,0 +1,22 @@ | ||
| 1 | +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_small_raw | |
| 2 | +KernelVersion: 4.18 | |
| 3 | +Contact: linux-iio@vger.kernel.org | |
| 4 | +Description: | |
| 5 | + Unit-less infrared intensity. The intensity is measured from 1 | |
| 6 | + dark photodiode. "small" indicate the surface area capturing | |
| 7 | + infrared. | |
| 8 | + | |
| 9 | +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_large_raw | |
| 10 | +KernelVersion: 4.18 | |
| 11 | +Contact: linux-iio@vger.kernel.org | |
| 12 | +Description: | |
| 13 | + Unit-less infrared intensity. The intensity is measured from 4 | |
| 14 | + dark photodiodes. "large" indicate the surface area capturing | |
| 15 | + infrared. | |
| 16 | + | |
| 17 | +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_large_raw | |
| 18 | +KernelVersion: 4.18 | |
| 19 | +Contact: linux-iio@vger.kernel.org | |
| 20 | +Description: | |
| 21 | + Unit-less light intensity with more diodes. | |
| 22 | + | |
| @@ -1,9 +1,12 @@ | ||
| 1 | −Analog Devices ADXL345 3-Axis, +/-(2g/4g/8g/16g) Digital Accelerometer | |
| 1 | +Analog Devices ADXL345/ADXL375 3-Axis Digital Accelerometers | |
| 2 | 2 | |
| 3 | 3 | http://www.analog.com/en/products/mems/accelerometers/adxl345.html |
| 4 | +http://www.analog.com/en/products/sensors-mems/accelerometers/adxl375.html | |
| 4 | 5 | |
| 5 | 6 | Required properties: |
| 6 | − - compatible : should be "adi,adxl345" | |
| 7 | + - compatible : should be one of | |
| 8 | + "adi,adxl345" | |
| 9 | + "adi,adxl375" | |
| 7 | 10 | - reg : the I2C address or SPI chip select number of the sensor |
| 8 | 11 | |
| 9 | 12 | Required properties for SPI bus usage: |
| @@ -4,6 +4,7 @@ Required properties: · 3 unmodified lines | ||
| 4 | 4 | - compatible: depending on the SoC this should be one of: |
| 5 | 5 | - "amlogic,meson8-saradc" for Meson8 |
| 6 | 6 | - "amlogic,meson8b-saradc" for Meson8b |
| 7 | + - "amlogic,meson8m2-saradc" for Meson8m2 | |
| 7 | 8 | - "amlogic,meson-gxbb-saradc" for GXBB |
| 8 | 9 | - "amlogic,meson-gxl-saradc" for GXL |
| 9 | 10 | - "amlogic,meson-gxm-saradc" for GXM |
| @@ -8,11 +8,17 @@ Required properties: · 7 unmodified lines | ||
| 8 | 8 | See Documentation/devicetree/bindings/gpio/gpio.txt |
| 9 | 9 | - avdd-supply: Definition of the regulator used as analog supply |
| 10 | 10 | |
| 11 | +Optional properties: | |
| 12 | + - clock-frequency: Frequency of PD_SCK in Hz | |
| 13 | + Minimum value allowed is 10 kHz because of maximum | |
| 14 | + high time of 50 microseconds. | |
| 15 | + | |
| 11 | 16 | Example: |
| 12 | −weight@0 { | |
| 17 | +weight { | |
| 13 | 18 | compatible = "avia,hx711"; |
| 14 | 19 | sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>; |
| 15 | 20 | dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; |
| 16 | 21 | avdd-suppy = <&avdd>; |
| 22 | + clock-frequency = <100000>; | |
| 17 | 23 | }; |
| 18 | 24 | |
| @@ -7,7 +7,7 @@ Required properties: · 6 unmodified lines | ||
| 7 | 7 | |
| 8 | 8 | Example node: |
| 9 | 9 | |
| 10 | − ads1202: adc@0 { | |
| 10 | + ads1202: adc { | |
| 11 | 11 | compatible = "sd-modulator"; |
| 12 | 12 | #io-channel-cells = <0>; |
| 13 | 13 | }; |
| @@ -0,0 +1,36 @@ | ||
| 1 | +Spreadtrum SC27XX series PMICs ADC binding | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: Should be one of the following. | |
| 5 | + "sprd,sc2720-adc" | |
| 6 | + "sprd,sc2721-adc" | |
| 7 | + "sprd,sc2723-adc" | |
| 8 | + "sprd,sc2730-adc" | |
| 9 | + "sprd,sc2731-adc" | |
| 10 | +- reg: The address offset of ADC controller. | |
| 11 | +- interrupt-parent: The interrupt controller. | |
| 12 | +- interrupts: The interrupt number for the ADC device. | |
| 13 | +- #io-channel-cells: Number of cells in an IIO specifier. | |
| 14 | +- hwlocks: Reference to a phandle of a hwlock provider node. | |
| 15 | + | |
| 16 | +Example: | |
| 17 | + | |
| 18 | + sc2731_pmic: pmic@0 { | |
| 19 | + compatible = "sprd,sc2731"; | |
| 20 | + reg = <0>; | |
| 21 | + spi-max-frequency = <26000000>; | |
| 22 | + interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>; | |
| 23 | + interrupt-controller; | |
| 24 | + #interrupt-cells = <2>; | |
| 25 | + #address-cells = <1>; | |
| 26 | + #size-cells = <0>; | |
| 27 | + | |
| 28 | + pmic_adc: adc@480 { | |
| 29 | + compatible = "sprd,sc2731-adc"; | |
| 30 | + reg = <0x480>; | |
| 31 | + interrupt-parent = <&sc2731_pmic>; | |
| 32 | + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; | |
| 33 | + #io-channel-cells = <1>; | |
| 34 | + hwlocks = <&hwlock 4>; | |
| 35 | + }; | |
| 36 | + }; | |
| @@ -0,0 +1,78 @@ | ||
| 1 | +Analog Devices AD5758 DAC device driver | |
| 2 | + | |
| 3 | +Required properties for the AD5758: | |
| 4 | + - compatible: Must be "adi,ad5758" | |
| 5 | + - reg: SPI chip select number for the device | |
| 6 | + - spi-max-frequency: Max SPI frequency to use (< 50000000) | |
| 7 | + - spi-cpha: is the only mode that is supported | |
| 8 | + | |
| 9 | +Required properties: | |
| 10 | + | |
| 11 | + - adi,dc-dc-mode: Mode of operation of the dc-to-dc converter | |
| 12 | + Dynamic Power Control (DPC) | |
| 13 | + In this mode, the AD5758 circuitry senses the output | |
| 14 | + voltage and dynamically regulates the supply voltage, | |
| 15 | + VDPC+, to meet compliance requirements plus an optimized | |
| 16 | + headroom voltage for the output buffer. | |
| 17 | + | |
| 18 | + Programmable Power Control (PPC) | |
| 19 | + In this mode, the VDPC+ voltage is user-programmable to | |
| 20 | + a fixed level that needs to accommodate the maximum output | |
| 21 | + load required. | |
| 22 | + | |
| 23 | + The output of the DAC core is either converted to a | |
| 24 | + current or voltage output at the VIOUT pin. Only one mode | |
| 25 | + can be enabled at any one time. | |
| 26 | + | |
| 27 | + The following values are currently supported: | |
| 28 | + * 1: DPC current mode | |
| 29 | + * 2: DPC voltage mode | |
| 30 | + * 3: PPC current mode | |
| 31 | + | |
| 32 | + Depending on the selected output mode (voltage or current) one of the | |
| 33 | + two properties must | |
| 34 | + be present: | |
| 35 | + | |
| 36 | + - adi,range-microvolt: Voltage output range | |
| 37 | + The array of voltage output ranges must contain two fields: | |
| 38 | + * <0 5000000>: 0 V to 5 V voltage range | |
| 39 | + * <0 10000000>: 0 V to 10 V voltage range | |
| 40 | + * <(-5000000) 5000000>: ±5 V voltage range | |
| 41 | + * <(-10000000) 10000000>: ±10 V voltage range | |
| 42 | + - adi,range-microamp: Current output range | |
| 43 | + The array of current output ranges must contain two fields: | |
| 44 | + * <0 20000>: 0 mA to 20 mA current range | |
| 45 | + * <0 24000>: 0 mA to 24 mA current range | |
| 46 | + * <4 24000>: 4 mA to 20 mA current range | |
| 47 | + * <(-20000) 20000>: ±20 mA current range | |
| 48 | + * <(-24000) 24000>: ±24 mA current range | |
| 49 | + * <(-1000) 22000>: −1 mA to +22 mA current range | |
| 50 | + | |
| 51 | +Optional properties: | |
| 52 | + | |
| 53 | + - adi,dc-dc-ilim-microamp: The dc-to-dc converter current limit | |
| 54 | + The following values are currently supported [uA]: | |
| 55 | + * 150000 | |
| 56 | + * 200000 | |
| 57 | + * 250000 | |
| 58 | + * 300000 | |
| 59 | + * 350000 | |
| 60 | + * 400000 | |
| 61 | + | |
| 62 | + - adi,slew-time-us: The time it takes for the output to reach the | |
| 63 | + full scale [uS] | |
| 64 | + The supported range is between 133us up to 1023984375us | |
| 65 | + | |
| 66 | +AD5758 Example: | |
| 67 | + | |
| 68 | + dac@0 { | |
| 69 | + compatible = "adi,ad5758"; | |
| 70 | + reg = <0>; | |
| 71 | + spi-max-frequency = <1000000>; | |
| 72 | + spi-cpha; | |
| 73 | + | |
| 74 | + adi,dc-dc-mode = <2>; | |
| 75 | + adi,range-microvolt = <0 10000000>; | |
| 76 | + adi,dc-dc-ilim-microamp = <200000>; | |
| 77 | + adi,slew-time-us = <125000>; | |
| 78 | + }; | |
| @@ -6,6 +6,7 @@ Required properties: · 5 unmodified lines | ||
| 6 | 6 | - compatible : should be one of |
| 7 | 7 | "invensense,mpu6050" |
| 8 | 8 | "invensense,mpu6500" |
| 9 | + "invensense,mpu6515" | |
| 9 | 10 | "invensense,mpu9150" |
| 10 | 11 | "invensense,mpu9250" |
| 11 | 12 | "invensense,mpu9255" |
| @@ -0,0 +1,13 @@ | ||
| 1 | +* ISL29501 Time-of-flight sensor. | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | + | |
| 5 | + - compatible : should be "renesas,isl29501" | |
| 6 | + - reg : the I2C address of the sensor | |
| 7 | + | |
| 8 | +Example: | |
| 9 | + | |
| 10 | +isl29501@57 { | |
| 11 | + compatible = "renesas,isl29501"; | |
| 12 | + reg = <0x57>; | |
| 13 | +}; | |
| @@ -8,10 +8,6 @@ Required properties: · 7 unmodified lines | ||
| 8 | 8 | "bosch,bme280" |
| 9 | 9 | |
| 10 | 10 | Optional properties: |
| 11 | −- chip-id: configurable chip id for non-default chip revisions | |
| 12 | −- temp-measurement-period: temperature measurement period (milliseconds) | |
| 13 | −- default-oversampling: default oversampling value to be used at startup, | |
| 14 | − value range is 0-3 with rising sensitivity. | |
| 15 | 11 | - interrupt-parent: should be the phandle for the interrupt controller |
| 16 | 12 | - interrupts: interrupt mapping for IRQ |
| 17 | 13 | - reset-gpios: a GPIO line handling reset of the sensor: as the line is |
| @@ -24,9 +20,6 @@ Example: · 6 unmodified lines | ||
| 24 | 20 | pressure@77 { |
| 25 | 21 | compatible = "bosch,bmp085"; |
| 26 | 22 | reg = <0x77>; |
| 27 | − chip-id = <10>; | |
| 28 | − temp-measurement-period = <100>; | |
| 29 | − default-oversampling = <2>; | |
| 30 | 23 | interrupt-parent = <&gpio0>; |
| 31 | 24 | interrupts = <25 IRQ_TYPE_EDGE_RISING>; |
| 32 | 25 | reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; |
| @@ -796,6 +796,14 @@ M: Michael Hanselmann <linux-kernel@hansmi.ch> · 795 unmodified lines | ||
| 796 | 796 | S: Supported |
| 797 | 797 | F: drivers/macintosh/ams/ |
| 798 | 798 | |
| 799 | +ANALOG DEVICES INC AD5758 DRIVER | |
| 800 | +M: Stefan Popa <stefan.popa@analog.com> | |
| 801 | +L: linux-iio@vger.kernel.org | |
| 802 | +W: http://ez.analog.com/community/linux-device-drivers | |
| 803 | +S: Supported | |
| 804 | +F: drivers/iio/dac/ad5758.c | |
| 805 | +F: Documentation/devicetree/bindings/iio/dac/ad5758.txt | |
| 806 | + | |
| 799 | 807 | ANALOG DEVICES INC AD5686 DRIVER |
| 800 | 808 | M: Stefan Popa <stefan.popa@analog.com> |
| 801 | 809 | L: linux-pm@vger.kernel.org |
| @@ -40,7 +40,7 @@ config ADXL345_I2C · 39 unmodified lines | ||
| 40 | 40 | select REGMAP_I2C |
| 41 | 41 | help |
| 42 | 42 | Say Y here if you want to build support for the Analog Devices |
| 43 | − ADXL345 3-axis digital accelerometer. | |
| 43 | + ADXL345 or ADXL375 3-axis digital accelerometer. | |
| 44 | 44 | |
| 45 | 45 | To compile this driver as a module, choose M here: the module |
| 46 | 46 | will be called adxl345_i2c and you will also get adxl345_core |
| @@ -54,7 +54,7 @@ config ADXL345_SPI · 7 unmodified lines | ||
| 54 | 54 | select REGMAP_SPI |
| 55 | 55 | help |
| 56 | 56 | Say Y here if you want to build support for the Analog Devices |
| 57 | − ADXL345 3-axis digital accelerometer. | |
| 57 | + ADXL345 or ADXL375 3-axis digital accelerometer. | |
| 58 | 58 | |
| 59 | 59 | To compile this driver as a module, choose M here: the module |
| 60 | 60 | will be called adxl345_spi and you will also get adxl345_core |
| @@ -11,8 +11,13 @@ · 10 unmodified lines | ||
| 11 | 11 | #ifndef _ADXL345_H_ |
| 12 | 12 | #define _ADXL345_H_ |
| 13 | 13 | |
| 14 | +enum adxl345_device_type { | |
| 15 | + ADXL345, | |
| 16 | + ADXL375, | |
| 17 | +}; | |
| 18 | + | |
| 14 | 19 | int adxl345_core_probe(struct device *dev, struct regmap *regmap, |
| 15 | − const char *name); | |
| 20 | + enum adxl345_device_type type, const char *name); | |
| 16 | 21 | int adxl345_core_remove(struct device *dev); |
| 17 | 22 | |
| 18 | 23 | #endif /* _ADXL345_H_ */ |
| @@ -6,21 +6,35 @@ · 5 unmodified lines | ||
| 6 | 6 | * This file is subject to the terms and conditions of version 2 of |
| 7 | 7 | * the GNU General Public License. See the file COPYING in the main |
| 8 | 8 | * directory of this archive for more details. |
| 9 | + * | |
| 10 | + * Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf | |
| 9 | 11 | */ |
| 10 | 12 | |
| 11 | 13 | #include <linux/module.h> |
| 12 | 14 | #include <linux/regmap.h> |
| 13 | 15 | |
| 14 | 16 | #include <linux/iio/iio.h> |
| 17 | +#include <linux/iio/sysfs.h> | |
| 15 | 18 | |
| 16 | 19 | #include "adxl345.h" |
| 17 | 20 | |
| 18 | 21 | #define ADXL345_REG_DEVID 0x00 |
| 22 | +#define ADXL345_REG_OFSX 0x1e | |
| 23 | +#define ADXL345_REG_OFSY 0x1f | |
| 24 | +#define ADXL345_REG_OFSZ 0x20 | |
| 25 | +#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) | |
| 26 | +#define ADXL345_REG_BW_RATE 0x2C | |
| 19 | 27 | #define ADXL345_REG_POWER_CTL 0x2D |
| 20 | 28 | #define ADXL345_REG_DATA_FORMAT 0x31 |
| 21 | 29 | #define ADXL345_REG_DATAX0 0x32 |
| 22 | 30 | #define ADXL345_REG_DATAY0 0x34 |
| 23 | 31 | #define ADXL345_REG_DATAZ0 0x36 |
| 32 | +#define ADXL345_REG_DATA_AXIS(index) \ | |
| 33 | + (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) | |
| 34 | + | |
| 35 | +#define ADXL345_BW_RATE GENMASK(3, 0) | |
| 36 | +#define ADXL345_BASE_RATE_NANO_HZ 97656250LL | |
| 37 | +#define NHZ_PER_HZ 1000000000LL | |
| 24 | 38 | |
| 25 | 39 | #define ADXL345_POWER_CTL_MEASURE BIT(3) |
| 26 | 40 | #define ADXL345_POWER_CTL_STANDBY 0x00 |
| @@ -42,24 +56,33 @@ · 15 unmodified lines | ||
| 42 | 56 | */ |
| 43 | 57 | static const int adxl345_uscale = 38300; |
| 44 | 58 | |
| 59 | +/* | |
| 60 | + * The Datasheet lists a resolution of Resolution is ~49 mg per LSB. That's | |
| 61 | + * ~480mm/s**2 per LSB. | |
| 62 | + */ | |
| 63 | +static const int adxl375_uscale = 480000; | |
| 64 | + | |
| 45 | 65 | struct adxl345_data { |
| 46 | 66 | struct regmap *regmap; |
| 47 | 67 | u8 data_range; |
| 68 | + enum adxl345_device_type type; | |
| 48 | 69 | }; |
| 49 | 70 | |
| 50 | −#define ADXL345_CHANNEL(reg, axis) { \ | |
| 71 | +#define ADXL345_CHANNEL(index, axis) { \ | |
| 51 | 72 | .type = IIO_ACCEL, \ |
| 52 | 73 | .modified = 1, \ |
| 53 | 74 | .channel2 = IIO_MOD_##axis, \ |
| 54 | − .address = reg, \ | |
| 55 | − .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
| 56 | − .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
| 75 | + .address = index, \ | |
| 76 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
| 77 | + BIT(IIO_CHAN_INFO_CALIBBIAS), \ | |
| 78 | + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ | |
| 79 | + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| 57 | 80 | } |
| 58 | 81 | |
| 59 | 82 | static const struct iio_chan_spec adxl345_channels[] = { |
| 60 | − ADXL345_CHANNEL(ADXL345_REG_DATAX0, X), | |
| 61 | − ADXL345_CHANNEL(ADXL345_REG_DATAY0, Y), | |
| 62 | − ADXL345_CHANNEL(ADXL345_REG_DATAZ0, Z), | |
| 83 | + ADXL345_CHANNEL(0, X), | |
| 84 | + ADXL345_CHANNEL(1, Y), | |
| 85 | + ADXL345_CHANNEL(2, Z), | |
| 63 | 86 | }; |
| 64 | 87 | |
| 65 | 88 | static int adxl345_read_raw(struct iio_dev *indio_dev, |
| @@ -67,7 +90,9 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, · 1 unmodified line | ||
| 67 | 90 | int *val, int *val2, long mask) |
| 68 | 91 | { |
| 69 | 92 | struct adxl345_data *data = iio_priv(indio_dev); |
| 70 | − __le16 regval; | |
| 93 | + __le16 accel; | |
| 94 | + long long samp_freq_nhz; | |
| 95 | + unsigned int regval; | |
| 71 | 96 | int ret; |
| 72 | 97 | |
| 73 | 98 | switch (mask) { |
| @@ -77,29 +102,117 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, · 3 unmodified lines | ||
| 77 | 102 | * ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte |
| 78 | 103 | * and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte |
| 79 | 104 | */ |
| 80 | − ret = regmap_bulk_read(data->regmap, chan->address, ®val, | |
| 81 | − sizeof(regval)); | |
| 105 | + ret = regmap_bulk_read(data->regmap, | |
| 106 | + ADXL345_REG_DATA_AXIS(chan->address), | |
| 107 | + &accel, sizeof(accel)); | |
| 82 | 108 | if (ret < 0) |
| 83 | 109 | return ret; |
| 84 | 110 | |
| 85 | − *val = sign_extend32(le16_to_cpu(regval), 12); | |
| 111 | + *val = sign_extend32(le16_to_cpu(accel), 12); | |
| 86 | 112 | return IIO_VAL_INT; |
| 87 | 113 | case IIO_CHAN_INFO_SCALE: |
| 88 | 114 | *val = 0; |
| 89 | − *val2 = adxl345_uscale; | |
| 115 | + switch (data->type) { | |
| 116 | + case ADXL345: | |
| 117 | + *val2 = adxl345_uscale; | |
| 118 | + break; | |
| 119 | + case ADXL375: | |
| 120 | + *val2 = adxl375_uscale; | |
| 121 | + break; | |
| 122 | + } | |
| 90 | 123 | |
| 91 | 124 | return IIO_VAL_INT_PLUS_MICRO; |
| 125 | + case IIO_CHAN_INFO_CALIBBIAS: | |
| 126 | + ret = regmap_read(data->regmap, | |
| 127 | + ADXL345_REG_OFS_AXIS(chan->address), ®val); | |
| 128 | + if (ret < 0) | |
| 129 | + return ret; | |
| 130 | + /* | |
| 131 | + * 8-bit resolution at +/- 2g, that is 4x accel data scale | |
| 132 | + * factor | |
| 133 | + */ | |
| 134 | + *val = sign_extend32(regval, 7) * 4; | |
| 135 | + | |
| 136 | + return IIO_VAL_INT; | |
| 137 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 138 | + ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, ®val); | |
| 139 | + if (ret < 0) | |
| 140 | + return ret; | |
| 141 | + | |
| 142 | + samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ << | |
| 143 | + (regval & ADXL345_BW_RATE); | |
| 144 | + *val = div_s64_rem(samp_freq_nhz, NHZ_PER_HZ, val2); | |
| 145 | + | |
| 146 | + return IIO_VAL_INT_PLUS_NANO; | |
| 92 | 147 | } |
| 93 | 148 | |
| 94 | 149 | return -EINVAL; |
| 95 | 150 | } |
| 96 | 151 | |
| 152 | +static int adxl345_write_raw(struct iio_dev *indio_dev, | |
| 153 | + struct iio_chan_spec const *chan, | |
| 154 | + int val, int val2, long mask) | |
| 155 | +{ | |
| 156 | + struct adxl345_data *data = iio_priv(indio_dev); | |
| 157 | + s64 n; | |
| 158 | + | |
| 159 | + switch (mask) { | |
| 160 | + case IIO_CHAN_INFO_CALIBBIAS: | |
| 161 | + /* | |
| 162 | + * 8-bit resolution at +/- 2g, that is 4x accel data scale | |
| 163 | + * factor | |
| 164 | + */ | |
| 165 | + return regmap_write(data->regmap, | |
| 166 | + ADXL345_REG_OFS_AXIS(chan->address), | |
| 167 | + val / 4); | |
| 168 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 169 | + n = div_s64(val * NHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ); | |
| 170 | + | |
| 171 | + return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE, | |
| 172 | + ADXL345_BW_RATE, | |
| 173 | + clamp_val(ilog2(n), 0, | |
| 174 | + ADXL345_BW_RATE)); | |
| 175 | + } | |
| 176 | + | |
| 177 | + return -EINVAL; | |
| 178 | +} | |
| 179 | + | |
| 180 | +static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, | |
| 181 | + struct iio_chan_spec const *chan, | |
| 182 | + long mask) | |
| 183 | +{ | |
| 184 | + switch (mask) { | |
| 185 | + case IIO_CHAN_INFO_CALIBBIAS: | |
| 186 | + return IIO_VAL_INT; | |
| 187 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 188 | + return IIO_VAL_INT_PLUS_NANO; | |
| 189 | + default: | |
| 190 | + return -EINVAL; | |
| 191 | + } | |
| 192 | +} | |
| 193 | + | |
| 194 | +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( | |
| 195 | +"0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200" | |
| 196 | +); | |
| 197 | + | |
| 198 | +static struct attribute *adxl345_attrs[] = { | |
| 199 | + &iio_const_attr_sampling_frequency_available.dev_attr.attr, | |
| 200 | + NULL, | |
| 201 | +}; | |
| 202 | + | |
| 203 | +static const struct attribute_group adxl345_attrs_group = { | |
| 204 | + .attrs = adxl345_attrs, | |
| 205 | +}; | |
| 206 | + | |
| 97 | 207 | static const struct iio_info adxl345_info = { |
| 208 | + .attrs = &adxl345_attrs_group, | |
| 98 | 209 | .read_raw = adxl345_read_raw, |
| 210 | + .write_raw = adxl345_write_raw, | |
| 211 | + .write_raw_get_fmt = adxl345_write_raw_get_fmt, | |
| 99 | 212 | }; |
| 100 | 213 | |
| 101 | 214 | int adxl345_core_probe(struct device *dev, struct regmap *regmap, |
| 102 | − const char *name) | |
| 215 | + enum adxl345_device_type type, const char *name) | |
| 103 | 216 | { |
| 104 | 217 | struct adxl345_data *data; |
| 105 | 218 | struct iio_dev *indio_dev; |
| @@ -125,6 +238,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, · 19 unmodified lines | ||
| 125 | 238 | data = iio_priv(indio_dev); |
| 126 | 239 | dev_set_drvdata(dev, indio_dev); |
| 127 | 240 | data->regmap = regmap; |
| 241 | + data->type = type; | |
| 128 | 242 | /* Enable full-resolution mode */ |
| 129 | 243 | data->data_range = ADXL345_DATA_FORMAT_FULL_RES; |
| 130 | 244 | |
| @@ -34,7 +34,8 @@ static int adxl345_i2c_probe(struct i2c_client *client, · 33 unmodified lines | ||
| 34 | 34 | return PTR_ERR(regmap); |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | − return adxl345_core_probe(&client->dev, regmap, id ? id->name : NULL); | |
| 37 | + return adxl345_core_probe(&client->dev, regmap, id->driver_data, | |
| 38 | + id ? id->name : NULL); | |
| 38 | 39 | } |
| 39 | 40 | |
| 40 | 41 | static int adxl345_i2c_remove(struct i2c_client *client) |
| @@ -43,7 +44,8 @@ static int adxl345_i2c_remove(struct i2c_client *client) · 2 unmodified lines | ||
| 43 | 44 | } |
| 44 | 45 | |
| 45 | 46 | static const struct i2c_device_id adxl345_i2c_id[] = { |
| 46 | − { "adxl345", 0 }, | |
| 47 | + { "adxl345", ADXL345 }, | |
| 48 | + { "adxl375", ADXL375 }, | |
| 47 | 49 | { } |
| 48 | 50 | }; |
| 49 | 51 | |
| @@ -51,6 +53,7 @@ MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id); · 1 unmodified line | ||
| 51 | 53 | |
| 52 | 54 | static const struct of_device_id adxl345_of_match[] = { |
| 53 | 55 | { .compatible = "adi,adxl345" }, |
| 56 | + { .compatible = "adi,adxl375" }, | |
| 54 | 57 | { }, |
| 55 | 58 | }; |
| 56 | 59 | |
| @@ -42,7 +42,7 @@ static int adxl345_spi_probe(struct spi_device *spi) · 41 unmodified lines | ||
| 42 | 42 | return PTR_ERR(regmap); |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | − return adxl345_core_probe(&spi->dev, regmap, id->name); | |
| 45 | + return adxl345_core_probe(&spi->dev, regmap, id->driver_data, id->name); | |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | 48 | static int adxl345_spi_remove(struct spi_device *spi) |
| @@ -51,7 +51,8 @@ static int adxl345_spi_remove(struct spi_device *spi) · 2 unmodified lines | ||
| 51 | 51 | } |
| 52 | 52 | |
| 53 | 53 | static const struct spi_device_id adxl345_spi_id[] = { |
| 54 | − { "adxl345", 0 }, | |
| 54 | + { "adxl345", ADXL345 }, | |
| 55 | + { "adxl375", ADXL375 }, | |
| 55 | 56 | { } |
| 56 | 57 | }; |
| 57 | 58 | |
| @@ -59,6 +60,7 @@ MODULE_DEVICE_TABLE(spi, adxl345_spi_id); · 1 unmodified line | ||
| 59 | 60 | |
| 60 | 61 | static const struct of_device_id adxl345_of_match[] = { |
| 61 | 62 | { .compatible = "adi,adxl345" }, |
| 63 | + { .compatible = "adi,adxl375" }, | |
| 62 | 64 | { }, |
| 63 | 65 | }; |
| 64 | 66 | |
| @@ -1547,6 +1547,7 @@ static int mma8452_probe(struct i2c_client *client, · 1546 unmodified lines | ||
| 1547 | 1547 | case FXLS8471_DEVICE_ID: |
| 1548 | 1548 | if (ret == data->chip_info->chip_id) |
| 1549 | 1549 | break; |
| 1550 | + /* else: fall through */ | |
| 1550 | 1551 | default: |
| 1551 | 1552 | return -ENODEV; |
| 1552 | 1553 | } |
| @@ -797,6 +797,7 @@ static int sca3000_write_raw(struct iio_dev *indio_dev, · 796 unmodified lines | ||
| 797 | 797 | mutex_lock(&st->lock); |
| 798 | 798 | ret = sca3000_write_3db_freq(st, val); |
| 799 | 799 | mutex_unlock(&st->lock); |
| 800 | + return ret; | |
| 800 | 801 | default: |
| 801 | 802 | return -EINVAL; |
| 802 | 803 | } |
| @@ -14,8 +14,8 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <linux/acpi.h> |
| 15 | 15 | #include <linux/i2c.h> |
| 16 | 16 | #include <linux/iio/iio.h> |
| 17 | +#include <linux/property.h> | |
| 17 | 18 | |
| 18 | −#include <linux/iio/common/st_sensors.h> | |
| 19 | 19 | #include <linux/iio/common/st_sensors_i2c.h> |
| 20 | 20 | #include "st_accel.h" |
| 21 | 21 | |
| @@ -107,8 +107,8 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match); · 85 unmodified lines | ||
| 107 | 107 | |
| 108 | 108 | #ifdef CONFIG_ACPI |
| 109 | 109 | static const struct acpi_device_id st_accel_acpi_match[] = { |
| 110 | − {"SMO8840", LNG2DM}, | |
| 111 | − {"SMO8A90", LNG2DM}, | |
| 110 | + {"SMO8840", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME}, | |
| 111 | + {"SMO8A90", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME}, | |
| 112 | 112 | { }, |
| 113 | 113 | }; |
| 114 | 114 | MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); |
| @@ -117,33 +117,33 @@ MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); · 2 unmodified lines | ||
| 117 | 117 | #endif |
| 118 | 118 | |
| 119 | 119 | static const struct i2c_device_id st_accel_id_table[] = { |
| 120 | − { LSM303DLH_ACCEL_DEV_NAME, LSM303DLH }, | |
| 121 | − { LSM303DLHC_ACCEL_DEV_NAME, LSM303DLHC }, | |
| 122 | − { LIS3DH_ACCEL_DEV_NAME, LIS3DH }, | |
| 123 | − { LSM330D_ACCEL_DEV_NAME, LSM330D }, | |
| 124 | − { LSM330DL_ACCEL_DEV_NAME, LSM330DL }, | |
| 125 | − { LSM330DLC_ACCEL_DEV_NAME, LSM330DLC }, | |
| 126 | − { LIS331DLH_ACCEL_DEV_NAME, LIS331DLH }, | |
| 127 | − { LSM303DL_ACCEL_DEV_NAME, LSM303DL }, | |
| 128 | − { LSM303DLM_ACCEL_DEV_NAME, LSM303DLM }, | |
| 129 | − { LSM330_ACCEL_DEV_NAME, LSM330 }, | |
| 130 | − { LSM303AGR_ACCEL_DEV_NAME, LSM303AGR }, | |
| 131 | − { LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 }, | |
| 132 | − { LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ }, | |
| 133 | − { LNG2DM_ACCEL_DEV_NAME, LNG2DM }, | |
| 134 | − { H3LIS331DL_ACCEL_DEV_NAME, H3LIS331DL }, | |
| 135 | − { LIS331DL_ACCEL_DEV_NAME, LIS331DL }, | |
| 136 | − { LIS3LV02DL_ACCEL_DEV_NAME, LIS3LV02DL }, | |
| 137 | − { LIS2DW12_ACCEL_DEV_NAME, LIS2DW12 }, | |
| 120 | + { LSM303DLH_ACCEL_DEV_NAME }, | |
| 121 | + { LSM303DLHC_ACCEL_DEV_NAME }, | |
| 122 | + { LIS3DH_ACCEL_DEV_NAME }, | |
| 123 | + { LSM330D_ACCEL_DEV_NAME }, | |
| 124 | + { LSM330DL_ACCEL_DEV_NAME }, | |
| 125 | + { LSM330DLC_ACCEL_DEV_NAME }, | |
| 126 | + { LIS331DLH_ACCEL_DEV_NAME }, | |
| 127 | + { LSM303DL_ACCEL_DEV_NAME }, | |
| 128 | + { LSM303DLM_ACCEL_DEV_NAME }, | |
| 129 | + { LSM330_ACCEL_DEV_NAME }, | |
| 130 | + { LSM303AGR_ACCEL_DEV_NAME }, | |
| 131 | + { LIS2DH12_ACCEL_DEV_NAME }, | |
| 132 | + { LIS3L02DQ_ACCEL_DEV_NAME }, | |
| 133 | + { LNG2DM_ACCEL_DEV_NAME }, | |
| 134 | + { H3LIS331DL_ACCEL_DEV_NAME }, | |
| 135 | + { LIS331DL_ACCEL_DEV_NAME }, | |
| 136 | + { LIS3LV02DL_ACCEL_DEV_NAME }, | |
| 137 | + { LIS2DW12_ACCEL_DEV_NAME }, | |
| 138 | 138 | {}, |
| 139 | 139 | }; |
| 140 | 140 | MODULE_DEVICE_TABLE(i2c, st_accel_id_table); |
| 141 | 141 | |
| 142 | −static int st_accel_i2c_probe(struct i2c_client *client, | |
| 143 | − const struct i2c_device_id *id) | |
| 142 | +static int st_accel_i2c_probe(struct i2c_client *client) | |
| 144 | 143 | { |
| 145 | 144 | struct iio_dev *indio_dev; |
| 146 | 145 | struct st_sensor_data *adata; |
| 146 | + const char *match; | |
| 147 | 147 | int ret; |
| 148 | 148 | |
| 149 | 149 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata)); |
| @@ -152,19 +152,9 @@ static int st_accel_i2c_probe(struct i2c_client *client, · 2 unmodified lines | ||
| 152 | 152 | |
| 153 | 153 | adata = iio_priv(indio_dev); |
| 154 | 154 | |
| 155 | − if (client->dev.of_node) { | |
| 156 | − st_sensors_of_name_probe(&client->dev, st_accel_of_match, | |
| 157 | − client->name, sizeof(client->name)); | |
| 158 | − } else if (ACPI_HANDLE(&client->dev)) { | |
| 159 | − ret = st_sensors_match_acpi_device(&client->dev); | |
| 160 | − if ((ret < 0) || (ret >= ST_ACCEL_MAX)) | |
| 161 | − return -ENODEV; | |
| 162 | − | |
| 163 | − strlcpy(client->name, st_accel_id_table[ret].name, | |
| 164 | − sizeof(client->name)); | |
| 165 | − } else if (!id) | |
| 166 | − return -ENODEV; | |
| 167 | − | |
| 155 | + match = device_get_match_data(&client->dev); | |
| 156 | + if (match) | |
| 157 | + strlcpy(client->name, match, sizeof(client->name)); | |
| 168 | 158 | |
| 169 | 159 | st_sensors_i2c_configure(indio_dev, client, adata); |
| 170 | 160 | |
| @@ -188,7 +178,7 @@ static struct i2c_driver st_accel_driver = { · 17 unmodified lines | ||
| 188 | 178 | .of_match_table = of_match_ptr(st_accel_of_match), |
| 189 | 179 | .acpi_match_table = ACPI_PTR(st_accel_acpi_match), |
| 190 | 180 | }, |
| 191 | − .probe = st_accel_i2c_probe, | |
| 181 | + .probe_new = st_accel_i2c_probe, | |
| 192 | 182 | .remove = st_accel_i2c_remove, |
| 193 | 183 | .id_table = st_accel_id_table, |
| 194 | 184 | }; |
| @@ -620,6 +620,16 @@ config ROCKCHIP_SARADC · 619 unmodified lines | ||
| 620 | 620 | To compile this driver as a module, choose M here: the |
| 621 | 621 | module will be called rockchip_saradc. |
| 622 | 622 | |
| 623 | +config SC27XX_ADC | |
| 624 | + tristate "Spreadtrum SC27xx series PMICs ADC" | |
| 625 | + depends on MFD_SC27XX_PMIC || COMPILE_TEST | |
| 626 | + help | |
| 627 | + Say yes here to build support for the integrated ADC inside the | |
| 628 | + Spreadtrum SC27xx series PMICs. | |
| 629 | + | |
| 630 | + This driver can also be built as a module. If so, the module | |
| 631 | + will be called sc27xx_adc. | |
| 632 | + | |
| 623 | 633 | config SPEAR_ADC |
| 624 | 634 | tristate "ST SPEAr ADC" |
| 625 | 635 | depends on PLAT_SPEAR || COMPILE_TEST |
| @@ -59,6 +59,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o · 58 unmodified lines | ||
| 59 | 59 | obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o |
| 60 | 60 | obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o |
| 61 | 61 | obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o |
| 62 | +obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o | |
| 62 | 63 | obj-$(CONFIG_SPEAR_ADC) += spear_adc.o |
| 63 | 64 | obj-$(CONFIG_STX104) += stx104.o |
| 64 | 65 | obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o |
| @@ -1296,6 +1296,7 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, · 1295 unmodified lines | ||
| 1296 | 1296 | { |
| 1297 | 1297 | struct at91_adc_state *st = iio_priv(indio_dev); |
| 1298 | 1298 | u32 cor = 0; |
| 1299 | + u16 tmp_val; | |
| 1299 | 1300 | int ret; |
| 1300 | 1301 | |
| 1301 | 1302 | /* |
| @@ -1309,7 +1310,8 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, · 7 unmodified lines | ||
| 1309 | 1310 | mutex_lock(&st->lock); |
| 1310 | 1311 | |
| 1311 | 1312 | ret = at91_adc_read_position(st, chan->channel, |
| 1312 | − (u16 *)val); | |
| 1313 | + &tmp_val); | |
| 1314 | + *val = tmp_val; | |
| 1313 | 1315 | mutex_unlock(&st->lock); |
| 1314 | 1316 | iio_device_release_direct_mode(indio_dev); |
| 1315 | 1317 | |
| @@ -1322,7 +1324,8 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, · 6 unmodified lines | ||
| 1322 | 1324 | mutex_lock(&st->lock); |
| 1323 | 1325 | |
| 1324 | 1326 | ret = at91_adc_read_pressure(st, chan->channel, |
| 1325 | − (u16 *)val); | |
| 1327 | + &tmp_val); | |
| 1328 | + *val = tmp_val; | |
| 1326 | 1329 | mutex_unlock(&st->lock); |
| 1327 | 1330 | iio_device_release_direct_mode(indio_dev); |
| 1328 | 1331 | |
| @@ -97,6 +97,14 @@ struct hx711_data { · 96 unmodified lines | ||
| 97 | 97 | * 2x32-bit channel + 64-bit timestamp |
| 98 | 98 | */ |
| 99 | 99 | u32 buffer[4]; |
| 100 | + /* | |
| 101 | + * delay after a rising edge on SCK until the data is ready DOUT | |
| 102 | + * this is dependent on the hx711 where the datasheet tells a | |
| 103 | + * maximum value of 100 ns | |
| 104 | + * but also on potential parasitic capacities on the wiring | |
| 105 | + */ | |
| 106 | + u32 data_ready_delay_ns; | |
| 107 | + u32 clock_frequency; | |
| 100 | 108 | }; |
| 101 | 109 | |
| 102 | 110 | static int hx711_cycle(struct hx711_data *hx711_data) |
| @@ -110,6 +118,14 @@ static int hx711_cycle(struct hx711_data *hx711_data) · 7 unmodified lines | ||
| 110 | 118 | */ |
| 111 | 119 | preempt_disable(); |
| 112 | 120 | gpiod_set_value(hx711_data->gpiod_pd_sck, 1); |
| 121 | + | |
| 122 | + /* | |
| 123 | + * wait until DOUT is ready | |
| 124 | + * it turned out that parasitic capacities are extending the time | |
| 125 | + * until DOUT has reached it's value | |
| 126 | + */ | |
| 127 | + ndelay(hx711_data->data_ready_delay_ns); | |
| 128 | + | |
| 113 | 129 | val = gpiod_get_value(hx711_data->gpiod_dout); |
| 114 | 130 | /* |
| 115 | 131 | * here we are not waiting for 0.2 us as suggested by the datasheet, |
| @@ -120,6 +136,12 @@ static int hx711_cycle(struct hx711_data *hx711_data) · 4 unmodified lines | ||
| 120 | 136 | gpiod_set_value(hx711_data->gpiod_pd_sck, 0); |
| 121 | 137 | preempt_enable(); |
| 122 | 138 | |
| 139 | + /* | |
| 140 | + * make it a square wave for addressing cases with capacitance on | |
| 141 | + * PC_SCK | |
| 142 | + */ | |
| 143 | + ndelay(hx711_data->data_ready_delay_ns); | |
| 144 | + | |
| 123 | 145 | return val; |
| 124 | 146 | } |
| 125 | 147 | |
| @@ -458,6 +480,7 @@ static const struct iio_chan_spec hx711_chan_spec[] = { · 332 unmodified lines | ||
| 458 | 480 | static int hx711_probe(struct platform_device *pdev) |
| 459 | 481 | { |
| 460 | 482 | struct device *dev = &pdev->dev; |
| 483 | + struct device_node *np = dev->of_node; | |
| 461 | 484 | struct hx711_data *hx711_data; |
| 462 | 485 | struct iio_dev *indio_dev; |
| 463 | 486 | int ret; |
| @@ -530,6 +553,22 @@ static int hx711_probe(struct platform_device *pdev) · 66 unmodified lines | ||
| 530 | 553 | hx711_data->gain_set = 128; |
| 531 | 554 | hx711_data->gain_chan_a = 128; |
| 532 | 555 | |
| 556 | + hx711_data->clock_frequency = 400000; | |
| 557 | + ret = of_property_read_u32(np, "clock-frequency", | |
| 558 | + &hx711_data->clock_frequency); | |
| 559 | + | |
| 560 | + /* | |
| 561 | + * datasheet says the high level of PD_SCK has a maximum duration | |
| 562 | + * of 50 microseconds | |
| 563 | + */ | |
| 564 | + if (hx711_data->clock_frequency < 20000) { | |
| 565 | + dev_warn(dev, "clock-frequency too low - assuming 400 kHz\n"); | |
| 566 | + hx711_data->clock_frequency = 400000; | |
| 567 | + } | |
| 568 | + | |
| 569 | + hx711_data->data_ready_delay_ns = | |
| 570 | + 1000000000 / hx711_data->clock_frequency; | |
| 571 | + | |
| 533 | 572 | platform_set_drvdata(pdev, indio_dev); |
| 534 | 573 | |
| 535 | 574 | indio_dev->name = "hx711"; |
| @@ -30,6 +30,7 @@ · 29 unmodified lines | ||
| 30 | 30 | #include <linux/module.h> |
| 31 | 31 | #include <linux/of_device.h> |
| 32 | 32 | #include <linux/regmap.h> |
| 33 | +#include <linux/sched/task.h> | |
| 33 | 34 | #include <linux/util_macros.h> |
| 34 | 35 | |
| 35 | 36 | #include <linux/platform_data/ina2xx.h> |
| @@ -826,6 +827,7 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) · 790 unmodified lines | ||
| 826 | 827 | { |
| 827 | 828 | struct ina2xx_chip_info *chip = iio_priv(indio_dev); |
| 828 | 829 | unsigned int sampling_us = SAMPLING_PERIOD(chip); |
| 830 | + struct task_struct *task; | |
| 829 | 831 | |
| 830 | 832 | dev_dbg(&indio_dev->dev, "Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n", |
| 831 | 833 | (unsigned int)(*indio_dev->active_scan_mask), |
| @@ -835,11 +837,17 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) · 3 unmodified lines | ||
| 835 | 837 | dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", |
| 836 | 838 | chip->allow_async_readout); |
| 837 | 839 | |
| 838 | − chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, | |
| 839 | − "%s:%d-%uus", indio_dev->name, indio_dev->id, | |
| 840 | − sampling_us); | |
| 840 | + task = kthread_create(ina2xx_capture_thread, (void *)indio_dev, | |
| 841 | + "%s:%d-%uus", indio_dev->name, indio_dev->id, | |
| 842 | + sampling_us); | |
| 843 | + if (IS_ERR(task)) | |
| 844 | + return PTR_ERR(task); | |
| 845 | + | |
| 846 | + get_task_struct(task); | |
| 847 | + wake_up_process(task); | |
| 848 | + chip->task = task; | |
| 841 | 849 | |
| 842 | − return PTR_ERR_OR_ZERO(chip->task); | |
| 850 | + return 0; | |
| 843 | 851 | } |
| 844 | 852 | |
| 845 | 853 | static int ina2xx_buffer_disable(struct iio_dev *indio_dev) |
| @@ -848,6 +856,7 @@ static int ina2xx_buffer_disable(struct iio_dev *indio_dev) · 2 unmodified lines | ||
| 848 | 856 | |
| 849 | 857 | if (chip->task) { |
| 850 | 858 | kthread_stop(chip->task); |
| 859 | + put_task_struct(chip->task); | |
| 851 | 860 | chip->task = NULL; |
| 852 | 861 | } |
| 853 | 862 | |
| @@ -1577,7 +1577,6 @@ static int max1363_probe(struct i2c_client *client, · 1576 unmodified lines | ||
| 1577 | 1577 | struct max1363_state *st; |
| 1578 | 1578 | struct iio_dev *indio_dev; |
| 1579 | 1579 | struct regulator *vref; |
| 1580 | − const struct of_device_id *match; | |
| 1581 | 1580 | |
| 1582 | 1581 | indio_dev = devm_iio_device_alloc(&client->dev, |
| 1583 | 1582 | sizeof(struct max1363_state)); |
| @@ -1604,11 +1603,8 @@ static int max1363_probe(struct i2c_client *client, · 20 unmodified lines | ||
| 1604 | 1603 | /* this is only used for device removal purposes */ |
| 1605 | 1604 | i2c_set_clientdata(client, indio_dev); |
| 1606 | 1605 | |
| 1607 | − match = of_match_device(of_match_ptr(max1363_of_match), | |
| 1608 | − &client->dev); | |
| 1609 | − if (match) | |
| 1610 | − st->chip_info = of_device_get_match_data(&client->dev); | |
| 1611 | − else | |
| 1606 | + st->chip_info = of_device_get_match_data(&client->dev); | |
| 1607 | + if (!st->chip_info) | |
| 1612 | 1608 | st->chip_info = &max1363_chip_info_tbl[id->driver_data]; |
| 1613 | 1609 | st->client = client; |
| 1614 | 1610 | |
| @@ -922,6 +922,11 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { · 921 unmodified lines | ||
| 922 | 922 | .name = "meson-meson8b-saradc", |
| 923 | 923 | }; |
| 924 | 924 | |
| 925 | +static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = { | |
| 926 | + .param = &meson_sar_adc_meson8_param, | |
| 927 | + .name = "meson-meson8m2-saradc", | |
| 928 | +}; | |
| 929 | + | |
| 925 | 930 | static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { |
| 926 | 931 | .param = &meson_sar_adc_gxbb_param, |
| 927 | 932 | .name = "meson-gxbb-saradc", |
| @@ -951,6 +956,10 @@ static const struct of_device_id meson_sar_adc_of_match[] = { · 23 unmodified lines | ||
| 951 | 956 | .compatible = "amlogic,meson8b-saradc", |
| 952 | 957 | .data = &meson_sar_adc_meson8b_data, |
| 953 | 958 | }, |
| 959 | + { | |
| 960 | + .compatible = "amlogic,meson8m2-saradc", | |
| 961 | + .data = &meson_sar_adc_meson8m2_data, | |
| 962 | + }, | |
| 954 | 963 | { |
| 955 | 964 | .compatible = "amlogic,meson-gxbb-saradc", |
| 956 | 965 | .data = &meson_sar_adc_gxbb_data, |
| @@ -0,0 +1,522 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +// Copyright (C) 2018 Spreadtrum Communications Inc. | |
| 3 | + | |
| 4 | +#include <linux/hwspinlock.h> | |
| 5 | +#include <linux/iio/iio.h> | |
| 6 | +#include <linux/interrupt.h> | |
| 7 | +#include <linux/module.h> | |
| 8 | +#include <linux/of.h> | |
| 9 | +#include <linux/of_device.h> | |
| 10 | +#include <linux/platform_device.h> | |
| 11 | +#include <linux/regmap.h> | |
| 12 | + | |
| 13 | +/* PMIC global registers definition */ | |
| 14 | +#define SC27XX_MODULE_EN 0xc08 | |
| 15 | +#define SC27XX_MODULE_ADC_EN BIT(5) | |
| 16 | +#define SC27XX_ARM_CLK_EN 0xc10 | |
| 17 | +#define SC27XX_CLK_ADC_EN BIT(5) | |
| 18 | +#define SC27XX_CLK_ADC_CLK_EN BIT(6) | |
| 19 | + | |
| 20 | +/* ADC controller registers definition */ | |
| 21 | +#define SC27XX_ADC_CTL 0x0 | |
| 22 | +#define SC27XX_ADC_CH_CFG 0x4 | |
| 23 | +#define SC27XX_ADC_DATA 0x4c | |
| 24 | +#define SC27XX_ADC_INT_EN 0x50 | |
| 25 | +#define SC27XX_ADC_INT_CLR 0x54 | |
| 26 | +#define SC27XX_ADC_INT_STS 0x58 | |
| 27 | +#define SC27XX_ADC_INT_RAW 0x5c | |
| 28 | + | |
| 29 | +/* Bits and mask definition for SC27XX_ADC_CTL register */ | |
| 30 | +#define SC27XX_ADC_EN BIT(0) | |
| 31 | +#define SC27XX_ADC_CHN_RUN BIT(1) | |
| 32 | +#define SC27XX_ADC_12BIT_MODE BIT(2) | |
| 33 | +#define SC27XX_ADC_RUN_NUM_MASK GENMASK(7, 4) | |
| 34 | +#define SC27XX_ADC_RUN_NUM_SHIFT 4 | |
| 35 | + | |
| 36 | +/* Bits and mask definition for SC27XX_ADC_CH_CFG register */ | |
| 37 | +#define SC27XX_ADC_CHN_ID_MASK GENMASK(4, 0) | |
| 38 | +#define SC27XX_ADC_SCALE_MASK GENMASK(10, 8) | |
| 39 | +#define SC27XX_ADC_SCALE_SHIFT 8 | |
| 40 | + | |
| 41 | +/* Bits definitions for SC27XX_ADC_INT_EN registers */ | |
| 42 | +#define SC27XX_ADC_IRQ_EN BIT(0) | |
| 43 | + | |
| 44 | +/* Bits definitions for SC27XX_ADC_INT_CLR registers */ | |
| 45 | +#define SC27XX_ADC_IRQ_CLR BIT(0) | |
| 46 | + | |
| 47 | +/* Mask definition for SC27XX_ADC_DATA register */ | |
| 48 | +#define SC27XX_ADC_DATA_MASK GENMASK(11, 0) | |
| 49 | + | |
| 50 | +/* Timeout (ms) for the trylock of hardware spinlocks */ | |
| 51 | +#define SC27XX_ADC_HWLOCK_TIMEOUT 5000 | |
| 52 | + | |
| 53 | +/* Maximum ADC channel number */ | |
| 54 | +#define SC27XX_ADC_CHANNEL_MAX 32 | |
| 55 | + | |
| 56 | +/* ADC voltage ratio definition */ | |
| 57 | +#define SC27XX_VOLT_RATIO(n, d) \ | |
| 58 | + (((n) << SC27XX_RATIO_NUMERATOR_OFFSET) | (d)) | |
| 59 | +#define SC27XX_RATIO_NUMERATOR_OFFSET 16 | |
| 60 | +#define SC27XX_RATIO_DENOMINATOR_MASK GENMASK(15, 0) | |
| 61 | + | |
| 62 | +struct sc27xx_adc_data { | |
| 63 | + struct device *dev; | |
| 64 | + struct regmap *regmap; | |
| 65 | + /* | |
| 66 | + * One hardware spinlock to synchronize between the multiple | |
| 67 | + * subsystems which will access the unique ADC controller. | |
| 68 | + */ | |
| 69 | + struct hwspinlock *hwlock; | |
| 70 | + struct completion completion; | |
| 71 | + int channel_scale[SC27XX_ADC_CHANNEL_MAX]; | |
| 72 | + u32 base; | |
| 73 | + int value; | |
| 74 | + int irq; | |
| 75 | +}; | |
| 76 | + | |
| 77 | +struct sc27xx_adc_linear_graph { | |
| 78 | + int volt0; | |
| 79 | + int adc0; | |
| 80 | + int volt1; | |
| 81 | + int adc1; | |
| 82 | +}; | |
| 83 | + | |
| 84 | +/* | |
| 85 | + * According to the datasheet, we can convert one ADC value to one voltage value | |
| 86 | + * through 2 points in the linear graph. If the voltage is less than 1.2v, we | |
| 87 | + * should use the small-scale graph, and if more than 1.2v, we should use the | |
| 88 | + * big-scale graph. | |
| 89 | + */ | |
| 90 | +static const struct sc27xx_adc_linear_graph big_scale_graph = { | |
| 91 | + 4200, 3310, | |
| 92 | + 3600, 2832, | |
| 93 | +}; | |
| 94 | + | |
| 95 | +static const struct sc27xx_adc_linear_graph small_scale_graph = { | |
| 96 | + 1000, 3413, | |
| 97 | + 100, 341, | |
| 98 | +}; | |
| 99 | + | |
| 100 | +static int sc27xx_adc_get_ratio(int channel, int scale) | |
| 101 | +{ | |
| 102 | + switch (channel) { | |
| 103 | + case 1: | |
| 104 | + case 2: | |
| 105 | + case 3: | |
| 106 | + case 4: | |
| 107 | + return scale ? SC27XX_VOLT_RATIO(400, 1025) : | |
| 108 | + SC27XX_VOLT_RATIO(1, 1); | |
| 109 | + case 5: | |
| 110 | + return SC27XX_VOLT_RATIO(7, 29); | |
| 111 | + case 6: | |
| 112 | + return SC27XX_VOLT_RATIO(375, 9000); | |
| 113 | + case 7: | |
| 114 | + case 8: | |
| 115 | + return scale ? SC27XX_VOLT_RATIO(100, 125) : | |
| 116 | + SC27XX_VOLT_RATIO(1, 1); | |
| 117 | + case 19: | |
| 118 | + return SC27XX_VOLT_RATIO(1, 3); | |
| 119 | + default: | |
| 120 | + return SC27XX_VOLT_RATIO(1, 1); | |
| 121 | + } | |
| 122 | + return SC27XX_VOLT_RATIO(1, 1); | |
| 123 | +} | |
| 124 | + | |
| 125 | +static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel, | |
| 126 | + int scale, int *val) | |
| 127 | +{ | |
| 128 | + int ret; | |
| 129 | + u32 tmp; | |
| 130 | + | |
| 131 | + reinit_completion(&data->completion); | |
| 132 | + | |
| 133 | + ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT); | |
| 134 | + if (ret) { | |
| 135 | + dev_err(data->dev, "timeout to get the hwspinlock\n"); | |
| 136 | + return ret; | |
| 137 | + } | |
| 138 | + | |
| 139 | + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, | |
| 140 | + SC27XX_ADC_EN, SC27XX_ADC_EN); | |
| 141 | + if (ret) | |
| 142 | + goto unlock_adc; | |
| 143 | + | |
| 144 | + /* Configure the channel id and scale */ | |
| 145 | + tmp = (scale << SC27XX_ADC_SCALE_SHIFT) & SC27XX_ADC_SCALE_MASK; | |
| 146 | + tmp |= channel & SC27XX_ADC_CHN_ID_MASK; | |
| 147 | + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG, | |
| 148 | + SC27XX_ADC_CHN_ID_MASK | SC27XX_ADC_SCALE_MASK, | |
| 149 | + tmp); | |
| 150 | + if (ret) | |
| 151 | + goto disable_adc; | |
| 152 | + | |
| 153 | + /* Select 12bit conversion mode, and only sample 1 time */ | |
| 154 | + tmp = SC27XX_ADC_12BIT_MODE; | |
| 155 | + tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK; | |
| 156 | + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, | |
| 157 | + SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE, | |
| 158 | + tmp); | |
| 159 | + if (ret) | |
| 160 | + goto disable_adc; | |
| 161 | + | |
| 162 | + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, | |
| 163 | + SC27XX_ADC_CHN_RUN, SC27XX_ADC_CHN_RUN); | |
| 164 | + if (ret) | |
| 165 | + goto disable_adc; | |
| 166 | + | |
| 167 | + wait_for_completion(&data->completion); | |
| 168 | + | |
| 169 | +disable_adc: | |
| 170 | + regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, | |
| 171 | + SC27XX_ADC_EN, 0); | |
| 172 | +unlock_adc: | |
| 173 | + hwspin_unlock_raw(data->hwlock); | |
| 174 | + | |
| 175 | + if (!ret) | |
| 176 | + *val = data->value; | |
| 177 | + | |
| 178 | + return ret; | |
| 179 | +} | |
| 180 | + | |
| 181 | +static irqreturn_t sc27xx_adc_isr(int irq, void *dev_id) | |
| 182 | +{ | |
| 183 | + struct sc27xx_adc_data *data = dev_id; | |
| 184 | + int ret; | |
| 185 | + | |
| 186 | + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR, | |
| 187 | + SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR); | |
| 188 | + if (ret) | |
| 189 | + return IRQ_RETVAL(ret); | |
| 190 | + | |
| 191 | + ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, | |
| 192 | + &data->value); | |
| 193 | + if (ret) | |
| 194 | + return IRQ_RETVAL(ret); | |
| 195 | + | |
| 196 | + data->value &= SC27XX_ADC_DATA_MASK; | |
| 197 | + complete(&data->completion); | |
| 198 | + | |
| 199 | + return IRQ_HANDLED; | |
| 200 | +} | |
| 201 | + | |
| 202 | +static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, | |
| 203 | + int channel, int scale, | |
| 204 | + u32 *div_numerator, u32 *div_denominator) | |
| 205 | +{ | |
| 206 | + u32 ratio = sc27xx_adc_get_ratio(channel, scale); | |
| 207 | + | |
| 208 | + *div_numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET; | |
| 209 | + *div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK; | |
| 210 | +} | |
| 211 | + | |
| 212 | +static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph, | |
| 213 | + int raw_adc) | |
| 214 | +{ | |
| 215 | + int tmp; | |
| 216 | + | |
| 217 | + tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1); | |
| 218 | + tmp /= (graph->adc0 - graph->adc1); | |
| 219 | + tmp += graph->volt1; | |
| 220 | + | |
| 221 | + return tmp < 0 ? 0 : tmp; | |
| 222 | +} | |
| 223 | + | |
| 224 | +static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, | |
| 225 | + int scale, int raw_adc) | |
| 226 | +{ | |
| 227 | + u32 numerator, denominator; | |
| 228 | + u32 volt; | |
| 229 | + | |
| 230 | + /* | |
| 231 | + * Convert ADC values to voltage values according to the linear graph, | |
| 232 | + * and channel 5 and channel 1 has been calibrated, so we can just | |
| 233 | + * return the voltage values calculated by the linear graph. But other | |
| 234 | + * channels need be calculated to the real voltage values with the | |
| 235 | + * voltage ratio. | |
| 236 | + */ | |
| 237 | + switch (channel) { | |
| 238 | + case 5: | |
| 239 | + return sc27xx_adc_to_volt(&big_scale_graph, raw_adc); | |
| 240 | + | |
| 241 | + case 1: | |
| 242 | + return sc27xx_adc_to_volt(&small_scale_graph, raw_adc); | |
| 243 | + | |
| 244 | + default: | |
| 245 | + volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc); | |
| 246 | + break; | |
| 247 | + } | |
| 248 | + | |
| 249 | + sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator); | |
| 250 | + | |
| 251 | + return (volt * denominator + numerator / 2) / numerator; | |
| 252 | +} | |
| 253 | + | |
| 254 | +static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data, | |
| 255 | + int channel, int scale, int *val) | |
| 256 | +{ | |
| 257 | + int ret, raw_adc; | |
| 258 | + | |
| 259 | + ret = sc27xx_adc_read(data, channel, scale, &raw_adc); | |
| 260 | + if (ret) | |
| 261 | + return ret; | |
| 262 | + | |
| 263 | + *val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc); | |
| 264 | + return 0; | |
| 265 | +} | |
| 266 | + | |
| 267 | +static int sc27xx_adc_read_raw(struct iio_dev *indio_dev, | |
| 268 | + struct iio_chan_spec const *chan, | |
| 269 | + int *val, int *val2, long mask) | |
| 270 | +{ | |
| 271 | + struct sc27xx_adc_data *data = iio_priv(indio_dev); | |
| 272 | + int scale = data->channel_scale[chan->channel]; | |
| 273 | + int ret, tmp; | |
| 274 | + | |
| 275 | + switch (mask) { | |
| 276 | + case IIO_CHAN_INFO_PROCESSED: | |
| 277 | + mutex_lock(&indio_dev->mlock); | |
| 278 | + ret = sc27xx_adc_read_processed(data, chan->channel, scale, | |
| 279 | + &tmp); | |
| 280 | + mutex_unlock(&indio_dev->mlock); | |
| 281 | + | |
| 282 | + if (ret) | |
| 283 | + return ret; | |
| 284 | + | |
| 285 | + *val = tmp; | |
| 286 | + return IIO_VAL_INT; | |
| 287 | + | |
| 288 | + case IIO_CHAN_INFO_SCALE: | |
| 289 | + *val = scale; | |
| 290 | + return IIO_VAL_INT; | |
| 291 | + | |
| 292 | + default: | |
| 293 | + return -EINVAL; | |
| 294 | + } | |
| 295 | +} | |
| 296 | + | |
| 297 | +static int sc27xx_adc_write_raw(struct iio_dev *indio_dev, | |
| 298 | + struct iio_chan_spec const *chan, | |
| 299 | + int val, int val2, long mask) | |
| 300 | +{ | |
| 301 | + struct sc27xx_adc_data *data = iio_priv(indio_dev); | |
| 302 | + | |
| 303 | + switch (mask) { | |
| 304 | + case IIO_CHAN_INFO_SCALE: | |
| 305 | + data->channel_scale[chan->channel] = val; | |
| 306 | + return IIO_VAL_INT; | |
| 307 | + | |
| 308 | + default: | |
| 309 | + return -EINVAL; | |
| 310 | + } | |
| 311 | +} | |
| 312 | + | |
| 313 | +static const struct iio_info sc27xx_info = { | |
| 314 | + .read_raw = &sc27xx_adc_read_raw, | |
| 315 | + .write_raw = &sc27xx_adc_write_raw, | |
| 316 | +}; | |
| 317 | + | |
| 318 | +#define SC27XX_ADC_CHANNEL(index) { \ | |
| 319 | + .type = IIO_VOLTAGE, \ | |
| 320 | + .channel = index, \ | |
| 321 | + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \ | |
| 322 | + BIT(IIO_CHAN_INFO_SCALE), \ | |
| 323 | + .datasheet_name = "CH##index", \ | |
| 324 | + .indexed = 1, \ | |
| 325 | +} | |
| 326 | + | |
| 327 | +static const struct iio_chan_spec sc27xx_channels[] = { | |
| 328 | + SC27XX_ADC_CHANNEL(0), | |
| 329 | + SC27XX_ADC_CHANNEL(1), | |
| 330 | + SC27XX_ADC_CHANNEL(2), | |
| 331 | + SC27XX_ADC_CHANNEL(3), | |
| 332 | + SC27XX_ADC_CHANNEL(4), | |
| 333 | + SC27XX_ADC_CHANNEL(5), | |
| 334 | + SC27XX_ADC_CHANNEL(6), | |
| 335 | + SC27XX_ADC_CHANNEL(7), | |
| 336 | + SC27XX_ADC_CHANNEL(8), | |
| 337 | + SC27XX_ADC_CHANNEL(9), | |
| 338 | + SC27XX_ADC_CHANNEL(10), | |
| 339 | + SC27XX_ADC_CHANNEL(11), | |
| 340 | + SC27XX_ADC_CHANNEL(12), | |
| 341 | + SC27XX_ADC_CHANNEL(13), | |
| 342 | + SC27XX_ADC_CHANNEL(14), | |
| 343 | + SC27XX_ADC_CHANNEL(15), | |
| 344 | + SC27XX_ADC_CHANNEL(16), | |
| 345 | + SC27XX_ADC_CHANNEL(17), | |
| 346 | + SC27XX_ADC_CHANNEL(18), | |
| 347 | + SC27XX_ADC_CHANNEL(19), | |
| 348 | + SC27XX_ADC_CHANNEL(20), | |
| 349 | + SC27XX_ADC_CHANNEL(21), | |
| 350 | + SC27XX_ADC_CHANNEL(22), | |
| 351 | + SC27XX_ADC_CHANNEL(23), | |
| 352 | + SC27XX_ADC_CHANNEL(24), | |
| 353 | + SC27XX_ADC_CHANNEL(25), | |
| 354 | + SC27XX_ADC_CHANNEL(26), | |
| 355 | + SC27XX_ADC_CHANNEL(27), | |
| 356 | + SC27XX_ADC_CHANNEL(28), | |
| 357 | + SC27XX_ADC_CHANNEL(29), | |
| 358 | + SC27XX_ADC_CHANNEL(30), | |
| 359 | + SC27XX_ADC_CHANNEL(31), | |
| 360 | +}; | |
| 361 | + | |
| 362 | +static int sc27xx_adc_enable(struct sc27xx_adc_data *data) | |
| 363 | +{ | |
| 364 | + int ret; | |
| 365 | + | |
| 366 | + ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN, | |
| 367 | + SC27XX_MODULE_ADC_EN, SC27XX_MODULE_ADC_EN); | |
| 368 | + if (ret) | |
| 369 | + return ret; | |
| 370 | + | |
| 371 | + /* Enable ADC work clock and controller clock */ | |
| 372 | + ret = regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN, | |
| 373 | + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, | |
| 374 | + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); | |
| 375 | + if (ret) | |
| 376 | + goto disable_adc; | |
| 377 | + | |
| 378 | + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN, | |
| 379 | + SC27XX_ADC_IRQ_EN, SC27XX_ADC_IRQ_EN); | |
| 380 | + if (ret) | |
| 381 | + goto disable_clk; | |
| 382 | + | |
| 383 | + return 0; | |
| 384 | + | |
| 385 | +disable_clk: | |
| 386 | + regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN, | |
| 387 | + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0); | |
| 388 | +disable_adc: | |
| 389 | + regmap_update_bits(data->regmap, SC27XX_MODULE_EN, | |
| 390 | + SC27XX_MODULE_ADC_EN, 0); | |
| 391 | + | |
| 392 | + return ret; | |
| 393 | +} | |
| 394 | + | |
| 395 | +static void sc27xx_adc_disable(void *_data) | |
| 396 | +{ | |
| 397 | + struct sc27xx_adc_data *data = _data; | |
| 398 | + | |
| 399 | + regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN, | |
| 400 | + SC27XX_ADC_IRQ_EN, 0); | |
| 401 | + | |
| 402 | + /* Disable ADC work clock and controller clock */ | |
| 403 | + regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN, | |
| 404 | + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0); | |
| 405 | + | |
| 406 | + regmap_update_bits(data->regmap, SC27XX_MODULE_EN, | |
| 407 | + SC27XX_MODULE_ADC_EN, 0); | |
| 408 | +} | |
| 409 | + | |
| 410 | +static void sc27xx_adc_free_hwlock(void *_data) | |
| 411 | +{ | |
| 412 | + struct hwspinlock *hwlock = _data; | |
| 413 | + | |
| 414 | + hwspin_lock_free(hwlock); | |
| 415 | +} | |
| 416 | + | |
| 417 | +static int sc27xx_adc_probe(struct platform_device *pdev) | |
| 418 | +{ | |
| 419 | + struct device_node *np = pdev->dev.of_node; | |
| 420 | + struct sc27xx_adc_data *sc27xx_data; | |
| 421 | + struct iio_dev *indio_dev; | |
| 422 | + int ret; | |
| 423 | + | |
| 424 | + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data)); | |
| 425 | + if (!indio_dev) | |
| 426 | + return -ENOMEM; | |
| 427 | + | |
| 428 | + sc27xx_data = iio_priv(indio_dev); | |
| 429 | + | |
| 430 | + sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL); | |
| 431 | + if (!sc27xx_data->regmap) { | |
| 432 | + dev_err(&pdev->dev, "failed to get ADC regmap\n"); | |
| 433 | + return -ENODEV; | |
| 434 | + } | |
| 435 | + | |
| 436 | + ret = of_property_read_u32(np, "reg", &sc27xx_data->base); | |
| 437 | + if (ret) { | |
| 438 | + dev_err(&pdev->dev, "failed to get ADC base address\n"); | |
| 439 | + return ret; | |
| 440 | + } | |
| 441 | + | |
| 442 | + sc27xx_data->irq = platform_get_irq(pdev, 0); | |
| 443 | + if (sc27xx_data->irq < 0) { | |
| 444 | + dev_err(&pdev->dev, "failed to get ADC irq number\n"); | |
| 445 | + return sc27xx_data->irq; | |
| 446 | + } | |
| 447 | + | |
| 448 | + ret = of_hwspin_lock_get_id(np, 0); | |
| 449 | + if (ret < 0) { | |
| 450 | + dev_err(&pdev->dev, "failed to get hwspinlock id\n"); | |
| 451 | + return ret; | |
| 452 | + } | |
| 453 | + | |
| 454 | + sc27xx_data->hwlock = hwspin_lock_request_specific(ret); | |
| 455 | + if (!sc27xx_data->hwlock) { | |
| 456 | + dev_err(&pdev->dev, "failed to request hwspinlock\n"); | |
| 457 | + return -ENXIO; | |
| 458 | + } | |
| 459 | + | |
| 460 | + ret = devm_add_action(&pdev->dev, sc27xx_adc_free_hwlock, | |
| 461 | + sc27xx_data->hwlock); | |
| 462 | + if (ret) { | |
| 463 | + sc27xx_adc_free_hwlock(sc27xx_data->hwlock); | |
| 464 | + dev_err(&pdev->dev, "failed to add hwspinlock action\n"); | |
| 465 | + return ret; | |
| 466 | + } | |
| 467 | + | |
| 468 | + init_completion(&sc27xx_data->completion); | |
| 469 | + sc27xx_data->dev = &pdev->dev; | |
| 470 | + | |
| 471 | + ret = sc27xx_adc_enable(sc27xx_data); | |
| 472 | + if (ret) { | |
| 473 | + dev_err(&pdev->dev, "failed to enable ADC module\n"); | |
| 474 | + return ret; | |
| 475 | + } | |
| 476 | + | |
| 477 | + ret = devm_add_action(&pdev->dev, sc27xx_adc_disable, sc27xx_data); | |
| 478 | + if (ret) { | |
| 479 | + sc27xx_adc_disable(sc27xx_data); | |
| 480 | + dev_err(&pdev->dev, "failed to add ADC disable action\n"); | |
| 481 | + return ret; | |
| 482 | + } | |
| 483 | + | |
| 484 | + ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL, | |
| 485 | + sc27xx_adc_isr, IRQF_ONESHOT, | |
| 486 | + pdev->name, sc27xx_data); | |
| 487 | + if (ret) { | |
| 488 | + dev_err(&pdev->dev, "failed to request ADC irq\n"); | |
| 489 | + return ret; | |
| 490 | + } | |
| 491 | + | |
| 492 | + indio_dev->dev.parent = &pdev->dev; | |
| 493 | + indio_dev->name = dev_name(&pdev->dev); | |
| 494 | + indio_dev->modes = INDIO_DIRECT_MODE; | |
| 495 | + indio_dev->info = &sc27xx_info; | |
| 496 | + indio_dev->channels = sc27xx_channels; | |
| 497 | + indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels); | |
| 498 | + ret = devm_iio_device_register(&pdev->dev, indio_dev); | |
| 499 | + if (ret) | |
| 500 | + dev_err(&pdev->dev, "could not register iio (ADC)"); | |
| 501 | + | |
| 502 | + return ret; | |
| 503 | +} | |
| 504 | + | |
| 505 | +static const struct of_device_id sc27xx_adc_of_match[] = { | |
| 506 | + { .compatible = "sprd,sc2731-adc", }, | |
| 507 | + { } | |
| 508 | +}; | |
| 509 | + | |
| 510 | +static struct platform_driver sc27xx_adc_driver = { | |
| 511 | + .probe = sc27xx_adc_probe, | |
| 512 | + .driver = { | |
| 513 | + .name = "sc27xx-adc", | |
| 514 | + .of_match_table = sc27xx_adc_of_match, | |
| 515 | + }, | |
| 516 | +}; | |
| 517 | + | |
| 518 | +module_platform_driver(sc27xx_adc_driver); | |
| 519 | + | |
| 520 | +MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>"); | |
| 521 | +MODULE_DESCRIPTION("Spreadtrum SC27XX ADC Driver"); | |
| 522 | +MODULE_LICENSE("GPL v2"); | |
| @@ -1,3 +1,4 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 1 | 2 | /* |
| 2 | 3 | * Texas Instruments ADS7950 SPI ADC driver |
| 3 | 4 | * |
| @@ -10,15 +11,6 @@ · 6 unmodified lines | ||
| 10 | 11 | * And also on hwmon/ads79xx.c |
| 11 | 12 | * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ |
| 12 | 13 | * Nishanth Menon |
| 13 | − * | |
| 14 | − * This program is free software; you can redistribute it and/or | |
| 15 | − * modify it under the terms of the GNU General Public License as | |
| 16 | − * published by the Free Software Foundation version 2. | |
| 17 | − * | |
| 18 | − * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
| 19 | − * kind, whether express or implied; without even the implied warranty | |
| 20 | − * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 21 | − * GNU General Public License for more details. | |
| 22 | 14 | */ |
| 23 | 15 | |
| 24 | 16 | #include <linux/acpi.h> |
| @@ -76,6 +68,9 @@ struct ti_ads7950_state { · 51 unmodified lines | ||
| 76 | 68 | __be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE] |
| 77 | 69 | ____cacheline_aligned; |
| 78 | 70 | __be16 tx_buf[TI_ADS7950_MAX_CHAN]; |
| 71 | + __be16 single_tx; | |
| 72 | + __be16 single_rx; | |
| 73 | + | |
| 79 | 74 | }; |
| 80 | 75 | |
| 81 | 76 | struct ti_ads7950_chip_info { |
| @@ -295,18 +290,26 @@ out: · 213 unmodified lines | ||
| 295 | 290 | return IRQ_HANDLED; |
| 296 | 291 | } |
| 297 | 292 | |
| 298 | −static int ti_ads7950_scan_direct(struct ti_ads7950_state *st, unsigned int ch) | |
| 293 | +static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch) | |
| 299 | 294 | { |
| 295 | + struct ti_ads7950_state *st = iio_priv(indio_dev); | |
| 300 | 296 | int ret, cmd; |
| 301 | 297 | |
| 298 | + mutex_lock(&indio_dev->mlock); | |
| 299 | + | |
| 302 | 300 | cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings; |
| 303 | − st->tx_buf[0] = cpu_to_be16(cmd); | |
| 301 | + st->single_tx = cpu_to_be16(cmd); | |
| 304 | 302 | |
| 305 | 303 | ret = spi_sync(st->spi, &st->scan_single_msg); |
| 306 | 304 | if (ret) |
| 307 | − return ret; | |
| 305 | + goto out; | |
| 306 | + | |
| 307 | + ret = be16_to_cpu(st->single_rx); | |
| 308 | 308 | |
| 309 | − return be16_to_cpu(st->rx_buf[0]); | |
| 309 | +out: | |
| 310 | + mutex_unlock(&indio_dev->mlock); | |
| 311 | + | |
| 312 | + return ret; | |
| 310 | 313 | } |
| 311 | 314 | |
| 312 | 315 | static int ti_ads7950_get_range(struct ti_ads7950_state *st) |
| @@ -338,13 +341,7 @@ static int ti_ads7950_read_raw(struct iio_dev *indio_dev, · 25 unmodified lines | ||
| 338 | 341 | |
| 339 | 342 | switch (m) { |
| 340 | 343 | case IIO_CHAN_INFO_RAW: |
| 341 | − | |
| 342 | − ret = iio_device_claim_direct_mode(indio_dev); | |
| 343 | − if (ret < 0) | |
| 344 | − return ret; | |
| 345 | − | |
| 346 | − ret = ti_ads7950_scan_direct(st, chan->address); | |
| 347 | − iio_device_release_direct_mode(indio_dev); | |
| 344 | + ret = ti_ads7950_scan_direct(indio_dev, chan->address); | |
| 348 | 345 | if (ret < 0) |
| 349 | 346 | return ret; |
| 350 | 347 | |
| @@ -410,13 +407,13 @@ static int ti_ads7950_probe(struct spi_device *spi) · 59 unmodified lines | ||
| 410 | 407 | * was read at the end of the first transfer. |
| 411 | 408 | */ |
| 412 | 409 | |
| 413 | − st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; | |
| 410 | + st->scan_single_xfer[0].tx_buf = &st->single_tx; | |
| 414 | 411 | st->scan_single_xfer[0].len = 2; |
| 415 | 412 | st->scan_single_xfer[0].cs_change = 1; |
| 416 | − st->scan_single_xfer[1].tx_buf = &st->tx_buf[0]; | |
| 413 | + st->scan_single_xfer[1].tx_buf = &st->single_tx; | |
| 417 | 414 | st->scan_single_xfer[1].len = 2; |
| 418 | 415 | st->scan_single_xfer[1].cs_change = 1; |
| 419 | − st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; | |
| 416 | + st->scan_single_xfer[2].rx_buf = &st->single_rx; | |
| 420 | 417 | st->scan_single_xfer[2].len = 2; |
| 421 | 418 | |
| 422 | 419 | spi_message_init_with_transfers(&st->scan_single_msg, |
| @@ -341,8 +341,6 @@ static int xadc_zynq_setup(struct platform_device *pdev, · 340 unmodified lines | ||
| 341 | 341 | |
| 342 | 342 | pcap_rate = clk_get_rate(xadc->clk); |
| 343 | 343 | |
| 344 | − if (tck_rate > XADC_ZYNQ_TCK_RATE_MAX) | |
| 345 | − tck_rate = XADC_ZYNQ_TCK_RATE_MAX; | |
| 346 | 344 | if (tck_rate > pcap_rate / 2) { |
| 347 | 345 | div = 2; |
| 348 | 346 | } else { |
| @@ -1045,7 +1043,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, · 696 unmodified lines | ||
| 1045 | 1043 | unsigned int num_channels; |
| 1046 | 1044 | const char *external_mux; |
| 1047 | 1045 | u32 ext_mux_chan; |
| 1048 | − int reg; | |
| 1046 | + u32 reg; | |
| 1049 | 1047 | int ret; |
| 1050 | 1048 | |
| 1051 | 1049 | *conf = 0; |
| @@ -380,8 +380,7 @@ void st_sensors_of_name_probe(struct device *dev, · 379 unmodified lines | ||
| 380 | 380 | return; |
| 381 | 381 | |
| 382 | 382 | /* The name from the OF match takes precedence if present */ |
| 383 | − strncpy(name, of_id->data, len); | |
| 384 | − name[len - 1] = '\0'; | |
| 383 | + strlcpy(name, of_id->data, len); | |
| 385 | 384 | } |
| 386 | 385 | EXPORT_SYMBOL(st_sensors_of_name_probe); |
| 387 | 386 | #else |
| @@ -167,6 +167,16 @@ config AD5755 · 166 unmodified lines | ||
| 167 | 167 | To compile this driver as a module, choose M here: the |
| 168 | 168 | module will be called ad5755. |
| 169 | 169 | |
| 170 | +config AD5758 | |
| 171 | + tristate "Analog Devices AD5758 DAC driver" | |
| 172 | + depends on SPI_MASTER | |
| 173 | + help | |
| 174 | + Say yes here to build support for Analog Devices AD5758 single channel | |
| 175 | + Digital to Analog Converter. | |
| 176 | + | |
| 177 | + To compile this driver as a module, choose M here: the | |
| 178 | + module will be called ad5758. | |
| 179 | + | |
| 170 | 180 | config AD5761 |
| 171 | 181 | tristate "Analog Devices AD5761/61R/21/21R DAC driver" |
| 172 | 182 | depends on SPI_MASTER |
| @@ -16,6 +16,7 @@ obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o · 15 unmodified lines | ||
| 16 | 16 | obj-$(CONFIG_AD5592R) += ad5592r.o |
| 17 | 17 | obj-$(CONFIG_AD5593R) += ad5593r.o |
| 18 | 18 | obj-$(CONFIG_AD5755) += ad5755.o |
| 19 | +obj-$(CONFIG_AD5755) += ad5758.o | |
| 19 | 20 | obj-$(CONFIG_AD5761) += ad5761.o |
| 20 | 21 | obj-$(CONFIG_AD5764) += ad5764.o |
| 21 | 22 | obj-$(CONFIG_AD5791) += ad5791.o |
| @@ -0,0 +1,897 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | +/* | |
| 3 | + * AD5758 Digital to analog converters driver | |
| 4 | + * | |
| 5 | + * Copyright 2018 Analog Devices Inc. | |
| 6 | + * | |
| 7 | + * TODO: Currently CRC is not supported in this driver | |
| 8 | + */ | |
| 9 | +#include <linux/bsearch.h> | |
| 10 | +#include <linux/delay.h> | |
| 11 | +#include <linux/kernel.h> | |
| 12 | +#include <linux/module.h> | |
| 13 | +#include <linux/property.h> | |
| 14 | +#include <linux/spi/spi.h> | |
| 15 | + | |
| 16 | +#include <linux/iio/iio.h> | |
| 17 | +#include <linux/iio/sysfs.h> | |
| 18 | + | |
| 19 | +/* AD5758 registers definition */ | |
| 20 | +#define AD5758_NOP 0x00 | |
| 21 | +#define AD5758_DAC_INPUT 0x01 | |
| 22 | +#define AD5758_DAC_OUTPUT 0x02 | |
| 23 | +#define AD5758_CLEAR_CODE 0x03 | |
| 24 | +#define AD5758_USER_GAIN 0x04 | |
| 25 | +#define AD5758_USER_OFFSET 0x05 | |
| 26 | +#define AD5758_DAC_CONFIG 0x06 | |
| 27 | +#define AD5758_SW_LDAC 0x07 | |
| 28 | +#define AD5758_KEY 0x08 | |
| 29 | +#define AD5758_GP_CONFIG1 0x09 | |
| 30 | +#define AD5758_GP_CONFIG2 0x0A | |
| 31 | +#define AD5758_DCDC_CONFIG1 0x0B | |
| 32 | +#define AD5758_DCDC_CONFIG2 0x0C | |
| 33 | +#define AD5758_WDT_CONFIG 0x0F | |
| 34 | +#define AD5758_DIGITAL_DIAG_CONFIG 0x10 | |
| 35 | +#define AD5758_ADC_CONFIG 0x11 | |
| 36 | +#define AD5758_FAULT_PIN_CONFIG 0x12 | |
| 37 | +#define AD5758_TWO_STAGE_READBACK_SELECT 0x13 | |
| 38 | +#define AD5758_DIGITAL_DIAG_RESULTS 0x14 | |
| 39 | +#define AD5758_ANALOG_DIAG_RESULTS 0x15 | |
| 40 | +#define AD5758_STATUS 0x16 | |
| 41 | +#define AD5758_CHIP_ID 0x17 | |
| 42 | +#define AD5758_FREQ_MONITOR 0x18 | |
| 43 | +#define AD5758_DEVICE_ID_0 0x19 | |
| 44 | +#define AD5758_DEVICE_ID_1 0x1A | |
| 45 | +#define AD5758_DEVICE_ID_2 0x1B | |
| 46 | +#define AD5758_DEVICE_ID_3 0x1C | |
| 47 | + | |
| 48 | +/* AD5758_DAC_CONFIG */ | |
| 49 | +#define AD5758_DAC_CONFIG_RANGE_MSK GENMASK(3, 0) | |
| 50 | +#define AD5758_DAC_CONFIG_RANGE_MODE(x) (((x) & 0xF) << 0) | |
| 51 | +#define AD5758_DAC_CONFIG_INT_EN_MSK BIT(5) | |
| 52 | +#define AD5758_DAC_CONFIG_INT_EN_MODE(x) (((x) & 0x1) << 5) | |
| 53 | +#define AD5758_DAC_CONFIG_OUT_EN_MSK BIT(6) | |
| 54 | +#define AD5758_DAC_CONFIG_OUT_EN_MODE(x) (((x) & 0x1) << 6) | |
| 55 | +#define AD5758_DAC_CONFIG_SR_EN_MSK BIT(8) | |
| 56 | +#define AD5758_DAC_CONFIG_SR_EN_MODE(x) (((x) & 0x1) << 8) | |
| 57 | +#define AD5758_DAC_CONFIG_SR_CLOCK_MSK GENMASK(12, 9) | |
| 58 | +#define AD5758_DAC_CONFIG_SR_CLOCK_MODE(x) (((x) & 0xF) << 9) | |
| 59 | +#define AD5758_DAC_CONFIG_SR_STEP_MSK GENMASK(15, 13) | |
| 60 | +#define AD5758_DAC_CONFIG_SR_STEP_MODE(x) (((x) & 0x7) << 13) | |
| 61 | + | |
| 62 | +/* AD5758_KEY */ | |
| 63 | +#define AD5758_KEY_CODE_RESET_1 0x15FA | |
| 64 | +#define AD5758_KEY_CODE_RESET_2 0xAF51 | |
| 65 | +#define AD5758_KEY_CODE_SINGLE_ADC_CONV 0x1ADC | |
| 66 | +#define AD5758_KEY_CODE_RESET_WDT 0x0D06 | |
| 67 | +#define AD5758_KEY_CODE_CALIB_MEM_REFRESH 0xFCBA | |
| 68 | + | |
| 69 | +/* AD5758_DCDC_CONFIG1 */ | |
| 70 | +#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MSK GENMASK(4, 0) | |
| 71 | +#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x) (((x) & 0x1F) << 0) | |
| 72 | +#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK GENMASK(6, 5) | |
| 73 | +#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x) (((x) & 0x3) << 5) | |
| 74 | +#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK BIT(7) | |
| 75 | +#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(x) (((x) & 0x1) << 7) | |
| 76 | + | |
| 77 | +/* AD5758_DCDC_CONFIG2 */ | |
| 78 | +#define AD5758_DCDC_CONFIG2_ILIMIT_MSK GENMASK(3, 1) | |
| 79 | +#define AD5758_DCDC_CONFIG2_ILIMIT_MODE(x) (((x) & 0x7) << 1) | |
| 80 | +#define AD5758_DCDC_CONFIG2_INTR_SAT_3WI_MSK BIT(11) | |
| 81 | +#define AD5758_DCDC_CONFIG2_BUSY_3WI_MSK BIT(12) | |
| 82 | + | |
| 83 | +/* AD5758_DIGITAL_DIAG_RESULTS */ | |
| 84 | +#define AD5758_CAL_MEM_UNREFRESHED_MSK BIT(15) | |
| 85 | + | |
| 86 | +#define AD5758_WR_FLAG_MSK(x) (0x80 | ((x) & 0x1F)) | |
| 87 | + | |
| 88 | +#define AD5758_FULL_SCALE_MICRO 65535000000ULL | |
| 89 | + | |
| 90 | +/** | |
| 91 | + * struct ad5758_state - driver instance specific data | |
| 92 | + * @spi: spi_device | |
| 93 | + * @lock: mutex lock | |
| 94 | + * @out_range: struct which stores the output range | |
| 95 | + * @dc_dc_mode: variable which stores the mode of operation | |
| 96 | + * @dc_dc_ilim: variable which stores the dc-to-dc converter current limit | |
| 97 | + * @slew_time: variable which stores the target slew time | |
| 98 | + * @pwr_down: variable which contains whether a channel is powered down or not | |
| 99 | + * @data: spi transfer buffers | |
| 100 | + */ | |
| 101 | + | |
| 102 | +struct ad5758_range { | |
| 103 | + int reg; | |
| 104 | + int min; | |
| 105 | + int max; | |
| 106 | +}; | |
| 107 | + | |
| 108 | +struct ad5758_state { | |
| 109 | + struct spi_device *spi; | |
| 110 | + struct mutex lock; | |
| 111 | + struct ad5758_range out_range; | |
| 112 | + unsigned int dc_dc_mode; | |
| 113 | + unsigned int dc_dc_ilim; | |
| 114 | + unsigned int slew_time; | |
| 115 | + bool pwr_down; | |
| 116 | + __be32 d32[3]; | |
| 117 | +}; | |
| 118 | + | |
| 119 | +/** | |
| 120 | + * Output ranges corresponding to bits [3:0] from DAC_CONFIG register | |
| 121 | + * 0000: 0 V to 5 V voltage range | |
| 122 | + * 0001: 0 V to 10 V voltage range | |
| 123 | + * 0010: ±5 V voltage range | |
| 124 | + * 0011: ±10 V voltage range | |
| 125 | + * 1000: 0 mA to 20 mA current range | |
| 126 | + * 1001: 0 mA to 24 mA current range | |
| 127 | + * 1010: 4 mA to 20 mA current range | |
| 128 | + * 1011: ±20 mA current range | |
| 129 | + * 1100: ±24 mA current range | |
| 130 | + * 1101: -1 mA to +22 mA current range | |
| 131 | + */ | |
| 132 | +enum ad5758_output_range { | |
| 133 | + AD5758_RANGE_0V_5V, | |
| 134 | + AD5758_RANGE_0V_10V, | |
| 135 | + AD5758_RANGE_PLUSMINUS_5V, | |
| 136 | + AD5758_RANGE_PLUSMINUS_10V, | |
| 137 | + AD5758_RANGE_0mA_20mA = 8, | |
| 138 | + AD5758_RANGE_0mA_24mA, | |
| 139 | + AD5758_RANGE_4mA_24mA, | |
| 140 | + AD5758_RANGE_PLUSMINUS_20mA, | |
| 141 | + AD5758_RANGE_PLUSMINUS_24mA, | |
| 142 | + AD5758_RANGE_MINUS_1mA_PLUS_22mA, | |
| 143 | +}; | |
| 144 | + | |
| 145 | +enum ad5758_dc_dc_mode { | |
| 146 | + AD5758_DCDC_MODE_POWER_OFF, | |
| 147 | + AD5758_DCDC_MODE_DPC_CURRENT, | |
| 148 | + AD5758_DCDC_MODE_DPC_VOLTAGE, | |
| 149 | + AD5758_DCDC_MODE_PPC_CURRENT, | |
| 150 | +}; | |
| 151 | + | |
| 152 | +static const struct ad5758_range ad5758_voltage_range[] = { | |
| 153 | + { AD5758_RANGE_0V_5V, 0, 5000000 }, | |
| 154 | + { AD5758_RANGE_0V_10V, 0, 10000000 }, | |
| 155 | + { AD5758_RANGE_PLUSMINUS_5V, -5000000, 5000000 }, | |
| 156 | + { AD5758_RANGE_PLUSMINUS_10V, -10000000, 10000000 } | |
| 157 | +}; | |
| 158 | + | |
| 159 | +static const struct ad5758_range ad5758_current_range[] = { | |
| 160 | + { AD5758_RANGE_0mA_20mA, 0, 20000}, | |
| 161 | + { AD5758_RANGE_0mA_24mA, 0, 24000 }, | |
| 162 | + { AD5758_RANGE_4mA_24mA, 4, 24000 }, | |
| 163 | + { AD5758_RANGE_PLUSMINUS_20mA, -20000, 20000 }, | |
| 164 | + { AD5758_RANGE_PLUSMINUS_24mA, -24000, 24000 }, | |
| 165 | + { AD5758_RANGE_MINUS_1mA_PLUS_22mA, -1000, 22000 }, | |
| 166 | +}; | |
| 167 | + | |
| 168 | +static const int ad5758_sr_clk[16] = { | |
| 169 | + 240000, 200000, 150000, 128000, 64000, 32000, 16000, 8000, 4000, 2000, | |
| 170 | + 1000, 512, 256, 128, 64, 16 | |
| 171 | +}; | |
| 172 | + | |
| 173 | +static const int ad5758_sr_step[8] = { | |
| 174 | + 4, 12, 64, 120, 256, 500, 1820, 2048 | |
| 175 | +}; | |
| 176 | + | |
| 177 | +static const int ad5758_dc_dc_ilim[6] = { | |
| 178 | + 150000, 200000, 250000, 300000, 350000, 400000 | |
| 179 | +}; | |
| 180 | + | |
| 181 | +static int ad5758_spi_reg_read(struct ad5758_state *st, unsigned int addr) | |
| 182 | +{ | |
| 183 | + struct spi_transfer t[] = { | |
| 184 | + { | |
| 185 | + .tx_buf = &st->d32[0], | |
| 186 | + .len = 4, | |
| 187 | + .cs_change = 1, | |
| 188 | + }, { | |
| 189 | + .tx_buf = &st->d32[1], | |
| 190 | + .rx_buf = &st->d32[2], | |
| 191 | + .len = 4, | |
| 192 | + }, | |
| 193 | + }; | |
| 194 | + int ret; | |
| 195 | + | |
| 196 | + st->d32[0] = cpu_to_be32( | |
| 197 | + (AD5758_WR_FLAG_MSK(AD5758_TWO_STAGE_READBACK_SELECT) << 24) | | |
| 198 | + (addr << 8)); | |
| 199 | + st->d32[1] = cpu_to_be32(AD5758_WR_FLAG_MSK(AD5758_NOP) << 24); | |
| 200 | + | |
| 201 | + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); | |
| 202 | + if (ret < 0) | |
| 203 | + return ret; | |
| 204 | + | |
| 205 | + return (be32_to_cpu(st->d32[2]) >> 8) & 0xFFFF; | |
| 206 | +} | |
| 207 | + | |
| 208 | +static int ad5758_spi_reg_write(struct ad5758_state *st, | |
| 209 | + unsigned int addr, | |
| 210 | + unsigned int val) | |
| 211 | +{ | |
| 212 | + st->d32[0] = cpu_to_be32((AD5758_WR_FLAG_MSK(addr) << 24) | | |
| 213 | + ((val & 0xFFFF) << 8)); | |
| 214 | + | |
| 215 | + return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0])); | |
| 216 | +} | |
| 217 | + | |
| 218 | +static int ad5758_spi_write_mask(struct ad5758_state *st, | |
| 219 | + unsigned int addr, | |
| 220 | + unsigned long int mask, | |
| 221 | + unsigned int val) | |
| 222 | +{ | |
| 223 | + int regval; | |
| 224 | + | |
| 225 | + regval = ad5758_spi_reg_read(st, addr); | |
| 226 | + if (regval < 0) | |
| 227 | + return regval; | |
| 228 | + | |
| 229 | + regval &= ~mask; | |
| 230 | + regval |= val; | |
| 231 | + | |
| 232 | + return ad5758_spi_reg_write(st, addr, regval); | |
| 233 | +} | |
| 234 | + | |
| 235 | +static int cmpfunc(const void *a, const void *b) | |
| 236 | +{ | |
| 237 | + return *(int *)a - *(int *)b; | |
| 238 | +} | |
| 239 | + | |
| 240 | +static int ad5758_find_closest_match(const int *array, | |
| 241 | + unsigned int size, int val) | |
| 242 | +{ | |
| 243 | + int i; | |
| 244 | + | |
| 245 | + for (i = 0; i < size; i++) { | |
| 246 | + if (val <= array[i]) | |
| 247 | + return i; | |
| 248 | + } | |
| 249 | + | |
| 250 | + return size - 1; | |
| 251 | +} | |
| 252 | + | |
| 253 | +static int ad5758_wait_for_task_complete(struct ad5758_state *st, | |
| 254 | + unsigned int reg, | |
| 255 | + unsigned int mask) | |
| 256 | +{ | |
| 257 | + unsigned int timeout; | |
| 258 | + int ret; | |
| 259 | + | |
| 260 | + timeout = 10; | |
| 261 | + do { | |
| 262 | + ret = ad5758_spi_reg_read(st, reg); | |
| 263 | + if (ret < 0) | |
| 264 | + return ret; | |
| 265 | + | |
| 266 | + if (!(ret & mask)) | |
| 267 | + return 0; | |
| 268 | + | |
| 269 | + usleep_range(100, 1000); | |
| 270 | + } while (--timeout); | |
| 271 | + | |
| 272 | + dev_err(&st->spi->dev, | |
| 273 | + "Error reading bit 0x%x in 0x%x register\n", mask, reg); | |
| 274 | + | |
| 275 | + return -EIO; | |
| 276 | +} | |
| 277 | + | |
| 278 | +static int ad5758_calib_mem_refresh(struct ad5758_state *st) | |
| 279 | +{ | |
| 280 | + int ret; | |
| 281 | + | |
| 282 | + ret = ad5758_spi_reg_write(st, AD5758_KEY, | |
| 283 | + AD5758_KEY_CODE_CALIB_MEM_REFRESH); | |
| 284 | + if (ret < 0) { | |
| 285 | + dev_err(&st->spi->dev, | |
| 286 | + "Failed to initiate a calibration memory refresh\n"); | |
| 287 | + return ret; | |
| 288 | + } | |
| 289 | + | |
| 290 | + /* Wait to allow time for the internal calibrations to complete */ | |
| 291 | + return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS, | |
| 292 | + AD5758_CAL_MEM_UNREFRESHED_MSK); | |
| 293 | +} | |
| 294 | + | |
| 295 | +static int ad5758_soft_reset(struct ad5758_state *st) | |
| 296 | +{ | |
| 297 | + int ret; | |
| 298 | + | |
| 299 | + ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_1); | |
| 300 | + if (ret < 0) | |
| 301 | + return ret; | |
| 302 | + | |
| 303 | + ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_2); | |
| 304 | + | |
| 305 | + /* Perform a software reset and wait at least 100us */ | |
| 306 | + usleep_range(100, 1000); | |
| 307 | + | |
| 308 | + return ret; | |
| 309 | +} | |
| 310 | + | |
| 311 | +static int ad5758_set_dc_dc_conv_mode(struct ad5758_state *st, | |
| 312 | + enum ad5758_dc_dc_mode mode) | |
| 313 | +{ | |
| 314 | + int ret; | |
| 315 | + | |
| 316 | + ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1, | |
| 317 | + AD5758_DCDC_CONFIG1_DCDC_MODE_MSK, | |
| 318 | + AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode)); | |
| 319 | + if (ret < 0) | |
| 320 | + return ret; | |
| 321 | + | |
| 322 | + /* | |
| 323 | + * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0. | |
| 324 | + * This allows the 3-wire interface communication to complete. | |
| 325 | + */ | |
| 326 | + ret = ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2, | |
| 327 | + AD5758_DCDC_CONFIG2_BUSY_3WI_MSK); | |
| 328 | + if (ret < 0) | |
| 329 | + return ret; | |
| 330 | + | |
| 331 | + st->dc_dc_mode = mode; | |
| 332 | + | |
| 333 | + return ret; | |
| 334 | +} | |
| 335 | + | |
| 336 | +static int ad5758_set_dc_dc_ilim(struct ad5758_state *st, unsigned int ilim) | |
| 337 | +{ | |
| 338 | + int ret; | |
| 339 | + | |
| 340 | + ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG2, | |
| 341 | + AD5758_DCDC_CONFIG2_ILIMIT_MSK, | |
| 342 | + AD5758_DCDC_CONFIG2_ILIMIT_MODE(ilim)); | |
| 343 | + if (ret < 0) | |
| 344 | + return ret; | |
| 345 | + /* | |
| 346 | + * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0. | |
| 347 | + * This allows the 3-wire interface communication to complete. | |
| 348 | + */ | |
| 349 | + return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2, | |
| 350 | + AD5758_DCDC_CONFIG2_BUSY_3WI_MSK); | |
| 351 | +} | |
| 352 | + | |
| 353 | +static int ad5758_slew_rate_set(struct ad5758_state *st, | |
| 354 | + unsigned int sr_clk_idx, | |
| 355 | + unsigned int sr_step_idx) | |
| 356 | +{ | |
| 357 | + unsigned int mode; | |
| 358 | + unsigned long int mask; | |
| 359 | + int ret; | |
| 360 | + | |
| 361 | + mask = AD5758_DAC_CONFIG_SR_EN_MSK | | |
| 362 | + AD5758_DAC_CONFIG_SR_CLOCK_MSK | | |
| 363 | + AD5758_DAC_CONFIG_SR_STEP_MSK; | |
| 364 | + mode = AD5758_DAC_CONFIG_SR_EN_MODE(1) | | |
| 365 | + AD5758_DAC_CONFIG_SR_STEP_MODE(sr_step_idx) | | |
| 366 | + AD5758_DAC_CONFIG_SR_CLOCK_MODE(sr_clk_idx); | |
| 367 | + | |
| 368 | + ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, mask, mode); | |
| 369 | + if (ret < 0) | |
| 370 | + return ret; | |
| 371 | + | |
| 372 | + /* Wait to allow time for the internal calibrations to complete */ | |
| 373 | + return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS, | |
| 374 | + AD5758_CAL_MEM_UNREFRESHED_MSK); | |
| 375 | +} | |
| 376 | + | |
| 377 | +static int ad5758_slew_rate_config(struct ad5758_state *st) | |
| 378 | +{ | |
| 379 | + unsigned int sr_clk_idx, sr_step_idx; | |
| 380 | + int i, res; | |
| 381 | + s64 diff_new, diff_old; | |
| 382 | + u64 sr_step, calc_slew_time; | |
| 383 | + | |
| 384 | + sr_clk_idx = 0; | |
| 385 | + sr_step_idx = 0; | |
| 386 | + diff_old = S64_MAX; | |
| 387 | + /* | |
| 388 | + * The slew time can be determined by using the formula: | |
| 389 | + * Slew Time = (Full Scale Out / (Step Size x Update Clk Freq)) | |
| 390 | + * where Slew time is expressed in microseconds | |
| 391 | + * Given the desired slew time, the following algorithm determines the | |
| 392 | + * best match for the step size and the update clock frequency. | |
| 393 | + */ | |
| 394 | + for (i = 0; i < ARRAY_SIZE(ad5758_sr_clk); i++) { | |
| 395 | + /* | |
| 396 | + * Go through each valid update clock freq and determine a raw | |
| 397 | + * value for the step size by using the formula: | |
| 398 | + * Step Size = Full Scale Out / (Update Clk Freq * Slew Time) | |
| 399 | + */ | |
| 400 | + sr_step = AD5758_FULL_SCALE_MICRO; | |
| 401 | + do_div(sr_step, ad5758_sr_clk[i]); | |
| 402 | + do_div(sr_step, st->slew_time); | |
| 403 | + /* | |
| 404 | + * After a raw value for step size was determined, find the | |
| 405 | + * closest valid match | |
| 406 | + */ | |
| 407 | + res = ad5758_find_closest_match(ad5758_sr_step, | |
| 408 | + ARRAY_SIZE(ad5758_sr_step), | |
| 409 | + sr_step); | |
| 410 | + /* Calculate the slew time */ | |
| 411 | + calc_slew_time = AD5758_FULL_SCALE_MICRO; | |
| 412 | + do_div(calc_slew_time, ad5758_sr_step[res]); | |
| 413 | + do_div(calc_slew_time, ad5758_sr_clk[i]); | |
| 414 | + /* | |
| 415 | + * Determine with how many microseconds the calculated slew time | |
| 416 | + * is different from the desired slew time and store the diff | |
| 417 | + * for the next iteration | |
| 418 | + */ | |
| 419 | + diff_new = abs(st->slew_time - calc_slew_time); | |
| 420 | + if (diff_new < diff_old) { | |
| 421 | + diff_old = diff_new; | |
| 422 | + sr_clk_idx = i; | |
| 423 | + sr_step_idx = res; | |
| 424 | + } | |
| 425 | + } | |
| 426 | + | |
| 427 | + return ad5758_slew_rate_set(st, sr_clk_idx, sr_step_idx); | |
| 428 | +} | |
| 429 | + | |
| 430 | +static int ad5758_set_out_range(struct ad5758_state *st, int range) | |
| 431 | +{ | |
| 432 | + int ret; | |
| 433 | + | |
| 434 | + ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, | |
| 435 | + AD5758_DAC_CONFIG_RANGE_MSK, | |
| 436 | + AD5758_DAC_CONFIG_RANGE_MODE(range)); | |
| 437 | + if (ret < 0) | |
| 438 | + return ret; | |
| 439 | + | |
| 440 | + /* Wait to allow time for the internal calibrations to complete */ | |
| 441 | + return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS, | |
| 442 | + AD5758_CAL_MEM_UNREFRESHED_MSK); | |
| 443 | +} | |
| 444 | + | |
| 445 | +static int ad5758_fault_prot_switch_en(struct ad5758_state *st, bool enable) | |
| 446 | +{ | |
| 447 | + int ret; | |
| 448 | + | |
| 449 | + ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1, | |
| 450 | + AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK, | |
| 451 | + AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(enable)); | |
| 452 | + if (ret < 0) | |
| 453 | + return ret; | |
| 454 | + /* | |
| 455 | + * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0. | |
| 456 | + * This allows the 3-wire interface communication to complete. | |
| 457 | + */ | |
| 458 | + return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2, | |
| 459 | + AD5758_DCDC_CONFIG2_BUSY_3WI_MSK); | |
| 460 | +} | |
| 461 | + | |
| 462 | +static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable) | |
| 463 | +{ | |
| 464 | + int ret; | |
| 465 | + | |
| 466 | + ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, | |
| 467 | + AD5758_DAC_CONFIG_INT_EN_MSK, | |
| 468 | + AD5758_DAC_CONFIG_INT_EN_MODE(enable)); | |
| 469 | + if (ret < 0) | |
| 470 | + return ret; | |
| 471 | + | |
| 472 | + /* Wait to allow time for the internal calibrations to complete */ | |
| 473 | + return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS, | |
| 474 | + AD5758_CAL_MEM_UNREFRESHED_MSK); | |
| 475 | +} | |
| 476 | + | |
| 477 | +static int ad5758_reg_access(struct iio_dev *indio_dev, | |
| 478 | + unsigned int reg, | |
| 479 | + unsigned int writeval, | |
| 480 | + unsigned int *readval) | |
| 481 | +{ | |
| 482 | + struct ad5758_state *st = iio_priv(indio_dev); | |
| 483 | + int ret; | |
| 484 | + | |
| 485 | + mutex_lock(&st->lock); | |
| 486 | + if (readval) { | |
| 487 | + ret = ad5758_spi_reg_read(st, reg); | |
| 488 | + if (ret < 0) { | |
| 489 | + mutex_unlock(&st->lock); | |
| 490 | + return ret; | |
| 491 | + } | |
| 492 | + | |
| 493 | + *readval = ret; | |
| 494 | + ret = 0; | |
| 495 | + } else { | |
| 496 | + ret = ad5758_spi_reg_write(st, reg, writeval); | |
| 497 | + } | |
| 498 | + mutex_unlock(&st->lock); | |
| 499 | + | |
| 500 | + return ret; | |
| 501 | +} | |
| 502 | + | |
| 503 | +static int ad5758_read_raw(struct iio_dev *indio_dev, | |
| 504 | + struct iio_chan_spec const *chan, | |
| 505 | + int *val, int *val2, long info) | |
| 506 | +{ | |
| 507 | + struct ad5758_state *st = iio_priv(indio_dev); | |
| 508 | + int max, min, ret; | |
| 509 | + | |
| 510 | + switch (info) { | |
| 511 | + case IIO_CHAN_INFO_RAW: | |
| 512 | + mutex_lock(&st->lock); | |
| 513 | + ret = ad5758_spi_reg_read(st, AD5758_DAC_INPUT); | |
| 514 | + mutex_unlock(&st->lock); | |
| 515 | + if (ret < 0) | |
| 516 | + return ret; | |
| 517 | + | |
| 518 | + *val = ret; | |
| 519 | + return IIO_VAL_INT; | |
| 520 | + case IIO_CHAN_INFO_SCALE: | |
| 521 | + min = st->out_range.min; | |
| 522 | + max = st->out_range.max; | |
| 523 | + *val = (max - min) / 1000; | |
| 524 | + *val2 = 16; | |
| 525 | + return IIO_VAL_FRACTIONAL_LOG2; | |
| 526 | + case IIO_CHAN_INFO_OFFSET: | |
| 527 | + min = st->out_range.min; | |
| 528 | + max = st->out_range.max; | |
| 529 | + *val = ((min * (1 << 16)) / (max - min)) / 1000; | |
| 530 | + return IIO_VAL_INT; | |
| 531 | + default: | |
| 532 | + return -EINVAL; | |
| 533 | + } | |
| 534 | +} | |
| 535 | + | |
| 536 | +static int ad5758_write_raw(struct iio_dev *indio_dev, | |
| 537 | + struct iio_chan_spec const *chan, | |
| 538 | + int val, int val2, long info) | |
| 539 | +{ | |
| 540 | + struct ad5758_state *st = iio_priv(indio_dev); | |
| 541 | + int ret; | |
| 542 | + | |
| 543 | + switch (info) { | |
| 544 | + case IIO_CHAN_INFO_RAW: | |
| 545 | + mutex_lock(&st->lock); | |
| 546 | + ret = ad5758_spi_reg_write(st, AD5758_DAC_INPUT, val); | |
| 547 | + mutex_unlock(&st->lock); | |
| 548 | + return ret; | |
| 549 | + default: | |
| 550 | + return -EINVAL; | |
| 551 | + } | |
| 552 | +} | |
| 553 | + | |
| 554 | +static ssize_t ad5758_read_powerdown(struct iio_dev *indio_dev, | |
| 555 | + uintptr_t priv, | |
| 556 | + const struct iio_chan_spec *chan, | |
| 557 | + char *buf) | |
| 558 | +{ | |
| 559 | + struct ad5758_state *st = iio_priv(indio_dev); | |
| 560 | + | |
| 561 | + return sprintf(buf, "%d\n", st->pwr_down); | |
| 562 | +} | |
| 563 | + | |
| 564 | +static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev, | |
| 565 | + uintptr_t priv, | |
| 566 | + struct iio_chan_spec const *chan, | |
| 567 | + const char *buf, size_t len) | |
| 568 | +{ | |
| 569 | + struct ad5758_state *st = iio_priv(indio_dev); | |
| 570 | + bool pwr_down; | |
| 571 | + unsigned int dcdc_config1_mode, dc_dc_mode, dac_config_mode, val; | |
| 572 | + unsigned long int dcdc_config1_msk, dac_config_msk; | |
| 573 | + int ret; | |
| 574 | + | |
| 575 | + ret = kstrtobool(buf, &pwr_down); | |
| 576 | + if (ret) | |
| 577 | + return ret; | |
| 578 | + | |
| 579 | + mutex_lock(&st->lock); | |
| 580 | + if (pwr_down) { | |
| 581 | + dc_dc_mode = AD5758_DCDC_MODE_POWER_OFF; | |
| 582 | + val = 0; | |
| 583 | + } else { | |
| 584 | + dc_dc_mode = st->dc_dc_mode; | |
| 585 | + val = 1; | |
| 586 | + } | |
| 587 | + | |
| 588 | + dcdc_config1_mode = AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(dc_dc_mode) | | |
| 589 | + AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(val); | |
| 590 | + dcdc_config1_msk = AD5758_DCDC_CONFIG1_DCDC_MODE_MSK | | |
| 591 | + AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK; | |
| 592 | + | |
| 593 | + ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1, | |
| 594 | + dcdc_config1_msk, | |
| 595 | + dcdc_config1_mode); | |
| 596 | + if (ret < 0) | |
| 597 | + goto err_unlock; | |
| 598 | + | |
| 599 | + dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) | | |
| 600 | + AD5758_DAC_CONFIG_INT_EN_MODE(val); | |
| 601 | + dac_config_msk = AD5758_DAC_CONFIG_OUT_EN_MSK | | |
| 602 | + AD5758_DAC_CONFIG_INT_EN_MSK; | |
| 603 | + | |
| 604 | + ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, | |
| 605 | + dac_config_msk, | |
| 606 | + dac_config_mode); | |
| 607 | + if (ret < 0) | |
| 608 | + goto err_unlock; | |
| 609 | + | |
| 610 | + st->pwr_down = pwr_down; | |
| 611 | + | |
| 612 | +err_unlock: | |
| 613 | + mutex_unlock(&st->lock); | |
| 614 | + | |
| 615 | + return ret ? ret : len; | |
| 616 | +} | |
| 617 | + | |
| 618 | +static const struct iio_info ad5758_info = { | |
| 619 | + .read_raw = ad5758_read_raw, | |
| 620 | + .write_raw = ad5758_write_raw, | |
| 621 | + .debugfs_reg_access = &ad5758_reg_access, | |
| 622 | +}; | |
| 623 | + | |
| 624 | +static const struct iio_chan_spec_ext_info ad5758_ext_info[] = { | |
| 625 | + { | |
| 626 | + .name = "powerdown", | |
| 627 | + .read = ad5758_read_powerdown, | |
| 628 | + .write = ad5758_write_powerdown, | |
| 629 | + .shared = IIO_SHARED_BY_TYPE, | |
| 630 | + }, | |
| 631 | + { } | |
| 632 | +}; | |
| 633 | + | |
| 634 | +#define AD5758_DAC_CHAN(_chan_type) { \ | |
| 635 | + .type = (_chan_type), \ | |
| 636 | + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \ | |
| 637 | + BIT(IIO_CHAN_INFO_SCALE) | \ | |
| 638 | + BIT(IIO_CHAN_INFO_OFFSET), \ | |
| 639 | + .indexed = 1, \ | |
| 640 | + .output = 1, \ | |
| 641 | + .ext_info = ad5758_ext_info, \ | |
| 642 | +} | |
| 643 | + | |
| 644 | +static const struct iio_chan_spec ad5758_voltage_ch[] = { | |
| 645 | + AD5758_DAC_CHAN(IIO_VOLTAGE) | |
| 646 | +}; | |
| 647 | + | |
| 648 | +static const struct iio_chan_spec ad5758_current_ch[] = { | |
| 649 | + AD5758_DAC_CHAN(IIO_CURRENT) | |
| 650 | +}; | |
| 651 | + | |
| 652 | +static bool ad5758_is_valid_mode(enum ad5758_dc_dc_mode mode) | |
| 653 | +{ | |
| 654 | + switch (mode) { | |
| 655 | + case AD5758_DCDC_MODE_DPC_CURRENT: | |
| 656 | + case AD5758_DCDC_MODE_DPC_VOLTAGE: | |
| 657 | + case AD5758_DCDC_MODE_PPC_CURRENT: | |
| 658 | + return true; | |
| 659 | + default: | |
| 660 | + return false; | |
| 661 | + } | |
| 662 | +} | |
| 663 | + | |
| 664 | +static int ad5758_crc_disable(struct ad5758_state *st) | |
| 665 | +{ | |
| 666 | + unsigned int mask; | |
| 667 | + | |
| 668 | + mask = (AD5758_WR_FLAG_MSK(AD5758_DIGITAL_DIAG_CONFIG) << 24) | 0x5C3A; | |
| 669 | + st->d32[0] = cpu_to_be32(mask); | |
| 670 | + | |
| 671 | + return spi_write(st->spi, &st->d32[0], 4); | |
| 672 | +} | |
| 673 | + | |
| 674 | +static int ad5758_find_out_range(struct ad5758_state *st, | |
| 675 | + const struct ad5758_range *range, | |
| 676 | + unsigned int size, | |
| 677 | + int min, int max) | |
| 678 | +{ | |
| 679 | + int i; | |
| 680 | + | |
| 681 | + for (i = 0; i < size; i++) { | |
| 682 | + if ((min == range[i].min) && (max == range[i].max)) { | |
| 683 | + st->out_range.reg = range[i].reg; | |
| 684 | + st->out_range.min = range[i].min; | |
| 685 | + st->out_range.max = range[i].max; | |
| 686 | + | |
| 687 | + return 0; | |
| 688 | + } | |
| 689 | + } | |
| 690 | + | |
| 691 | + return -EINVAL; | |
| 692 | +} | |
| 693 | + | |
| 694 | +static int ad5758_parse_dt(struct ad5758_state *st) | |
| 695 | +{ | |
| 696 | + unsigned int tmp, tmparray[2], size; | |
| 697 | + const struct ad5758_range *range; | |
| 698 | + int *index, ret; | |
| 699 | + | |
| 700 | + st->dc_dc_ilim = 0; | |
| 701 | + ret = device_property_read_u32(&st->spi->dev, | |
| 702 | + "adi,dc-dc-ilim-microamp", &tmp); | |
| 703 | + if (ret) { | |
| 704 | + dev_dbg(&st->spi->dev, | |
| 705 | + "Missing \"dc-dc-ilim-microamp\" property\n"); | |
| 706 | + } else { | |
| 707 | + index = bsearch(&tmp, ad5758_dc_dc_ilim, | |
| 708 | + ARRAY_SIZE(ad5758_dc_dc_ilim), | |
| 709 | + sizeof(int), cmpfunc); | |
| 710 | + if (!index) | |
| 711 | + dev_dbg(&st->spi->dev, "dc-dc-ilim out of range\n"); | |
| 712 | + else | |
| 713 | + st->dc_dc_ilim = index - ad5758_dc_dc_ilim; | |
| 714 | + } | |
| 715 | + | |
| 716 | + ret = device_property_read_u32(&st->spi->dev, "adi,dc-dc-mode", | |
| 717 | + &st->dc_dc_mode); | |
| 718 | + if (ret) { | |
| 719 | + dev_err(&st->spi->dev, "Missing \"dc-dc-mode\" property\n"); | |
| 720 | + return ret; | |
| 721 | + } | |
| 722 | + | |
| 723 | + if (!ad5758_is_valid_mode(st->dc_dc_mode)) | |
| 724 | + return -EINVAL; | |
| 725 | + | |
| 726 | + if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE) { | |
| 727 | + ret = device_property_read_u32_array(&st->spi->dev, | |
| 728 | + "adi,range-microvolt", | |
| 729 | + tmparray, 2); | |
| 730 | + if (ret) { | |
| 731 | + dev_err(&st->spi->dev, | |
| 732 | + "Missing \"range-microvolt\" property\n"); | |
| 733 | + return ret; | |
| 734 | + } | |
| 735 | + range = ad5758_voltage_range; | |
| 736 | + size = ARRAY_SIZE(ad5758_voltage_range); | |
| 737 | + } else { | |
| 738 | + ret = device_property_read_u32_array(&st->spi->dev, | |
| 739 | + "adi,range-microamp", | |
| 740 | + tmparray, 2); | |
| 741 | + if (ret) { | |
| 742 | + dev_err(&st->spi->dev, | |
| 743 | + "Missing \"range-microamp\" property\n"); | |
| 744 | + return ret; | |
| 745 | + } | |
| 746 | + range = ad5758_current_range; | |
| 747 | + size = ARRAY_SIZE(ad5758_current_range); | |
| 748 | + } | |
| 749 | + | |
| 750 | + ret = ad5758_find_out_range(st, range, size, tmparray[0], tmparray[1]); | |
| 751 | + if (ret) { | |
| 752 | + dev_err(&st->spi->dev, "range invalid\n"); | |
| 753 | + return ret; | |
| 754 | + } | |
| 755 | + | |
| 756 | + ret = device_property_read_u32(&st->spi->dev, "adi,slew-time-us", &tmp); | |
| 757 | + if (ret) { | |
| 758 | + dev_dbg(&st->spi->dev, "Missing \"slew-time-us\" property\n"); | |
| 759 | + st->slew_time = 0; | |
| 760 | + } else { | |
| 761 | + st->slew_time = tmp; | |
| 762 | + } | |
| 763 | + | |
| 764 | + return 0; | |
| 765 | +} | |
| 766 | + | |
| 767 | +static int ad5758_init(struct ad5758_state *st) | |
| 768 | +{ | |
| 769 | + int regval, ret; | |
| 770 | + | |
| 771 | + /* Disable CRC checks */ | |
| 772 | + ret = ad5758_crc_disable(st); | |
| 773 | + if (ret < 0) | |
| 774 | + return ret; | |
| 775 | + | |
| 776 | + /* Perform a software reset */ | |
| 777 | + ret = ad5758_soft_reset(st); | |
| 778 | + if (ret < 0) | |
| 779 | + return ret; | |
| 780 | + | |
| 781 | + /* Disable CRC checks */ | |
| 782 | + ret = ad5758_crc_disable(st); | |
| 783 | + if (ret < 0) | |
| 784 | + return ret; | |
| 785 | + | |
| 786 | + /* Perform a calibration memory refresh */ | |
| 787 | + ret = ad5758_calib_mem_refresh(st); | |
| 788 | + if (ret < 0) | |
| 789 | + return ret; | |
| 790 | + | |
| 791 | + regval = ad5758_spi_reg_read(st, AD5758_DIGITAL_DIAG_RESULTS); | |
| 792 | + if (regval < 0) | |
| 793 | + return regval; | |
| 794 | + | |
| 795 | + /* Clear all the error flags */ | |
| 796 | + ret = ad5758_spi_reg_write(st, AD5758_DIGITAL_DIAG_RESULTS, regval); | |
| 797 | + if (ret < 0) | |
| 798 | + return ret; | |
| 799 | + | |
| 800 | + /* Set the dc-to-dc current limit */ | |
| 801 | + ret = ad5758_set_dc_dc_ilim(st, st->dc_dc_ilim); | |
| 802 | + if (ret < 0) | |
| 803 | + return ret; | |
| 804 | + | |
| 805 | + /* Configure the dc-to-dc controller mode */ | |
| 806 | + ret = ad5758_set_dc_dc_conv_mode(st, st->dc_dc_mode); | |
| 807 | + if (ret < 0) | |
| 808 | + return ret; | |
| 809 | + | |
| 810 | + /* Configure the output range */ | |
| 811 | + ret = ad5758_set_out_range(st, st->out_range.reg); | |
| 812 | + if (ret < 0) | |
| 813 | + return ret; | |
| 814 | + | |
| 815 | + /* Enable Slew Rate Control, set the slew rate clock and step */ | |
| 816 | + if (st->slew_time) { | |
| 817 | + ret = ad5758_slew_rate_config(st); | |
| 818 | + if (ret < 0) | |
| 819 | + return ret; | |
| 820 | + } | |
| 821 | + | |
| 822 | + /* Enable the VIOUT fault protection switch (FPS is closed) */ | |
| 823 | + ret = ad5758_fault_prot_switch_en(st, 1); | |
| 824 | + if (ret < 0) | |
| 825 | + return ret; | |
| 826 | + | |
| 827 | + /* Power up the DAC and internal (INT) amplifiers */ | |
| 828 | + ret = ad5758_internal_buffers_en(st, 1); | |
| 829 | + if (ret < 0) | |
| 830 | + return ret; | |
| 831 | + | |
| 832 | + /* Enable VIOUT */ | |
| 833 | + return ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, | |
| 834 | + AD5758_DAC_CONFIG_OUT_EN_MSK, | |
| 835 | + AD5758_DAC_CONFIG_OUT_EN_MODE(1)); | |
| 836 | +} | |
| 837 | + | |
| 838 | +static int ad5758_probe(struct spi_device *spi) | |
| 839 | +{ | |
| 840 | + struct ad5758_state *st; | |
| 841 | + struct iio_dev *indio_dev; | |
| 842 | + int ret; | |
| 843 | + | |
| 844 | + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | |
| 845 | + if (!indio_dev) | |
| 846 | + return -ENOMEM; | |
| 847 | + | |
| 848 | + st = iio_priv(indio_dev); | |
| 849 | + spi_set_drvdata(spi, indio_dev); | |
| 850 | + | |
| 851 | + st->spi = spi; | |
| 852 | + | |
| 853 | + mutex_init(&st->lock); | |
| 854 | + | |
| 855 | + indio_dev->dev.parent = &spi->dev; | |
| 856 | + indio_dev->name = spi_get_device_id(spi)->name; | |
| 857 | + indio_dev->info = &ad5758_info; | |
| 858 | + indio_dev->modes = INDIO_DIRECT_MODE; | |
| 859 | + indio_dev->num_channels = 1; | |
| 860 | + | |
| 861 | + ret = ad5758_parse_dt(st); | |
| 862 | + if (ret < 0) | |
| 863 | + return ret; | |
| 864 | + | |
| 865 | + if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE) | |
| 866 | + indio_dev->channels = ad5758_voltage_ch; | |
| 867 | + else | |
| 868 | + indio_dev->channels = ad5758_current_ch; | |
| 869 | + | |
| 870 | + ret = ad5758_init(st); | |
| 871 | + if (ret < 0) { | |
| 872 | + dev_err(&spi->dev, "AD5758 init failed\n"); | |
| 873 | + return ret; | |
| 874 | + } | |
| 875 | + | |
| 876 | + return devm_iio_device_register(&st->spi->dev, indio_dev); | |
| 877 | +} | |
| 878 | + | |
| 879 | +static const struct spi_device_id ad5758_id[] = { | |
| 880 | + { "ad5758", 0 }, | |
| 881 | + {} | |
| 882 | +}; | |
| 883 | +MODULE_DEVICE_TABLE(spi, ad5758_id); | |
| 884 | + | |
| 885 | +static struct spi_driver ad5758_driver = { | |
| 886 | + .driver = { | |
| 887 | + .name = KBUILD_MODNAME, | |
| 888 | + }, | |
| 889 | + .probe = ad5758_probe, | |
| 890 | + .id_table = ad5758_id, | |
| 891 | +}; | |
| 892 | + | |
| 893 | +module_spi_driver(ad5758_driver); | |
| 894 | + | |
| 895 | +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); | |
| 896 | +MODULE_DESCRIPTION("Analog Devices AD5758 DAC"); | |
| 897 | +MODULE_LICENSE("GPL v2"); | |
| @@ -87,12 +87,7 @@ static int ltc2632_read_raw(struct iio_dev *indio_dev, · 86 unmodified lines | ||
| 87 | 87 | int *val2, |
| 88 | 88 | long m) |
| 89 | 89 | { |
| 90 | − struct ltc2632_chip_info *chip_info; | |
| 91 | − | |
| 92 | 90 | const struct ltc2632_state *st = iio_priv(indio_dev); |
| 93 | − const struct spi_device_id *spi_dev_id = spi_get_device_id(st->spi_dev); | |
| 94 | − | |
| 95 | − chip_info = (struct ltc2632_chip_info *)spi_dev_id->driver_data; | |
| 96 | 91 | |
| 97 | 92 | switch (m) { |
| 98 | 93 | case IIO_CHAN_INFO_SCALE: |
| @@ -97,9 +97,6 @@ static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val) · 96 unmodified lines | ||
| 97 | 97 | |
| 98 | 98 | static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn) |
| 99 | 99 | { |
| 100 | − unsigned int shift; | |
| 101 | − | |
| 102 | − shift = 12 - data->spec->resolution; | |
| 103 | 100 | data->buf[1] = 0; |
| 104 | 101 | data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS; |
| 105 | 102 | |
| @@ -111,9 +108,6 @@ static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrd · 5 unmodified lines | ||
| 111 | 108 | |
| 112 | 109 | static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn) |
| 113 | 110 | { |
| 114 | − unsigned int shift; | |
| 115 | − | |
| 116 | − shift = 16 - data->spec->resolution; | |
| 117 | 111 | data->buf[2] = 0; |
| 118 | 112 | data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS; |
| 119 | 113 | data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | |
| @@ -274,6 +274,15 @@ struct ad9523_state { · 273 unmodified lines | ||
| 274 | 274 | unsigned long vco_out_freq[AD9523_NUM_CLK_SRC]; |
| 275 | 275 | unsigned char vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC]; |
| 276 | 276 | |
| 277 | + /* | |
| 278 | + * Lock for accessing device registers. Some operations require | |
| 279 | + * multiple consecutive R/W operations, during which the device | |
| 280 | + * shouldn't be interrupted. The buffers are also shared across | |
| 281 | + * all operations so need to be protected on stand alone reads and | |
| 282 | + * writes. | |
| 283 | + */ | |
| 284 | + struct mutex lock; | |
| 285 | + | |
| 277 | 286 | /* |
| 278 | 287 | * DMA (thus cache coherency maintenance) requires the |
| 279 | 288 | * transfer buffers to live in their own cache lines. |
| @@ -500,6 +509,7 @@ static ssize_t ad9523_store(struct device *dev, · 220 unmodified lines | ||
| 500 | 509 | { |
| 501 | 510 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 502 | 511 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| 512 | + struct ad9523_state *st = iio_priv(indio_dev); | |
| 503 | 513 | bool state; |
| 504 | 514 | int ret; |
| 505 | 515 | |
| @@ -510,7 +520,7 @@ static ssize_t ad9523_store(struct device *dev, · 4 unmodified lines | ||
| 510 | 520 | if (!state) |
| 511 | 521 | return 0; |
| 512 | 522 | |
| 513 | − mutex_lock(&indio_dev->mlock); | |
| 523 | + mutex_lock(&st->lock); | |
| 514 | 524 | switch ((u32)this_attr->address) { |
| 515 | 525 | case AD9523_SYNC: |
| 516 | 526 | ret = ad9523_sync(indio_dev); |
| @@ -521,7 +531,7 @@ static ssize_t ad9523_store(struct device *dev, · 4 unmodified lines | ||
| 521 | 531 | default: |
| 522 | 532 | ret = -ENODEV; |
| 523 | 533 | } |
| 524 | − mutex_unlock(&indio_dev->mlock); | |
| 534 | + mutex_unlock(&st->lock); | |
| 525 | 535 | |
| 526 | 536 | return ret ? ret : len; |
| 527 | 537 | } |
| @@ -532,15 +542,16 @@ static ssize_t ad9523_show(struct device *dev, · 4 unmodified lines | ||
| 532 | 542 | { |
| 533 | 543 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
| 534 | 544 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); |
| 545 | + struct ad9523_state *st = iio_priv(indio_dev); | |
| 535 | 546 | int ret; |
| 536 | 547 | |
| 537 | − mutex_lock(&indio_dev->mlock); | |
| 548 | + mutex_lock(&st->lock); | |
| 538 | 549 | ret = ad9523_read(indio_dev, AD9523_READBACK_0); |
| 539 | 550 | if (ret >= 0) { |
| 540 | 551 | ret = sprintf(buf, "%d\n", !!(ret & (1 << |
| 541 | 552 | (u32)this_attr->address))); |
| 542 | 553 | } |
| 543 | − mutex_unlock(&indio_dev->mlock); | |
| 554 | + mutex_unlock(&st->lock); | |
| 544 | 555 | |
| 545 | 556 | return ret; |
| 546 | 557 | } |
| @@ -623,9 +634,9 @@ static int ad9523_read_raw(struct iio_dev *indio_dev, · 76 unmodified lines | ||
| 623 | 634 | unsigned int code; |
| 624 | 635 | int ret; |
| 625 | 636 | |
| 626 | − mutex_lock(&indio_dev->mlock); | |
| 637 | + mutex_lock(&st->lock); | |
| 627 | 638 | ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel)); |
| 628 | − mutex_unlock(&indio_dev->mlock); | |
| 639 | + mutex_unlock(&st->lock); | |
| 629 | 640 | |
| 630 | 641 | if (ret < 0) |
| 631 | 642 | return ret; |
| @@ -642,7 +653,7 @@ static int ad9523_read_raw(struct iio_dev *indio_dev, · 10 unmodified lines | ||
| 642 | 653 | code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) / |
| 643 | 654 | AD9523_CLK_DIST_DIV_REV(ret); |
| 644 | 655 | *val = code / 1000000; |
| 645 | − *val2 = (code % 1000000) * 10; | |
| 656 | + *val2 = code % 1000000; | |
| 646 | 657 | return IIO_VAL_INT_PLUS_MICRO; |
| 647 | 658 | default: |
| 648 | 659 | return -EINVAL; |
| @@ -659,7 +670,7 @@ static int ad9523_write_raw(struct iio_dev *indio_dev, · 10 unmodified lines | ||
| 659 | 670 | unsigned int reg; |
| 660 | 671 | int ret, tmp, code; |
| 661 | 672 | |
| 662 | − mutex_lock(&indio_dev->mlock); | |
| 673 | + mutex_lock(&st->lock); | |
| 663 | 674 | ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel)); |
| 664 | 675 | if (ret < 0) |
| 665 | 676 | goto out; |
| @@ -705,7 +716,7 @@ static int ad9523_write_raw(struct iio_dev *indio_dev, · 39 unmodified lines | ||
| 705 | 716 | |
| 706 | 717 | ad9523_io_update(indio_dev); |
| 707 | 718 | out: |
| 708 | − mutex_unlock(&indio_dev->mlock); | |
| 719 | + mutex_unlock(&st->lock); | |
| 709 | 720 | return ret; |
| 710 | 721 | } |
| 711 | 722 | |
| @@ -713,9 +724,10 @@ static int ad9523_reg_access(struct iio_dev *indio_dev, · 1 unmodified line | ||
| 713 | 724 | unsigned int reg, unsigned int writeval, |
| 714 | 725 | unsigned int *readval) |
| 715 | 726 | { |
| 727 | + struct ad9523_state *st = iio_priv(indio_dev); | |
| 716 | 728 | int ret; |
| 717 | 729 | |
| 718 | − mutex_lock(&indio_dev->mlock); | |
| 730 | + mutex_lock(&st->lock); | |
| 719 | 731 | if (readval == NULL) { |
| 720 | 732 | ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval); |
| 721 | 733 | ad9523_io_update(indio_dev); |
| @@ -728,7 +740,7 @@ static int ad9523_reg_access(struct iio_dev *indio_dev, · 6 unmodified lines | ||
| 728 | 740 | } |
| 729 | 741 | |
| 730 | 742 | out_unlock: |
| 731 | − mutex_unlock(&indio_dev->mlock); | |
| 743 | + mutex_unlock(&st->lock); | |
| 732 | 744 | |
| 733 | 745 | return ret; |
| 734 | 746 | } |
| @@ -967,6 +979,8 @@ static int ad9523_probe(struct spi_device *spi) · 232 unmodified lines | ||
| 967 | 979 | |
| 968 | 980 | st = iio_priv(indio_dev); |
| 969 | 981 | |
| 982 | + mutex_init(&st->lock); | |
| 983 | + | |
| 970 | 984 | st->reg = devm_regulator_get(&spi->dev, "vcc"); |
| 971 | 985 | if (!IS_ERR(st->reg)) { |
| 972 | 986 | ret = regulator_enable(st->reg); |
| @@ -81,9 +81,11 @@ int adis_write_reg(struct adis *adis, unsigned int reg, · 80 unmodified lines | ||
| 81 | 81 | adis->tx[9] = (value >> 24) & 0xff; |
| 82 | 82 | adis->tx[6] = ADIS_WRITE_REG(reg + 2); |
| 83 | 83 | adis->tx[7] = (value >> 16) & 0xff; |
| 84 | + /* fall through */ | |
| 84 | 85 | case 2: |
| 85 | 86 | adis->tx[4] = ADIS_WRITE_REG(reg + 1); |
| 86 | 87 | adis->tx[5] = (value >> 8) & 0xff; |
| 88 | + /* fall through */ | |
| 87 | 89 | case 1: |
| 88 | 90 | adis->tx[2] = ADIS_WRITE_REG(reg); |
| 89 | 91 | adis->tx[3] = value & 0xff; |
| @@ -167,6 +169,7 @@ int adis_read_reg(struct adis *adis, unsigned int reg, · 77 unmodified lines | ||
| 167 | 169 | adis->tx[2] = ADIS_READ_REG(reg + 2); |
| 168 | 170 | adis->tx[3] = 0; |
| 169 | 171 | spi_message_add_tail(&xfers[1], &msg); |
| 172 | + /* fall through */ | |
| 170 | 173 | case 2: |
| 171 | 174 | adis->tx[4] = ADIS_READ_REG(reg); |
| 172 | 175 | adis->tx[5] = 0; |
| @@ -103,6 +103,12 @@ static const struct inv_mpu6050_hw hw_info[] = { · 102 unmodified lines | ||
| 103 | 103 | .reg = ®_set_6500, |
| 104 | 104 | .config = &chip_config_6050, |
| 105 | 105 | }, |
| 106 | + { | |
| 107 | + .whoami = INV_MPU6515_WHOAMI_VALUE, | |
| 108 | + .name = "MPU6515", | |
| 109 | + .reg = ®_set_6500, | |
| 110 | + .config = &chip_config_6050, | |
| 111 | + }, | |
| 106 | 112 | { |
| 107 | 113 | .whoami = INV_MPU6000_WHOAMI_VALUE, |
| 108 | 114 | .name = "MPU6000", |
| @@ -174,6 +174,7 @@ static int inv_mpu_remove(struct i2c_client *client) · 173 unmodified lines | ||
| 174 | 174 | static const struct i2c_device_id inv_mpu_id[] = { |
| 175 | 175 | {"mpu6050", INV_MPU6050}, |
| 176 | 176 | {"mpu6500", INV_MPU6500}, |
| 177 | + {"mpu6515", INV_MPU6515}, | |
| 177 | 178 | {"mpu9150", INV_MPU9150}, |
| 178 | 179 | {"mpu9250", INV_MPU9250}, |
| 179 | 180 | {"mpu9255", INV_MPU9255}, |
| @@ -192,6 +193,10 @@ static const struct of_device_id inv_of_match[] = { · 12 unmodified lines | ||
| 192 | 193 | .compatible = "invensense,mpu6500", |
| 193 | 194 | .data = (void *)INV_MPU6500 |
| 194 | 195 | }, |
| 196 | + { | |
| 197 | + .compatible = "invensense,mpu6515", | |
| 198 | + .data = (void *)INV_MPU6515 | |
| 199 | + }, | |
| 195 | 200 | { |
| 196 | 201 | .compatible = "invensense,mpu9150", |
| 197 | 202 | .data = (void *)INV_MPU9150 |
| @@ -71,6 +71,7 @@ struct inv_mpu6050_reg_map { · 70 unmodified lines | ||
| 71 | 71 | enum inv_devices { |
| 72 | 72 | INV_MPU6050, |
| 73 | 73 | INV_MPU6500, |
| 74 | + INV_MPU6515, | |
| 74 | 75 | INV_MPU6000, |
| 75 | 76 | INV_MPU9150, |
| 76 | 77 | INV_MPU9250, |
| @@ -256,6 +257,7 @@ struct inv_mpu6050_state { · 179 unmodified lines | ||
| 256 | 257 | #define INV_MPU9150_WHOAMI_VALUE 0x68 |
| 257 | 258 | #define INV_MPU9250_WHOAMI_VALUE 0x71 |
| 258 | 259 | #define INV_MPU9255_WHOAMI_VALUE 0x73 |
| 260 | +#define INV_MPU6515_WHOAMI_VALUE 0x74 | |
| 259 | 261 | #define INV_ICM20608_WHOAMI_VALUE 0xAF |
| 260 | 262 | |
| 261 | 263 | /* scan element definition */ |
| @@ -298,8 +298,11 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) · 297 unmodified lines | ||
| 298 | 298 | err = regmap_bulk_read(hw->regmap, |
| 299 | 299 | hw->settings->fifo_ops.fifo_diff.addr, |
| 300 | 300 | &fifo_status, sizeof(fifo_status)); |
| 301 | − if (err < 0) | |
| 301 | + if (err < 0) { | |
| 302 | + dev_err(hw->dev, "failed to read fifo status (err=%d)\n", | |
| 303 | + err); | |
| 302 | 304 | return err; |
| 305 | + } | |
| 303 | 306 | |
| 304 | 307 | if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK)) |
| 305 | 308 | return 0; |
| @@ -313,8 +316,12 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) · 7 unmodified lines | ||
| 313 | 316 | |
| 314 | 317 | for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { |
| 315 | 318 | err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len); |
| 316 | − if (err < 0) | |
| 319 | + if (err < 0) { | |
| 320 | + dev_err(hw->dev, | |
| 321 | + "failed to read pattern from fifo (err=%d)\n", | |
| 322 | + err); | |
| 317 | 323 | return err; |
| 324 | + } | |
| 318 | 325 | |
| 319 | 326 | /* |
| 320 | 327 | * Data are written to the FIFO with a specific pattern |
| @@ -385,8 +392,11 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) · 64 unmodified lines | ||
| 385 | 392 | |
| 386 | 393 | if (unlikely(reset_ts)) { |
| 387 | 394 | err = st_lsm6dsx_reset_hw_ts(hw); |
| 388 | − if (err < 0) | |
| 395 | + if (err < 0) { | |
| 396 | + dev_err(hw->dev, "failed to reset hw ts (err=%d)\n", | |
| 397 | + err); | |
| 389 | 398 | return err; |
| 399 | + } | |
| 390 | 400 | } |
| 391 | 401 | return read_len; |
| 392 | 402 | } |
| @@ -86,6 +86,7 @@ static const char * const iio_chan_type_name_spec[] = { · 85 unmodified lines | ||
| 86 | 86 | [IIO_INDEX] = "index", |
| 87 | 87 | [IIO_GRAVITY] = "gravity", |
| 88 | 88 | [IIO_POSITIONRELATIVE] = "positionrelative", |
| 89 | + [IIO_PHASE] = "phase", | |
| 89 | 90 | }; |
| 90 | 91 | |
| 91 | 92 | static const char * const iio_modifier_names[] = { |
| @@ -109,6 +110,7 @@ static const char * const iio_modifier_names[] = { · 17 unmodified lines | ||
| 109 | 110 | [IIO_MOD_LIGHT_GREEN] = "green", |
| 110 | 111 | [IIO_MOD_LIGHT_BLUE] = "blue", |
| 111 | 112 | [IIO_MOD_LIGHT_UV] = "uv", |
| 113 | + [IIO_MOD_LIGHT_DUV] = "duv", | |
| 112 | 114 | [IIO_MOD_QUATERNION] = "quaternion", |
| 113 | 115 | [IIO_MOD_TEMP_AMBIENT] = "ambient", |
| 114 | 116 | [IIO_MOD_TEMP_OBJECT] = "object", |
| @@ -1,3 +1,4 @@ | ||
| 1 | + | |
| 1 | 2 | # |
| 2 | 3 | # Light sensors |
| 3 | 4 | # |
| @@ -319,6 +320,17 @@ config PA12203001 · 315 unmodified lines | ||
| 319 | 320 | This driver can also be built as a module. If so, the module |
| 320 | 321 | will be called pa12203001. |
| 321 | 322 | |
| 323 | +config SI1133 | |
| 324 | + tristate "SI1133 UV Index Sensor and Ambient Light Sensor" | |
| 325 | + depends on I2C | |
| 326 | + select REGMAP_I2C | |
| 327 | + help | |
| 328 | + Say Y here if you want to build a driver for the Silicon Labs SI1133 | |
| 329 | + UV Index Sensor and Ambient Light Sensor chip. | |
| 330 | + | |
| 331 | + To compile this driver as a module, choose M here: the module will be | |
| 332 | + called si1133. | |
| 333 | + | |
| 322 | 334 | config SI1145 |
| 323 | 335 | tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor" |
| 324 | 336 | depends on I2C |
| @@ -32,6 +32,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o · 31 unmodified lines | ||
| 32 | 32 | obj-$(CONFIG_PA12203001) += pa12203001.o |
| 33 | 33 | obj-$(CONFIG_RPR0521) += rpr0521.o |
| 34 | 34 | obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o |
| 35 | +obj-$(CONFIG_SI1133) += si1133.o | |
| 35 | 36 | obj-$(CONFIG_SI1145) += si1145.o |
| 36 | 37 | obj-$(CONFIG_STK3310) += stk3310.o |
| 37 | 38 | obj-$(CONFIG_ST_UVIS25) += st_uvis25_core.o |
| @@ -0,0 +1,1068 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | +/* | |
| 3 | + * si1133.c - Support for Silabs SI1133 combined ambient | |
| 4 | + * light and UV index sensors | |
| 5 | + * | |
| 6 | + * Copyright 2018 Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com> | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/delay.h> | |
| 10 | +#include <linux/i2c.h> | |
| 11 | +#include <linux/interrupt.h> | |
| 12 | +#include <linux/module.h> | |
| 13 | +#include <linux/regmap.h> | |
| 14 | + | |
| 15 | +#include <linux/iio/iio.h> | |
| 16 | +#include <linux/iio/sysfs.h> | |
| 17 | + | |
| 18 | +#include <linux/util_macros.h> | |
| 19 | + | |
| 20 | +#define SI1133_REG_PART_ID 0x00 | |
| 21 | +#define SI1133_REG_REV_ID 0x01 | |
| 22 | +#define SI1133_REG_MFR_ID 0x02 | |
| 23 | +#define SI1133_REG_INFO0 0x03 | |
| 24 | +#define SI1133_REG_INFO1 0x04 | |
| 25 | + | |
| 26 | +#define SI1133_PART_ID 0x33 | |
| 27 | + | |
| 28 | +#define SI1133_REG_HOSTIN0 0x0A | |
| 29 | +#define SI1133_REG_COMMAND 0x0B | |
| 30 | +#define SI1133_REG_IRQ_ENABLE 0x0F | |
| 31 | +#define SI1133_REG_RESPONSE1 0x10 | |
| 32 | +#define SI1133_REG_RESPONSE0 0x11 | |
| 33 | +#define SI1133_REG_IRQ_STATUS 0x12 | |
| 34 | +#define SI1133_REG_MEAS_RATE 0x1A | |
| 35 | + | |
| 36 | +#define SI1133_IRQ_CHANNEL_ENABLE 0xF | |
| 37 | + | |
| 38 | +#define SI1133_CMD_RESET_CTR 0x00 | |
| 39 | +#define SI1133_CMD_RESET_SW 0x01 | |
| 40 | +#define SI1133_CMD_FORCE 0x11 | |
| 41 | +#define SI1133_CMD_START_AUTONOMOUS 0x13 | |
| 42 | +#define SI1133_CMD_PARAM_SET 0x80 | |
| 43 | +#define SI1133_CMD_PARAM_QUERY 0x40 | |
| 44 | +#define SI1133_CMD_PARAM_MASK 0x3F | |
| 45 | + | |
| 46 | +#define SI1133_CMD_ERR_MASK BIT(4) | |
| 47 | +#define SI1133_CMD_SEQ_MASK 0xF | |
| 48 | +#define SI1133_MAX_CMD_CTR 0xF | |
| 49 | + | |
| 50 | +#define SI1133_PARAM_REG_CHAN_LIST 0x01 | |
| 51 | +#define SI1133_PARAM_REG_ADCCONFIG(x) ((x) * 4) + 2 | |
| 52 | +#define SI1133_PARAM_REG_ADCSENS(x) ((x) * 4) + 3 | |
| 53 | +#define SI1133_PARAM_REG_ADCPOST(x) ((x) * 4) + 4 | |
| 54 | + | |
| 55 | +#define SI1133_ADCMUX_MASK 0x1F | |
| 56 | + | |
| 57 | +#define SI1133_ADCCONFIG_DECIM_RATE(x) (x) << 5 | |
| 58 | + | |
| 59 | +#define SI1133_ADCSENS_SCALE_MASK 0x70 | |
| 60 | +#define SI1133_ADCSENS_SCALE_SHIFT 4 | |
| 61 | +#define SI1133_ADCSENS_HSIG_MASK BIT(7) | |
| 62 | +#define SI1133_ADCSENS_HSIG_SHIFT 7 | |
| 63 | +#define SI1133_ADCSENS_HW_GAIN_MASK 0xF | |
| 64 | +#define SI1133_ADCSENS_NB_MEAS(x) fls(x) << SI1133_ADCSENS_SCALE_SHIFT | |
| 65 | + | |
| 66 | +#define SI1133_ADCPOST_24BIT_EN BIT(6) | |
| 67 | +#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) (x & GENMASK(2, 0)) << 3 | |
| 68 | + | |
| 69 | +#define SI1133_PARAM_ADCMUX_SMALL_IR 0x0 | |
| 70 | +#define SI1133_PARAM_ADCMUX_MED_IR 0x1 | |
| 71 | +#define SI1133_PARAM_ADCMUX_LARGE_IR 0x2 | |
| 72 | +#define SI1133_PARAM_ADCMUX_WHITE 0xB | |
| 73 | +#define SI1133_PARAM_ADCMUX_LARGE_WHITE 0xD | |
| 74 | +#define SI1133_PARAM_ADCMUX_UV 0x18 | |
| 75 | +#define SI1133_PARAM_ADCMUX_UV_DEEP 0x19 | |
| 76 | + | |
| 77 | +#define SI1133_ERR_INVALID_CMD 0x0 | |
| 78 | +#define SI1133_ERR_INVALID_LOCATION_CMD 0x1 | |
| 79 | +#define SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION 0x2 | |
| 80 | +#define SI1133_ERR_OUTPUT_BUFFER_OVERFLOW 0x3 | |
| 81 | + | |
| 82 | +#define SI1133_COMPLETION_TIMEOUT_MS 500 | |
| 83 | + | |
| 84 | +#define SI1133_CMD_MINSLEEP_US_LOW 5000 | |
| 85 | +#define SI1133_CMD_MINSLEEP_US_HIGH 7500 | |
| 86 | +#define SI1133_CMD_TIMEOUT_MS 25 | |
| 87 | +#define SI1133_CMD_LUX_TIMEOUT_MS 5000 | |
| 88 | +#define SI1133_CMD_TIMEOUT_US SI1133_CMD_TIMEOUT_MS * 1000 | |
| 89 | + | |
| 90 | +#define SI1133_REG_HOSTOUT(x) (x) + 0x13 | |
| 91 | + | |
| 92 | +#define SI1133_MEASUREMENT_FREQUENCY 1250 | |
| 93 | + | |
| 94 | +#define SI1133_X_ORDER_MASK 0x0070 | |
| 95 | +#define SI1133_Y_ORDER_MASK 0x0007 | |
| 96 | +#define si1133_get_x_order(m) ((m) & SI1133_X_ORDER_MASK) >> 4 | |
| 97 | +#define si1133_get_y_order(m) ((m) & SI1133_Y_ORDER_MASK) | |
| 98 | + | |
| 99 | +#define SI1133_LUX_ADC_MASK 0xE | |
| 100 | +#define SI1133_ADC_THRESHOLD 16000 | |
| 101 | +#define SI1133_INPUT_FRACTION_HIGH 7 | |
| 102 | +#define SI1133_INPUT_FRACTION_LOW 15 | |
| 103 | +#define SI1133_LUX_OUTPUT_FRACTION 12 | |
| 104 | +#define SI1133_LUX_BUFFER_SIZE 9 | |
| 105 | + | |
| 106 | +static const int si1133_scale_available[] = { | |
| 107 | + 1, 2, 4, 8, 16, 32, 64, 128}; | |
| 108 | + | |
| 109 | +static IIO_CONST_ATTR(scale_available, "1 2 4 8 16 32 64 128"); | |
| 110 | + | |
| 111 | +static IIO_CONST_ATTR_INT_TIME_AVAIL("0.0244 0.0488 0.0975 0.195 0.390 0.780 " | |
| 112 | + "1.560 3.120 6.24 12.48 25.0 50.0"); | |
| 113 | + | |
| 114 | +/* A.K.A. HW_GAIN in datasheet */ | |
| 115 | +enum si1133_int_time { | |
| 116 | + _24_4_us = 0, | |
| 117 | + _48_8_us = 1, | |
| 118 | + _97_5_us = 2, | |
| 119 | + _195_0_us = 3, | |
| 120 | + _390_0_us = 4, | |
| 121 | + _780_0_us = 5, | |
| 122 | + _1_560_0_us = 6, | |
| 123 | + _3_120_0_us = 7, | |
| 124 | + _6_240_0_us = 8, | |
| 125 | + _12_480_0_us = 9, | |
| 126 | + _25_ms = 10, | |
| 127 | + _50_ms = 11, | |
| 128 | +}; | |
| 129 | + | |
| 130 | +/* Integration time in milliseconds, nanoseconds */ | |
| 131 | +static const int si1133_int_time_table[][2] = { | |
| 132 | + [_24_4_us] = {0, 24400}, | |
| 133 | + [_48_8_us] = {0, 48800}, | |
| 134 | + [_97_5_us] = {0, 97500}, | |
| 135 | + [_195_0_us] = {0, 195000}, | |
| 136 | + [_390_0_us] = {0, 390000}, | |
| 137 | + [_780_0_us] = {0, 780000}, | |
| 138 | + [_1_560_0_us] = {1, 560000}, | |
| 139 | + [_3_120_0_us] = {3, 120000}, | |
| 140 | + [_6_240_0_us] = {6, 240000}, | |
| 141 | + [_12_480_0_us] = {12, 480000}, | |
| 142 | + [_25_ms] = {25, 000000}, | |
| 143 | + [_50_ms] = {50, 000000}, | |
| 144 | +}; | |
| 145 | + | |
| 146 | +static const struct regmap_range si1133_reg_ranges[] = { | |
| 147 | + regmap_reg_range(0x00, 0x02), | |
| 148 | + regmap_reg_range(0x0A, 0x0B), | |
| 149 | + regmap_reg_range(0x0F, 0x0F), | |
| 150 | + regmap_reg_range(0x10, 0x12), | |
| 151 | + regmap_reg_range(0x13, 0x2C), | |
| 152 | +}; | |
| 153 | + | |
| 154 | +static const struct regmap_range si1133_reg_ro_ranges[] = { | |
| 155 | + regmap_reg_range(0x00, 0x02), | |
| 156 | + regmap_reg_range(0x10, 0x2C), | |
| 157 | +}; | |
| 158 | + | |
| 159 | +static const struct regmap_range si1133_precious_ranges[] = { | |
| 160 | + regmap_reg_range(0x12, 0x12), | |
| 161 | +}; | |
| 162 | + | |
| 163 | +static const struct regmap_access_table si1133_write_ranges_table = { | |
| 164 | + .yes_ranges = si1133_reg_ranges, | |
| 165 | + .n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges), | |
| 166 | + .no_ranges = si1133_reg_ro_ranges, | |
| 167 | + .n_no_ranges = ARRAY_SIZE(si1133_reg_ro_ranges), | |
| 168 | +}; | |
| 169 | + | |
| 170 | +static const struct regmap_access_table si1133_read_ranges_table = { | |
| 171 | + .yes_ranges = si1133_reg_ranges, | |
| 172 | + .n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges), | |
| 173 | +}; | |
| 174 | + | |
| 175 | +static const struct regmap_access_table si1133_precious_table = { | |
| 176 | + .yes_ranges = si1133_precious_ranges, | |
| 177 | + .n_yes_ranges = ARRAY_SIZE(si1133_precious_ranges), | |
| 178 | +}; | |
| 179 | + | |
| 180 | +static const struct regmap_config si1133_regmap_config = { | |
| 181 | + .reg_bits = 8, | |
| 182 | + .val_bits = 8, | |
| 183 | + | |
| 184 | + .max_register = 0x2C, | |
| 185 | + | |
| 186 | + .wr_table = &si1133_write_ranges_table, | |
| 187 | + .rd_table = &si1133_read_ranges_table, | |
| 188 | + | |
| 189 | + .precious_table = &si1133_precious_table, | |
| 190 | +}; | |
| 191 | + | |
| 192 | +struct si1133_data { | |
| 193 | + struct regmap *regmap; | |
| 194 | + struct i2c_client *client; | |
| 195 | + | |
| 196 | + /* Lock protecting one command at a time can be processed */ | |
| 197 | + struct mutex mutex; | |
| 198 | + | |
| 199 | + int rsp_seq; | |
| 200 | + u8 scan_mask; | |
| 201 | + u8 adc_sens[6]; | |
| 202 | + u8 adc_config[6]; | |
| 203 | + | |
| 204 | + struct completion completion; | |
| 205 | +}; | |
| 206 | + | |
| 207 | +struct si1133_coeff { | |
| 208 | + s16 info; | |
| 209 | + u16 mag; | |
| 210 | +}; | |
| 211 | + | |
| 212 | +struct si1133_lux_coeff { | |
| 213 | + struct si1133_coeff coeff_high[4]; | |
| 214 | + struct si1133_coeff coeff_low[9]; | |
| 215 | +}; | |
| 216 | + | |
| 217 | +static const struct si1133_lux_coeff lux_coeff = { | |
| 218 | + { | |
| 219 | + { 0, 209}, | |
| 220 | + { 1665, 93}, | |
| 221 | + { 2064, 65}, | |
| 222 | + {-2671, 234} | |
| 223 | + }, | |
| 224 | + { | |
| 225 | + { 0, 0}, | |
| 226 | + { 1921, 29053}, | |
| 227 | + {-1022, 36363}, | |
| 228 | + { 2320, 20789}, | |
| 229 | + { -367, 57909}, | |
| 230 | + {-1774, 38240}, | |
| 231 | + { -608, 46775}, | |
| 232 | + {-1503, 51831}, | |
| 233 | + {-1886, 58928} | |
| 234 | + } | |
| 235 | +}; | |
| 236 | + | |
| 237 | +static int si1133_calculate_polynomial_inner(u32 input, u8 fraction, u16 mag, | |
| 238 | + s8 shift) | |
| 239 | +{ | |
| 240 | + return ((input << fraction) / mag) << shift; | |
| 241 | +} | |
| 242 | + | |
| 243 | +static int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order, | |
| 244 | + u8 input_fraction, s8 sign, | |
| 245 | + const struct si1133_coeff *coeffs) | |
| 246 | +{ | |
| 247 | + s8 shift; | |
| 248 | + int x1 = 1; | |
| 249 | + int x2 = 1; | |
| 250 | + int y1 = 1; | |
| 251 | + int y2 = 1; | |
| 252 | + | |
| 253 | + shift = ((u16)coeffs->info & 0xFF00) >> 8; | |
| 254 | + shift ^= 0xFF; | |
| 255 | + shift += 1; | |
| 256 | + shift = -shift; | |
| 257 | + | |
| 258 | + if (x_order > 0) { | |
| 259 | + x1 = si1133_calculate_polynomial_inner(x, input_fraction, | |
| 260 | + coeffs->mag, shift); | |
| 261 | + if (x_order > 1) | |
| 262 | + x2 = x1; | |
| 263 | + } | |
| 264 | + | |
| 265 | + if (y_order > 0) { | |
| 266 | + y1 = si1133_calculate_polynomial_inner(y, input_fraction, | |
| 267 | + coeffs->mag, shift); | |
| 268 | + if (y_order > 1) | |
| 269 | + y2 = y1; | |
| 270 | + } | |
| 271 | + | |
| 272 | + return sign * x1 * x2 * y1 * y2; | |
| 273 | +} | |
| 274 | + | |
| 275 | +/* | |
| 276 | + * The algorithm is from: | |
| 277 | + * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00716 | |
| 278 | + */ | |
| 279 | +static int si1133_calc_polynomial(u32 x, u32 y, u8 input_fraction, u8 num_coeff, | |
| 280 | + const struct si1133_coeff *coeffs) | |
| 281 | +{ | |
| 282 | + u8 x_order, y_order; | |
| 283 | + u8 counter; | |
| 284 | + s8 sign; | |
| 285 | + int output = 0; | |
| 286 | + | |
| 287 | + for (counter = 0; counter < num_coeff; counter++) { | |
| 288 | + if (coeffs->info < 0) | |
| 289 | + sign = -1; | |
| 290 | + else | |
| 291 | + sign = 1; | |
| 292 | + | |
| 293 | + x_order = si1133_get_x_order(coeffs->info); | |
| 294 | + y_order = si1133_get_y_order(coeffs->info); | |
| 295 | + | |
| 296 | + if ((x_order == 0) && (y_order == 0)) | |
| 297 | + output += | |
| 298 | + sign * coeffs->mag << SI1133_LUX_OUTPUT_FRACTION; | |
| 299 | + else | |
| 300 | + output += si1133_calculate_output(x, y, x_order, | |
| 301 | + y_order, | |
| 302 | + input_fraction, sign, | |
| 303 | + coeffs); | |
| 304 | + coeffs++; | |
| 305 | + } | |
| 306 | + | |
| 307 | + return abs(output); | |
| 308 | +} | |
| 309 | + | |
| 310 | +static int si1133_cmd_reset_sw(struct si1133_data *data) | |
| 311 | +{ | |
| 312 | + struct device *dev = &data->client->dev; | |
| 313 | + unsigned int resp; | |
| 314 | + unsigned long timeout; | |
| 315 | + int err; | |
| 316 | + | |
| 317 | + err = regmap_write(data->regmap, SI1133_REG_COMMAND, | |
| 318 | + SI1133_CMD_RESET_SW); | |
| 319 | + if (err) | |
| 320 | + return err; | |
| 321 | + | |
| 322 | + timeout = jiffies + msecs_to_jiffies(SI1133_CMD_TIMEOUT_MS); | |
| 323 | + while (true) { | |
| 324 | + err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp); | |
| 325 | + if (err == -ENXIO) { | |
| 326 | + usleep_range(SI1133_CMD_MINSLEEP_US_LOW, | |
| 327 | + SI1133_CMD_MINSLEEP_US_HIGH); | |
| 328 | + continue; | |
| 329 | + } | |
| 330 | + | |
| 331 | + if ((resp & SI1133_MAX_CMD_CTR) == SI1133_MAX_CMD_CTR) | |
| 332 | + break; | |
| 333 | + | |
| 334 | + if (time_after(jiffies, timeout)) { | |
| 335 | + dev_warn(dev, "Timeout on reset ctr resp: %d\n", resp); | |
| 336 | + return -ETIMEDOUT; | |
| 337 | + } | |
| 338 | + } | |
| 339 | + | |
| 340 | + if (!err) | |
| 341 | + data->rsp_seq = SI1133_MAX_CMD_CTR; | |
| 342 | + | |
| 343 | + return err; | |
| 344 | +} | |
| 345 | + | |
| 346 | +static int si1133_parse_response_err(struct device *dev, u32 resp, u8 cmd) | |
| 347 | +{ | |
| 348 | + resp &= 0xF; | |
| 349 | + | |
| 350 | + switch (resp) { | |
| 351 | + case SI1133_ERR_OUTPUT_BUFFER_OVERFLOW: | |
| 352 | + dev_warn(dev, "Output buffer overflow: %#02hhx\n", cmd); | |
| 353 | + return -EOVERFLOW; | |
| 354 | + case SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION: | |
| 355 | + dev_warn(dev, "Saturation of the ADC or overflow of accumulation: %#02hhx\n", | |
| 356 | + cmd); | |
| 357 | + return -EOVERFLOW; | |
| 358 | + case SI1133_ERR_INVALID_LOCATION_CMD: | |
| 359 | + dev_warn(dev, | |
| 360 | + "Parameter access to an invalid location: %#02hhx\n", | |
| 361 | + cmd); | |
| 362 | + return -EINVAL; | |
| 363 | + case SI1133_ERR_INVALID_CMD: | |
| 364 | + dev_warn(dev, "Invalid command %#02hhx\n", cmd); | |
| 365 | + return -EINVAL; | |
| 366 | + default: | |
| 367 | + dev_warn(dev, "Unknown error %#02hhx\n", cmd); | |
| 368 | + return -EINVAL; | |
| 369 | + } | |
| 370 | +} | |
| 371 | + | |
| 372 | +static int si1133_cmd_reset_counter(struct si1133_data *data) | |
| 373 | +{ | |
| 374 | + int err = regmap_write(data->regmap, SI1133_REG_COMMAND, | |
| 375 | + SI1133_CMD_RESET_CTR); | |
| 376 | + if (err) | |
| 377 | + return err; | |
| 378 | + | |
| 379 | + data->rsp_seq = 0; | |
| 380 | + | |
| 381 | + return 0; | |
| 382 | +} | |
| 383 | + | |
| 384 | +static int si1133_command(struct si1133_data *data, u8 cmd) | |
| 385 | +{ | |
| 386 | + struct device *dev = &data->client->dev; | |
| 387 | + u32 resp; | |
| 388 | + int err; | |
| 389 | + int expected_seq; | |
| 390 | + | |
| 391 | + mutex_lock(&data->mutex); | |
| 392 | + | |
| 393 | + expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR; | |
| 394 | + | |
| 395 | + if (cmd == SI1133_CMD_FORCE) | |
| 396 | + reinit_completion(&data->completion); | |
| 397 | + | |
| 398 | + err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd); | |
| 399 | + if (err) { | |
| 400 | + dev_warn(dev, "Failed to write command %#02hhx, ret=%d\n", cmd, | |
| 401 | + err); | |
| 402 | + goto out; | |
| 403 | + } | |
| 404 | + | |
| 405 | + if (cmd == SI1133_CMD_FORCE) { | |
| 406 | + /* wait for irq */ | |
| 407 | + if (!wait_for_completion_timeout(&data->completion, | |
| 408 | + msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) { | |
| 409 | + err = -ETIMEDOUT; | |
| 410 | + goto out; | |
| 411 | + } | |
| 412 | + } else { | |
| 413 | + err = regmap_read_poll_timeout(data->regmap, | |
| 414 | + SI1133_REG_RESPONSE0, resp, | |
| 415 | + (resp & SI1133_CMD_SEQ_MASK) == | |
| 416 | + expected_seq || | |
| 417 | + (resp & SI1133_CMD_ERR_MASK), | |
| 418 | + SI1133_CMD_MINSLEEP_US_LOW, | |
| 419 | + SI1133_CMD_TIMEOUT_MS * 1000); | |
| 420 | + if (err) { | |
| 421 | + dev_warn(dev, | |
| 422 | + "Failed to read command %#02hhx, ret=%d\n", | |
| 423 | + cmd, err); | |
| 424 | + goto out; | |
| 425 | + } | |
| 426 | + } | |
| 427 | + | |
| 428 | + if (resp & SI1133_CMD_ERR_MASK) { | |
| 429 | + err = si1133_parse_response_err(dev, resp, cmd); | |
| 430 | + si1133_cmd_reset_counter(data); | |
| 431 | + } else { | |
| 432 | + data->rsp_seq = expected_seq; | |
| 433 | + } | |
| 434 | + | |
| 435 | +out: | |
| 436 | + mutex_unlock(&data->mutex); | |
| 437 | + | |
| 438 | + return err; | |
| 439 | +} | |
| 440 | + | |
| 441 | +static int si1133_param_set(struct si1133_data *data, u8 param, u32 value) | |
| 442 | +{ | |
| 443 | + int err = regmap_write(data->regmap, SI1133_REG_HOSTIN0, value); | |
| 444 | + | |
| 445 | + if (err) | |
| 446 | + return err; | |
| 447 | + | |
| 448 | + return si1133_command(data, SI1133_CMD_PARAM_SET | | |
| 449 | + (param & SI1133_CMD_PARAM_MASK)); | |
| 450 | +} | |
| 451 | + | |
| 452 | +static int si1133_param_query(struct si1133_data *data, u8 param, u32 *result) | |
| 453 | +{ | |
| 454 | + int err = si1133_command(data, SI1133_CMD_PARAM_QUERY | | |
| 455 | + (param & SI1133_CMD_PARAM_MASK)); | |
| 456 | + if (err) | |
| 457 | + return err; | |
| 458 | + | |
| 459 | + return regmap_read(data->regmap, SI1133_REG_RESPONSE1, result); | |
| 460 | +} | |
| 461 | + | |
| 462 | +#define SI1133_CHANNEL(_ch, _type) \ | |
| 463 | + .type = _type, \ | |
| 464 | + .channel = _ch, \ | |
| 465 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
| 466 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | \ | |
| 467 | + BIT(IIO_CHAN_INFO_SCALE) | \ | |
| 468 | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ | |
| 469 | + | |
| 470 | +static const struct iio_chan_spec si1133_channels[] = { | |
| 471 | + { | |
| 472 | + .type = IIO_LIGHT, | |
| 473 | + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | |
| 474 | + .channel = 0, | |
| 475 | + }, | |
| 476 | + { | |
| 477 | + SI1133_CHANNEL(SI1133_PARAM_ADCMUX_WHITE, IIO_INTENSITY) | |
| 478 | + .channel2 = IIO_MOD_LIGHT_BOTH, | |
| 479 | + }, | |
| 480 | + { | |
| 481 | + SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_WHITE, IIO_INTENSITY) | |
| 482 | + .channel2 = IIO_MOD_LIGHT_BOTH, | |
| 483 | + .extend_name = "large", | |
| 484 | + }, | |
| 485 | + { | |
| 486 | + SI1133_CHANNEL(SI1133_PARAM_ADCMUX_SMALL_IR, IIO_INTENSITY) | |
| 487 | + .extend_name = "small", | |
| 488 | + .modified = 1, | |
| 489 | + .channel2 = IIO_MOD_LIGHT_IR, | |
| 490 | + }, | |
| 491 | + { | |
| 492 | + SI1133_CHANNEL(SI1133_PARAM_ADCMUX_MED_IR, IIO_INTENSITY) | |
| 493 | + .modified = 1, | |
| 494 | + .channel2 = IIO_MOD_LIGHT_IR, | |
| 495 | + }, | |
| 496 | + { | |
| 497 | + SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_IR, IIO_INTENSITY) | |
| 498 | + .extend_name = "large", | |
| 499 | + .modified = 1, | |
| 500 | + .channel2 = IIO_MOD_LIGHT_IR, | |
| 501 | + }, | |
| 502 | + { | |
| 503 | + SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV, IIO_UVINDEX) | |
| 504 | + }, | |
| 505 | + { | |
| 506 | + SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV_DEEP, IIO_UVINDEX) | |
| 507 | + .modified = 1, | |
| 508 | + .channel2 = IIO_MOD_LIGHT_DUV, | |
| 509 | + } | |
| 510 | +}; | |
| 511 | + | |
| 512 | +static int si1133_get_int_time_index(int milliseconds, int nanoseconds) | |
| 513 | +{ | |
| 514 | + int i; | |
| 515 | + | |
| 516 | + for (i = 0; i < ARRAY_SIZE(si1133_int_time_table); i++) { | |
| 517 | + if (milliseconds == si1133_int_time_table[i][0] && | |
| 518 | + nanoseconds == si1133_int_time_table[i][1]) | |
| 519 | + return i; | |
| 520 | + } | |
| 521 | + return -EINVAL; | |
| 522 | +} | |
| 523 | + | |
| 524 | +static int si1133_set_integration_time(struct si1133_data *data, u8 adc, | |
| 525 | + int milliseconds, int nanoseconds) | |
| 526 | +{ | |
| 527 | + int index; | |
| 528 | + | |
| 529 | + index = si1133_get_int_time_index(milliseconds, nanoseconds); | |
| 530 | + if (index < 0) | |
| 531 | + return index; | |
| 532 | + | |
| 533 | + data->adc_sens[adc] &= 0xF0; | |
| 534 | + data->adc_sens[adc] |= index; | |
| 535 | + | |
| 536 | + return si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(0), | |
| 537 | + data->adc_sens[adc]); | |
| 538 | +} | |
| 539 | + | |
| 540 | +static int si1133_set_chlist(struct si1133_data *data, u8 scan_mask) | |
| 541 | +{ | |
| 542 | + /* channel list already set, no need to reprogram */ | |
| 543 | + if (data->scan_mask == scan_mask) | |
| 544 | + return 0; | |
| 545 | + | |
| 546 | + data->scan_mask = scan_mask; | |
| 547 | + | |
| 548 | + return si1133_param_set(data, SI1133_PARAM_REG_CHAN_LIST, scan_mask); | |
| 549 | +} | |
| 550 | + | |
| 551 | +static int si1133_chan_set_adcconfig(struct si1133_data *data, u8 adc, | |
| 552 | + u8 adc_config) | |
| 553 | +{ | |
| 554 | + int err; | |
| 555 | + | |
| 556 | + err = si1133_param_set(data, SI1133_PARAM_REG_ADCCONFIG(adc), | |
| 557 | + adc_config); | |
| 558 | + if (err) | |
| 559 | + return err; | |
| 560 | + | |
| 561 | + data->adc_config[adc] = adc_config; | |
| 562 | + | |
| 563 | + return 0; | |
| 564 | +} | |
| 565 | + | |
| 566 | +static int si1133_update_adcconfig(struct si1133_data *data, uint8_t adc, | |
| 567 | + u8 mask, u8 shift, u8 value) | |
| 568 | +{ | |
| 569 | + u32 adc_config; | |
| 570 | + int err; | |
| 571 | + | |
| 572 | + err = si1133_param_query(data, SI1133_PARAM_REG_ADCCONFIG(adc), | |
| 573 | + &adc_config); | |
| 574 | + if (err) | |
| 575 | + return err; | |
| 576 | + | |
| 577 | + adc_config &= ~mask; | |
| 578 | + adc_config |= (value << shift); | |
| 579 | + | |
| 580 | + return si1133_chan_set_adcconfig(data, adc, adc_config); | |
| 581 | +} | |
| 582 | + | |
| 583 | +static int si1133_set_adcmux(struct si1133_data *data, u8 adc, u8 mux) | |
| 584 | +{ | |
| 585 | + if ((mux & data->adc_config[adc]) == mux) | |
| 586 | + return 0; /* mux already set to correct value */ | |
| 587 | + | |
| 588 | + return si1133_update_adcconfig(data, adc, SI1133_ADCMUX_MASK, 0, mux); | |
| 589 | +} | |
| 590 | + | |
| 591 | +static int si1133_force_measurement(struct si1133_data *data) | |
| 592 | +{ | |
| 593 | + return si1133_command(data, SI1133_CMD_FORCE); | |
| 594 | +} | |
| 595 | + | |
| 596 | +static int si1133_bulk_read(struct si1133_data *data, u8 start_reg, u8 length, | |
| 597 | + u8 *buffer) | |
| 598 | +{ | |
| 599 | + int err; | |
| 600 | + | |
| 601 | + err = si1133_force_measurement(data); | |
| 602 | + if (err) | |
| 603 | + return err; | |
| 604 | + | |
| 605 | + return regmap_bulk_read(data->regmap, start_reg, buffer, length); | |
| 606 | +} | |
| 607 | + | |
| 608 | +static int si1133_measure(struct si1133_data *data, | |
| 609 | + struct iio_chan_spec const *chan, | |
| 610 | + int *val) | |
| 611 | +{ | |
| 612 | + int err; | |
| 613 | + | |
| 614 | + __be16 resp; | |
| 615 | + | |
| 616 | + err = si1133_set_adcmux(data, 0, chan->channel); | |
| 617 | + if (err) | |
| 618 | + return err; | |
| 619 | + | |
| 620 | + /* Deactivate lux measurements if they were active */ | |
| 621 | + err = si1133_set_chlist(data, BIT(0)); | |
| 622 | + if (err) | |
| 623 | + return err; | |
| 624 | + | |
| 625 | + err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(resp), | |
| 626 | + (u8 *)&resp); | |
| 627 | + if (err) | |
| 628 | + return err; | |
| 629 | + | |
| 630 | + *val = be16_to_cpu(resp); | |
| 631 | + | |
| 632 | + return err; | |
| 633 | +} | |
| 634 | + | |
| 635 | +static irqreturn_t si1133_threaded_irq_handler(int irq, void *private) | |
| 636 | +{ | |
| 637 | + struct iio_dev *iio_dev = private; | |
| 638 | + struct si1133_data *data = iio_priv(iio_dev); | |
| 639 | + u32 irq_status; | |
| 640 | + int err; | |
| 641 | + | |
| 642 | + err = regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &irq_status); | |
| 643 | + if (err) { | |
| 644 | + dev_err_ratelimited(&iio_dev->dev, "Error reading IRQ\n"); | |
| 645 | + goto out; | |
| 646 | + } | |
| 647 | + | |
| 648 | + if (irq_status != data->scan_mask) | |
| 649 | + return IRQ_NONE; | |
| 650 | + | |
| 651 | +out: | |
| 652 | + complete(&data->completion); | |
| 653 | + | |
| 654 | + return IRQ_HANDLED; | |
| 655 | +} | |
| 656 | + | |
| 657 | +static int si1133_scale_to_swgain(int scale_integer, int scale_fractional) | |
| 658 | +{ | |
| 659 | + scale_integer = find_closest(scale_integer, si1133_scale_available, | |
| 660 | + ARRAY_SIZE(si1133_scale_available)); | |
| 661 | + if (scale_integer < 0 || | |
| 662 | + scale_integer > ARRAY_SIZE(si1133_scale_available) || | |
| 663 | + scale_fractional != 0) | |
| 664 | + return -EINVAL; | |
| 665 | + | |
| 666 | + return scale_integer; | |
| 667 | +} | |
| 668 | + | |
| 669 | +static int si1133_chan_set_adcsens(struct si1133_data *data, u8 adc, | |
| 670 | + u8 adc_sens) | |
| 671 | +{ | |
| 672 | + int err; | |
| 673 | + | |
| 674 | + err = si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(adc), adc_sens); | |
| 675 | + if (err) | |
| 676 | + return err; | |
| 677 | + | |
| 678 | + data->adc_sens[adc] = adc_sens; | |
| 679 | + | |
| 680 | + return 0; | |
| 681 | +} | |
| 682 | + | |
| 683 | +static int si1133_update_adcsens(struct si1133_data *data, u8 mask, | |
| 684 | + u8 shift, u8 value) | |
| 685 | +{ | |
| 686 | + int err; | |
| 687 | + u32 adc_sens; | |
| 688 | + | |
| 689 | + err = si1133_param_query(data, SI1133_PARAM_REG_ADCSENS(0), | |
| 690 | + &adc_sens); | |
| 691 | + if (err) | |
| 692 | + return err; | |
| 693 | + | |
| 694 | + adc_sens &= ~mask; | |
| 695 | + adc_sens |= (value << shift); | |
| 696 | + | |
| 697 | + return si1133_chan_set_adcsens(data, 0, adc_sens); | |
| 698 | +} | |
| 699 | + | |
| 700 | +static int si1133_get_lux(struct si1133_data *data, int *val) | |
| 701 | +{ | |
| 702 | + int err; | |
| 703 | + int lux; | |
| 704 | + u32 high_vis; | |
| 705 | + u32 low_vis; | |
| 706 | + u32 ir; | |
| 707 | + u8 buffer[SI1133_LUX_BUFFER_SIZE]; | |
| 708 | + | |
| 709 | + /* Activate lux channels */ | |
| 710 | + err = si1133_set_chlist(data, SI1133_LUX_ADC_MASK); | |
| 711 | + if (err) | |
| 712 | + return err; | |
| 713 | + | |
| 714 | + err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), | |
| 715 | + SI1133_LUX_BUFFER_SIZE, buffer); | |
| 716 | + if (err) | |
| 717 | + return err; | |
| 718 | + | |
| 719 | + high_vis = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; | |
| 720 | + low_vis = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5]; | |
| 721 | + ir = (buffer[6] << 16) | (buffer[7] << 8) | buffer[8]; | |
| 722 | + | |
| 723 | + if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD) | |
| 724 | + lux = si1133_calc_polynomial(high_vis, ir, | |
| 725 | + SI1133_INPUT_FRACTION_HIGH, | |
| 726 | + ARRAY_SIZE(lux_coeff.coeff_high), | |
| 727 | + &lux_coeff.coeff_high[0]); | |
| 728 | + else | |
| 729 | + lux = si1133_calc_polynomial(low_vis, ir, | |
| 730 | + SI1133_INPUT_FRACTION_LOW, | |
| 731 | + ARRAY_SIZE(lux_coeff.coeff_low), | |
| 732 | + &lux_coeff.coeff_low[0]); | |
| 733 | + | |
| 734 | + *val = lux >> SI1133_LUX_OUTPUT_FRACTION; | |
| 735 | + | |
| 736 | + return err; | |
| 737 | +} | |
| 738 | + | |
| 739 | +static int si1133_read_raw(struct iio_dev *iio_dev, | |
| 740 | + struct iio_chan_spec const *chan, | |
| 741 | + int *val, int *val2, long mask) | |
| 742 | +{ | |
| 743 | + struct si1133_data *data = iio_priv(iio_dev); | |
| 744 | + u8 adc_sens = data->adc_sens[0]; | |
| 745 | + int err; | |
| 746 | + | |
| 747 | + switch (mask) { | |
| 748 | + case IIO_CHAN_INFO_PROCESSED: | |
| 749 | + switch (chan->type) { | |
| 750 | + case IIO_LIGHT: | |
| 751 | + err = si1133_get_lux(data, val); | |
| 752 | + if (err) | |
| 753 | + return err; | |
| 754 | + | |
| 755 | + return IIO_VAL_INT; | |
| 756 | + default: | |
| 757 | + return -EINVAL; | |
| 758 | + } | |
| 759 | + case IIO_CHAN_INFO_RAW: | |
| 760 | + switch (chan->type) { | |
| 761 | + case IIO_INTENSITY: | |
| 762 | + case IIO_UVINDEX: | |
| 763 | + err = si1133_measure(data, chan, val); | |
| 764 | + if (err) | |
| 765 | + return err; | |
| 766 | + | |
| 767 | + return IIO_VAL_INT; | |
| 768 | + default: | |
| 769 | + return -EINVAL; | |
| 770 | + } | |
| 771 | + case IIO_CHAN_INFO_INT_TIME: | |
| 772 | + switch (chan->type) { | |
| 773 | + case IIO_INTENSITY: | |
| 774 | + case IIO_UVINDEX: | |
| 775 | + adc_sens &= SI1133_ADCSENS_HW_GAIN_MASK; | |
| 776 | + | |
| 777 | + *val = si1133_int_time_table[adc_sens][0]; | |
| 778 | + *val2 = si1133_int_time_table[adc_sens][1]; | |
| 779 | + return IIO_VAL_INT_PLUS_MICRO; | |
| 780 | + default: | |
| 781 | + return -EINVAL; | |
| 782 | + } | |
| 783 | + case IIO_CHAN_INFO_SCALE: | |
| 784 | + switch (chan->type) { | |
| 785 | + case IIO_INTENSITY: | |
| 786 | + case IIO_UVINDEX: | |
| 787 | + adc_sens &= SI1133_ADCSENS_SCALE_MASK; | |
| 788 | + adc_sens >>= SI1133_ADCSENS_SCALE_SHIFT; | |
| 789 | + | |
| 790 | + *val = BIT(adc_sens); | |
| 791 | + | |
| 792 | + return IIO_VAL_INT; | |
| 793 | + default: | |
| 794 | + return -EINVAL; | |
| 795 | + } | |
| 796 | + case IIO_CHAN_INFO_HARDWAREGAIN: | |
| 797 | + switch (chan->type) { | |
| 798 | + case IIO_INTENSITY: | |
| 799 | + case IIO_UVINDEX: | |
| 800 | + adc_sens >>= SI1133_ADCSENS_HSIG_SHIFT; | |
| 801 | + | |
| 802 | + *val = adc_sens; | |
| 803 | + | |
| 804 | + return IIO_VAL_INT; | |
| 805 | + default: | |
| 806 | + return -EINVAL; | |
| 807 | + } | |
| 808 | + default: | |
| 809 | + return -EINVAL; | |
| 810 | + } | |
| 811 | +} | |
| 812 | + | |
| 813 | +static int si1133_write_raw(struct iio_dev *iio_dev, | |
| 814 | + struct iio_chan_spec const *chan, | |
| 815 | + int val, int val2, long mask) | |
| 816 | +{ | |
| 817 | + struct si1133_data *data = iio_priv(iio_dev); | |
| 818 | + | |
| 819 | + switch (mask) { | |
| 820 | + case IIO_CHAN_INFO_SCALE: | |
| 821 | + switch (chan->type) { | |
| 822 | + case IIO_INTENSITY: | |
| 823 | + case IIO_UVINDEX: | |
| 824 | + val = si1133_scale_to_swgain(val, val2); | |
| 825 | + if (val < 0) | |
| 826 | + return val; | |
| 827 | + | |
| 828 | + return si1133_update_adcsens(data, | |
| 829 | + SI1133_ADCSENS_SCALE_MASK, | |
| 830 | + SI1133_ADCSENS_SCALE_SHIFT, | |
| 831 | + val); | |
| 832 | + default: | |
| 833 | + return -EINVAL; | |
| 834 | + } | |
| 835 | + case IIO_CHAN_INFO_INT_TIME: | |
| 836 | + return si1133_set_integration_time(data, 0, val, val2); | |
| 837 | + case IIO_CHAN_INFO_HARDWAREGAIN: | |
| 838 | + switch (chan->type) { | |
| 839 | + case IIO_INTENSITY: | |
| 840 | + case IIO_UVINDEX: | |
| 841 | + if (val != 0 || val != 1) | |
| 842 | + return -EINVAL; | |
| 843 | + | |
| 844 | + return si1133_update_adcsens(data, | |
| 845 | + SI1133_ADCSENS_HSIG_MASK, | |
| 846 | + SI1133_ADCSENS_HSIG_SHIFT, | |
| 847 | + val); | |
| 848 | + default: | |
| 849 | + return -EINVAL; | |
| 850 | + } | |
| 851 | + default: | |
| 852 | + return -EINVAL; | |
| 853 | + } | |
| 854 | +} | |
| 855 | + | |
| 856 | +static struct attribute *si1133_attributes[] = { | |
| 857 | + &iio_const_attr_integration_time_available.dev_attr.attr, | |
| 858 | + &iio_const_attr_scale_available.dev_attr.attr, | |
| 859 | + NULL, | |
| 860 | +}; | |
| 861 | + | |
| 862 | +static const struct attribute_group si1133_attribute_group = { | |
| 863 | + .attrs = si1133_attributes, | |
| 864 | +}; | |
| 865 | + | |
| 866 | +static const struct iio_info si1133_info = { | |
| 867 | + .read_raw = si1133_read_raw, | |
| 868 | + .write_raw = si1133_write_raw, | |
| 869 | + .attrs = &si1133_attribute_group, | |
| 870 | +}; | |
| 871 | + | |
| 872 | +/* | |
| 873 | + * si1133_init_lux_channels - Configure 3 different channels(adc) (1,2 and 3) | |
| 874 | + * The channel configuration for the lux measurement was taken from : | |
| 875 | + * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00578 | |
| 876 | + * | |
| 877 | + * Reserved the channel 0 for the other raw measurements | |
| 878 | + */ | |
| 879 | +static int si1133_init_lux_channels(struct si1133_data *data) | |
| 880 | +{ | |
| 881 | + int err; | |
| 882 | + | |
| 883 | + err = si1133_chan_set_adcconfig(data, 1, | |
| 884 | + SI1133_ADCCONFIG_DECIM_RATE(1) | | |
| 885 | + SI1133_PARAM_ADCMUX_LARGE_WHITE); | |
| 886 | + if (err) | |
| 887 | + return err; | |
| 888 | + | |
| 889 | + err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(1), | |
| 890 | + SI1133_ADCPOST_24BIT_EN | | |
| 891 | + SI1133_ADCPOST_POSTSHIFT_BITQTY(0)); | |
| 892 | + if (err) | |
| 893 | + return err; | |
| 894 | + err = si1133_chan_set_adcsens(data, 1, SI1133_ADCSENS_HSIG_MASK | | |
| 895 | + SI1133_ADCSENS_NB_MEAS(64) | _48_8_us); | |
| 896 | + if (err) | |
| 897 | + return err; | |
| 898 | + | |
| 899 | + err = si1133_chan_set_adcconfig(data, 2, | |
| 900 | + SI1133_ADCCONFIG_DECIM_RATE(1) | | |
| 901 | + SI1133_PARAM_ADCMUX_LARGE_WHITE); | |
| 902 | + if (err) | |
| 903 | + return err; | |
| 904 | + | |
| 905 | + err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(2), | |
| 906 | + SI1133_ADCPOST_24BIT_EN | | |
| 907 | + SI1133_ADCPOST_POSTSHIFT_BITQTY(2)); | |
| 908 | + if (err) | |
| 909 | + return err; | |
| 910 | + | |
| 911 | + err = si1133_chan_set_adcsens(data, 2, SI1133_ADCSENS_HSIG_MASK | | |
| 912 | + SI1133_ADCSENS_NB_MEAS(1) | _3_120_0_us); | |
| 913 | + if (err) | |
| 914 | + return err; | |
| 915 | + | |
| 916 | + err = si1133_chan_set_adcconfig(data, 3, | |
| 917 | + SI1133_ADCCONFIG_DECIM_RATE(1) | | |
| 918 | + SI1133_PARAM_ADCMUX_MED_IR); | |
| 919 | + if (err) | |
| 920 | + return err; | |
| 921 | + | |
| 922 | + err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(3), | |
| 923 | + SI1133_ADCPOST_24BIT_EN | | |
| 924 | + SI1133_ADCPOST_POSTSHIFT_BITQTY(2)); | |
| 925 | + if (err) | |
| 926 | + return err; | |
| 927 | + | |
| 928 | + return si1133_chan_set_adcsens(data, 3, SI1133_ADCSENS_HSIG_MASK | | |
| 929 | + SI1133_ADCSENS_NB_MEAS(64) | _48_8_us); | |
| 930 | +} | |
| 931 | + | |
| 932 | +static int si1133_initialize(struct si1133_data *data) | |
| 933 | +{ | |
| 934 | + int err; | |
| 935 | + | |
| 936 | + err = si1133_cmd_reset_sw(data); | |
| 937 | + if (err) | |
| 938 | + return err; | |
| 939 | + | |
| 940 | + /* Turn off autonomous mode */ | |
| 941 | + err = si1133_param_set(data, SI1133_REG_MEAS_RATE, 0); | |
| 942 | + if (err) | |
| 943 | + return err; | |
| 944 | + | |
| 945 | + err = si1133_init_lux_channels(data); | |
| 946 | + if (err) | |
| 947 | + return err; | |
| 948 | + | |
| 949 | + return regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, | |
| 950 | + SI1133_IRQ_CHANNEL_ENABLE); | |
| 951 | +} | |
| 952 | + | |
| 953 | +static int si1133_validate_ids(struct iio_dev *iio_dev) | |
| 954 | +{ | |
| 955 | + struct si1133_data *data = iio_priv(iio_dev); | |
| 956 | + | |
| 957 | + unsigned int part_id, rev_id, mfr_id; | |
| 958 | + int err; | |
| 959 | + | |
| 960 | + err = regmap_read(data->regmap, SI1133_REG_PART_ID, &part_id); | |
| 961 | + if (err) | |
| 962 | + return err; | |
| 963 | + | |
| 964 | + err = regmap_read(data->regmap, SI1133_REG_REV_ID, &rev_id); | |
| 965 | + if (err) | |
| 966 | + return err; | |
| 967 | + | |
| 968 | + err = regmap_read(data->regmap, SI1133_REG_MFR_ID, &mfr_id); | |
| 969 | + if (err) | |
| 970 | + return err; | |
| 971 | + | |
| 972 | + dev_info(&iio_dev->dev, | |
| 973 | + "Device ID part %#02hhx rev %#02hhx mfr %#02hhx\n", | |
| 974 | + part_id, rev_id, mfr_id); | |
| 975 | + if (part_id != SI1133_PART_ID) { | |
| 976 | + dev_err(&iio_dev->dev, | |
| 977 | + "Part ID mismatch got %#02hhx, expected %#02x\n", | |
| 978 | + part_id, SI1133_PART_ID); | |
| 979 | + return -ENODEV; | |
| 980 | + } | |
| 981 | + | |
| 982 | + return 0; | |
| 983 | +} | |
| 984 | + | |
| 985 | +static int si1133_probe(struct i2c_client *client, | |
| 986 | + const struct i2c_device_id *id) | |
| 987 | +{ | |
| 988 | + struct si1133_data *data; | |
| 989 | + struct iio_dev *iio_dev; | |
| 990 | + int err; | |
| 991 | + | |
| 992 | + iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
| 993 | + if (!iio_dev) | |
| 994 | + return -ENOMEM; | |
| 995 | + | |
| 996 | + data = iio_priv(iio_dev); | |
| 997 | + | |
| 998 | + init_completion(&data->completion); | |
| 999 | + | |
| 1000 | + data->regmap = devm_regmap_init_i2c(client, &si1133_regmap_config); | |
| 1001 | + if (IS_ERR(data->regmap)) { | |
| 1002 | + err = PTR_ERR(data->regmap); | |
| 1003 | + dev_err(&client->dev, "Failed to initialise regmap: %d\n", err); | |
| 1004 | + return err; | |
| 1005 | + } | |
| 1006 | + | |
| 1007 | + i2c_set_clientdata(client, iio_dev); | |
| 1008 | + data->client = client; | |
| 1009 | + | |
| 1010 | + iio_dev->dev.parent = &client->dev; | |
| 1011 | + iio_dev->name = id->name; | |
| 1012 | + iio_dev->channels = si1133_channels; | |
| 1013 | + iio_dev->num_channels = ARRAY_SIZE(si1133_channels); | |
| 1014 | + iio_dev->info = &si1133_info; | |
| 1015 | + iio_dev->modes = INDIO_DIRECT_MODE; | |
| 1016 | + | |
| 1017 | + mutex_init(&data->mutex); | |
| 1018 | + | |
| 1019 | + err = si1133_validate_ids(iio_dev); | |
| 1020 | + if (err) | |
| 1021 | + return err; | |
| 1022 | + | |
| 1023 | + err = si1133_initialize(data); | |
| 1024 | + if (err) { | |
| 1025 | + dev_err(&client->dev, | |
| 1026 | + "Error when initializing chip: %d\n", err); | |
| 1027 | + return err; | |
| 1028 | + } | |
| 1029 | + | |
| 1030 | + if (!client->irq) { | |
| 1031 | + dev_err(&client->dev, | |
| 1032 | + "Required interrupt not provided, cannot proceed\n"); | |
| 1033 | + return -EINVAL; | |
| 1034 | + } | |
| 1035 | + | |
| 1036 | + err = devm_request_threaded_irq(&client->dev, client->irq, | |
| 1037 | + NULL, | |
| 1038 | + si1133_threaded_irq_handler, | |
| 1039 | + IRQF_ONESHOT | IRQF_SHARED, | |
| 1040 | + client->name, iio_dev); | |
| 1041 | + if (err) { | |
| 1042 | + dev_warn(&client->dev, "Request irq %d failed: %i\n", | |
| 1043 | + client->irq, err); | |
| 1044 | + return err; | |
| 1045 | + } | |
| 1046 | + | |
| 1047 | + return devm_iio_device_register(&client->dev, iio_dev); | |
| 1048 | +} | |
| 1049 | + | |
| 1050 | +static const struct i2c_device_id si1133_ids[] = { | |
| 1051 | + { "si1133", 0 }, | |
| 1052 | + { } | |
| 1053 | +}; | |
| 1054 | +MODULE_DEVICE_TABLE(i2c, si1133_ids); | |
| 1055 | + | |
| 1056 | +static struct i2c_driver si1133_driver = { | |
| 1057 | + .driver = { | |
| 1058 | + .name = "si1133", | |
| 1059 | + }, | |
| 1060 | + .probe = si1133_probe, | |
| 1061 | + .id_table = si1133_ids, | |
| 1062 | +}; | |
| 1063 | + | |
| 1064 | +module_i2c_driver(si1133_driver); | |
| 1065 | + | |
| 1066 | +MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>"); | |
| 1067 | +MODULE_DESCRIPTION("Silabs SI1133, UV index sensor and ambient light sensor driver"); | |
| 1068 | +MODULE_LICENSE("GPL"); | |
| @@ -94,9 +94,8 @@ static int st_press_i2c_probe(struct i2c_client *client, · 93 unmodified lines | ||
| 94 | 94 | if ((ret < 0) || (ret >= ST_PRESS_MAX)) |
| 95 | 95 | return -ENODEV; |
| 96 | 96 | |
| 97 | − strncpy(client->name, st_press_id_table[ret].name, | |
| 97 | + strlcpy(client->name, st_press_id_table[ret].name, | |
| 98 | 98 | sizeof(client->name)); |
| 99 | − client->name[sizeof(client->name) - 1] = '\0'; | |
| 100 | 99 | } else if (!id) |
| 101 | 100 | return -ENODEV; |
| 102 | 101 | |
| @@ -20,6 +20,19 @@ endmenu · 19 unmodified lines | ||
| 20 | 20 | |
| 21 | 21 | menu "Proximity and distance sensors" |
| 22 | 22 | |
| 23 | +config ISL29501 | |
| 24 | + tristate "Intersil ISL29501 Time Of Flight sensor" | |
| 25 | + depends on I2C | |
| 26 | + select IIO_BUFFER | |
| 27 | + select IIO_TRIGGERED_BUFFER | |
| 28 | + select IIO_KFIFO_BUF | |
| 29 | + help | |
| 30 | + Say Y here if you want to build a driver for the Intersil ISL29501 | |
| 31 | + Time of Flight sensor. | |
| 32 | + | |
| 33 | + To compile this driver as a module, choose M here: the module will be | |
| 34 | + called isl29501. | |
| 35 | + | |
| 23 | 36 | config LIDAR_LITE_V2 |
| 24 | 37 | tristate "PulsedLight LIDAR sensor" |
| 25 | 38 | select IIO_BUFFER |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | |
| 6 | 6 | # When adding new entries keep the list in alphabetical order |
| 7 | 7 | obj-$(CONFIG_AS3935) += as3935.o |
| 8 | +obj-$(CONFIG_ISL29501) += isl29501.o | |
| 8 | 9 | obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o |
| 9 | 10 | obj-$(CONFIG_RFD77402) += rfd77402.o |
| 10 | 11 | obj-$(CONFIG_SRF04) += srf04.o |
| @@ -0,0 +1,1027 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * isl29501.c: ISL29501 Time of Flight sensor driver. | |
| 4 | + * | |
| 5 | + * Copyright (C) 2018 | |
| 6 | + * Author: Mathieu Othacehe <m.othacehe@gmail.com> | |
| 7 | + * | |
| 8 | + * 7-bit I2C slave address: 0x57 | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#include <linux/kernel.h> | |
| 12 | +#include <linux/module.h> | |
| 13 | +#include <linux/i2c.h> | |
| 14 | +#include <linux/err.h> | |
| 15 | +#include <linux/of_device.h> | |
| 16 | +#include <linux/iio/iio.h> | |
| 17 | +#include <linux/iio/sysfs.h> | |
| 18 | + | |
| 19 | +#include <linux/iio/trigger_consumer.h> | |
| 20 | +#include <linux/iio/buffer.h> | |
| 21 | +#include <linux/iio/triggered_buffer.h> | |
| 22 | + | |
| 23 | +/* Control, setting and status registers */ | |
| 24 | +#define ISL29501_DEVICE_ID 0x00 | |
| 25 | +#define ISL29501_ID 0x0A | |
| 26 | + | |
| 27 | +/* Sampling control registers */ | |
| 28 | +#define ISL29501_INTEGRATION_PERIOD 0x10 | |
| 29 | +#define ISL29501_SAMPLE_PERIOD 0x11 | |
| 30 | + | |
| 31 | +/* Closed loop calibration registers */ | |
| 32 | +#define ISL29501_CROSSTALK_I_MSB 0x24 | |
| 33 | +#define ISL29501_CROSSTALK_I_LSB 0x25 | |
| 34 | +#define ISL29501_CROSSTALK_I_EXPONENT 0x26 | |
| 35 | +#define ISL29501_CROSSTALK_Q_MSB 0x27 | |
| 36 | +#define ISL29501_CROSSTALK_Q_LSB 0x28 | |
| 37 | +#define ISL29501_CROSSTALK_Q_EXPONENT 0x29 | |
| 38 | +#define ISL29501_CROSSTALK_GAIN_MSB 0x2A | |
| 39 | +#define ISL29501_CROSSTALK_GAIN_LSB 0x2B | |
| 40 | +#define ISL29501_MAGNITUDE_REF_EXP 0x2C | |
| 41 | +#define ISL29501_MAGNITUDE_REF_MSB 0x2D | |
| 42 | +#define ISL29501_MAGNITUDE_REF_LSB 0x2E | |
| 43 | +#define ISL29501_PHASE_OFFSET_MSB 0x2F | |
| 44 | +#define ISL29501_PHASE_OFFSET_LSB 0x30 | |
| 45 | + | |
| 46 | +/* Analog control registers */ | |
| 47 | +#define ISL29501_DRIVER_RANGE 0x90 | |
| 48 | +#define ISL29501_EMITTER_DAC 0x91 | |
| 49 | + | |
| 50 | +#define ISL29501_COMMAND_REGISTER 0xB0 | |
| 51 | + | |
| 52 | +/* Commands */ | |
| 53 | +#define ISL29501_EMUL_SAMPLE_START_PIN 0x49 | |
| 54 | +#define ISL29501_RESET_ALL_REGISTERS 0xD7 | |
| 55 | +#define ISL29501_RESET_INT_SM 0xD1 | |
| 56 | + | |
| 57 | +/* Ambiant light and temperature corrections */ | |
| 58 | +#define ISL29501_TEMP_REFERENCE 0x31 | |
| 59 | +#define ISL29501_PHASE_EXPONENT 0x33 | |
| 60 | +#define ISL29501_TEMP_COEFF_A 0x34 | |
| 61 | +#define ISL29501_TEMP_COEFF_B 0x39 | |
| 62 | +#define ISL29501_AMBIANT_COEFF_A 0x36 | |
| 63 | +#define ISL29501_AMBIANT_COEFF_B 0x3B | |
| 64 | + | |
| 65 | +/* Data output registers */ | |
| 66 | +#define ISL29501_DISTANCE_MSB_DATA 0xD1 | |
| 67 | +#define ISL29501_DISTANCE_LSB_DATA 0xD2 | |
| 68 | +#define ISL29501_PRECISION_MSB 0xD3 | |
| 69 | +#define ISL29501_PRECISION_LSB 0xD4 | |
| 70 | +#define ISL29501_MAGNITUDE_EXPONENT 0xD5 | |
| 71 | +#define ISL29501_MAGNITUDE_MSB 0xD6 | |
| 72 | +#define ISL29501_MAGNITUDE_LSB 0xD7 | |
| 73 | +#define ISL29501_PHASE_MSB 0xD8 | |
| 74 | +#define ISL29501_PHASE_LSB 0xD9 | |
| 75 | +#define ISL29501_I_RAW_EXPONENT 0xDA | |
| 76 | +#define ISL29501_I_RAW_MSB 0xDB | |
| 77 | +#define ISL29501_I_RAW_LSB 0xDC | |
| 78 | +#define ISL29501_Q_RAW_EXPONENT 0xDD | |
| 79 | +#define ISL29501_Q_RAW_MSB 0xDE | |
| 80 | +#define ISL29501_Q_RAW_LSB 0xDF | |
| 81 | +#define ISL29501_DIE_TEMPERATURE 0xE2 | |
| 82 | +#define ISL29501_AMBIENT_LIGHT 0xE3 | |
| 83 | +#define ISL29501_GAIN_MSB 0xE6 | |
| 84 | +#define ISL29501_GAIN_LSB 0xE7 | |
| 85 | + | |
| 86 | +#define ISL29501_MAX_EXP_VAL 15 | |
| 87 | + | |
| 88 | +#define ISL29501_INT_TIME_AVAILABLE \ | |
| 89 | + "0.00007 0.00014 0.00028 0.00057 0.00114 " \ | |
| 90 | + "0.00228 0.00455 0.00910 0.01820 0.03640 " \ | |
| 91 | + "0.07281 0.14561" | |
| 92 | + | |
| 93 | +#define ISL29501_CURRENT_SCALE_AVAILABLE \ | |
| 94 | + "0.0039 0.0078 0.0118 0.0157 0.0196 " \ | |
| 95 | + "0.0235 0.0275 0.0314 0.0352 0.0392 " \ | |
| 96 | + "0.0431 0.0471 0.0510 0.0549 0.0588" | |
| 97 | + | |
| 98 | +enum isl29501_correction_coeff { | |
| 99 | + COEFF_TEMP_A, | |
| 100 | + COEFF_TEMP_B, | |
| 101 | + COEFF_LIGHT_A, | |
| 102 | + COEFF_LIGHT_B, | |
| 103 | + COEFF_MAX, | |
| 104 | +}; | |
| 105 | + | |
| 106 | +struct isl29501_private { | |
| 107 | + struct i2c_client *client; | |
| 108 | + struct mutex lock; | |
| 109 | + /* Exact representation of correction coefficients. */ | |
| 110 | + unsigned int shadow_coeffs[COEFF_MAX]; | |
| 111 | +}; | |
| 112 | + | |
| 113 | +enum isl29501_register_name { | |
| 114 | + REG_DISTANCE, | |
| 115 | + REG_PHASE, | |
| 116 | + REG_TEMPERATURE, | |
| 117 | + REG_AMBIENT_LIGHT, | |
| 118 | + REG_GAIN, | |
| 119 | + REG_GAIN_BIAS, | |
| 120 | + REG_PHASE_EXP, | |
| 121 | + REG_CALIB_PHASE_TEMP_A, | |
| 122 | + REG_CALIB_PHASE_TEMP_B, | |
| 123 | + REG_CALIB_PHASE_LIGHT_A, | |
| 124 | + REG_CALIB_PHASE_LIGHT_B, | |
| 125 | + REG_DISTANCE_BIAS, | |
| 126 | + REG_TEMPERATURE_BIAS, | |
| 127 | + REG_INT_TIME, | |
| 128 | + REG_SAMPLE_TIME, | |
| 129 | + REG_DRIVER_RANGE, | |
| 130 | + REG_EMITTER_DAC, | |
| 131 | +}; | |
| 132 | + | |
| 133 | +struct isl29501_register_desc { | |
| 134 | + u8 msb; | |
| 135 | + u8 lsb; | |
| 136 | +}; | |
| 137 | + | |
| 138 | +static const struct isl29501_register_desc isl29501_registers[] = { | |
| 139 | + [REG_DISTANCE] = { | |
| 140 | + .msb = ISL29501_DISTANCE_MSB_DATA, | |
| 141 | + .lsb = ISL29501_DISTANCE_LSB_DATA, | |
| 142 | + }, | |
| 143 | + [REG_PHASE] = { | |
| 144 | + .msb = ISL29501_PHASE_MSB, | |
| 145 | + .lsb = ISL29501_PHASE_LSB, | |
| 146 | + }, | |
| 147 | + [REG_TEMPERATURE] = { | |
| 148 | + .lsb = ISL29501_DIE_TEMPERATURE, | |
| 149 | + }, | |
| 150 | + [REG_AMBIENT_LIGHT] = { | |
| 151 | + .lsb = ISL29501_AMBIENT_LIGHT, | |
| 152 | + }, | |
| 153 | + [REG_GAIN] = { | |
| 154 | + .msb = ISL29501_GAIN_MSB, | |
| 155 | + .lsb = ISL29501_GAIN_LSB, | |
| 156 | + }, | |
| 157 | + [REG_GAIN_BIAS] = { | |
| 158 | + .msb = ISL29501_CROSSTALK_GAIN_MSB, | |
| 159 | + .lsb = ISL29501_CROSSTALK_GAIN_LSB, | |
| 160 | + }, | |
| 161 | + [REG_PHASE_EXP] = { | |
| 162 | + .lsb = ISL29501_PHASE_EXPONENT, | |
| 163 | + }, | |
| 164 | + [REG_CALIB_PHASE_TEMP_A] = { | |
| 165 | + .lsb = ISL29501_TEMP_COEFF_A, | |
| 166 | + }, | |
| 167 | + [REG_CALIB_PHASE_TEMP_B] = { | |
| 168 | + .lsb = ISL29501_TEMP_COEFF_B, | |
| 169 | + }, | |
| 170 | + [REG_CALIB_PHASE_LIGHT_A] = { | |
| 171 | + .lsb = ISL29501_AMBIANT_COEFF_A, | |
| 172 | + }, | |
| 173 | + [REG_CALIB_PHASE_LIGHT_B] = { | |
| 174 | + .lsb = ISL29501_AMBIANT_COEFF_B, | |
| 175 | + }, | |
| 176 | + [REG_DISTANCE_BIAS] = { | |
| 177 | + .msb = ISL29501_PHASE_OFFSET_MSB, | |
| 178 | + .lsb = ISL29501_PHASE_OFFSET_LSB, | |
| 179 | + }, | |
| 180 | + [REG_TEMPERATURE_BIAS] = { | |
| 181 | + .lsb = ISL29501_TEMP_REFERENCE, | |
| 182 | + }, | |
| 183 | + [REG_INT_TIME] = { | |
| 184 | + .lsb = ISL29501_INTEGRATION_PERIOD, | |
| 185 | + }, | |
| 186 | + [REG_SAMPLE_TIME] = { | |
| 187 | + .lsb = ISL29501_SAMPLE_PERIOD, | |
| 188 | + }, | |
| 189 | + [REG_DRIVER_RANGE] = { | |
| 190 | + .lsb = ISL29501_DRIVER_RANGE, | |
| 191 | + }, | |
| 192 | + [REG_EMITTER_DAC] = { | |
| 193 | + .lsb = ISL29501_EMITTER_DAC, | |
| 194 | + }, | |
| 195 | +}; | |
| 196 | + | |
| 197 | +static int isl29501_register_read(struct isl29501_private *isl29501, | |
| 198 | + enum isl29501_register_name name, | |
| 199 | + u32 *val) | |
| 200 | +{ | |
| 201 | + const struct isl29501_register_desc *reg = &isl29501_registers[name]; | |
| 202 | + u8 msb = 0, lsb = 0; | |
| 203 | + s32 ret; | |
| 204 | + | |
| 205 | + mutex_lock(&isl29501->lock); | |
| 206 | + if (reg->msb) { | |
| 207 | + ret = i2c_smbus_read_byte_data(isl29501->client, reg->msb); | |
| 208 | + if (ret < 0) | |
| 209 | + goto err; | |
| 210 | + msb = ret; | |
| 211 | + } | |
| 212 | + | |
| 213 | + if (reg->lsb) { | |
| 214 | + ret = i2c_smbus_read_byte_data(isl29501->client, reg->lsb); | |
| 215 | + if (ret < 0) | |
| 216 | + goto err; | |
| 217 | + lsb = ret; | |
| 218 | + } | |
| 219 | + mutex_unlock(&isl29501->lock); | |
| 220 | + | |
| 221 | + *val = (msb << 8) + lsb; | |
| 222 | + | |
| 223 | + return 0; | |
| 224 | +err: | |
| 225 | + mutex_unlock(&isl29501->lock); | |
| 226 | + | |
| 227 | + return ret; | |
| 228 | +} | |
| 229 | + | |
| 230 | +static u32 isl29501_register_write(struct isl29501_private *isl29501, | |
| 231 | + enum isl29501_register_name name, | |
| 232 | + u32 value) | |
| 233 | +{ | |
| 234 | + const struct isl29501_register_desc *reg = &isl29501_registers[name]; | |
| 235 | + u8 msb, lsb; | |
| 236 | + int ret; | |
| 237 | + | |
| 238 | + if (!reg->msb && value > U8_MAX) | |
| 239 | + return -ERANGE; | |
| 240 | + | |
| 241 | + if (value > U16_MAX) | |
| 242 | + return -ERANGE; | |
| 243 | + | |
| 244 | + if (!reg->msb) { | |
| 245 | + lsb = value & 0xFF; | |
| 246 | + } else { | |
| 247 | + msb = (value >> 8) & 0xFF; | |
| 248 | + lsb = value & 0xFF; | |
| 249 | + } | |
| 250 | + | |
| 251 | + mutex_lock(&isl29501->lock); | |
| 252 | + if (reg->msb) { | |
| 253 | + ret = i2c_smbus_write_byte_data(isl29501->client, | |
| 254 | + reg->msb, msb); | |
| 255 | + if (ret < 0) | |
| 256 | + goto err; | |
| 257 | + } | |
| 258 | + | |
| 259 | + ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, lsb); | |
| 260 | + | |
| 261 | +err: | |
| 262 | + mutex_unlock(&isl29501->lock); | |
| 263 | + return ret; | |
| 264 | +} | |
| 265 | + | |
| 266 | +static ssize_t isl29501_read_ext(struct iio_dev *indio_dev, | |
| 267 | + uintptr_t private, | |
| 268 | + const struct iio_chan_spec *chan, | |
| 269 | + char *buf) | |
| 270 | +{ | |
| 271 | + struct isl29501_private *isl29501 = iio_priv(indio_dev); | |
| 272 | + enum isl29501_register_name reg = private; | |
| 273 | + int ret; | |
| 274 | + u32 value, gain, coeff, exp; | |
| 275 | + | |
| 276 | + switch (reg) { | |
| 277 | + case REG_GAIN: | |
| 278 | + case REG_GAIN_BIAS: | |
| 279 | + ret = isl29501_register_read(isl29501, reg, &gain); | |
| 280 | + if (ret < 0) | |
| 281 | + return ret; | |
| 282 | + | |
| 283 | + value = gain; | |
| 284 | + break; | |
| 285 | + case REG_CALIB_PHASE_TEMP_A: | |
| 286 | + case REG_CALIB_PHASE_TEMP_B: | |
| 287 | + case REG_CALIB_PHASE_LIGHT_A: | |
| 288 | + case REG_CALIB_PHASE_LIGHT_B: | |
| 289 | + ret = isl29501_register_read(isl29501, REG_PHASE_EXP, &exp); | |
| 290 | + if (ret < 0) | |
| 291 | + return ret; | |
| 292 | + | |
| 293 | + ret = isl29501_register_read(isl29501, reg, &coeff); | |
| 294 | + if (ret < 0) | |
| 295 | + return ret; | |
| 296 | + | |
| 297 | + value = coeff << exp; | |
| 298 | + break; | |
| 299 | + default: | |
| 300 | + return -EINVAL; | |
| 301 | + } | |
| 302 | + | |
| 303 | + return sprintf(buf, "%u\n", value); | |
| 304 | +} | |
| 305 | + | |
| 306 | +static int isl29501_set_shadow_coeff(struct isl29501_private *isl29501, | |
| 307 | + enum isl29501_register_name reg, | |
| 308 | + unsigned int val) | |
| 309 | +{ | |
| 310 | + enum isl29501_correction_coeff coeff; | |
| 311 | + | |
| 312 | + switch (reg) { | |
| 313 | + case REG_CALIB_PHASE_TEMP_A: | |
| 314 | + coeff = COEFF_TEMP_A; | |
| 315 | + break; | |
| 316 | + case REG_CALIB_PHASE_TEMP_B: | |
| 317 | + coeff = COEFF_TEMP_B; | |
| 318 | + break; | |
| 319 | + case REG_CALIB_PHASE_LIGHT_A: | |
| 320 | + coeff = COEFF_LIGHT_A; | |
| 321 | + break; | |
| 322 | + case REG_CALIB_PHASE_LIGHT_B: | |
| 323 | + coeff = COEFF_LIGHT_B; | |
| 324 | + break; | |
| 325 | + default: | |
| 326 | + return -EINVAL; | |
| 327 | + } | |
| 328 | + isl29501->shadow_coeffs[coeff] = val; | |
| 329 | + | |
| 330 | + return 0; | |
| 331 | +} | |
| 332 | + | |
| 333 | +static int isl29501_write_coeff(struct isl29501_private *isl29501, | |
| 334 | + enum isl29501_correction_coeff coeff, | |
| 335 | + int val) | |
| 336 | +{ | |
| 337 | + enum isl29501_register_name reg; | |
| 338 | + | |
| 339 | + switch (coeff) { | |
| 340 | + case COEFF_TEMP_A: | |
| 341 | + reg = REG_CALIB_PHASE_TEMP_A; | |
| 342 | + break; | |
| 343 | + case COEFF_TEMP_B: | |
| 344 | + reg = REG_CALIB_PHASE_TEMP_B; | |
| 345 | + break; | |
| 346 | + case COEFF_LIGHT_A: | |
| 347 | + reg = REG_CALIB_PHASE_LIGHT_A; | |
| 348 | + break; | |
| 349 | + case COEFF_LIGHT_B: | |
| 350 | + reg = REG_CALIB_PHASE_LIGHT_B; | |
| 351 | + break; | |
| 352 | + default: | |
| 353 | + return -EINVAL; | |
| 354 | + } | |
| 355 | + | |
| 356 | + return isl29501_register_write(isl29501, reg, val); | |
| 357 | +} | |
| 358 | + | |
| 359 | +static unsigned int isl29501_find_corr_exp(unsigned int val, | |
| 360 | + unsigned int max_exp, | |
| 361 | + unsigned int max_mantissa) | |
| 362 | +{ | |
| 363 | + unsigned int exp = 1; | |
| 364 | + | |
| 365 | + /* | |
| 366 | + * Correction coefficients are represented under | |
| 367 | + * mantissa * 2^exponent form, where mantissa and exponent | |
| 368 | + * are stored in two separate registers of the sensor. | |
| 369 | + * | |
| 370 | + * Compute and return the lowest exponent such as: | |
| 371 | + * mantissa = value / 2^exponent | |
| 372 | + * | |
| 373 | + * where mantissa < max_mantissa. | |
| 374 | + */ | |
| 375 | + if (val <= max_mantissa) | |
| 376 | + return 0; | |
| 377 | + | |
| 378 | + while ((val >> exp) > max_mantissa) { | |
| 379 | + exp++; | |
| 380 | + | |
| 381 | + if (exp > max_exp) | |
| 382 | + return max_exp; | |
| 383 | + } | |
| 384 | + | |
| 385 | + return exp; | |
| 386 | +} | |
| 387 | + | |
| 388 | +static ssize_t isl29501_write_ext(struct iio_dev *indio_dev, | |
| 389 | + uintptr_t private, | |
| 390 | + const struct iio_chan_spec *chan, | |
| 391 | + const char *buf, size_t len) | |
| 392 | +{ | |
| 393 | + struct isl29501_private *isl29501 = iio_priv(indio_dev); | |
| 394 | + enum isl29501_register_name reg = private; | |
| 395 | + unsigned int val; | |
| 396 | + int max_exp = 0; | |
| 397 | + int ret; | |
| 398 | + int i; | |
| 399 | + | |
| 400 | + ret = kstrtouint(buf, 10, &val); | |
| 401 | + if (ret) | |
| 402 | + return ret; | |
| 403 | + | |
| 404 | + switch (reg) { | |
| 405 | + case REG_GAIN_BIAS: | |
| 406 | + if (val > U16_MAX) | |
| 407 | + return -ERANGE; | |
| 408 | + | |
| 409 | + ret = isl29501_register_write(isl29501, reg, val); | |
| 410 | + if (ret < 0) | |
| 411 | + return ret; | |
| 412 | + | |
| 413 | + break; | |
| 414 | + case REG_CALIB_PHASE_TEMP_A: | |
| 415 | + case REG_CALIB_PHASE_TEMP_B: | |
| 416 | + case REG_CALIB_PHASE_LIGHT_A: | |
| 417 | + case REG_CALIB_PHASE_LIGHT_B: | |
| 418 | + | |
| 419 | + if (val > (U8_MAX << ISL29501_MAX_EXP_VAL)) | |
| 420 | + return -ERANGE; | |
| 421 | + | |
| 422 | + /* Store the correction coefficient under its exact form. */ | |
| 423 | + ret = isl29501_set_shadow_coeff(isl29501, reg, val); | |
| 424 | + if (ret < 0) | |
| 425 | + return ret; | |
| 426 | + | |
| 427 | + /* | |
| 428 | + * Find the highest exponent needed to represent | |
| 429 | + * correction coefficients. | |
| 430 | + */ | |
| 431 | + for (i = 0; i < COEFF_MAX; i++) { | |
| 432 | + int corr; | |
| 433 | + int corr_exp; | |
| 434 | + | |
| 435 | + corr = isl29501->shadow_coeffs[i]; | |
| 436 | + corr_exp = isl29501_find_corr_exp(corr, | |
| 437 | + ISL29501_MAX_EXP_VAL, | |
| 438 | + U8_MAX / 2); | |
| 439 | + dev_dbg(&isl29501->client->dev, | |
| 440 | + "found exp of corr(%d) = %d\n", corr, corr_exp); | |
| 441 | + | |
| 442 | + max_exp = max(max_exp, corr_exp); | |
| 443 | + } | |
| 444 | + | |
| 445 | + /* | |
| 446 | + * Represent every correction coefficient under | |
| 447 | + * mantissa * 2^max_exponent form and force the | |
| 448 | + * writing of those coefficients on the sensor. | |
| 449 | + */ | |
| 450 | + for (i = 0; i < COEFF_MAX; i++) { | |
| 451 | + int corr; | |
| 452 | + int mantissa; | |
| 453 | + | |
| 454 | + corr = isl29501->shadow_coeffs[i]; | |
| 455 | + if (!corr) | |
| 456 | + continue; | |
| 457 | + | |
| 458 | + mantissa = corr >> max_exp; | |
| 459 | + | |
| 460 | + ret = isl29501_write_coeff(isl29501, i, mantissa); | |
| 461 | + if (ret < 0) | |
| 462 | + return ret; | |
| 463 | + } | |
| 464 | + | |
| 465 | + ret = isl29501_register_write(isl29501, REG_PHASE_EXP, max_exp); | |
| 466 | + if (ret < 0) | |
| 467 | + return ret; | |
| 468 | + | |
| 469 | + break; | |
| 470 | + default: | |
| 471 | + return -EINVAL; | |
| 472 | + } | |
| 473 | + | |
| 474 | + return len; | |
| 475 | +} | |
| 476 | + | |
| 477 | +#define _ISL29501_EXT_INFO(_name, _ident) { \ | |
| 478 | + .name = _name, \ | |
| 479 | + .read = isl29501_read_ext, \ | |
| 480 | + .write = isl29501_write_ext, \ | |
| 481 | + .private = _ident, \ | |
| 482 | + .shared = IIO_SEPARATE, \ | |
| 483 | +} | |
| 484 | + | |
| 485 | +static const struct iio_chan_spec_ext_info isl29501_ext_info[] = { | |
| 486 | + _ISL29501_EXT_INFO("agc_gain", REG_GAIN), | |
| 487 | + _ISL29501_EXT_INFO("agc_gain_bias", REG_GAIN_BIAS), | |
| 488 | + _ISL29501_EXT_INFO("calib_phase_temp_a", REG_CALIB_PHASE_TEMP_A), | |
| 489 | + _ISL29501_EXT_INFO("calib_phase_temp_b", REG_CALIB_PHASE_TEMP_B), | |
| 490 | + _ISL29501_EXT_INFO("calib_phase_light_a", REG_CALIB_PHASE_LIGHT_A), | |
| 491 | + _ISL29501_EXT_INFO("calib_phase_light_b", REG_CALIB_PHASE_LIGHT_B), | |
| 492 | + { }, | |
| 493 | +}; | |
| 494 | + | |
| 495 | +#define ISL29501_DISTANCE_SCAN_INDEX 0 | |
| 496 | +#define ISL29501_TIMESTAMP_SCAN_INDEX 1 | |
| 497 | + | |
| 498 | +static const struct iio_chan_spec isl29501_channels[] = { | |
| 499 | + { | |
| 500 | + .type = IIO_PROXIMITY, | |
| 501 | + .scan_index = ISL29501_DISTANCE_SCAN_INDEX, | |
| 502 | + .info_mask_separate = | |
| 503 | + BIT(IIO_CHAN_INFO_RAW) | | |
| 504 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 505 | + BIT(IIO_CHAN_INFO_CALIBBIAS), | |
| 506 | + .scan_type = { | |
| 507 | + .sign = 'u', | |
| 508 | + .realbits = 16, | |
| 509 | + .storagebits = 16, | |
| 510 | + .endianness = IIO_CPU, | |
| 511 | + }, | |
| 512 | + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | | |
| 513 | + BIT(IIO_CHAN_INFO_SAMP_FREQ), | |
| 514 | + .ext_info = isl29501_ext_info, | |
| 515 | + }, | |
| 516 | + { | |
| 517 | + .type = IIO_PHASE, | |
| 518 | + .scan_index = -1, | |
| 519 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 520 | + BIT(IIO_CHAN_INFO_SCALE), | |
| 521 | + }, | |
| 522 | + { | |
| 523 | + .type = IIO_CURRENT, | |
| 524 | + .scan_index = -1, | |
| 525 | + .output = 1, | |
| 526 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 527 | + BIT(IIO_CHAN_INFO_SCALE), | |
| 528 | + }, | |
| 529 | + { | |
| 530 | + .type = IIO_TEMP, | |
| 531 | + .scan_index = -1, | |
| 532 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 533 | + BIT(IIO_CHAN_INFO_SCALE) | | |
| 534 | + BIT(IIO_CHAN_INFO_CALIBBIAS), | |
| 535 | + }, | |
| 536 | + { | |
| 537 | + .type = IIO_INTENSITY, | |
| 538 | + .scan_index = -1, | |
| 539 | + .modified = 1, | |
| 540 | + .channel2 = IIO_MOD_LIGHT_CLEAR, | |
| 541 | + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 542 | + BIT(IIO_CHAN_INFO_SCALE), | |
| 543 | + }, | |
| 544 | + IIO_CHAN_SOFT_TIMESTAMP(ISL29501_TIMESTAMP_SCAN_INDEX), | |
| 545 | +}; | |
| 546 | + | |
| 547 | +static int isl29501_reset_registers(struct isl29501_private *isl29501) | |
| 548 | +{ | |
| 549 | + int ret; | |
| 550 | + | |
| 551 | + ret = i2c_smbus_write_byte_data(isl29501->client, | |
| 552 | + ISL29501_COMMAND_REGISTER, | |
| 553 | + ISL29501_RESET_ALL_REGISTERS); | |
| 554 | + if (ret < 0) { | |
| 555 | + dev_err(&isl29501->client->dev, | |
| 556 | + "cannot reset registers %d\n", ret); | |
| 557 | + return ret; | |
| 558 | + } | |
| 559 | + | |
| 560 | + ret = i2c_smbus_write_byte_data(isl29501->client, | |
| 561 | + ISL29501_COMMAND_REGISTER, | |
| 562 | + ISL29501_RESET_INT_SM); | |
| 563 | + if (ret < 0) | |
| 564 | + dev_err(&isl29501->client->dev, | |
| 565 | + "cannot reset state machine %d\n", ret); | |
| 566 | + | |
| 567 | + return ret; | |
| 568 | +} | |
| 569 | + | |
| 570 | +static int isl29501_begin_acquisition(struct isl29501_private *isl29501) | |
| 571 | +{ | |
| 572 | + int ret; | |
| 573 | + | |
| 574 | + ret = i2c_smbus_write_byte_data(isl29501->client, | |
| 575 | + ISL29501_COMMAND_REGISTER, | |
| 576 | + ISL29501_EMUL_SAMPLE_START_PIN); | |
| 577 | + if (ret < 0) | |
| 578 | + dev_err(&isl29501->client->dev, | |
| 579 | + "cannot begin acquisition %d\n", ret); | |
| 580 | + | |
| 581 | + return ret; | |
| 582 | +} | |
| 583 | + | |
| 584 | +static IIO_CONST_ATTR_INT_TIME_AVAIL(ISL29501_INT_TIME_AVAILABLE); | |
| 585 | +static IIO_CONST_ATTR(out_current_scale_available, | |
| 586 | + ISL29501_CURRENT_SCALE_AVAILABLE); | |
| 587 | + | |
| 588 | +static struct attribute *isl29501_attributes[] = { | |
| 589 | + &iio_const_attr_integration_time_available.dev_attr.attr, | |
| 590 | + &iio_const_attr_out_current_scale_available.dev_attr.attr, | |
| 591 | + NULL | |
| 592 | +}; | |
| 593 | + | |
| 594 | +static const struct attribute_group isl29501_attribute_group = { | |
| 595 | + .attrs = isl29501_attributes, | |
| 596 | +}; | |
| 597 | + | |
| 598 | +static const int isl29501_current_scale_table[][2] = { | |
| 599 | + {0, 3900}, {0, 7800}, {0, 11800}, {0, 15700}, | |
| 600 | + {0, 19600}, {0, 23500}, {0, 27500}, {0, 31400}, | |
| 601 | + {0, 35200}, {0, 39200}, {0, 43100}, {0, 47100}, | |
| 602 | + {0, 51000}, {0, 54900}, {0, 58800}, | |
| 603 | +}; | |
| 604 | + | |
| 605 | +static const int isl29501_int_time[][2] = { | |
| 606 | + {0, 70}, /* 0.07 ms */ | |
| 607 | + {0, 140}, /* 0.14 ms */ | |
| 608 | + {0, 280}, /* 0.28 ms */ | |
| 609 | + {0, 570}, /* 0.57 ms */ | |
| 610 | + {0, 1140}, /* 1.14 ms */ | |
| 611 | + {0, 2280}, /* 2.28 ms */ | |
| 612 | + {0, 4550}, /* 4.55 ms */ | |
| 613 | + {0, 9100}, /* 9.11 ms */ | |
| 614 | + {0, 18200}, /* 18.2 ms */ | |
| 615 | + {0, 36400}, /* 36.4 ms */ | |
| 616 | + {0, 72810}, /* 72.81 ms */ | |
| 617 | + {0, 145610} /* 145.28 ms */ | |
| 618 | +}; | |
| 619 | + | |
| 620 | +static int isl29501_get_raw(struct isl29501_private *isl29501, | |
| 621 | + const struct iio_chan_spec *chan, | |
| 622 | + int *raw) | |
| 623 | +{ | |
| 624 | + int ret; | |
| 625 | + | |
| 626 | + switch (chan->type) { | |
| 627 | + case IIO_PROXIMITY: | |
| 628 | + ret = isl29501_register_read(isl29501, REG_DISTANCE, raw); | |
| 629 | + if (ret < 0) | |
| 630 | + return ret; | |
| 631 | + | |
| 632 | + return IIO_VAL_INT; | |
| 633 | + case IIO_INTENSITY: | |
| 634 | + ret = isl29501_register_read(isl29501, | |
| 635 | + REG_AMBIENT_LIGHT, | |
| 636 | + raw); | |
| 637 | + if (ret < 0) | |
| 638 | + return ret; | |
| 639 | + | |
| 640 | + return IIO_VAL_INT; | |
| 641 | + case IIO_PHASE: | |
| 642 | + ret = isl29501_register_read(isl29501, REG_PHASE, raw); | |
| 643 | + if (ret < 0) | |
| 644 | + return ret; | |
| 645 | + | |
| 646 | + return IIO_VAL_INT; | |
| 647 | + case IIO_CURRENT: | |
| 648 | + ret = isl29501_register_read(isl29501, REG_EMITTER_DAC, raw); | |
| 649 | + if (ret < 0) | |
| 650 | + return ret; | |
| 651 | + | |
| 652 | + return IIO_VAL_INT; | |
| 653 | + case IIO_TEMP: | |
| 654 | + ret = isl29501_register_read(isl29501, REG_TEMPERATURE, raw); | |
| 655 | + if (ret < 0) | |
| 656 | + return ret; | |
| 657 | + | |
| 658 | + return IIO_VAL_INT; | |
| 659 | + default: | |
| 660 | + return -EINVAL; | |
| 661 | + } | |
| 662 | +} | |
| 663 | + | |
| 664 | +static int isl29501_get_scale(struct isl29501_private *isl29501, | |
| 665 | + const struct iio_chan_spec *chan, | |
| 666 | + int *val, int *val2) | |
| 667 | +{ | |
| 668 | + int ret; | |
| 669 | + u32 current_scale; | |
| 670 | + | |
| 671 | + switch (chan->type) { | |
| 672 | + case IIO_PROXIMITY: | |
| 673 | + /* distance = raw_distance * 33.31 / 65536 (m) */ | |
| 674 | + *val = 3331; | |
| 675 | + *val2 = 6553600; | |
| 676 | + | |
| 677 | + return IIO_VAL_FRACTIONAL; | |
| 678 | + case IIO_PHASE: | |
| 679 | + /* phase = raw_phase * 2pi / 65536 (rad) */ | |
| 680 | + *val = 0; | |
| 681 | + *val2 = 95874; | |
| 682 | + | |
| 683 | + return IIO_VAL_INT_PLUS_NANO; | |
| 684 | + case IIO_INTENSITY: | |
| 685 | + /* light = raw_light * 35 / 10000 (mA) */ | |
| 686 | + *val = 35; | |
| 687 | + *val2 = 10000; | |
| 688 | + | |
| 689 | + return IIO_VAL_FRACTIONAL; | |
| 690 | + case IIO_CURRENT: | |
| 691 | + ret = isl29501_register_read(isl29501, | |
| 692 | + REG_DRIVER_RANGE, | |
| 693 | + ¤t_scale); | |
| 694 | + if (ret < 0) | |
| 695 | + return ret; | |
| 696 | + | |
| 697 | + if (current_scale > ARRAY_SIZE(isl29501_current_scale_table)) | |
| 698 | + return -EINVAL; | |
| 699 | + | |
| 700 | + if (!current_scale) { | |
| 701 | + *val = 0; | |
| 702 | + *val2 = 0; | |
| 703 | + return IIO_VAL_INT; | |
| 704 | + } | |
| 705 | + | |
| 706 | + *val = isl29501_current_scale_table[current_scale - 1][0]; | |
| 707 | + *val2 = isl29501_current_scale_table[current_scale - 1][1]; | |
| 708 | + | |
| 709 | + return IIO_VAL_INT_PLUS_MICRO; | |
| 710 | + case IIO_TEMP: | |
| 711 | + /* temperature = raw_temperature * 125 / 100000 (milli °C) */ | |
| 712 | + *val = 125; | |
| 713 | + *val2 = 100000; | |
| 714 | + | |
| 715 | + return IIO_VAL_FRACTIONAL; | |
| 716 | + default: | |
| 717 | + return -EINVAL; | |
| 718 | + } | |
| 719 | +} | |
| 720 | + | |
| 721 | +static int isl29501_get_calibbias(struct isl29501_private *isl29501, | |
| 722 | + const struct iio_chan_spec *chan, | |
| 723 | + int *bias) | |
| 724 | +{ | |
| 725 | + switch (chan->type) { | |
| 726 | + case IIO_PROXIMITY: | |
| 727 | + return isl29501_register_read(isl29501, | |
| 728 | + REG_DISTANCE_BIAS, | |
| 729 | + bias); | |
| 730 | + case IIO_TEMP: | |
| 731 | + return isl29501_register_read(isl29501, | |
| 732 | + REG_TEMPERATURE_BIAS, | |
| 733 | + bias); | |
| 734 | + default: | |
| 735 | + return -EINVAL; | |
| 736 | + } | |
| 737 | +} | |
| 738 | + | |
| 739 | +static int isl29501_get_inttime(struct isl29501_private *isl29501, | |
| 740 | + int *val, int *val2) | |
| 741 | +{ | |
| 742 | + int ret; | |
| 743 | + u32 inttime; | |
| 744 | + | |
| 745 | + ret = isl29501_register_read(isl29501, REG_INT_TIME, &inttime); | |
| 746 | + if (ret < 0) | |
| 747 | + return ret; | |
| 748 | + | |
| 749 | + if (inttime >= ARRAY_SIZE(isl29501_int_time)) | |
| 750 | + return -EINVAL; | |
| 751 | + | |
| 752 | + *val = isl29501_int_time[inttime][0]; | |
| 753 | + *val2 = isl29501_int_time[inttime][1]; | |
| 754 | + | |
| 755 | + return IIO_VAL_INT_PLUS_MICRO; | |
| 756 | +} | |
| 757 | + | |
| 758 | +static int isl29501_get_freq(struct isl29501_private *isl29501, | |
| 759 | + int *val, int *val2) | |
| 760 | +{ | |
| 761 | + int ret; | |
| 762 | + int sample_time; | |
| 763 | + unsigned long long freq; | |
| 764 | + u32 temp; | |
| 765 | + | |
| 766 | + ret = isl29501_register_read(isl29501, REG_SAMPLE_TIME, &sample_time); | |
| 767 | + if (ret < 0) | |
| 768 | + return ret; | |
| 769 | + | |
| 770 | + /* freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */ | |
| 771 | + freq = 1000000ULL * 1000000ULL; | |
| 772 | + | |
| 773 | + do_div(freq, 450 * (sample_time + 1)); | |
| 774 | + | |
| 775 | + temp = do_div(freq, 1000000); | |
| 776 | + *val = freq; | |
| 777 | + *val2 = temp; | |
| 778 | + | |
| 779 | + return IIO_VAL_INT_PLUS_MICRO; | |
| 780 | +} | |
| 781 | + | |
| 782 | +static int isl29501_read_raw(struct iio_dev *indio_dev, | |
| 783 | + struct iio_chan_spec const *chan, int *val, | |
| 784 | + int *val2, long mask) | |
| 785 | +{ | |
| 786 | + struct isl29501_private *isl29501 = iio_priv(indio_dev); | |
| 787 | + | |
| 788 | + switch (mask) { | |
| 789 | + case IIO_CHAN_INFO_RAW: | |
| 790 | + return isl29501_get_raw(isl29501, chan, val); | |
| 791 | + case IIO_CHAN_INFO_SCALE: | |
| 792 | + return isl29501_get_scale(isl29501, chan, val, val2); | |
| 793 | + case IIO_CHAN_INFO_INT_TIME: | |
| 794 | + return isl29501_get_inttime(isl29501, val, val2); | |
| 795 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 796 | + return isl29501_get_freq(isl29501, val, val2); | |
| 797 | + case IIO_CHAN_INFO_CALIBBIAS: | |
| 798 | + return isl29501_get_calibbias(isl29501, chan, val); | |
| 799 | + default: | |
| 800 | + return -EINVAL; | |
| 801 | + } | |
| 802 | +} | |
| 803 | + | |
| 804 | +static int isl29501_set_raw(struct isl29501_private *isl29501, | |
| 805 | + const struct iio_chan_spec *chan, | |
| 806 | + int raw) | |
| 807 | +{ | |
| 808 | + switch (chan->type) { | |
| 809 | + case IIO_CURRENT: | |
| 810 | + return isl29501_register_write(isl29501, REG_EMITTER_DAC, raw); | |
| 811 | + default: | |
| 812 | + return -EINVAL; | |
| 813 | + } | |
| 814 | +} | |
| 815 | + | |
| 816 | +static int isl29501_set_inttime(struct isl29501_private *isl29501, | |
| 817 | + int val, int val2) | |
| 818 | +{ | |
| 819 | + int i; | |
| 820 | + | |
| 821 | + for (i = 0; i < ARRAY_SIZE(isl29501_int_time); i++) { | |
| 822 | + if (isl29501_int_time[i][0] == val && | |
| 823 | + isl29501_int_time[i][1] == val2) { | |
| 824 | + return isl29501_register_write(isl29501, | |
| 825 | + REG_INT_TIME, | |
| 826 | + i); | |
| 827 | + } | |
| 828 | + } | |
| 829 | + | |
| 830 | + return -EINVAL; | |
| 831 | +} | |
| 832 | + | |
| 833 | +static int isl29501_set_scale(struct isl29501_private *isl29501, | |
| 834 | + const struct iio_chan_spec *chan, | |
| 835 | + int val, int val2) | |
| 836 | +{ | |
| 837 | + int i; | |
| 838 | + | |
| 839 | + if (chan->type != IIO_CURRENT) | |
| 840 | + return -EINVAL; | |
| 841 | + | |
| 842 | + for (i = 0; i < ARRAY_SIZE(isl29501_current_scale_table); i++) { | |
| 843 | + if (isl29501_current_scale_table[i][0] == val && | |
| 844 | + isl29501_current_scale_table[i][1] == val2) { | |
| 845 | + return isl29501_register_write(isl29501, | |
| 846 | + REG_DRIVER_RANGE, | |
| 847 | + i + 1); | |
| 848 | + } | |
| 849 | + } | |
| 850 | + | |
| 851 | + return -EINVAL; | |
| 852 | +} | |
| 853 | + | |
| 854 | +static int isl29501_set_calibbias(struct isl29501_private *isl29501, | |
| 855 | + const struct iio_chan_spec *chan, | |
| 856 | + int bias) | |
| 857 | +{ | |
| 858 | + switch (chan->type) { | |
| 859 | + case IIO_PROXIMITY: | |
| 860 | + return isl29501_register_write(isl29501, | |
| 861 | + REG_DISTANCE_BIAS, | |
| 862 | + bias); | |
| 863 | + case IIO_TEMP: | |
| 864 | + return isl29501_register_write(isl29501, | |
| 865 | + REG_TEMPERATURE_BIAS, | |
| 866 | + bias); | |
| 867 | + default: | |
| 868 | + return -EINVAL; | |
| 869 | + } | |
| 870 | +} | |
| 871 | + | |
| 872 | +static int isl29501_set_freq(struct isl29501_private *isl29501, | |
| 873 | + int val, int val2) | |
| 874 | +{ | |
| 875 | + int freq; | |
| 876 | + unsigned long long sample_time; | |
| 877 | + | |
| 878 | + /* sample_freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */ | |
| 879 | + freq = val * 1000000 + val2 % 1000000; | |
| 880 | + sample_time = 2222ULL * 1000000ULL; | |
| 881 | + do_div(sample_time, freq); | |
| 882 | + | |
| 883 | + sample_time -= 1; | |
| 884 | + | |
| 885 | + if (sample_time > 255) | |
| 886 | + return -ERANGE; | |
| 887 | + | |
| 888 | + return isl29501_register_write(isl29501, REG_SAMPLE_TIME, sample_time); | |
| 889 | +} | |
| 890 | + | |
| 891 | +static int isl29501_write_raw(struct iio_dev *indio_dev, | |
| 892 | + struct iio_chan_spec const *chan, | |
| 893 | + int val, int val2, long mask) | |
| 894 | +{ | |
| 895 | + struct isl29501_private *isl29501 = iio_priv(indio_dev); | |
| 896 | + | |
| 897 | + switch (mask) { | |
| 898 | + case IIO_CHAN_INFO_RAW: | |
| 899 | + return isl29501_set_raw(isl29501, chan, val); | |
| 900 | + case IIO_CHAN_INFO_INT_TIME: | |
| 901 | + return isl29501_set_inttime(isl29501, val, val2); | |
| 902 | + case IIO_CHAN_INFO_SAMP_FREQ: | |
| 903 | + return isl29501_set_freq(isl29501, val, val2); | |
| 904 | + case IIO_CHAN_INFO_SCALE: | |
| 905 | + return isl29501_set_scale(isl29501, chan, val, val2); | |
| 906 | + case IIO_CHAN_INFO_CALIBBIAS: | |
| 907 | + return isl29501_set_calibbias(isl29501, chan, val); | |
| 908 | + default: | |
| 909 | + return -EINVAL; | |
| 910 | + } | |
| 911 | +} | |
| 912 | + | |
| 913 | +static const struct iio_info isl29501_info = { | |
| 914 | + .read_raw = &isl29501_read_raw, | |
| 915 | + .write_raw = &isl29501_write_raw, | |
| 916 | + .attrs = &isl29501_attribute_group, | |
| 917 | +}; | |
| 918 | + | |
| 919 | +static int isl29501_init_chip(struct isl29501_private *isl29501) | |
| 920 | +{ | |
| 921 | + int ret; | |
| 922 | + | |
| 923 | + ret = i2c_smbus_read_byte_data(isl29501->client, ISL29501_DEVICE_ID); | |
| 924 | + if (ret < 0) { | |
| 925 | + dev_err(&isl29501->client->dev, "Error reading device id\n"); | |
| 926 | + return ret; | |
| 927 | + } | |
| 928 | + | |
| 929 | + if (ret != ISL29501_ID) { | |
| 930 | + dev_err(&isl29501->client->dev, | |
| 931 | + "Wrong chip id, got %x expected %x\n", | |
| 932 | + ret, ISL29501_DEVICE_ID); | |
| 933 | + return -ENODEV; | |
| 934 | + } | |
| 935 | + | |
| 936 | + ret = isl29501_reset_registers(isl29501); | |
| 937 | + if (ret < 0) | |
| 938 | + return ret; | |
| 939 | + | |
| 940 | + return isl29501_begin_acquisition(isl29501); | |
| 941 | +} | |
| 942 | + | |
| 943 | +static irqreturn_t isl29501_trigger_handler(int irq, void *p) | |
| 944 | +{ | |
| 945 | + struct iio_poll_func *pf = p; | |
| 946 | + struct iio_dev *indio_dev = pf->indio_dev; | |
| 947 | + struct isl29501_private *isl29501 = iio_priv(indio_dev); | |
| 948 | + const unsigned long *active_mask = indio_dev->active_scan_mask; | |
| 949 | + u32 buffer[4] = {}; /* 1x16-bit + ts */ | |
| 950 | + | |
| 951 | + if (test_bit(ISL29501_DISTANCE_SCAN_INDEX, active_mask)) | |
| 952 | + isl29501_register_read(isl29501, REG_DISTANCE, buffer); | |
| 953 | + | |
| 954 | + iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp); | |
| 955 | + iio_trigger_notify_done(indio_dev->trig); | |
| 956 | + | |
| 957 | + return IRQ_HANDLED; | |
| 958 | +} | |
| 959 | + | |
| 960 | +static int isl29501_probe(struct i2c_client *client, | |
| 961 | + const struct i2c_device_id *id) | |
| 962 | +{ | |
| 963 | + struct iio_dev *indio_dev; | |
| 964 | + struct isl29501_private *isl29501; | |
| 965 | + int ret; | |
| 966 | + | |
| 967 | + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*isl29501)); | |
| 968 | + if (!indio_dev) | |
| 969 | + return -ENOMEM; | |
| 970 | + | |
| 971 | + isl29501 = iio_priv(indio_dev); | |
| 972 | + | |
| 973 | + i2c_set_clientdata(client, indio_dev); | |
| 974 | + isl29501->client = client; | |
| 975 | + | |
| 976 | + mutex_init(&isl29501->lock); | |
| 977 | + | |
| 978 | + ret = isl29501_init_chip(isl29501); | |
| 979 | + if (ret < 0) | |
| 980 | + return ret; | |
| 981 | + | |
| 982 | + indio_dev->modes = INDIO_DIRECT_MODE; | |
| 983 | + indio_dev->dev.parent = &client->dev; | |
| 984 | + indio_dev->channels = isl29501_channels; | |
| 985 | + indio_dev->num_channels = ARRAY_SIZE(isl29501_channels); | |
| 986 | + indio_dev->name = client->name; | |
| 987 | + indio_dev->info = &isl29501_info; | |
| 988 | + | |
| 989 | + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, | |
| 990 | + iio_pollfunc_store_time, | |
| 991 | + isl29501_trigger_handler, | |
| 992 | + NULL); | |
| 993 | + if (ret < 0) { | |
| 994 | + dev_err(&client->dev, "unable to setup iio triggered buffer\n"); | |
| 995 | + return ret; | |
| 996 | + } | |
| 997 | + | |
| 998 | + return devm_iio_device_register(&client->dev, indio_dev); | |
| 999 | +} | |
| 1000 | + | |
| 1001 | +static const struct i2c_device_id isl29501_id[] = { | |
| 1002 | + {"isl29501", 0}, | |
| 1003 | + {} | |
| 1004 | +}; | |
| 1005 | + | |
| 1006 | +MODULE_DEVICE_TABLE(i2c, isl29501_id); | |
| 1007 | + | |
| 1008 | +#if defined(CONFIG_OF) | |
| 1009 | +static const struct of_device_id isl29501_i2c_matches[] = { | |
| 1010 | + { .compatible = "renesas,isl29501" }, | |
| 1011 | + { } | |
| 1012 | +}; | |
| 1013 | +MODULE_DEVICE_TABLE(of, isl29501_i2c_matches); | |
| 1014 | +#endif | |
| 1015 | + | |
| 1016 | +static struct i2c_driver isl29501_driver = { | |
| 1017 | + .driver = { | |
| 1018 | + .name = "isl29501", | |
| 1019 | + }, | |
| 1020 | + .id_table = isl29501_id, | |
| 1021 | + .probe = isl29501_probe, | |
| 1022 | +}; | |
| 1023 | +module_i2c_driver(isl29501_driver); | |
| 1024 | + | |
| 1025 | +MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); | |
| 1026 | +MODULE_DESCRIPTION("ISL29501 Time of Flight sensor driver"); | |
| 1027 | +MODULE_LICENSE("GPL v2"); | |
| @@ -116,45 +116,26 @@ static struct ad5933_platform_data ad5933_default_pdata = { · 115 unmodified lines | ||
| 116 | 116 | .vref_mv = 3300, |
| 117 | 117 | }; |
| 118 | 118 | |
| 119 | +#define AD5933_CHANNEL(_type, _extend_name, _info_mask_separate, _address, \ | |
| 120 | + _scan_index, _realbits) { \ | |
| 121 | + .type = (_type), \ | |
| 122 | + .extend_name = (_extend_name), \ | |
| 123 | + .info_mask_separate = (_info_mask_separate), \ | |
| 124 | + .address = (_address), \ | |
| 125 | + .scan_index = (_scan_index), \ | |
| 126 | + .scan_type = { \ | |
| 127 | + .sign = 's', \ | |
| 128 | + .realbits = (_realbits), \ | |
| 129 | + .storagebits = 16, \ | |
| 130 | + }, \ | |
| 131 | +} | |
| 132 | + | |
| 119 | 133 | static const struct iio_chan_spec ad5933_channels[] = { |
| 120 | − { | |
| 121 | − .type = IIO_TEMP, | |
| 122 | − .indexed = 1, | |
| 123 | − .channel = 0, | |
| 124 | − .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| 125 | − BIT(IIO_CHAN_INFO_SCALE), | |
| 126 | − .address = AD5933_REG_TEMP_DATA, | |
| 127 | − .scan_index = -1, | |
| 128 | − .scan_type = { | |
| 129 | − .sign = 's', | |
| 130 | − .realbits = 14, | |
| 131 | − .storagebits = 16, | |
| 132 | − }, | |
| 133 | − }, { /* Ring Channels */ | |
| 134 | − .type = IIO_VOLTAGE, | |
| 135 | − .indexed = 1, | |
| 136 | − .channel = 0, | |
| 137 | − .extend_name = "real", | |
| 138 | − .address = AD5933_REG_REAL_DATA, | |
| 139 | − .scan_index = 0, | |
| 140 | − .scan_type = { | |
| 141 | − .sign = 's', | |
| 142 | − .realbits = 16, | |
| 143 | − .storagebits = 16, | |
| 144 | − }, | |
| 145 | − }, { | |
| 146 | − .type = IIO_VOLTAGE, | |
| 147 | − .indexed = 1, | |
| 148 | − .channel = 0, | |
| 149 | − .extend_name = "imag", | |
| 150 | − .address = AD5933_REG_IMAG_DATA, | |
| 151 | − .scan_index = 1, | |
| 152 | − .scan_type = { | |
| 153 | − .sign = 's', | |
| 154 | − .realbits = 16, | |
| 155 | − .storagebits = 16, | |
| 156 | − }, | |
| 157 | − }, | |
| 134 | + AD5933_CHANNEL(IIO_TEMP, NULL, BIT(IIO_CHAN_INFO_RAW) | | |
| 135 | + BIT(IIO_CHAN_INFO_SCALE), AD5933_REG_TEMP_DATA, -1, 14), | |
| 136 | + /* Ring Channels */ | |
| 137 | + AD5933_CHANNEL(IIO_VOLTAGE, "real", 0, AD5933_REG_REAL_DATA, 0, 16), | |
| 138 | + AD5933_CHANNEL(IIO_VOLTAGE, "imag", 0, AD5933_REG_IMAG_DATA, 1, 16), | |
| 158 | 139 | }; |
| 159 | 140 | |
| 160 | 141 | static int ad5933_i2c_write(struct i2c_client *client, u8 reg, u8 len, u8 *data) |
| @@ -45,6 +45,7 @@ enum iio_chan_type { · 44 unmodified lines | ||
| 45 | 45 | IIO_INDEX, |
| 46 | 46 | IIO_GRAVITY, |
| 47 | 47 | IIO_POSITIONRELATIVE, |
| 48 | + IIO_PHASE, | |
| 48 | 49 | }; |
| 49 | 50 | |
| 50 | 51 | enum iio_modifier { |
| @@ -85,6 +86,7 @@ enum iio_modifier { · 34 unmodified lines | ||
| 85 | 86 | IIO_MOD_CO2, |
| 86 | 87 | IIO_MOD_VOC, |
| 87 | 88 | IIO_MOD_LIGHT_UV, |
| 89 | + IIO_MOD_LIGHT_DUV, | |
| 88 | 90 | }; |
| 89 | 91 | |
| 90 | 92 | enum iio_event_type { |
| @@ -59,6 +59,7 @@ static const char * const iio_chan_type_name_spec[] = { · 58 unmodified lines | ||
| 59 | 59 | [IIO_UVINDEX] = "uvindex", |
| 60 | 60 | [IIO_GRAVITY] = "gravity", |
| 61 | 61 | [IIO_POSITIONRELATIVE] = "positionrelative", |
| 62 | + [IIO_PHASE] = "phase", | |
| 62 | 63 | }; |
| 63 | 64 | |
| 64 | 65 | static const char * const iio_ev_type_text[] = { |
| @@ -97,6 +98,7 @@ static const char * const iio_modifier_names[] = { · 32 unmodified lines | ||
| 97 | 98 | [IIO_MOD_LIGHT_GREEN] = "green", |
| 98 | 99 | [IIO_MOD_LIGHT_BLUE] = "blue", |
| 99 | 100 | [IIO_MOD_LIGHT_UV] = "uv", |
| 101 | + [IIO_MOD_LIGHT_DUV] = "duv", | |
| 100 | 102 | [IIO_MOD_QUATERNION] = "quaternion", |
| 101 | 103 | [IIO_MOD_TEMP_AMBIENT] = "ambient", |
| 102 | 104 | [IIO_MOD_TEMP_OBJECT] = "object", |
| @@ -153,6 +155,7 @@ static bool event_is_known(struct iio_event_data *event) · 50 unmodified lines | ||
| 153 | 155 | case IIO_UVINDEX: |
| 154 | 156 | case IIO_GRAVITY: |
| 155 | 157 | case IIO_POSITIONRELATIVE: |
| 158 | + case IIO_PHASE: | |
| 156 | 159 | break; |
| 157 | 160 | default: |
| 158 | 161 | return false; |
| @@ -180,6 +183,7 @@ static bool event_is_known(struct iio_event_data *event) · 21 unmodified lines | ||
| 180 | 183 | case IIO_MOD_LIGHT_GREEN: |
| 181 | 184 | case IIO_MOD_LIGHT_BLUE: |
| 182 | 185 | case IIO_MOD_LIGHT_UV: |
| 186 | + case IIO_MOD_LIGHT_DUV: | |
| 183 | 187 | case IIO_MOD_QUATERNION: |
| 184 | 188 | case IIO_MOD_TEMP_AMBIENT: |
| 185 | 189 | case IIO_MOD_TEMP_OBJECT: |