mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-16 05:50:19 +00:00
First round of IIO new device support, features and cleanups for the 4.14 cycle.
4 completely new drivers in this set and plenty of other stuff. One ABI change due to a silly mistake a long time back. Hopefully no one will notice. It effects the numerical order of consumer device channels which was the reverse of the obvious. It's going the slow way to allow us some margin to spot if we have broken userspace or not (seems unlikely) New Device Support * ccs811 - new driver for the Volatile Organic Compounds (VOC) sensor. * dln2 adc - new driver for the ADC on this flexible usb board. * EP93xx - new driver for this Cirrus logic SoC ADC. * ltc2471 - new ADC driver support the ltc2471 and ltc2473 * st_accel - add trivial table entries to support H3LIS331DL, LIS331DL, LIS3LV02DL. * st_gyro - add L3GD20H support (again) having fixed the various things that were broken in the first try. Includes devicetree binding. * stm32 dac - add support for the DACs in the STM32F4 series Features * Documentation - add missing power attribute documentation to the ABI docs. * at91-sama5d2 - add hardware trigger and buffered capture support with bindings. - suspend and resume functionality. * bmc150 - support for the BOSC0200 ACPI device id seen on some tablets. * hdc100x - devicetree bindings - document supported devices - match table and device ids. * hts221 - support active low interrupts (with bindings) - open drain mode with bindings. * htu21 - OF match table and bindings. * lsm6dsx - open drain mode with bindings * ltc2497 - add support for board file based consumer mapping. * ms5367 - OF match table and bindings. * mt7622 - binding document and OF match table. - suspend and resume support. * rpr0521 - triggered buffer support. * tsys01 - OF match table and bindings. Cleanups and minor fixes * core - fix ordering of IIO channels to entry numbers when using iio_map_array_register rather than reversing them. - use the new %pOF format specifier rather than full name for the device tree nodes. * ad7280a - fix potential issue with macro argument reuse. * ad7766 - drop a pointless NULL value check as it's done in the gpiod code. * adis16400 - unsigned -> unsigned int. * at91 adc - make some init data static to reduce code size. * at91-sama5d2 ADC - make some init data static to reduce code size. * da311 - make some init data static to reduce code size. * hid-sensor-rotation - drop an unnecessary static. * hts221 - refactor the write_with_mask code. - move the BDU configuration to probe time as there is no reason for it to change. - avoid overwriting reserved data during power-down. This is a fix, but the infrastructure need was too invasive to send it to mainline except in a merge window. It's not a regression as it was always wrong. - avoid reconfigure the sampling frequency multiple times by just doing it in the write_raw function directly. - refactor the power_on/off calls into a set_enable. - move the dry-enable logic into trig_set_state as that is the only place it was used. * ina219 - fix polling of ina226 conversion ready flag. * imx7d - add vendor name in kconfig for consistency with similar parts. * mcp3422 - Change initial channel to 0 as it feels more logical. - Check for some errors in probe. * meson-saradc - add a check of of_match_device return value. * mpu3050 - allow open drain for any interrupt type. * rockchip adc - add check on of_match_device return value. * sca3000 - drop a trailing whitespace. * stm32 adc - make array stm32h7_adc_ckmodes_spec static. * stm32 dac - fix an error message. * stm32 timers - fix clock name in docs to match reality after changes. * st_accel - explicit OF table (spi). - add missing entries to OF table (i2c). - rename of_device_id table to drop the part name. - adding missing lis3l02dq entry to bindings. - rename H3LIS331DL_DRIVER_NAME to line up with similar entries in driver. * st_gyro - explicit OF table (spi). * st_magn - explicit OF table (spi). - enable multiread for lis3mdl. * st_pressure - explicit OF table (spi). * st_sensors common. - move st_sensors_of_i2c_probe and rename to make it available for spi drivers. * tsc3472 - don't write an extra byte when writing the ATIME register. - add a link to the datasheet. * tsl2x7x - continued staging cleanups - add of_match_table. - drop redundant power_state sysfs attribute. - drop wrapper tsl2x7x_i2c_read. - clean up i2c calls made in tsl2x7x_als_calibrate. - refactor the read and write _event_value callbacks to handle additional elements. - use usleep_range instead of mdelay. - check return value from tsl2x7x_invoke_change. * zpa2326 - add some newline to the end of logging macros. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAll3oWIRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FoimeBAAs2WBPr8kQZeDfdjNS5isk+GoBJ4btHDL +BpBODylDku/WIJb7hpBymH2Xs/7qUuYLYwz0P1XVrSo/1kK+krJQR0DUwcracfy gqMS4KEmgWbhmBV2ksPfCGcoT5rimxLUqpka5+WWAszNtwi7YOt5FadTb9yK4WdG Di5AzaVLKAUBpQrrHdFPXewxenVs1P/X0ES7fSNU1SIL2bRAaPDj9duu3URivt9l XRh7qqNpuNMIQ3MmeEeLDJkyeeeWHYdnps/XDfW0i5VElxwZImTPD+AFAoc2E51J pujvXlu1a6FgMH+hp7hbOxNuf3eKlIq9mrfGre4K6DkTB0gro3oU2bCa5BEq/be1 PrKQZsfkK0KmrLCh0UqwTTcWdorOfussWpZ6Ib4/l4JQEeII/odwyZJ3vHNlm0Gy 0n/TVfNVQEY2zLswWdUOaQ2bvLGaXoeIBH0sMtCPOKks/4u692qZg3LAC1g5mEKF 4ykKG8oJ/UvWcqb00Z1H1qkT+B0ZmOEZzf6M7rFlPKr48DHrbu1YZcoaPYoBxeWP nL9S0zdygs06HI3/5Rl4Vv7HMTCbKabOp7eamW3IDqRpcW6dLzsXrG/e3YFbvMxq sGfd3g8jXaf1rlA9qP5FOHdQM2ySoi/WliwShH46LTY17sHSwQd46FYDKn4ZMT4q 6oMBnYOeadg= =imPz -----END PGP SIGNATURE----- Merge tag 'iio-for-4.14a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of IIO new device support, features and cleanups for the 4.14 cycle. 4 completely new drivers in this set and plenty of other stuff. One ABI change due to a silly mistake a long time back. Hopefully no one will notice. It effects the numerical order of consumer device channels which was the reverse of the obvious. It's going the slow way to allow us some margin to spot if we have broken userspace or not (seems unlikely) New Device Support * ccs811 - new driver for the Volatile Organic Compounds (VOC) sensor. * dln2 adc - new driver for the ADC on this flexible usb board. * EP93xx - new driver for this Cirrus logic SoC ADC. * ltc2471 - new ADC driver support the ltc2471 and ltc2473 * st_accel - add trivial table entries to support H3LIS331DL, LIS331DL, LIS3LV02DL. * st_gyro - add L3GD20H support (again) having fixed the various things that were broken in the first try. Includes devicetree binding. * stm32 dac - add support for the DACs in the STM32F4 series Features * Documentation - add missing power attribute documentation to the ABI docs. * at91-sama5d2 - add hardware trigger and buffered capture support with bindings. - suspend and resume functionality. * bmc150 - support for the BOSC0200 ACPI device id seen on some tablets. * hdc100x - devicetree bindings - document supported devices - match table and device ids. * hts221 - support active low interrupts (with bindings) - open drain mode with bindings. * htu21 - OF match table and bindings. * lsm6dsx - open drain mode with bindings * ltc2497 - add support for board file based consumer mapping. * ms5367 - OF match table and bindings. * mt7622 - binding document and OF match table. - suspend and resume support. * rpr0521 - triggered buffer support. * tsys01 - OF match table and bindings. Cleanups and minor fixes * core - fix ordering of IIO channels to entry numbers when using iio_map_array_register rather than reversing them. - use the new %pOF format specifier rather than full name for the device tree nodes. * ad7280a - fix potential issue with macro argument reuse. * ad7766 - drop a pointless NULL value check as it's done in the gpiod code. * adis16400 - unsigned -> unsigned int. * at91 adc - make some init data static to reduce code size. * at91-sama5d2 ADC - make some init data static to reduce code size. * da311 - make some init data static to reduce code size. * hid-sensor-rotation - drop an unnecessary static. * hts221 - refactor the write_with_mask code. - move the BDU configuration to probe time as there is no reason for it to change. - avoid overwriting reserved data during power-down. This is a fix, but the infrastructure need was too invasive to send it to mainline except in a merge window. It's not a regression as it was always wrong. - avoid reconfigure the sampling frequency multiple times by just doing it in the write_raw function directly. - refactor the power_on/off calls into a set_enable. - move the dry-enable logic into trig_set_state as that is the only place it was used. * ina219 - fix polling of ina226 conversion ready flag. * imx7d - add vendor name in kconfig for consistency with similar parts. * mcp3422 - Change initial channel to 0 as it feels more logical. - Check for some errors in probe. * meson-saradc - add a check of of_match_device return value. * mpu3050 - allow open drain for any interrupt type. * rockchip adc - add check on of_match_device return value. * sca3000 - drop a trailing whitespace. * stm32 adc - make array stm32h7_adc_ckmodes_spec static. * stm32 dac - fix an error message. * stm32 timers - fix clock name in docs to match reality after changes. * st_accel - explicit OF table (spi). - add missing entries to OF table (i2c). - rename of_device_id table to drop the part name. - adding missing lis3l02dq entry to bindings. - rename H3LIS331DL_DRIVER_NAME to line up with similar entries in driver. * st_gyro - explicit OF table (spi). * st_magn - explicit OF table (spi). - enable multiread for lis3mdl. * st_pressure - explicit OF table (spi). * st_sensors common. - move st_sensors_of_i2c_probe and rename to make it available for spi drivers. * tsc3472 - don't write an extra byte when writing the ATIME register. - add a link to the datasheet. * tsl2x7x - continued staging cleanups - add of_match_table. - drop redundant power_state sysfs attribute. - drop wrapper tsl2x7x_i2c_read. - clean up i2c calls made in tsl2x7x_als_calibrate. - refactor the read and write _event_value callbacks to handle additional elements. - use usleep_range instead of mdelay. - check return value from tsl2x7x_invoke_change. * zpa2326 - add some newline to the end of logging macros.
This commit is contained in:
commit
98c2f10d23
@ -119,6 +119,15 @@ Description:
|
||||
unique to allow association with event codes. Units after
|
||||
application of scale and offset are milliamps.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_raw
|
||||
KernelVersion: 4.5
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled no bias removal etc.) power measurement from
|
||||
channel Y. The number must always be specified and
|
||||
unique to allow association with event codes. Units after
|
||||
application of scale and offset are milliwatts.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
|
||||
KernelVersion: 3.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -11,6 +11,11 @@ Required properties:
|
||||
- atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
|
||||
- atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
|
||||
- atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
|
||||
- atmel,trigger-edge-type: One of possible edge types for the ADTRG hardware
|
||||
trigger pin. When the specific edge type is detected, the conversion will
|
||||
start. Possible values are rising, falling, or both.
|
||||
This property uses the IRQ edge types values: IRQ_TYPE_EDGE_RISING ,
|
||||
IRQ_TYPE_EDGE_FALLING or IRQ_TYPE_EDGE_BOTH
|
||||
|
||||
Example:
|
||||
|
||||
@ -25,4 +30,5 @@ adc: adc@fc030000 {
|
||||
atmel,startup-time-ms = <4>;
|
||||
vddana-supply = <&vdd_3v3_lp_reg>;
|
||||
vref-supply = <&vdd_3v3_lp_reg>;
|
||||
atmel,trigger-edge-type = <IRQ_TYPE_EDGE_BOTH>;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ for the Thermal Controller which holds a phandle to the AUXADC.
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-auxadc": For MT2701 family of SoCs
|
||||
- "mediatek,mt7622-auxadc": For MT7622 family of SoCs
|
||||
- "mediatek,mt8173-auxadc": For MT8173 family of SoCs
|
||||
- reg: Address range of the AUXADC unit.
|
||||
- clocks: Should contain a clock specifier for each entry in clock-names
|
||||
|
@ -10,7 +10,9 @@ current.
|
||||
Contents of a stm32 dac root node:
|
||||
-----------------------------------
|
||||
Required properties:
|
||||
- compatible: Must be "st,stm32h7-dac-core".
|
||||
- compatible: Should be one of:
|
||||
"st,stm32f4-dac-core"
|
||||
"st,stm32h7-dac-core"
|
||||
- reg: Offset and length of the device's register set.
|
||||
- clocks: Must contain an entry for pclk (which feeds the peripheral bus
|
||||
interface)
|
||||
|
17
Documentation/devicetree/bindings/iio/humidity/hdc100x.txt
Normal file
17
Documentation/devicetree/bindings/iio/humidity/hdc100x.txt
Normal file
@ -0,0 +1,17 @@
|
||||
* HDC100x temperature + humidity sensors
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain one of the following:
|
||||
ti,hdc1000
|
||||
ti,hdc1008
|
||||
ti,hdc1010
|
||||
ti,hdc1050
|
||||
ti,hdc1080
|
||||
- reg: i2c address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
hdc100x@40 {
|
||||
compatible = "ti,hdc1000";
|
||||
reg = <0x40>;
|
||||
};
|
@ -5,9 +5,18 @@ Required properties:
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
- drive-open-drain: the interrupt/data ready line will be configured
|
||||
as open drain, which is useful if several sensors share the same
|
||||
interrupt line. This is a boolean property.
|
||||
If the requested interrupt is configured as IRQ_TYPE_LEVEL_HIGH or
|
||||
IRQ_TYPE_EDGE_RISING a pull-down resistor is needed to drive the line
|
||||
when it is not active, whereas a pull-up one is needed when interrupt
|
||||
line is configured as IRQ_TYPE_LEVEL_LOW or IRQ_TYPE_EDGE_FALLING.
|
||||
Refer to pinctrl/pinctrl-bindings.txt for the property description.
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
|
||||
flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
||||
IRQ_TYPE_EDGE_FALLING.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt
|
||||
client node bindings.
|
||||
|
13
Documentation/devicetree/bindings/iio/humidity/htu21.txt
Normal file
13
Documentation/devicetree/bindings/iio/humidity/htu21.txt
Normal file
@ -0,0 +1,13 @@
|
||||
*HTU21 - Measurement-Specialties htu21 temperature & humidity sensor and humidity part of MS8607 sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "meas,htu21" or "meas,ms8607-humidity"
|
||||
- reg: I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
htu21@40 {
|
||||
compatible = "meas,htu21";
|
||||
reg = <0x40>;
|
||||
};
|
@ -11,6 +11,14 @@ Required properties:
|
||||
Optional properties:
|
||||
- st,drdy-int-pin: the pin on the package that will be used to signal
|
||||
"data ready" (valid values: 1 or 2).
|
||||
- drive-open-drain: the interrupt/data ready line will be configured
|
||||
as open drain, which is useful if several sensors share the same
|
||||
interrupt line. This is a boolean property.
|
||||
(This binding is taken from pinctrl/pinctrl-bindings.txt)
|
||||
If the requested interrupt is configured as IRQ_TYPE_LEVEL_HIGH or
|
||||
IRQ_TYPE_EDGE_RISING a pull-down resistor is needed to drive the line
|
||||
when it is not active, whereas a pull-up one is needed when interrupt
|
||||
line is configured as IRQ_TYPE_LEVEL_LOW or IRQ_TYPE_EDGE_FALLING.
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
||||
|
17
Documentation/devicetree/bindings/iio/pressure/ms5637.txt
Normal file
17
Documentation/devicetree/bindings/iio/pressure/ms5637.txt
Normal file
@ -0,0 +1,17 @@
|
||||
* MS5637 - Measurement-Specialties MS5637, MS5805, MS5837 and MS8607 pressure & temperature sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
-compatible: should be one of the following
|
||||
meas,ms5637
|
||||
meas,ms5805
|
||||
meas,ms5837
|
||||
meas,ms8607-temppressure
|
||||
-reg: I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
ms5637@76 {
|
||||
compatible = "meas,ms5637";
|
||||
reg = <0x76>;
|
||||
};
|
@ -45,6 +45,7 @@ Accelerometers:
|
||||
- st,lis2dh12-accel
|
||||
- st,h3lis331dl-accel
|
||||
- st,lng2dm-accel
|
||||
- st,lis3l02dq
|
||||
|
||||
Gyroscopes:
|
||||
- st,l3g4200d-gyro
|
||||
@ -52,6 +53,7 @@ Gyroscopes:
|
||||
- st,lsm330dl-gyro
|
||||
- st,lsm330dlc-gyro
|
||||
- st,l3gd20-gyro
|
||||
- st,l3gd20h-gyro
|
||||
- st,l3g4is-gyro
|
||||
- st,lsm330-gyro
|
||||
- st,lsm9ds0-gyro
|
||||
|
19
Documentation/devicetree/bindings/iio/temperature/tsys01.txt
Normal file
19
Documentation/devicetree/bindings/iio/temperature/tsys01.txt
Normal file
@ -0,0 +1,19 @@
|
||||
* TSYS01 - Measurement Specialties temperature sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "meas,tsys01"
|
||||
- reg: I2C address of the sensor (changeable via CSB pin)
|
||||
|
||||
------------------------
|
||||
| CSB | Device Address |
|
||||
------------------------
|
||||
1 0x76
|
||||
0 0x77
|
||||
|
||||
Example:
|
||||
|
||||
tsys01@76 {
|
||||
compatible = "meas,tsys01";
|
||||
reg = <0x76>;
|
||||
};
|
@ -14,7 +14,7 @@ Example:
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "clk_int";
|
||||
clock-names = "int";
|
||||
|
||||
timer@0 {
|
||||
compatible = "st,stm32-timer-trigger";
|
||||
|
29
Documentation/iio/ep93xx_adc.txt
Normal file
29
Documentation/iio/ep93xx_adc.txt
Normal file
@ -0,0 +1,29 @@
|
||||
Cirrus Logic EP93xx ADC driver.
|
||||
|
||||
1. Overview
|
||||
|
||||
The driver is intended to work on both low-end (EP9301, EP9302) devices with
|
||||
5-channel ADC and high-end (EP9307, EP9312, EP9315) devices with 10-channel
|
||||
touchscreen/ADC module.
|
||||
|
||||
2. Channel numbering
|
||||
|
||||
Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets.
|
||||
EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is
|
||||
not defined. So the last three are numbered randomly, let's say.
|
||||
|
||||
Assuming ep93xx_adc is IIO device0, you'd find the following entries under
|
||||
/sys/bus/iio/devices/iio:device0/:
|
||||
|
||||
+-----------------+---------------+
|
||||
| sysfs entry | ball/pin name |
|
||||
+-----------------+---------------+
|
||||
| in_voltage0_raw | YM |
|
||||
| in_voltage1_raw | SXP |
|
||||
| in_voltage2_raw | SXM |
|
||||
| in_voltage3_raw | SYP |
|
||||
| in_voltage4_raw | SYM |
|
||||
| in_voltage5_raw | XP |
|
||||
| in_voltage6_raw | XM |
|
||||
| in_voltage7_raw | YP |
|
||||
+-----------------+---------------+
|
@ -64,6 +64,7 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = {
|
||||
{"BMA250E", bma250e},
|
||||
{"BMA222E", bma222e},
|
||||
{"BMA0280", bma280},
|
||||
{"BOSC0200"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
|
||||
|
@ -139,7 +139,7 @@ static int da311_register_mask_write(struct i2c_client *client, u16 addr,
|
||||
/* Init sequence taken from the android driver */
|
||||
static int da311_reset(struct i2c_client *client)
|
||||
{
|
||||
const struct {
|
||||
static const struct {
|
||||
u16 addr;
|
||||
u8 mask;
|
||||
u8 data;
|
||||
|
@ -36,7 +36,7 @@
|
||||
#define SCA3000_LOCKED BIT(5)
|
||||
#define SCA3000_EEPROM_CS_ERROR BIT(1)
|
||||
#define SCA3000_SPI_FRAME_ERROR BIT(0)
|
||||
|
||||
|
||||
/* All reads done using register decrement so no need to directly access LSBs */
|
||||
#define SCA3000_REG_X_MSB_ADDR 0x05
|
||||
#define SCA3000_REG_Y_MSB_ADDR 0x07
|
||||
@ -74,7 +74,7 @@
|
||||
#define SCA3000_REG_INT_STATUS_ADDR 0x16
|
||||
#define SCA3000_REG_INT_STATUS_THREE_QUARTERS BIT(7)
|
||||
#define SCA3000_REG_INT_STATUS_HALF BIT(6)
|
||||
|
||||
|
||||
#define SCA3000_INT_STATUS_FREE_FALL BIT(3)
|
||||
#define SCA3000_INT_STATUS_Y_TRIGGER BIT(2)
|
||||
#define SCA3000_INT_STATUS_X_TRIGGER BIT(1)
|
||||
@ -124,7 +124,7 @@
|
||||
|
||||
#define SCA3000_REG_INT_MASK_ADDR 0x21
|
||||
#define SCA3000_REG_INT_MASK_PROT_MASK 0x1C
|
||||
|
||||
|
||||
#define SCA3000_REG_INT_MASK_RING_THREE_QUARTER BIT(7)
|
||||
#define SCA3000_REG_INT_MASK_RING_HALF BIT(6)
|
||||
|
||||
|
@ -29,10 +29,13 @@ enum st_accel_type {
|
||||
LIS2DH12,
|
||||
LIS3L02DQ,
|
||||
LNG2DM,
|
||||
H3LIS331DL,
|
||||
LIS331DL,
|
||||
LIS3LV02DL,
|
||||
ST_ACCEL_MAX,
|
||||
};
|
||||
|
||||
#define H3LIS331DL_DRIVER_NAME "h3lis331dl_accel"
|
||||
#define H3LIS331DL_ACCEL_DEV_NAME "h3lis331dl_accel"
|
||||
#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel"
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
#define LIS3DH_ACCEL_DEV_NAME "lis3dh"
|
||||
|
@ -444,7 +444,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||
.wai = 0x32,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = H3LIS331DL_DRIVER_NAME,
|
||||
[0] = H3LIS331DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
|
@ -84,7 +84,7 @@ static const struct of_device_id st_accel_of_match[] = {
|
||||
},
|
||||
{
|
||||
.compatible = "st,h3lis331dl-accel",
|
||||
.data = H3LIS331DL_DRIVER_NAME,
|
||||
.data = H3LIS331DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3l02dq",
|
||||
@ -126,6 +126,9 @@ static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ },
|
||||
{ LNG2DM_ACCEL_DEV_NAME, LNG2DM },
|
||||
{ H3LIS331DL_ACCEL_DEV_NAME, H3LIS331DL },
|
||||
{ LIS331DL_ACCEL_DEV_NAME, LIS331DL },
|
||||
{ LIS3LV02DL_ACCEL_DEV_NAME, LIS3LV02DL },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
@ -144,7 +147,8 @@ static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
adata = iio_priv(indio_dev);
|
||||
|
||||
if (client->dev.of_node) {
|
||||
st_sensors_of_i2c_probe(client, st_accel_of_match);
|
||||
st_sensors_of_name_probe(&client->dev, st_accel_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
ret = st_sensors_match_acpi_device(&client->dev);
|
||||
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
|
||||
|
@ -18,6 +18,77 @@
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* For new single-chip sensors use <device_name> as compatible string.
|
||||
* For old single-chip devices keep <device_name>-accel to maintain
|
||||
* compatibility
|
||||
*/
|
||||
static const struct of_device_id st_accel_of_match[] = {
|
||||
{
|
||||
/* An older compatible */
|
||||
.compatible = "st,lis302dl-spi",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3lv02dl-accel",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3dh-accel",
|
||||
.data = LIS3DH_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330d-accel",
|
||||
.data = LSM330D_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dl-accel",
|
||||
.data = LSM330DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dlc-accel",
|
||||
.data = LSM330DLC_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis331dlh-accel",
|
||||
.data = LIS331DLH_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330-accel",
|
||||
.data = LSM330_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303agr-accel",
|
||||
.data = LSM303AGR_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis2dh12-accel",
|
||||
.data = LIS2DH12_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3l02dq",
|
||||
.data = LIS3L02DQ_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lng2dm-accel",
|
||||
.data = LNG2DM_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,h3lis331dl-accel",
|
||||
.data = H3LIS331DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis331dl-accel",
|
||||
.data = LIS331DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
#else
|
||||
#define st_accel_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_accel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@ -30,6 +101,8 @@ static int st_accel_spi_probe(struct spi_device *spi)
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
@ -57,22 +130,17 @@ static const struct spi_device_id st_accel_id_table[] = {
|
||||
{ LIS2DH12_ACCEL_DEV_NAME },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME },
|
||||
{ LNG2DM_ACCEL_DEV_NAME },
|
||||
{ H3LIS331DL_ACCEL_DEV_NAME },
|
||||
{ LIS331DL_ACCEL_DEV_NAME },
|
||||
{ LIS3LV02DL_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id lis302dl_spi_dt_ids[] = {
|
||||
{ .compatible = "st,lis302dl-spi" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct spi_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.name = "st-accel-spi",
|
||||
.of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
|
||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||
},
|
||||
.probe = st_accel_spi_probe,
|
||||
.remove = st_accel_spi_remove,
|
||||
|
@ -239,6 +239,15 @@ config DA9150_GPADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called berlin2-adc.
|
||||
|
||||
config DLN2_ADC
|
||||
tristate "Diolan DLN-2 ADC driver support"
|
||||
depends on MFD_DLN2
|
||||
help
|
||||
Say yes here to build support for Diolan DLN-2 ADC.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called adc_dln2.
|
||||
|
||||
config ENVELOPE_DETECTOR
|
||||
tristate "Envelope detector using a DAC and a comparator"
|
||||
depends on OF
|
||||
@ -249,6 +258,17 @@ config ENVELOPE_DETECTOR
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called envelope-detector.
|
||||
|
||||
config EP93XX_ADC
|
||||
tristate "Cirrus Logic EP93XX ADC driver"
|
||||
depends on ARCH_EP93XX
|
||||
help
|
||||
Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic.
|
||||
It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this
|
||||
case driver will reduce its CPU usage by 90% in some use cases.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ep93xx_adc.
|
||||
|
||||
config EXYNOS_ADC
|
||||
tristate "Exynos ADC driver support"
|
||||
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
|
||||
@ -322,7 +342,7 @@ config INA2XX_ADC
|
||||
This driver is mutually exclusive with the HWMON version.
|
||||
|
||||
config IMX7D_ADC
|
||||
tristate "IMX7D ADC driver"
|
||||
tristate "Freescale IMX7D ADC driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
@ -362,6 +382,16 @@ config LPC32XX_ADC
|
||||
activate only one via device tree selection. Provides direct access
|
||||
via sysfs.
|
||||
|
||||
config LTC2471
|
||||
tristate "Linear Technology LTC2471 and LTC2473 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Linear Technology LTC2471 and
|
||||
LTC2473 16-bit I2C ADC.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc2471.
|
||||
|
||||
config LTC2485
|
||||
tristate "Linear Technology LTC2485 ADC driver"
|
||||
depends on I2C
|
||||
|
@ -24,7 +24,9 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
|
||||
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
|
||||
obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
|
||||
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
|
||||
obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
|
||||
obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
|
||||
obj-$(CONFIG_EP93XX_ADC) += ep93xx_adc.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
|
||||
obj-$(CONFIG_HI8435) += hi8435.o
|
||||
@ -34,6 +36,7 @@ obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
|
||||
obj-$(CONFIG_LTC2471) += ltc2471.o
|
||||
obj-$(CONFIG_LTC2485) += ltc2485.o
|
||||
obj-$(CONFIG_LTC2497) += ltc2497.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
|
@ -103,8 +103,7 @@ static int ad7766_preenable(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ad7766->pd_gpio)
|
||||
gpiod_set_value(ad7766->pd_gpio, 0);
|
||||
gpiod_set_value(ad7766->pd_gpio, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -113,8 +112,7 @@ static int ad7766_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7766 *ad7766 = iio_priv(indio_dev);
|
||||
|
||||
if (ad7766->pd_gpio)
|
||||
gpiod_set_value(ad7766->pd_gpio, 1);
|
||||
gpiod_set_value(ad7766->pd_gpio, 1);
|
||||
|
||||
/*
|
||||
* The PD pin is synchronous to the clock, so give it some time to
|
||||
|
@ -25,6 +25,11 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* Control Register */
|
||||
@ -132,6 +137,17 @@
|
||||
#define AT91_SAMA5D2_PRESSR 0xbc
|
||||
/* Trigger Register */
|
||||
#define AT91_SAMA5D2_TRGR 0xc0
|
||||
/* Mask for TRGMOD field of TRGR register */
|
||||
#define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0)
|
||||
/* No trigger, only software trigger can start conversions */
|
||||
#define AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER 0
|
||||
/* Trigger Mode external trigger rising edge */
|
||||
#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE 1
|
||||
/* Trigger Mode external trigger falling edge */
|
||||
#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL 2
|
||||
/* Trigger Mode external trigger any edge */
|
||||
#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY 3
|
||||
|
||||
/* Correction Select Register */
|
||||
#define AT91_SAMA5D2_COSR 0xd0
|
||||
/* Correction Value Register */
|
||||
@ -145,14 +161,29 @@
|
||||
/* Version Register */
|
||||
#define AT91_SAMA5D2_VERSION 0xfc
|
||||
|
||||
#define AT91_SAMA5D2_HW_TRIG_CNT 3
|
||||
#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12
|
||||
#define AT91_SAMA5D2_DIFF_CHAN_CNT 6
|
||||
|
||||
/*
|
||||
* Maximum number of bytes to hold conversion from all channels
|
||||
* plus the timestamp
|
||||
*/
|
||||
#define AT91_BUFFER_MAX_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \
|
||||
AT91_SAMA5D2_DIFF_CHAN_CNT) * 2 + 8)
|
||||
|
||||
#define AT91_BUFFER_MAX_HWORDS (AT91_BUFFER_MAX_BYTES / 2)
|
||||
|
||||
#define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = num, \
|
||||
.address = addr, \
|
||||
.scan_index = num, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
@ -168,9 +199,11 @@
|
||||
.channel = num, \
|
||||
.channel2 = num2, \
|
||||
.address = addr, \
|
||||
.scan_index = num + AT91_SAMA5D2_SINGLE_CHAN_CNT, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
@ -188,6 +221,12 @@ struct at91_adc_soc_info {
|
||||
unsigned max_sample_rate;
|
||||
};
|
||||
|
||||
struct at91_adc_trigger {
|
||||
char *name;
|
||||
unsigned int trgmod_value;
|
||||
unsigned int edge_type;
|
||||
};
|
||||
|
||||
struct at91_adc_state {
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
@ -195,11 +234,14 @@ struct at91_adc_state {
|
||||
struct regulator *reg;
|
||||
struct regulator *vref;
|
||||
int vref_uv;
|
||||
struct iio_trigger *trig;
|
||||
const struct at91_adc_trigger *selected_trig;
|
||||
const struct iio_chan_spec *chan;
|
||||
bool conversion_done;
|
||||
u32 conversion_value;
|
||||
struct at91_adc_soc_info soc_info;
|
||||
wait_queue_head_t wq_data_available;
|
||||
u16 buffer[AT91_BUFFER_MAX_HWORDS];
|
||||
/*
|
||||
* lock to prevent concurrent 'single conversion' requests through
|
||||
* sysfs.
|
||||
@ -207,6 +249,24 @@ struct at91_adc_state {
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const struct at91_adc_trigger at91_adc_trigger_list[] = {
|
||||
{
|
||||
.name = "external_rising",
|
||||
.trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE,
|
||||
.edge_type = IRQ_TYPE_EDGE_RISING,
|
||||
},
|
||||
{
|
||||
.name = "external_falling",
|
||||
.trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL,
|
||||
.edge_type = IRQ_TYPE_EDGE_FALLING,
|
||||
},
|
||||
{
|
||||
.name = "external_any",
|
||||
.trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY,
|
||||
.edge_type = IRQ_TYPE_EDGE_BOTH,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec at91_adc_channels[] = {
|
||||
AT91_SAMA5D2_CHAN_SINGLE(0, 0x50),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(1, 0x54),
|
||||
@ -226,12 +286,132 @@ static const struct iio_chan_spec at91_adc_channels[] = {
|
||||
AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68),
|
||||
AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70),
|
||||
AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(AT91_SAMA5D2_SINGLE_CHAN_CNT
|
||||
+ AT91_SAMA5D2_DIFF_CHAN_CNT + 1),
|
||||
};
|
||||
|
||||
static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
struct at91_adc_state *st = iio_priv(indio);
|
||||
u32 status = at91_adc_readl(st, AT91_SAMA5D2_TRGR);
|
||||
u8 bit;
|
||||
|
||||
/* clear TRGMOD */
|
||||
status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK;
|
||||
|
||||
if (state)
|
||||
status |= st->selected_trig->trgmod_value;
|
||||
|
||||
/* set/unset hw trigger */
|
||||
at91_adc_writel(st, AT91_SAMA5D2_TRGR, status);
|
||||
|
||||
for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) {
|
||||
struct iio_chan_spec const *chan = indio->channels + bit;
|
||||
|
||||
if (state) {
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHER,
|
||||
BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IER,
|
||||
BIT(chan->channel));
|
||||
} else {
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR,
|
||||
BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHDR,
|
||||
BIT(chan->channel));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_adc_reenable_trigger(struct iio_trigger *trig)
|
||||
{
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
struct at91_adc_state *st = iio_priv(indio);
|
||||
|
||||
enable_irq(st->irq);
|
||||
|
||||
/* Needed to ACK the DRDY interruption */
|
||||
at91_adc_readl(st, AT91_SAMA5D2_LCDR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops at91_adc_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &at91_adc_configure_trigger,
|
||||
.try_reenable = &at91_adc_reenable_trigger,
|
||||
};
|
||||
|
||||
static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio,
|
||||
char *trigger_name)
|
||||
{
|
||||
struct iio_trigger *trig;
|
||||
int ret;
|
||||
|
||||
trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name,
|
||||
indio->id, trigger_name);
|
||||
if (!trig)
|
||||
return NULL;
|
||||
|
||||
trig->dev.parent = indio->dev.parent;
|
||||
iio_trigger_set_drvdata(trig, indio);
|
||||
trig->ops = &at91_adc_trigger_ops;
|
||||
|
||||
ret = devm_iio_trigger_register(&indio->dev, trig);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return trig;
|
||||
}
|
||||
|
||||
static int at91_adc_trigger_init(struct iio_dev *indio)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio);
|
||||
|
||||
st->trig = at91_adc_allocate_trigger(indio, st->selected_trig->name);
|
||||
if (IS_ERR(st->trig)) {
|
||||
dev_err(&indio->dev,
|
||||
"could not allocate trigger\n");
|
||||
return PTR_ERR(st->trig);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio = pf->indio_dev;
|
||||
struct at91_adc_state *st = iio_priv(indio);
|
||||
int i = 0;
|
||||
u8 bit;
|
||||
|
||||
for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) {
|
||||
struct iio_chan_spec const *chan = indio->channels + bit;
|
||||
|
||||
st->buffer[i] = at91_adc_readl(st, chan->address);
|
||||
i++;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio, st->buffer, pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int at91_adc_buffer_init(struct iio_dev *indio)
|
||||
{
|
||||
return devm_iio_triggered_buffer_setup(&indio->dev, indio,
|
||||
&iio_pollfunc_store_time,
|
||||
&at91_adc_trigger_handler, NULL);
|
||||
}
|
||||
|
||||
static unsigned at91_adc_startup_time(unsigned startup_time_min,
|
||||
unsigned adc_clk_khz)
|
||||
{
|
||||
const unsigned startup_lookup[] = {
|
||||
static const unsigned int startup_lookup[] = {
|
||||
0, 8, 16, 24,
|
||||
64, 80, 96, 112,
|
||||
512, 576, 640, 704,
|
||||
@ -293,14 +473,18 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private)
|
||||
u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
|
||||
u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
|
||||
|
||||
if (status & imr) {
|
||||
if (!(status & imr))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (iio_buffer_enabled(indio)) {
|
||||
disable_irq_nosync(irq);
|
||||
iio_trigger_poll(indio->trig);
|
||||
} else {
|
||||
st->conversion_value = at91_adc_readl(st, st->chan->address);
|
||||
st->conversion_done = true;
|
||||
wake_up_interruptible(&st->wq_data_available);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
||||
@ -313,6 +497,11 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/* we cannot use software trigger if hw trigger enabled */
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
st->chan = chan;
|
||||
@ -344,6 +533,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
@ -386,12 +577,27 @@ static const struct iio_info at91_adc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void at91_adc_hw_init(struct at91_adc_state *st)
|
||||
{
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
|
||||
/*
|
||||
* Transfer field must be set to 2 according to the datasheet and
|
||||
* allows different analog settings for each channel.
|
||||
*/
|
||||
at91_adc_writel(st, AT91_SAMA5D2_MR,
|
||||
AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
|
||||
|
||||
at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
|
||||
}
|
||||
|
||||
static int at91_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct at91_adc_state *st;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
int ret, i;
|
||||
u32 edge_type;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
@ -432,6 +638,27 @@ static int at91_adc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"atmel,trigger-edge-type", &edge_type);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid or missing value for atmel,trigger-edge-type\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->selected_trig = NULL;
|
||||
|
||||
for (i = 0; i < AT91_SAMA5D2_HW_TRIG_CNT; i++)
|
||||
if (at91_adc_trigger_list[i].edge_type == edge_type) {
|
||||
st->selected_trig = &at91_adc_trigger_list[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!st->selected_trig) {
|
||||
dev_err(&pdev->dev, "invalid external trigger edge value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&st->wq_data_available);
|
||||
mutex_init(&st->lock);
|
||||
|
||||
@ -482,16 +709,7 @@ static int at91_adc_probe(struct platform_device *pdev)
|
||||
goto vref_disable;
|
||||
}
|
||||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
|
||||
/*
|
||||
* Transfer field must be set to 2 according to the datasheet and
|
||||
* allows different analog settings for each channel.
|
||||
*/
|
||||
at91_adc_writel(st, AT91_SAMA5D2_MR,
|
||||
AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
|
||||
|
||||
at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
|
||||
at91_adc_hw_init(st);
|
||||
|
||||
ret = clk_prepare_enable(st->per_clk);
|
||||
if (ret)
|
||||
@ -499,10 +717,25 @@ static int at91_adc_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = at91_adc_buffer_init(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "couldn't initialize the buffer.\n");
|
||||
goto per_clk_disable_unprepare;
|
||||
}
|
||||
|
||||
ret = at91_adc_trigger_init(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "couldn't setup the triggers.\n");
|
||||
goto per_clk_disable_unprepare;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto per_clk_disable_unprepare;
|
||||
|
||||
dev_info(&pdev->dev, "setting up trigger as %s\n",
|
||||
st->selected_trig->name);
|
||||
|
||||
dev_info(&pdev->dev, "version: %x\n",
|
||||
readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
|
||||
|
||||
@ -532,6 +765,69 @@ static int at91_adc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __maybe_unused int at91_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
* Do a sofware reset of the ADC before we go to suspend.
|
||||
* this will ensure that all pins are free from being muxed by the ADC
|
||||
* and can be used by for other devices.
|
||||
* Otherwise, ADC will hog them and we can't go to suspend mode.
|
||||
*/
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
|
||||
|
||||
clk_disable_unprepare(st->per_clk);
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
||||
static __maybe_unused int at91_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_pm_select_default_state(dev);
|
||||
if (ret)
|
||||
goto resume_failed;
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto resume_failed;
|
||||
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret)
|
||||
goto reg_disable_resume;
|
||||
|
||||
ret = clk_prepare_enable(st->per_clk);
|
||||
if (ret)
|
||||
goto vref_disable_resume;
|
||||
|
||||
at91_adc_hw_init(st);
|
||||
|
||||
/* reconfiguring trigger hardware state */
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
at91_adc_configure_trigger(st->trig, true);
|
||||
|
||||
return 0;
|
||||
|
||||
vref_disable_resume:
|
||||
regulator_disable(st->vref);
|
||||
reg_disable_resume:
|
||||
regulator_disable(st->reg);
|
||||
resume_failed:
|
||||
dev_err(&indio_dev->dev, "failed to resume\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
|
||||
|
||||
static const struct of_device_id at91_adc_dt_match[] = {
|
||||
{
|
||||
.compatible = "atmel,sama5d2-adc",
|
||||
@ -547,6 +843,7 @@ static struct platform_driver at91_adc_driver = {
|
||||
.driver = {
|
||||
.name = "at91-sama5d2_adc",
|
||||
.of_match_table = at91_adc_dt_match,
|
||||
.pm = &at91_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(at91_adc_driver)
|
||||
|
@ -799,7 +799,7 @@ static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz)
|
||||
* For sama5d3x and at91sam9x5, the formula changes to:
|
||||
* Startup Time = <lookup_table_value> / ADC Clock
|
||||
*/
|
||||
const int startup_lookup[] = {
|
||||
static const int startup_lookup[] = {
|
||||
0, 8, 16, 24,
|
||||
64, 80, 96, 112,
|
||||
512, 576, 640, 704,
|
||||
|
722
drivers/iio/adc/dln2-adc.c
Normal file
722
drivers/iio/adc/dln2-adc.c
Normal file
@ -0,0 +1,722 @@
|
||||
/*
|
||||
* Driver for the Diolan DLN-2 USB-ADC adapter
|
||||
*
|
||||
* Copyright (c) 2017 Jack Andersen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/dln2.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#define DLN2_ADC_MOD_NAME "dln2-adc"
|
||||
|
||||
#define DLN2_ADC_ID 0x06
|
||||
|
||||
#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_ENABLE DLN2_CMD(0x02, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_DISABLE DLN2_CMD(0x03, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_CHANNEL_ENABLE DLN2_CMD(0x05, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_CHANNEL_DISABLE DLN2_CMD(0x06, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_SET_RESOLUTION DLN2_CMD(0x08, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_CHANNEL_GET_VAL DLN2_CMD(0x0A, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_CHANNEL_GET_ALL_VAL DLN2_CMD(0x0B, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_CHANNEL_SET_CFG DLN2_CMD(0x0C, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_CHANNEL_GET_CFG DLN2_CMD(0x0D, DLN2_ADC_ID)
|
||||
#define DLN2_ADC_CONDITION_MET_EV DLN2_CMD(0x10, DLN2_ADC_ID)
|
||||
|
||||
#define DLN2_ADC_EVENT_NONE 0
|
||||
#define DLN2_ADC_EVENT_BELOW 1
|
||||
#define DLN2_ADC_EVENT_LEVEL_ABOVE 2
|
||||
#define DLN2_ADC_EVENT_OUTSIDE 3
|
||||
#define DLN2_ADC_EVENT_INSIDE 4
|
||||
#define DLN2_ADC_EVENT_ALWAYS 5
|
||||
|
||||
#define DLN2_ADC_MAX_CHANNELS 8
|
||||
#define DLN2_ADC_DATA_BITS 10
|
||||
|
||||
/*
|
||||
* Plays similar role to iio_demux_table in subsystem core; except allocated
|
||||
* in a fixed 8-element array.
|
||||
*/
|
||||
struct dln2_adc_demux_table {
|
||||
unsigned int from;
|
||||
unsigned int to;
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
struct dln2_adc {
|
||||
struct platform_device *pdev;
|
||||
struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
|
||||
int port, trigger_chan;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex mutex;
|
||||
/* Cached sample period in milliseconds */
|
||||
unsigned int sample_period;
|
||||
/* Demux table */
|
||||
unsigned int demux_count;
|
||||
struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS];
|
||||
/* Precomputed timestamp padding offset and length */
|
||||
unsigned int ts_pad_offset, ts_pad_length;
|
||||
};
|
||||
|
||||
struct dln2_adc_port_chan {
|
||||
u8 port;
|
||||
u8 chan;
|
||||
};
|
||||
|
||||
struct dln2_adc_get_all_vals {
|
||||
__le16 channel_mask;
|
||||
__le16 values[DLN2_ADC_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
static void dln2_adc_add_demux(struct dln2_adc *dln2,
|
||||
unsigned int in_loc, unsigned int out_loc,
|
||||
unsigned int length)
|
||||
{
|
||||
struct dln2_adc_demux_table *p = dln2->demux_count ?
|
||||
&dln2->demux[dln2->demux_count - 1] : NULL;
|
||||
|
||||
if (p && p->from + p->length == in_loc &&
|
||||
p->to + p->length == out_loc) {
|
||||
p->length += length;
|
||||
} else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
|
||||
p = &dln2->demux[dln2->demux_count++];
|
||||
p->from = in_loc;
|
||||
p->to = out_loc;
|
||||
p->length = length;
|
||||
}
|
||||
}
|
||||
|
||||
static void dln2_adc_update_demux(struct dln2_adc *dln2)
|
||||
{
|
||||
int in_ind = -1, out_ind;
|
||||
unsigned int in_loc = 0, out_loc = 0;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
|
||||
|
||||
/* Clear out any old demux */
|
||||
dln2->demux_count = 0;
|
||||
|
||||
/* Optimize all 8-channels case */
|
||||
if (indio_dev->masklength &&
|
||||
(*indio_dev->active_scan_mask & 0xff) == 0xff) {
|
||||
dln2_adc_add_demux(dln2, 0, 0, 16);
|
||||
dln2->ts_pad_offset = 0;
|
||||
dln2->ts_pad_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build demux table from fixed 8-channels to active_scan_mask */
|
||||
for_each_set_bit(out_ind,
|
||||
indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
/* Handle timestamp separately */
|
||||
if (out_ind == DLN2_ADC_MAX_CHANNELS)
|
||||
break;
|
||||
for (++in_ind; in_ind != out_ind; ++in_ind)
|
||||
in_loc += 2;
|
||||
dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
|
||||
out_loc += 2;
|
||||
in_loc += 2;
|
||||
}
|
||||
|
||||
if (indio_dev->scan_timestamp) {
|
||||
size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1;
|
||||
|
||||
dln2->ts_pad_offset = out_loc;
|
||||
dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc;
|
||||
} else {
|
||||
dln2->ts_pad_offset = 0;
|
||||
dln2->ts_pad_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int dln2_adc_get_chan_count(struct dln2_adc *dln2)
|
||||
{
|
||||
int ret;
|
||||
u8 port = dln2->port;
|
||||
u8 count;
|
||||
int olen = sizeof(count);
|
||||
|
||||
ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
|
||||
&port, sizeof(port), &count, &olen);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
if (olen < sizeof(count))
|
||||
return -EPROTO;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
|
||||
{
|
||||
int ret;
|
||||
struct dln2_adc_port_chan port_chan = {
|
||||
.port = dln2->port,
|
||||
.chan = DLN2_ADC_DATA_BITS,
|
||||
};
|
||||
|
||||
ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
|
||||
&port_chan, sizeof(port_chan));
|
||||
if (ret < 0)
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
|
||||
int channel, bool enable)
|
||||
{
|
||||
int ret;
|
||||
struct dln2_adc_port_chan port_chan = {
|
||||
.port = dln2->port,
|
||||
.chan = channel,
|
||||
};
|
||||
u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
|
||||
|
||||
ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
|
||||
if (ret < 0)
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
|
||||
u16 *conflict_out)
|
||||
{
|
||||
int ret;
|
||||
u8 port = dln2->port;
|
||||
__le16 conflict;
|
||||
int olen = sizeof(conflict);
|
||||
u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
|
||||
|
||||
if (conflict_out)
|
||||
*conflict_out = 0;
|
||||
|
||||
ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
|
||||
&conflict, &olen);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
|
||||
__func__, (int)enable);
|
||||
if (conflict_out && enable && olen >= sizeof(conflict))
|
||||
*conflict_out = le16_to_cpu(conflict);
|
||||
return ret;
|
||||
}
|
||||
if (enable && olen < sizeof(conflict))
|
||||
return -EPROTO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
|
||||
unsigned int channel, unsigned int period)
|
||||
{
|
||||
int ret;
|
||||
struct {
|
||||
struct dln2_adc_port_chan port_chan;
|
||||
__u8 type;
|
||||
__le16 period;
|
||||
__le16 low;
|
||||
__le16 high;
|
||||
} __packed set_cfg = {
|
||||
.port_chan.port = dln2->port,
|
||||
.port_chan.chan = channel,
|
||||
.type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
|
||||
.period = cpu_to_le16(period)
|
||||
};
|
||||
|
||||
ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
|
||||
&set_cfg, sizeof(set_cfg));
|
||||
if (ret < 0)
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
|
||||
{
|
||||
int ret, i;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
|
||||
u16 conflict;
|
||||
__le16 value;
|
||||
int olen = sizeof(value);
|
||||
struct dln2_adc_port_chan port_chan = {
|
||||
.port = dln2->port,
|
||||
.chan = channel,
|
||||
};
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = dln2_adc_set_chan_enabled(dln2, channel, true);
|
||||
if (ret < 0)
|
||||
goto release_direct;
|
||||
|
||||
ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
|
||||
if (ret < 0) {
|
||||
if (conflict) {
|
||||
dev_err(&dln2->pdev->dev,
|
||||
"ADC pins conflict with mask %04X\n",
|
||||
(int)conflict);
|
||||
ret = -EBUSY;
|
||||
}
|
||||
goto disable_chan;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call GET_VAL twice due to initial zero-return immediately after
|
||||
* enabling channel.
|
||||
*/
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
|
||||
&port_chan, sizeof(port_chan),
|
||||
&value, &olen);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
goto disable_port;
|
||||
}
|
||||
if (olen < sizeof(value)) {
|
||||
ret = -EPROTO;
|
||||
goto disable_port;
|
||||
}
|
||||
}
|
||||
|
||||
ret = le16_to_cpu(value);
|
||||
|
||||
disable_port:
|
||||
dln2_adc_set_port_enabled(dln2, false, NULL);
|
||||
disable_chan:
|
||||
dln2_adc_set_chan_enabled(dln2, channel, false);
|
||||
release_direct:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dln2_adc_read_all(struct dln2_adc *dln2,
|
||||
struct dln2_adc_get_all_vals *get_all_vals)
|
||||
{
|
||||
int ret;
|
||||
__u8 port = dln2->port;
|
||||
int olen = sizeof(*get_all_vals);
|
||||
|
||||
ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
|
||||
&port, sizeof(port), get_all_vals, &olen);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
if (olen < sizeof(*get_all_vals))
|
||||
return -EPROTO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dln2_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
unsigned int microhertz;
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&dln2->mutex);
|
||||
ret = dln2_adc_read(dln2, chan->channel);
|
||||
mutex_unlock(&dln2->mutex);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/*
|
||||
* Voltage reference is fixed at 3.3v
|
||||
* 3.3 / (1 << 10) * 1000000000
|
||||
*/
|
||||
*val = 0;
|
||||
*val2 = 3222656;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (dln2->sample_period) {
|
||||
microhertz = 1000000000 / dln2->sample_period;
|
||||
*val = microhertz / 1000000;
|
||||
*val2 = microhertz % 1000000;
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dln2_adc_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
unsigned int microhertz;
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
microhertz = 1000000 * val + val2;
|
||||
|
||||
mutex_lock(&dln2->mutex);
|
||||
|
||||
dln2->sample_period =
|
||||
microhertz ? 1000000000 / microhertz : UINT_MAX;
|
||||
if (dln2->sample_period > 65535) {
|
||||
dln2->sample_period = 65535;
|
||||
dev_warn(&dln2->pdev->dev,
|
||||
"clamping period to 65535ms\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* The first requested channel is arbitrated as a shared
|
||||
* trigger source, so only one event is registered with the
|
||||
* DLN. The event handler will then read all enabled channel
|
||||
* values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
|
||||
* synchronization between ADC readings.
|
||||
*/
|
||||
if (dln2->trigger_chan != -1)
|
||||
ret = dln2_adc_set_chan_period(dln2,
|
||||
dln2->trigger_chan, dln2->sample_period);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
mutex_unlock(&dln2->mutex);
|
||||
|
||||
return ret;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dln2_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
int chan_count = indio_dev->num_channels - 1;
|
||||
int ret, i, j;
|
||||
|
||||
mutex_lock(&dln2->mutex);
|
||||
|
||||
for (i = 0; i < chan_count; ++i) {
|
||||
ret = dln2_adc_set_chan_enabled(dln2, i,
|
||||
test_bit(i, scan_mask));
|
||||
if (ret < 0) {
|
||||
for (j = 0; j < i; ++j)
|
||||
dln2_adc_set_chan_enabled(dln2, j, false);
|
||||
mutex_unlock(&dln2->mutex);
|
||||
dev_err(&dln2->pdev->dev,
|
||||
"Unable to enable ADC channel %d\n", i);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
dln2_adc_update_demux(dln2);
|
||||
|
||||
mutex_unlock(&dln2->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DLN2_ADC_CHAN(lval, idx) { \
|
||||
lval.type = IIO_VOLTAGE; \
|
||||
lval.channel = idx; \
|
||||
lval.indexed = 1; \
|
||||
lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW); \
|
||||
lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ); \
|
||||
lval.scan_index = idx; \
|
||||
lval.scan_type.sign = 'u'; \
|
||||
lval.scan_type.realbits = DLN2_ADC_DATA_BITS; \
|
||||
lval.scan_type.storagebits = 16; \
|
||||
lval.scan_type.endianness = IIO_LE; \
|
||||
}
|
||||
|
||||
/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */
|
||||
#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) { \
|
||||
lval.type = IIO_TIMESTAMP; \
|
||||
lval.channel = -1; \
|
||||
lval.scan_index = _si; \
|
||||
lval.scan_type.sign = 's'; \
|
||||
lval.scan_type.realbits = 64; \
|
||||
lval.scan_type.storagebits = 64; \
|
||||
}
|
||||
|
||||
static const struct iio_info dln2_adc_info = {
|
||||
.read_raw = dln2_adc_read_raw,
|
||||
.write_raw = dln2_adc_write_raw,
|
||||
.update_scan_mode = dln2_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct {
|
||||
__le16 values[DLN2_ADC_MAX_CHANNELS];
|
||||
int64_t timestamp_space;
|
||||
} data;
|
||||
struct dln2_adc_get_all_vals dev_data;
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
const struct dln2_adc_demux_table *t;
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&dln2->mutex);
|
||||
ret = dln2_adc_read_all(dln2, &dev_data);
|
||||
mutex_unlock(&dln2->mutex);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
/* Demux operation */
|
||||
for (i = 0; i < dln2->demux_count; ++i) {
|
||||
t = &dln2->demux[i];
|
||||
memcpy((void *)data.values + t->to,
|
||||
(void *)dev_data.values + t->from, t->length);
|
||||
}
|
||||
|
||||
/* Zero padding space between values and timestamp */
|
||||
if (dln2->ts_pad_length)
|
||||
memset((void *)data.values + dln2->ts_pad_offset,
|
||||
0, dln2->ts_pad_length);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
u16 conflict;
|
||||
unsigned int trigger_chan;
|
||||
|
||||
mutex_lock(&dln2->mutex);
|
||||
|
||||
/* Enable ADC */
|
||||
ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&dln2->mutex);
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
if (conflict) {
|
||||
dev_err(&dln2->pdev->dev,
|
||||
"ADC pins conflict with mask %04X\n",
|
||||
(int)conflict);
|
||||
ret = -EBUSY;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Assign trigger channel based on first enabled channel */
|
||||
trigger_chan = find_first_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
|
||||
dln2->trigger_chan = trigger_chan;
|
||||
ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
|
||||
dln2->sample_period);
|
||||
mutex_unlock(&dln2->mutex);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dln2->trigger_chan = -1;
|
||||
mutex_unlock(&dln2->mutex);
|
||||
}
|
||||
|
||||
return iio_triggered_buffer_postenable(indio_dev);
|
||||
}
|
||||
|
||||
static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&dln2->mutex);
|
||||
|
||||
/* Disable trigger channel */
|
||||
if (dln2->trigger_chan != -1) {
|
||||
dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
|
||||
dln2->trigger_chan = -1;
|
||||
}
|
||||
|
||||
/* Disable ADC */
|
||||
ret = dln2_adc_set_port_enabled(dln2, false, NULL);
|
||||
|
||||
mutex_unlock(&dln2->mutex);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
|
||||
.postenable = dln2_adc_triggered_buffer_postenable,
|
||||
.predisable = dln2_adc_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
static void dln2_adc_event(struct platform_device *pdev, u16 echo,
|
||||
const void *data, int len)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
|
||||
/* Called via URB completion handler */
|
||||
iio_trigger_poll(dln2->trig);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops dln2_adc_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int dln2_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dln2_adc *dln2;
|
||||
struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct iio_dev *indio_dev;
|
||||
int i, ret, chans;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dln2 = iio_priv(indio_dev);
|
||||
dln2->pdev = pdev;
|
||||
dln2->port = pdata->port;
|
||||
dln2->trigger_chan = -1;
|
||||
mutex_init(&dln2->mutex);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = dln2_adc_set_port_resolution(dln2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set ADC resolution to 10 bits\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
chans = dln2_adc_get_chan_count(dln2);
|
||||
if (chans < 0) {
|
||||
dev_err(dev, "failed to get channel count: %d\n", chans);
|
||||
return chans;
|
||||
}
|
||||
if (chans > DLN2_ADC_MAX_CHANNELS) {
|
||||
chans = DLN2_ADC_MAX_CHANNELS;
|
||||
dev_warn(dev, "clamping channels to %d\n",
|
||||
DLN2_ADC_MAX_CHANNELS);
|
||||
}
|
||||
|
||||
for (i = 0; i < chans; ++i)
|
||||
DLN2_ADC_CHAN(dln2->iio_channels[i], i)
|
||||
IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i);
|
||||
|
||||
indio_dev->name = DLN2_ADC_MOD_NAME;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &dln2_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = dln2->iio_channels;
|
||||
indio_dev->num_channels = chans + 1;
|
||||
indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
|
||||
|
||||
dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
if (!dln2->trig) {
|
||||
dev_err(dev, "failed to allocate trigger\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dln2->trig->ops = &dln2_adc_trigger_ops;
|
||||
iio_trigger_set_drvdata(dln2->trig, dln2);
|
||||
devm_iio_trigger_register(dev, dln2->trig);
|
||||
iio_trigger_set_immutable(indio_dev, dln2->trig);
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
dln2_adc_trigger_h,
|
||||
&dln2_adc_buffer_setup_ops);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
|
||||
dln2_adc_event);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register iio device: %d\n", ret);
|
||||
goto unregister_event;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
unregister_event:
|
||||
dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dln2_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dln2_adc_driver = {
|
||||
.driver.name = DLN2_ADC_MOD_NAME,
|
||||
.probe = dln2_adc_probe,
|
||||
.remove = dln2_adc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(dln2_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
|
||||
MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:dln2-adc");
|
255
drivers/iio/adc/ep93xx_adc.c
Normal file
255
drivers/iio/adc/ep93xx_adc.c
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Driver for ADC module on the Cirrus Logic EP93xx series of SoCs
|
||||
*
|
||||
* Copyright (C) 2015 Alexander Sverdlin
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The driver uses polling to get the conversion status. According to EP93xx
|
||||
* datasheets, reading ADCResult register starts the conversion, but user is also
|
||||
* responsible for ensuring that delay between adjacent conversion triggers is
|
||||
* long enough so that maximum allowed conversion rate is not exceeded. This
|
||||
* basically renders IRQ mode unusable.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/*
|
||||
* This code could benefit from real HR Timers, but jiffy granularity would
|
||||
* lower ADC conversion rate down to CONFIG_HZ, so we fallback to busy wait
|
||||
* in such case.
|
||||
*
|
||||
* HR Timers-based version loads CPU only up to 10% during back to back ADC
|
||||
* conversion, while busy wait-based version consumes whole CPU power.
|
||||
*/
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
#define ep93xx_adc_delay(usmin, usmax) usleep_range(usmin, usmax)
|
||||
#else
|
||||
#define ep93xx_adc_delay(usmin, usmax) udelay(usmin)
|
||||
#endif
|
||||
|
||||
#define EP93XX_ADC_RESULT 0x08
|
||||
#define EP93XX_ADC_SDR BIT(31)
|
||||
#define EP93XX_ADC_SWITCH 0x18
|
||||
#define EP93XX_ADC_SW_LOCK 0x20
|
||||
|
||||
struct ep93xx_adc_priv {
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
int lastch;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
#define EP93XX_ADC_CH(index, dname, swcfg) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.address = swcfg, \
|
||||
.datasheet_name = dname, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
}
|
||||
|
||||
/*
|
||||
* Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets.
|
||||
* EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is
|
||||
* not defined. So the last three are numbered randomly, let's say.
|
||||
*/
|
||||
static const struct iio_chan_spec ep93xx_adc_channels[8] = {
|
||||
EP93XX_ADC_CH(0, "YM", 0x608),
|
||||
EP93XX_ADC_CH(1, "SXP", 0x680),
|
||||
EP93XX_ADC_CH(2, "SXM", 0x640),
|
||||
EP93XX_ADC_CH(3, "SYP", 0x620),
|
||||
EP93XX_ADC_CH(4, "SYM", 0x610),
|
||||
EP93XX_ADC_CH(5, "XP", 0x601),
|
||||
EP93XX_ADC_CH(6, "XM", 0x602),
|
||||
EP93XX_ADC_CH(7, "YP", 0x604),
|
||||
};
|
||||
|
||||
static int ep93xx_read_raw(struct iio_dev *iiodev,
|
||||
struct iio_chan_spec const *channel, int *value,
|
||||
int *shift, long mask)
|
||||
{
|
||||
struct ep93xx_adc_priv *priv = iio_priv(iiodev);
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&priv->lock);
|
||||
if (priv->lastch != channel->channel) {
|
||||
priv->lastch = channel->channel;
|
||||
/*
|
||||
* Switch register is software-locked, unlocking must be
|
||||
* immediately followed by write
|
||||
*/
|
||||
local_irq_disable();
|
||||
writel_relaxed(0xAA, priv->base + EP93XX_ADC_SW_LOCK);
|
||||
writel_relaxed(channel->address,
|
||||
priv->base + EP93XX_ADC_SWITCH);
|
||||
local_irq_enable();
|
||||
/*
|
||||
* Settling delay depends on module clock and could be
|
||||
* 2ms or 500us
|
||||
*/
|
||||
ep93xx_adc_delay(2000, 2000);
|
||||
}
|
||||
/* Start the conversion, eventually discarding old result */
|
||||
readl_relaxed(priv->base + EP93XX_ADC_RESULT);
|
||||
/* Ensure maximum conversion rate is not exceeded */
|
||||
ep93xx_adc_delay(DIV_ROUND_UP(1000000, 925),
|
||||
DIV_ROUND_UP(1000000, 925));
|
||||
/* At this point conversion must be completed, but anyway... */
|
||||
ret = IIO_VAL_INT;
|
||||
timeout = jiffies + msecs_to_jiffies(1) + 1;
|
||||
while (1) {
|
||||
u32 t;
|
||||
|
||||
t = readl_relaxed(priv->base + EP93XX_ADC_RESULT);
|
||||
if (t & EP93XX_ADC_SDR) {
|
||||
*value = sign_extend32(t, 15);
|
||||
break;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(&iiodev->dev, "Conversion timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* According to datasheet, range is -25000..25000 */
|
||||
*value = 25000;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Typical supply voltage is 3.3v */
|
||||
*value = (1ULL << 32) * 3300 / 50000;
|
||||
*shift = 32;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ep93xx_adc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ep93xx_read_raw,
|
||||
};
|
||||
|
||||
static int ep93xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *iiodev;
|
||||
struct ep93xx_adc_priv *priv;
|
||||
struct clk *pclk;
|
||||
struct resource *res;
|
||||
|
||||
iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
if (!iiodev)
|
||||
return -ENOMEM;
|
||||
priv = iio_priv(iiodev);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Cannot obtain memory resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->base)) {
|
||||
dev_err(&pdev->dev, "Cannot map memory resource\n");
|
||||
return PTR_ERR(priv->base);
|
||||
}
|
||||
|
||||
iiodev->dev.parent = &pdev->dev;
|
||||
iiodev->name = dev_name(&pdev->dev);
|
||||
iiodev->modes = INDIO_DIRECT_MODE;
|
||||
iiodev->info = &ep93xx_adc_info;
|
||||
iiodev->num_channels = ARRAY_SIZE(ep93xx_adc_channels);
|
||||
iiodev->channels = ep93xx_adc_channels;
|
||||
|
||||
priv->lastch = -1;
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
platform_set_drvdata(pdev, iiodev);
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(&pdev->dev, "Cannot obtain clock\n");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
|
||||
pclk = clk_get_parent(priv->clk);
|
||||
if (!pclk) {
|
||||
dev_warn(&pdev->dev, "Cannot obtain parent clock\n");
|
||||
} else {
|
||||
/*
|
||||
* This is actually a place for improvement:
|
||||
* EP93xx ADC supports two clock divisors -- 4 and 16,
|
||||
* resulting in conversion rates 3750 and 925 samples per second
|
||||
* with 500us or 2ms settling time respectively.
|
||||
* One might find this interesting enough to be configurable.
|
||||
*/
|
||||
ret = clk_set_rate(priv->clk, clk_get_rate(pclk) / 16);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "Cannot set clock rate\n");
|
||||
/*
|
||||
* We can tolerate rate setting failure because the module should
|
||||
* work in any case.
|
||||
*/
|
||||
}
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(iiodev);
|
||||
if (ret)
|
||||
clk_disable(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ep93xx_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *iiodev = platform_get_drvdata(pdev);
|
||||
struct ep93xx_adc_priv *priv = iio_priv(iiodev);
|
||||
|
||||
iio_device_unregister(iiodev);
|
||||
clk_disable(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ep93xx_adc_driver = {
|
||||
.driver = {
|
||||
.name = "ep93xx-adc",
|
||||
},
|
||||
.probe = ep93xx_adc_probe,
|
||||
.remove = ep93xx_adc_remove,
|
||||
};
|
||||
module_platform_driver(ep93xx_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexander Sverdlin <alexander.sverdlin@gmail.com>");
|
||||
MODULE_DESCRIPTION("Cirrus Logic EP93XX ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:ep93xx-adc");
|
@ -44,6 +44,7 @@
|
||||
|
||||
#define INA226_MASK_ENABLE 0x06
|
||||
#define INA226_CVRF BIT(3)
|
||||
#define INA219_CNVR BIT(1)
|
||||
|
||||
#define INA2XX_MAX_REGISTERS 8
|
||||
|
||||
@ -592,6 +593,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
|
||||
int bit, ret, i = 0;
|
||||
s64 time_a, time_b;
|
||||
unsigned int alert;
|
||||
int cnvr_need_clear = 0;
|
||||
|
||||
time_a = iio_get_time_ns(indio_dev);
|
||||
|
||||
@ -603,22 +605,30 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
|
||||
* we check the ConVersionReadyFlag.
|
||||
* On hardware that supports using the ALERT pin to toggle a
|
||||
* GPIO a triggered buffer could be used instead.
|
||||
* For now, we pay for that extra read of the ALERT register
|
||||
* For now, we do an extra read of the MASK_ENABLE register (INA226)
|
||||
* resp. the BUS_VOLTAGE register (INA219).
|
||||
*/
|
||||
if (!chip->allow_async_readout)
|
||||
do {
|
||||
ret = regmap_read(chip->regmap, INA226_MASK_ENABLE,
|
||||
&alert);
|
||||
if (chip->config->chip_id == ina226) {
|
||||
ret = regmap_read(chip->regmap,
|
||||
INA226_MASK_ENABLE, &alert);
|
||||
alert &= INA226_CVRF;
|
||||
} else {
|
||||
ret = regmap_read(chip->regmap,
|
||||
INA2XX_BUS_VOLTAGE, &alert);
|
||||
alert &= INA219_CNVR;
|
||||
cnvr_need_clear = alert;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
alert &= INA226_CVRF;
|
||||
} while (!alert);
|
||||
|
||||
/*
|
||||
* Single register reads: bulk_read will not work with ina226
|
||||
* as there is no auto-increment of the address register for
|
||||
* data length longer than 16bits.
|
||||
* Single register reads: bulk_read will not work with ina226/219
|
||||
* as there is no auto-increment of the register pointer.
|
||||
*/
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
@ -630,6 +640,18 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
|
||||
data[i++] = val;
|
||||
|
||||
if (INA2XX_SHUNT_VOLTAGE + bit == INA2XX_POWER)
|
||||
cnvr_need_clear = 0;
|
||||
}
|
||||
|
||||
/* Dummy read on INA219 power register to clear CNVR flag */
|
||||
if (cnvr_need_clear && chip->config->chip_id == ina219) {
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(chip->regmap, INA2XX_POWER, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_b = iio_get_time_ns(indio_dev);
|
||||
|
160
drivers/iio/adc/ltc2471.c
Normal file
160
drivers/iio/adc/ltc2471.c
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Driver for Linear Technology LTC2471 and LTC2473 voltage monitors
|
||||
* The LTC2473 is identical to the 2471, but reports a differential signal.
|
||||
*
|
||||
* Copyright (C) 2017 Topic Embedded Products
|
||||
* Author: Mike Looijmans <mike.looijmans@topic.nl>
|
||||
*
|
||||
* License: GPLv2
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
enum ltc2471_chips {
|
||||
ltc2471,
|
||||
ltc2473,
|
||||
};
|
||||
|
||||
struct ltc2471_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
/* Reference voltage is 1.25V */
|
||||
#define LTC2471_VREF 1250
|
||||
|
||||
/* Read two bytes from the I2C bus to obtain the ADC result */
|
||||
static int ltc2471_get_value(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
__be16 buf;
|
||||
|
||||
ret = i2c_master_recv(client, (char *)&buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != sizeof(buf))
|
||||
return -EIO;
|
||||
|
||||
/* MSB first */
|
||||
return be16_to_cpu(buf);
|
||||
}
|
||||
|
||||
static int ltc2471_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ltc2471_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ltc2471_get_value(data->client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->differential)
|
||||
/* Output ranges from -VREF to +VREF */
|
||||
*val = 2 * LTC2471_VREF;
|
||||
else
|
||||
/* Output ranges from 0 to VREF */
|
||||
*val = LTC2471_VREF;
|
||||
*val2 = 16; /* 16 data bits */
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* Only differential chip has this property */
|
||||
*val = -LTC2471_VREF;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltc2471_channel[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ltc2473_channel[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.differential = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ltc2471_info = {
|
||||
.read_raw = ltc2471_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ltc2471_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc2471_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = <c2471_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
if (id->driver_data == ltc2473)
|
||||
indio_dev->channels = ltc2473_channel;
|
||||
else
|
||||
indio_dev->channels = ltc2471_channel;
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
/* Trigger once to start conversion and check if chip is there */
|
||||
ret = ltc2471_get_value(client);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Cannot read from device.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc2471_i2c_id[] = {
|
||||
{ "ltc2471", ltc2471 },
|
||||
{ "ltc2473", ltc2473 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2471_i2c_id);
|
||||
|
||||
static struct i2c_driver ltc2471_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2471",
|
||||
},
|
||||
.probe = ltc2471_i2c_probe,
|
||||
.id_table = ltc2471_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ltc2471_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("LTC2471/LTC2473 ADC driver");
|
||||
MODULE_AUTHOR("Topic Embedded Products");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -11,6 +11,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -127,13 +128,14 @@ static int ltc2497_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN(_chan, _addr) { \
|
||||
#define LTC2497_CHAN(_chan, _addr, _ds_name) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = (_ds_name), \
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN_DIFF(_chan, _addr) { \
|
||||
@ -148,22 +150,22 @@ static int ltc2497_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltc2497_channel[] = {
|
||||
LTC2497_CHAN(0, LTC2497_SGL),
|
||||
LTC2497_CHAN(1, LTC2497_SGL),
|
||||
LTC2497_CHAN(2, LTC2497_SGL),
|
||||
LTC2497_CHAN(3, LTC2497_SGL),
|
||||
LTC2497_CHAN(4, LTC2497_SGL),
|
||||
LTC2497_CHAN(5, LTC2497_SGL),
|
||||
LTC2497_CHAN(6, LTC2497_SGL),
|
||||
LTC2497_CHAN(7, LTC2497_SGL),
|
||||
LTC2497_CHAN(8, LTC2497_SGL),
|
||||
LTC2497_CHAN(9, LTC2497_SGL),
|
||||
LTC2497_CHAN(10, LTC2497_SGL),
|
||||
LTC2497_CHAN(11, LTC2497_SGL),
|
||||
LTC2497_CHAN(12, LTC2497_SGL),
|
||||
LTC2497_CHAN(13, LTC2497_SGL),
|
||||
LTC2497_CHAN(14, LTC2497_SGL),
|
||||
LTC2497_CHAN(15, LTC2497_SGL),
|
||||
LTC2497_CHAN(0, LTC2497_SGL, "CH0"),
|
||||
LTC2497_CHAN(1, LTC2497_SGL, "CH1"),
|
||||
LTC2497_CHAN(2, LTC2497_SGL, "CH2"),
|
||||
LTC2497_CHAN(3, LTC2497_SGL, "CH3"),
|
||||
LTC2497_CHAN(4, LTC2497_SGL, "CH4"),
|
||||
LTC2497_CHAN(5, LTC2497_SGL, "CH5"),
|
||||
LTC2497_CHAN(6, LTC2497_SGL, "CH6"),
|
||||
LTC2497_CHAN(7, LTC2497_SGL, "CH7"),
|
||||
LTC2497_CHAN(8, LTC2497_SGL, "CH8"),
|
||||
LTC2497_CHAN(9, LTC2497_SGL, "CH9"),
|
||||
LTC2497_CHAN(10, LTC2497_SGL, "CH10"),
|
||||
LTC2497_CHAN(11, LTC2497_SGL, "CH11"),
|
||||
LTC2497_CHAN(12, LTC2497_SGL, "CH12"),
|
||||
LTC2497_CHAN(13, LTC2497_SGL, "CH13"),
|
||||
LTC2497_CHAN(14, LTC2497_SGL, "CH14"),
|
||||
LTC2497_CHAN(15, LTC2497_SGL, "CH15"),
|
||||
LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
|
||||
@ -192,6 +194,7 @@ static int ltc2497_probe(struct i2c_client *client,
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc2497_st *st;
|
||||
struct iio_map *plat_data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
@ -221,19 +224,31 @@ static int ltc2497_probe(struct i2c_client *client,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->dev.platform_data) {
|
||||
plat_data = ((struct iio_map *)client->dev.platform_data);
|
||||
ret = iio_map_array_register(indio_dev, plat_data);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT);
|
||||
if (ret < 0)
|
||||
goto err_regulator_disable;
|
||||
goto err_array_unregister;
|
||||
|
||||
st->addr_prev = LTC2497_CONFIG_DEFAULT;
|
||||
st->time_prev = ktime_get();
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_regulator_disable;
|
||||
goto err_array_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_array_unregister:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
err_regulator_disable:
|
||||
regulator_disable(st->ref);
|
||||
|
||||
@ -245,6 +260,7 @@ static int ltc2497_remove(struct i2c_client *client)
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ltc2497_st *st = iio_priv(indio_dev);
|
||||
|
||||
iio_map_array_unregister(indio_dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(st->ref);
|
||||
|
||||
|
@ -549,8 +549,8 @@ static int max9611_probe(struct i2c_client *client,
|
||||
ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Missing %s property for %s node\n",
|
||||
shunt_res_prop, of_node->full_name);
|
||||
"Missing %s property for %pOF node\n",
|
||||
shunt_res_prop, of_node);
|
||||
return ret;
|
||||
}
|
||||
max9611->shunt_resistor_uohm = of_shunt;
|
||||
|
@ -379,10 +379,12 @@ static int mcp3422_probe(struct i2c_client *client,
|
||||
|
||||
/* meaningful default configuration */
|
||||
config = (MCP3422_CONT_SAMPLING
|
||||
| MCP3422_CHANNEL_VALUE(1)
|
||||
| MCP3422_CHANNEL_VALUE(0)
|
||||
| MCP3422_PGA_VALUE(MCP3422_PGA_1)
|
||||
| MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240));
|
||||
mcp3422_update_config(adc, config);
|
||||
err = mcp3422_update_config(adc, config);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = devm_iio_device_register(&client->dev, indio_dev);
|
||||
if (err < 0)
|
||||
|
@ -572,8 +572,8 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
|
||||
struct clk_init_data init;
|
||||
const char *clk_parents[1];
|
||||
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div",
|
||||
of_node_full_name(indio_dev->dev.of_node));
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_div",
|
||||
indio_dev->dev.of_node);
|
||||
init.flags = 0;
|
||||
init.ops = &clk_divider_ops;
|
||||
clk_parents[0] = __clk_get_name(priv->clkin);
|
||||
@ -591,8 +591,8 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
|
||||
if (WARN_ON(IS_ERR(priv->adc_div_clk)))
|
||||
return PTR_ERR(priv->adc_div_clk);
|
||||
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en",
|
||||
of_node_full_name(indio_dev->dev.of_node));
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_en",
|
||||
indio_dev->dev.of_node);
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.ops = &clk_gate_ops;
|
||||
clk_parents[0] = __clk_get_name(priv->adc_div_clk);
|
||||
@ -915,6 +915,11 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
init_completion(&priv->done);
|
||||
|
||||
match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "failed to match device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->data = match->data;
|
||||
|
||||
indio_dev->name = priv->data->name;
|
||||
|
@ -184,6 +184,37 @@ static const struct iio_info mt6577_auxadc_info = {
|
||||
.read_raw = &mt6577_auxadc_read_raw,
|
||||
};
|
||||
|
||||
static int __maybe_unused mt6577_auxadc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(adc_dev->adc_clk);
|
||||
if (ret) {
|
||||
pr_err("failed to enable auxadc clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
MT6577_AUXADC_PDN_EN, 0);
|
||||
mdelay(MT6577_AUXADC_POWER_READY_MS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mt6577_auxadc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
0, MT6577_AUXADC_PDN_EN);
|
||||
clk_disable_unprepare(adc_dev->adc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6577_auxadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6577_auxadc_device *adc_dev;
|
||||
@ -269,8 +300,13 @@ static int mt6577_auxadc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops,
|
||||
mt6577_auxadc_suspend,
|
||||
mt6577_auxadc_resume);
|
||||
|
||||
static const struct of_device_id mt6577_auxadc_of_match[] = {
|
||||
{ .compatible = "mediatek,mt2701-auxadc", },
|
||||
{ .compatible = "mediatek,mt7622-auxadc", },
|
||||
{ .compatible = "mediatek,mt8173-auxadc", },
|
||||
{ }
|
||||
};
|
||||
@ -280,6 +316,7 @@ static struct platform_driver mt6577_auxadc_driver = {
|
||||
.driver = {
|
||||
.name = "mt6577-auxadc",
|
||||
.of_match_table = mt6577_auxadc_of_match,
|
||||
.pm = &mt6577_auxadc_pm_ops,
|
||||
},
|
||||
.probe = mt6577_auxadc_probe,
|
||||
.remove = mt6577_auxadc_remove,
|
||||
|
@ -224,6 +224,11 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
match = of_match_device(rockchip_saradc_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "failed to match device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info->data = match->data;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -172,7 +172,7 @@ struct stm32h7_adc_ck_spec {
|
||||
int div;
|
||||
};
|
||||
|
||||
const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
|
||||
static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
|
||||
/* 00: CK_ADC[1..3]: Asynchronous clock modes */
|
||||
{ 0, 0, 1 },
|
||||
{ 0, 1, 2 },
|
||||
|
@ -505,24 +505,24 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
|
||||
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
|
||||
|
||||
if (of_property_read_u32(node, "reg", &pval)) {
|
||||
dev_err(&client->dev, "invalid reg on %s\n",
|
||||
node->full_name);
|
||||
dev_err(&client->dev, "invalid reg on %pOF\n",
|
||||
node);
|
||||
continue;
|
||||
}
|
||||
|
||||
channel = pval;
|
||||
if (channel >= ADS1015_CHANNELS) {
|
||||
dev_err(&client->dev,
|
||||
"invalid channel index %d on %s\n",
|
||||
channel, node->full_name);
|
||||
"invalid channel index %d on %pOF\n",
|
||||
channel, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(node, "ti,gain", &pval)) {
|
||||
pga = pval;
|
||||
if (pga > 6) {
|
||||
dev_err(&client->dev, "invalid gain on %s\n",
|
||||
node->full_name);
|
||||
dev_err(&client->dev, "invalid gain on %pOF\n",
|
||||
node);
|
||||
of_node_put(node);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -532,8 +532,8 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
|
||||
data_rate = pval;
|
||||
if (data_rate > 7) {
|
||||
dev_err(&client->dev,
|
||||
"invalid data_rate on %s\n",
|
||||
node->full_name);
|
||||
"invalid data_rate on %pOF\n",
|
||||
node);
|
||||
of_node_put(node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -21,6 +21,13 @@ config ATLAS_PH_SENSOR
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
|
||||
config CCS811
|
||||
tristate "AMS CCS811 VOC sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to build I2C interface support for the AMS
|
||||
CCS811 VOC (Volatile Organic Compounds) sensor
|
||||
|
||||
config IAQCORE
|
||||
tristate "AMS iAQ-Core VOC sensors"
|
||||
depends on I2C
|
||||
|
@ -4,5 +4,6 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
|
||||
obj-$(CONFIG_CCS811) += ccs811.o
|
||||
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
|
||||
obj-$(CONFIG_VZ89X) += vz89x.o
|
||||
|
339
drivers/iio/chemical/ccs811.c
Normal file
339
drivers/iio/chemical/ccs811.c
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* ccs811.c - Support for AMS CCS811 VOC Sensor
|
||||
*
|
||||
* Copyright (C) 2017 Narcisa Vasile <narcisaanamaria12@gmail.com>
|
||||
*
|
||||
* Datasheet: ams.com/content/download/951091/2269479/CCS811_DS000459_3-00.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High)
|
||||
*
|
||||
* TODO:
|
||||
* 1. Make the drive mode selectable form userspace
|
||||
* 2. Add support for interrupts
|
||||
* 3. Adjust time to wait for data to be ready based on selected operation mode
|
||||
* 4. Read error register and put the information in logs
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define CCS811_STATUS 0x00
|
||||
#define CCS811_MEAS_MODE 0x01
|
||||
#define CCS811_ALG_RESULT_DATA 0x02
|
||||
#define CCS811_RAW_DATA 0x03
|
||||
#define CCS811_HW_ID 0x20
|
||||
#define CCS881_HW_ID_VALUE 0x81
|
||||
#define CCS811_HW_VERSION 0x21
|
||||
#define CCS811_HW_VERSION_VALUE 0x10
|
||||
#define CCS811_HW_VERSION_MASK 0xF0
|
||||
#define CCS811_ERR 0xE0
|
||||
/* Used to transition from boot to application mode */
|
||||
#define CCS811_APP_START 0xF4
|
||||
|
||||
/* Status register flags */
|
||||
#define CCS811_STATUS_ERROR BIT(0)
|
||||
#define CCS811_STATUS_DATA_READY BIT(3)
|
||||
#define CCS811_STATUS_APP_VALID_MASK BIT(4)
|
||||
#define CCS811_STATUS_APP_VALID_LOADED BIT(4)
|
||||
/*
|
||||
* Value of FW_MODE bit of STATUS register describes the sensor's state:
|
||||
* 0: Firmware is in boot mode, this allows new firmware to be loaded
|
||||
* 1: Firmware is in application mode. CCS811 is ready to take ADC measurements
|
||||
*/
|
||||
#define CCS811_STATUS_FW_MODE_MASK BIT(7)
|
||||
#define CCS811_STATUS_FW_MODE_APPLICATION BIT(7)
|
||||
|
||||
/* Measurement modes */
|
||||
#define CCS811_MODE_IDLE 0x00
|
||||
#define CCS811_MODE_IAQ_1SEC 0x10
|
||||
#define CCS811_MODE_IAQ_10SEC 0x20
|
||||
#define CCS811_MODE_IAQ_60SEC 0x30
|
||||
#define CCS811_MODE_RAW_DATA 0x40
|
||||
|
||||
#define CCS811_VOLTAGE_MASK 0x3FF
|
||||
|
||||
struct ccs811_reading {
|
||||
__be16 co2;
|
||||
__be16 voc;
|
||||
u8 status;
|
||||
u8 error;
|
||||
__be16 resistance;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct ccs811_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock; /* Protect readings */
|
||||
struct ccs811_reading buffer;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ccs811_channels[] = {
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
}, {
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_CO2,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
}, {
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_VOC,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* The CCS811 powers-up in boot mode. A setup write to CCS811_APP_START will
|
||||
* transition the sensor to application mode.
|
||||
*/
|
||||
static int ccs811_start_sensor_application(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, CCS811_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret & CCS811_STATUS_APP_VALID_MASK) !=
|
||||
CCS811_STATUS_APP_VALID_LOADED)
|
||||
return -EIO;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, CCS811_APP_START);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, CCS811_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret & CCS811_STATUS_FW_MODE_MASK) !=
|
||||
CCS811_STATUS_FW_MODE_APPLICATION) {
|
||||
dev_err(&client->dev, "Application failed to start. Sensor is still in boot mode.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccs811_setup(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ccs811_start_sensor_application(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE,
|
||||
CCS811_MODE_IAQ_1SEC);
|
||||
}
|
||||
|
||||
static int ccs811_get_measurement(struct ccs811_data *data)
|
||||
{
|
||||
int ret, tries = 11;
|
||||
|
||||
/* Maximum waiting time: 1s, as measurements are made every second */
|
||||
while (tries-- > 0) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, CCS811_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret & CCS811_STATUS_DATA_READY) || tries == 0)
|
||||
break;
|
||||
msleep(100);
|
||||
}
|
||||
if (!(ret & CCS811_STATUS_DATA_READY))
|
||||
return -EIO;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data(data->client,
|
||||
CCS811_ALG_RESULT_DATA, 8,
|
||||
(char *)&data->buffer);
|
||||
}
|
||||
|
||||
static int ccs811_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ccs811_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->lock);
|
||||
ret = ccs811_get_measurement(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
*val = be16_to_cpu(data->buffer.resistance) &
|
||||
CCS811_VOLTAGE_MASK;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CURRENT:
|
||||
*val = be16_to_cpu(data->buffer.resistance) >> 10;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CONCENTRATION:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_CO2:
|
||||
*val = be16_to_cpu(data->buffer.co2);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_MOD_VOC:
|
||||
*val = be16_to_cpu(data->buffer.voc);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
*val = 1;
|
||||
*val2 = 612903;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CURRENT:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CONCENTRATION:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_CO2:
|
||||
*val = 0;
|
||||
*val2 = 12834;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_MOD_VOC:
|
||||
*val = 0;
|
||||
*val2 = 84246;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (!(chan->type == IIO_CONCENTRATION &&
|
||||
chan->channel2 == IIO_MOD_CO2))
|
||||
return -EINVAL;
|
||||
*val = -400;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ccs811_info = {
|
||||
.read_raw = ccs811_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ccs811_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ccs811_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE
|
||||
| I2C_FUNC_SMBUS_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_READ_I2C_BLOCK))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Check hardware id (should be 0x81 for this family of devices) */
|
||||
ret = i2c_smbus_read_byte_data(client, CCS811_HW_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != CCS881_HW_ID_VALUE) {
|
||||
dev_err(&client->dev, "hardware id doesn't match CCS81x\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, CCS811_HW_VERSION);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret & CCS811_HW_VERSION_MASK) != CCS811_HW_VERSION_VALUE) {
|
||||
dev_err(&client->dev, "no CCS811 sensor\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ccs811_setup(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = &ccs811_info;
|
||||
|
||||
indio_dev->channels = ccs811_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ccs811_channels);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int ccs811_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE,
|
||||
CCS811_MODE_IDLE);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ccs811_id[] = {
|
||||
{"ccs811", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ccs811_id);
|
||||
|
||||
static struct i2c_driver ccs811_driver = {
|
||||
.driver = {
|
||||
.name = "ccs811",
|
||||
},
|
||||
.probe = ccs811_probe,
|
||||
.remove = ccs811_remove,
|
||||
.id_table = ccs811_id,
|
||||
};
|
||||
module_i2c_driver(ccs811_driver);
|
||||
|
||||
MODULE_AUTHOR("Narcisa Vasile <narcisaanamaria12@gmail.com>");
|
||||
MODULE_DESCRIPTION("CCS811 volatile organic compounds sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -15,6 +15,7 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
@ -345,6 +346,36 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_sensors_of_name_probe() - device tree probe for ST sensor name
|
||||
* @dev: driver model representation of the device.
|
||||
* @match: the OF match table for the device, containing compatible strings
|
||||
* but also a .data field with the corresponding internal kernel name
|
||||
* used by this sensor.
|
||||
* @name: device name buffer reference.
|
||||
* @len: device name buffer length.
|
||||
*
|
||||
* In effect this function matches a compatible string to an internal kernel
|
||||
* name for a certain sensor device, so that the rest of the autodetection can
|
||||
* rely on that name from this point on. I2C/SPI devices will be renamed
|
||||
* to match the internal kernel convention.
|
||||
*/
|
||||
void st_sensors_of_name_probe(struct device *dev,
|
||||
const struct of_device_id *match,
|
||||
char *name, int len)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_device(match, dev);
|
||||
if (!of_id || !of_id->data)
|
||||
return;
|
||||
|
||||
/* The name from the OF match takes precedence if present */
|
||||
strncpy(name, of_id->data, len);
|
||||
name[len - 1] = '\0';
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_of_name_probe);
|
||||
#else
|
||||
static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
||||
struct st_sensors_platform_data *defdata)
|
||||
|
@ -79,35 +79,6 @@ void st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_i2c_configure);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* st_sensors_of_i2c_probe() - device tree probe for ST I2C sensors
|
||||
* @client: the I2C client device for the sensor
|
||||
* @match: the OF match table for the device, containing compatible strings
|
||||
* but also a .data field with the corresponding internal kernel name
|
||||
* used by this sensor.
|
||||
*
|
||||
* In effect this function matches a compatible string to an internal kernel
|
||||
* name for a certain sensor device, so that the rest of the autodetection can
|
||||
* rely on that name from this point on. I2C client devices will be renamed
|
||||
* to match the internal kernel convention.
|
||||
*/
|
||||
void st_sensors_of_i2c_probe(struct i2c_client *client,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_device(match, &client->dev);
|
||||
if (!of_id)
|
||||
return;
|
||||
|
||||
/* The name from the OF match takes precedence if present */
|
||||
strncpy(client->name, of_id->data, sizeof(client->name));
|
||||
client->name[sizeof(client->name) - 1] = '\0';
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_of_i2c_probe);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int st_sensors_match_acpi_device(struct device *dev)
|
||||
{
|
||||
|
@ -42,6 +42,14 @@ struct stm32_dac_priv {
|
||||
struct stm32_dac_common common;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_dac_cfg - DAC configuration
|
||||
* @has_hfsel: DAC has high frequency control
|
||||
*/
|
||||
struct stm32_dac_cfg {
|
||||
bool has_hfsel;
|
||||
};
|
||||
|
||||
static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com)
|
||||
{
|
||||
return container_of(com, struct stm32_dac_priv, common);
|
||||
@ -57,6 +65,7 @@ static const struct regmap_config stm32_dac_regmap_cfg = {
|
||||
static int stm32_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct stm32_dac_cfg *cfg;
|
||||
struct stm32_dac_priv *priv;
|
||||
struct regmap *regmap;
|
||||
struct resource *res;
|
||||
@ -69,6 +78,8 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
cfg = (const struct stm32_dac_cfg *)
|
||||
of_match_device(dev->driver->of_match_table, dev)->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mmio = devm_ioremap_resource(dev, res);
|
||||
@ -121,12 +132,16 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
reset_control_deassert(priv->rst);
|
||||
}
|
||||
|
||||
/* When clock speed is higher than 80MHz, set HFSEL */
|
||||
priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
|
||||
ret = regmap_update_bits(regmap, STM32_DAC_CR, STM32H7_DAC_CR_HFSEL,
|
||||
priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0);
|
||||
if (ret)
|
||||
goto err_pclk;
|
||||
if (cfg && cfg->has_hfsel) {
|
||||
/* When clock speed is higher than 80MHz, set HFSEL */
|
||||
priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
|
||||
ret = regmap_update_bits(regmap, STM32_DAC_CR,
|
||||
STM32H7_DAC_CR_HFSEL,
|
||||
priv->common.hfsel ?
|
||||
STM32H7_DAC_CR_HFSEL : 0);
|
||||
if (ret)
|
||||
goto err_pclk;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, &priv->common);
|
||||
|
||||
@ -158,8 +173,17 @@ static int stm32_dac_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct stm32_dac_cfg stm32h7_dac_cfg = {
|
||||
.has_hfsel = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_dac_of_match[] = {
|
||||
{ .compatible = "st,stm32h7-dac-core", },
|
||||
{
|
||||
.compatible = "st,stm32f4-dac-core",
|
||||
}, {
|
||||
.compatible = "st,stm32h7-dac-core",
|
||||
.data = (void *)&stm32h7_dac_cfg,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
|
||||
|
@ -268,7 +268,7 @@ static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(stm32_dac_channels)) {
|
||||
dev_err(&indio_dev->dev, "Invalid st,dac-channel\n");
|
||||
dev_err(&indio_dev->dev, "Invalid reg property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1063,11 +1063,6 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq)
|
||||
case IRQF_TRIGGER_RISING:
|
||||
dev_info(&indio_dev->dev,
|
||||
"pulse interrupts on the rising edge\n");
|
||||
if (mpu3050->irq_opendrain) {
|
||||
dev_info(&indio_dev->dev,
|
||||
"rising edge incompatible with open drain\n");
|
||||
mpu3050->irq_opendrain = false;
|
||||
}
|
||||
break;
|
||||
case IRQF_TRIGGER_FALLING:
|
||||
mpu3050->irq_actl = true;
|
||||
@ -1078,11 +1073,6 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq)
|
||||
mpu3050->irq_latch = true;
|
||||
dev_info(&indio_dev->dev,
|
||||
"interrupts active high level\n");
|
||||
if (mpu3050->irq_opendrain) {
|
||||
dev_info(&indio_dev->dev,
|
||||
"active high incompatible with open drain\n");
|
||||
mpu3050->irq_opendrain = false;
|
||||
}
|
||||
/*
|
||||
* With level IRQs, we mask the IRQ until it is processed,
|
||||
* but with edge IRQs (pulses) we can queue several interrupts
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define LSM330DL_GYRO_DEV_NAME "lsm330dl_gyro"
|
||||
#define LSM330DLC_GYRO_DEV_NAME "lsm330dlc_gyro"
|
||||
#define L3GD20_GYRO_DEV_NAME "l3gd20"
|
||||
#define L3GD20H_GYRO_DEV_NAME "l3gd20h"
|
||||
#define L3G4IS_GYRO_DEV_NAME "l3g4is_ui"
|
||||
#define LSM330_GYRO_DEV_NAME "lsm330_gyro"
|
||||
#define LSM9DS0_GYRO_DEV_NAME "lsm9ds0_gyro"
|
||||
|
@ -35,6 +35,7 @@
|
||||
#define ST_GYRO_DEFAULT_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_GYRO_FS_AVL_245DPS 245
|
||||
#define ST_GYRO_FS_AVL_250DPS 250
|
||||
#define ST_GYRO_FS_AVL_500DPS 500
|
||||
#define ST_GYRO_FS_AVL_2000DPS 2000
|
||||
@ -196,17 +197,17 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
.wai = 0xd7,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = L3GD20_GYRO_DEV_NAME,
|
||||
[0] = L3GD20H_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
.addr = 0x20,
|
||||
.mask = 0xc0,
|
||||
.odr_avl = {
|
||||
{ .hz = 95, .value = 0x00, },
|
||||
{ .hz = 190, .value = 0x01, },
|
||||
{ .hz = 380, .value = 0x02, },
|
||||
{ .hz = 760, .value = 0x03, },
|
||||
{ .hz = 100, .value = 0x00, },
|
||||
{ .hz = 200, .value = 0x01, },
|
||||
{ .hz = 400, .value = 0x02, },
|
||||
{ .hz = 800, .value = 0x03, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
@ -224,7 +225,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
.mask = 0x30,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_GYRO_FS_AVL_250DPS,
|
||||
.num = ST_GYRO_FS_AVL_245DPS,
|
||||
.value = 0x00,
|
||||
.gain = IIO_DEGREE_TO_RAD(8750),
|
||||
},
|
||||
|
@ -40,6 +40,10 @@ static const struct of_device_id st_gyro_of_match[] = {
|
||||
.compatible = "st,l3gd20-gyro",
|
||||
.data = L3GD20_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,l3gd20h-gyro",
|
||||
.data = L3GD20H_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,l3g4is-gyro",
|
||||
.data = L3G4IS_GYRO_DEV_NAME,
|
||||
@ -71,7 +75,8 @@ static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
st_sensors_of_i2c_probe(client, st_gyro_of_match);
|
||||
st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, gdata);
|
||||
|
||||
@ -95,6 +100,7 @@ static const struct i2c_device_id st_gyro_id_table[] = {
|
||||
{ LSM330DL_GYRO_DEV_NAME },
|
||||
{ LSM330DLC_GYRO_DEV_NAME },
|
||||
{ L3GD20_GYRO_DEV_NAME },
|
||||
{ L3GD20H_GYRO_DEV_NAME },
|
||||
{ L3G4IS_GYRO_DEV_NAME },
|
||||
{ LSM330_GYRO_DEV_NAME },
|
||||
{ LSM9DS0_GYRO_DEV_NAME },
|
||||
|
@ -18,6 +18,56 @@
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_gyro.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* For new single-chip sensors use <device_name> as compatible string.
|
||||
* For old single-chip devices keep <device_name>-gyro to maintain
|
||||
* compatibility
|
||||
*/
|
||||
static const struct of_device_id st_gyro_of_match[] = {
|
||||
{
|
||||
.compatible = "st,l3g4200d-gyro",
|
||||
.data = L3G4200D_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330d-gyro",
|
||||
.data = LSM330D_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dl-gyro",
|
||||
.data = LSM330DL_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dlc-gyro",
|
||||
.data = LSM330DLC_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,l3gd20-gyro",
|
||||
.data = L3GD20_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,l3gd20h-gyro",
|
||||
.data = L3GD20H_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,l3g4is-gyro",
|
||||
.data = L3G4IS_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330-gyro",
|
||||
.data = LSM330_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm9ds0-gyro",
|
||||
.data = LSM9DS0_GYRO_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
||||
#else
|
||||
#define st_gyro_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@ -30,6 +80,8 @@ static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, gdata);
|
||||
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
@ -52,6 +104,7 @@ static const struct spi_device_id st_gyro_id_table[] = {
|
||||
{ LSM330DL_GYRO_DEV_NAME },
|
||||
{ LSM330DLC_GYRO_DEV_NAME },
|
||||
{ L3GD20_GYRO_DEV_NAME },
|
||||
{ L3GD20H_GYRO_DEV_NAME },
|
||||
{ L3G4IS_GYRO_DEV_NAME },
|
||||
{ LSM330_GYRO_DEV_NAME },
|
||||
{ LSM9DS0_GYRO_DEV_NAME },
|
||||
@ -62,6 +115,7 @@ MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
|
||||
static struct spi_driver st_gyro_driver = {
|
||||
.driver = {
|
||||
.name = "st-gyro-spi",
|
||||
.of_match_table = of_match_ptr(st_gyro_of_match),
|
||||
},
|
||||
.probe = st_gyro_spi_probe,
|
||||
.remove = st_gyro_spi_remove,
|
||||
|
@ -31,7 +31,8 @@ config HDC100X
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Texas Instruments
|
||||
HDC1000 and HDC1008 relative humidity and temperature sensors.
|
||||
HDC1000, HDC1008, HDC1010, HDC1050, and HDC1080 relative
|
||||
humidity and temperature sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
|
@ -13,6 +13,12 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Datasheets:
|
||||
* http://www.ti.com/product/HDC1000/datasheet
|
||||
* http://www.ti.com/product/HDC1008/datasheet
|
||||
* http://www.ti.com/product/HDC1010/datasheet
|
||||
* http://www.ti.com/product/HDC1050/datasheet
|
||||
* http://www.ti.com/product/HDC1080/datasheet
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -414,13 +420,29 @@ static int hdc100x_remove(struct i2c_client *client)
|
||||
|
||||
static const struct i2c_device_id hdc100x_id[] = {
|
||||
{ "hdc100x", 0 },
|
||||
{ "hdc1000", 0 },
|
||||
{ "hdc1008", 0 },
|
||||
{ "hdc1010", 0 },
|
||||
{ "hdc1050", 0 },
|
||||
{ "hdc1080", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, hdc100x_id);
|
||||
|
||||
static const struct of_device_id hdc100x_dt_ids[] = {
|
||||
{ .compatible = "ti,hdc1000" },
|
||||
{ .compatible = "ti,hdc1008" },
|
||||
{ .compatible = "ti,hdc1010" },
|
||||
{ .compatible = "ti,hdc1050" },
|
||||
{ .compatible = "ti,hdc1080" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hdc100x_dt_ids);
|
||||
|
||||
static struct i2c_driver hdc100x_driver = {
|
||||
.driver = {
|
||||
.name = "hdc100x",
|
||||
.of_match_table = of_match_ptr(hdc100x_dt_ids),
|
||||
},
|
||||
.probe = hdc100x_probe,
|
||||
.remove = hdc100x_remove,
|
||||
|
@ -30,12 +30,6 @@ struct hts221_transfer_function {
|
||||
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
};
|
||||
|
||||
#define HTS221_AVG_DEPTH 8
|
||||
struct hts221_avg_avl {
|
||||
u16 avg;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
enum hts221_sensor_type {
|
||||
HTS221_SENSOR_H,
|
||||
HTS221_SENSOR_T,
|
||||
@ -66,10 +60,9 @@ struct hts221_hw {
|
||||
|
||||
extern const struct dev_pm_ops hts221_pm_ops;
|
||||
|
||||
int hts221_config_drdy(struct hts221_hw *hw, bool enable);
|
||||
int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val);
|
||||
int hts221_probe(struct iio_dev *iio_dev);
|
||||
int hts221_power_on(struct hts221_hw *hw);
|
||||
int hts221_power_off(struct hts221_hw *hw);
|
||||
int hts221_set_enable(struct hts221_hw *hw, bool enable);
|
||||
int hts221_allocate_buffers(struct hts221_hw *hw);
|
||||
int hts221_allocate_trigger(struct hts221_hw *hw);
|
||||
|
||||
|
@ -20,8 +20,16 @@
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/platform_data/st_sensors_pdata.h>
|
||||
|
||||
#include "hts221.h"
|
||||
|
||||
#define HTS221_REG_DRDY_HL_ADDR 0x22
|
||||
#define HTS221_REG_DRDY_HL_MASK BIT(7)
|
||||
#define HTS221_REG_DRDY_PP_OD_ADDR 0x22
|
||||
#define HTS221_REG_DRDY_PP_OD_MASK BIT(6)
|
||||
#define HTS221_REG_DRDY_EN_ADDR 0x22
|
||||
#define HTS221_REG_DRDY_EN_MASK BIT(2)
|
||||
#define HTS221_REG_STATUS_ADDR 0x27
|
||||
#define HTS221_RH_DRDY_MASK BIT(1)
|
||||
#define HTS221_TEMP_DRDY_MASK BIT(0)
|
||||
@ -30,8 +38,12 @@ static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
return hts221_config_drdy(hw, state);
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR,
|
||||
HTS221_REG_DRDY_EN_MASK, state);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops hts221_trigger_ops = {
|
||||
@ -67,6 +79,9 @@ static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
|
||||
int hts221_allocate_trigger(struct hts221_hw *hw)
|
||||
{
|
||||
struct iio_dev *iio_dev = iio_priv_to_dev(hw);
|
||||
bool irq_active_low = false, open_drain = false;
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
struct st_sensors_platform_data *pdata;
|
||||
unsigned long irq_type;
|
||||
int err;
|
||||
|
||||
@ -76,6 +91,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
case IRQF_TRIGGER_RISING:
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
case IRQF_TRIGGER_FALLING:
|
||||
irq_active_low = true;
|
||||
break;
|
||||
default:
|
||||
dev_info(hw->dev,
|
||||
"mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
|
||||
@ -84,6 +103,24 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
|
||||
break;
|
||||
}
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR,
|
||||
HTS221_REG_DRDY_HL_MASK, irq_active_low);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
|
||||
if ((np && of_property_read_bool(np, "drive-open-drain")) ||
|
||||
(pdata && pdata->open_drain)) {
|
||||
irq_type |= IRQF_SHARED;
|
||||
open_drain = true;
|
||||
}
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR,
|
||||
HTS221_REG_DRDY_PP_OD_MASK,
|
||||
open_drain);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
|
||||
hts221_trigger_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
@ -109,12 +146,12 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
|
||||
|
||||
static int hts221_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return hts221_power_on(iio_priv(iio_dev));
|
||||
return hts221_set_enable(iio_priv(iio_dev), true);
|
||||
}
|
||||
|
||||
static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return hts221_power_off(iio_priv(iio_dev));
|
||||
return hts221_set_enable(iio_priv(iio_dev), false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops hts221_buffer_ops = {
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#define HTS221_REG_CNTRL1_ADDR 0x20
|
||||
#define HTS221_REG_CNTRL2_ADDR 0x21
|
||||
#define HTS221_REG_CNTRL3_ADDR 0x22
|
||||
|
||||
#define HTS221_REG_AVG_ADDR 0x10
|
||||
#define HTS221_REG_H_OUT_L 0x28
|
||||
@ -32,30 +31,9 @@
|
||||
#define HTS221_HUMIDITY_AVG_MASK 0x07
|
||||
#define HTS221_TEMP_AVG_MASK 0x38
|
||||
|
||||
#define HTS221_ODR_MASK 0x87
|
||||
#define HTS221_ODR_MASK 0x03
|
||||
#define HTS221_BDU_MASK BIT(2)
|
||||
|
||||
#define HTS221_DRDY_MASK BIT(2)
|
||||
|
||||
#define HTS221_ENABLE_SENSOR BIT(7)
|
||||
|
||||
#define HTS221_HUMIDITY_AVG_4 0x00 /* 0.4 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_8 0x01 /* 0.3 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_16 0x02 /* 0.2 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_32 0x03 /* 0.15 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_64 0x04 /* 0.1 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_128 0x05 /* 0.07 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_256 0x06 /* 0.05 %RH */
|
||||
#define HTS221_HUMIDITY_AVG_512 0x07 /* 0.03 %RH */
|
||||
|
||||
#define HTS221_TEMP_AVG_2 0x00 /* 0.08 degC */
|
||||
#define HTS221_TEMP_AVG_4 0x08 /* 0.05 degC */
|
||||
#define HTS221_TEMP_AVG_8 0x10 /* 0.04 degC */
|
||||
#define HTS221_TEMP_AVG_16 0x18 /* 0.03 degC */
|
||||
#define HTS221_TEMP_AVG_32 0x20 /* 0.02 degC */
|
||||
#define HTS221_TEMP_AVG_64 0x28 /* 0.015 degC */
|
||||
#define HTS221_TEMP_AVG_128 0x30 /* 0.01 degC */
|
||||
#define HTS221_TEMP_AVG_256 0x38 /* 0.007 degC */
|
||||
#define HTS221_ENABLE_MASK BIT(7)
|
||||
|
||||
/* calibration registers */
|
||||
#define HTS221_REG_0RH_CAL_X_H 0x36
|
||||
@ -73,10 +51,11 @@ struct hts221_odr {
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define HTS221_AVG_DEPTH 8
|
||||
struct hts221_avg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
|
||||
u16 avg_avl[HTS221_AVG_DEPTH];
|
||||
};
|
||||
|
||||
static const struct hts221_odr hts221_odr_table[] = {
|
||||
@ -90,28 +69,28 @@ static const struct hts221_avg hts221_avg_list[] = {
|
||||
.addr = HTS221_REG_AVG_ADDR,
|
||||
.mask = HTS221_HUMIDITY_AVG_MASK,
|
||||
.avg_avl = {
|
||||
{ 4, HTS221_HUMIDITY_AVG_4 },
|
||||
{ 8, HTS221_HUMIDITY_AVG_8 },
|
||||
{ 16, HTS221_HUMIDITY_AVG_16 },
|
||||
{ 32, HTS221_HUMIDITY_AVG_32 },
|
||||
{ 64, HTS221_HUMIDITY_AVG_64 },
|
||||
{ 128, HTS221_HUMIDITY_AVG_128 },
|
||||
{ 256, HTS221_HUMIDITY_AVG_256 },
|
||||
{ 512, HTS221_HUMIDITY_AVG_512 },
|
||||
4, /* 0.4 %RH */
|
||||
8, /* 0.3 %RH */
|
||||
16, /* 0.2 %RH */
|
||||
32, /* 0.15 %RH */
|
||||
64, /* 0.1 %RH */
|
||||
128, /* 0.07 %RH */
|
||||
256, /* 0.05 %RH */
|
||||
512, /* 0.03 %RH */
|
||||
},
|
||||
},
|
||||
{
|
||||
.addr = HTS221_REG_AVG_ADDR,
|
||||
.mask = HTS221_TEMP_AVG_MASK,
|
||||
.avg_avl = {
|
||||
{ 2, HTS221_TEMP_AVG_2 },
|
||||
{ 4, HTS221_TEMP_AVG_4 },
|
||||
{ 8, HTS221_TEMP_AVG_8 },
|
||||
{ 16, HTS221_TEMP_AVG_16 },
|
||||
{ 32, HTS221_TEMP_AVG_32 },
|
||||
{ 64, HTS221_TEMP_AVG_64 },
|
||||
{ 128, HTS221_TEMP_AVG_128 },
|
||||
{ 256, HTS221_TEMP_AVG_256 },
|
||||
2, /* 0.08 degC */
|
||||
4, /* 0.05 degC */
|
||||
8, /* 0.04 degC */
|
||||
16, /* 0.03 degC */
|
||||
32, /* 0.02 degC */
|
||||
64, /* 0.015 degC */
|
||||
128, /* 0.01 degC */
|
||||
256, /* 0.007 degC */
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -152,8 +131,7 @@ static const struct iio_chan_spec hts221_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
|
||||
u8 val)
|
||||
int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
@ -166,7 +144,7 @@ static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
data = (data & ~mask) | (val & mask);
|
||||
data = (data & ~mask) | ((val << __ffs(mask)) & mask);
|
||||
|
||||
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
|
||||
if (err < 0)
|
||||
@ -199,21 +177,9 @@ static int hts221_check_whoami(struct hts221_hw *hw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hts221_config_drdy(struct hts221_hw *hw, bool enable)
|
||||
{
|
||||
u8 val = enable ? BIT(2) : 0;
|
||||
int err;
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
|
||||
HTS221_DRDY_MASK, val);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
|
||||
{
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
|
||||
if (hts221_odr_table[i].hz == odr)
|
||||
@ -222,9 +188,8 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
|
||||
if (i == ARRAY_SIZE(hts221_odr_table))
|
||||
return -EINVAL;
|
||||
|
||||
val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ODR_MASK, val);
|
||||
HTS221_ODR_MASK, hts221_odr_table[i].val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -241,14 +206,13 @@ static int hts221_update_avg(struct hts221_hw *hw,
|
||||
const struct hts221_avg *avg = &hts221_avg_list[type];
|
||||
|
||||
for (i = 0; i < HTS221_AVG_DEPTH; i++)
|
||||
if (avg->avg_avl[i].avg == val)
|
||||
if (avg->avg_avl[i] == val)
|
||||
break;
|
||||
|
||||
if (i == HTS221_AVG_DEPTH)
|
||||
return -EINVAL;
|
||||
|
||||
err = hts221_write_with_mask(hw, avg->addr, avg->mask,
|
||||
avg->avg_avl[i].val);
|
||||
err = hts221_write_with_mask(hw, avg->addr, avg->mask, i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -283,7 +247,7 @@ hts221_sysfs_rh_oversampling_avail(struct device *dev,
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
avg->avg_avl[i].avg);
|
||||
avg->avg_avl[i]);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
@ -300,36 +264,22 @@ hts221_sysfs_temp_oversampling_avail(struct device *dev,
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
avg->avg_avl[i].avg);
|
||||
avg->avg_avl[i]);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int hts221_power_on(struct hts221_hw *hw)
|
||||
int hts221_set_enable(struct hts221_hw *hw, bool enable)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = hts221_update_odr(hw, hw->odr);
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hts221_power_off(struct hts221_hw *hw)
|
||||
{
|
||||
__le16 data = 0;
|
||||
int err;
|
||||
|
||||
err = hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
|
||||
(u8 *)&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->enabled = false;
|
||||
hw->enabled = enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -484,7 +434,7 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
|
||||
u8 data[HTS221_DATA_SIZE];
|
||||
int err;
|
||||
|
||||
err = hts221_power_on(hw);
|
||||
err = hts221_set_enable(hw, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -494,7 +444,7 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hts221_power_off(hw);
|
||||
hts221_set_enable(hw, false);
|
||||
|
||||
*val = (s16)get_unaligned_le16(data);
|
||||
|
||||
@ -534,13 +484,13 @@ static int hts221_read_raw(struct iio_dev *iio_dev,
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
avg = &hts221_avg_list[HTS221_SENSOR_H];
|
||||
idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
|
||||
*val = avg->avg_avl[idx].avg;
|
||||
*val = avg->avg_avl[idx];
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
avg = &hts221_avg_list[HTS221_SENSOR_T];
|
||||
idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
|
||||
*val = avg->avg_avl[idx].avg;
|
||||
*val = avg->avg_avl[idx];
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
@ -644,8 +594,6 @@ int hts221_probe(struct iio_dev *iio_dev)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->odr = hts221_odr_table[0].hz;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
iio_dev->available_scan_masks = hts221_scan_masks;
|
||||
@ -654,6 +602,16 @@ int hts221_probe(struct iio_dev *iio_dev)
|
||||
iio_dev->name = HTS221_DEV_NAME;
|
||||
iio_dev->info = &hts221_info;
|
||||
|
||||
/* enable Block Data Update */
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_BDU_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = hts221_update_odr(hw, hts221_odr_table[0].hz);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* configure humidity sensor */
|
||||
err = hts221_parse_rh_caldata(hw);
|
||||
if (err < 0) {
|
||||
@ -661,7 +619,7 @@ int hts221_probe(struct iio_dev *iio_dev)
|
||||
return err;
|
||||
}
|
||||
|
||||
data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
|
||||
data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3];
|
||||
err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to set rh oversampling ratio\n");
|
||||
@ -676,7 +634,7 @@ int hts221_probe(struct iio_dev *iio_dev)
|
||||
return err;
|
||||
}
|
||||
|
||||
data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
|
||||
data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3];
|
||||
err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev,
|
||||
@ -702,11 +660,10 @@ static int __maybe_unused hts221_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
__le16 data = 0;
|
||||
int err;
|
||||
|
||||
err = hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
|
||||
(u8 *)&data);
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK, false);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
@ -718,7 +675,8 @@ static int __maybe_unused hts221_resume(struct device *dev)
|
||||
int err = 0;
|
||||
|
||||
if (hw->enabled)
|
||||
err = hts221_update_odr(hw, hw->odr);
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK, true);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -238,11 +238,19 @@ static const struct i2c_device_id htu21_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, htu21_id);
|
||||
|
||||
static const struct of_device_id htu21_of_match[] = {
|
||||
{ .compatible = "meas,htu21", },
|
||||
{ .compatible = "meas,ms8607-humidity", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, htu21_of_match);
|
||||
|
||||
static struct i2c_driver htu21_driver = {
|
||||
.probe = htu21_probe,
|
||||
.id_table = htu21_id,
|
||||
.driver = {
|
||||
.name = "htu21",
|
||||
.of_match_table = of_match_ptr(htu21_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -217,7 +217,7 @@ static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq)
|
||||
return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val);
|
||||
}
|
||||
|
||||
static const unsigned adis16400_3db_divisors[] = {
|
||||
static const unsigned int adis16400_3db_divisors[] = {
|
||||
[0] = 2, /* Special case */
|
||||
[1] = 6,
|
||||
[2] = 12,
|
||||
@ -890,7 +890,7 @@ static const struct adis_data adis16400_data = {
|
||||
static void adis16400_setup_chan_mask(struct adis16400_state *st)
|
||||
{
|
||||
const struct adis16400_chip_info *chip_info = st->variant;
|
||||
unsigned i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < chip_info->num_channels; i++) {
|
||||
const struct iio_chan_spec *ch = &chip_info->channels[i];
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/platform_data/st_sensors_pdata.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define ST_LSM6DSX_REG_FIFO_THL_ADDR 0x06
|
||||
@ -39,6 +41,8 @@
|
||||
#define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR 0x08
|
||||
#define ST_LSM6DSX_REG_HLACTIVE_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_HLACTIVE_MASK BIT(5)
|
||||
#define ST_LSM6DSX_REG_PP_OD_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_PP_OD_MASK BIT(4)
|
||||
#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a
|
||||
#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0)
|
||||
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
|
||||
@ -417,6 +421,8 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
|
||||
|
||||
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
struct st_sensors_platform_data *pdata;
|
||||
struct iio_buffer *buffer;
|
||||
unsigned long irq_type;
|
||||
bool irq_active_low;
|
||||
@ -444,6 +450,17 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
|
||||
if ((np && of_property_read_bool(np, "drive-open-drain")) ||
|
||||
(pdata && pdata->open_drain)) {
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_PP_OD_ADDR,
|
||||
ST_LSM6DSX_REG_PP_OD_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
irq_type |= IRQF_SHARED;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||
st_lsm6dsx_handler_irq,
|
||||
st_lsm6dsx_handler_thread,
|
||||
|
@ -44,7 +44,7 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
|
||||
}
|
||||
mapi->map = &maps[i];
|
||||
mapi->indio_dev = indio_dev;
|
||||
list_add(&mapi->l, &iio_map_list);
|
||||
list_add_tail(&mapi->l, &iio_map_list);
|
||||
i++;
|
||||
}
|
||||
error_ret:
|
||||
@ -205,8 +205,8 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
||||
if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER)
|
||||
break;
|
||||
else if (name && index >= 0) {
|
||||
pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",
|
||||
np->full_name, name ? name : "", index);
|
||||
pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n",
|
||||
np, name ? name : "", index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
* IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
|
||||
*
|
||||
* TODO: illuminance channel, buffer
|
||||
* TODO: illuminance channel
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -20,6 +20,10 @@
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
@ -30,6 +34,7 @@
|
||||
#define RPR0521_REG_PXS_DATA 0x44 /* 16-bit, little endian */
|
||||
#define RPR0521_REG_ALS_DATA0 0x46 /* 16-bit, little endian */
|
||||
#define RPR0521_REG_ALS_DATA1 0x48 /* 16-bit, little endian */
|
||||
#define RPR0521_REG_INTERRUPT 0x4A
|
||||
#define RPR0521_REG_PS_OFFSET_LSB 0x53
|
||||
#define RPR0521_REG_ID 0x92
|
||||
|
||||
@ -42,16 +47,31 @@
|
||||
#define RPR0521_ALS_DATA1_GAIN_SHIFT 2
|
||||
#define RPR0521_PXS_GAIN_MASK GENMASK(5, 4)
|
||||
#define RPR0521_PXS_GAIN_SHIFT 4
|
||||
#define RPR0521_PXS_PERSISTENCE_MASK GENMASK(3, 0)
|
||||
#define RPR0521_INTERRUPT_INT_TRIG_PS_MASK BIT(0)
|
||||
#define RPR0521_INTERRUPT_INT_TRIG_ALS_MASK BIT(1)
|
||||
#define RPR0521_INTERRUPT_INT_REASSERT_MASK BIT(3)
|
||||
#define RPR0521_INTERRUPT_ALS_INT_STATUS_MASK BIT(6)
|
||||
#define RPR0521_INTERRUPT_PS_INT_STATUS_MASK BIT(7)
|
||||
|
||||
#define RPR0521_MODE_ALS_ENABLE BIT(7)
|
||||
#define RPR0521_MODE_ALS_DISABLE 0x00
|
||||
#define RPR0521_MODE_PXS_ENABLE BIT(6)
|
||||
#define RPR0521_MODE_PXS_DISABLE 0x00
|
||||
#define RPR0521_PXS_PERSISTENCE_DRDY 0x00
|
||||
|
||||
#define RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE BIT(0)
|
||||
#define RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE 0x00
|
||||
#define RPR0521_INTERRUPT_INT_TRIG_ALS_ENABLE BIT(1)
|
||||
#define RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE 0x00
|
||||
#define RPR0521_INTERRUPT_INT_REASSERT_ENABLE BIT(3)
|
||||
#define RPR0521_INTERRUPT_INT_REASSERT_DISABLE 0x00
|
||||
|
||||
#define RPR0521_MANUFACT_ID 0xE0
|
||||
#define RPR0521_DEFAULT_MEAS_TIME 0x06 /* ALS - 100ms, PXS - 100ms */
|
||||
|
||||
#define RPR0521_DRV_NAME "RPR0521"
|
||||
#define RPR0521_IRQ_NAME "rpr0521_event"
|
||||
#define RPR0521_REGMAP_NAME "rpr0521_regmap"
|
||||
|
||||
#define RPR0521_SLEEP_DELAY_MS 2000
|
||||
@ -167,6 +187,9 @@ struct rpr0521_data {
|
||||
bool als_dev_en;
|
||||
bool pxs_dev_en;
|
||||
|
||||
struct iio_trigger *drdy_trigger0;
|
||||
s64 irq_timestamp;
|
||||
|
||||
/* optimize runtime pm ops - enable/disable device only if needed */
|
||||
bool als_ps_need_en;
|
||||
bool pxs_ps_need_en;
|
||||
@ -196,6 +219,19 @@ static const struct attribute_group rpr0521_attribute_group = {
|
||||
.attrs = rpr0521_attributes,
|
||||
};
|
||||
|
||||
/* Order of the channel data in buffer */
|
||||
enum rpr0521_scan_index_order {
|
||||
RPR0521_CHAN_INDEX_PXS,
|
||||
RPR0521_CHAN_INDEX_BOTH,
|
||||
RPR0521_CHAN_INDEX_IR,
|
||||
};
|
||||
|
||||
static const unsigned long rpr0521_available_scan_masks[] = {
|
||||
BIT(RPR0521_CHAN_INDEX_PXS) | BIT(RPR0521_CHAN_INDEX_BOTH) |
|
||||
BIT(RPR0521_CHAN_INDEX_IR),
|
||||
0
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec rpr0521_channels[] = {
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
@ -204,6 +240,13 @@ static const struct iio_chan_spec rpr0521_channels[] = {
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = RPR0521_CHAN_INDEX_PXS,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
@ -213,6 +256,13 @@ static const struct iio_chan_spec rpr0521_channels[] = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = RPR0521_CHAN_INDEX_BOTH,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
@ -222,6 +272,13 @@ static const struct iio_chan_spec rpr0521_channels[] = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = RPR0521_CHAN_INDEX_IR,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -330,6 +387,198 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Interrupt register tells if this sensor caused the interrupt or not. */
|
||||
static inline bool rpr0521_is_triggered(struct rpr0521_data *data)
|
||||
{
|
||||
int ret;
|
||||
int reg;
|
||||
|
||||
ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, ®);
|
||||
if (ret < 0)
|
||||
return false; /* Reg read failed. */
|
||||
if (reg &
|
||||
(RPR0521_INTERRUPT_ALS_INT_STATUS_MASK |
|
||||
RPR0521_INTERRUPT_PS_INT_STATUS_MASK))
|
||||
return true;
|
||||
else
|
||||
return false; /* Int not from this sensor. */
|
||||
}
|
||||
|
||||
/* IRQ to trigger handler */
|
||||
static irqreturn_t rpr0521_drdy_irq_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->irq_timestamp = iio_get_time_ns(indio_dev);
|
||||
/*
|
||||
* We need to wake the thread to read the interrupt reg. It
|
||||
* is not possible to do that here because regmap_read takes a
|
||||
* mutex.
|
||||
*/
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t rpr0521_drdy_irq_thread(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (rpr0521_is_triggered(data)) {
|
||||
iio_trigger_poll_chained(data->drdy_trigger0);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t rpr0521_trigger_consumer_store_time(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
|
||||
/* Other trigger polls store time here. */
|
||||
if (!iio_trigger_using_own(indio_dev))
|
||||
pf->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||
int err;
|
||||
|
||||
u8 buffer[16]; /* 3 16-bit channels + padding + ts */
|
||||
|
||||
/* Use irq timestamp when reasonable. */
|
||||
if (iio_trigger_using_own(indio_dev) && data->irq_timestamp) {
|
||||
pf->timestamp = data->irq_timestamp;
|
||||
data->irq_timestamp = 0;
|
||||
}
|
||||
/* Other chained trigger polls get timestamp only here. */
|
||||
if (!pf->timestamp)
|
||||
pf->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
|
||||
&buffer,
|
||||
(3 * 2) + 1); /* 3 * 16-bit + (discarded) int clear reg. */
|
||||
if (!err)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
buffer, pf->timestamp);
|
||||
else
|
||||
dev_err(&data->client->dev,
|
||||
"Trigger consumer can't read from sensor.\n");
|
||||
pf->timestamp = 0;
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rpr0521_write_int_enable(struct rpr0521_data *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Interrupt after each measurement */
|
||||
err = regmap_update_bits(data->regmap, RPR0521_REG_PXS_CTRL,
|
||||
RPR0521_PXS_PERSISTENCE_MASK,
|
||||
RPR0521_PXS_PERSISTENCE_DRDY);
|
||||
if (err) {
|
||||
dev_err(&data->client->dev, "PS control reg write fail.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Ignore latch and mode because of drdy */
|
||||
err = regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
|
||||
RPR0521_INTERRUPT_INT_REASSERT_DISABLE |
|
||||
RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
|
||||
RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE
|
||||
);
|
||||
if (err) {
|
||||
dev_err(&data->client->dev, "Interrupt setup write fail.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpr0521_write_int_disable(struct rpr0521_data *data)
|
||||
{
|
||||
/* Don't care of clearing mode, assert and latch. */
|
||||
return regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
|
||||
RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
|
||||
RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger producer enable / disable. Note that there will be trigs only when
|
||||
* measurement data is ready to be read.
|
||||
*/
|
||||
static int rpr0521_pxs_drdy_set_state(struct iio_trigger *trigger,
|
||||
bool enable_drdy)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);
|
||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||
int err;
|
||||
|
||||
if (enable_drdy)
|
||||
err = rpr0521_write_int_enable(data);
|
||||
else
|
||||
err = rpr0521_write_int_disable(data);
|
||||
if (err)
|
||||
dev_err(&data->client->dev, "rpr0521_pxs_drdy_set_state failed\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops rpr0521_trigger_ops = {
|
||||
.set_trigger_state = rpr0521_pxs_drdy_set_state,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static int rpr0521_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
err = rpr0521_set_power_state(data, true,
|
||||
(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
|
||||
mutex_unlock(&data->lock);
|
||||
if (err)
|
||||
dev_err(&data->client->dev, "_buffer_preenable fail\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rpr0521_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
err = rpr0521_set_power_state(data, false,
|
||||
(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
|
||||
mutex_unlock(&data->lock);
|
||||
if (err)
|
||||
dev_err(&data->client->dev, "_buffer_postdisable fail\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops = {
|
||||
.preenable = rpr0521_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
.postdisable = rpr0521_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int rpr0521_get_gain(struct rpr0521_data *data, int chan,
|
||||
int *val, int *val2)
|
||||
{
|
||||
@ -473,6 +722,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int busy;
|
||||
u8 device_mask;
|
||||
__le16 raw_data;
|
||||
|
||||
@ -481,26 +731,30 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
|
||||
if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY)
|
||||
return -EINVAL;
|
||||
|
||||
busy = iio_device_claim_direct_mode(indio_dev);
|
||||
if (busy)
|
||||
return -EBUSY;
|
||||
|
||||
device_mask = rpr0521_data_reg[chan->address].device_mask;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = rpr0521_set_power_state(data, true, device_mask);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto rpr0521_read_raw_out;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap,
|
||||
rpr0521_data_reg[chan->address].address,
|
||||
&raw_data, sizeof(raw_data));
|
||||
if (ret < 0) {
|
||||
rpr0521_set_power_state(data, false, device_mask);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
goto rpr0521_read_raw_out;
|
||||
}
|
||||
|
||||
ret = rpr0521_set_power_state(data, false, device_mask);
|
||||
|
||||
rpr0521_read_raw_out:
|
||||
mutex_unlock(&data->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -617,12 +871,15 @@ static int rpr0521_init(struct rpr0521_data *data)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
data->irq_timestamp = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpr0521_poweroff(struct rpr0521_data *data)
|
||||
{
|
||||
int ret;
|
||||
int tmp;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
|
||||
RPR0521_MODE_ALS_MASK |
|
||||
@ -635,6 +892,16 @@ static int rpr0521_poweroff(struct rpr0521_data *data)
|
||||
data->als_dev_en = false;
|
||||
data->pxs_dev_en = false;
|
||||
|
||||
/*
|
||||
* Int pin keeps state after power off. Set pin to high impedance
|
||||
* mode to prevent power drain.
|
||||
*/
|
||||
ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, &tmp);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "Failed to reset int pin.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -707,6 +974,61 @@ static int rpr0521_probe(struct i2c_client *client,
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
/*
|
||||
* If sensor write/read is needed in _probe after _use_autosuspend,
|
||||
* sensor needs to be _resumed first using rpr0521_set_power_state().
|
||||
*/
|
||||
|
||||
/* IRQ to trigger setup */
|
||||
if (client->irq) {
|
||||
/* Trigger0 producer setup */
|
||||
data->drdy_trigger0 = devm_iio_trigger_alloc(
|
||||
indio_dev->dev.parent,
|
||||
"%s-dev%d", indio_dev->name, indio_dev->id);
|
||||
if (!data->drdy_trigger0) {
|
||||
ret = -ENOMEM;
|
||||
goto err_pm_disable;
|
||||
}
|
||||
data->drdy_trigger0->dev.parent = indio_dev->dev.parent;
|
||||
data->drdy_trigger0->ops = &rpr0521_trigger_ops;
|
||||
indio_dev->available_scan_masks = rpr0521_available_scan_masks;
|
||||
iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);
|
||||
|
||||
/* Ties irq to trigger producer handler. */
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
RPR0521_IRQ_NAME, indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "request irq %d for trigger0 failed\n",
|
||||
client->irq);
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
ret = devm_iio_trigger_register(indio_dev->dev.parent,
|
||||
data->drdy_trigger0);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "iio trigger register failed\n");
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now whole pipe from physical interrupt (irq defined by
|
||||
* devicetree to device) to trigger0 output is set up.
|
||||
*/
|
||||
|
||||
/* Trigger consumer setup */
|
||||
ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent,
|
||||
indio_dev,
|
||||
rpr0521_trigger_consumer_store_time,
|
||||
rpr0521_trigger_consumer_handler,
|
||||
&rpr0521_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
goto err_pm_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
|
@ -11,6 +11,8 @@
|
||||
* 7-bit I2C slave address 0x39 (TCS34721, TCS34723) or 0x29 (TCS34725,
|
||||
* TCS34727)
|
||||
*
|
||||
* Datasheet: http://ams.com/eng/content/download/319364/1117183/file/TCS3472_Datasheet_EN_v2.pdf
|
||||
*
|
||||
* TODO: interrupt support, thresholds, wait time
|
||||
*/
|
||||
|
||||
@ -169,7 +171,7 @@ static int tcs3472_write_raw(struct iio_dev *indio_dev,
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (val2 == (256 - i) * 2400) {
|
||||
data->atime = i;
|
||||
return i2c_smbus_write_word_data(
|
||||
return i2c_smbus_write_byte_data(
|
||||
data->client, TCS3472_ATIME,
|
||||
data->atime);
|
||||
}
|
||||
|
@ -784,6 +784,7 @@ static const struct iio_info ak8975_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id ak_acpi_match[] = {
|
||||
{"AK8975", AK8975},
|
||||
{"AK8963", AK8963},
|
||||
@ -793,6 +794,7 @@ static const struct acpi_device_id ak_acpi_match[] = {
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
|
||||
#endif
|
||||
|
||||
static const char *ak8975_match_acpi_device(struct device *dev,
|
||||
enum asahi_compass_chipset *chipset)
|
||||
|
@ -315,7 +315,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||
},
|
||||
},
|
||||
},
|
||||
.multi_read_bit = false,
|
||||
.multi_read_bit = true,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
|
@ -59,7 +59,8 @@ static int st_magn_i2c_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
st_sensors_of_i2c_probe(client, st_magn_of_match);
|
||||
st_sensors_of_name_probe(&client->dev, st_magn_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, mdata);
|
||||
|
||||
|
@ -18,6 +18,28 @@
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_magn.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* For new single-chip sensors use <device_name> as compatible string.
|
||||
* For old single-chip devices keep <device_name>-magn to maintain
|
||||
* compatibility
|
||||
*/
|
||||
static const struct of_device_id st_magn_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lis3mdl-magn",
|
||||
.data = LIS3MDL_MAGN_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303agr-magn",
|
||||
.data = LSM303AGR_MAGN_DEV_NAME,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_magn_of_match);
|
||||
#else
|
||||
#define st_magn_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_magn_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@ -30,6 +52,8 @@ static int st_magn_spi_probe(struct spi_device *spi)
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_magn_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, mdata);
|
||||
|
||||
err = st_magn_common_probe(indio_dev);
|
||||
@ -57,6 +81,7 @@ MODULE_DEVICE_TABLE(spi, st_magn_id_table);
|
||||
static struct spi_driver st_magn_driver = {
|
||||
.driver = {
|
||||
.name = "st-magn-spi",
|
||||
.of_match_table = of_match_ptr(st_magn_of_match),
|
||||
},
|
||||
.probe = st_magn_spi_probe,
|
||||
.remove = st_magn_spi_remove,
|
||||
|
@ -238,7 +238,7 @@ static int dev_rot_parse_report(struct platform_device *pdev,
|
||||
static int hid_dev_rot_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
static char *name;
|
||||
char *name;
|
||||
struct iio_dev *indio_dev;
|
||||
struct dev_rot_state *rot_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
|
@ -181,11 +181,21 @@ static const struct i2c_device_id ms5637_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ms5637_id);
|
||||
|
||||
static const struct of_device_id ms5637_of_match[] = {
|
||||
{ .compatible = "meas,ms5637", },
|
||||
{ .compatible = "meas,ms5805", },
|
||||
{ .compatible = "meas,ms5837", },
|
||||
{ .compatible = "meas,ms8607-temppressure", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ms5637_of_match);
|
||||
|
||||
static struct i2c_driver ms5637_driver = {
|
||||
.probe = ms5637_probe,
|
||||
.id_table = ms5637_id,
|
||||
.driver = {
|
||||
.name = "ms5637"
|
||||
.name = "ms5637",
|
||||
.of_match_table = of_match_ptr(ms5637_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -77,7 +77,8 @@ static int st_press_i2c_probe(struct i2c_client *client,
|
||||
press_data = iio_priv(indio_dev);
|
||||
|
||||
if (client->dev.of_node) {
|
||||
st_sensors_of_i2c_probe(client, st_press_of_match);
|
||||
st_sensors_of_name_probe(&client->dev, st_press_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
ret = st_sensors_match_acpi_device(&client->dev);
|
||||
if ((ret < 0) || (ret >= ST_PRESS_MAX))
|
||||
|
@ -18,6 +18,36 @@
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_pressure.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* For new single-chip sensors use <device_name> as compatible string.
|
||||
* For old single-chip devices keep <device_name>-press to maintain
|
||||
* compatibility
|
||||
*/
|
||||
static const struct of_device_id st_press_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lps001wp-press",
|
||||
.data = LPS001WP_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps25h-press",
|
||||
.data = LPS25H_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps331ap-press",
|
||||
.data = LPS331AP_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps22hb-press",
|
||||
.data = LPS22HB_PRESS_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_press_of_match);
|
||||
#else
|
||||
#define st_press_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_press_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@ -30,6 +60,8 @@ static int st_press_spi_probe(struct spi_device *spi)
|
||||
|
||||
press_data = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_press_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, press_data);
|
||||
|
||||
err = st_press_common_probe(indio_dev);
|
||||
@ -58,6 +90,7 @@ MODULE_DEVICE_TABLE(spi, st_press_id_table);
|
||||
static struct spi_driver st_press_driver = {
|
||||
.driver = {
|
||||
.name = "st-press-spi",
|
||||
.of_match_table = of_match_ptr(st_press_of_match),
|
||||
},
|
||||
.probe = st_press_spi_probe,
|
||||
.remove = st_press_spi_remove,
|
||||
|
@ -141,14 +141,14 @@ struct zpa2326_private {
|
||||
struct regulator *vdd;
|
||||
};
|
||||
|
||||
#define zpa2326_err(_idev, _format, _arg...) \
|
||||
dev_err(_idev->dev.parent, _format, ##_arg)
|
||||
#define zpa2326_err(idev, fmt, ...) \
|
||||
dev_err(idev->dev.parent, fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define zpa2326_warn(_idev, _format, _arg...) \
|
||||
dev_warn(_idev->dev.parent, _format, ##_arg)
|
||||
#define zpa2326_warn(idev, fmt, ...) \
|
||||
dev_warn(idev->dev.parent, fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define zpa2326_dbg(_idev, _format, _arg...) \
|
||||
dev_dbg(_idev->dev.parent, _format, ##_arg)
|
||||
#define zpa2326_dbg(idev, fmt, ...) \
|
||||
dev_dbg(idev->dev.parent, fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
|
@ -214,11 +214,18 @@ static const struct i2c_device_id tsys01_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tsys01_id);
|
||||
|
||||
static const struct of_device_id tsys01_of_match[] = {
|
||||
{ .compatible = "meas,tsys01", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tsys01_of_match);
|
||||
|
||||
static struct i2c_driver tsys01_driver = {
|
||||
.probe = tsys01_i2c_probe,
|
||||
.id_table = tsys01_id,
|
||||
.driver = {
|
||||
.name = "tsys01",
|
||||
.of_match_table = of_match_ptr(tsys01_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -99,9 +99,14 @@
|
||||
#define AD7280A_DEVADDR_MASTER 0
|
||||
#define AD7280A_DEVADDR_ALL 0x1F
|
||||
/* 5-bit device address is sent LSB first */
|
||||
#define AD7280A_DEVADDR(addr) (((addr & 0x1) << 4) | ((addr & 0x2) << 3) | \
|
||||
(addr & 0x4) | ((addr & 0x8) >> 3) | \
|
||||
((addr & 0x10) >> 4))
|
||||
static unsigned int ad7280a_devaddr(unsigned int addr)
|
||||
{
|
||||
return ((addr & 0x1) << 4) |
|
||||
((addr & 0x2) << 3) |
|
||||
(addr & 0x4) |
|
||||
((addr & 0x8) >> 3) |
|
||||
((addr & 0x10) >> 4);
|
||||
}
|
||||
|
||||
/* During a read a valid write is mandatory.
|
||||
* So writing to the highest available address (Address 0x1F)
|
||||
@ -372,7 +377,7 @@ static int ad7280_chain_setup(struct ad7280_state *st)
|
||||
if (ad7280_check_crc(st, val))
|
||||
return -EIO;
|
||||
|
||||
if (n != AD7280A_DEVADDR(val >> 27))
|
||||
if (n != ad7280a_devaddr(val >> 27))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -511,7 +516,7 @@ static int ad7280_channel_init(struct ad7280_state *st)
|
||||
st->channels[cnt].info_mask_shared_by_type =
|
||||
BIT(IIO_CHAN_INFO_SCALE);
|
||||
st->channels[cnt].address =
|
||||
AD7280A_DEVADDR(dev) << 8 | ch;
|
||||
ad7280a_devaddr(dev) << 8 | ch;
|
||||
st->channels[cnt].scan_index = cnt;
|
||||
st->channels[cnt].scan_type.sign = 'u';
|
||||
st->channels[cnt].scan_type.realbits = 12;
|
||||
@ -558,7 +563,7 @@ static int ad7280_attr_init(struct ad7280_state *st)
|
||||
for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6;
|
||||
ch++, cnt++) {
|
||||
st->iio_attr[cnt].address =
|
||||
AD7280A_DEVADDR(dev) << 8 | ch;
|
||||
ad7280a_devaddr(dev) << 8 | ch;
|
||||
st->iio_attr[cnt].dev_attr.attr.mode =
|
||||
0644;
|
||||
st->iio_attr[cnt].dev_attr.show =
|
||||
@ -574,7 +579,7 @@ static int ad7280_attr_init(struct ad7280_state *st)
|
||||
&st->iio_attr[cnt].dev_attr.attr;
|
||||
cnt++;
|
||||
st->iio_attr[cnt].address =
|
||||
AD7280A_DEVADDR(dev) << 8 |
|
||||
ad7280a_devaddr(dev) << 8 |
|
||||
(AD7280A_CB1_TIMER + ch);
|
||||
st->iio_attr[cnt].dev_attr.attr.mode =
|
||||
0644;
|
||||
@ -918,7 +923,7 @@ static int ad7280_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
|
||||
ret = ad7280_write(st, AD7280A_DEVADDR(st->slave_num),
|
||||
ret = ad7280_write(st, ad7280a_devaddr(st->slave_num),
|
||||
AD7280A_ALERT, 0,
|
||||
AD7280A_ALERT_GEN_STATIC_HIGH |
|
||||
(pdata->chain_last_alert_ignore & 0xF));
|
||||
|
@ -284,35 +284,6 @@ static const u8 device_channel_config[] = {
|
||||
ALSPRX2
|
||||
};
|
||||
|
||||
/**
|
||||
* tsl2x7x_i2c_read() - Read a byte from a register.
|
||||
* @client: i2c client
|
||||
* @reg: device register to read from
|
||||
* @*val: pointer to location to store register contents.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* select register to write */
|
||||
ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to write register %x\n", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* read the data */
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret >= 0)
|
||||
*val = (u8)ret;
|
||||
else
|
||||
dev_err(&client->dev, "failed to read register %x\n", reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* tsl2x7x_get_lux() - Reads and calculates current lux value.
|
||||
* @indio_dev: pointer to IIO device
|
||||
@ -352,15 +323,15 @@ static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = tsl2x7x_i2c_read(chip->client,
|
||||
(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &buf[0]);
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
TSL2X7X_CMD_REG | TSL2X7X_STATUS);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"%s: Failed to read STATUS Reg\n", __func__);
|
||||
goto out_unlock;
|
||||
}
|
||||
/* is data new & valid */
|
||||
if (!(buf[0] & TSL2X7X_STA_ADC_VALID)) {
|
||||
if (!(ret & TSL2X7X_STA_ADC_VALID)) {
|
||||
dev_err(&chip->client->dev,
|
||||
"%s: data not valid yet\n", __func__);
|
||||
ret = chip->als_cur_info.lux; /* return LAST VALUE */
|
||||
@ -368,14 +339,16 @@ static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
ret = tsl2x7x_i2c_read(chip->client,
|
||||
(TSL2X7X_CMD_REG |
|
||||
(TSL2X7X_ALS_CHAN0LO + i)), &buf[i]);
|
||||
int reg = TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(chip->client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to read. err=%x\n", ret);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
buf[i] = ret;
|
||||
}
|
||||
|
||||
/* clear any existing interrupt status */
|
||||
@ -475,7 +448,6 @@ static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
u8 status;
|
||||
u8 chdata[2];
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
@ -485,8 +457,8 @@ static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = tsl2x7x_i2c_read(chip->client,
|
||||
(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &status);
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
TSL2X7X_CMD_REG | TSL2X7X_STATUS);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "i2c err=%d\n", ret);
|
||||
goto prox_poll_err;
|
||||
@ -498,7 +470,7 @@ static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
|
||||
case tmd2671:
|
||||
case tsl2771:
|
||||
case tmd2771:
|
||||
if (!(status & TSL2X7X_STA_ADC_VALID))
|
||||
if (!(ret & TSL2X7X_STA_ADC_VALID))
|
||||
goto prox_poll_err;
|
||||
break;
|
||||
case tsl2572:
|
||||
@ -506,17 +478,19 @@ static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
|
||||
case tmd2672:
|
||||
case tsl2772:
|
||||
case tmd2772:
|
||||
if (!(status & TSL2X7X_STA_PRX_VALID))
|
||||
if (!(ret & TSL2X7X_STA_PRX_VALID))
|
||||
goto prox_poll_err;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret = tsl2x7x_i2c_read(chip->client,
|
||||
(TSL2X7X_CMD_REG |
|
||||
(TSL2X7X_PRX_LO + i)), &chdata[i]);
|
||||
int reg = TSL2X7X_CMD_REG | (TSL2X7X_PRX_LO + i);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(chip->client, reg);
|
||||
if (ret < 0)
|
||||
goto prox_poll_err;
|
||||
|
||||
chdata[i] = ret;
|
||||
}
|
||||
|
||||
chip->prox_data =
|
||||
@ -568,39 +542,29 @@ static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
|
||||
static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
u8 reg_val;
|
||||
int gain_trim_val;
|
||||
int ret;
|
||||
int lux_val;
|
||||
|
||||
ret = i2c_smbus_write_byte(chip->client,
|
||||
(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
TSL2X7X_CMD_REG | TSL2X7X_CNTRL);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to write CNTRL register, ret=%d\n", ret);
|
||||
"%s: failed to read from the CNTRL register\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_val = i2c_smbus_read_byte(chip->client);
|
||||
if ((reg_val & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
|
||||
!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
|
||||
if ((ret & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
|
||||
!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
|
||||
dev_err(&chip->client->dev,
|
||||
"%s: failed: ADC not enabled\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte(chip->client,
|
||||
(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
|
||||
if (ret < 0) {
|
||||
"%s: Device is not powered on and/or ADC is not enabled\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
} else if ((ret & TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
|
||||
dev_err(&chip->client->dev,
|
||||
"failed to write ctrl reg: ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_val = i2c_smbus_read_byte(chip->client);
|
||||
if ((reg_val & TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
|
||||
dev_err(&chip->client->dev,
|
||||
"%s: failed: STATUS - ADC not valid.\n", __func__);
|
||||
"%s: The two ADC channels have not completed an integration cycle\n",
|
||||
__func__);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
@ -722,7 +686,8 @@ static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
|
||||
}
|
||||
}
|
||||
|
||||
mdelay(3); /* Power-on settling time */
|
||||
/* Power-on settling time */
|
||||
usleep_range(3000, 3500);
|
||||
|
||||
/*
|
||||
* NOW enable the ADC
|
||||
@ -806,22 +771,24 @@ int tsl2x7x_invoke_change(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
int device_status = chip->tsl2x7x_chip_status;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->als_mutex);
|
||||
mutex_lock(&chip->prox_mutex);
|
||||
|
||||
if (device_status == TSL2X7X_CHIP_WORKING)
|
||||
tsl2x7x_chip_off(indio_dev);
|
||||
if (device_status == TSL2X7X_CHIP_WORKING) {
|
||||
ret = tsl2x7x_chip_off(indio_dev);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
tsl2x7x_chip_on(indio_dev);
|
||||
|
||||
if (device_status != TSL2X7X_CHIP_WORKING)
|
||||
tsl2x7x_chip_off(indio_dev);
|
||||
ret = tsl2x7x_chip_on(indio_dev);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&chip->prox_mutex);
|
||||
mutex_unlock(&chip->als_mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
@ -889,7 +856,7 @@ static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
|
||||
|
||||
/*gather the samples*/
|
||||
for (i = 0; i < chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
|
||||
mdelay(15);
|
||||
usleep_range(15000, 17500);
|
||||
tsl2x7x_get_prox(indio_dev);
|
||||
prox_history[i] = chip->prox_data;
|
||||
dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
|
||||
@ -915,33 +882,6 @@ static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
|
||||
tsl2x7x_chip_on(indio_dev);
|
||||
}
|
||||
|
||||
static ssize_t power_state_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
|
||||
}
|
||||
|
||||
static ssize_t power_state_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
bool value;
|
||||
|
||||
if (strtobool(buf, &value))
|
||||
return -EINVAL;
|
||||
|
||||
if (value)
|
||||
tsl2x7x_chip_on(indio_dev);
|
||||
else
|
||||
tsl2x7x_chip_off(indio_dev);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t in_illuminance0_calibscale_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -1027,6 +967,7 @@ static ssize_t in_illuminance0_target_input_store(struct device *dev,
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
unsigned long value;
|
||||
int ret;
|
||||
|
||||
if (kstrtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
@ -1034,7 +975,9 @@ static ssize_t in_illuminance0_target_input_store(struct device *dev,
|
||||
if (value)
|
||||
chip->tsl2x7x_settings.als_cal_target = value;
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
ret = tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -1083,7 +1026,9 @@ static ssize_t in_intensity0_thresh_period_store(struct device *dev,
|
||||
dev_info(&chip->client->dev, "%s: als persistence = %d",
|
||||
__func__, filter_delay);
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
ret = tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
@ -1131,7 +1076,10 @@ static ssize_t in_proximity0_thresh_period_store(struct device *dev,
|
||||
dev_info(&chip->client->dev, "%s: prox persistence = %d",
|
||||
__func__, filter_delay);
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
ret = tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
@ -1142,6 +1090,7 @@ static ssize_t in_illuminance0_calibrate_store(struct device *dev,
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
bool value;
|
||||
int ret;
|
||||
|
||||
if (strtobool(buf, &value))
|
||||
return -EINVAL;
|
||||
@ -1149,7 +1098,9 @@ static ssize_t in_illuminance0_calibrate_store(struct device *dev,
|
||||
if (value)
|
||||
tsl2x7x_als_calibrate(indio_dev);
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
ret = tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -1189,7 +1140,7 @@ static ssize_t in_illuminance0_lux_table_store(struct device *dev,
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
int value[ARRAY_SIZE(chip->tsl2x7x_device_lux) * 3 + 1];
|
||||
int n;
|
||||
int n, ret;
|
||||
|
||||
get_options(buf, ARRAY_SIZE(value), value);
|
||||
|
||||
@ -1217,7 +1168,9 @@ static ssize_t in_illuminance0_lux_table_store(struct device *dev,
|
||||
memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
|
||||
memcpy(chip->tsl2x7x_device_lux, &value[1], (value[0] * 4));
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
ret = tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -1228,6 +1181,7 @@ static ssize_t in_proximity0_calibrate_store(struct device *dev,
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
bool value;
|
||||
int ret;
|
||||
|
||||
if (strtobool(buf, &value))
|
||||
return -EINVAL;
|
||||
@ -1235,7 +1189,9 @@ static ssize_t in_proximity0_calibrate_store(struct device *dev,
|
||||
if (value)
|
||||
tsl2x7x_prox_cal(indio_dev);
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
ret = tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -1263,6 +1219,7 @@ static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
|
||||
int val)
|
||||
{
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (chan->type == IIO_INTENSITY) {
|
||||
if (val)
|
||||
@ -1276,83 +1233,108 @@ static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
|
||||
chip->tsl2x7x_settings.interrupts_en &= 0x10;
|
||||
}
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
ret = tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
static int tsl2x7x_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (chan->type == IIO_INTENSITY) {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
chip->tsl2x7x_settings.als_thresh_high = val;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
chip->tsl2x7x_settings.als_thresh_low = val;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
chip->tsl2x7x_settings.prox_thres_high = val;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
chip->tsl2x7x_settings.prox_thres_low = val;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
if (chan->type == IIO_INTENSITY) {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
chip->tsl2x7x_settings.als_thresh_high = val;
|
||||
ret = 0;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
chip->tsl2x7x_settings.als_thresh_low = val;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
chip->tsl2x7x_settings.prox_thres_high = val;
|
||||
ret = 0;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
chip->tsl2x7x_settings.prox_thres_low = val;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return tsl2x7x_invoke_change(indio_dev);
|
||||
}
|
||||
|
||||
static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
static int tsl2x7x_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (chan->type == IIO_INTENSITY) {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = chip->tsl2x7x_settings.als_thresh_high;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
*val = chip->tsl2x7x_settings.als_thresh_low;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = chip->tsl2x7x_settings.prox_thres_high;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
*val = chip->tsl2x7x_settings.prox_thres_low;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
if (chan->type == IIO_INTENSITY) {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = chip->tsl2x7x_settings.als_thresh_high;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
*val = chip->tsl2x7x_settings.als_thresh_low;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = chip->tsl2x7x_settings.prox_thres_high;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
*val = chip->tsl2x7x_settings.prox_thres_low;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
|
||||
@ -1489,13 +1471,9 @@ static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsl2x7x_invoke_change(indio_dev);
|
||||
|
||||
return 0;
|
||||
return tsl2x7x_invoke_change(indio_dev);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(power_state);
|
||||
|
||||
static DEVICE_ATTR_RO(in_proximity0_calibscale_available);
|
||||
|
||||
static DEVICE_ATTR_RO(in_illuminance0_calibscale_available);
|
||||
@ -1515,7 +1493,7 @@ static DEVICE_ATTR_RW(in_intensity0_thresh_period);
|
||||
static DEVICE_ATTR_RW(in_proximity0_thresh_period);
|
||||
|
||||
/* Use the default register values to identify the Taos device */
|
||||
static int tsl2x7x_device_id(unsigned char *id, int target)
|
||||
static int tsl2x7x_device_id(int *id, int target)
|
||||
{
|
||||
switch (target) {
|
||||
case tsl2571:
|
||||
@ -1580,7 +1558,6 @@ static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
|
||||
}
|
||||
|
||||
static struct attribute *tsl2x7x_ALS_device_attrs[] = {
|
||||
&dev_attr_power_state.attr,
|
||||
&dev_attr_in_illuminance0_calibscale_available.attr,
|
||||
&dev_attr_in_illuminance0_integration_time.attr,
|
||||
&iio_const_attr_in_illuminance0_integration_time_available.dev_attr.attr,
|
||||
@ -1591,13 +1568,11 @@ static struct attribute *tsl2x7x_ALS_device_attrs[] = {
|
||||
};
|
||||
|
||||
static struct attribute *tsl2x7x_PRX_device_attrs[] = {
|
||||
&dev_attr_power_state.attr,
|
||||
&dev_attr_in_proximity0_calibrate.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
|
||||
&dev_attr_power_state.attr,
|
||||
&dev_attr_in_illuminance0_calibscale_available.attr,
|
||||
&dev_attr_in_illuminance0_integration_time.attr,
|
||||
&iio_const_attr_in_illuminance0_integration_time_available.dev_attr.attr,
|
||||
@ -1609,14 +1584,12 @@ static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
|
||||
};
|
||||
|
||||
static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
|
||||
&dev_attr_power_state.attr,
|
||||
&dev_attr_in_proximity0_calibrate.attr,
|
||||
&dev_attr_in_proximity0_calibscale_available.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
|
||||
&dev_attr_power_state.attr,
|
||||
&dev_attr_in_illuminance0_calibscale_available.attr,
|
||||
&dev_attr_in_illuminance0_integration_time.attr,
|
||||
&iio_const_attr_in_illuminance0_integration_time_available.dev_attr.attr,
|
||||
@ -1684,8 +1657,8 @@ static const struct iio_info tsl2X7X_device_info[] = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2x7x_read_raw,
|
||||
.write_raw = &tsl2x7x_write_raw,
|
||||
.read_event_value = &tsl2x7x_read_thresh,
|
||||
.write_event_value = &tsl2x7x_write_thresh,
|
||||
.read_event_value = &tsl2x7x_read_event_value,
|
||||
.write_event_value = &tsl2x7x_write_event_value,
|
||||
.read_event_config = &tsl2x7x_read_interrupt_config,
|
||||
.write_event_config = &tsl2x7x_write_interrupt_config,
|
||||
},
|
||||
@ -1695,8 +1668,8 @@ static const struct iio_info tsl2X7X_device_info[] = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2x7x_read_raw,
|
||||
.write_raw = &tsl2x7x_write_raw,
|
||||
.read_event_value = &tsl2x7x_read_thresh,
|
||||
.write_event_value = &tsl2x7x_write_thresh,
|
||||
.read_event_value = &tsl2x7x_read_event_value,
|
||||
.write_event_value = &tsl2x7x_write_event_value,
|
||||
.read_event_config = &tsl2x7x_read_interrupt_config,
|
||||
.write_event_config = &tsl2x7x_write_interrupt_config,
|
||||
},
|
||||
@ -1706,8 +1679,8 @@ static const struct iio_info tsl2X7X_device_info[] = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2x7x_read_raw,
|
||||
.write_raw = &tsl2x7x_write_raw,
|
||||
.read_event_value = &tsl2x7x_read_thresh,
|
||||
.write_event_value = &tsl2x7x_write_thresh,
|
||||
.read_event_value = &tsl2x7x_read_event_value,
|
||||
.write_event_value = &tsl2x7x_write_event_value,
|
||||
.read_event_config = &tsl2x7x_read_interrupt_config,
|
||||
.write_event_config = &tsl2x7x_write_interrupt_config,
|
||||
},
|
||||
@ -1717,8 +1690,8 @@ static const struct iio_info tsl2X7X_device_info[] = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2x7x_read_raw,
|
||||
.write_raw = &tsl2x7x_write_raw,
|
||||
.read_event_value = &tsl2x7x_read_thresh,
|
||||
.write_event_value = &tsl2x7x_write_thresh,
|
||||
.read_event_value = &tsl2x7x_read_event_value,
|
||||
.write_event_value = &tsl2x7x_write_event_value,
|
||||
.read_event_config = &tsl2x7x_read_interrupt_config,
|
||||
.write_event_config = &tsl2x7x_write_interrupt_config,
|
||||
},
|
||||
@ -1728,8 +1701,8 @@ static const struct iio_info tsl2X7X_device_info[] = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2x7x_read_raw,
|
||||
.write_raw = &tsl2x7x_write_raw,
|
||||
.read_event_value = &tsl2x7x_read_thresh,
|
||||
.write_event_value = &tsl2x7x_write_thresh,
|
||||
.read_event_value = &tsl2x7x_read_event_value,
|
||||
.write_event_value = &tsl2x7x_write_event_value,
|
||||
.read_event_config = &tsl2x7x_read_interrupt_config,
|
||||
.write_event_config = &tsl2x7x_write_interrupt_config,
|
||||
},
|
||||
@ -1877,7 +1850,6 @@ static int tsl2x7x_probe(struct i2c_client *clientp,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
unsigned char device_id;
|
||||
struct iio_dev *indio_dev;
|
||||
struct tsl2X7X_chip *chip;
|
||||
|
||||
@ -1889,13 +1861,13 @@ static int tsl2x7x_probe(struct i2c_client *clientp,
|
||||
chip->client = clientp;
|
||||
i2c_set_clientdata(clientp, indio_dev);
|
||||
|
||||
ret = tsl2x7x_i2c_read(chip->client,
|
||||
TSL2X7X_CHIPID, &device_id);
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
TSL2X7X_CMD_REG | TSL2X7X_CHIPID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
|
||||
(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
|
||||
if ((!tsl2x7x_device_id(&ret, id->driver_data)) ||
|
||||
(tsl2x7x_device_id(&ret, id->driver_data) == -EINVAL)) {
|
||||
dev_info(&chip->client->dev,
|
||||
"%s: i2c device found does not match expected id\n",
|
||||
__func__);
|
||||
@ -2026,6 +1998,21 @@ static struct i2c_device_id tsl2x7x_idtable[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
|
||||
|
||||
static const struct of_device_id tsl2x7x_of_match[] = {
|
||||
{ .compatible = "amstaos,tsl2571" },
|
||||
{ .compatible = "amstaos,tsl2671" },
|
||||
{ .compatible = "amstaos,tmd2671" },
|
||||
{ .compatible = "amstaos,tsl2771" },
|
||||
{ .compatible = "amstaos,tmd2771" },
|
||||
{ .compatible = "amstaos,tsl2572" },
|
||||
{ .compatible = "amstaos,tsl2672" },
|
||||
{ .compatible = "amstaos,tmd2672" },
|
||||
{ .compatible = "amstaos,tsl2772" },
|
||||
{ .compatible = "amstaos,tmd2772" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tsl2x7x_of_match);
|
||||
|
||||
static const struct dev_pm_ops tsl2x7x_pm_ops = {
|
||||
.suspend = tsl2x7x_suspend,
|
||||
.resume = tsl2x7x_resume,
|
||||
@ -2035,6 +2022,7 @@ static const struct dev_pm_ops tsl2x7x_pm_ops = {
|
||||
static struct i2c_driver tsl2x7x_driver = {
|
||||
.driver = {
|
||||
.name = "tsl2x7x",
|
||||
.of_match_table = tsl2x7x_of_match,
|
||||
.pm = &tsl2x7x_pm_ops,
|
||||
},
|
||||
.id_table = tsl2x7x_idtable,
|
||||
|
@ -325,4 +325,16 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
void st_sensors_of_name_probe(struct device *dev,
|
||||
const struct of_device_id *match,
|
||||
char *name, int len);
|
||||
#else
|
||||
static inline void st_sensors_of_name_probe(struct device *dev,
|
||||
const struct of_device_id *match,
|
||||
char *name, int len)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ST_SENSORS_H */
|
||||
|
@ -18,16 +18,6 @@
|
||||
void st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
struct i2c_client *client, struct st_sensor_data *sdata);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
void st_sensors_of_i2c_probe(struct i2c_client *client,
|
||||
const struct of_device_id *match);
|
||||
#else
|
||||
static inline void st_sensors_of_i2c_probe(struct i2c_client *client,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int st_sensors_match_acpi_device(struct device *dev);
|
||||
#else
|
||||
|
Loading…
Reference in New Issue
Block a user