Staging driver patches for 4.2-rc1
Here's the big, really big, staging tree patches for 4.2-rc1. Loads of stuff in here, almost all just coding style fixes / churn, and a few new drivers as well, one of which I just disabled from the build a few minutes ago due to way too many build warnings. Other than the one "disable this driver" patch, all of these have been in linux-next for quite a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlWNpc0ACgkQMUfUDdst+ym8EgCg0pL1Qcf9Se3jAc96fLt+itpv Rd0AoI9uJcq8Qm7d+IXnz3ojLnN9xvN3 =xt0u -----END PGP SIGNATURE----- Merge tag 'staging-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver updates from Greg KH: "Here's the big, really big, staging tree patches for 4.2-rc1. Loads of stuff in here, almost all just coding style fixes / churn, and a few new drivers as well, one of which I just disabled from the build a few minutes ago due to way too many build warnings. Other than the one "disable this driver" patch, all of these have been in linux-next for quite a while with no reported issues" * tag 'staging-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1163 commits) staging: wilc1000: disable driver due to build warnings Staging: rts5208: fix CHANGE_LINK_STATE value Staging: sm750fb: ddk750_swi2c.c: Insert spaces before parenthesis Staging: sm750fb: ddk750_swi2c.c: Place braces on correct lines Staging: sm750fb: ddk750_swi2c.c: Insert spaces around operators Staging: sm750fb: ddk750_swi2c.c: Replace spaces with tabs Staging: sm750fb: ddk750_swi2c.h: Shorten lines to under 80 characters Staging: sm750fb: ddk750_swi2c.h: Replace spaces with tabs Staging: sm750fb: modedb.h: Shorten lines to under 80 characters Staging: sm750fb: modedb.h: Replace spaces with tabs staging: comedi: addi_apci_3120: rename 'this_board' variables staging: comedi: addi_apci_1516: rename 'this_board' variables staging: comedi: ni_atmio: cleanup ni_getboardtype() staging: comedi: vmk80xx: sanity check context used to get the boardinfo staging: comedi: vmk80xx: rename 'boardinfo' variables staging: comedi: dt3000: rename 'this_board' variables staging: comedi: adv_pci_dio: rename 'this_board' variables staging: comedi: cb_pcidas64: rename 'thisboard' variables staging: comedi: cb_pcidas: rename 'thisboard' variables staging: comedi: me4000: rename 'thisboard' variables ...
This commit is contained in:
commit
23908db413
767 changed files with 64663 additions and 50270 deletions
|
@ -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
|
||||
|
@ -1364,3 +1440,26 @@ Description:
|
|||
hwfifo_watermak_min but not equal to any of the values in this
|
||||
list, the driver will chose an appropriate value for the
|
||||
hardware fifo watermark level.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibemissivity
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_calibemissivity
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_object_calibemissivity
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_object_calibemissivity
|
||||
KernelVersion: 4.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
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.
|
||||
|
|
7
Documentation/ABI/testing/sysfs-bus-iio-vf610
Normal file
7
Documentation/ABI/testing/sysfs-bus-iio-vf610
Normal file
|
@ -0,0 +1,7 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/conversion_mode
|
||||
KernelVersion: 4.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies the hardware conversion mode used. The three
|
||||
available modes are "normal", "high-speed" and "low-power",
|
||||
where the last is the default mode.
|
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
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@ Required properties:
|
|||
- clock-names: Must contain "adc", matching entry in the clocks property.
|
||||
- vref-supply: The regulator supply ADC reference voltage.
|
||||
|
||||
Recommended properties:
|
||||
- fsl,adck-max-frequency: Maximum frequencies according to datasheets operating
|
||||
requirements. Three values are required, depending on conversion mode:
|
||||
- Frequency in normal mode (ADLPC=0, ADHSC=0)
|
||||
- Frequency in high-speed mode (ADLPC=0, ADHSC=1)
|
||||
- Frequency in low-power mode (ADLPC=1, ADHSC=0)
|
||||
|
||||
Example:
|
||||
adc0: adc@4003b000 {
|
||||
compatible = "fsl,vf610-adc";
|
||||
|
@ -18,5 +25,7 @@ adc0: adc@4003b000 {
|
|||
interrupts = <0 53 0x04>;
|
||||
clocks = <&clks VF610_CLK_ADC0>;
|
||||
clock-names = "adc";
|
||||
fsl,adck-max-frequency = <30000000>, <40000000>,
|
||||
<20000000>;
|
||||
vref-supply = <®_vcc_3v3_mcu>;
|
||||
};
|
||||
|
|
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>;
|
||||
};
|
|
@ -30,6 +30,7 @@ Accelerometers:
|
|||
- st,lsm330d-accel
|
||||
- st,lsm330dl-accel
|
||||
- st,lsm330dlc-accel
|
||||
- st,lis331dl-accel
|
||||
- st,lis331dlh-accel
|
||||
- st,lsm303dl-accel
|
||||
- st,lsm303dlm-accel
|
||||
|
@ -45,6 +46,7 @@ Gyroscopes:
|
|||
- st,lsm330-gyro
|
||||
|
||||
Magnetometers:
|
||||
- st,lsm303dlh-magn
|
||||
- st,lsm303dlhc-magn
|
||||
- st,lsm303dlm-magn
|
||||
- st,lis3mdl-magn
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
* Melexis MLX90614 contactless IR temperature sensor
|
||||
|
||||
http://melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90614-615.aspx
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "melexis,mlx90614"
|
||||
- reg: the I2C address of the sensor
|
||||
|
||||
Optional properties:
|
||||
|
||||
- wakeup-gpios: device tree identifier of the GPIO connected to the SDA line
|
||||
to hold low in order to wake up the device. In normal operation, the
|
||||
GPIO is set as input and will not interfere in I2C communication. There
|
||||
is no need for a GPIO driving the SCL line. If no GPIO is given, power
|
||||
management is disabled.
|
||||
|
||||
Example:
|
||||
|
||||
mlx90614@5a {
|
||||
compatible = "melexis,mlx90614";
|
||||
reg = <0x5a>;
|
||||
wakeup-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ lltc Linear Technology Corporation
|
|||
marvell Marvell Technology Group Ltd.
|
||||
maxim Maxim Integrated Products
|
||||
mediatek MediaTek Inc.
|
||||
melexis Melexis N.V.
|
||||
merrii Merrii Technology Co., Ltd.
|
||||
micrel Micrel Inc.
|
||||
microchip Microchip Technology Inc.
|
||||
|
|
|
@ -171,6 +171,12 @@ This functions by reading the offset, applying the mask. If the bits selected in
|
|||
the mask match with the values of the corresponding bits in the compare field,
|
||||
the value of swap is written the specified offset.
|
||||
|
||||
Parts of a VME window can be mapped into user space memory using the following
|
||||
function:
|
||||
|
||||
int vme_master_mmap(struct vme_resource *resource,
|
||||
struct vm_area_struct *vma)
|
||||
|
||||
|
||||
Slave windows
|
||||
=============
|
||||
|
|
|
@ -9696,6 +9696,15 @@ M: Forest Bond <forest@alittletooquiet.net>
|
|||
S: Odd Fixes
|
||||
F: drivers/staging/vt665?/
|
||||
|
||||
STAGING - WILC1000 WIFI DRIVER
|
||||
M: Johnny Kim <johnny.kim@atmel.com>
|
||||
M: Rachel Kim <rachel.kim@atmel.com>
|
||||
M: Dean Lee <dean.lee@atmel.com>
|
||||
M: Chris Park <chris.park@atmel.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/staging/wilc1000/
|
||||
|
||||
STAGING - XGI Z7,Z9,Z11 PCI DISPLAY DRIVER
|
||||
M: Arnaud Patard <arnaud.patard@rtp-net.org>
|
||||
S: Odd Fixes
|
||||
|
|
|
@ -98,7 +98,6 @@ obj-$(CONFIG_USB_GADGET) += usb/
|
|||
obj-$(CONFIG_SERIO) += input/serio/
|
||||
obj-$(CONFIG_GAMEPORT) += input/gameport/
|
||||
obj-$(CONFIG_INPUT) += input/
|
||||
obj-$(CONFIG_I2O) += message/
|
||||
obj-$(CONFIG_RTC_LIB) += rtc/
|
||||
obj-y += i2c/ media/
|
||||
obj-$(CONFIG_PPS) += pps/
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -875,15 +875,18 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
static int kxcjk1013_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (data->dready_trig != trig && data->motion_trig != trig)
|
||||
return -EINVAL;
|
||||
return kxcjk1013_set_power_state(data, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
static int kxcjk1013_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||
|
||||
return kxcjk1013_set_power_state(data, false);
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
|
||||
|
@ -935,6 +938,13 @@ static const struct iio_chan_spec kxcjk1013_channels[] = {
|
|||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = {
|
||||
.preenable = kxcjk1013_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.postdisable = kxcjk1013_buffer_postdisable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
static const struct iio_info kxcjk1013_info = {
|
||||
.attrs = &kxcjk1013_attrs_group,
|
||||
.read_raw = kxcjk1013_read_raw,
|
||||
|
@ -943,7 +953,6 @@ static const struct iio_info kxcjk1013_info = {
|
|||
.write_event_value = kxcjk1013_write_event,
|
||||
.write_event_config = kxcjk1013_write_event_config,
|
||||
.read_event_config = kxcjk1013_read_event_config,
|
||||
.validate_trigger = kxcjk1013_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -1147,8 +1156,10 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
|
|||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
if (strcmp(id->id, "SMO8500") == 0)
|
||||
*is_smo8500_device = true;
|
||||
|
||||
*chipset = (enum kx_chipset)id->driver_data;
|
||||
|
||||
return dev_name(dev);
|
||||
|
@ -1163,6 +1174,7 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
|
|||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->is_smo8500_device)
|
||||
return -ENOTSUPP;
|
||||
|
||||
|
@ -1276,16 +1288,15 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
data->motion_trig = NULL;
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
kxcjk1013_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,
|
||||
kxcjk1013_trigger_handler,
|
||||
&kxcjk1013_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);
|
||||
|
@ -1418,6 +1429,7 @@ static const struct dev_pm_ops kxcjk1013_pm_ops = {
|
|||
static const struct acpi_device_id kx_acpi_match[] = {
|
||||
{"KXCJ1013", KXCJK1013},
|
||||
{"KXCJ1008", KXCJ91008},
|
||||
{"KXCJ9000", KXCJ91008},
|
||||
{"KXTJ1009", KXTJ21009},
|
||||
{"SMO8500", KXCJ91008},
|
||||
{ },
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
|
||||
*
|
||||
* TODO: interrupt, thresholding, orientation / freefall events, autosleep
|
||||
* TODO: orientation / freefall events, autosleep
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -18,20 +18,42 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define MMA8452_STATUS 0x00
|
||||
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
|
||||
#define MMA8452_OUT_Y 0x03
|
||||
#define MMA8452_OUT_Z 0x05
|
||||
#define MMA8452_INT_SRC 0x0c
|
||||
#define MMA8452_WHO_AM_I 0x0d
|
||||
#define MMA8452_DATA_CFG 0x0e
|
||||
#define MMA8452_HP_FILTER_CUTOFF 0x0f
|
||||
#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK (BIT(0) | BIT(1))
|
||||
#define MMA8452_TRANSIENT_CFG 0x1d
|
||||
#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
|
||||
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
|
||||
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
|
||||
#define MMA8452_TRANSIENT_SRC 0x1e
|
||||
#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
|
||||
#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3)
|
||||
#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5)
|
||||
#define MMA8452_TRANSIENT_THS 0x1f
|
||||
#define MMA8452_TRANSIENT_THS_MASK 0x7f
|
||||
#define MMA8452_TRANSIENT_COUNT 0x20
|
||||
#define MMA8452_OFF_X 0x2f
|
||||
#define MMA8452_OFF_Y 0x30
|
||||
#define MMA8452_OFF_Z 0x31
|
||||
#define MMA8452_CTRL_REG1 0x2a
|
||||
#define MMA8452_CTRL_REG2 0x2b
|
||||
#define MMA8452_CTRL_REG2_RST BIT(6)
|
||||
#define MMA8452_CTRL_REG4 0x2d
|
||||
#define MMA8452_CTRL_REG5 0x2e
|
||||
|
||||
#define MMA8452_MAX_REG 0x31
|
||||
|
||||
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||
|
||||
|
@ -44,6 +66,10 @@
|
|||
#define MMA8452_DATA_CFG_FS_2G 0
|
||||
#define MMA8452_DATA_CFG_FS_4G 1
|
||||
#define MMA8452_DATA_CFG_FS_8G 2
|
||||
#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
|
||||
|
||||
#define MMA8452_INT_DRDY BIT(0)
|
||||
#define MMA8452_INT_TRANS BIT(5)
|
||||
|
||||
#define MMA8452_DEVICE_ID 0x2a
|
||||
|
||||
|
@ -106,6 +132,12 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mma8452_get_odr_index(struct mma8452_data *data)
|
||||
{
|
||||
return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
|
||||
MMA8452_CTRL_DR_SHIFT;
|
||||
}
|
||||
|
||||
static const int mma8452_samp_freq[8][2] = {
|
||||
{800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
|
||||
{6, 250000}, {1, 560000}
|
||||
|
@ -121,6 +153,30 @@ static const int mma8452_scales[3][2] = {
|
|||
{0, 9577}, {0, 19154}, {0, 38307}
|
||||
};
|
||||
|
||||
/* Datasheet table 35 (step time vs sample frequency) */
|
||||
static const int mma8452_transient_time_step_us[8] = {
|
||||
1250,
|
||||
2500,
|
||||
5000,
|
||||
10000,
|
||||
20000,
|
||||
20000,
|
||||
20000,
|
||||
20000
|
||||
};
|
||||
|
||||
/* Datasheet table 18 (normal mode) */
|
||||
static const int mma8452_hp_filter_cutoff[8][4][2] = {
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */
|
||||
{ {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */
|
||||
{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, /* 100 Hz sample */
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 50 Hz sample */
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */
|
||||
};
|
||||
|
||||
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -135,9 +191,23 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
|
|||
ARRAY_SIZE(mma8452_scales));
|
||||
}
|
||||
|
||||
static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int i = mma8452_get_odr_index(data);
|
||||
|
||||
return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i],
|
||||
ARRAY_SIZE(mma8452_hp_filter_cutoff[0]));
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
|
||||
static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
|
||||
mma8452_show_scale_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
|
||||
S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0);
|
||||
|
||||
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
|
@ -153,6 +223,31 @@ static int mma8452_get_scale_index(struct mma8452_data *data,
|
|||
ARRAY_SIZE(mma8452_scales), val, val2);
|
||||
}
|
||||
|
||||
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
int i = mma8452_get_odr_index(data);
|
||||
|
||||
return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i],
|
||||
ARRAY_SIZE(mma8452_scales[0]), val, val2);
|
||||
}
|
||||
|
||||
static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i = mma8452_get_odr_index(data);
|
||||
ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
|
||||
*hz = mma8452_hp_filter_cutoff[i][ret][0];
|
||||
*uHz = mma8452_hp_filter_cutoff[i][ret][1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mma8452_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
|
@ -180,8 +275,7 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
|||
*val2 = mma8452_scales[i][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
|
||||
MMA8452_CTRL_DR_SHIFT;
|
||||
i = mma8452_get_odr_index(data);
|
||||
*val = mma8452_samp_freq[i][0];
|
||||
*val2 = mma8452_samp_freq[i][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
@ -192,6 +286,16 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
*val = sign_extend32(ret, 7);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||
if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
|
||||
ret = mma8452_read_hp_filter(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -233,12 +337,31 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
int i, reg;
|
||||
|
||||
i = mma8452_get_hp_filter_index(data, val, val2);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_HP_FILTER_CUTOFF);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
|
||||
reg |= i;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg);
|
||||
}
|
||||
|
||||
static int mma8452_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
@ -266,11 +389,217 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
return mma8452_change_config(data, MMA8452_OFF_X +
|
||||
chan->scan_index, val);
|
||||
|
||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||
if (val == 0 && val2 == 0) {
|
||||
data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK;
|
||||
} else {
|
||||
data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
|
||||
ret = mma8452_set_hp_filter_frequency(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret, us;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_TRANSIENT_THS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret & MMA8452_TRANSIENT_THS_MASK;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_TRANSIENT_COUNT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
us = ret * mma8452_transient_time_step_us[
|
||||
mma8452_get_odr_index(data)];
|
||||
*val = us / USEC_PER_SEC;
|
||||
*val2 = us % USEC_PER_SEC;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_TRANSIENT_CFG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) {
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
} else {
|
||||
ret = mma8452_read_hp_filter(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mma8452_write_thresh(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret, reg, steps;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
return mma8452_change_config(data, MMA8452_TRANSIENT_THS,
|
||||
val & MMA8452_TRANSIENT_THS_MASK);
|
||||
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
steps = (val * USEC_PER_SEC + val2) /
|
||||
mma8452_transient_time_step_us[
|
||||
mma8452_get_odr_index(data)];
|
||||
|
||||
if (steps > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
|
||||
steps);
|
||||
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
|
||||
reg = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_TRANSIENT_CFG);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
if (val == 0 && val2 == 0) {
|
||||
reg |= MMA8452_TRANSIENT_CFG_HPF_BYP;
|
||||
} else {
|
||||
reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP;
|
||||
ret = mma8452_set_hp_filter_frequency(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mma8452_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int mma8452_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)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int val;
|
||||
|
||||
val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (state)
|
||||
val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
|
||||
else
|
||||
val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
|
||||
|
||||
val |= MMA8452_TRANSIENT_CFG_ELE;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val);
|
||||
}
|
||||
|
||||
static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
s64 ts = iio_get_time_ns();
|
||||
int src;
|
||||
|
||||
src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
|
||||
if (src < 0)
|
||||
return;
|
||||
|
||||
if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
ts);
|
||||
|
||||
if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
ts);
|
||||
|
||||
if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
ts);
|
||||
}
|
||||
|
||||
static irqreturn_t mma8452_interrupt(int irq, void *p)
|
||||
{
|
||||
struct iio_dev *indio_dev = p;
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret = IRQ_NONE;
|
||||
int src;
|
||||
|
||||
src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC);
|
||||
if (src < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (src & MMA8452_INT_DRDY) {
|
||||
iio_trigger_poll_chained(indio_dev->trig);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (src & MMA8452_INT_TRANS) {
|
||||
mma8452_transient_interrupt(indio_dev);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t mma8452_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
|
@ -291,6 +620,55 @@ 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;
|
||||
}
|
||||
|
||||
static const struct iio_event_spec mma8452_transient_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_PERIOD) |
|
||||
BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Threshold is configured in fixed 8G/127 steps regardless of
|
||||
* currently selected scale for measurement.
|
||||
*/
|
||||
static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742");
|
||||
|
||||
static struct attribute *mma8452_event_attributes[] = {
|
||||
&iio_const_attr_accel_transient_scale.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group mma8452_event_attribute_group = {
|
||||
.attrs = mma8452_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
#define MMA8452_CHANNEL(axis, idx) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
|
@ -298,7 +676,8 @@ done:
|
|||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.scan_index = idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
|
@ -307,6 +686,8 @@ done:
|
|||
.shift = 4, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.event_spec = mma8452_transient_event, \
|
||||
.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mma8452_channels[] = {
|
||||
|
@ -319,6 +700,7 @@ static const struct iio_chan_spec mma8452_channels[] = {
|
|||
static struct attribute *mma8452_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -330,11 +712,107 @@ static const struct iio_info mma8452_info = {
|
|||
.attrs = &mma8452_group,
|
||||
.read_raw = &mma8452_read_raw,
|
||||
.write_raw = &mma8452_write_raw,
|
||||
.event_attrs = &mma8452_event_attribute_group,
|
||||
.read_event_value = &mma8452_read_thresh,
|
||||
.write_event_value = &mma8452_write_thresh,
|
||||
.read_event_config = &mma8452_read_event_config,
|
||||
.write_event_config = &mma8452_write_event_config,
|
||||
.debugfs_reg_access = &mma8452_reg_access_dbg,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long mma8452_scan_masks[] = {0x7, 0};
|
||||
|
||||
static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
if (state)
|
||||
reg |= MMA8452_INT_DRDY;
|
||||
else
|
||||
reg &= ~MMA8452_INT_DRDY;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
|
||||
}
|
||||
|
||||
static int mma8452_validate_device(struct iio_trigger *trig,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
|
||||
if (indio != indio_dev)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops mma8452_trigger_ops = {
|
||||
.set_trigger_state = mma8452_data_rdy_trigger_set_state,
|
||||
.validate_device = mma8452_validate_device,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mma8452_trigger_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
struct iio_trigger *trig;
|
||||
int ret;
|
||||
|
||||
trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!trig)
|
||||
return -ENOMEM;
|
||||
|
||||
trig->dev.parent = &data->client->dev;
|
||||
trig->ops = &mma8452_trigger_ops;
|
||||
iio_trigger_set_drvdata(trig, indio_dev);
|
||||
|
||||
ret = iio_trigger_register(trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->trig = trig;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mma8452_trigger_cleanup(struct iio_dev *indio_dev)
|
||||
{
|
||||
if (indio_dev->trig)
|
||||
iio_trigger_unregister(indio_dev->trig);
|
||||
}
|
||||
|
||||
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 +843,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,18 +853,77 @@ static int mma8452_probe(struct i2c_client *client,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* By default set transient threshold to max to avoid events if
|
||||
* enabling without configuring threshold.
|
||||
*/
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
|
||||
MMA8452_TRANSIENT_THS_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->irq) {
|
||||
/*
|
||||
* Although we enable the transient interrupt source once and
|
||||
* for all here the transient event detection itself is not
|
||||
* enabled until userspace asks for it by
|
||||
* mma8452_write_event_config()
|
||||
*/
|
||||
int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS;
|
||||
int enabled_interrupts = MMA8452_INT_TRANS;
|
||||
|
||||
/* Assume wired to INT1 pin */
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
MMA8452_CTRL_REG5,
|
||||
supported_interrupts);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
MMA8452_CTRL_REG4,
|
||||
enabled_interrupts);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mma8452_trigger_setup(indio_dev);
|
||||
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)
|
||||
goto trigger_cleanup;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
mma8452_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto trigger_cleanup;
|
||||
|
||||
if (client->irq) {
|
||||
ret = devm_request_threaded_irq(&client->dev,
|
||||
client->irq,
|
||||
NULL, mma8452_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
client->name, indio_dev);
|
||||
if (ret)
|
||||
goto buffer_cleanup;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
trigger_cleanup:
|
||||
mma8452_trigger_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -399,6 +933,7 @@ static int mma8452_remove(struct i2c_client *client)
|
|||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
mma8452_trigger_cleanup(indio_dev);
|
||||
mma8452_standby(iio_priv(indio_dev));
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -374,7 +374,7 @@ EXPORT_SYMBOL(mma9551_read_status_word);
|
|||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @len: Length of array to read in bytes
|
||||
* @val: Array of words to read
|
||||
* @buf: Array of words to read
|
||||
*
|
||||
* Read multiple configuration registers (word-sized registers).
|
||||
*
|
||||
|
@ -414,7 +414,7 @@ EXPORT_SYMBOL(mma9551_read_config_words);
|
|||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @len: Length of array to read in bytes
|
||||
* @val: Array of words to read
|
||||
* @buf: Array of words to read
|
||||
*
|
||||
* Read multiple status registers (word-sized registers).
|
||||
*
|
||||
|
@ -454,7 +454,7 @@ EXPORT_SYMBOL(mma9551_read_status_words);
|
|||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @len: Length of array to write in bytes
|
||||
* @val: Array of words to write
|
||||
* @buf: Array of words to write
|
||||
*
|
||||
* Write multiple configuration registers (word-sized registers).
|
||||
*
|
||||
|
@ -800,7 +800,7 @@ EXPORT_SYMBOL(mma9551_read_accel_scale);
|
|||
*/
|
||||
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
|
||||
{
|
||||
return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
|
||||
return mma9551_write_config_byte(client, MMA9551_APPID_RSC,
|
||||
MMA9551_RSC_RESET +
|
||||
MMA9551_RSC_OFFSET(app_mask),
|
||||
MMA9551_RSC_VAL(app_mask));
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#define MMA9551_APPID_TILT 0x0B
|
||||
#define MMA9551_APPID_SLEEP_WAKE 0x12
|
||||
#define MMA9551_APPID_PEDOMETER 0x15
|
||||
#define MMA9551_APPID_RCS 0x17
|
||||
#define MMA9551_APPID_RSC 0x17
|
||||
#define MMA9551_APPID_NONE 0xff
|
||||
|
||||
/* Reset/Suspend/Clear application app masks */
|
||||
|
|
|
@ -63,8 +63,8 @@
|
|||
#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
|
||||
#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
|
||||
#define MMA9553_MASK_STATUS_SUSP BIT(11)
|
||||
#define MMA9553_MASK_STATUS_ACTIVITY (BIT(10) | BIT(9) | BIT(8))
|
||||
#define MMA9553_MASK_STATUS_VERSION 0x00FF
|
||||
#define MMA9553_MASK_STATUS_ACTIVITY GENMASK(10, 8)
|
||||
#define MMA9553_MASK_STATUS_VERSION GENMASK(7, 0)
|
||||
|
||||
#define MMA9553_REG_STEPCNT 0x02
|
||||
#define MMA9553_REG_DISTANCE 0x04
|
||||
|
@ -76,14 +76,15 @@
|
|||
#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
|
||||
#define MMA9553_DEFAULT_GPIO_POLARITY 0
|
||||
|
||||
/* Bitnum used for gpio configuration = bit number in high status byte */
|
||||
#define STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
|
||||
/* Bitnum used for GPIO configuration = bit number in high status byte */
|
||||
#define MMA9553_STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
|
||||
#define MMA9553_MAX_BITNUM MMA9553_STATUS_TO_BITNUM(BIT(16))
|
||||
|
||||
#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
|
||||
|
||||
/*
|
||||
* The internal activity level must be stable for ACTTHD samples before
|
||||
* ACTIVITY is updated.The ACTIVITY variable contains the current activity
|
||||
* ACTIVITY is updated. The ACTIVITY variable contains the current activity
|
||||
* level and is updated every time a step is detected or once a second
|
||||
* if there are no steps.
|
||||
*/
|
||||
|
@ -351,11 +352,11 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
|
|||
* This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
|
||||
*/
|
||||
if (activity_enabled && ev_step_detect->enabled)
|
||||
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
|
||||
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
|
||||
else if (ev_step_detect->enabled)
|
||||
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
|
||||
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
|
||||
else if (activity_enabled)
|
||||
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
|
||||
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
|
||||
else /* Reset */
|
||||
appid = MMA9551_APPID_NONE;
|
||||
|
||||
|
@ -363,9 +364,12 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
|
|||
return 0;
|
||||
|
||||
/* Save initial values for activity and stepcnt */
|
||||
if (activity_enabled || ev_step_detect->enabled)
|
||||
mma9553_read_activity_stepcnt(data, &data->activity,
|
||||
&data->stepcnt);
|
||||
if (activity_enabled || ev_step_detect->enabled) {
|
||||
ret = mma9553_read_activity_stepcnt(data, &data->activity,
|
||||
&data->stepcnt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mma9551_gpio_config(data->client,
|
||||
MMA9553_DEFAULT_GPIO_PIN,
|
||||
|
@ -396,13 +400,13 @@ static int mma9553_init(struct mma9553_data *data)
|
|||
sizeof(data->conf), (u16 *) &data->conf);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"device is not MMA9553L: failed to read cfg regs\n");
|
||||
"failed to read configuration registers\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Reset gpio */
|
||||
data->gpio_bitnum = -1;
|
||||
/* Reset GPIO */
|
||||
data->gpio_bitnum = MMA9553_MAX_BITNUM;
|
||||
ret = mma9553_conf_gpio(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -436,6 +440,32 @@ static int mma9553_init(struct mma9553_data *data)
|
|||
return mma9551_set_device_state(data->client, true);
|
||||
}
|
||||
|
||||
static int mma9553_read_status_word(struct mma9553_data *data, u16 reg,
|
||||
u16 *tmp)
|
||||
{
|
||||
bool powered_on;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The HW only counts steps and other dependent
|
||||
* parameters (speed, distance, calories, activity)
|
||||
* if power is on (from enabling an event or the
|
||||
* step counter).
|
||||
*/
|
||||
powered_on = mma9553_is_any_event_enabled(data, false, 0) ||
|
||||
data->stepcnt_enabled;
|
||||
if (!powered_on) {
|
||||
dev_err(&data->client->dev, "No channels enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mma9551_read_status_word(data->client, MMA9551_APPID_PEDOMETER,
|
||||
reg, tmp);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma9553_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
|
@ -444,69 +474,30 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
|
|||
int ret;
|
||||
u16 tmp;
|
||||
u8 activity;
|
||||
bool powered_on;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_STEPS:
|
||||
/*
|
||||
* The HW only counts steps and other dependent
|
||||
* parameters (speed, distance, calories, activity)
|
||||
* if power is on (from enabling an event or the
|
||||
* step counter */
|
||||
powered_on =
|
||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
||||
data->stepcnt_enabled;
|
||||
if (!powered_on) {
|
||||
dev_err(&data->client->dev,
|
||||
"No channels enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mma9551_read_status_word(data->client,
|
||||
MMA9551_APPID_PEDOMETER,
|
||||
ret = mma9553_read_status_word(data,
|
||||
MMA9553_REG_STEPCNT,
|
||||
&tmp);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = tmp;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_DISTANCE:
|
||||
powered_on =
|
||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
||||
data->stepcnt_enabled;
|
||||
if (!powered_on) {
|
||||
dev_err(&data->client->dev,
|
||||
"No channels enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mma9551_read_status_word(data->client,
|
||||
MMA9551_APPID_PEDOMETER,
|
||||
ret = mma9553_read_status_word(data,
|
||||
MMA9553_REG_DISTANCE,
|
||||
&tmp);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = tmp;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_ACTIVITY:
|
||||
powered_on =
|
||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
||||
data->stepcnt_enabled;
|
||||
if (!powered_on) {
|
||||
dev_err(&data->client->dev,
|
||||
"No channels enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mma9551_read_status_word(data->client,
|
||||
MMA9551_APPID_PEDOMETER,
|
||||
ret = mma9553_read_status_word(data,
|
||||
MMA9553_REG_STATUS,
|
||||
&tmp);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -531,38 +522,17 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_VELOCITY: /* m/h */
|
||||
if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
|
||||
return -EINVAL;
|
||||
powered_on =
|
||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
||||
data->stepcnt_enabled;
|
||||
if (!powered_on) {
|
||||
dev_err(&data->client->dev,
|
||||
"No channels enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mma9551_read_status_word(data->client,
|
||||
MMA9551_APPID_PEDOMETER,
|
||||
MMA9553_REG_SPEED, &tmp);
|
||||
mutex_unlock(&data->mutex);
|
||||
ret = mma9553_read_status_word(data,
|
||||
MMA9553_REG_SPEED,
|
||||
&tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = tmp;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_ENERGY: /* Cal or kcal */
|
||||
powered_on =
|
||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
||||
data->stepcnt_enabled;
|
||||
if (!powered_on) {
|
||||
dev_err(&data->client->dev,
|
||||
"No channels enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mma9551_read_status_word(data->client,
|
||||
MMA9551_APPID_PEDOMETER,
|
||||
ret = mma9553_read_status_word(data,
|
||||
MMA9553_REG_CALORIES,
|
||||
&tmp);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = tmp;
|
||||
|
@ -789,7 +759,7 @@ static int mma9553_write_event_config(struct iio_dev *indio_dev,
|
|||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
err_conf_gpio:
|
||||
if (state) {
|
||||
|
@ -897,7 +867,7 @@ static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
|
|||
gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
|
||||
/*
|
||||
* HW expects 0 for female and 1 for male,
|
||||
* while iio index is 0 for male and 1 for female
|
||||
* while iio index is 0 for male and 1 for female.
|
||||
*/
|
||||
return !gender;
|
||||
}
|
||||
|
@ -944,11 +914,11 @@ static const struct iio_event_spec mma9553_activity_events[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const char * const calibgender_modes[] = { "male", "female" };
|
||||
static const char * const mma9553_calibgender_modes[] = { "male", "female" };
|
||||
|
||||
static const struct iio_enum mma9553_calibgender_enum = {
|
||||
.items = calibgender_modes,
|
||||
.num_items = ARRAY_SIZE(calibgender_modes),
|
||||
.items = mma9553_calibgender_modes,
|
||||
.num_items = ARRAY_SIZE(mma9553_calibgender_modes),
|
||||
.get = mma9553_get_calibgender_mode,
|
||||
.set = mma9553_set_calibgender_mode,
|
||||
};
|
||||
|
@ -1110,16 +1080,16 @@ static int mma9553_gpio_probe(struct i2c_client *client)
|
|||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
/* data ready GPIO interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
dev_err(dev, "ACPI GPIO get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
|
||||
#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel"
|
||||
#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel"
|
||||
#define LIS331DL_ACCEL_DEV_NAME "lis331dl_accel"
|
||||
#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh"
|
||||
#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel"
|
||||
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"
|
||||
|
|
|
@ -153,6 +153,44 @@
|
|||
#define ST_ACCEL_4_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_4_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 5 */
|
||||
#define ST_ACCEL_5_WAI_EXP 0x3b
|
||||
#define ST_ACCEL_5_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_5_ODR_MASK 0x80
|
||||
#define ST_ACCEL_5_ODR_AVL_100HZ_VAL 0x00
|
||||
#define ST_ACCEL_5_ODR_AVL_400HZ_VAL 0x01
|
||||
#define ST_ACCEL_5_PW_ADDR 0x20
|
||||
#define ST_ACCEL_5_PW_MASK 0x40
|
||||
#define ST_ACCEL_5_FS_ADDR 0x20
|
||||
#define ST_ACCEL_5_FS_MASK 0x20
|
||||
#define ST_ACCEL_5_FS_AVL_2_VAL 0X00
|
||||
#define ST_ACCEL_5_FS_AVL_8_VAL 0X01
|
||||
/* TODO: check these resulting gain settings, these are not in the datsheet */
|
||||
#define ST_ACCEL_5_FS_AVL_2_GAIN IIO_G_TO_M_S_2(18000)
|
||||
#define ST_ACCEL_5_FS_AVL_8_GAIN IIO_G_TO_M_S_2(72000)
|
||||
#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_ACCEL_5_IG1_EN_ADDR 0x21
|
||||
#define ST_ACCEL_5_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_5_MULTIREAD_BIT false
|
||||
|
||||
static const struct iio_chan_spec st_accel_8bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8,
|
||||
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_accel_12bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
@ -454,6 +492,54 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
|
||||
.bootime = 2, /* guess */
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_5_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS331DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_5_ODR_ADDR,
|
||||
.mask = ST_ACCEL_5_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL },
|
||||
{ 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_5_PW_ADDR,
|
||||
.mask = ST_ACCEL_5_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_5_FS_ADDR,
|
||||
.mask = ST_ACCEL_5_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_5_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_5_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_5_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_5_FS_AVL_8_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
|
||||
.bootime = 2, /* guess */
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
@ -48,6 +48,10 @@ static const struct of_device_id st_accel_of_match[] = {
|
|||
.compatible = "st,lsm330dlc-accel",
|
||||
.data = LSM330DLC_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis331dl-accel",
|
||||
.data = LIS331DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis331dlh-accel",
|
||||
.data = LIS331DLH_ACCEL_DEV_NAME,
|
||||
|
|
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++;
|
||||
}
|
||||
|
@ -395,16 +424,43 @@ static const struct iio_info tiadc_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tiadc_parse_dt(struct platform_device *pdev,
|
||||
struct tiadc_device *adc_dev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct property *prop;
|
||||
const __be32 *cur;
|
||||
int channels = 0;
|
||||
u32 val;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int tiadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct tiadc_device *adc_dev;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct property *prop;
|
||||
const __be32 *cur;
|
||||
int err;
|
||||
u32 val;
|
||||
int channels = 0;
|
||||
|
||||
if (!node) {
|
||||
dev_err(&pdev->dev, "Could not find valid DT data.\n");
|
||||
|
@ -420,12 +476,7 @@ static int tiadc_probe(struct platform_device *pdev)
|
|||
adc_dev = iio_priv(indio_dev);
|
||||
|
||||
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
|
||||
|
||||
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
|
||||
adc_dev->channel_line[channels] = val;
|
||||
channels++;
|
||||
}
|
||||
adc_dev->channels = channels;
|
||||
tiadc_parse_dt(pdev, adc_dev);
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
|
|
|
@ -235,7 +235,7 @@ static int twl4030battery_temperature(int raw_volt)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
|
||||
curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10;
|
||||
/* Getting and calculating the thermistor resistance in ohms */
|
||||
res = volt * 1000 / curr;
|
||||
/* calculating temperature */
|
||||
|
@ -662,10 +662,8 @@ EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
|
|||
*
|
||||
* @madc: pointer to twl4030_madc_data struct
|
||||
* @chan: can be one of the two values:
|
||||
* TWL4030_BCI_ITHEN
|
||||
* Enables bias current for main battery type reading
|
||||
* TWL4030_BCI_TYPEN
|
||||
* Enables bias current for main battery temperature sensing
|
||||
* 0 - Enables bias current for main battery type reading
|
||||
* 1 - Enables bias current for main battery temperature sensing
|
||||
* @on: enable or disable chan.
|
||||
*
|
||||
* Function to enable or disable bias current for
|
||||
|
|
|
@ -118,15 +118,21 @@ enum average_sel {
|
|||
VF610_ADC_SAMPLE_32,
|
||||
};
|
||||
|
||||
enum conversion_mode_sel {
|
||||
VF610_ADC_CONV_NORMAL,
|
||||
VF610_ADC_CONV_HIGH_SPEED,
|
||||
VF610_ADC_CONV_LOW_POWER,
|
||||
};
|
||||
|
||||
struct vf610_adc_feature {
|
||||
enum clk_sel clk_sel;
|
||||
enum vol_ref vol_ref;
|
||||
enum conversion_mode_sel conv_mode;
|
||||
|
||||
int clk_div;
|
||||
int sample_rate;
|
||||
int res_mode;
|
||||
|
||||
bool lpm;
|
||||
bool calibration;
|
||||
bool ovwren;
|
||||
};
|
||||
|
@ -139,6 +145,8 @@ struct vf610_adc {
|
|||
u32 vref_uv;
|
||||
u32 value;
|
||||
struct regulator *vref;
|
||||
|
||||
u32 max_adck_rate[3];
|
||||
struct vf610_adc_feature adc_feature;
|
||||
|
||||
u32 sample_freq_avail[5];
|
||||
|
@ -148,46 +156,22 @@ struct vf610_adc {
|
|||
|
||||
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
|
||||
|
||||
#define VF610_ADC_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(0, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(1, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(2, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(3, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(4, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(5, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(6, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(7, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(8, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(9, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(10, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(11, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(12, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(13, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
|
||||
int i;
|
||||
int divisor, i;
|
||||
|
||||
adck_rate = info->max_adck_rate[adc_feature->conv_mode];
|
||||
|
||||
if (adck_rate) {
|
||||
/* calculate clk divider which is within specification */
|
||||
divisor = ipg_rate / adck_rate;
|
||||
adc_feature->clk_div = 1 << fls(divisor + 1);
|
||||
} else {
|
||||
/* fall-back value using a safe divisor */
|
||||
adc_feature->clk_div = 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate ADC sample frequencies
|
||||
|
@ -219,10 +203,8 @@ static inline void vf610_adc_cfg_init(struct vf610_adc *info)
|
|||
|
||||
adc_feature->res_mode = 12;
|
||||
adc_feature->sample_rate = 1;
|
||||
adc_feature->lpm = true;
|
||||
|
||||
/* Use a save ADCK which is below 20MHz on all devices */
|
||||
adc_feature->clk_div = 8;
|
||||
adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER;
|
||||
|
||||
vf610_adc_calculate_rates(info);
|
||||
}
|
||||
|
@ -304,10 +286,12 @@ static void vf610_adc_cfg_set(struct vf610_adc *info)
|
|||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
|
||||
cfg_data &= ~VF610_ADC_ADLPC_EN;
|
||||
if (adc_feature->lpm)
|
||||
if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER)
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
cfg_data &= ~VF610_ADC_ADHSC_EN;
|
||||
if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED)
|
||||
cfg_data |= VF610_ADC_ADHSC_EN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
}
|
||||
|
@ -409,6 +393,81 @@ static void vf610_adc_hw_init(struct vf610_adc *info)
|
|||
vf610_adc_cfg_set(info);
|
||||
}
|
||||
|
||||
static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
info->adc_feature.conv_mode = mode;
|
||||
vf610_adc_calculate_rates(info);
|
||||
vf610_adc_hw_init(info);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
return info->adc_feature.conv_mode;
|
||||
}
|
||||
|
||||
static const char * const vf610_conv_modes[] = { "normal", "high-speed",
|
||||
"low-power" };
|
||||
|
||||
static const struct iio_enum vf610_conversion_mode = {
|
||||
.items = vf610_conv_modes,
|
||||
.num_items = ARRAY_SIZE(vf610_conv_modes),
|
||||
.get = vf610_get_conversion_mode,
|
||||
.set = vf610_set_conversion_mode,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
|
||||
IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode),
|
||||
{},
|
||||
};
|
||||
|
||||
#define VF610_ADC_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.ext_info = vf610_ext_info, \
|
||||
}
|
||||
|
||||
#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(0, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(1, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(2, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(3, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(4, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(5, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(6, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(7, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(8, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(9, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(10, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(11, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(12, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(13, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
static int vf610_adc_read_data(struct vf610_adc *info)
|
||||
{
|
||||
int result;
|
||||
|
@ -651,6 +710,9 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
|||
|
||||
info->vref_uv = regulator_get_voltage(info->vref);
|
||||
|
||||
of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency",
|
||||
info->max_adck_rate, 3);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
|
|
@ -33,6 +33,8 @@ static void iio_buffer_cb_release(struct iio_buffer *buffer)
|
|||
static const struct iio_buffer_access_funcs iio_cb_access = {
|
||||
.store_to = &iio_buffer_cb_store_to,
|
||||
.release = &iio_buffer_cb_release,
|
||||
|
||||
.modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
|
||||
};
|
||||
|
||||
struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
||||
|
|
|
@ -245,6 +245,16 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
/* Sensor does not support interrupts */
|
||||
if (sdata->sensor_settings->drdy_irq.addr == 0) {
|
||||
if (pdata->drdy_int_pin)
|
||||
dev_info(&indio_dev->dev,
|
||||
"DRDY on pin INT%d specified, but sensor "
|
||||
"does not support interrupts\n",
|
||||
pdata->drdy_int_pin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (pdata->drdy_int_pin) {
|
||||
case 1:
|
||||
if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
|
||||
|
@ -285,7 +295,7 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
|||
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
|
||||
pdata->drdy_int_pin = (u8) val;
|
||||
else
|
||||
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 1;
|
||||
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
@ -332,11 +342,13 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
|||
return err;
|
||||
|
||||
/* set BDU */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
if (sdata->sensor_settings->bdu.addr) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor_settings->bdu.addr,
|
||||
sdata->sensor_settings->bdu.mask, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
|
@ -419,7 +431,9 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
|||
if (err < 0)
|
||||
goto st_sensors_free_memory;
|
||||
|
||||
if (byte_for_channel == 2)
|
||||
if (byte_for_channel == 1)
|
||||
*data = (s8)*outdata;
|
||||
else if (byte_for_channel == 2)
|
||||
*data = (s16)get_unaligned_le16(outdata);
|
||||
else if (byte_for_channel == 3)
|
||||
*data = (s32)st_sensors_get_unaligned_le24(outdata);
|
||||
|
@ -489,7 +503,8 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
|||
break;
|
||||
}
|
||||
if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
|
||||
dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
|
||||
dev_err(&indio_dev->dev, "device name \"%s\" and WhoAmI (0x%02x) mismatch",
|
||||
indio_dev->name, wai);
|
||||
goto sensor_name_mismatch;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,10 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
|||
IRQF_TRIGGER_RISING,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
|
||||
goto request_irq_error;
|
||||
}
|
||||
|
||||
iio_trigger_set_drvdata(sdata->trig, indio_dev);
|
||||
sdata->trig->ops = trigger_ops;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -239,13 +239,19 @@ static ssize_t iio_scan_el_show(struct device *dev,
|
|||
/* Note NULL used as error indicator as it doesn't make sense. */
|
||||
static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
|
||||
unsigned int masklength,
|
||||
const unsigned long *mask)
|
||||
const unsigned long *mask,
|
||||
bool strict)
|
||||
{
|
||||
if (bitmap_empty(mask, masklength))
|
||||
return NULL;
|
||||
while (*av_masks) {
|
||||
if (bitmap_subset(mask, av_masks, masklength))
|
||||
return av_masks;
|
||||
if (strict) {
|
||||
if (bitmap_equal(mask, av_masks, masklength))
|
||||
return av_masks;
|
||||
} else {
|
||||
if (bitmap_subset(mask, av_masks, masklength))
|
||||
return av_masks;
|
||||
}
|
||||
av_masks += BITS_TO_LONGS(masklength);
|
||||
}
|
||||
return NULL;
|
||||
|
@ -295,7 +301,7 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
|
|||
if (indio_dev->available_scan_masks) {
|
||||
mask = iio_scan_mask_match(indio_dev->available_scan_masks,
|
||||
indio_dev->masklength,
|
||||
trialmask);
|
||||
trialmask, false);
|
||||
if (!mask)
|
||||
goto err_invalid_mask;
|
||||
}
|
||||
|
@ -539,26 +545,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 +568,277 @@ 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);
|
||||
}
|
||||
|
||||
struct iio_device_config {
|
||||
unsigned int mode;
|
||||
const unsigned long *scan_mask;
|
||||
unsigned int scan_bytes;
|
||||
bool scan_timestamp;
|
||||
};
|
||||
|
||||
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;
|
||||
bool strict_scanmask = false;
|
||||
struct iio_buffer *buffer;
|
||||
bool scan_timestamp;
|
||||
unsigned int modes;
|
||||
|
||||
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;
|
||||
|
||||
modes = indio_dev->modes;
|
||||
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
if (buffer == remove_buffer)
|
||||
continue;
|
||||
modes &= buffer->access->modes;
|
||||
}
|
||||
|
||||
if (remove_buffer)
|
||||
iio_buffer_deactivate(remove_buffer);
|
||||
if (insert_buffer)
|
||||
iio_buffer_activate(indio_dev, insert_buffer);
|
||||
modes &= insert_buffer->access->modes;
|
||||
|
||||
/* 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;
|
||||
/* Definitely possible for devices to support both of these. */
|
||||
if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
|
||||
config->mode = INDIO_BUFFER_TRIGGERED;
|
||||
} else if (modes & INDIO_BUFFER_HARDWARE) {
|
||||
/*
|
||||
* Keep things simple for now and only allow a single buffer to
|
||||
* be connected in hardware mode.
|
||||
*/
|
||||
if (insert_buffer && !list_empty(&indio_dev->buffer_list))
|
||||
return -EINVAL;
|
||||
config->mode = INDIO_BUFFER_HARDWARE;
|
||||
strict_scanmask = true;
|
||||
} else if (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,
|
||||
strict_scanmask);
|
||||
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 +880,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,
|
||||
|
@ -806,8 +915,6 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
|
|||
ret = __iio_update_buffers(indio_dev,
|
||||
NULL, indio_dev->buffer);
|
||||
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
done:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return (ret < 0) ? ret : len;
|
||||
|
@ -886,6 +993,15 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
|
|||
int ret, i, attrn, attrcount, attrcount_orig = 0;
|
||||
const struct iio_chan_spec *channels;
|
||||
|
||||
channels = indio_dev->channels;
|
||||
if (channels) {
|
||||
int ml = indio_dev->masklength;
|
||||
|
||||
for (i = 0; i < indio_dev->num_channels; i++)
|
||||
ml = max(ml, channels[i].scan_index + 1);
|
||||
indio_dev->masklength = ml;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
return 0;
|
||||
|
||||
|
@ -929,12 +1045,6 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
|
|||
if (channels[i].scan_index < 0)
|
||||
continue;
|
||||
|
||||
/* Establish necessary mask length */
|
||||
if (channels[i].scan_index >
|
||||
(int)indio_dev->masklength - 1)
|
||||
indio_dev->masklength
|
||||
= channels[i].scan_index + 1;
|
||||
|
||||
ret = iio_buffer_add_channel_sysfs(indio_dev,
|
||||
&channels[i]);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -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",
|
||||
|
@ -128,6 +132,8 @@ static const char * const iio_chan_info_postfix[] = {
|
|||
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
|
||||
[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)
|
||||
|
|
|
@ -136,6 +136,8 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
|
|||
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
|
||||
.set_length = &iio_set_length_kfifo,
|
||||
.release = &iio_kfifo_buffer_release,
|
||||
|
||||
.modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
|
||||
};
|
||||
|
||||
struct iio_buffer *iio_kfifo_allocate(void)
|
||||
|
|
|
@ -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"
|
||||
|
@ -169,11 +192,23 @@ config LTR501
|
|||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for the Lite-On LTR-501ALS-01
|
||||
ambient light and proximity sensor.
|
||||
ambient light and proximity sensor. This driver also supports LTR-559
|
||||
ALS/PS or LTR-301 ALS sensors.
|
||||
|
||||
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",
|
||||
|
|
File diff suppressed because it is too large
Load diff
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;
|
||||
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");
|
|
@ -240,7 +240,7 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
|
|||
* convert between normalized values and HW values obtained using given
|
||||
* timing and gain settings.
|
||||
*/
|
||||
static int adc_shiftbits(u8 timing)
|
||||
static int tsl2563_adc_shiftbits(u8 timing)
|
||||
{
|
||||
int shift = 0;
|
||||
|
||||
|
@ -263,9 +263,9 @@ static int adc_shiftbits(u8 timing)
|
|||
}
|
||||
|
||||
/* Convert a HW ADC value to normalized scale. */
|
||||
static u32 normalize_adc(u16 adc, u8 timing)
|
||||
static u32 tsl2563_normalize_adc(u16 adc, u8 timing)
|
||||
{
|
||||
return adc << adc_shiftbits(timing);
|
||||
return adc << tsl2563_adc_shiftbits(timing);
|
||||
}
|
||||
|
||||
static void tsl2563_wait_adc(struct tsl2563_chip *chip)
|
||||
|
@ -350,8 +350,8 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip)
|
|||
retry = tsl2563_adjust_gainlevel(chip, adc0);
|
||||
}
|
||||
|
||||
chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
|
||||
chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
|
||||
chip->data0 = tsl2563_normalize_adc(adc0, chip->gainlevel->gaintime);
|
||||
chip->data1 = tsl2563_normalize_adc(adc1, chip->gainlevel->gaintime);
|
||||
|
||||
if (!chip->int_enabled)
|
||||
schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
|
||||
|
@ -361,13 +361,13 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int calib_to_sysfs(u32 calib)
|
||||
static inline int tsl2563_calib_to_sysfs(u32 calib)
|
||||
{
|
||||
return (int) (((calib * CALIB_BASE_SYSFS) +
|
||||
CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
|
||||
}
|
||||
|
||||
static inline u32 calib_from_sysfs(int value)
|
||||
static inline u32 tsl2563_calib_from_sysfs(int value)
|
||||
{
|
||||
return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
|
||||
}
|
||||
|
@ -426,7 +426,7 @@ static const struct tsl2563_lux_coeff lux_table[] = {
|
|||
};
|
||||
|
||||
/* Convert normalized, scaled ADC values to lux. */
|
||||
static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
||||
static unsigned int tsl2563_adc_to_lux(u32 adc0, u32 adc1)
|
||||
{
|
||||
const struct tsl2563_lux_coeff *lp = lux_table;
|
||||
unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
|
||||
|
@ -442,7 +442,7 @@ static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
|||
}
|
||||
|
||||
/* Apply calibration coefficient to ADC count. */
|
||||
static u32 calib_adc(u32 adc, u32 calib)
|
||||
static u32 tsl2563_calib_adc(u32 adc, u32 calib)
|
||||
{
|
||||
unsigned long scaled = adc;
|
||||
|
||||
|
@ -463,9 +463,9 @@ static int tsl2563_write_raw(struct iio_dev *indio_dev,
|
|||
if (mask != IIO_CHAN_INFO_CALIBSCALE)
|
||||
return -EINVAL;
|
||||
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
|
||||
chip->calib0 = calib_from_sysfs(val);
|
||||
chip->calib0 = tsl2563_calib_from_sysfs(val);
|
||||
else if (chan->channel2 == IIO_MOD_LIGHT_IR)
|
||||
chip->calib1 = calib_from_sysfs(val);
|
||||
chip->calib1 = tsl2563_calib_from_sysfs(val);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -491,11 +491,11 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
|
|||
ret = tsl2563_get_adc(chip);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
calib0 = calib_adc(chip->data0, chip->calib0) *
|
||||
calib0 = tsl2563_calib_adc(chip->data0, chip->calib0) *
|
||||
chip->cover_comp_gain;
|
||||
calib1 = calib_adc(chip->data1, chip->calib1) *
|
||||
calib1 = tsl2563_calib_adc(chip->data1, chip->calib1) *
|
||||
chip->cover_comp_gain;
|
||||
*val = adc_to_lux(calib0, calib1);
|
||||
*val = tsl2563_adc_to_lux(calib0, calib1);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_INTENSITY:
|
||||
|
@ -515,9 +515,9 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
|
||||
*val = calib_to_sysfs(chip->calib0);
|
||||
*val = tsl2563_calib_to_sysfs(chip->calib0);
|
||||
else
|
||||
*val = calib_to_sysfs(chip->calib1);
|
||||
*val = tsl2563_calib_to_sysfs(chip->calib1);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
|
@ -750,8 +750,8 @@ static int tsl2563_probe(struct i2c_client *client,
|
|||
chip->high_thres = 0xffff;
|
||||
chip->gainlevel = tsl2563_gainlevel_table;
|
||||
chip->intr = TSL2563_INT_PERSIST(4);
|
||||
chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
|
||||
chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
|
||||
chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
|
||||
chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
|
||||
|
||||
if (pdata)
|
||||
chip->cover_comp_gain = pdata->cover_comp_gain;
|
||||
|
|
|
@ -24,12 +24,12 @@
|
|||
|
||||
#define TSL4531_DRV_NAME "tsl4531"
|
||||
|
||||
#define TCS3472_COMMAND BIT(7)
|
||||
#define TSL4531_COMMAND BIT(7)
|
||||
|
||||
#define TSL4531_CONTROL (TCS3472_COMMAND | 0x00)
|
||||
#define TSL4531_CONFIG (TCS3472_COMMAND | 0x01)
|
||||
#define TSL4531_DATA (TCS3472_COMMAND | 0x04)
|
||||
#define TSL4531_ID (TCS3472_COMMAND | 0x0a)
|
||||
#define TSL4531_CONTROL (TSL4531_COMMAND | 0x00)
|
||||
#define TSL4531_CONFIG (TSL4531_COMMAND | 0x01)
|
||||
#define TSL4531_DATA (TSL4531_COMMAND | 0x04)
|
||||
#define TSL4531_ID (TSL4531_COMMAND | 0x0a)
|
||||
|
||||
/* operating modes in control register */
|
||||
#define TSL4531_MODE_POWERDOWN 0x00
|
||||
|
|
|
@ -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",
|
||||
|
|
579
drivers/iio/magnetometer/mmc35240.c
Normal file
579
drivers/iio/magnetometer/mmc35240.c
Normal file
|
@ -0,0 +1,579 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
||||
/*
|
||||
* Memsic OTP process code piece is put here for reference:
|
||||
*
|
||||
* #define OTP_CONVERT(REG) ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006
|
||||
* 1) For X axis, the COEFFICIENT is always 1.
|
||||
* 2) For Y axis, the COEFFICIENT is as below:
|
||||
* f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) |
|
||||
* (reg_data[2] >> 4)) + 1.0;
|
||||
* 3) For Z axis, the COEFFICIENT is as below:
|
||||
* f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35;
|
||||
* We implemented the OTP logic into driver.
|
||||
*/
|
||||
|
||||
/* scale = 1000 here for Y otp */
|
||||
#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6)
|
||||
|
||||
/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */
|
||||
#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81)
|
||||
|
||||
#define MMC35240_X_COEFF(x) (x)
|
||||
#define MMC35240_Y_COEFF(y) (y + 1000)
|
||||
#define MMC35240_Z_COEFF(z) (z + 13500)
|
||||
|
||||
#define MMC35240_OTP_START_ADDR 0x1B
|
||||
|
||||
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, 1024},
|
||||
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;
|
||||
|
||||
/* OTP compensation */
|
||||
int axis_coef[3];
|
||||
int axis_scale[3];
|
||||
};
|
||||
|
||||
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_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
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,
|
||||
NULL
|
||||
};
|
||||
|
||||
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, y_convert, z_convert;
|
||||
unsigned int reg_id;
|
||||
u8 otp_data[6];
|
||||
|
||||
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 */
|
||||
ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
|
||||
MMC35240_CTRL1_BW_MASK,
|
||||
data->res << MMC35240_CTRL1_BW_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR,
|
||||
(u8 *)otp_data, sizeof(otp_data));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) |
|
||||
(otp_data[2] >> 4));
|
||||
z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f);
|
||||
|
||||
data->axis_coef[0] = MMC35240_X_COEFF(1);
|
||||
data->axis_coef[1] = MMC35240_Y_COEFF(y_convert);
|
||||
data->axis_coef[2] = MMC35240_Z_COEFF(z_convert);
|
||||
|
||||
data->axis_scale[0] = 1;
|
||||
data->axis_scale[1] = 1000;
|
||||
data->axis_scale[2] = 10000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
/* minimum wait time to complete measurement is 10 ms */
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply
|
||||
compensation for output value.
|
||||
*
|
||||
* @data: device private data
|
||||
* @index: axis index for which we want the conversion
|
||||
* @buf: raw data to be converted, 2 bytes in little endian format
|
||||
* @val: compensated output reading (unit is milli gauss)
|
||||
*
|
||||
* Returns: 0 in case of success, -EINVAL when @index is not valid
|
||||
*/
|
||||
static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
|
||||
__le16 buf[], int *val)
|
||||
{
|
||||
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) * 1000 / sens_x;
|
||||
break;
|
||||
case AXIS_Y:
|
||||
*val = (raw_y - nfo) * 1000 / sens_y -
|
||||
(raw_z - nfo) * 1000 / sens_z;
|
||||
break;
|
||||
case AXIS_Z:
|
||||
*val = (raw_y - nfo) * 1000 / sens_y +
|
||||
(raw_z - nfo) * 1000 / sens_z;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
/* apply OTP compensation */
|
||||
*val = (*val) * data->axis_coef[index] / data->axis_scale[index];
|
||||
|
||||
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_RAW:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mmc35240_read_measurement(data, buf);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
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");
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LSM303DLH_MAGN_DEV_NAME "lsm303dlh_magn"
|
||||
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
|
||||
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
|
||||
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
||||
|
|
|
@ -45,6 +45,46 @@
|
|||
#define ST_MAGN_FS_AVL_12000MG 12000
|
||||
#define ST_MAGN_FS_AVL_16000MG 16000
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 0 */
|
||||
#define ST_MAGN_0_ODR_ADDR 0x00
|
||||
#define ST_MAGN_0_ODR_MASK 0x1c
|
||||
#define ST_MAGN_0_ODR_AVL_1HZ_VAL 0x00
|
||||
#define ST_MAGN_0_ODR_AVL_2HZ_VAL 0x01
|
||||
#define ST_MAGN_0_ODR_AVL_3HZ_VAL 0x02
|
||||
#define ST_MAGN_0_ODR_AVL_8HZ_VAL 0x03
|
||||
#define ST_MAGN_0_ODR_AVL_15HZ_VAL 0x04
|
||||
#define ST_MAGN_0_ODR_AVL_30HZ_VAL 0x05
|
||||
#define ST_MAGN_0_ODR_AVL_75HZ_VAL 0x06
|
||||
#define ST_MAGN_0_ODR_AVL_220HZ_VAL 0x07
|
||||
#define ST_MAGN_0_PW_ADDR 0x02
|
||||
#define ST_MAGN_0_PW_MASK 0x03
|
||||
#define ST_MAGN_0_PW_ON 0x00
|
||||
#define ST_MAGN_0_PW_OFF 0x03
|
||||
#define ST_MAGN_0_FS_ADDR 0x01
|
||||
#define ST_MAGN_0_FS_MASK 0xe0
|
||||
#define ST_MAGN_0_FS_AVL_1300_VAL 0x01
|
||||
#define ST_MAGN_0_FS_AVL_1900_VAL 0x02
|
||||
#define ST_MAGN_0_FS_AVL_2500_VAL 0x03
|
||||
#define ST_MAGN_0_FS_AVL_4000_VAL 0x04
|
||||
#define ST_MAGN_0_FS_AVL_4700_VAL 0x05
|
||||
#define ST_MAGN_0_FS_AVL_5600_VAL 0x06
|
||||
#define ST_MAGN_0_FS_AVL_8100_VAL 0x07
|
||||
#define ST_MAGN_0_FS_AVL_1300_GAIN_XY 1100
|
||||
#define ST_MAGN_0_FS_AVL_1900_GAIN_XY 855
|
||||
#define ST_MAGN_0_FS_AVL_2500_GAIN_XY 670
|
||||
#define ST_MAGN_0_FS_AVL_4000_GAIN_XY 450
|
||||
#define ST_MAGN_0_FS_AVL_4700_GAIN_XY 400
|
||||
#define ST_MAGN_0_FS_AVL_5600_GAIN_XY 330
|
||||
#define ST_MAGN_0_FS_AVL_8100_GAIN_XY 230
|
||||
#define ST_MAGN_0_FS_AVL_1300_GAIN_Z 980
|
||||
#define ST_MAGN_0_FS_AVL_1900_GAIN_Z 760
|
||||
#define ST_MAGN_0_FS_AVL_2500_GAIN_Z 600
|
||||
#define ST_MAGN_0_FS_AVL_4000_GAIN_Z 400
|
||||
#define ST_MAGN_0_FS_AVL_4700_GAIN_Z 355
|
||||
#define ST_MAGN_0_FS_AVL_5600_GAIN_Z 295
|
||||
#define ST_MAGN_0_FS_AVL_8100_GAIN_Z 205
|
||||
#define ST_MAGN_0_MULTIREAD_BIT false
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||
#define ST_MAGN_1_WAI_EXP 0x3c
|
||||
#define ST_MAGN_1_ODR_ADDR 0x00
|
||||
|
@ -150,6 +190,82 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
|
|||
};
|
||||
|
||||
static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||
{
|
||||
.wai = 0, /* This sensor has no valid WhoAmI report 0 */
|
||||
.sensors_supported = {
|
||||
[0] = LSM303DLH_MAGN_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_MAGN_0_ODR_ADDR,
|
||||
.mask = ST_MAGN_0_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
|
||||
{ 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
|
||||
{ 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
|
||||
{ 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
|
||||
{ 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
|
||||
{ 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
|
||||
{ 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_MAGN_0_PW_ADDR,
|
||||
.mask = ST_MAGN_0_PW_MASK,
|
||||
.value_on = ST_MAGN_0_PW_ON,
|
||||
.value_off = ST_MAGN_0_PW_OFF,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_MAGN_0_FS_ADDR,
|
||||
.mask = ST_MAGN_0_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_MAGN_FS_AVL_1300MG,
|
||||
.value = ST_MAGN_0_FS_AVL_1300_VAL,
|
||||
.gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
|
||||
.gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_MAGN_FS_AVL_1900MG,
|
||||
.value = ST_MAGN_0_FS_AVL_1900_VAL,
|
||||
.gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
|
||||
.gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_MAGN_FS_AVL_2500MG,
|
||||
.value = ST_MAGN_0_FS_AVL_2500_VAL,
|
||||
.gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
|
||||
.gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_MAGN_FS_AVL_4000MG,
|
||||
.value = ST_MAGN_0_FS_AVL_4000_VAL,
|
||||
.gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
|
||||
.gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
|
||||
},
|
||||
[4] = {
|
||||
.num = ST_MAGN_FS_AVL_4700MG,
|
||||
.value = ST_MAGN_0_FS_AVL_4700_VAL,
|
||||
.gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
|
||||
.gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
|
||||
},
|
||||
[5] = {
|
||||
.num = ST_MAGN_FS_AVL_5600MG,
|
||||
.value = ST_MAGN_0_FS_AVL_5600_VAL,
|
||||
.gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
|
||||
.gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
|
||||
},
|
||||
[6] = {
|
||||
.num = ST_MAGN_FS_AVL_8100MG,
|
||||
.value = ST_MAGN_0_FS_AVL_8100_VAL,
|
||||
.gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
|
||||
.gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
|
||||
},
|
||||
},
|
||||
},
|
||||
.multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_MAGN_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_magn_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lsm303dlh-magn",
|
||||
.data = LSM303DLH_MAGN_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303dlhc-magn",
|
||||
.data = LSM303DLHC_MAGN_DEV_NAME,
|
||||
|
@ -71,6 +75,7 @@ static int st_magn_i2c_remove(struct i2c_client *client)
|
|||
}
|
||||
|
||||
static const struct i2c_device_id st_magn_id_table[] = {
|
||||
{ LSM303DLH_MAGN_DEV_NAME },
|
||||
{ LSM303DLHC_MAGN_DEV_NAME },
|
||||
{ LSM303DLM_MAGN_DEV_NAME },
|
||||
{ LIS3MDL_MAGN_DEV_NAME },
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
@ -29,7 +31,9 @@
|
|||
|
||||
#define SX9500_DRIVER_NAME "sx9500"
|
||||
#define SX9500_IRQ_NAME "sx9500_event"
|
||||
#define SX9500_GPIO_NAME "sx9500_gpio"
|
||||
|
||||
#define SX9500_GPIO_INT "interrupt"
|
||||
#define SX9500_GPIO_RESET "reset"
|
||||
|
||||
/* Register definitions. */
|
||||
#define SX9500_REG_IRQ_SRC 0x00
|
||||
|
@ -73,6 +77,7 @@
|
|||
#define SX9500_CONVDONE_IRQ BIT(3)
|
||||
|
||||
#define SX9500_PROXSTAT_SHIFT 4
|
||||
#define SX9500_COMPSTAT_MASK GENMASK(3, 0)
|
||||
|
||||
#define SX9500_NUM_CHANNELS 4
|
||||
|
||||
|
@ -81,6 +86,7 @@ struct sx9500_data {
|
|||
struct i2c_client *client;
|
||||
struct iio_trigger *trig;
|
||||
struct regmap *regmap;
|
||||
struct gpio_desc *gpiod_rst;
|
||||
/*
|
||||
* Last reading of the proximity status for each channel. We
|
||||
* only send an event to user space when this changes.
|
||||
|
@ -89,6 +95,11 @@ struct sx9500_data {
|
|||
bool event_enabled[SX9500_NUM_CHANNELS];
|
||||
bool trigger_enabled;
|
||||
u16 *buffer;
|
||||
/* Remember enabled channels and sample rate during suspend. */
|
||||
unsigned int suspend_ctrl0;
|
||||
struct completion completion;
|
||||
int data_rdy_users, close_far_users;
|
||||
int channel_users[SX9500_NUM_CHANNELS];
|
||||
};
|
||||
|
||||
static const struct iio_event_spec sx9500_events[] = {
|
||||
|
@ -139,6 +150,10 @@ static const struct {
|
|||
{2, 500000},
|
||||
};
|
||||
|
||||
static const unsigned int sx9500_scan_period_table[] = {
|
||||
30, 60, 90, 120, 150, 200, 300, 400,
|
||||
};
|
||||
|
||||
static const struct regmap_range sx9500_writable_reg_ranges[] = {
|
||||
regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
|
||||
regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
|
||||
|
@ -191,7 +206,67 @@ static const struct regmap_config sx9500_regmap_config = {
|
|||
.volatile_table = &sx9500_volatile_regs,
|
||||
};
|
||||
|
||||
static int sx9500_read_proximity(struct sx9500_data *data,
|
||||
static int sx9500_inc_users(struct sx9500_data *data, int *counter,
|
||||
unsigned int reg, unsigned int bitmask)
|
||||
{
|
||||
(*counter)++;
|
||||
if (*counter != 1)
|
||||
/* Bit is already active, nothing to do. */
|
||||
return 0;
|
||||
|
||||
return regmap_update_bits(data->regmap, reg, bitmask, bitmask);
|
||||
}
|
||||
|
||||
static int sx9500_dec_users(struct sx9500_data *data, int *counter,
|
||||
unsigned int reg, unsigned int bitmask)
|
||||
{
|
||||
(*counter)--;
|
||||
if (*counter != 0)
|
||||
/* There are more users, do not deactivate. */
|
||||
return 0;
|
||||
|
||||
return regmap_update_bits(data->regmap, reg, bitmask, 0);
|
||||
}
|
||||
|
||||
static int sx9500_inc_chan_users(struct sx9500_data *data, int chan)
|
||||
{
|
||||
return sx9500_inc_users(data, &data->channel_users[chan],
|
||||
SX9500_REG_PROX_CTRL0, BIT(chan));
|
||||
}
|
||||
|
||||
static int sx9500_dec_chan_users(struct sx9500_data *data, int chan)
|
||||
{
|
||||
return sx9500_dec_users(data, &data->channel_users[chan],
|
||||
SX9500_REG_PROX_CTRL0, BIT(chan));
|
||||
}
|
||||
|
||||
static int sx9500_inc_data_rdy_users(struct sx9500_data *data)
|
||||
{
|
||||
return sx9500_inc_users(data, &data->data_rdy_users,
|
||||
SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
|
||||
}
|
||||
|
||||
static int sx9500_dec_data_rdy_users(struct sx9500_data *data)
|
||||
{
|
||||
return sx9500_dec_users(data, &data->data_rdy_users,
|
||||
SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
|
||||
}
|
||||
|
||||
static int sx9500_inc_close_far_users(struct sx9500_data *data)
|
||||
{
|
||||
return sx9500_inc_users(data, &data->close_far_users,
|
||||
SX9500_REG_IRQ_MSK,
|
||||
SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
|
||||
}
|
||||
|
||||
static int sx9500_dec_close_far_users(struct sx9500_data *data)
|
||||
{
|
||||
return sx9500_dec_users(data, &data->close_far_users,
|
||||
SX9500_REG_IRQ_MSK,
|
||||
SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
|
||||
}
|
||||
|
||||
static int sx9500_read_prox_data(struct sx9500_data *data,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
{
|
||||
|
@ -211,6 +286,79 @@ static int sx9500_read_proximity(struct sx9500_data *data,
|
|||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have no interrupt support, we have to wait for a scan period
|
||||
* after enabling a channel to get a result.
|
||||
*/
|
||||
static int sx9500_wait_for_sample(struct sx9500_data *data)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = (val & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
|
||||
|
||||
msleep(sx9500_scan_period_table[val]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sx9500_read_proximity(struct sx9500_data *data,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
ret = sx9500_inc_chan_users(data, chan->channel);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = sx9500_inc_data_rdy_users(data);
|
||||
if (ret < 0)
|
||||
goto out_dec_chan;
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
if (data->client->irq > 0)
|
||||
ret = wait_for_completion_interruptible(&data->completion);
|
||||
else
|
||||
ret = sx9500_wait_for_sample(data);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
ret = sx9500_read_prox_data(data, chan, val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = sx9500_dec_chan_users(data, chan->channel);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = sx9500_dec_data_rdy_users(data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
|
||||
goto out;
|
||||
|
||||
out_dec_chan:
|
||||
sx9500_dec_chan_users(data, chan->channel);
|
||||
out:
|
||||
mutex_unlock(&data->mutex);
|
||||
reinit_completion(&data->completion);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx9500_read_samp_freq(struct sx9500_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
|
@ -236,7 +384,6 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
|
@ -244,10 +391,7 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
mutex_lock(&data->mutex);
|
||||
ret = sx9500_read_proximity(data, chan, val);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
return sx9500_read_proximity(data, chan, val);
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return sx9500_read_samp_freq(data, val, val2);
|
||||
default:
|
||||
|
@ -318,28 +462,16 @@ static irqreturn_t sx9500_irq_handler(int irq, void *private)
|
|||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
||||
static void sx9500_push_events(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned int val, chan;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "i2c transfer error in irq\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ)))
|
||||
goto out;
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
|
||||
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "i2c transfer error in irq\n");
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
val >>= SX9500_PROXSTAT_SHIFT;
|
||||
|
@ -354,15 +486,34 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
|||
/* No change on this channel. */
|
||||
continue;
|
||||
|
||||
dir = new_prox ? IIO_EV_DIR_FALLING :
|
||||
IIO_EV_DIR_RISING;
|
||||
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
|
||||
chan,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
dir);
|
||||
dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
|
||||
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
|
||||
IIO_EV_TYPE_THRESH, dir);
|
||||
iio_push_event(indio_dev, ev, iio_get_time_ns());
|
||||
data->prox_stat[chan] = new_prox;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "i2c transfer error in irq\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ))
|
||||
sx9500_push_events(indio_dev);
|
||||
|
||||
if (val & SX9500_CONVDONE_IRQ)
|
||||
complete_all(&data->completion);
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->mutex);
|
||||
|
@ -391,9 +542,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
|
|||
int state)
|
||||
{
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
bool any_active = false;
|
||||
unsigned int irqmask;
|
||||
int ret;
|
||||
|
||||
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
|
||||
dir != IIO_EV_DIR_EITHER)
|
||||
|
@ -401,24 +550,32 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
|
|||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
if (state == 1) {
|
||||
ret = sx9500_inc_chan_users(data, chan->channel);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
ret = sx9500_inc_close_far_users(data);
|
||||
if (ret < 0)
|
||||
goto out_undo_chan;
|
||||
} else {
|
||||
ret = sx9500_dec_chan_users(data, chan->channel);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
ret = sx9500_dec_close_far_users(data);
|
||||
if (ret < 0)
|
||||
goto out_undo_chan;
|
||||
}
|
||||
|
||||
data->event_enabled[chan->channel] = state;
|
||||
goto out_unlock;
|
||||
|
||||
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
|
||||
if (data->event_enabled[i]) {
|
||||
any_active = true;
|
||||
break;
|
||||
}
|
||||
|
||||
irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ;
|
||||
if (any_active)
|
||||
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
|
||||
irqmask, irqmask);
|
||||
out_undo_chan:
|
||||
if (state == 1)
|
||||
sx9500_dec_chan_users(data, chan->channel);
|
||||
else
|
||||
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
|
||||
irqmask, 0);
|
||||
|
||||
sx9500_inc_chan_users(data, chan->channel);
|
||||
out_unlock:
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -469,12 +626,16 @@ static int sx9500_set_trigger_state(struct iio_trigger *trig,
|
|||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
|
||||
SX9500_CONVDONE_IRQ,
|
||||
state ? SX9500_CONVDONE_IRQ : 0);
|
||||
if (ret == 0)
|
||||
data->trigger_enabled = state;
|
||||
if (state)
|
||||
ret = sx9500_inc_data_rdy_users(data);
|
||||
else
|
||||
ret = sx9500_dec_data_rdy_users(data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
data->trigger_enabled = state;
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
|
@ -496,7 +657,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private)
|
|||
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
|
||||
ret = sx9500_read_prox_data(data, &indio_dev->channels[bit],
|
||||
&val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -515,6 +676,62 @@ out:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
|
||||
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||
ret = sx9500_inc_chan_users(data, i);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
if (test_bit(i, indio_dev->active_scan_mask))
|
||||
sx9500_dec_chan_users(data, i);
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
|
||||
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||
ret = sx9500_dec_chan_users(data, i);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
if (test_bit(i, indio_dev->active_scan_mask))
|
||||
sx9500_inc_chan_users(data, i);
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = {
|
||||
.preenable = sx9500_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = sx9500_buffer_predisable,
|
||||
};
|
||||
|
||||
struct sx9500_reg_default {
|
||||
u8 reg;
|
||||
u8 def;
|
||||
|
@ -570,17 +787,57 @@ static const struct sx9500_reg_default sx9500_default_regs[] = {
|
|||
},
|
||||
{
|
||||
.reg = SX9500_REG_PROX_CTRL0,
|
||||
/* Scan period: 30ms, all sensors enabled. */
|
||||
.def = 0x0f,
|
||||
/* Scan period: 30ms, all sensors disabled. */
|
||||
.def = 0x00,
|
||||
},
|
||||
};
|
||||
|
||||
/* Activate all channels and perform an initial compensation. */
|
||||
static int sx9500_init_compensation(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||
GENMASK(SX9500_NUM_CHANNELS, 0),
|
||||
GENMASK(SX9500_NUM_CHANNELS, 0));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 10; i >= 0; i--) {
|
||||
usleep_range(10000, 20000);
|
||||
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (!(val & SX9500_COMPSTAT_MASK))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < 0) {
|
||||
dev_err(&data->client->dev, "initial compensation timed out");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
out:
|
||||
regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||
GENMASK(SX9500_NUM_CHANNELS, 0), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx9500_init_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
unsigned int val;
|
||||
|
||||
if (data->gpiod_rst) {
|
||||
gpiod_set_value_cansleep(data->gpiod_rst, 0);
|
||||
usleep_range(1000, 2000);
|
||||
gpiod_set_value_cansleep(data->gpiod_rst, 1);
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -602,33 +859,34 @@ static int sx9500_init_device(struct iio_dev *indio_dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return sx9500_init_compensation(indio_dev);
|
||||
}
|
||||
|
||||
static int sx9500_gpio_probe(struct i2c_client *client,
|
||||
struct sx9500_data *data)
|
||||
static void sx9500_gpio_probe(struct i2c_client *client,
|
||||
struct sx9500_data *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
return;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
if (client->irq <= 0) {
|
||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_INT, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio))
|
||||
dev_err(dev, "gpio get irq failed\n");
|
||||
else
|
||||
client->irq = gpiod_to_irq(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
data->gpiod_rst = devm_gpiod_get_index(dev, SX9500_GPIO_RESET,
|
||||
0, GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->gpiod_rst)) {
|
||||
dev_warn(dev, "gpio get reset pin failed\n");
|
||||
data->gpiod_rst = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sx9500_probe(struct i2c_client *client,
|
||||
|
@ -645,14 +903,13 @@ static int sx9500_probe(struct i2c_client *client,
|
|||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->mutex);
|
||||
init_completion(&data->completion);
|
||||
data->trigger_enabled = false;
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
|
||||
if (IS_ERR(data->regmap))
|
||||
return PTR_ERR(data->regmap);
|
||||
|
||||
sx9500_init_device(indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = SX9500_DRIVER_NAME;
|
||||
indio_dev->channels = sx9500_channels;
|
||||
|
@ -661,10 +918,15 @@ static int sx9500_probe(struct i2c_client *client,
|
|||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
if (client->irq <= 0)
|
||||
client->irq = sx9500_gpio_probe(client, data);
|
||||
sx9500_gpio_probe(client, data);
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = sx9500_init_device(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->irq <= 0)
|
||||
dev_warn(&client->dev, "no valid irq found\n");
|
||||
else {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
sx9500_irq_handler, sx9500_irq_thread_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
|
@ -687,7 +949,8 @@ static int sx9500_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
sx9500_trigger_handler, NULL);
|
||||
sx9500_trigger_handler,
|
||||
&sx9500_buffer_setup_ops);
|
||||
if (ret < 0)
|
||||
goto out_trigger_unregister;
|
||||
|
||||
|
@ -720,6 +983,49 @@ static int sx9500_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sx9500_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||
&data->suspend_ctrl0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Scan period doesn't matter because when all the sensors are
|
||||
* deactivated the device is in sleep mode.
|
||||
*/
|
||||
ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0, 0);
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sx9500_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||
data->suspend_ctrl0);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops sx9500_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id sx9500_acpi_match[] = {
|
||||
{"SSX9500", 0},
|
||||
{ },
|
||||
|
@ -728,7 +1034,7 @@ MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
|
|||
|
||||
static const struct i2c_device_id sx9500_id[] = {
|
||||
{"sx9500", 0},
|
||||
{}
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sx9500_id);
|
||||
|
||||
|
@ -736,6 +1042,7 @@ static struct i2c_driver sx9500_driver = {
|
|||
.driver = {
|
||||
.name = SX9500_DRIVER_NAME,
|
||||
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
|
||||
.pm = &sx9500_pm_ops,
|
||||
},
|
||||
.probe = sx9500_probe,
|
||||
.remove = sx9500_remove,
|
||||
|
|
|
@ -12,12 +12,24 @@
|
|||
*
|
||||
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
|
||||
*
|
||||
* TODO: sleep mode, configuration EEPROM
|
||||
* To wake up from sleep mode, the SDA line must be held low while SCL is high
|
||||
* for at least 33ms. This is achieved with an extra GPIO that can be connected
|
||||
* directly to the SDA line. In normal operation, the GPIO is set as input and
|
||||
* will not interfere in I2C communication. While the GPIO is driven low, the
|
||||
* i2c adapter is locked since it cannot be used by other clients. The SCL line
|
||||
* always has a pull-up so we do not need an extra GPIO to drive it high. If
|
||||
* the "wakeup" GPIO is not given, power management will be disabled.
|
||||
*
|
||||
* TODO: filter configuration
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
|
@ -51,10 +63,101 @@
|
|||
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
|
||||
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
|
||||
|
||||
#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
|
||||
|
||||
struct mlx90614_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock; /* for EEPROM access only */
|
||||
struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
|
||||
unsigned long ready_timestamp; /* in jiffies */
|
||||
};
|
||||
|
||||
/*
|
||||
* Erase an address and write word.
|
||||
* The mutex must be locked before calling.
|
||||
*/
|
||||
static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
|
||||
u16 value)
|
||||
{
|
||||
/*
|
||||
* Note: The mlx90614 requires a PEC on writing but does not send us a
|
||||
* valid PEC on reading. Hence, we cannot set I2C_CLIENT_PEC in
|
||||
* i2c_client.flags. As a workaround, we use i2c_smbus_xfer here.
|
||||
*/
|
||||
union i2c_smbus_data data;
|
||||
s32 ret;
|
||||
|
||||
dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command);
|
||||
|
||||
data.word = 0x0000; /* erase command */
|
||||
ret = i2c_smbus_xfer(client->adapter, client->addr,
|
||||
client->flags | I2C_CLIENT_PEC,
|
||||
I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_WORD_DATA, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
msleep(MLX90614_TIMING_EEPROM);
|
||||
|
||||
data.word = value; /* actual write */
|
||||
ret = i2c_smbus_xfer(client->adapter, client->addr,
|
||||
client->flags | I2C_CLIENT_PEC,
|
||||
I2C_SMBUS_WRITE, command,
|
||||
I2C_SMBUS_WORD_DATA, &data);
|
||||
|
||||
msleep(MLX90614_TIMING_EEPROM);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
|
||||
* the last wake-up. This is normally only needed to get a valid temperature
|
||||
* reading. EEPROM access does not need such delay.
|
||||
* Return 0 on success, <0 on error.
|
||||
*/
|
||||
static int mlx90614_power_get(struct mlx90614_data *data, bool startup)
|
||||
{
|
||||
unsigned long now;
|
||||
|
||||
if (!data->wakeup_gpio)
|
||||
return 0;
|
||||
|
||||
pm_runtime_get_sync(&data->client->dev);
|
||||
|
||||
if (startup) {
|
||||
now = jiffies;
|
||||
if (time_before(now, data->ready_timestamp) &&
|
||||
msleep_interruptible(jiffies_to_msecs(
|
||||
data->ready_timestamp - now)) != 0) {
|
||||
pm_runtime_put_autosuspend(&data->client->dev);
|
||||
return -EINTR;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlx90614_power_put(struct mlx90614_data *data)
|
||||
{
|
||||
if (!data->wakeup_gpio)
|
||||
return;
|
||||
|
||||
pm_runtime_mark_last_busy(&data->client->dev);
|
||||
pm_runtime_put_autosuspend(&data->client->dev);
|
||||
}
|
||||
#else
|
||||
static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mlx90614_power_put(struct mlx90614_data *data)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
|
@ -85,9 +188,19 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, cmd);
|
||||
ret = mlx90614_power_get(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_read_word_data(data->client, cmd);
|
||||
mlx90614_power_put(data);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* MSB is an error flag */
|
||||
if (ret & 0x8000)
|
||||
return -EIO;
|
||||
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
|
@ -97,6 +210,63 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 20;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
||||
mlx90614_power_get(data, false);
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
MLX90614_EMISSIVITY);
|
||||
mutex_unlock(&data->lock);
|
||||
mlx90614_power_put(data);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret == 65535) {
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = ret * 15259; /* 1/65535 ~ 0.000015259 */
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mlx90614_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
s32 ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
||||
if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
|
||||
return -EINVAL;
|
||||
val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
|
||||
|
||||
mlx90614_power_get(data, false);
|
||||
mutex_lock(&data->lock);
|
||||
ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
|
||||
val);
|
||||
mutex_unlock(&data->lock);
|
||||
mlx90614_power_put(data);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBEMISSIVITY:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -115,7 +285,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||
.type = IIO_TEMP,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
|
@ -125,7 +296,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||
.modified = 1,
|
||||
.channel = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
|
@ -133,9 +305,103 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||
|
||||
static const struct iio_info mlx90614_info = {
|
||||
.read_raw = mlx90614_read_raw,
|
||||
.write_raw = mlx90614_write_raw,
|
||||
.write_raw_get_fmt = mlx90614_write_raw_get_fmt,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mlx90614_sleep(struct mlx90614_data *data)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
if (!data->wakeup_gpio) {
|
||||
dev_dbg(&data->client->dev, "Sleep disabled");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
dev_dbg(&data->client->dev, "Requesting sleep");
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
|
||||
data->client->flags | I2C_CLIENT_PEC,
|
||||
I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
|
||||
I2C_SMBUS_BYTE, NULL);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx90614_wakeup(struct mlx90614_data *data)
|
||||
{
|
||||
if (!data->wakeup_gpio) {
|
||||
dev_dbg(&data->client->dev, "Wake-up disabled");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
dev_dbg(&data->client->dev, "Requesting wake-up");
|
||||
|
||||
i2c_lock_adapter(data->client->adapter);
|
||||
gpiod_direction_output(data->wakeup_gpio, 0);
|
||||
msleep(MLX90614_TIMING_WAKEUP);
|
||||
gpiod_direction_input(data->wakeup_gpio);
|
||||
i2c_unlock_adapter(data->client->adapter);
|
||||
|
||||
data->ready_timestamp = jiffies +
|
||||
msecs_to_jiffies(MLX90614_TIMING_STARTUP);
|
||||
|
||||
/*
|
||||
* Quirk: the i2c controller may get confused right after the
|
||||
* wake-up signal has been sent. As a workaround, do a dummy read.
|
||||
* If the read fails, the controller will probably be reset so that
|
||||
* further reads will work.
|
||||
*/
|
||||
i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
|
||||
static struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
|
||||
{
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE)) {
|
||||
dev_info(&client->dev,
|
||||
"i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
|
||||
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_warn(&client->dev,
|
||||
"gpio acquisition failed with error %ld, sleep disabled",
|
||||
PTR_ERR(gpio));
|
||||
return NULL;
|
||||
} else if (!gpio) {
|
||||
dev_info(&client->dev,
|
||||
"wakeup-gpio not found, sleep disabled");
|
||||
}
|
||||
|
||||
return gpio;
|
||||
}
|
||||
#else
|
||||
static inline int mlx90614_sleep(struct mlx90614_data *data)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline int mlx90614_wakeup(struct mlx90614_data *data)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
|
||||
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
|
||||
{
|
||||
|
@ -166,6 +432,10 @@ static int mlx90614_probe(struct i2c_client *client,
|
|||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
data->wakeup_gpio = mlx90614_probe_wakeup(client);
|
||||
|
||||
mlx90614_wakeup(data);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
|
@ -188,12 +458,30 @@ static int mlx90614_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (data->wakeup_gpio) {
|
||||
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||
MLX90614_AUTOSLEEP_DELAY);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
pm_runtime_set_active(&client->dev);
|
||||
pm_runtime_enable(&client->dev);
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int mlx90614_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (data->wakeup_gpio) {
|
||||
pm_runtime_disable(&client->dev);
|
||||
if (!pm_runtime_status_suspended(&client->dev))
|
||||
mlx90614_sleep(data);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -204,10 +492,67 @@ static const struct i2c_device_id mlx90614_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mlx90614_id);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mlx90614_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (data->wakeup_gpio && pm_runtime_active(dev))
|
||||
return mlx90614_sleep(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx90614_pm_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
int err;
|
||||
|
||||
if (data->wakeup_gpio) {
|
||||
err = mlx90614_wakeup(data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mlx90614_pm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
|
||||
return mlx90614_sleep(data);
|
||||
}
|
||||
|
||||
static int mlx90614_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
|
||||
return mlx90614_wakeup(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mlx90614_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
|
||||
mlx90614_pm_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct i2c_driver mlx90614_driver = {
|
||||
.driver = {
|
||||
.name = "mlx90614",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mlx90614_pm_ops,
|
||||
},
|
||||
.probe = mlx90614_probe,
|
||||
.remove = mlx90614_remove,
|
||||
|
|
|
@ -41,8 +41,8 @@
|
|||
#define TMP006_CONFIG_CR_MASK 0x0e00
|
||||
#define TMP006_CONFIG_CR_SHIFT 9
|
||||
|
||||
#define MANUFACTURER_MAGIC 0x5449
|
||||
#define DEVICE_MAGIC 0x0067
|
||||
#define TMP006_MANUFACTURER_MAGIC 0x5449
|
||||
#define TMP006_DEVICE_MAGIC 0x0067
|
||||
|
||||
struct tmp006_data {
|
||||
struct i2c_client *client;
|
||||
|
@ -191,7 +191,7 @@ static bool tmp006_check_identification(struct i2c_client *client)
|
|||
if (did < 0)
|
||||
return false;
|
||||
|
||||
return mid == MANUFACTURER_MAGIC && did == DEVICE_MAGIC;
|
||||
return mid == TMP006_MANUFACTURER_MAGIC && did == TMP006_DEVICE_MAGIC;
|
||||
}
|
||||
|
||||
static int tmp006_probe(struct i2c_client *client,
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
***************************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
|
|
|
@ -158,8 +158,8 @@ static u32 goldfish_cmd_status(struct goldfish_pipe *pipe, u32 cmd)
|
|||
struct goldfish_pipe_dev *dev = pipe->dev;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
gf_write64((u64)(unsigned long)pipe, dev->base + PIPE_REG_CHANNEL,
|
||||
dev->base + PIPE_REG_CHANNEL_HIGH);
|
||||
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
|
||||
dev->base + PIPE_REG_CHANNEL_HIGH);
|
||||
writel(cmd, dev->base + PIPE_REG_COMMAND);
|
||||
status = readl(dev->base + PIPE_REG_STATUS);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
@ -172,8 +172,8 @@ static void goldfish_cmd(struct goldfish_pipe *pipe, u32 cmd)
|
|||
struct goldfish_pipe_dev *dev = pipe->dev;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
gf_write64((u64)(unsigned long)pipe, dev->base + PIPE_REG_CHANNEL,
|
||||
dev->base + PIPE_REG_CHANNEL_HIGH);
|
||||
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
|
||||
dev->base + PIPE_REG_CHANNEL_HIGH);
|
||||
writel(cmd, dev->base + PIPE_REG_COMMAND);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
@ -327,12 +327,12 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
|||
spin_lock_irqsave(&dev->lock, irq_flags);
|
||||
if (access_with_param(dev, CMD_WRITE_BUFFER + cmd_offset,
|
||||
address, avail, pipe, &status)) {
|
||||
gf_write64((u64)(unsigned long)pipe,
|
||||
dev->base + PIPE_REG_CHANNEL,
|
||||
dev->base + PIPE_REG_CHANNEL_HIGH);
|
||||
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
|
||||
dev->base + PIPE_REG_CHANNEL_HIGH);
|
||||
writel(avail, dev->base + PIPE_REG_SIZE);
|
||||
gf_write64(address, dev->base + PIPE_REG_ADDRESS,
|
||||
dev->base + PIPE_REG_ADDRESS_HIGH);
|
||||
gf_write_ptr((void *)address,
|
||||
dev->base + PIPE_REG_ADDRESS,
|
||||
dev->base + PIPE_REG_ADDRESS_HIGH);
|
||||
writel(CMD_WRITE_BUFFER + cmd_offset,
|
||||
dev->base + PIPE_REG_COMMAND);
|
||||
status = readl(dev->base + PIPE_REG_STATUS);
|
||||
|
|
|
@ -108,8 +108,8 @@ source "drivers/staging/clocking-wizard/Kconfig"
|
|||
|
||||
source "drivers/staging/fbtft/Kconfig"
|
||||
|
||||
source "drivers/staging/i2o/Kconfig"
|
||||
|
||||
source "drivers/staging/fsl-mc/Kconfig"
|
||||
|
||||
source "drivers/staging/wilc1000/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
|
|
@ -23,7 +23,7 @@ obj-$(CONFIG_VT6656) += vt6656/
|
|||
obj-$(CONFIG_VME_BUS) += vme/
|
||||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
|
||||
obj-$(CONFIG_FB_SM7XX) += sm750fb/
|
||||
obj-$(CONFIG_FB_SM750) += sm750fb/
|
||||
obj-$(CONFIG_FB_XGI) += xgifb/
|
||||
obj-$(CONFIG_USB_EMXX) += emxx_udc/
|
||||
obj-$(CONFIG_FT1000) += ft1000/
|
||||
|
@ -46,5 +46,5 @@ obj-$(CONFIG_CRYPTO_SKEIN) += skein/
|
|||
obj-$(CONFIG_UNISYSSPAR) += unisys/
|
||||
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
|
||||
obj-$(CONFIG_FB_TFT) += fbtft/
|
||||
obj-$(CONFIG_I2O) += i2o/
|
||||
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
|
||||
obj-$(CONFIG_WILC1000) += wilc1000/
|
||||
|
|
|
@ -22,11 +22,20 @@ config ANDROID_TIMED_GPIO
|
|||
tristate "Android timed gpio driver"
|
||||
depends on GPIOLIB && ANDROID_TIMED_OUTPUT
|
||||
default n
|
||||
---help---
|
||||
Unlike generic gpio is to allow programs to access and manipulate gpio
|
||||
registers from user space, timed output/gpio is a system to allow changing
|
||||
a gpio pin and restore it automatically after a specified timeout.
|
||||
|
||||
config ANDROID_LOW_MEMORY_KILLER
|
||||
bool "Android Low Memory Killer"
|
||||
---help---
|
||||
Registers processes to be killed when memory is low
|
||||
Registers processes to be killed when low memory conditions, this is useful
|
||||
as there is no particular swap space on android.
|
||||
|
||||
The registered process will kills according to the priorities in android init
|
||||
scripts (/init.rc), and it defines priority values with minimum free memory size
|
||||
for each priority.
|
||||
|
||||
config SYNC
|
||||
bool "Synchronization framework"
|
||||
|
|
|
@ -1579,6 +1579,7 @@ struct ion_device *ion_device_create(long (*custom_ioctl)
|
|||
ret = misc_register(&idev->dev);
|
||||
if (ret) {
|
||||
pr_err("ion: failed to register misc device.\n");
|
||||
kfree(idev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
|
|||
chunk_heap->heap.ops = &chunk_heap_ops;
|
||||
chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
|
||||
chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
|
||||
pr_info("%s: base %lu size %zu align %ld\n", __func__, chunk_heap->base,
|
||||
pr_debug("%s: base %lu size %zu align %ld\n", __func__, chunk_heap->base,
|
||||
heap_data->size, heap_data->align);
|
||||
|
||||
return &chunk_heap->heap;
|
||||
|
|
|
@ -33,7 +33,7 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
|
|||
|
||||
/**
|
||||
* struct ion_buffer - metadata for a particular buffer
|
||||
* @ref: refernce count
|
||||
* @ref: reference count
|
||||
* @node: node in the ion_device buffers tree
|
||||
* @dev: back pointer to the ion_device
|
||||
* @heap: back pointer to the heap the buffer came from
|
||||
|
@ -46,7 +46,7 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
|
|||
* an ion_phys_addr_t (and someday a phys_addr_t)
|
||||
* @lock: protects the buffers cnt fields
|
||||
* @kmap_cnt: number of times the buffer is mapped to the kernel
|
||||
* @vaddr: the kenrel mapping if kmap_cnt is not zero
|
||||
* @vaddr: the kernel mapping if kmap_cnt is not zero
|
||||
* @dmap_cnt: number of times the buffer is mapped for dma
|
||||
* @sg_table: the sg table for the buffer if dmap_cnt is not zero
|
||||
* @pages: flat array of pages in the buffer -- used by fault
|
||||
|
@ -266,7 +266,7 @@ void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer);
|
|||
/**
|
||||
* ion_heap_freelist_drain - drain the deferred free list
|
||||
* @heap: the heap
|
||||
* @size: ammount of memory to drain in bytes
|
||||
* @size: amount of memory to drain in bytes
|
||||
*
|
||||
* Drains the indicated amount of memory from the deferred freelist immediately.
|
||||
* Returns the total amount freed. The total freed may be higher depending
|
||||
|
|
|
@ -261,7 +261,20 @@ static int __init ion_test_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ion_test_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ion_test_device *testdev;
|
||||
|
||||
testdev = platform_get_drvdata(pdev);
|
||||
if (!testdev)
|
||||
return -ENODATA;
|
||||
|
||||
return misc_deregister(&testdev->misc);
|
||||
}
|
||||
|
||||
static struct platform_device *ion_test_pdev;
|
||||
static struct platform_driver ion_test_platform_driver = {
|
||||
.remove = ion_test_remove,
|
||||
.driver = {
|
||||
.name = "ion-test",
|
||||
},
|
||||
|
@ -269,13 +282,18 @@ static struct platform_driver ion_test_platform_driver = {
|
|||
|
||||
static int __init ion_test_init(void)
|
||||
{
|
||||
platform_device_register_simple("ion-test", -1, NULL, 0);
|
||||
ion_test_pdev = platform_device_register_simple("ion-test",
|
||||
-1, NULL, 0);
|
||||
if (!ion_test_pdev)
|
||||
return -ENODEV;
|
||||
|
||||
return platform_driver_probe(&ion_test_platform_driver, ion_test_probe);
|
||||
}
|
||||
|
||||
static void __exit ion_test_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ion_test_platform_driver);
|
||||
platform_device_unregister(ion_test_pdev);
|
||||
}
|
||||
|
||||
module_init(ion_test_init);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include "../ion.h"
|
||||
|
|
|
@ -156,20 +156,27 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
|
|||
p->pid, p->comm, oom_score_adj, tasksize);
|
||||
}
|
||||
if (selected) {
|
||||
lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n",
|
||||
selected->pid, selected->comm,
|
||||
selected_oom_score_adj, selected_tasksize);
|
||||
lowmem_deathpending_timeout = jiffies + HZ;
|
||||
task_lock(selected);
|
||||
if (!selected->mm) {
|
||||
/* Already exited, cannot do mark_tsk_oom_victim() */
|
||||
task_unlock(selected);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* FIXME: lowmemorykiller shouldn't abuse global OOM killer
|
||||
* infrastructure. There is no real reason why the selected
|
||||
* task should have access to the memory reserves.
|
||||
*/
|
||||
mark_oom_victim(selected);
|
||||
task_unlock(selected);
|
||||
lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n",
|
||||
selected->pid, selected->comm,
|
||||
selected_oom_score_adj, selected_tasksize);
|
||||
lowmem_deathpending_timeout = jiffies + HZ;
|
||||
send_sig(SIGKILL, selected, 0);
|
||||
rem += selected_tasksize;
|
||||
}
|
||||
|
||||
out:
|
||||
lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
|
||||
sc->nr_to_scan, sc->gfp_mask, rem);
|
||||
rcu_read_unlock();
|
||||
|
|
|
@ -179,7 +179,7 @@ struct ion_custom_data {
|
|||
* DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory
|
||||
*
|
||||
* Deprecated in favor of using the dma_buf api's correctly (syncing
|
||||
* will happend automatically when the buffer is mapped to a device).
|
||||
* will happen automatically when the buffer is mapped to a device).
|
||||
* If necessary should be used after touching a cached buffer from the cpu,
|
||||
* this will make the buffer in memory coherent.
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
config STAGING_BOARD
|
||||
bool "Staging Board Support"
|
||||
depends on OF_ADDRESS
|
||||
depends on BROKEN
|
||||
help
|
||||
Select to enable per-board staging support code.
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
obj-y := board.o
|
||||
obj-$(CONFIG_ARCH_EMEV2) += kzm9d.o
|
||||
obj-$(CONFIG_ARCH_EMEV2) += kzm9d.o
|
||||
obj-$(CONFIG_ARCH_R8A7740) += armadillo800eva.o
|
||||
|
|
105
drivers/staging/board/armadillo800eva.c
Normal file
105
drivers/staging/board/armadillo800eva.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Staging board support for Armadillo 800 eva.
|
||||
* Enable not-yet-DT-capable devices here.
|
||||
*
|
||||
* Based on board-armadillo800eva.c
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
* Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.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; version 2 of the License.
|
||||
*
|
||||
* 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/dma-mapping.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <video/sh_mobile_lcdc.h>
|
||||
|
||||
#include "board.h"
|
||||
|
||||
|
||||
static struct fb_videomode lcdc0_mode = {
|
||||
.name = "AMPIER/AM-800480",
|
||||
.xres = 800,
|
||||
.yres = 480,
|
||||
.left_margin = 88,
|
||||
.right_margin = 40,
|
||||
.hsync_len = 128,
|
||||
.upper_margin = 20,
|
||||
.lower_margin = 5,
|
||||
.vsync_len = 5,
|
||||
.sync = 0,
|
||||
};
|
||||
|
||||
static struct sh_mobile_lcdc_info lcdc0_info = {
|
||||
.clock_source = LCDC_CLK_BUS,
|
||||
.ch[0] = {
|
||||
.chan = LCDC_CHAN_MAINLCD,
|
||||
.fourcc = V4L2_PIX_FMT_RGB565,
|
||||
.interface_type = RGB24,
|
||||
.clock_divider = 5,
|
||||
.flags = 0,
|
||||
.lcd_modes = &lcdc0_mode,
|
||||
.num_modes = 1,
|
||||
.panel_cfg = {
|
||||
.width = 111,
|
||||
.height = 68,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource lcdc0_resources[] = {
|
||||
[0] = {
|
||||
.name = "LCD0",
|
||||
.start = 0xfe940000,
|
||||
.end = 0xfe943fff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = 177 + 32,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device lcdc0_device = {
|
||||
.name = "sh_mobile_lcdc_fb",
|
||||
.num_resources = ARRAY_SIZE(lcdc0_resources),
|
||||
.resource = lcdc0_resources,
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &lcdc0_info,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct board_staging_clk lcdc0_clocks[] __initconst = {
|
||||
{ "lcdc0", NULL, "sh_mobile_lcdc_fb.0" },
|
||||
};
|
||||
|
||||
static const struct board_staging_dev armadillo800eva_devices[] __initconst = {
|
||||
{
|
||||
.pdev = &lcdc0_device,
|
||||
.clocks = lcdc0_clocks,
|
||||
.nclocks = ARRAY_SIZE(lcdc0_clocks),
|
||||
.domain = "a4lc",
|
||||
},
|
||||
};
|
||||
|
||||
static void __init armadillo800eva_init(void)
|
||||
{
|
||||
board_staging_gic_setup_xlate("arm,cortex-a9-gic", 32);
|
||||
board_staging_register_devices(armadillo800eva_devices,
|
||||
ARRAY_SIZE(armadillo800eva_devices));
|
||||
}
|
||||
|
||||
board_staging("renesas,armadillo800eva", armadillo800eva_init);
|
|
@ -1,10 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Magnus Damm
|
||||
* Copyright (C) 2015 Glider bvba
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "board_staging: " fmt
|
||||
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
|
||||
#include "board.h"
|
||||
|
||||
static struct device_node *irqc_node __initdata;
|
||||
static unsigned int irqc_base __initdata;
|
||||
|
||||
static bool find_by_address(u64 base_address)
|
||||
{
|
||||
struct device_node *dn = of_find_all_nodes(NULL);
|
||||
|
@ -38,3 +58,119 @@ bool __init board_staging_dt_node_available(const struct resource *resource,
|
|||
|
||||
return false; /* Nothing found */
|
||||
}
|
||||
|
||||
int __init board_staging_gic_setup_xlate(const char *gic_match,
|
||||
unsigned int base)
|
||||
{
|
||||
WARN_ON(irqc_node);
|
||||
|
||||
irqc_node = of_find_compatible_node(NULL, NULL, gic_match);
|
||||
|
||||
WARN_ON(!irqc_node);
|
||||
if (!irqc_node)
|
||||
return -ENOENT;
|
||||
|
||||
irqc_base = base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init gic_fixup_resource(struct resource *res)
|
||||
{
|
||||
struct of_phandle_args irq_data;
|
||||
unsigned int hwirq = res->start;
|
||||
unsigned int virq;
|
||||
|
||||
if (resource_type(res) != IORESOURCE_IRQ || !irqc_node)
|
||||
return;
|
||||
|
||||
irq_data.np = irqc_node;
|
||||
irq_data.args_count = 3;
|
||||
irq_data.args[0] = 0;
|
||||
irq_data.args[1] = hwirq - irqc_base;
|
||||
switch (res->flags &
|
||||
(IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE |
|
||||
IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL)) {
|
||||
case IORESOURCE_IRQ_LOWEDGE:
|
||||
irq_data.args[2] = IRQ_TYPE_EDGE_FALLING;
|
||||
break;
|
||||
case IORESOURCE_IRQ_HIGHEDGE:
|
||||
irq_data.args[2] = IRQ_TYPE_EDGE_RISING;
|
||||
break;
|
||||
case IORESOURCE_IRQ_LOWLEVEL:
|
||||
irq_data.args[2] = IRQ_TYPE_LEVEL_LOW;
|
||||
break;
|
||||
case IORESOURCE_IRQ_HIGHLEVEL:
|
||||
default:
|
||||
irq_data.args[2] = IRQ_TYPE_LEVEL_HIGH;
|
||||
break;
|
||||
}
|
||||
|
||||
virq = irq_create_of_mapping(&irq_data);
|
||||
if (WARN_ON(!virq))
|
||||
return;
|
||||
|
||||
pr_debug("hwirq %u -> virq %u\n", hwirq, virq);
|
||||
res->start = virq;
|
||||
}
|
||||
|
||||
void __init board_staging_gic_fixup_resources(struct resource *res,
|
||||
unsigned int nres)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < nres; i++)
|
||||
gic_fixup_resource(&res[i]);
|
||||
}
|
||||
|
||||
int __init board_staging_register_clock(const struct board_staging_clk *bsc)
|
||||
{
|
||||
int error;
|
||||
|
||||
pr_debug("Aliasing clock %s for con_id %s dev_id %s\n", bsc->clk,
|
||||
bsc->con_id, bsc->dev_id);
|
||||
error = clk_add_alias(bsc->con_id, bsc->dev_id, bsc->clk, NULL);
|
||||
if (error)
|
||||
pr_err("Failed to alias clock %s (%d)\n", bsc->clk, error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int __init board_staging_register_device(const struct board_staging_dev *dev)
|
||||
{
|
||||
struct platform_device *pdev = dev->pdev;
|
||||
unsigned int i;
|
||||
int error;
|
||||
|
||||
pr_debug("Trying to register device %s\n", pdev->name);
|
||||
if (board_staging_dt_node_available(pdev->resource,
|
||||
pdev->num_resources)) {
|
||||
pr_warn("Skipping %s, already in DT\n", pdev->name);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
board_staging_gic_fixup_resources(pdev->resource, pdev->num_resources);
|
||||
|
||||
for (i = 0; i < dev->nclocks; i++)
|
||||
board_staging_register_clock(&dev->clocks[i]);
|
||||
|
||||
error = platform_device_register(pdev);
|
||||
if (error) {
|
||||
pr_err("Failed to register device %s (%d)\n", pdev->name,
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (dev->domain)
|
||||
__pm_genpd_name_add_device(dev->domain, &pdev->dev, NULL);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void __init board_staging_register_devices(const struct board_staging_dev *devs,
|
||||
unsigned int ndevs)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ndevs; i++)
|
||||
board_staging_register_device(&devs[i]);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,35 @@
|
|||
#ifndef __BOARD_H__
|
||||
#define __BOARD_H__
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
struct board_staging_clk {
|
||||
const char *clk;
|
||||
const char *con_id;
|
||||
const char *dev_id;
|
||||
};
|
||||
|
||||
struct board_staging_dev {
|
||||
/* Platform Device */
|
||||
struct platform_device *pdev;
|
||||
/* Clocks (optional) */
|
||||
const struct board_staging_clk *clocks;
|
||||
unsigned int nclocks;
|
||||
/* Generic PM Domain (optional) */
|
||||
const char *domain;
|
||||
};
|
||||
|
||||
struct resource;
|
||||
|
||||
bool board_staging_dt_node_available(const struct resource *resource,
|
||||
unsigned int num_resources);
|
||||
int board_staging_gic_setup_xlate(const char *gic_match, unsigned int base);
|
||||
void board_staging_gic_fixup_resources(struct resource *res, unsigned int nres);
|
||||
int board_staging_register_clock(const struct board_staging_clk *bsc);
|
||||
int board_staging_register_device(const struct board_staging_dev *dev);
|
||||
void board_staging_register_devices(const struct board_staging_dev *devs,
|
||||
unsigned int ndevs);
|
||||
|
||||
#define board_staging(str, fn) \
|
||||
static int __init runtime_board_check(void) \
|
||||
|
@ -15,6 +40,6 @@ static int __init runtime_board_check(void) \
|
|||
return 0; \
|
||||
} \
|
||||
\
|
||||
late_initcall(runtime_board_check)
|
||||
device_initcall(runtime_board_check)
|
||||
|
||||
#endif /* __BOARD_H__ */
|
||||
|
|
|
@ -4,16 +4,22 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include "board.h"
|
||||
|
||||
static const struct resource usbs1_res[] __initconst = {
|
||||
static struct resource usbs1_res[] __initdata = {
|
||||
DEFINE_RES_MEM(0xe2800000, 0x2000),
|
||||
DEFINE_RES_IRQ(159),
|
||||
};
|
||||
|
||||
static void __init kzm9d_init(void)
|
||||
{
|
||||
if (!board_staging_dt_node_available(usbs1_res, ARRAY_SIZE(usbs1_res)))
|
||||
board_staging_gic_setup_xlate("arm,cortex-a9-gic", 32);
|
||||
|
||||
if (!board_staging_dt_node_available(usbs1_res,
|
||||
ARRAY_SIZE(usbs1_res))) {
|
||||
board_staging_gic_fixup_resources(usbs1_res,
|
||||
ARRAY_SIZE(usbs1_res));
|
||||
platform_device_register_simple("emxx_udc", -1, usbs1_res,
|
||||
ARRAY_SIZE(usbs1_res));
|
||||
}
|
||||
}
|
||||
|
||||
board_staging("renesas,kzm9d", kzm9d_init);
|
||||
|
|
|
@ -1247,16 +1247,22 @@ config COMEDI_8254
|
|||
tristate
|
||||
|
||||
config COMEDI_8255
|
||||
tristate "Generic 8255 support"
|
||||
tristate
|
||||
|
||||
config COMEDI_8255_SA
|
||||
tristate "Standalone 8255 support"
|
||||
select COMEDI_8255
|
||||
---help---
|
||||
Enable generic 8255 support.
|
||||
Enable support for 8255 digital I/O as a standalone driver.
|
||||
|
||||
You should enable compilation this driver if you plan to use a board
|
||||
that has an 8255 chip. For multifunction boards, the main driver will
|
||||
configure the 8255 subdevice automatically.
|
||||
that has an 8255 chip at a known I/O base address and there are no
|
||||
other Comedi drivers for the board.
|
||||
|
||||
Note that most PCI based 8255 boards use the 8255_pci driver as a
|
||||
wrapper around this driver.
|
||||
Note that Comedi drivers for most multi-function boards incorporating
|
||||
an 8255 chip use the 'comedi_8255' module. Most PCI-based 8255
|
||||
boards use the 8255_pci driver as a wrapper around the 'comedi_8255'
|
||||
module.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called 8255.
|
||||
|
|
|
@ -217,7 +217,7 @@
|
|||
#define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */
|
||||
#define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */
|
||||
#define SDF_PACKED 0x20000000 /* subdevice can do packed DIO */
|
||||
/* re recyle these flags for PWM */
|
||||
/* re recycle these flags for PWM */
|
||||
#define SDF_PWM_COUNTER SDF_MODE0 /* PWM can automatically switch off */
|
||||
#define SDF_PWM_HBRIDGE SDF_MODE1 /* PWM is signed (H-bridge) */
|
||||
|
||||
|
|
|
@ -43,6 +43,23 @@
|
|||
|
||||
#include "comedi_internal.h"
|
||||
|
||||
/**
|
||||
* comedi_subdevice "runflags"
|
||||
* @COMEDI_SRF_RT: DEPRECATED: command is running real-time
|
||||
* @COMEDI_SRF_ERROR: indicates an COMEDI_CB_ERROR event has occurred
|
||||
* since the last command was started
|
||||
* @COMEDI_SRF_RUNNING: command is running
|
||||
* @COMEDI_SRF_FREE_SPRIV: free s->private on detach
|
||||
*
|
||||
* @COMEDI_SRF_BUSY_MASK: runflags that indicate the subdevice is "busy"
|
||||
*/
|
||||
#define COMEDI_SRF_RT BIT(1)
|
||||
#define COMEDI_SRF_ERROR BIT(2)
|
||||
#define COMEDI_SRF_RUNNING BIT(27)
|
||||
#define COMEDI_SRF_FREE_SPRIV BIT(31)
|
||||
|
||||
#define COMEDI_SRF_BUSY_MASK (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING)
|
||||
|
||||
/**
|
||||
* struct comedi_file - per-file private data for comedi device
|
||||
* @dev: comedi_device struct
|
||||
|
@ -679,8 +696,28 @@ static bool comedi_is_subdevice_idle(struct comedi_subdevice *s)
|
|||
return !(runflags & COMEDI_SRF_BUSY_MASK);
|
||||
}
|
||||
|
||||
bool comedi_can_auto_free_spriv(struct comedi_subdevice *s)
|
||||
{
|
||||
unsigned runflags = __comedi_get_subdevice_runflags(s);
|
||||
|
||||
return runflags & COMEDI_SRF_FREE_SPRIV;
|
||||
}
|
||||
|
||||
/**
|
||||
* comedi_alloc_spriv() - Allocate memory for the subdevice private data.
|
||||
* comedi_set_spriv_auto_free - mark subdevice private data as freeable
|
||||
* @s: comedi_subdevice struct
|
||||
*
|
||||
* Mark the subdevice as having a pointer to private data that can be
|
||||
* automatically freed by the comedi core during the detach.
|
||||
*/
|
||||
void comedi_set_spriv_auto_free(struct comedi_subdevice *s)
|
||||
{
|
||||
__comedi_set_subdevice_runflags(s, COMEDI_SRF_FREE_SPRIV);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_set_spriv_auto_free);
|
||||
|
||||
/**
|
||||
* comedi_alloc_spriv - Allocate memory for the subdevice private data.
|
||||
* @s: comedi_subdevice struct
|
||||
* @size: size of the memory to allocate
|
||||
*
|
||||
|
@ -691,7 +728,7 @@ void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size)
|
|||
{
|
||||
s->private = kzalloc(size, GFP_KERNEL);
|
||||
if (s->private)
|
||||
s->runflags |= COMEDI_SRF_FREE_SPRIV;
|
||||
comedi_set_spriv_auto_free(s);
|
||||
return s->private;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_alloc_spriv);
|
||||
|
@ -1048,11 +1085,6 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
|
|||
if (put_user(x, it.rangelist + i))
|
||||
return -EFAULT;
|
||||
}
|
||||
#if 0
|
||||
if (copy_to_user(it.rangelist, s->range_type_list,
|
||||
s->n_chan * sizeof(unsigned int)))
|
||||
return -EFAULT;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1725,7 +1757,7 @@ cleanup:
|
|||
|
||||
/*
|
||||
* COMEDI_CMDTEST ioctl
|
||||
* asynchronous aquisition command testing
|
||||
* asynchronous acquisition command testing
|
||||
*
|
||||
* arg:
|
||||
* pointer to comedi_cmd structure
|
||||
|
|
|
@ -33,6 +33,7 @@ struct comedi_buf_map *comedi_buf_map_from_subdev_get(
|
|||
struct comedi_subdevice *s);
|
||||
unsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s);
|
||||
void comedi_device_cancel_all(struct comedi_device *dev);
|
||||
bool comedi_can_auto_free_spriv(struct comedi_subdevice *s);
|
||||
|
||||
extern unsigned int comedi_default_buf_size_kb;
|
||||
extern unsigned int comedi_default_buf_maxsize_kb;
|
||||
|
|
|
@ -227,12 +227,12 @@ struct comedi_async {
|
|||
* @COMEDI_CB_ERROR_MASK: events that indicate an error has occurred
|
||||
* @COMEDI_CB_CANCEL_MASK: events that will cancel an async command
|
||||
*/
|
||||
#define COMEDI_CB_EOS (1 << 0)
|
||||
#define COMEDI_CB_EOA (1 << 1)
|
||||
#define COMEDI_CB_BLOCK (1 << 2)
|
||||
#define COMEDI_CB_EOBUF (1 << 3)
|
||||
#define COMEDI_CB_ERROR (1 << 4)
|
||||
#define COMEDI_CB_OVERFLOW (1 << 5)
|
||||
#define COMEDI_CB_EOS BIT(0)
|
||||
#define COMEDI_CB_EOA BIT(1)
|
||||
#define COMEDI_CB_BLOCK BIT(2)
|
||||
#define COMEDI_CB_EOBUF BIT(3)
|
||||
#define COMEDI_CB_ERROR BIT(4)
|
||||
#define COMEDI_CB_OVERFLOW BIT(5)
|
||||
|
||||
#define COMEDI_CB_ERROR_MASK (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)
|
||||
#define COMEDI_CB_CANCEL_MASK (COMEDI_CB_EOA | COMEDI_CB_ERROR_MASK)
|
||||
|
@ -303,26 +303,10 @@ void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s);
|
|||
struct comedi_device *comedi_dev_get_from_minor(unsigned minor);
|
||||
int comedi_dev_put(struct comedi_device *dev);
|
||||
|
||||
/**
|
||||
* comedi_subdevice "runflags"
|
||||
* @COMEDI_SRF_RT: DEPRECATED: command is running real-time
|
||||
* @COMEDI_SRF_ERROR: indicates an COMEDI_CB_ERROR event has occurred
|
||||
* since the last command was started
|
||||
* @COMEDI_SRF_RUNNING: command is running
|
||||
* @COMEDI_SRF_FREE_SPRIV: free s->private on detach
|
||||
*
|
||||
* @COMEDI_SRF_BUSY_MASK: runflags that indicate the subdevice is "busy"
|
||||
*/
|
||||
#define COMEDI_SRF_RT BIT(1)
|
||||
#define COMEDI_SRF_ERROR BIT(2)
|
||||
#define COMEDI_SRF_RUNNING BIT(27)
|
||||
#define COMEDI_SRF_FREE_SPRIV BIT(31)
|
||||
|
||||
#define COMEDI_SRF_BUSY_MASK (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING)
|
||||
|
||||
bool comedi_is_subdevice_running(struct comedi_subdevice *s);
|
||||
|
||||
void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size);
|
||||
void comedi_set_spriv_auto_free(struct comedi_subdevice *s);
|
||||
|
||||
int comedi_check_chanlist(struct comedi_subdevice *s,
|
||||
int n,
|
||||
|
|
|
@ -125,7 +125,7 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
|
|||
if (dev->subdevices) {
|
||||
for (i = 0; i < dev->n_subdevices; i++) {
|
||||
s = &dev->subdevices[i];
|
||||
if (s->runflags & COMEDI_SRF_FREE_SPRIV)
|
||||
if (comedi_can_auto_free_spriv(s))
|
||||
kfree(s->private);
|
||||
comedi_free_subdevice_minor(s);
|
||||
if (s->async) {
|
||||
|
|
|
@ -53,221 +53,6 @@
|
|||
|
||||
#include "8255.h"
|
||||
|
||||
struct subdev_8255_private {
|
||||
unsigned long regbase;
|
||||
int (*io)(struct comedi_device *, int, int, int, unsigned long);
|
||||
};
|
||||
|
||||
static int subdev_8255_io(struct comedi_device *dev,
|
||||
int dir, int port, int data, unsigned long regbase)
|
||||
{
|
||||
if (dir) {
|
||||
outb(data, dev->iobase + regbase + port);
|
||||
return 0;
|
||||
}
|
||||
return inb(dev->iobase + regbase + port);
|
||||
}
|
||||
|
||||
static int subdev_8255_mmio(struct comedi_device *dev,
|
||||
int dir, int port, int data, unsigned long regbase)
|
||||
{
|
||||
if (dir) {
|
||||
writeb(data, dev->mmio + regbase + port);
|
||||
return 0;
|
||||
}
|
||||
return readb(dev->mmio + regbase + port);
|
||||
}
|
||||
|
||||
static int subdev_8255_insn(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct subdev_8255_private *spriv = s->private;
|
||||
unsigned long regbase = spriv->regbase;
|
||||
unsigned int mask;
|
||||
unsigned int v;
|
||||
|
||||
mask = comedi_dio_update_state(s, data);
|
||||
if (mask) {
|
||||
if (mask & 0xff)
|
||||
spriv->io(dev, 1, I8255_DATA_A_REG,
|
||||
s->state & 0xff, regbase);
|
||||
if (mask & 0xff00)
|
||||
spriv->io(dev, 1, I8255_DATA_B_REG,
|
||||
(s->state >> 8) & 0xff, regbase);
|
||||
if (mask & 0xff0000)
|
||||
spriv->io(dev, 1, I8255_DATA_C_REG,
|
||||
(s->state >> 16) & 0xff, regbase);
|
||||
}
|
||||
|
||||
v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, regbase);
|
||||
v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, regbase) << 8);
|
||||
v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, regbase) << 16);
|
||||
|
||||
data[1] = v;
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static void subdev_8255_do_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s)
|
||||
{
|
||||
struct subdev_8255_private *spriv = s->private;
|
||||
unsigned long regbase = spriv->regbase;
|
||||
int config;
|
||||
|
||||
config = I8255_CTRL_CW;
|
||||
/* 1 in io_bits indicates output, 1 in config indicates input */
|
||||
if (!(s->io_bits & 0x0000ff))
|
||||
config |= I8255_CTRL_A_IO;
|
||||
if (!(s->io_bits & 0x00ff00))
|
||||
config |= I8255_CTRL_B_IO;
|
||||
if (!(s->io_bits & 0x0f0000))
|
||||
config |= I8255_CTRL_C_LO_IO;
|
||||
if (!(s->io_bits & 0xf00000))
|
||||
config |= I8255_CTRL_C_HI_IO;
|
||||
|
||||
spriv->io(dev, 1, I8255_CTRL_REG, config, regbase);
|
||||
}
|
||||
|
||||
static int subdev_8255_insn_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
unsigned int chan = CR_CHAN(insn->chanspec);
|
||||
unsigned int mask;
|
||||
int ret;
|
||||
|
||||
if (chan < 8)
|
||||
mask = 0x0000ff;
|
||||
else if (chan < 16)
|
||||
mask = 0x00ff00;
|
||||
else if (chan < 20)
|
||||
mask = 0x0f0000;
|
||||
else
|
||||
mask = 0xf00000;
|
||||
|
||||
ret = comedi_dio_insn_config(dev, s, insn, data, mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
subdev_8255_do_config(dev, s);
|
||||
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static int __subdev_8255_init(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
int (*io)(struct comedi_device *,
|
||||
int, int, int, unsigned long),
|
||||
unsigned long regbase,
|
||||
bool is_mmio)
|
||||
{
|
||||
struct subdev_8255_private *spriv;
|
||||
|
||||
spriv = comedi_alloc_spriv(s, sizeof(*spriv));
|
||||
if (!spriv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (io)
|
||||
spriv->io = io;
|
||||
else if (is_mmio)
|
||||
spriv->io = subdev_8255_mmio;
|
||||
else
|
||||
spriv->io = subdev_8255_io;
|
||||
spriv->regbase = regbase;
|
||||
|
||||
s->type = COMEDI_SUBD_DIO;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
s->n_chan = 24;
|
||||
s->range_table = &range_digital;
|
||||
s->maxdata = 1;
|
||||
s->insn_bits = subdev_8255_insn;
|
||||
s->insn_config = subdev_8255_insn_config;
|
||||
|
||||
subdev_8255_do_config(dev, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* subdev_8255_init - initialize DIO subdevice for driving I/O mapped 8255
|
||||
* @dev: comedi device owning subdevice
|
||||
* @s: comedi subdevice to initialize
|
||||
* @io: (optional) register I/O call-back function
|
||||
* @regbase: offset of 8255 registers from dev->iobase, or call-back context
|
||||
*
|
||||
* Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
|
||||
*
|
||||
* If the optional I/O call-back function is provided, its prototype is of
|
||||
* the following form:
|
||||
*
|
||||
* int my_8255_callback(struct comedi_device *dev,
|
||||
* struct comedi_subdevice *s, int dir, int port,
|
||||
* int data, unsigned long regbase);
|
||||
*
|
||||
* where 'dev', 's', and 'regbase' match the values passed to this function,
|
||||
* 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
|
||||
* is the direction (0 for read, 1 for write) and 'data' is the value to be
|
||||
* written. It should return 0 if writing or the value read if reading.
|
||||
*
|
||||
* If the optional I/O call-back function is not provided, an internal
|
||||
* call-back function is used which uses consecutive I/O port addresses
|
||||
* starting at dev->iobase + regbase.
|
||||
*
|
||||
* Return: -ENOMEM if failed to allocate memory, zero on success.
|
||||
*/
|
||||
int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
int (*io)(struct comedi_device *,
|
||||
int, int, int, unsigned long),
|
||||
unsigned long regbase)
|
||||
{
|
||||
return __subdev_8255_init(dev, s, io, regbase, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subdev_8255_init);
|
||||
|
||||
/**
|
||||
* subdev_8255_mm_init - initialize DIO subdevice for driving mmio-mapped 8255
|
||||
* @dev: comedi device owning subdevice
|
||||
* @s: comedi subdevice to initialize
|
||||
* @io: (optional) register I/O call-back function
|
||||
* @regbase: offset of 8255 registers from dev->mmio, or call-back context
|
||||
*
|
||||
* Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
|
||||
*
|
||||
* If the optional I/O call-back function is provided, its prototype is of
|
||||
* the following form:
|
||||
*
|
||||
* int my_8255_callback(struct comedi_device *dev,
|
||||
* struct comedi_subdevice *s, int dir, int port,
|
||||
* int data, unsigned long regbase);
|
||||
*
|
||||
* where 'dev', 's', and 'regbase' match the values passed to this function,
|
||||
* 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
|
||||
* is the direction (0 for read, 1 for write) and 'data' is the value to be
|
||||
* written. It should return 0 if writing or the value read if reading.
|
||||
*
|
||||
* If the optional I/O call-back function is not provided, an internal
|
||||
* call-back function is used which uses consecutive MMIO virtual addresses
|
||||
* starting at dev->mmio + regbase.
|
||||
*
|
||||
* Return: -ENOMEM if failed to allocate memory, zero on success.
|
||||
*/
|
||||
int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
int (*io)(struct comedi_device *,
|
||||
int, int, int, unsigned long),
|
||||
unsigned long regbase)
|
||||
{
|
||||
return __subdev_8255_init(dev, s, io, regbase, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(subdev_8255_mm_init);
|
||||
|
||||
/*
|
||||
* Start of the 8255 standalone device
|
||||
*/
|
||||
|
||||
static int dev_8255_attach(struct comedi_device *dev,
|
||||
struct comedi_devconfig *it)
|
||||
{
|
||||
|
@ -306,8 +91,15 @@ static int dev_8255_attach(struct comedi_device *dev,
|
|||
s->type = COMEDI_SUBD_UNUSED;
|
||||
} else {
|
||||
ret = subdev_8255_init(dev, s, NULL, iobase);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
/*
|
||||
* Release the I/O port region here, as the
|
||||
* "detach" handler cannot find it.
|
||||
*/
|
||||
release_region(iobase, I8255_SIZE);
|
||||
s->type = COMEDI_SUBD_UNUSED;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,14 +109,14 @@ static int dev_8255_attach(struct comedi_device *dev,
|
|||
static void dev_8255_detach(struct comedi_device *dev)
|
||||
{
|
||||
struct comedi_subdevice *s;
|
||||
struct subdev_8255_private *spriv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->n_subdevices; i++) {
|
||||
s = &dev->subdevices[i];
|
||||
if (s->type != COMEDI_SUBD_UNUSED) {
|
||||
spriv = s->private;
|
||||
release_region(spriv->regbase, I8255_SIZE);
|
||||
unsigned long regbase = subdev_8255_regbase(s);
|
||||
|
||||
release_region(regbase, I8255_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -338,5 +130,5 @@ static struct comedi_driver dev_8255_driver = {
|
|||
module_comedi_driver(dev_8255_driver);
|
||||
|
||||
MODULE_AUTHOR("Comedi http://www.comedi.org");
|
||||
MODULE_DESCRIPTION("Comedi low-level driver");
|
||||
MODULE_DESCRIPTION("Comedi driver for standalone 8255 devices");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#ifndef _8255_H
|
||||
#define _8255_H
|
||||
|
||||
#include "../comedidev.h"
|
||||
|
||||
#define I8255_SIZE 0x04
|
||||
|
||||
#define I8255_DATA_A_REG 0x00
|
||||
|
@ -35,14 +33,19 @@
|
|||
#define I8255_CTRL_A_MODE(x) ((x) << 5)
|
||||
#define I8255_CTRL_CW (1 << 7)
|
||||
|
||||
int subdev_8255_init(struct comedi_device *, struct comedi_subdevice *,
|
||||
int (*io)(struct comedi_device *,
|
||||
int, int, int, unsigned long),
|
||||
struct comedi_device;
|
||||
struct comedi_subdevice;
|
||||
|
||||
int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
int (*io)(struct comedi_device *dev, int dir, int port,
|
||||
int data, unsigned long regbase),
|
||||
unsigned long regbase);
|
||||
|
||||
int subdev_8255_mm_init(struct comedi_device *, struct comedi_subdevice *,
|
||||
int (*io)(struct comedi_device *,
|
||||
int, int, int, unsigned long),
|
||||
int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
int (*io)(struct comedi_device *dev, int dir, int port,
|
||||
int data, unsigned long regbase),
|
||||
unsigned long regbase);
|
||||
|
||||
unsigned long subdev_8255_regbase(struct comedi_subdevice *s);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -139,7 +139,8 @@ obj-$(CONFIG_COMEDI_NI_TIOCMD) += ni_tiocmd.o
|
|||
obj-$(CONFIG_COMEDI_NI_LABPC) += ni_labpc_common.o
|
||||
obj-$(CONFIG_COMEDI_NI_LABPC_ISADMA) += ni_labpc_isadma.o
|
||||
|
||||
obj-$(CONFIG_COMEDI_8255) += 8255.o
|
||||
obj-$(CONFIG_COMEDI_8255) += comedi_8255.o
|
||||
obj-$(CONFIG_COMEDI_8255_SA) += 8255.o
|
||||
obj-$(CONFIG_COMEDI_AMPLC_DIO200) += amplc_dio200_common.o
|
||||
obj-$(CONFIG_COMEDI_AMPLC_PC236) += amplc_pc236_common.o
|
||||
obj-$(CONFIG_COMEDI_DAS08) += das08.o
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue