Second set of new driver, functionality and cleanups for IIO in the 4.2 cycle.
Core functionality * i and q modifiers from quadrature channels. * IIO_CHAN_INFO_OVERSAMPLING_RATIO added. * High pass filter attributes added to mirror the existing low pass filter ones. Core cleanups * Make IIO tools building more cross compiler friendly. * Substantial rework of the function __iio_update_buffers to greatly simplify a hideously evolved function. New drivers and support * ACPI0008 ambient light sensor driver. This one has been around a long time to will be good to finally get it into mainline. * Berlin SOC ADC support. * BMC150 magnetometer. The accelerometer in the same package has been supported for quite some time, so good to have this half as well. * m62332 DAC driver * MEMSIC MMC35420 magnetometer. * ROHM BH1710 and similar ambient light sensors. * Sensortek STK3310 light sensor. * Sensortek STK8312 accelerometer. * Sensortek STK8BA50 accelerometer. * ti-adc128s052 gains support form the adc122s021 2 channel ADC. Driver cleanups and functionality. * Allow various drivers to compile with !GPIOLIB if COMPILE_TEST enabled. * bmc150 - decouple trigger from buffer to allow other triggers to be used. * bmg160 - decouple trigger from buffer to allow other triggers to be used. Fix a trivial unused field. * Constify a load of platform_device_id structures. * inv_mpu6050 - device tree bindings. * hid-sensors - fix a memory leak during probe if certain errors occur. * ltr501 - illuminance channel derived (in an non obvious fashion) from the intensity channels. * ltr501 - fix a boundary check on the proximity threshold. * mlx90614 - drop a pointless return. * mma8452 - Debugfs register access and fix a bug that had no effect (by coincidence) * ti_am335x_adc - add device tree bindings for sample-delay, open-delay and averaging. The ideal settings for these tend to be board design specific. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJVYfYpAAoJEFSFNJnE9BaI1ScQAIJ2jsFZdf8fcVnWeq0bYx9I VUPfz/bJ/kLQRGm/LDgpMPc5o1mOE+rupwFpp/iQf15vVUN86+CRLt0qd5I/cAEg I3qbaieS1H9Qyd2dLTgAcZAh6tH7ZvFJm/hB6T5xQAYFGY2IMq/n3qA4//W37tUb 2bKTRb67LWbGivOvwbxdSpEkBLtVcUw3UNn9nfqjB8BEAHIesh88gJkVKAAuRYqk Tm8AzQ7EGsosz2R7mIvukSBwXBcvRyxyOxCdLBPIWSESeLwMiiat0zCfv3MxrYiD FVpdlywoReIjDG6z9ALOm4VMtRF2m2VrjPHclQ3kYgYSgyf0fRmoiyGowv7hkeya Z+p9ltOZ8qdis+yH1ci9Ch695HURa1m0seirX4exqiv0Crx8UF+iNIvs9Ai84Rv8 NNVlscoeEyijUaqoBb1YvG/Fryh2IEiGXTkF4Eld+EhW8AKkFFNIqR+Gwvs1YegT 02A8kHxD0GyMYJo1uEwd+TnKwCBglwsie8omkxOXqsY860DRtBr7jOxyb/RzkSVi jGtq1Y4nxVv7q3nkn+vQDRNgAQTbH1EJfrDilpIxIWK+9onNmKMnhKnSTVNAdld/ Hhn9g/MHptQtmA+DwMcJ3Aqn0xMUdgoE9GIkMGoKSZku9H0DhWHLdxTa2lxdJFUo OiWVvP0eJuvu0E0h4eA2 =uX2k -----END PGP SIGNATURE----- Merge tag 'iio-for-v4.2b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Second set of new driver, functionality and cleanups for IIO in the 4.2 cycle. Core functionality * i and q modifiers from quadrature channels. * IIO_CHAN_INFO_OVERSAMPLING_RATIO added. * High pass filter attributes added to mirror the existing low pass filter ones. Core cleanups * Make IIO tools building more cross compiler friendly. * Substantial rework of the function __iio_update_buffers to greatly simplify a hideously evolved function. New drivers and support * ACPI0008 ambient light sensor driver. This one has been around a long time to will be good to finally get it into mainline. * Berlin SOC ADC support. * BMC150 magnetometer. The accelerometer in the same package has been supported for quite some time, so good to have this half as well. * m62332 DAC driver * MEMSIC MMC35420 magnetometer. * ROHM BH1710 and similar ambient light sensors. * Sensortek STK3310 light sensor. * Sensortek STK8312 accelerometer. * Sensortek STK8BA50 accelerometer. * ti-adc128s052 gains support form the adc122s021 2 channel ADC. Driver cleanups and functionality. * Allow various drivers to compile with !GPIOLIB if COMPILE_TEST enabled. * bmc150 - decouple trigger from buffer to allow other triggers to be used. * bmg160 - decouple trigger from buffer to allow other triggers to be used. Fix a trivial unused field. * Constify a load of platform_device_id structures. * inv_mpu6050 - device tree bindings. * hid-sensors - fix a memory leak during probe if certain errors occur. * ltr501 - illuminance channel derived (in an non obvious fashion) from the intensity channels. * ltr501 - fix a boundary check on the proximity threshold. * mlx90614 - drop a pointless return. * mma8452 - Debugfs register access and fix a bug that had no effect (by coincidence) * ti_am335x_adc - add device tree bindings for sample-delay, open-delay and averaging. The ideal settings for these tend to be board design specific.
This commit is contained in:
commit
61e331202f
@ -71,6 +71,8 @@ Description:
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_raw
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -81,6 +83,11 @@ Description:
|
||||
unique to allow association with event codes. Units after
|
||||
application of scale and offset are millivolts.
|
||||
|
||||
Channels with 'i' and 'q' modifiers always exist in pairs and both
|
||||
channels refer to the same signal. The 'i' channel contains the in-phase
|
||||
component of the signal while the 'q' channel contains the quadrature
|
||||
component.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_raw
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
@ -246,8 +253,16 @@ What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_current_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_current_q_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_current_i_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
|
||||
@ -273,14 +288,22 @@ Description:
|
||||
to the _raw output.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_current_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_current_i_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_current_q_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
|
||||
@ -328,6 +351,10 @@ Description:
|
||||
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale
|
||||
What /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale
|
||||
@ -420,6 +447,16 @@ Description:
|
||||
to the underlying data channel, then this parameter
|
||||
gives the 3dB frequency of the filter in Hz.
|
||||
|
||||
What: /sys/.../in_accel_filter_high_pass_3db_frequency
|
||||
What: /sys/.../in_anglvel_filter_high_pass_3db_frequency
|
||||
What: /sys/.../in_magn_filter_high_pass_3db_frequency
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
If a known or controllable high pass filter is applied
|
||||
to the underlying data channel, then this parameter
|
||||
gives the 3dB frequency of the filter in Hz.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_raw
|
||||
KernelVersion: 2.6.37
|
||||
@ -880,6 +917,26 @@ Description:
|
||||
met before an event is generated. If direction is not
|
||||
specified then this period applies to both directions.
|
||||
|
||||
What: /sys/.../events/in_accel_thresh_rising_low_pass_filter_3db
|
||||
What: /sys/.../events/in_anglvel_thresh_rising_low_pass_filter_3db
|
||||
What: /sys/.../events/in_magn_thresh_rising_low_pass_filter_3db
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
If a low pass filter can be applied to the event generation
|
||||
this property gives its 3db frequency in Hz.
|
||||
A value of zero disables the filter.
|
||||
|
||||
What: /sys/.../events/in_accel_thresh_rising_high_pass_filter_3db
|
||||
What: /sys/.../events/in_anglvel_thresh_rising_high_pass_filter_3db
|
||||
What: /sys/.../events/in_magn_thresh_rising_high_pass_filter_3db
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
If a high pass filter can be applied to the event generation
|
||||
this property gives its 3db frequency in Hz.
|
||||
A value of zero disables the filter.
|
||||
|
||||
What: /sys/.../events/in_activity_still_thresh_rising_en
|
||||
What: /sys/.../events/in_activity_still_thresh_falling_en
|
||||
What: /sys/.../events/in_activity_walking_thresh_rising_en
|
||||
@ -1016,6 +1073,10 @@ What: /sys/.../iio:deviceX/scan_elements/in_timestamp_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY-voltageZ_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en
|
||||
@ -1034,6 +1095,10 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltage_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_pressure_type
|
||||
@ -1071,6 +1136,10 @@ Description:
|
||||
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_accel_x_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_accel_y_index
|
||||
What: /sys/.../iio:deviceX/scan_elements/in_accel_z_index
|
||||
@ -1230,6 +1299,8 @@ Description:
|
||||
or without compensation from tilt sensors.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_i_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_q_raw
|
||||
KernelVersion: 3.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -1238,6 +1309,11 @@ Description:
|
||||
present, output should be considered as processed with the
|
||||
unit in milliamps.
|
||||
|
||||
Channels with 'i' and 'q' modifiers always exist in pairs and both
|
||||
channels refer to the same signal. The 'i' channel contains the in-phase
|
||||
component of the signal while the 'q' channel contains the quadrature
|
||||
component.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_energy_en
|
||||
What: /sys/.../iio:deviceX/in_distance_en
|
||||
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
|
||||
@ -1375,3 +1451,15 @@ Description:
|
||||
The emissivity ratio of the surface in the field of view of the
|
||||
contactless temperature sensor. Emissivity varies from 0 to 1,
|
||||
with 1 being the emissivity of a black body.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_oversampling_ratio
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_oversampling_ratio
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_oversampling_ratio
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Hardware applied number of measurements for acquiring one
|
||||
data point. The HW will do <type>[_name]_oversampling_ratio
|
||||
measurements and return the average value as output data. Each
|
||||
value resulted from <type>[_name]_oversampling_ratio measurements
|
||||
is considered as one sample for <type>[_name]_sampling_frequency.
|
||||
|
19
Documentation/devicetree/bindings/iio/adc/berlin2_adc.txt
Normal file
19
Documentation/devicetree/bindings/iio/adc/berlin2_adc.txt
Normal file
@ -0,0 +1,19 @@
|
||||
* Berlin Analog to Digital Converter (ADC)
|
||||
|
||||
The Berlin ADC has 8 channels, with one connected to a temperature sensor.
|
||||
It is part of the system controller register set. The ADC node should be a
|
||||
sub-node of the system controller node.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "marvell,berlin2-adc"
|
||||
- interrupts: the interrupts for the ADC and the temperature sensor
|
||||
- interrupt-names: should be "adc" and "tsen"
|
||||
|
||||
Example:
|
||||
|
||||
adc: adc {
|
||||
compatible = "marvell,berlin2-adc";
|
||||
interrupt-parent = <&sic>;
|
||||
interrupts = <12>, <14>;
|
||||
interrupt-names = "adc", "tsen";
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
* Texas Instruments' ADC128S052 ADC chip
|
||||
* Texas Instruments' ADC128S052 and ADC122S021 ADC chip
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,adc128s052"
|
||||
- compatible: Should be "ti,adc128s052" or "ti,adc122s021"
|
||||
- reg: spi chip select number for the device
|
||||
- vref-supply: The regulator supply for ADC reference voltage
|
||||
|
||||
|
17
Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt
Normal file
17
Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt
Normal file
@ -0,0 +1,17 @@
|
||||
InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
|
||||
|
||||
http://www.invensense.com/mems/gyro/mpu6050.html
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "invensense,mpu6050"
|
||||
- reg : the I2C address of the sensor
|
||||
- interrupt-parent : should be the phandle for the interrupt controller
|
||||
- interrupts : interrupt mapping for GPIO IRQ
|
||||
|
||||
Example:
|
||||
mpu6050@68 {
|
||||
compatible = "invensense,mpu6050";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <18 1>;
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
* Bosch BMC150 magnetometer sensor
|
||||
|
||||
http://ae-bst.resource.bosch.com/media/products/dokumente/bmc150/BST-BMC150-DS000-04.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "bosch,bmc150_magn"
|
||||
- reg : the I2C address of the magnetometer
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-parent : phandle to the parent interrupt controller
|
||||
- interrupts : interrupt mapping for GPIO IRQ
|
||||
|
||||
Example:
|
||||
|
||||
bmc150_magn@12 {
|
||||
compatible = "bosch,bmc150_magn";
|
||||
reg = <0x12>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <0 1>;
|
||||
};
|
@ -42,6 +42,27 @@ Optional properties:
|
||||
hardware knob for adjusting the amount of "settling
|
||||
time".
|
||||
|
||||
- child "adc"
|
||||
ti,chan-step-opendelay: List of open delays for each channel of
|
||||
ADC in the order of ti,adc-channels. The
|
||||
value corresponds to the number of ADC
|
||||
clock cycles to wait after applying the
|
||||
step configuration registers and before
|
||||
sending the start of ADC conversion.
|
||||
Maximum value is 0x3FFFF.
|
||||
ti,chan-step-sampledelay: List of sample delays for each channel
|
||||
of ADC in the order of ti,adc-channels.
|
||||
The value corresponds to the number of
|
||||
ADC clock cycles to sample (to hold
|
||||
start of conversion high).
|
||||
Maximum value is 0xFF.
|
||||
ti,chan-step-avg: Number of averages to be performed for each
|
||||
channel of ADC. If average is 16 then input
|
||||
is sampled 16 times and averaged to get more
|
||||
accurate value. This increases the time taken
|
||||
by ADC to generate a sample. Valid range is 0
|
||||
average to 16 averages. Maximum value is 16.
|
||||
|
||||
Example:
|
||||
tscadc: tscadc@44e0d000 {
|
||||
compatible = "ti,am3359-tscadc";
|
||||
@ -55,5 +76,8 @@ Example:
|
||||
|
||||
adc {
|
||||
ti,adc-channels = <4 5 6 7>;
|
||||
ti,chan-step-opendelay = <0x098 0x3ffff 0x098 0x0>;
|
||||
ti,chan-step-sampledelay = <0xff 0x0 0xf 0x0>;
|
||||
ti,chan-step-avg = <16 2 4 8>;
|
||||
};
|
||||
}
|
||||
|
@ -136,4 +136,25 @@ config MMA9553
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma9553.
|
||||
|
||||
config STK8312
|
||||
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to get support for the Sensortek STK8312 3-axis
|
||||
accelerometer.
|
||||
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called stk8312.
|
||||
|
||||
config STK8BA50
|
||||
tristate "Sensortek STK8BA50 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to get support for the Sensortek STK8BA50 3-axis
|
||||
accelerometer.
|
||||
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called stk8ba50.
|
||||
|
||||
endmenu
|
||||
|
@ -14,6 +14,9 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
|
||||
obj-$(CONFIG_MMA9551) += mma9551.o
|
||||
obj-$(CONFIG_MMA9553) += mma9553.o
|
||||
|
||||
obj-$(CONFIG_STK8312) += stk8312.o
|
||||
obj-$(CONFIG_STK8BA50) += stk8ba50.o
|
||||
|
||||
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
|
||||
|
@ -196,7 +196,7 @@ struct bmc150_accel_data {
|
||||
u32 slope_thres;
|
||||
u32 range;
|
||||
int ev_enable_state;
|
||||
int64_t timestamp, old_timestamp;
|
||||
int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */
|
||||
const struct bmc150_accel_chip_info *chip_info;
|
||||
};
|
||||
|
||||
@ -1183,7 +1183,6 @@ static const struct iio_info bmc150_accel_info = {
|
||||
.write_event_value = bmc150_accel_write_event,
|
||||
.write_event_config = bmc150_accel_write_event_config,
|
||||
.read_event_config = bmc150_accel_read_event_config,
|
||||
.validate_trigger = bmc150_accel_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
@ -1222,7 +1221,7 @@ static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p)
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
data->timestamp);
|
||||
pf->timestamp);
|
||||
err_read:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
@ -1535,6 +1534,13 @@ static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bmc150_accel_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
|
||||
return bmc150_accel_set_power_state(data, true);
|
||||
}
|
||||
|
||||
static int bmc150_accel_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
@ -1591,9 +1597,18 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmc150_accel_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
|
||||
return bmc150_accel_set_power_state(data, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = {
|
||||
.preenable = bmc150_accel_buffer_preenable,
|
||||
.postenable = bmc150_accel_buffer_postenable,
|
||||
.predisable = bmc150_accel_buffer_predisable,
|
||||
.postdisable = bmc150_accel_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int bmc150_accel_probe(struct i2c_client *client,
|
||||
@ -1636,6 +1651,15 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmc150_accel_info;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
bmc150_accel_trigger_handler,
|
||||
&bmc150_accel_buffer_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed: iio triggered buffer setup\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = bmc150_accel_gpio_probe(client, data);
|
||||
|
||||
@ -1648,7 +1672,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
BMC150_ACCEL_IRQ_NAME,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
/*
|
||||
* Set latched mode interrupt. While certain interrupts are
|
||||
@ -1661,24 +1685,14 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
|
||||
return ret;
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
bmc150_accel_interrupts_setup(indio_dev, data);
|
||||
|
||||
ret = bmc150_accel_triggers_setup(indio_dev, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
bmc150_accel_trigger_handler,
|
||||
&bmc150_accel_buffer_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed: iio triggered buffer setup\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
|
||||
i2c_check_functionality(client->adapter,
|
||||
@ -1692,7 +1706,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Unable to register iio device\n");
|
||||
goto err_buffer_cleanup;
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
@ -1708,11 +1722,10 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
|
||||
err_iio_unregister:
|
||||
iio_device_unregister(indio_dev);
|
||||
err_buffer_cleanup:
|
||||
if (indio_dev->pollfunc)
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_trigger_unregister:
|
||||
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1730,6 +1743,8 @@ static int bmc150_accel_remove(struct i2c_client *client)
|
||||
|
||||
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_DEEP_SUSPEND, 0);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
@ -299,7 +299,6 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct accel_3d_state *accel_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct accel_3d_state));
|
||||
@ -320,21 +319,21 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(accel_3d_channels, sizeof(accel_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
indio_dev->channels = kmemdup(accel_3d_channels,
|
||||
sizeof(accel_3d_channels), GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = accel_3d_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, accel_state);
|
||||
ret = accel_3d_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, accel_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &accel_3d_info;
|
||||
@ -400,7 +399,7 @@ static int hid_accel_3d_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_accel_3d_ids[] = {
|
||||
static const struct platform_device_id hid_accel_3d_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200073",
|
||||
|
@ -32,6 +32,9 @@
|
||||
#define MMA8452_OFF_Z 0x31
|
||||
#define MMA8452_CTRL_REG1 0x2a
|
||||
#define MMA8452_CTRL_REG2 0x2b
|
||||
#define MMA8452_CTRL_REG2_RST BIT(6)
|
||||
|
||||
#define MMA8452_MAX_REG 0x31
|
||||
|
||||
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||
|
||||
@ -291,6 +294,28 @@ done:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
int ret;
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (reg > MMA8452_MAX_REG)
|
||||
return -EINVAL;
|
||||
|
||||
if (!readval)
|
||||
return mma8452_change_config(data, reg, writeval);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*readval = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MMA8452_CHANNEL(axis, idx) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
@ -330,11 +355,36 @@ static const struct iio_info mma8452_info = {
|
||||
.attrs = &mma8452_group,
|
||||
.read_raw = &mma8452_read_raw,
|
||||
.write_raw = &mma8452_write_raw,
|
||||
.debugfs_reg_access = &mma8452_reg_access_dbg,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long mma8452_scan_masks[] = {0x7, 0};
|
||||
|
||||
static int mma8452_reset(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2,
|
||||
MMA8452_CTRL_REG2_RST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
usleep_range(100, 200);
|
||||
ret = i2c_smbus_read_byte_data(client, MMA8452_CTRL_REG2);
|
||||
if (ret == -EIO)
|
||||
continue; /* I2C comm reset */
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!(ret & MMA8452_CTRL_REG2_RST))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int mma8452_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -365,10 +415,7 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
|
||||
indio_dev->available_scan_masks = mma8452_scan_masks;
|
||||
|
||||
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
|
||||
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
ret = mma8452_reset(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -378,6 +425,13 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
|
||||
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
mma8452_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
|
390
drivers/iio/accel/stk8312.c
Normal file
390
drivers/iio/accel/stk8312.c
Normal file
@ -0,0 +1,390 @@
|
||||
/**
|
||||
* Sensortek STK8312 3-Axis Accelerometer
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for STK8312; 7-bit I2C address: 0x3D.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define STK8312_REG_XOUT 0x00
|
||||
#define STK8312_REG_YOUT 0x01
|
||||
#define STK8312_REG_ZOUT 0x02
|
||||
#define STK8312_REG_MODE 0x07
|
||||
#define STK8312_REG_STH 0x13
|
||||
#define STK8312_REG_RESET 0x20
|
||||
#define STK8312_REG_AFECTRL 0x24
|
||||
#define STK8312_REG_OTPADDR 0x3D
|
||||
#define STK8312_REG_OTPDATA 0x3E
|
||||
#define STK8312_REG_OTPCTRL 0x3F
|
||||
|
||||
#define STK8312_MODE_ACTIVE 1
|
||||
#define STK8312_MODE_STANDBY 0
|
||||
#define STK8312_MODE_MASK 0x01
|
||||
#define STK8312_RNG_MASK 0xC0
|
||||
#define STK8312_RNG_SHIFT 6
|
||||
#define STK8312_READ_RETRIES 16
|
||||
|
||||
#define STK8312_DRIVER_NAME "stk8312"
|
||||
|
||||
/*
|
||||
* The accelerometer has two measurement ranges:
|
||||
*
|
||||
* -6g - +6g (8-bit, signed)
|
||||
* -16g - +16g (8-bit, signed)
|
||||
*
|
||||
* scale1 = (6 + 6) * 9.81 / (2^8 - 1) = 0.4616
|
||||
* scale2 = (16 + 16) * 9.81 / (2^8 - 1) = 1.2311
|
||||
*/
|
||||
#define STK8312_SCALE_AVAIL "0.4616 1.2311"
|
||||
|
||||
static const int stk8312_scale_table[][2] = {
|
||||
{0, 461600}, {1, 231100}
|
||||
};
|
||||
|
||||
#define STK8312_ACCEL_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec stk8312_channels[] = {
|
||||
STK8312_ACCEL_CHANNEL(STK8312_REG_XOUT, X),
|
||||
STK8312_ACCEL_CHANNEL(STK8312_REG_YOUT, Y),
|
||||
STK8312_ACCEL_CHANNEL(STK8312_REG_ZOUT, Z),
|
||||
};
|
||||
|
||||
struct stk8312_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
int range;
|
||||
u8 mode;
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
|
||||
|
||||
static struct attribute *stk8312_attributes[] = {
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group stk8312_attribute_group = {
|
||||
.attrs = stk8312_attributes
|
||||
};
|
||||
|
||||
static int stk8312_otp_init(struct stk8312_data *data)
|
||||
{
|
||||
int ret;
|
||||
int count = 10;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPADDR, 0x70);
|
||||
if (ret < 0)
|
||||
goto exit_err;
|
||||
ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPCTRL, 0x02);
|
||||
if (ret < 0)
|
||||
goto exit_err;
|
||||
|
||||
do {
|
||||
usleep_range(1000, 5000);
|
||||
ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPCTRL);
|
||||
if (ret < 0)
|
||||
goto exit_err;
|
||||
count--;
|
||||
} while (!(ret & 0x80) && count > 0);
|
||||
|
||||
if (count == 0)
|
||||
goto exit_err;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPDATA);
|
||||
if (ret < 0)
|
||||
goto exit_err;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
STK8312_REG_AFECTRL, ret);
|
||||
if (ret < 0)
|
||||
goto exit_err;
|
||||
msleep(150);
|
||||
|
||||
return ret;
|
||||
|
||||
exit_err:
|
||||
dev_err(&client->dev, "failed to initialize sensor\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
|
||||
{
|
||||
int ret;
|
||||
u8 masked_reg;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
if (mode > 1)
|
||||
return -EINVAL;
|
||||
else if (mode == data->mode)
|
||||
return 0;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to change sensor mode\n");
|
||||
return ret;
|
||||
}
|
||||
masked_reg = ret & (~STK8312_MODE_MASK);
|
||||
masked_reg |= mode;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
STK8312_REG_MODE, masked_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to change sensor mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->mode = mode;
|
||||
if (mode == STK8312_MODE_ACTIVE) {
|
||||
/* Need to run OTP sequence before entering active mode */
|
||||
usleep_range(1000, 5000);
|
||||
ret = stk8312_otp_init(data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk8312_set_range(struct stk8312_data *data, u8 range)
|
||||
{
|
||||
int ret;
|
||||
u8 masked_reg;
|
||||
u8 mode;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
if (range != 1 && range != 2)
|
||||
return -EINVAL;
|
||||
else if (range == data->range)
|
||||
return 0;
|
||||
|
||||
mode = data->mode;
|
||||
/* We need to go in standby mode to modify registers */
|
||||
ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to change sensor range\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
masked_reg = ret & (~STK8312_RNG_MASK);
|
||||
masked_reg |= range << STK8312_RNG_SHIFT;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "failed to change sensor range\n");
|
||||
else
|
||||
data->range = range;
|
||||
|
||||
return stk8312_set_mode(data, mode);
|
||||
}
|
||||
|
||||
static int stk8312_read_accel(struct stk8312_data *data, u8 address)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
if (address > 2)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, address);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "register read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sign_extend32(ret, 7);
|
||||
}
|
||||
|
||||
static int stk8312_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct stk8312_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (chan->type != IIO_ACCEL)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->lock);
|
||||
*val = stk8312_read_accel(data, chan->address);
|
||||
mutex_unlock(&data->lock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = stk8312_scale_table[data->range - 1][0];
|
||||
*val2 = stk8312_scale_table[data->range - 1][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stk8312_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int i;
|
||||
int index = -1;
|
||||
int ret;
|
||||
struct stk8312_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
|
||||
if (val == stk8312_scale_table[i][0] &&
|
||||
val2 == stk8312_scale_table[i][1]) {
|
||||
index = i + 1;
|
||||
break;
|
||||
}
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = stk8312_set_range(data, index);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info stk8312_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = stk8312_read_raw,
|
||||
.write_raw = stk8312_write_raw,
|
||||
.attrs = &stk8312_attribute_group,
|
||||
};
|
||||
|
||||
static int stk8312_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct stk8312_data *data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &stk8312_info;
|
||||
indio_dev->name = STK8312_DRIVER_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = stk8312_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
|
||||
|
||||
/* A software reset is recommended at power-on */
|
||||
ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET, 0x00);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to reset sensor\n");
|
||||
return ret;
|
||||
}
|
||||
ret = stk8312_set_range(data, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk8312_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int stk8312_suspend(struct device *dev)
|
||||
{
|
||||
struct stk8312_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
||||
}
|
||||
|
||||
static int stk8312_resume(struct device *dev)
|
||||
{
|
||||
struct stk8312_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
|
||||
|
||||
#define STK8312_PM_OPS (&stk8312_pm_ops)
|
||||
#else
|
||||
#define STK8312_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id stk8312_i2c_id[] = {
|
||||
{"STK8312", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct acpi_device_id stk8312_acpi_id[] = {
|
||||
{"STK8312", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
|
||||
|
||||
static struct i2c_driver stk8312_driver = {
|
||||
.driver = {
|
||||
.name = "stk8312",
|
||||
.pm = STK8312_PM_OPS,
|
||||
.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
|
||||
},
|
||||
.probe = stk8312_probe,
|
||||
.remove = stk8312_remove,
|
||||
.id_table = stk8312_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(stk8312_driver);
|
||||
|
||||
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
|
||||
MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
302
drivers/iio/accel/stk8ba50.c
Normal file
302
drivers/iio/accel/stk8ba50.c
Normal file
@ -0,0 +1,302 @@
|
||||
/**
|
||||
* Sensortek STK8BA50 3-Axis Accelerometer
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* STK8BA50 7-bit I2C address: 0x18.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define STK8BA50_REG_XOUT 0x02
|
||||
#define STK8BA50_REG_YOUT 0x04
|
||||
#define STK8BA50_REG_ZOUT 0x06
|
||||
#define STK8BA50_REG_RANGE 0x0F
|
||||
#define STK8BA50_REG_POWMODE 0x11
|
||||
#define STK8BA50_REG_SWRST 0x14
|
||||
|
||||
#define STK8BA50_MODE_NORMAL 0
|
||||
#define STK8BA50_MODE_SUSPEND 1
|
||||
#define STK8BA50_MODE_POWERBIT BIT(7)
|
||||
#define STK8BA50_DATA_SHIFT 6
|
||||
#define STK8BA50_RESET_CMD 0xB6
|
||||
|
||||
#define STK8BA50_DRIVER_NAME "stk8ba50"
|
||||
|
||||
#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
|
||||
|
||||
/*
|
||||
* The accelerometer has four measurement ranges:
|
||||
* +/-2g; +/-4g; +/-8g; +/-16g
|
||||
*
|
||||
* Acceleration values are 10-bit, 2's complement.
|
||||
* Scales are calculated as following:
|
||||
*
|
||||
* scale1 = (2 + 2) * 9.81 / (2^10 - 1) = 0.0384
|
||||
* scale2 = (4 + 4) * 9.81 / (2^10 - 1) = 0.0767
|
||||
* etc.
|
||||
*
|
||||
* Scales are stored in this format:
|
||||
* { <register value>, <scale value> }
|
||||
*
|
||||
* Locally, the range is stored as a table index.
|
||||
*/
|
||||
static const int stk8ba50_scale_table[][2] = {
|
||||
{3, 38400}, {5, 76700}, {8, 153400}, {12, 306900}
|
||||
};
|
||||
|
||||
struct stk8ba50_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
int range;
|
||||
};
|
||||
|
||||
#define STK8BA50_ACCEL_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec stk8ba50_channels[] = {
|
||||
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_XOUT, X),
|
||||
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_YOUT, Y),
|
||||
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_ZOUT, Z),
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_accel_scale_available, STK8BA50_SCALE_AVAIL);
|
||||
|
||||
static struct attribute *stk8ba50_attributes[] = {
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group stk8ba50_attribute_group = {
|
||||
.attrs = stk8ba50_attributes
|
||||
};
|
||||
|
||||
static int stk8ba50_read_accel(struct stk8ba50_data *data, u8 reg)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "register read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9);
|
||||
}
|
||||
|
||||
static int stk8ba50_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->lock);
|
||||
*val = stk8ba50_read_accel(data, chan->address);
|
||||
mutex_unlock(&data->lock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = stk8ba50_scale_table[data->range][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stk8ba50_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
int index = -1;
|
||||
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stk8ba50_scale_table); i++)
|
||||
if (val2 == stk8ba50_scale_table[i][1]) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
STK8BA50_REG_RANGE,
|
||||
stk8ba50_scale_table[index][0]);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev,
|
||||
"failed to set measurement range\n");
|
||||
else
|
||||
data->range = index;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info stk8ba50_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = stk8ba50_read_raw,
|
||||
.write_raw = stk8ba50_write_raw,
|
||||
.attrs = &stk8ba50_attribute_group,
|
||||
};
|
||||
|
||||
static int stk8ba50_set_power(struct stk8ba50_data *data, bool mode)
|
||||
{
|
||||
int ret;
|
||||
u8 masked_reg;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, STK8BA50_REG_POWMODE);
|
||||
if (ret < 0)
|
||||
goto exit_err;
|
||||
|
||||
if (mode)
|
||||
masked_reg = ret | STK8BA50_MODE_POWERBIT;
|
||||
else
|
||||
masked_reg = ret & (~STK8BA50_MODE_POWERBIT);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, STK8BA50_REG_POWMODE,
|
||||
masked_reg);
|
||||
if (ret < 0)
|
||||
goto exit_err;
|
||||
|
||||
return ret;
|
||||
|
||||
exit_err:
|
||||
dev_err(&client->dev, "failed to change sensor mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk8ba50_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct stk8ba50_data *data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &stk8ba50_info;
|
||||
indio_dev->name = STK8BA50_DRIVER_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = stk8ba50_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(stk8ba50_channels);
|
||||
|
||||
/* Reset all registers on startup */
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
STK8BA50_REG_SWRST, STK8BA50_RESET_CMD);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to reset sensor\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The default range is +/-2g */
|
||||
data->range = 0;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk8ba50_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return stk8ba50_set_power(iio_priv(indio_dev), STK8BA50_MODE_SUSPEND);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int stk8ba50_suspend(struct device *dev)
|
||||
{
|
||||
struct stk8ba50_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
||||
}
|
||||
|
||||
static int stk8ba50_resume(struct device *dev)
|
||||
{
|
||||
struct stk8ba50_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume);
|
||||
|
||||
#define STK8BA50_PM_OPS (&stk8ba50_pm_ops)
|
||||
#else
|
||||
#define STK8BA50_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id stk8ba50_i2c_id[] = {
|
||||
{"stk8ba50", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct acpi_device_id stk8ba50_acpi_id[] = {
|
||||
{"STK8BA50", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id);
|
||||
|
||||
static struct i2c_driver stk8ba50_driver = {
|
||||
.driver = {
|
||||
.name = "stk8ba50",
|
||||
.pm = STK8BA50_PM_OPS,
|
||||
.acpi_match_table = ACPI_PTR(stk8ba50_acpi_id),
|
||||
},
|
||||
.probe = stk8ba50_probe,
|
||||
.remove = stk8ba50_remove,
|
||||
.id_table = stk8ba50_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(stk8ba50_driver);
|
||||
|
||||
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
|
||||
MODULE_DESCRIPTION("STK8BA50 3-Axis Accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -135,6 +135,13 @@ config AXP288_ADC
|
||||
device. Depending on platform configuration, this general purpose ADC can
|
||||
be used for sampling sensors such as thermal resistors.
|
||||
|
||||
config BERLIN2_ADC
|
||||
tristate "Marvell Berlin2 ADC driver"
|
||||
depends on ARCH_BERLIN
|
||||
help
|
||||
Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for
|
||||
temperature measurement.
|
||||
|
||||
config DA9150_GPADC
|
||||
tristate "Dialog DA9150 GPADC driver support"
|
||||
depends on MFD_DA9150
|
||||
@ -285,11 +292,11 @@ config TI_ADC081C
|
||||
called ti-adc081c.
|
||||
|
||||
config TI_ADC128S052
|
||||
tristate "Texas Instruments ADC128S052"
|
||||
tristate "Texas Instruments ADC128S052/ADC122S021"
|
||||
depends on SPI
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC128S052
|
||||
chip.
|
||||
and ADC122S021 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc128s052.
|
||||
|
@ -15,6 +15,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
|
||||
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
|
||||
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
|
||||
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
|
@ -238,7 +238,7 @@ static int axp288_adc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id axp288_adc_id_table[] = {
|
||||
static const struct platform_device_id axp288_adc_id_table[] = {
|
||||
{ .name = "axp288_adc" },
|
||||
{},
|
||||
};
|
||||
|
378
drivers/iio/adc/berlin2-adc.c
Normal file
378
drivers/iio/adc/berlin2-adc.c
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Marvell Berlin2 ADC driver
|
||||
*
|
||||
* Copyright (C) 2015 Marvell Technology Group Ltd.
|
||||
*
|
||||
* Antoine Tenart <antoine.tenart@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#define BERLIN2_SM_CTRL 0x14
|
||||
#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
|
||||
#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
|
||||
#define BERLIN2_SM_CTRL_ADC_SEL(x) (BIT(x) << 5) /* 0-15 */
|
||||
#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5)
|
||||
#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK (0x3 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_START BIT(12)
|
||||
#define BERLIN2_SM_CTRL_ADC_RESET BIT(13)
|
||||
#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14)
|
||||
#define BERLIN2_SM_CTRL_ADC_CONT_SINGLE (0x0 << 15)
|
||||
#define BERLIN2_SM_CTRL_ADC_CONT_CONTINUOUS (0x1 << 15)
|
||||
#define BERLIN2_SM_CTRL_ADC_BUFFER_EN BIT(16)
|
||||
#define BERLIN2_SM_CTRL_ADC_VREF_EXT (0x0 << 17)
|
||||
#define BERLIN2_SM_CTRL_ADC_VREF_INT (0x1 << 17)
|
||||
#define BERLIN2_SM_CTRL_ADC_ROTATE BIT(19)
|
||||
#define BERLIN2_SM_CTRL_TSEN_EN BIT(20)
|
||||
#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_125 (0x0 << 21) /* 1.25 MHz */
|
||||
#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_250 (0x1 << 21) /* 2.5 MHz */
|
||||
#define BERLIN2_SM_CTRL_TSEN_MODE_0_125 (0x0 << 22) /* 0-125 C */
|
||||
#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */
|
||||
#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29)
|
||||
#define BERLIN2_SM_ADC_DATA 0x20
|
||||
#define BERLIN2_SM_ADC_MASK 0x3ff
|
||||
#define BERLIN2_SM_ADC_STATUS 0x1c
|
||||
#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
|
||||
#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK 0xf
|
||||
#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */
|
||||
#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK (0xf << 16)
|
||||
#define BERLIN2_SM_TSEN_STATUS 0x24
|
||||
#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0)
|
||||
#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1)
|
||||
#define BERLIN2_SM_TSEN_DATA 0x28
|
||||
#define BERLIN2_SM_TSEN_MASK 0xfff
|
||||
#define BERLIN2_SM_TSEN_CTRL 0x74
|
||||
#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
|
||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
|
||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */
|
||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK (0x1 << 21)
|
||||
#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22)
|
||||
#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK (0xf << 22)
|
||||
|
||||
struct berlin2_adc_priv {
|
||||
struct regmap *regmap;
|
||||
struct mutex lock;
|
||||
wait_queue_head_t wq;
|
||||
bool data_available;
|
||||
int data;
|
||||
};
|
||||
|
||||
#define BERLIN2_ADC_CHANNEL(n, t) \
|
||||
{ \
|
||||
.channel = n, \
|
||||
.datasheet_name = "channel"#n, \
|
||||
.type = t, \
|
||||
.indexed = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static struct iio_chan_spec berlin2_adc_channels[] = {
|
||||
BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
|
||||
BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */
|
||||
BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */
|
||||
BERLIN2_ADC_CHANNEL(3, IIO_VOLTAGE), /* external input */
|
||||
BERLIN2_ADC_CHANNEL(4, IIO_VOLTAGE), /* reserved */
|
||||
BERLIN2_ADC_CHANNEL(5, IIO_VOLTAGE), /* reserved */
|
||||
{ /* temperature sensor */
|
||||
.channel = 6,
|
||||
.datasheet_name = "channel6",
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */
|
||||
};
|
||||
#define BERLIN2_N_CHANNELS ARRAY_SIZE(berlin2_adc_channels)
|
||||
|
||||
static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
|
||||
{
|
||||
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
|
||||
int data, ret;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* Configure the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK
|
||||
| BERLIN2_SM_CTRL_ADC_START,
|
||||
BERLIN2_SM_CTRL_ADC_SEL(channel) | BERLIN2_SM_CTRL_ADC_START);
|
||||
|
||||
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
/* Disable the interrupts */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS,
|
||||
BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
|
||||
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_START, 0);
|
||||
|
||||
data = priv->data;
|
||||
priv->data_available = false;
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
|
||||
int data, ret;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* Configure the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE,
|
||||
BERLIN2_SM_CTRL_ADC_ROTATE);
|
||||
|
||||
/* Configure the temperature sensor */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
|
||||
BERLIN2_SM_TSEN_CTRL_TRIM_MASK | BERLIN2_SM_TSEN_CTRL_SETTLING_MASK
|
||||
| BERLIN2_SM_TSEN_CTRL_START,
|
||||
BERLIN2_SM_TSEN_CTRL_TRIM(3) | BERLIN2_SM_TSEN_CTRL_SETTLING_12
|
||||
| BERLIN2_SM_TSEN_CTRL_START);
|
||||
|
||||
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
/* Disable interrupts */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS,
|
||||
BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
|
||||
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
|
||||
BERLIN2_SM_TSEN_CTRL_START, 0);
|
||||
|
||||
data = priv->data;
|
||||
priv->data_available = false;
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
|
||||
int temp;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type != IIO_VOLTAGE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Enable the interrupts */
|
||||
regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
|
||||
BERLIN2_SM_ADC_STATUS_INT_EN(chan->channel));
|
||||
|
||||
*val = berlin2_adc_read(indio_dev, chan->channel);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
if (chan->type != IIO_TEMP)
|
||||
return -EINVAL;
|
||||
|
||||
/* Enable interrupts */
|
||||
regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
|
||||
BERLIN2_SM_TSEN_STATUS_INT_EN);
|
||||
|
||||
temp = berlin2_adc_tsen_read(indio_dev);
|
||||
if (temp < 0)
|
||||
return temp;
|
||||
|
||||
if (temp > 2047)
|
||||
temp = -(4096 - temp);
|
||||
|
||||
/* Convert to milli Celsius */
|
||||
*val = ((temp * 100000) / 264 - 270000);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static irqreturn_t berlin2_adc_irq(int irq, void *private)
|
||||
{
|
||||
struct berlin2_adc_priv *priv = iio_priv(private);
|
||||
unsigned val;
|
||||
|
||||
regmap_read(priv->regmap, BERLIN2_SM_ADC_STATUS, &val);
|
||||
if (val & BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK) {
|
||||
regmap_read(priv->regmap, BERLIN2_SM_ADC_DATA, &priv->data);
|
||||
priv->data &= BERLIN2_SM_ADC_MASK;
|
||||
|
||||
val &= ~BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK;
|
||||
regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, val);
|
||||
|
||||
priv->data_available = true;
|
||||
wake_up_interruptible(&priv->wq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t berlin2_adc_tsen_irq(int irq, void *private)
|
||||
{
|
||||
struct berlin2_adc_priv *priv = iio_priv(private);
|
||||
unsigned val;
|
||||
|
||||
regmap_read(priv->regmap, BERLIN2_SM_TSEN_STATUS, &val);
|
||||
if (val & BERLIN2_SM_TSEN_STATUS_DATA_RDY) {
|
||||
regmap_read(priv->regmap, BERLIN2_SM_TSEN_DATA, &priv->data);
|
||||
priv->data &= BERLIN2_SM_TSEN_MASK;
|
||||
|
||||
val &= ~BERLIN2_SM_TSEN_STATUS_DATA_RDY;
|
||||
regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, val);
|
||||
|
||||
priv->data_available = true;
|
||||
wake_up_interruptible(&priv->wq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info berlin2_adc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = berlin2_adc_read_raw,
|
||||
};
|
||||
|
||||
static int berlin2_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct berlin2_adc_priv *priv;
|
||||
struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
|
||||
int irq, tsen_irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct berlin2_adc_priv));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
priv->regmap = syscon_node_to_regmap(parent_np);
|
||||
of_node_put(parent_np);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "adc");
|
||||
if (irq < 0)
|
||||
return -ENODEV;
|
||||
|
||||
tsen_irq = platform_get_irq_byname(pdev, "tsen");
|
||||
if (tsen_irq < 0)
|
||||
return -ENODEV;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
|
||||
pdev->dev.driver->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq,
|
||||
0, pdev->dev.driver->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_waitqueue_head(&priv->wq);
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &berlin2_adc_info;
|
||||
|
||||
indio_dev->num_channels = BERLIN2_N_CHANNELS;
|
||||
indio_dev->channels = berlin2_adc_channels;
|
||||
|
||||
/* Power up the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
/* Power down the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_POWER, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int berlin2_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
/* Power down the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_POWER, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id berlin2_adc_match[] = {
|
||||
{ .compatible = "marvell,berlin2-adc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, berlin2_adc_match);
|
||||
|
||||
static struct platform_driver berlin2_adc_driver = {
|
||||
.driver = {
|
||||
.name = "berlin2-adc",
|
||||
.of_match_table = berlin2_adc_match,
|
||||
},
|
||||
.probe = berlin2_adc_probe,
|
||||
.remove = berlin2_adc_remove,
|
||||
};
|
||||
module_platform_driver(berlin2_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Marvell Berlin2 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,9 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
* Driver for Texas Instruments' ADC128S052 ADC chip.
|
||||
* Datasheet can be found here:
|
||||
* Driver for Texas Instruments' ADC128S052 and ADC122S021 ADC chip.
|
||||
* Datasheets can be found here:
|
||||
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/adc122s021.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
|
||||
@ -16,6 +17,11 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct adc128_configuration {
|
||||
const struct iio_chan_spec *channels;
|
||||
u8 num_channels;
|
||||
};
|
||||
|
||||
struct adc128 {
|
||||
struct spi_device *spi;
|
||||
|
||||
@ -92,7 +98,7 @@ static int adc128_read_raw(struct iio_dev *indio_dev,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adc128_channels[] = {
|
||||
static const struct iio_chan_spec adc128s052_channels[] = {
|
||||
ADC128_VOLTAGE_CHANNEL(0),
|
||||
ADC128_VOLTAGE_CHANNEL(1),
|
||||
ADC128_VOLTAGE_CHANNEL(2),
|
||||
@ -103,6 +109,16 @@ static const struct iio_chan_spec adc128_channels[] = {
|
||||
ADC128_VOLTAGE_CHANNEL(7),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc122s021_channels[] = {
|
||||
ADC128_VOLTAGE_CHANNEL(0),
|
||||
ADC128_VOLTAGE_CHANNEL(1),
|
||||
};
|
||||
|
||||
static const struct adc128_configuration adc128_config[] = {
|
||||
{ adc128s052_channels, ARRAY_SIZE(adc128s052_channels) },
|
||||
{ adc122s021_channels, ARRAY_SIZE(adc122s021_channels) },
|
||||
};
|
||||
|
||||
static const struct iio_info adc128_info = {
|
||||
.read_raw = adc128_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
@ -112,6 +128,7 @@ static int adc128_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc128 *adc;
|
||||
int config = spi_get_device_id(spi)->driver_data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
@ -128,8 +145,8 @@ static int adc128_probe(struct spi_device *spi)
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &adc128_info;
|
||||
|
||||
indio_dev->channels = adc128_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc128_channels);
|
||||
indio_dev->channels = adc128_config[config].channels;
|
||||
indio_dev->num_channels = adc128_config[config].num_channels;
|
||||
|
||||
adc->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(adc->reg))
|
||||
@ -158,7 +175,8 @@ static int adc128_remove(struct spi_device *spi)
|
||||
}
|
||||
|
||||
static const struct spi_device_id adc128_id[] = {
|
||||
{ "adc128s052", 0},
|
||||
{ "adc128s052", 0}, /* index into adc128_config */
|
||||
{ "adc122s021", 1},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adc128_id);
|
||||
|
@ -37,6 +37,7 @@ struct tiadc_device {
|
||||
u8 channel_step[8];
|
||||
int buffer_en_ch_steps;
|
||||
u16 data[8];
|
||||
u32 open_delay[8], sample_delay[8], step_avg[8];
|
||||
};
|
||||
|
||||
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
|
||||
@ -85,6 +86,7 @@ static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
|
||||
static void tiadc_step_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct device *dev = adc_dev->mfd_tscadc->dev;
|
||||
unsigned int stepconfig;
|
||||
int i, steps = 0;
|
||||
|
||||
@ -98,20 +100,47 @@ static void tiadc_step_config(struct iio_dev *indio_dev)
|
||||
* needs to be given to ADC to digitalize data.
|
||||
*/
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
|
||||
| STEPCONFIG_MODE_SWCNT;
|
||||
else
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
|
||||
|
||||
for (i = 0; i < adc_dev->channels; i++) {
|
||||
int chan;
|
||||
|
||||
chan = adc_dev->channel_line[i];
|
||||
|
||||
if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) {
|
||||
dev_warn(dev, "chan %d step_avg truncating to %d\n",
|
||||
chan, STEPCONFIG_AVG_16);
|
||||
adc_dev->step_avg[i] = STEPCONFIG_AVG_16;
|
||||
}
|
||||
|
||||
if (adc_dev->step_avg[i])
|
||||
stepconfig =
|
||||
STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) |
|
||||
STEPCONFIG_FIFO1;
|
||||
else
|
||||
stepconfig = STEPCONFIG_FIFO1;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
stepconfig |= STEPCONFIG_MODE_SWCNT;
|
||||
|
||||
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
|
||||
stepconfig | STEPCONFIG_INP(chan));
|
||||
|
||||
if (adc_dev->open_delay[i] > STEPDELAY_OPEN_MASK) {
|
||||
dev_warn(dev, "chan %d open delay truncating to 0x3FFFF\n",
|
||||
chan);
|
||||
adc_dev->open_delay[i] = STEPDELAY_OPEN_MASK;
|
||||
}
|
||||
|
||||
if (adc_dev->sample_delay[i] > 0xFF) {
|
||||
dev_warn(dev, "chan %d sample delay truncating to 0xFF\n",
|
||||
chan);
|
||||
adc_dev->sample_delay[i] = 0xFF;
|
||||
}
|
||||
|
||||
tiadc_writel(adc_dev, REG_STEPDELAY(steps),
|
||||
STEPCONFIG_OPENDLY);
|
||||
STEPDELAY_OPEN(adc_dev->open_delay[i]) |
|
||||
STEPDELAY_SAMPLE(adc_dev->sample_delay[i]));
|
||||
|
||||
adc_dev->channel_step[i] = steps;
|
||||
steps++;
|
||||
}
|
||||
@ -406,9 +435,22 @@ static int tiadc_parse_dt(struct platform_device *pdev,
|
||||
|
||||
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
|
||||
adc_dev->channel_line[channels] = val;
|
||||
|
||||
/* Set Default values for optional DT parameters */
|
||||
adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY;
|
||||
adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY;
|
||||
adc_dev->step_avg[channels] = 16;
|
||||
|
||||
channels++;
|
||||
}
|
||||
|
||||
of_property_read_u32_array(node, "ti,chan-step-avg",
|
||||
adc_dev->step_avg, channels);
|
||||
of_property_read_u32_array(node, "ti,chan-step-opendelay",
|
||||
adc_dev->open_delay, channels);
|
||||
of_property_read_u32_array(node, "ti,chan-step-sampledelay",
|
||||
adc_dev->sample_delay, channels);
|
||||
|
||||
adc_dev->channels = channels;
|
||||
return 0;
|
||||
}
|
||||
|
@ -142,6 +142,16 @@ config AD7303
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad7303.
|
||||
|
||||
config M62332
|
||||
tristate "Mitsubishi M62332 DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Mitsubishi M62332
|
||||
(I2C 8-Bit DACs with rail-to-rail outputs).
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called m62332.
|
||||
|
||||
config MAX517
|
||||
tristate "Maxim MAX517/518/519/520/521 DAC driver"
|
||||
depends on I2C
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_M62332) += m62332.o
|
||||
obj-$(CONFIG_MAX517) += max517.o
|
||||
obj-$(CONFIG_MAX5821) += max5821.o
|
||||
obj-$(CONFIG_MCP4725) += mcp4725.o
|
||||
|
269
drivers/iio/dac/m62332.c
Normal file
269
drivers/iio/dac/m62332.c
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* m62332.c - Support for Mitsubishi m62332 DAC
|
||||
*
|
||||
* Copyright (c) 2014 Dmitry Eremin-Solenikov
|
||||
*
|
||||
* Based on max517 driver:
|
||||
* Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define M62332_CHANNELS 2
|
||||
|
||||
struct m62332_data {
|
||||
struct i2c_client *client;
|
||||
u16 vref_mv;
|
||||
struct regulator *vcc;
|
||||
struct mutex mutex;
|
||||
u8 raw[M62332_CHANNELS];
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
u8 save[M62332_CHANNELS];
|
||||
#endif
|
||||
};
|
||||
|
||||
static int m62332_set_value(struct iio_dev *indio_dev,
|
||||
u8 val, int channel)
|
||||
{
|
||||
struct m62332_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 outbuf[2];
|
||||
int res;
|
||||
|
||||
if (val == data->raw[channel])
|
||||
return 0;
|
||||
|
||||
outbuf[0] = channel;
|
||||
outbuf[1] = val;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
if (val) {
|
||||
res = regulator_enable(data->vcc);
|
||||
if (res)
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = i2c_master_send(client, outbuf, 2);
|
||||
if (res >= 0 && res != 2)
|
||||
res = -EIO;
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
data->raw[channel] = val;
|
||||
|
||||
if (!val)
|
||||
regulator_disable(data->vcc);
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int m62332_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct m62332_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Corresponds to Vref / 2^(bits) */
|
||||
*val = data->vref_mv;
|
||||
*val2 = 8;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->raw[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 1;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int m62332_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
|
||||
ret = m62332_set_value(indio_dev, val, chan->channel);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int m62332_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct m62332_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
data->save[0] = data->raw[0];
|
||||
data->save[1] = data->raw[1];
|
||||
|
||||
ret = m62332_set_value(indio_dev, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return m62332_set_value(indio_dev, 0, 1);
|
||||
}
|
||||
|
||||
static int m62332_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct m62332_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = m62332_set_value(indio_dev, data->save[0], 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return m62332_set_value(indio_dev, data->save[1], 1);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
|
||||
#define M62332_PM_OPS (&m62332_pm_ops)
|
||||
#else
|
||||
#define M62332_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct iio_info m62332_info = {
|
||||
.read_raw = m62332_read_raw,
|
||||
.write_raw = m62332_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define M62332_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.datasheet_name = "CH" #chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec m62332_channels[M62332_CHANNELS] = {
|
||||
M62332_CHANNEL(0),
|
||||
M62332_CHANNEL(1)
|
||||
};
|
||||
|
||||
static int m62332_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct m62332_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
data->vcc = devm_regulator_get(&client->dev, "VCC");
|
||||
if (IS_ERR(data->vcc))
|
||||
return PTR_ERR(data->vcc);
|
||||
|
||||
/* establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
indio_dev->num_channels = M62332_CHANNELS;
|
||||
indio_dev->channels = m62332_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &m62332_info;
|
||||
|
||||
ret = regulator_get_voltage(data->vcc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->vref_mv = ret / 1000; /* mV */
|
||||
|
||||
ret = iio_map_array_register(indio_dev, client->dev.platform_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int m62332_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id m62332_id[] = {
|
||||
{ "m62332", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, m62332_id);
|
||||
|
||||
static struct i2c_driver m62332_driver = {
|
||||
.driver = {
|
||||
.name = "m62332",
|
||||
.pm = M62332_PM_OPS,
|
||||
},
|
||||
.probe = m62332_probe,
|
||||
.remove = m62332_remove,
|
||||
.id_table = m62332_id,
|
||||
};
|
||||
module_i2c_driver(m62332_driver);
|
||||
|
||||
MODULE_AUTHOR("Dmitry Eremin-Solenikov");
|
||||
MODULE_DESCRIPTION("M62332 8-bit DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -108,7 +108,6 @@ struct bmg160_data {
|
||||
int slope_thres;
|
||||
bool dready_trigger_on;
|
||||
bool motion_trigger_on;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
enum bmg160_axis {
|
||||
@ -738,17 +737,6 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmg160_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (data->dready_trig != trig && data->motion_trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000");
|
||||
|
||||
static IIO_CONST_ATTR(in_anglvel_scale_available,
|
||||
@ -810,7 +798,6 @@ static const struct iio_info bmg160_info = {
|
||||
.write_event_value = bmg160_write_event,
|
||||
.write_event_config = bmg160_write_event_config,
|
||||
.read_event_config = bmg160_read_event_config,
|
||||
.validate_trigger = bmg160_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
@ -835,7 +822,7 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p)
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
data->timestamp);
|
||||
pf->timestamp);
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
@ -938,21 +925,21 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
|
||||
IIO_MOD_X,
|
||||
IIO_EV_TYPE_ROC,
|
||||
dir),
|
||||
data->timestamp);
|
||||
iio_get_time_ns());
|
||||
if (ret & BMG160_ANY_MOTION_BIT_Y)
|
||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
|
||||
0,
|
||||
IIO_MOD_Y,
|
||||
IIO_EV_TYPE_ROC,
|
||||
dir),
|
||||
data->timestamp);
|
||||
iio_get_time_ns());
|
||||
if (ret & BMG160_ANY_MOTION_BIT_Z)
|
||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
|
||||
0,
|
||||
IIO_MOD_Z,
|
||||
IIO_EV_TYPE_ROC,
|
||||
dir),
|
||||
data->timestamp);
|
||||
iio_get_time_ns());
|
||||
|
||||
ack_intr_status:
|
||||
if (!data->dready_trigger_on) {
|
||||
@ -973,8 +960,6 @@ static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private)
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->timestamp = iio_get_time_ns();
|
||||
|
||||
if (data->dready_trigger_on)
|
||||
iio_trigger_poll(data->dready_trig);
|
||||
else if (data->motion_trigger_on)
|
||||
@ -987,6 +972,27 @@ static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private)
|
||||
|
||||
}
|
||||
|
||||
static int bmg160_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
|
||||
return bmg160_set_power_state(data, true);
|
||||
}
|
||||
|
||||
static int bmg160_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
|
||||
return bmg160_set_power_state(data, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = {
|
||||
.preenable = bmg160_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
.postdisable = bmg160_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int bmg160_gpio_probe(struct i2c_client *client,
|
||||
struct bmg160_data *data)
|
||||
|
||||
@ -1103,16 +1109,16 @@ static int bmg160_probe(struct i2c_client *client,
|
||||
data->motion_trig = NULL;
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
NULL,
|
||||
bmg160_trigger_handler,
|
||||
NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"iio triggered buffer setup failed\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
bmg160_trigger_handler,
|
||||
&bmg160_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"iio triggered buffer setup failed\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
@ -1135,8 +1141,7 @@ static int bmg160_probe(struct i2c_client *client,
|
||||
err_iio_unregister:
|
||||
iio_device_unregister(indio_dev);
|
||||
err_buffer_cleanup:
|
||||
if (data->dready_trig)
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_trigger_unregister:
|
||||
if (data->dready_trig)
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
@ -1156,9 +1161,9 @@ static int bmg160_remove(struct i2c_client *client)
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
if (data->dready_trig) {
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
iio_trigger_unregister(data->motion_trig);
|
||||
}
|
||||
|
@ -298,7 +298,6 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct gyro_3d_state *gyro_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*gyro_state));
|
||||
if (!indio_dev)
|
||||
@ -317,21 +316,21 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(gyro_3d_channels, sizeof(gyro_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
indio_dev->channels = kmemdup(gyro_3d_channels,
|
||||
sizeof(gyro_3d_channels), GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = gyro_3d_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_GYRO_3D, gyro_state);
|
||||
ret = gyro_3d_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_GYRO_3D, gyro_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &gyro_3d_info;
|
||||
@ -397,7 +396,7 @@ static int hid_gyro_3d_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_gyro_3d_ids[] = {
|
||||
static const struct platform_device_id hid_gyro_3d_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200076",
|
||||
|
@ -5,7 +5,7 @@ menu "Humidity sensors"
|
||||
|
||||
config DHT11
|
||||
tristate "DHT11 (and compatible sensors) driver"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
This driver supports reading data via a single interrupt
|
||||
generating GPIO line. Currently tested are DHT11 and DHT22.
|
||||
|
@ -539,26 +539,13 @@ static void iio_buffer_deactivate(struct iio_buffer *buffer)
|
||||
iio_buffer_put(buffer);
|
||||
}
|
||||
|
||||
void iio_disable_all_buffers(struct iio_dev *indio_dev)
|
||||
static void iio_buffer_deactivate_all(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer, *_buffer;
|
||||
|
||||
if (list_empty(&indio_dev->buffer_list))
|
||||
return;
|
||||
|
||||
if (indio_dev->setup_ops->predisable)
|
||||
indio_dev->setup_ops->predisable(indio_dev);
|
||||
|
||||
list_for_each_entry_safe(buffer, _buffer,
|
||||
&indio_dev->buffer_list, buffer_list)
|
||||
iio_buffer_deactivate(buffer);
|
||||
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
if (indio_dev->setup_ops->postdisable)
|
||||
indio_dev->setup_ops->postdisable(indio_dev);
|
||||
|
||||
if (indio_dev->available_scan_masks == NULL)
|
||||
kfree(indio_dev->active_scan_mask);
|
||||
}
|
||||
|
||||
static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
|
||||
@ -575,167 +562,256 @@ static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
|
||||
buffer->access->set_bytes_per_datum(buffer, bytes);
|
||||
}
|
||||
|
||||
static int __iio_update_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *insert_buffer,
|
||||
struct iio_buffer *remove_buffer)
|
||||
static int iio_buffer_request_update(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
int ret;
|
||||
int success = 0;
|
||||
struct iio_buffer *buffer;
|
||||
unsigned long *compound_mask;
|
||||
const unsigned long *old_mask;
|
||||
|
||||
/* Wind down existing buffers - iff there are any */
|
||||
if (!list_empty(&indio_dev->buffer_list)) {
|
||||
if (indio_dev->setup_ops->predisable) {
|
||||
ret = indio_dev->setup_ops->predisable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
if (indio_dev->setup_ops->postdisable) {
|
||||
ret = indio_dev->setup_ops->postdisable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
iio_buffer_update_bytes_per_datum(indio_dev, buffer);
|
||||
if (buffer->access->request_update) {
|
||||
ret = buffer->access->request_update(buffer);
|
||||
if (ret) {
|
||||
dev_dbg(&indio_dev->dev,
|
||||
"Buffer not started: buffer parameter update failed (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* Keep a copy of current setup to allow roll back */
|
||||
old_mask = indio_dev->active_scan_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iio_free_scan_mask(struct iio_dev *indio_dev,
|
||||
const unsigned long *mask)
|
||||
{
|
||||
/* If the mask is dynamically allocated free it, otherwise do nothing */
|
||||
if (!indio_dev->available_scan_masks)
|
||||
indio_dev->active_scan_mask = NULL;
|
||||
kfree(mask);
|
||||
}
|
||||
|
||||
if (remove_buffer)
|
||||
iio_buffer_deactivate(remove_buffer);
|
||||
if (insert_buffer)
|
||||
iio_buffer_activate(indio_dev, insert_buffer);
|
||||
struct iio_device_config {
|
||||
unsigned int mode;
|
||||
const unsigned long *scan_mask;
|
||||
unsigned int scan_bytes;
|
||||
bool scan_timestamp;
|
||||
};
|
||||
|
||||
/* If no buffers in list, we are done */
|
||||
if (list_empty(&indio_dev->buffer_list)) {
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
if (indio_dev->available_scan_masks == NULL)
|
||||
kfree(old_mask);
|
||||
return 0;
|
||||
static int iio_verify_update(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *insert_buffer, struct iio_buffer *remove_buffer,
|
||||
struct iio_device_config *config)
|
||||
{
|
||||
unsigned long *compound_mask;
|
||||
const unsigned long *scan_mask;
|
||||
struct iio_buffer *buffer;
|
||||
bool scan_timestamp;
|
||||
|
||||
memset(config, 0, sizeof(*config));
|
||||
|
||||
/*
|
||||
* If there is just one buffer and we are removing it there is nothing
|
||||
* to verify.
|
||||
*/
|
||||
if (remove_buffer && !insert_buffer &&
|
||||
list_is_singular(&indio_dev->buffer_list))
|
||||
return 0;
|
||||
|
||||
/* Definitely possible for devices to support both of these. */
|
||||
if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
|
||||
config->mode = INDIO_BUFFER_TRIGGERED;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
|
||||
config->mode = INDIO_BUFFER_HARDWARE;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
|
||||
config->mode = INDIO_BUFFER_SOFTWARE;
|
||||
} else {
|
||||
/* Can only occur on first buffer */
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
|
||||
dev_dbg(&indio_dev->dev, "Buffer not started: no trigger\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* What scan mask do we actually have? */
|
||||
compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
|
||||
sizeof(long), GFP_KERNEL);
|
||||
if (compound_mask == NULL) {
|
||||
if (indio_dev->available_scan_masks == NULL)
|
||||
kfree(old_mask);
|
||||
if (compound_mask == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
indio_dev->scan_timestamp = 0;
|
||||
|
||||
scan_timestamp = false;
|
||||
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
if (buffer == remove_buffer)
|
||||
continue;
|
||||
bitmap_or(compound_mask, compound_mask, buffer->scan_mask,
|
||||
indio_dev->masklength);
|
||||
indio_dev->scan_timestamp |= buffer->scan_timestamp;
|
||||
scan_timestamp |= buffer->scan_timestamp;
|
||||
}
|
||||
|
||||
if (insert_buffer) {
|
||||
bitmap_or(compound_mask, compound_mask,
|
||||
insert_buffer->scan_mask, indio_dev->masklength);
|
||||
scan_timestamp |= insert_buffer->scan_timestamp;
|
||||
}
|
||||
|
||||
if (indio_dev->available_scan_masks) {
|
||||
indio_dev->active_scan_mask =
|
||||
iio_scan_mask_match(indio_dev->available_scan_masks,
|
||||
indio_dev->masklength,
|
||||
compound_mask);
|
||||
if (indio_dev->active_scan_mask == NULL) {
|
||||
/*
|
||||
* Roll back.
|
||||
* Note can only occur when adding a buffer.
|
||||
*/
|
||||
iio_buffer_deactivate(insert_buffer);
|
||||
if (old_mask) {
|
||||
indio_dev->active_scan_mask = old_mask;
|
||||
success = -EINVAL;
|
||||
}
|
||||
else {
|
||||
kfree(compound_mask);
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks,
|
||||
indio_dev->masklength,
|
||||
compound_mask);
|
||||
kfree(compound_mask);
|
||||
if (scan_mask == NULL)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
indio_dev->active_scan_mask = compound_mask;
|
||||
scan_mask = compound_mask;
|
||||
}
|
||||
|
||||
config->scan_bytes = iio_compute_scan_bytes(indio_dev,
|
||||
scan_mask, scan_timestamp);
|
||||
config->scan_mask = scan_mask;
|
||||
config->scan_timestamp = scan_timestamp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_enable_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_device_config *config)
|
||||
{
|
||||
int ret;
|
||||
|
||||
indio_dev->active_scan_mask = config->scan_mask;
|
||||
indio_dev->scan_timestamp = config->scan_timestamp;
|
||||
indio_dev->scan_bytes = config->scan_bytes;
|
||||
|
||||
iio_update_demux(indio_dev);
|
||||
|
||||
/* Wind up again */
|
||||
if (indio_dev->setup_ops->preenable) {
|
||||
ret = indio_dev->setup_ops->preenable(indio_dev);
|
||||
if (ret) {
|
||||
printk(KERN_ERR
|
||||
dev_dbg(&indio_dev->dev,
|
||||
"Buffer not started: buffer preenable failed (%d)\n", ret);
|
||||
goto error_remove_inserted;
|
||||
}
|
||||
}
|
||||
indio_dev->scan_bytes =
|
||||
iio_compute_scan_bytes(indio_dev,
|
||||
indio_dev->active_scan_mask,
|
||||
indio_dev->scan_timestamp);
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
iio_buffer_update_bytes_per_datum(indio_dev, buffer);
|
||||
if (buffer->access->request_update) {
|
||||
ret = buffer->access->request_update(buffer);
|
||||
if (ret) {
|
||||
printk(KERN_INFO
|
||||
"Buffer not started: buffer parameter update failed (%d)\n", ret);
|
||||
goto error_run_postdisable;
|
||||
}
|
||||
goto err_undo_config;
|
||||
}
|
||||
}
|
||||
|
||||
if (indio_dev->info->update_scan_mode) {
|
||||
ret = indio_dev->info
|
||||
->update_scan_mode(indio_dev,
|
||||
indio_dev->active_scan_mask);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "Buffer not started: update scan mode failed (%d)\n", ret);
|
||||
goto error_run_postdisable;
|
||||
dev_dbg(&indio_dev->dev,
|
||||
"Buffer not started: update scan mode failed (%d)\n",
|
||||
ret);
|
||||
goto err_run_postdisable;
|
||||
}
|
||||
}
|
||||
/* Definitely possible for devices to support both of these. */
|
||||
if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
|
||||
} else { /* Should never be reached */
|
||||
/* Can only occur on first buffer */
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
|
||||
pr_info("Buffer not started: no trigger\n");
|
||||
ret = -EINVAL;
|
||||
goto error_run_postdisable;
|
||||
}
|
||||
|
||||
indio_dev->currentmode = config->mode;
|
||||
|
||||
if (indio_dev->setup_ops->postenable) {
|
||||
ret = indio_dev->setup_ops->postenable(indio_dev);
|
||||
if (ret) {
|
||||
printk(KERN_INFO
|
||||
dev_dbg(&indio_dev->dev,
|
||||
"Buffer not started: postenable failed (%d)\n", ret);
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
if (indio_dev->setup_ops->postdisable)
|
||||
indio_dev->setup_ops->postdisable(indio_dev);
|
||||
goto error_disable_all_buffers;
|
||||
goto err_run_postdisable;
|
||||
}
|
||||
}
|
||||
|
||||
if (indio_dev->available_scan_masks)
|
||||
kfree(compound_mask);
|
||||
else
|
||||
kfree(old_mask);
|
||||
return 0;
|
||||
|
||||
return success;
|
||||
|
||||
error_disable_all_buffers:
|
||||
err_run_postdisable:
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
error_run_postdisable:
|
||||
if (indio_dev->setup_ops->postdisable)
|
||||
indio_dev->setup_ops->postdisable(indio_dev);
|
||||
error_remove_inserted:
|
||||
err_undo_config:
|
||||
indio_dev->active_scan_mask = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_disable_buffers(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret2;
|
||||
|
||||
/* Wind down existing buffers - iff there are any */
|
||||
if (list_empty(&indio_dev->buffer_list))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If things go wrong at some step in disable we still need to continue
|
||||
* to perform the other steps, otherwise we leave the device in a
|
||||
* inconsistent state. We return the error code for the first error we
|
||||
* encountered.
|
||||
*/
|
||||
|
||||
if (indio_dev->setup_ops->predisable) {
|
||||
ret2 = indio_dev->setup_ops->predisable(indio_dev);
|
||||
if (ret2 && !ret)
|
||||
ret = ret2;
|
||||
}
|
||||
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
|
||||
if (indio_dev->setup_ops->postdisable) {
|
||||
ret2 = indio_dev->setup_ops->postdisable(indio_dev);
|
||||
if (ret2 && !ret)
|
||||
ret = ret2;
|
||||
}
|
||||
|
||||
iio_free_scan_mask(indio_dev, indio_dev->active_scan_mask);
|
||||
indio_dev->active_scan_mask = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __iio_update_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *insert_buffer,
|
||||
struct iio_buffer *remove_buffer)
|
||||
{
|
||||
struct iio_device_config new_config;
|
||||
int ret;
|
||||
|
||||
ret = iio_verify_update(indio_dev, insert_buffer, remove_buffer,
|
||||
&new_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (insert_buffer) {
|
||||
ret = iio_buffer_request_update(indio_dev, insert_buffer);
|
||||
if (ret)
|
||||
goto err_free_config;
|
||||
}
|
||||
|
||||
ret = iio_disable_buffers(indio_dev);
|
||||
if (ret)
|
||||
goto err_deactivate_all;
|
||||
|
||||
if (remove_buffer)
|
||||
iio_buffer_deactivate(remove_buffer);
|
||||
if (insert_buffer)
|
||||
iio_buffer_deactivate(insert_buffer);
|
||||
indio_dev->active_scan_mask = old_mask;
|
||||
kfree(compound_mask);
|
||||
iio_buffer_activate(indio_dev, insert_buffer);
|
||||
|
||||
/* If no buffers in list, we are done */
|
||||
if (list_empty(&indio_dev->buffer_list))
|
||||
return 0;
|
||||
|
||||
ret = iio_enable_buffers(indio_dev, &new_config);
|
||||
if (ret)
|
||||
goto err_deactivate_all;
|
||||
|
||||
return 0;
|
||||
|
||||
err_deactivate_all:
|
||||
/*
|
||||
* We've already verified that the config is valid earlier. If things go
|
||||
* wrong in either enable or disable the most likely reason is an IO
|
||||
* error from the device. In this case there is no good recovery
|
||||
* strategy. Just make sure to disable everything and leave the device
|
||||
* in a sane state. With a bit of luck the device might come back to
|
||||
* life again later and userspace can try again.
|
||||
*/
|
||||
iio_buffer_deactivate_all(indio_dev);
|
||||
|
||||
err_free_config:
|
||||
iio_free_scan_mask(indio_dev, new_config.scan_mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -777,6 +853,12 @@ out_unlock:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_update_buffers);
|
||||
|
||||
void iio_disable_all_buffers(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_disable_buffers(indio_dev);
|
||||
iio_buffer_deactivate_all(indio_dev);
|
||||
}
|
||||
|
||||
static ssize_t iio_buffer_store_enable(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
|
@ -101,6 +101,8 @@ static const char * const iio_modifier_names[] = {
|
||||
[IIO_MOD_WALKING] = "walking",
|
||||
[IIO_MOD_STILL] = "still",
|
||||
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
|
||||
[IIO_MOD_I] = "i",
|
||||
[IIO_MOD_Q] = "q",
|
||||
};
|
||||
|
||||
/* relies on pairs of these shared then separate */
|
||||
@ -117,6 +119,8 @@ static const char * const iio_chan_info_postfix[] = {
|
||||
[IIO_CHAN_INFO_AVERAGE_RAW] = "mean_raw",
|
||||
[IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY]
|
||||
= "filter_low_pass_3db_frequency",
|
||||
[IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY]
|
||||
= "filter_high_pass_3db_frequency",
|
||||
[IIO_CHAN_INFO_SAMP_FREQ] = "sampling_frequency",
|
||||
[IIO_CHAN_INFO_FREQUENCY] = "frequency",
|
||||
[IIO_CHAN_INFO_PHASE] = "phase",
|
||||
@ -129,6 +133,7 @@ static const char * const iio_chan_info_postfix[] = {
|
||||
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
|
||||
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
|
||||
[IIO_CHAN_INFO_CALIBEMISSIVITY] = "calibemissivity",
|
||||
[IIO_CHAN_INFO_OVERSAMPLING_RATIO] = "oversampling_ratio",
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -211,6 +211,8 @@ static const char * const iio_ev_info_text[] = {
|
||||
[IIO_EV_INFO_VALUE] = "value",
|
||||
[IIO_EV_INFO_HYSTERESIS] = "hysteresis",
|
||||
[IIO_EV_INFO_PERIOD] = "period",
|
||||
[IIO_EV_INFO_HIGH_PASS_FILTER_3DB] = "high_pass_filter_3db",
|
||||
[IIO_EV_INFO_LOW_PASS_FILTER_3DB] = "low_pass_filter_3db",
|
||||
};
|
||||
|
||||
static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)
|
||||
|
@ -5,6 +5,19 @@
|
||||
|
||||
menu "Light sensors"
|
||||
|
||||
config ACPI_ALS
|
||||
tristate "ACPI Ambient Light Sensor"
|
||||
depends on ACPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say Y here if you want to build a driver for the ACPI0008
|
||||
Ambient Light Sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called acpi-als.
|
||||
|
||||
config ADJD_S311
|
||||
tristate "ADJD-S311-CR999 digital color sensor"
|
||||
select IIO_BUFFER
|
||||
@ -37,6 +50,16 @@ config APDS9300
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called apds9300.
|
||||
|
||||
config BH1750
|
||||
tristate "ROHM BH1750 ambient light sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to build support for the ROHM BH1710, BH1715, BH1721,
|
||||
BH1750, BH1751 ambient light sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called bh1750.
|
||||
|
||||
config CM32181
|
||||
depends on I2C
|
||||
tristate "CM32181 driver"
|
||||
@ -175,6 +198,17 @@ config LTR501
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ltr501.
|
||||
|
||||
config STK3310
|
||||
tristate "STK3310 ALS and proximity sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to get support for the Sensortek STK3310 ambient light
|
||||
and proximity sensor. The STK3311 model is also supported by this
|
||||
driver.
|
||||
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called stk3310.
|
||||
|
||||
config TCS3414
|
||||
tristate "TAOS TCS3414 digital color sensor"
|
||||
depends on I2C
|
||||
|
@ -3,9 +3,11 @@
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ACPI_ALS) += acpi-als.o
|
||||
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_AL3320A) += al3320a.o
|
||||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_BH1750) += bh1750.o
|
||||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
obj-$(CONFIG_CM3323) += cm3323.o
|
||||
@ -18,6 +20,7 @@ obj-$(CONFIG_JSA1212) += jsa1212.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_LTR501) += ltr501.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_STK3310) += stk3310.o
|
||||
obj-$(CONFIG_TCS3414) += tcs3414.o
|
||||
obj-$(CONFIG_TCS3472) += tcs3472.o
|
||||
obj-$(CONFIG_TSL4531) += tsl4531.o
|
||||
|
231
drivers/iio/light/acpi-als.c
Normal file
231
drivers/iio/light/acpi-als.c
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* ACPI Ambient Light Sensor Driver
|
||||
*
|
||||
* Based on ALS driver:
|
||||
* Copyright (C) 2009 Zhang Rui <rui.zhang@intel.com>
|
||||
*
|
||||
* Rework for IIO subsystem:
|
||||
* Copyright (C) 2012-2013 Martin Liska <marxin.liska@gmail.com>
|
||||
*
|
||||
* Final cleanup and debugging:
|
||||
* Copyright (C) 2013-2014 Marek Vasut <marex@denx.de>
|
||||
* Copyright (C) 2015 Gabriele Mazzotta <gabriele.mzt@gmail.com>
|
||||
*
|
||||
* 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; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#define ACPI_ALS_CLASS "als"
|
||||
#define ACPI_ALS_DEVICE_NAME "acpi-als"
|
||||
#define ACPI_ALS_NOTIFY_ILLUMINANCE 0x80
|
||||
|
||||
ACPI_MODULE_NAME("acpi-als");
|
||||
|
||||
/*
|
||||
* So far, there's only one channel in here, but the specification for
|
||||
* ACPI0008 says there can be more to what the block can report. Like
|
||||
* chromaticity and such. We are ready for incoming additions!
|
||||
*/
|
||||
static const struct iio_chan_spec acpi_als_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
},
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* The event buffer contains timestamp and all the data from
|
||||
* the ACPI0008 block. There are multiple, but so far we only
|
||||
* support _ALI (illuminance). Once someone adds new channels
|
||||
* to acpi_als_channels[], the evt_buffer below will grow
|
||||
* automatically.
|
||||
*/
|
||||
#define EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels)
|
||||
#define EVT_BUFFER_SIZE \
|
||||
(sizeof(s64) + (EVT_NR_SOURCES * sizeof(s32)))
|
||||
|
||||
struct acpi_als {
|
||||
struct acpi_device *device;
|
||||
struct mutex lock;
|
||||
|
||||
s32 evt_buffer[EVT_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* All types of properties the ACPI0008 block can report. The ALI, ALC, ALT
|
||||
* and ALP can all be handled by als_read_value() below, while the ALR is
|
||||
* special.
|
||||
*
|
||||
* The _ALR property returns tables that can be used to fine-tune the values
|
||||
* reported by the other props based on the particular hardware type and it's
|
||||
* location (it contains tables for "rainy", "bright inhouse lighting" etc.).
|
||||
*
|
||||
* So far, we support only ALI (illuminance).
|
||||
*/
|
||||
#define ACPI_ALS_ILLUMINANCE "_ALI"
|
||||
#define ACPI_ALS_CHROMATICITY "_ALC"
|
||||
#define ACPI_ALS_COLOR_TEMP "_ALT"
|
||||
#define ACPI_ALS_POLLING "_ALP"
|
||||
#define ACPI_ALS_TABLES "_ALR"
|
||||
|
||||
static int als_read_value(struct acpi_als *als, char *prop, s32 *val)
|
||||
{
|
||||
unsigned long long temp_val;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(als->device->handle, prop, NULL,
|
||||
&temp_val);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS %s", prop));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*val = temp_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_als_notify(struct acpi_device *device, u32 event)
|
||||
{
|
||||
struct iio_dev *indio_dev = acpi_driver_data(device);
|
||||
struct acpi_als *als = iio_priv(indio_dev);
|
||||
s32 *buffer = als->evt_buffer;
|
||||
s64 time_ns = iio_get_time_ns();
|
||||
s32 val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&als->lock);
|
||||
|
||||
memset(buffer, 0, EVT_BUFFER_SIZE);
|
||||
|
||||
switch (event) {
|
||||
case ACPI_ALS_NOTIFY_ILLUMINANCE:
|
||||
ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
*buffer++ = val;
|
||||
break;
|
||||
default:
|
||||
/* Unhandled event */
|
||||
dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n",
|
||||
event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns);
|
||||
|
||||
out:
|
||||
mutex_unlock(&als->lock);
|
||||
}
|
||||
|
||||
static int acpi_als_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct acpi_als *als = iio_priv(indio_dev);
|
||||
s32 temp_val;
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
/* we support only illumination (_ALI) so far. */
|
||||
if (chan->type != IIO_LIGHT)
|
||||
return -EINVAL;
|
||||
|
||||
ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = temp_val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info acpi_als_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = acpi_als_read_raw,
|
||||
};
|
||||
|
||||
static int acpi_als_add(struct acpi_device *device)
|
||||
{
|
||||
struct acpi_als *als;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_buffer *buffer;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
als = iio_priv(indio_dev);
|
||||
|
||||
device->driver_data = indio_dev;
|
||||
als->device = device;
|
||||
mutex_init(&als->lock);
|
||||
|
||||
indio_dev->name = ACPI_ALS_DEVICE_NAME;
|
||||
indio_dev->dev.parent = &device->dev;
|
||||
indio_dev->info = &acpi_als_info;
|
||||
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->channels = acpi_als_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
|
||||
|
||||
buffer = devm_iio_kfifo_allocate(&device->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
return devm_iio_device_register(&device->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id acpi_als_device_ids[] = {
|
||||
{"ACPI0008", 0},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids);
|
||||
|
||||
static struct acpi_driver acpi_als_driver = {
|
||||
.name = "acpi_als",
|
||||
.class = ACPI_ALS_CLASS,
|
||||
.ids = acpi_als_device_ids,
|
||||
.ops = {
|
||||
.add = acpi_als_add,
|
||||
.notify = acpi_als_notify,
|
||||
},
|
||||
};
|
||||
|
||||
module_acpi_driver(acpi_als_driver);
|
||||
|
||||
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
|
||||
MODULE_AUTHOR("Martin Liska <marxin.liska@gmail.com>");
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_DESCRIPTION("ACPI Ambient Light Sensor Driver");
|
||||
MODULE_LICENSE("GPL");
|
334
drivers/iio/light/bh1750.c
Normal file
334
drivers/iio/light/bh1750.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Data sheets:
|
||||
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf
|
||||
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf
|
||||
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1721fvc-e.pdf
|
||||
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1750fvi-e.pdf
|
||||
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1751fvi-e.pdf
|
||||
*
|
||||
* 7-bit I2C slave addresses:
|
||||
* 0x23 (ADDR pin low)
|
||||
* 0x5C (ADDR pin high)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define BH1750_POWER_DOWN 0x00
|
||||
#define BH1750_ONE_TIME_H_RES_MODE 0x20 /* auto-mode for BH1721 */
|
||||
#define BH1750_CHANGE_INT_TIME_H_BIT 0x40
|
||||
#define BH1750_CHANGE_INT_TIME_L_BIT 0x60
|
||||
|
||||
enum {
|
||||
BH1710,
|
||||
BH1721,
|
||||
BH1750,
|
||||
};
|
||||
|
||||
struct bh1750_chip_info;
|
||||
struct bh1750_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
const struct bh1750_chip_info *chip_info;
|
||||
u16 mtreg;
|
||||
};
|
||||
|
||||
struct bh1750_chip_info {
|
||||
u16 mtreg_min;
|
||||
u16 mtreg_max;
|
||||
u16 mtreg_default;
|
||||
int mtreg_to_usec;
|
||||
int mtreg_to_scale;
|
||||
|
||||
/*
|
||||
* For BH1710/BH1721 all possible integration time values won't fit
|
||||
* into one page so displaying is limited to every second one.
|
||||
* Note, that user can still write proper values which were not
|
||||
* listed.
|
||||
*/
|
||||
int inc;
|
||||
|
||||
u16 int_time_low_mask;
|
||||
u16 int_time_high_mask;
|
||||
}
|
||||
|
||||
static const bh1750_chip_info_tbl[] = {
|
||||
[BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 },
|
||||
[BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 },
|
||||
[BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 0x00E0 },
|
||||
};
|
||||
|
||||
static int bh1750_change_int_time(struct bh1750_data *data, int usec)
|
||||
{
|
||||
int ret;
|
||||
u16 val;
|
||||
u8 regval;
|
||||
const struct bh1750_chip_info *chip_info = data->chip_info;
|
||||
|
||||
if ((usec % chip_info->mtreg_to_usec) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
val = usec / chip_info->mtreg_to_usec;
|
||||
if (val < chip_info->mtreg_min || val > chip_info->mtreg_max)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regval = (val & chip_info->int_time_high_mask) >> 5;
|
||||
ret = i2c_smbus_write_byte(data->client,
|
||||
BH1750_CHANGE_INT_TIME_H_BIT | regval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regval = val & chip_info->int_time_low_mask;
|
||||
ret = i2c_smbus_write_byte(data->client,
|
||||
BH1750_CHANGE_INT_TIME_L_BIT | regval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->mtreg = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bh1750_read(struct bh1750_data *data, int *val)
|
||||
{
|
||||
int ret;
|
||||
__be16 result;
|
||||
const struct bh1750_chip_info *chip_info = data->chip_info;
|
||||
unsigned long delay = chip_info->mtreg_to_usec * data->mtreg;
|
||||
|
||||
/*
|
||||
* BH1721 will enter continuous mode on receiving this command.
|
||||
* Note, that this eliminates need for bh1750_resume().
|
||||
*/
|
||||
ret = i2c_smbus_write_byte(data->client, BH1750_ONE_TIME_H_RES_MODE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(delay + 15000, delay + 40000);
|
||||
|
||||
ret = i2c_master_recv(data->client, (char *)&result, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = be16_to_cpu(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bh1750_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret, tmp;
|
||||
struct bh1750_data *data = iio_priv(indio_dev);
|
||||
const struct bh1750_chip_info *chip_info = data->chip_info;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
mutex_lock(&data->lock);
|
||||
ret = bh1750_read(data, val);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
tmp = chip_info->mtreg_to_scale / data->mtreg;
|
||||
*val = tmp / 1000000;
|
||||
*val2 = tmp % 1000000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = 0;
|
||||
*val2 = chip_info->mtreg_to_usec * data->mtreg;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bh1750_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct bh1750_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = bh1750_change_int_time(data, val2);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t bh1750_show_int_time_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i;
|
||||
size_t len = 0;
|
||||
struct bh1750_data *data = iio_priv(dev_to_iio_dev(dev));
|
||||
const struct bh1750_chip_info *chip_info = data->chip_info;
|
||||
|
||||
for (i = chip_info->mtreg_min; i <= chip_info->mtreg_max; i += chip_info->inc)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06d ",
|
||||
chip_info->mtreg_to_usec * i);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_INT_TIME_AVAIL(bh1750_show_int_time_available);
|
||||
|
||||
static struct attribute *bh1750_attributes[] = {
|
||||
&iio_dev_attr_integration_time_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group bh1750_attribute_group = {
|
||||
.attrs = bh1750_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info bh1750_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &bh1750_attribute_group,
|
||||
.read_raw = bh1750_read_raw,
|
||||
.write_raw = bh1750_write_raw,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec bh1750_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME)
|
||||
}
|
||||
};
|
||||
|
||||
static int bh1750_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret, usec;
|
||||
struct bh1750_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->chip_info = &bh1750_chip_info_tbl[id->driver_data];
|
||||
|
||||
usec = data->chip_info->mtreg_to_usec * data->chip_info->mtreg_default;
|
||||
ret = bh1750_change_int_time(data, usec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &bh1750_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = bh1750_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bh1750_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int bh1750_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct bh1750_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
i2c_smbus_write_byte(client, BH1750_POWER_DOWN);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bh1750_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct bh1750_data *data =
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
/*
|
||||
* This is mainly for BH1721 which doesn't enter power down
|
||||
* mode automatically.
|
||||
*/
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL);
|
||||
#define BH1750_PM_OPS (&bh1750_pm_ops)
|
||||
#else
|
||||
#define BH1750_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id bh1750_id[] = {
|
||||
{ "bh1710", BH1710 },
|
||||
{ "bh1715", BH1750 },
|
||||
{ "bh1721", BH1721 },
|
||||
{ "bh1750", BH1750 },
|
||||
{ "bh1751", BH1750 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bh1750_id);
|
||||
|
||||
static struct i2c_driver bh1750_driver = {
|
||||
.driver = {
|
||||
.name = "bh1750",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = BH1750_PM_OPS,
|
||||
},
|
||||
.probe = bh1750_probe,
|
||||
.remove = bh1750_remove,
|
||||
.id_table = bh1750_id,
|
||||
|
||||
};
|
||||
module_i2c_driver(bh1750_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
|
||||
MODULE_DESCRIPTION("ROHM BH1710/BH1715/BH1721/BH1750/BH1751 als driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -263,7 +263,6 @@ static int hid_als_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct als_state *als_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct als_state));
|
||||
if (!indio_dev)
|
||||
@ -281,20 +280,21 @@ static int hid_als_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(als_channels, sizeof(als_channels), GFP_KERNEL);
|
||||
if (!channels) {
|
||||
indio_dev->channels = kmemdup(als_channels,
|
||||
sizeof(als_channels), GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = als_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_ALS, als_state);
|
||||
ret = als_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_ALS, als_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(als_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
@ -361,7 +361,7 @@ static int hid_als_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_als_ids[] = {
|
||||
static const struct platform_device_id hid_als_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200041",
|
||||
|
@ -350,7 +350,7 @@ static int hid_prox_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_prox_ids[] = {
|
||||
static const struct platform_device_id hid_prox_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200011",
|
||||
|
@ -66,6 +66,9 @@
|
||||
|
||||
#define LTR501_REGMAP_NAME "ltr501_regmap"
|
||||
|
||||
#define LTR501_LUX_CONV(vis_coeff, vis_data, ir_coeff, ir_data) \
|
||||
((vis_coeff * vis_data) - (ir_coeff * ir_data))
|
||||
|
||||
static const int int_time_mapping[] = {100000, 50000, 200000, 400000};
|
||||
|
||||
static const struct reg_field reg_field_it =
|
||||
@ -298,6 +301,29 @@ static int ltr501_ps_read_samp_period(struct ltr501_data *data, int *val)
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
/* IR and visible spectrum coeff's are given in data sheet */
|
||||
static unsigned long ltr501_calculate_lux(u16 vis_data, u16 ir_data)
|
||||
{
|
||||
unsigned long ratio, lux;
|
||||
|
||||
if (vis_data == 0)
|
||||
return 0;
|
||||
|
||||
/* multiply numerator by 100 to avoid handling ratio < 1 */
|
||||
ratio = DIV_ROUND_UP(ir_data * 100, ir_data + vis_data);
|
||||
|
||||
if (ratio < 45)
|
||||
lux = LTR501_LUX_CONV(1774, vis_data, -1105, ir_data);
|
||||
else if (ratio >= 45 && ratio < 64)
|
||||
lux = LTR501_LUX_CONV(3772, vis_data, 1336, ir_data);
|
||||
else if (ratio >= 64 && ratio < 85)
|
||||
lux = LTR501_LUX_CONV(1690, vis_data, 169, ir_data);
|
||||
else
|
||||
lux = 0;
|
||||
|
||||
return lux / 1000;
|
||||
}
|
||||
|
||||
static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
|
||||
{
|
||||
int tries = 100;
|
||||
@ -548,7 +574,14 @@ static const struct iio_event_spec ltr501_pxs_event_spec[] = {
|
||||
.num_event_specs = _evsize,\
|
||||
}
|
||||
|
||||
#define LTR501_LIGHT_CHANNEL() { \
|
||||
.type = IIO_LIGHT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
.scan_index = -1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltr501_channels[] = {
|
||||
LTR501_LIGHT_CHANNEL(),
|
||||
LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0,
|
||||
ltr501_als_event_spec,
|
||||
ARRAY_SIZE(ltr501_als_event_spec)),
|
||||
@ -576,6 +609,7 @@ static const struct iio_chan_spec ltr501_channels[] = {
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ltr301_channels[] = {
|
||||
LTR501_LIGHT_CHANNEL(),
|
||||
LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0,
|
||||
ltr501_als_event_spec,
|
||||
ARRAY_SIZE(ltr501_als_event_spec)),
|
||||
@ -596,6 +630,23 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
|
||||
int ret, i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
mutex_lock(&data->lock_als);
|
||||
ret = ltr501_read_als(data, buf);
|
||||
mutex_unlock(&data->lock_als);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
|
||||
le16_to_cpu(buf[0]));
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
@ -865,9 +916,9 @@ static int ltr501_write_thresh(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_PROXIMITY:
|
||||
switch (dir) {
|
||||
if (val > LTR501_PS_THRESH_MASK)
|
||||
return -EINVAL;
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
mutex_lock(&data->lock_ps);
|
||||
ret = regmap_bulk_write(data->regmap,
|
||||
|
722
drivers/iio/light/stk3310.c
Normal file
722
drivers/iio/light/stk3310.c
Normal file
@ -0,0 +1,722 @@
|
||||
/**
|
||||
* Sensortek STK3310/STK3311 Ambient Light and Proximity Sensor
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define STK3310_REG_STATE 0x00
|
||||
#define STK3310_REG_PSCTRL 0x01
|
||||
#define STK3310_REG_ALSCTRL 0x02
|
||||
#define STK3310_REG_INT 0x04
|
||||
#define STK3310_REG_THDH_PS 0x06
|
||||
#define STK3310_REG_THDL_PS 0x08
|
||||
#define STK3310_REG_FLAG 0x10
|
||||
#define STK3310_REG_PS_DATA_MSB 0x11
|
||||
#define STK3310_REG_PS_DATA_LSB 0x12
|
||||
#define STK3310_REG_ALS_DATA_MSB 0x13
|
||||
#define STK3310_REG_ALS_DATA_LSB 0x14
|
||||
#define STK3310_REG_ID 0x3E
|
||||
#define STK3310_MAX_REG 0x80
|
||||
|
||||
#define STK3310_STATE_EN_PS 0x01
|
||||
#define STK3310_STATE_EN_ALS 0x02
|
||||
#define STK3310_STATE_STANDBY 0x00
|
||||
|
||||
#define STK3310_CHIP_ID_VAL 0x13
|
||||
#define STK3311_CHIP_ID_VAL 0x1D
|
||||
#define STK3310_PSINT_EN 0x01
|
||||
#define STK3310_PS_MAX_VAL 0xFFFF
|
||||
#define STK3310_THRESH_MAX 0xFFFF
|
||||
|
||||
#define STK3310_DRIVER_NAME "stk3310"
|
||||
#define STK3310_REGMAP_NAME "stk3310_regmap"
|
||||
#define STK3310_EVENT "stk3310_event"
|
||||
#define STK3310_GPIO "stk3310_gpio"
|
||||
|
||||
#define STK3310_SCALE_AVAILABLE "6.4 1.6 0.4 0.1"
|
||||
|
||||
#define STK3310_IT_AVAILABLE \
|
||||
"0.000185 0.000370 0.000741 0.001480 0.002960 0.005920 0.011840 " \
|
||||
"0.023680 0.047360 0.094720 0.189440 0.378880 0.757760 1.515520 " \
|
||||
"3.031040 6.062080"
|
||||
|
||||
#define STK3310_REGFIELD(name) \
|
||||
do { \
|
||||
data->reg_##name = \
|
||||
devm_regmap_field_alloc(&client->dev, regmap, \
|
||||
stk3310_reg_field_##name); \
|
||||
if (IS_ERR(data->reg_##name)) { \
|
||||
dev_err(&client->dev, "reg field alloc failed.\n"); \
|
||||
return PTR_ERR(data->reg_##name); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static const struct reg_field stk3310_reg_field_state =
|
||||
REG_FIELD(STK3310_REG_STATE, 0, 2);
|
||||
static const struct reg_field stk3310_reg_field_als_gain =
|
||||
REG_FIELD(STK3310_REG_ALSCTRL, 4, 5);
|
||||
static const struct reg_field stk3310_reg_field_ps_gain =
|
||||
REG_FIELD(STK3310_REG_PSCTRL, 4, 5);
|
||||
static const struct reg_field stk3310_reg_field_als_it =
|
||||
REG_FIELD(STK3310_REG_ALSCTRL, 0, 3);
|
||||
static const struct reg_field stk3310_reg_field_ps_it =
|
||||
REG_FIELD(STK3310_REG_PSCTRL, 0, 3);
|
||||
static const struct reg_field stk3310_reg_field_int_ps =
|
||||
REG_FIELD(STK3310_REG_INT, 0, 2);
|
||||
static const struct reg_field stk3310_reg_field_flag_psint =
|
||||
REG_FIELD(STK3310_REG_FLAG, 4, 4);
|
||||
static const struct reg_field stk3310_reg_field_flag_nf =
|
||||
REG_FIELD(STK3310_REG_FLAG, 0, 0);
|
||||
/*
|
||||
* Maximum PS values with regard to scale. Used to export the 'inverse'
|
||||
* PS value (high values for far objects, low values for near objects).
|
||||
*/
|
||||
static const int stk3310_ps_max[4] = {
|
||||
STK3310_PS_MAX_VAL / 64,
|
||||
STK3310_PS_MAX_VAL / 16,
|
||||
STK3310_PS_MAX_VAL / 4,
|
||||
STK3310_PS_MAX_VAL,
|
||||
};
|
||||
|
||||
static const int stk3310_scale_table[][2] = {
|
||||
{6, 400000}, {1, 600000}, {0, 400000}, {0, 100000}
|
||||
};
|
||||
|
||||
/* Integration time in seconds, microseconds */
|
||||
static const int stk3310_it_table[][2] = {
|
||||
{0, 185}, {0, 370}, {0, 741}, {0, 1480},
|
||||
{0, 2960}, {0, 5920}, {0, 11840}, {0, 23680},
|
||||
{0, 47360}, {0, 94720}, {0, 189440}, {0, 378880},
|
||||
{0, 757760}, {1, 515520}, {3, 31040}, {6, 62080},
|
||||
};
|
||||
|
||||
struct stk3310_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
bool als_enabled;
|
||||
bool ps_enabled;
|
||||
u64 timestamp;
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *reg_state;
|
||||
struct regmap_field *reg_als_gain;
|
||||
struct regmap_field *reg_ps_gain;
|
||||
struct regmap_field *reg_als_it;
|
||||
struct regmap_field *reg_ps_it;
|
||||
struct regmap_field *reg_int_ps;
|
||||
struct regmap_field *reg_flag_psint;
|
||||
struct regmap_field *reg_flag_nf;
|
||||
};
|
||||
|
||||
static const struct iio_event_spec stk3310_events[] = {
|
||||
/* Proximity event */
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
/* Out-of-proximity event */
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec stk3310_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
},
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.event_spec = stk3310_events,
|
||||
.num_event_specs = ARRAY_SIZE(stk3310_events),
|
||||
}
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_illuminance_scale_available, STK3310_SCALE_AVAILABLE);
|
||||
|
||||
static IIO_CONST_ATTR(in_proximity_scale_available, STK3310_SCALE_AVAILABLE);
|
||||
|
||||
static IIO_CONST_ATTR(in_illuminance_integration_time_available,
|
||||
STK3310_IT_AVAILABLE);
|
||||
|
||||
static IIO_CONST_ATTR(in_proximity_integration_time_available,
|
||||
STK3310_IT_AVAILABLE);
|
||||
|
||||
static struct attribute *stk3310_attributes[] = {
|
||||
&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_in_proximity_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
|
||||
&iio_const_attr_in_proximity_integration_time_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group stk3310_attribute_group = {
|
||||
.attrs = stk3310_attributes
|
||||
};
|
||||
|
||||
static int stk3310_get_index(const int table[][2], int table_size,
|
||||
int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < table_size; i++) {
|
||||
if (val == table[i][0] && val2 == table[i][1])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stk3310_read_event(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)
|
||||
{
|
||||
u8 reg;
|
||||
u16 buf;
|
||||
int ret;
|
||||
unsigned int index;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (info != IIO_EV_INFO_VALUE)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Only proximity interrupts are implemented at the moment.
|
||||
* Since we're inverting proximity values, the sensor's 'high'
|
||||
* threshold will become our 'low' threshold, associated with
|
||||
* 'near' events. Similarly, the sensor's 'low' threshold will
|
||||
* be our 'high' threshold, associated with 'far' events.
|
||||
*/
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
reg = STK3310_REG_THDL_PS;
|
||||
else if (dir == IIO_EV_DIR_FALLING)
|
||||
reg = STK3310_REG_THDH_PS;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "register read failed\n");
|
||||
return ret;
|
||||
}
|
||||
regmap_field_read(data->reg_ps_gain, &index);
|
||||
*val = swab16(stk3310_ps_max[index] - buf);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int stk3310_write_event(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)
|
||||
{
|
||||
u8 reg;
|
||||
u16 buf;
|
||||
int ret;
|
||||
unsigned int index;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
regmap_field_read(data->reg_ps_gain, &index);
|
||||
if (val > stk3310_ps_max[index])
|
||||
return -EINVAL;
|
||||
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
reg = STK3310_REG_THDL_PS;
|
||||
else if (dir == IIO_EV_DIR_FALLING)
|
||||
reg = STK3310_REG_THDH_PS;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
buf = swab16(stk3310_ps_max[index] - val);
|
||||
ret = regmap_bulk_write(data->regmap, reg, &buf, 2);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "failed to set PS threshold!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk3310_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
unsigned int event_val;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
|
||||
regmap_field_read(data->reg_int_ps, &event_val);
|
||||
|
||||
return event_val;
|
||||
}
|
||||
|
||||
static int stk3310_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
int ret;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
if (state < 0 || state > 7)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set INT_PS value */
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_field_write(data->reg_int_ps, state);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "failed to set interrupt mode\n");
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk3310_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
u8 reg;
|
||||
u16 buf;
|
||||
int ret;
|
||||
unsigned int index;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_LIGHT)
|
||||
reg = STK3310_REG_ALS_DATA_MSB;
|
||||
else if (chan->type == IIO_PROXIMITY)
|
||||
reg = STK3310_REG_PS_DATA_MSB;
|
||||
else
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "register read failed\n");
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
*val = swab16(buf);
|
||||
if (chan->type == IIO_PROXIMITY) {
|
||||
/*
|
||||
* Invert the proximity data so we return low values
|
||||
* for close objects and high values for far ones.
|
||||
*/
|
||||
regmap_field_read(data->reg_ps_gain, &index);
|
||||
*val = stk3310_ps_max[index] - *val;
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (chan->type == IIO_LIGHT)
|
||||
regmap_field_read(data->reg_als_it, &index);
|
||||
else
|
||||
regmap_field_read(data->reg_ps_it, &index);
|
||||
*val = stk3310_it_table[index][0];
|
||||
*val2 = stk3310_it_table[index][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_LIGHT)
|
||||
regmap_field_read(data->reg_als_gain, &index);
|
||||
else
|
||||
regmap_field_read(data->reg_ps_gain, &index);
|
||||
*val = stk3310_scale_table[index][0];
|
||||
*val2 = stk3310_scale_table[index][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stk3310_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
unsigned int index;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
index = stk3310_get_index(stk3310_it_table,
|
||||
ARRAY_SIZE(stk3310_it_table),
|
||||
val, val2);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->lock);
|
||||
if (chan->type == IIO_LIGHT)
|
||||
ret = regmap_field_write(data->reg_als_it, index);
|
||||
else
|
||||
ret = regmap_field_write(data->reg_ps_it, index);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev,
|
||||
"sensor configuration failed\n");
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
index = stk3310_get_index(stk3310_scale_table,
|
||||
ARRAY_SIZE(stk3310_scale_table),
|
||||
val, val2);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->lock);
|
||||
if (chan->type == IIO_LIGHT)
|
||||
ret = regmap_field_write(data->reg_als_gain, index);
|
||||
else
|
||||
ret = regmap_field_write(data->reg_ps_gain, index);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev,
|
||||
"sensor configuration failed\n");
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info stk3310_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = stk3310_read_raw,
|
||||
.write_raw = stk3310_write_raw,
|
||||
.attrs = &stk3310_attribute_group,
|
||||
.read_event_value = stk3310_read_event,
|
||||
.write_event_value = stk3310_write_event,
|
||||
.read_event_config = stk3310_read_event_config,
|
||||
.write_event_config = stk3310_write_event_config,
|
||||
};
|
||||
|
||||
static int stk3310_set_state(struct stk3310_data *data, u8 state)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
/* 3-bit state; 0b100 is not supported. */
|
||||
if (state > 7 || state == 4)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_field_write(data->reg_state, state);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to change sensor state\n");
|
||||
} else if (state != STK3310_STATE_STANDBY) {
|
||||
/* Don't reset the 'enabled' flags if we're going in standby */
|
||||
data->ps_enabled = !!(state & 0x01);
|
||||
data->als_enabled = !!(state & 0x02);
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk3310_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
int chipid;
|
||||
u8 state;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
regmap_read(data->regmap, STK3310_REG_ID, &chipid);
|
||||
if (chipid != STK3310_CHIP_ID_VAL &&
|
||||
chipid != STK3311_CHIP_ID_VAL) {
|
||||
dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
state = STK3310_STATE_EN_ALS | STK3310_STATE_EN_PS;
|
||||
ret = stk3310_set_state(data, state);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to enable sensor");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable PS interrupts */
|
||||
ret = regmap_field_write(data->reg_int_ps, STK3310_PSINT_EN);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "failed to enable interrupts!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk3310_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, STK3310_GPIO, 0);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case STK3310_REG_ALS_DATA_MSB:
|
||||
case STK3310_REG_ALS_DATA_LSB:
|
||||
case STK3310_REG_PS_DATA_LSB:
|
||||
case STK3310_REG_PS_DATA_MSB:
|
||||
case STK3310_REG_FLAG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config stk3310_regmap_config = {
|
||||
.name = STK3310_REGMAP_NAME,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = STK3310_MAX_REG,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = stk3310_is_volatile_reg,
|
||||
};
|
||||
|
||||
static int stk3310_regmap_init(struct stk3310_data *data)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *client;
|
||||
|
||||
client = data->client;
|
||||
regmap = devm_regmap_init_i2c(client, &stk3310_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "regmap initialization failed.\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
data->regmap = regmap;
|
||||
|
||||
STK3310_REGFIELD(state);
|
||||
STK3310_REGFIELD(als_gain);
|
||||
STK3310_REGFIELD(ps_gain);
|
||||
STK3310_REGFIELD(als_it);
|
||||
STK3310_REGFIELD(ps_it);
|
||||
STK3310_REGFIELD(int_ps);
|
||||
STK3310_REGFIELD(flag_psint);
|
||||
STK3310_REGFIELD(flag_nf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t stk3310_irq_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->timestamp = iio_get_time_ns();
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t stk3310_irq_event_handler(int irq, void *private)
|
||||
{
|
||||
int ret;
|
||||
unsigned int dir;
|
||||
u64 event;
|
||||
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
|
||||
/* Read FLAG_NF to figure out what threshold has been met. */
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_field_read(data->reg_flag_nf, &dir);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "register read failed\n");
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
event = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
(dir ? IIO_EV_DIR_RISING :
|
||||
IIO_EV_DIR_FALLING));
|
||||
iio_push_event(indio_dev, event, data->timestamp);
|
||||
|
||||
/* Reset the interrupt flag */
|
||||
ret = regmap_field_write(data->reg_flag_psint, 0);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev, "failed to reset interrupts\n");
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int stk3310_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct stk3310_data *data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
ret = stk3310_regmap_init(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &stk3310_info;
|
||||
indio_dev->name = STK3310_DRIVER_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = stk3310_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(stk3310_channels);
|
||||
|
||||
ret = stk3310_init(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
stk3310_set_state(data, STK3310_STATE_STANDBY);
|
||||
}
|
||||
|
||||
if (client->irq <= 0)
|
||||
client->irq = stk3310_gpio_probe(client);
|
||||
|
||||
if (client->irq >= 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
stk3310_irq_handler,
|
||||
stk3310_irq_event_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
STK3310_EVENT, indio_dev);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "request irq %d failed\n",
|
||||
client->irq);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk3310_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
return stk3310_set_state(iio_priv(indio_dev), STK3310_STATE_STANDBY);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int stk3310_suspend(struct device *dev)
|
||||
{
|
||||
struct stk3310_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return stk3310_set_state(data, STK3310_STATE_STANDBY);
|
||||
}
|
||||
|
||||
static int stk3310_resume(struct device *dev)
|
||||
{
|
||||
int state = 0;
|
||||
struct stk3310_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
if (data->ps_enabled)
|
||||
state |= STK3310_STATE_EN_PS;
|
||||
if (data->als_enabled)
|
||||
state |= STK3310_STATE_EN_ALS;
|
||||
|
||||
return stk3310_set_state(data, state);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume);
|
||||
|
||||
#define STK3310_PM_OPS (&stk3310_pm_ops)
|
||||
#else
|
||||
#define STK3310_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id stk3310_i2c_id[] = {
|
||||
{"STK3310", 0},
|
||||
{"STK3311", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct acpi_device_id stk3310_acpi_id[] = {
|
||||
{"STK3310", 0},
|
||||
{"STK3311", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id);
|
||||
|
||||
static struct i2c_driver stk3310_driver = {
|
||||
.driver = {
|
||||
.name = "stk3310",
|
||||
.pm = STK3310_PM_OPS,
|
||||
.acpi_match_table = ACPI_PTR(stk3310_acpi_id),
|
||||
},
|
||||
.probe = stk3310_probe,
|
||||
.remove = stk3310_remove,
|
||||
.id_table = stk3310_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(stk3310_driver);
|
||||
|
||||
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
|
||||
MODULE_DESCRIPTION("STK3310 Ambient Light and Proximity Sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -8,7 +8,7 @@ menu "Magnetometer sensors"
|
||||
config AK8975
|
||||
tristate "Asahi Kasei AK 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Asahi Kasei AK8975, AK8963,
|
||||
AK09911 or AK09912 3-Axis Magnetometer.
|
||||
@ -19,7 +19,7 @@ config AK8975
|
||||
config AK09911
|
||||
tristate "Asahi Kasei AK09911 3-axis Compass"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select AK8975
|
||||
help
|
||||
Deprecated: AK09911 is now supported by AK8975 driver.
|
||||
@ -47,6 +47,17 @@ config HID_SENSOR_MAGNETOMETER_3D
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Magnetometer 3D.
|
||||
|
||||
config MMC35240
|
||||
tristate "MEMSIC MMC35240 3-axis magnetic sensor"
|
||||
select REGMAP_I2C
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the MEMSIC MMC35240 3-axis
|
||||
magnetic sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mmc35240.
|
||||
|
||||
config IIO_ST_MAGN_3AXIS
|
||||
tristate "STMicroelectronics magnetometers 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
@ -76,4 +87,18 @@ config IIO_ST_MAGN_SPI_3AXIS
|
||||
depends on IIO_ST_MAGN_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
config BMC150_MAGN
|
||||
tristate "Bosch BMC150 Magnetometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the BMC150 magnetometer.
|
||||
|
||||
Currently this only supports the device via an i2c interface.
|
||||
|
||||
This is a combo module with both accelerometer and magnetometer.
|
||||
This driver is only implementing magnetometer part, which has
|
||||
its own address and register map.
|
||||
|
||||
endmenu
|
||||
|
@ -6,6 +6,7 @@
|
||||
obj-$(CONFIG_AK8975) += ak8975.o
|
||||
obj-$(CONFIG_MAG3110) += mag3110.o
|
||||
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
|
||||
obj-$(CONFIG_MMC35240) += mmc35240.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
|
||||
st_magn-y := st_magn_core.o
|
||||
@ -13,3 +14,5 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
|
||||
|
||||
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
|
||||
|
1109
drivers/iio/magnetometer/bmc150_magn.c
Normal file
1109
drivers/iio/magnetometer/bmc150_magn.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -510,7 +510,7 @@ static int hid_magn_3d_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_magn_3d_ids[] = {
|
||||
static const struct platform_device_id hid_magn_3d_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200083",
|
||||
|
512
drivers/iio/magnetometer/mmc35240.c
Normal file
512
drivers/iio/magnetometer/mmc35240.c
Normal file
@ -0,0 +1,512 @@
|
||||
/*
|
||||
* MMC35240 - MEMSIC 3-axis Magnetic Sensor
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for MMC35240 (7-bit I2C slave address 0x30).
|
||||
*
|
||||
* TODO: offset, ACPI, continuous measurement mode, PM
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define MMC35240_DRV_NAME "mmc35240"
|
||||
#define MMC35240_REGMAP_NAME "mmc35240_regmap"
|
||||
|
||||
#define MMC35240_REG_XOUT_L 0x00
|
||||
#define MMC35240_REG_XOUT_H 0x01
|
||||
#define MMC35240_REG_YOUT_L 0x02
|
||||
#define MMC35240_REG_YOUT_H 0x03
|
||||
#define MMC35240_REG_ZOUT_L 0x04
|
||||
#define MMC35240_REG_ZOUT_H 0x05
|
||||
|
||||
#define MMC35240_REG_STATUS 0x06
|
||||
#define MMC35240_REG_CTRL0 0x07
|
||||
#define MMC35240_REG_CTRL1 0x08
|
||||
|
||||
#define MMC35240_REG_ID 0x20
|
||||
|
||||
#define MMC35240_STATUS_MEAS_DONE_BIT BIT(0)
|
||||
|
||||
#define MMC35240_CTRL0_REFILL_BIT BIT(7)
|
||||
#define MMC35240_CTRL0_RESET_BIT BIT(6)
|
||||
#define MMC35240_CTRL0_SET_BIT BIT(5)
|
||||
#define MMC35240_CTRL0_CMM_BIT BIT(1)
|
||||
#define MMC35240_CTRL0_TM_BIT BIT(0)
|
||||
|
||||
/* output resolution bits */
|
||||
#define MMC35240_CTRL1_BW0_BIT BIT(0)
|
||||
#define MMC35240_CTRL1_BW1_BIT BIT(1)
|
||||
|
||||
#define MMC35240_CTRL1_BW_MASK (MMC35240_CTRL1_BW0_BIT | \
|
||||
MMC35240_CTRL1_BW1_BIT)
|
||||
#define MMC35240_CTRL1_BW_SHIFT 0
|
||||
|
||||
#define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */
|
||||
#define MMC53240_WAIT_SET_RESET 1000 /* us */
|
||||
|
||||
enum mmc35240_resolution {
|
||||
MMC35240_16_BITS_SLOW = 0, /* 100 Hz */
|
||||
MMC35240_16_BITS_FAST, /* 200 Hz */
|
||||
MMC35240_14_BITS, /* 333 Hz */
|
||||
MMC35240_12_BITS, /* 666 Hz */
|
||||
};
|
||||
|
||||
enum mmc35240_axis {
|
||||
AXIS_X = 0,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int sens[3]; /* sensitivity per X, Y, Z axis */
|
||||
int nfo; /* null field output */
|
||||
} mmc35240_props_table[] = {
|
||||
/* 16 bits, 100Hz ODR */
|
||||
{
|
||||
{1024, 1024, 770},
|
||||
32768,
|
||||
},
|
||||
/* 16 bits, 200Hz ODR */
|
||||
{
|
||||
{1024, 1024, 770},
|
||||
32768,
|
||||
},
|
||||
/* 14 bits, 333Hz ODR */
|
||||
{
|
||||
{256, 256, 193},
|
||||
8192,
|
||||
},
|
||||
/* 12 bits, 666Hz ODR */
|
||||
{
|
||||
{64, 64, 48},
|
||||
2048,
|
||||
},
|
||||
};
|
||||
|
||||
struct mmc35240_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex mutex;
|
||||
struct regmap *regmap;
|
||||
enum mmc35240_resolution res;
|
||||
};
|
||||
|
||||
static const int mmc35240_samp_freq[] = {100, 200, 333, 666};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 333 666");
|
||||
|
||||
#define MMC35240_CHANNEL(_axis) { \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## _axis, \
|
||||
.address = AXIS_ ## _axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mmc35240_channels[] = {
|
||||
MMC35240_CHANNEL(X),
|
||||
MMC35240_CHANNEL(Y),
|
||||
MMC35240_CHANNEL(Z),
|
||||
};
|
||||
|
||||
static struct attribute *mmc35240_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
};
|
||||
|
||||
static const struct attribute_group mmc35240_attribute_group = {
|
||||
.attrs = mmc35240_attributes,
|
||||
};
|
||||
|
||||
static int mmc35240_get_samp_freq_index(struct mmc35240_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mmc35240_samp_freq); i++)
|
||||
if (mmc35240_samp_freq[i] == val)
|
||||
return i;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mmc35240_hw_set(struct mmc35240_data *data, bool set)
|
||||
{
|
||||
int ret;
|
||||
u8 coil_bit;
|
||||
|
||||
/*
|
||||
* Recharge the capacitor at VCAP pin, requested to be issued
|
||||
* before a SET/RESET command.
|
||||
*/
|
||||
ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
|
||||
MMC35240_CTRL0_REFILL_BIT,
|
||||
MMC35240_CTRL0_REFILL_BIT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
usleep_range(MMC35240_WAIT_CHARGE_PUMP, MMC35240_WAIT_CHARGE_PUMP + 1);
|
||||
|
||||
if (set)
|
||||
coil_bit = MMC35240_CTRL0_SET_BIT;
|
||||
else
|
||||
coil_bit = MMC35240_CTRL0_RESET_BIT;
|
||||
|
||||
return regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
|
||||
MMC35240_CTRL0_REFILL_BIT,
|
||||
coil_bit);
|
||||
}
|
||||
|
||||
static int mmc35240_init(struct mmc35240_data *data)
|
||||
{
|
||||
int ret;
|
||||
unsigned int reg_id;
|
||||
|
||||
ret = regmap_read(data->regmap, MMC35240_REG_ID, ®_id);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading product id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&data->client->dev, "MMC35240 chip id %x\n", reg_id);
|
||||
|
||||
/*
|
||||
* make sure we restore sensor characteristics, by doing
|
||||
* a RESET/SET sequence
|
||||
*/
|
||||
ret = mmc35240_hw_set(data, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
usleep_range(MMC53240_WAIT_SET_RESET, MMC53240_WAIT_SET_RESET + 1);
|
||||
|
||||
ret = mmc35240_hw_set(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set default sampling frequency */
|
||||
return regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
|
||||
MMC35240_CTRL1_BW_MASK,
|
||||
data->res << MMC35240_CTRL1_BW_SHIFT);
|
||||
}
|
||||
|
||||
static int mmc35240_take_measurement(struct mmc35240_data *data)
|
||||
{
|
||||
int ret, tries = 100;
|
||||
unsigned int reg_status;
|
||||
|
||||
ret = regmap_write(data->regmap, MMC35240_REG_CTRL0,
|
||||
MMC35240_CTRL0_TM_BIT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (tries-- > 0) {
|
||||
ret = regmap_read(data->regmap, MMC35240_REG_STATUS,
|
||||
®_status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT)
|
||||
break;
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev, "data not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3])
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mmc35240_take_measurement(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return regmap_bulk_read(data->regmap, MMC35240_REG_XOUT_L, (u8 *)buf,
|
||||
3 * sizeof(__le16));
|
||||
}
|
||||
|
||||
static int mmc35240_raw_to_gauss(struct mmc35240_data *data, int index,
|
||||
__le16 buf[],
|
||||
int *val, int *val2)
|
||||
{
|
||||
int raw_x, raw_y, raw_z;
|
||||
int sens_x, sens_y, sens_z;
|
||||
int nfo;
|
||||
|
||||
raw_x = le16_to_cpu(buf[AXIS_X]);
|
||||
raw_y = le16_to_cpu(buf[AXIS_Y]);
|
||||
raw_z = le16_to_cpu(buf[AXIS_Z]);
|
||||
|
||||
sens_x = mmc35240_props_table[data->res].sens[AXIS_X];
|
||||
sens_y = mmc35240_props_table[data->res].sens[AXIS_Y];
|
||||
sens_z = mmc35240_props_table[data->res].sens[AXIS_Z];
|
||||
|
||||
nfo = mmc35240_props_table[data->res].nfo;
|
||||
|
||||
switch (index) {
|
||||
case AXIS_X:
|
||||
*val = (raw_x - nfo) / sens_x;
|
||||
*val2 = ((raw_x - nfo) % sens_x) * 1000000;
|
||||
break;
|
||||
case AXIS_Y:
|
||||
*val = (raw_y - nfo) / sens_y - (raw_z - nfo) / sens_z;
|
||||
*val2 = (((raw_y - nfo) % sens_y - (raw_z - nfo) % sens_z))
|
||||
* 1000000;
|
||||
break;
|
||||
case AXIS_Z:
|
||||
*val = (raw_y - nfo) / sens_y + (raw_z - nfo) / sens_z;
|
||||
*val2 = (((raw_y - nfo) % sens_y + (raw_z - nfo) % sens_z))
|
||||
* 1000000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc35240_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mmc35240_data *data = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
unsigned int reg;
|
||||
__le16 buf[3];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mmc35240_read_measurement(data, buf);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mmc35240_raw_to_gauss(data, chan->address,
|
||||
buf, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = regmap_read(data->regmap, MMC35240_REG_CTRL1, ®);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT;
|
||||
if (i < 0 || i > ARRAY_SIZE(mmc35240_samp_freq))
|
||||
return -EINVAL;
|
||||
|
||||
*val = mmc35240_samp_freq[i];
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mmc35240_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mmc35240_data *data = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = mmc35240_get_samp_freq_index(data, val, val2);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->mutex);
|
||||
ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
|
||||
MMC35240_CTRL1_BW_MASK,
|
||||
i << MMC35240_CTRL1_BW_SHIFT);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info mmc35240_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = mmc35240_read_raw,
|
||||
.write_raw = mmc35240_write_raw,
|
||||
.attrs = &mmc35240_attribute_group,
|
||||
};
|
||||
|
||||
static bool mmc35240_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MMC35240_REG_CTRL0:
|
||||
case MMC35240_REG_CTRL1:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mmc35240_is_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MMC35240_REG_XOUT_L:
|
||||
case MMC35240_REG_XOUT_H:
|
||||
case MMC35240_REG_YOUT_L:
|
||||
case MMC35240_REG_YOUT_H:
|
||||
case MMC35240_REG_ZOUT_L:
|
||||
case MMC35240_REG_ZOUT_H:
|
||||
case MMC35240_REG_STATUS:
|
||||
case MMC35240_REG_ID:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mmc35240_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MMC35240_REG_CTRL0:
|
||||
case MMC35240_REG_CTRL1:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static struct reg_default mmc35240_reg_defaults[] = {
|
||||
{ MMC35240_REG_CTRL0, 0x00 },
|
||||
{ MMC35240_REG_CTRL1, 0x00 },
|
||||
};
|
||||
|
||||
static const struct regmap_config mmc35240_regmap_config = {
|
||||
.name = MMC35240_REGMAP_NAME,
|
||||
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = MMC35240_REG_ID,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
|
||||
.writeable_reg = mmc35240_is_writeable_reg,
|
||||
.readable_reg = mmc35240_is_readable_reg,
|
||||
.volatile_reg = mmc35240_is_volatile_reg,
|
||||
|
||||
.reg_defaults = mmc35240_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(mmc35240_reg_defaults),
|
||||
};
|
||||
|
||||
static int mmc35240_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mmc35240_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &mmc35240_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "regmap initialization failed\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
data->regmap = regmap;
|
||||
data->res = MMC35240_16_BITS_SLOW;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &mmc35240_info;
|
||||
indio_dev->name = MMC35240_DRV_NAME;
|
||||
indio_dev->channels = mmc35240_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mmc35240_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = mmc35240_init(data);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "mmc35240 chip init failed\n");
|
||||
return ret;
|
||||
}
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mmc35240_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mmc35240_data *data = iio_priv(indio_dev);
|
||||
|
||||
regcache_cache_only(data->regmap, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc35240_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mmc35240_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
regcache_mark_dirty(data->regmap);
|
||||
ret = regcache_sync_region(data->regmap, MMC35240_REG_CTRL0,
|
||||
MMC35240_REG_CTRL1);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to restore control registers\n");
|
||||
|
||||
regcache_cache_only(data->regmap, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mmc35240_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id mmc35240_acpi_match[] = {
|
||||
{"MMC35240", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match);
|
||||
|
||||
static const struct i2c_device_id mmc35240_id[] = {
|
||||
{"MMC35240", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mmc35240_id);
|
||||
|
||||
static struct i2c_driver mmc35240_driver = {
|
||||
.driver = {
|
||||
.name = MMC35240_DRV_NAME,
|
||||
.pm = &mmc35240_pm_ops,
|
||||
.acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
|
||||
},
|
||||
.probe = mmc35240_probe,
|
||||
.id_table = mmc35240_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mmc35240_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
|
||||
MODULE_DESCRIPTION("MEMSIC MMC35240 magnetic sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -315,7 +315,6 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct incl_3d_state *incl_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct incl_3d_state));
|
||||
@ -336,21 +335,22 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(incl_3d_channels, sizeof(incl_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
indio_dev->channels = kmemdup(incl_3d_channels,
|
||||
sizeof(incl_3d_channels), GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = incl_3d_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_INCLINOMETER_3D, incl_state);
|
||||
ret = incl_3d_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_INCLINOMETER_3D,
|
||||
incl_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(incl_3d_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &incl_3d_info;
|
||||
@ -417,7 +417,7 @@ static int hid_incl_3d_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_incl_3d_ids[] = {
|
||||
static const struct platform_device_id hid_incl_3d_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200086",
|
||||
|
@ -222,7 +222,6 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct dev_rot_state *rot_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct dev_rot_state));
|
||||
@ -243,21 +242,23 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
|
||||
sizeof(dev_rot_channels), GFP_KERNEL);
|
||||
if (!channels) {
|
||||
indio_dev->channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
|
||||
sizeof(dev_rot_channels),
|
||||
GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = dev_rot_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_DEVICE_ORIENTATION, rot_state);
|
||||
ret = dev_rot_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_DEVICE_ORIENTATION,
|
||||
rot_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &dev_rot_info;
|
||||
@ -321,7 +322,7 @@ static int hid_dev_rot_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_dev_rot_ids[] = {
|
||||
static const struct platform_device_id hid_dev_rot_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-20008a",
|
||||
|
@ -260,7 +260,6 @@ static int hid_press_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct press_state *press_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct press_state));
|
||||
@ -280,20 +279,21 @@ static int hid_press_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(press_channels, sizeof(press_channels), GFP_KERNEL);
|
||||
if (!channels) {
|
||||
indio_dev->channels = kmemdup(press_channels, sizeof(press_channels),
|
||||
GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = press_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_PRESSURE, press_state);
|
||||
ret = press_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_PRESSURE, press_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(press_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
@ -360,7 +360,7 @@ static int hid_press_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_press_ids[] = {
|
||||
static const struct platform_device_id hid_press_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200031",
|
||||
|
@ -254,9 +254,7 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
|
||||
mutex_unlock(&data->lock);
|
||||
mlx90614_power_put(data);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return 0;
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ config LIS3L02DQ
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
depends on !IIO_BUFFER || IIO_KFIFO_BUF
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here to build SPI support for the ST microelectronics
|
||||
accelerometer. The driver supplies direct access via sysfs files
|
||||
|
@ -5,7 +5,7 @@ menu "Analog to digital converters"
|
||||
|
||||
config AD7606
|
||||
tristate "Analog Devices AD7606 ADC driver"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
@ -39,7 +39,7 @@ config AD7606_IFACE_SPI
|
||||
config AD7780
|
||||
tristate "Analog Devices AD7780 and similar ADCs driver"
|
||||
depends on SPI
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7170, AD7171,
|
||||
@ -52,7 +52,7 @@ config AD7780
|
||||
config AD7816
|
||||
tristate "Analog Devices AD7816/7/8 temperature sensor and ADC driver"
|
||||
depends on SPI
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7816/7/8
|
||||
temperature sensors and ADC.
|
||||
|
@ -5,7 +5,7 @@ menu "Analog digital bi-direction converters"
|
||||
|
||||
config ADT7316
|
||||
tristate "Analog Devices ADT7316/7/8 ADT7516/7/9 temperature sensor, ADC and DAC driver"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADT7316, ADT7317, ADT7318
|
||||
and ADT7516, ADT7517, ADT7519 temperature sensors, ADC and DAC.
|
||||
|
@ -16,7 +16,7 @@ config AD2S90
|
||||
config AD2S1200
|
||||
tristate "Analog Devices ad2s1200/ad2s1205 driver"
|
||||
depends on SPI
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Analog Devices spi resolver
|
||||
to digital converters, ad2s1200 and ad2s1205, provides direct access
|
||||
@ -28,7 +28,7 @@ config AD2S1200
|
||||
config AD2S1210
|
||||
tristate "Analog Devices ad2s1210 driver"
|
||||
depends on SPI
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Analog Devices spi resolver
|
||||
to digital converters, ad2s1210, provides direct access via sysfs.
|
||||
|
@ -32,6 +32,7 @@ enum iio_chan_info_enum {
|
||||
IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW,
|
||||
IIO_CHAN_INFO_AVERAGE_RAW,
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY,
|
||||
IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY,
|
||||
IIO_CHAN_INFO_SAMP_FREQ,
|
||||
IIO_CHAN_INFO_FREQUENCY,
|
||||
IIO_CHAN_INFO_PHASE,
|
||||
@ -44,6 +45,7 @@ enum iio_chan_info_enum {
|
||||
IIO_CHAN_INFO_DEBOUNCE_COUNT,
|
||||
IIO_CHAN_INFO_DEBOUNCE_TIME,
|
||||
IIO_CHAN_INFO_CALIBEMISSIVITY,
|
||||
IIO_CHAN_INFO_OVERSAMPLING_RATIO,
|
||||
};
|
||||
|
||||
enum iio_shared_by {
|
||||
|
@ -17,6 +17,8 @@ enum iio_event_info {
|
||||
IIO_EV_INFO_VALUE,
|
||||
IIO_EV_INFO_HYSTERESIS,
|
||||
IIO_EV_INFO_PERIOD,
|
||||
IIO_EV_INFO_HIGH_PASS_FILTER_3DB,
|
||||
IIO_EV_INFO_LOW_PASS_FILTER_3DB,
|
||||
};
|
||||
|
||||
#define IIO_VAL_INT 1
|
||||
|
@ -70,6 +70,8 @@ enum iio_modifier {
|
||||
IIO_MOD_WALKING,
|
||||
IIO_MOD_STILL,
|
||||
IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
|
||||
IIO_MOD_I,
|
||||
IIO_MOD_Q,
|
||||
};
|
||||
|
||||
enum iio_event_type {
|
||||
|
@ -1,5 +1,5 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -g -D_GNU_SOURCE
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CFLAGS += -Wall -g -D_GNU_SOURCE
|
||||
|
||||
all: iio_event_monitor lsiio generic_buffer
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user