forked from Minki/linux
Second round of IIO new drivers, functionality and cleanups for the 3.20 cycle.
New drivers and device support: * Capella cm3232 ambient light sensor driver. * Freescale MMA9553 pedometer and activity monitor. Note this involved a refactor of the mma9551 driver to pull out shared elements. * Samsung sensor hub (as used in the galaxy gear 2 watch) Core support and initial drivers (gyro and accelerometer) more to follow. An additional fix was applied on top of this for a build issue thrown up by the autobuilders on some platforms. * Cosmic Circuits 10001 ADC driver * Qualcomm SPMI PMIC voltage ADC driver (current adc merged a while back). * Add binding for AK8963 (in capitals) as unfortunately there are bios' out there not using lower case. New functionality * Add newe operating mode to the core to allow for non triggered software buffers. This is mostly semantics as previously drivers just claimed they had a hardware buffer (when they didn't). * Add distance channel type. * Add energy channel type. * Add velocity channel type and IIO_MOD_ROOT_SUM_SQUARED (i.e. speed when our channel type is velocity). * Add _debounce_count and _debounce_time filter attributes. Only really make sense for counting types of measurements. First use is for avoiding miss detection of steps prior to walking. * Add change event type. This replaces the briefly present INSTANCE type (which hadn't gained any users). It is more generic as it allows for events say every 10 steps rather than every step. * Add _calibweight attributes to the ABI (and core support). Used by activity monitors to estimate energy use. Can imagine there will be other uses for this one. Driver new functionality * mma9551 gains runtime pm support. * hid-sensors gain PM support. Cleanup * Change calibheight unit to m from cm. As there are no prior users and this was inconsistent with other distance units, it makes sense to fix it before hte mma9553 driver which uses it. * mpu6050 cleanups and devm_ use. * as3935 switch over to PM ops. * Fix a few format strings for signed vs unsigned. * Fix tcs3414 missindentation * Typo in industrialio-event * Stop requiring IIO_TRIGGER for IIO_KFIFO_BUF as we have drivers that don't need it. No one is quite sure why that dependency was there and it seems to not matter. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJUzNAYAAoJEFSFNJnE9BaI3UQQAJPp+6rDEk9UpWwOBfYdbyS4 sQIwXF4eDk6RLWMKYfLi3T0cMISBR7ysItJTWfkVm02uq7YyRExubMqm5OZ1msKl dPyrOACd58IOW614VVNsIpqGhLUvEBL4SJdTNd6YQv8kCb80Hoe/JZl5fL33/weZ jU1vUzTLEF4XsO6jF4pYA3/jg5DpgLk1NuRbYkl0aUQks9hMiKu0sLWMJbWjLYfp bP2aSriEb4PCpj3lVvRRs8nGAQ3pd21fTEkoxLIDgFVA5XDPhZKSRlOgtyPYSv9q e5wwCuXiEZbV3EG6DONg6Yzrd/IvP499xDKs+tY7M41pQ0ai+BmCSrxOJT46VK6k p4kDgfxXDpv7cbON7gOkKIruWF1ix4IM0WPz4IhqWOT+w7W9C03TPQO4ZkhsfU2c N1PjnN8OKUd629iz75Mj7FD+fK5Vkqee75Vizt2p4j3O1DDNYlcduCqSvYGrCXRb cAHpjJMEdG86V/0NReO98zudVYZ68j73Wfn1a2aZv7/Np46CnOYOeiz/cLYhfvkX HpoUC71k7mrNwPFzc6+Jxl1qSvrE3nNUVC+VazMX+DOp4RSjAm+acKFIUc97AM0X 3DgsXDF6iwNKa11QeSsQUvReFXqd7Ulo+Wics+pi7seiuQ4JYPcw3GtTHh1d7ee4 Ta5BzMt6qxjvHBEkQbJo =5x/n -----END PGP SIGNATURE----- Merge tag 'iio-for-3.20b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-testing Jonathan writes: Second round of IIO new drivers, functionality and cleanups for the 3.20 cycle. New drivers and device support: * Capella cm3232 ambient light sensor driver. * Freescale MMA9553 pedometer and activity monitor. Note this involved a refactor of the mma9551 driver to pull out shared elements. * Samsung sensor hub (as used in the galaxy gear 2 watch) Core support and initial drivers (gyro and accelerometer) more to follow. An additional fix was applied on top of this for a build issue thrown up by the autobuilders on some platforms. * Cosmic Circuits 10001 ADC driver * Qualcomm SPMI PMIC voltage ADC driver (current adc merged a while back). * Add binding for AK8963 (in capitals) as unfortunately there are bios' out there not using lower case. New functionality * Add newe operating mode to the core to allow for non triggered software buffers. This is mostly semantics as previously drivers just claimed they had a hardware buffer (when they didn't). * Add distance channel type. * Add energy channel type. * Add velocity channel type and IIO_MOD_ROOT_SUM_SQUARED (i.e. speed when our channel type is velocity). * Add _debounce_count and _debounce_time filter attributes. Only really make sense for counting types of measurements. First use is for avoiding miss detection of steps prior to walking. * Add change event type. This replaces the briefly present INSTANCE type (which hadn't gained any users). It is more generic as it allows for events say every 10 steps rather than every step. * Add _calibweight attributes to the ABI (and core support). Used by activity monitors to estimate energy use. Can imagine there will be other uses for this one. Driver new functionality * mma9551 gains runtime pm support. * hid-sensors gain PM support. Cleanup * Change calibheight unit to m from cm. As there are no prior users and this was inconsistent with other distance units, it makes sense to fix it before hte mma9553 driver which uses it. * mpu6050 cleanups and devm_ use. * as3935 switch over to PM ops. * Fix a few format strings for signed vs unsigned. * Fix tcs3414 missindentation * Typo in industrialio-event * Stop requiring IIO_TRIGGER for IIO_KFIFO_BUF as we have drivers that don't need it. No one is quite sure why that dependency was there and it seems to not matter.
This commit is contained in:
commit
89c468263b
@ -282,6 +282,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_current_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
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_distance_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
|
||||
@ -293,6 +295,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_rot_from_north_true_tilt_comp_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -340,14 +343,44 @@ Description:
|
||||
production inaccuracies). If shared across all channels,
|
||||
<type>_calibscale is used.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_steps_calibheight
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Gender of the user (e.g.: male, female) used by some pedometers
|
||||
to compute the stride length, distance, speed and activity
|
||||
type.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Lists all available gender values (e.g.: male, female).
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
|
||||
KernelVersion: 3.19
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Height of the user (in centimeters) used by some pedometers
|
||||
Height of the user (in meters) used by some pedometers
|
||||
to compute the stride length, distance, speed and activity
|
||||
type.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibweight
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Weight of the user (in kg). It is needed by some pedometers
|
||||
to compute the calories burnt by the user.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
|
||||
What: /sys/.../iio:deviceX/in_voltageX_scale_available
|
||||
What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available
|
||||
@ -808,6 +841,14 @@ What: /sys/.../events/in_tempY_roc_falling_period
|
||||
What: /sys/.../events/in_accel_x&y&z_mag_falling_period
|
||||
What: /sys/.../events/in_intensity0_thresh_period
|
||||
What: /sys/.../events/in_proximity0_thresh_period
|
||||
What: /sys/.../events/in_activity_still_thresh_rising_period
|
||||
What: /sys/.../events/in_activity_still_thresh_falling_period
|
||||
What: /sys/.../events/in_activity_walking_thresh_rising_period
|
||||
What: /sys/.../events/in_activity_walking_thresh_falling_period
|
||||
What: /sys/.../events/in_activity_jogging_thresh_rising_period
|
||||
What: /sys/.../events/in_activity_jogging_thresh_falling_period
|
||||
What: /sys/.../events/in_activity_running_thresh_rising_period
|
||||
What: /sys/.../events/in_activity_running_thresh_falling_period
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -881,12 +922,24 @@ Description:
|
||||
number or direction is not specified, applies to all channels of
|
||||
this type.
|
||||
|
||||
What: /sys/.../events/in_steps_instance_en
|
||||
KernelVersion: 3.19
|
||||
What: /sys/.../events/in_steps_change_en
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Enables or disables step detection. Each time the user takes a step an
|
||||
event of this type will be generated.
|
||||
Event generated when channel passes a threshold on the absolute
|
||||
change in value. E.g. for steps: a step change event is
|
||||
generated each time the user takes N steps, where N is set using
|
||||
in_steps_change_value.
|
||||
|
||||
What: /sys/.../events/in_steps_change_value
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies the value of change threshold that the
|
||||
device is comparing against for the events enabled by
|
||||
<type>[Y][_name]_roc[_rising|falling|]_en. E.g. for steps:
|
||||
if set to 3, a step change event will be generated every 3
|
||||
steps.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/trigger/current_trigger
|
||||
KernelVersion: 2.6.35
|
||||
@ -1049,6 +1102,24 @@ Description:
|
||||
For a list of available output power modes read
|
||||
in_accel_power_mode_available.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_energy_input
|
||||
What: /sys/.../iio:deviceX/in_energy_raw
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to read the energy value reported by the
|
||||
device (e.g.: human activity sensors report energy burnt by the
|
||||
user). Units after application of scale are Joules.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_distance_input
|
||||
What: /sys/.../iio:deviceX/in_distance_raw
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to read the distance covered by the user
|
||||
since the last reboot while activated. Units after application
|
||||
of scale are meters.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/store_eeprom
|
||||
KernelVersion: 3.4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
@ -1102,6 +1173,12 @@ Description:
|
||||
This attribute is used to get/set the integration time in
|
||||
seconds.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Number of seconds in which to compute speed.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
|
||||
KernelVersion: 3.15
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
@ -1130,13 +1207,17 @@ Description:
|
||||
present, output should be considered as processed with the
|
||||
unit in milliamps.
|
||||
|
||||
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
|
||||
What: /sys/.../iio:deviceX/in_steps_en
|
||||
KernelVersion: 3.19
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Activates the step counter. After activation, the number of steps
|
||||
taken by the user will be counted in hardware and exported through
|
||||
in_steps_input.
|
||||
Activates a device feature that runs in firmware/hardware.
|
||||
E.g. for steps: the pedometer saves power while not used;
|
||||
when activated, it will count the steps taken by the user in
|
||||
firmware and export them through in_steps_input.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_steps_input
|
||||
KernelVersion: 3.19
|
||||
@ -1144,3 +1225,27 @@ Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to read the number of steps taken by the user
|
||||
since the last reboot while activated.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_input
|
||||
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_raw
|
||||
KernelVersion: 3.19
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to read the current speed value of the
|
||||
user (which is the norm or magnitude of the velocity vector).
|
||||
Units after application of scale are m/s.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_steps_debounce_count
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies the number of steps that must occur within
|
||||
in_steps_filter_debounce_time for the pedometer to decide the
|
||||
consumer is making steps.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_steps_debounce_time
|
||||
KernelVersion: 3.20
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies number of seconds in which we compute the steps
|
||||
that occur in order to decide if the consumer is making steps.
|
||||
|
@ -34,6 +34,7 @@ atmel,24c512 i2c serial eeprom (24cxx)
|
||||
atmel,24c1024 i2c serial eeprom (24cxx)
|
||||
atmel,at97sc3204t i2c trusted platform module (TPM)
|
||||
capella,cm32181 CM32181: Ambient Light Sensor
|
||||
capella,cm3232 CM3232: Ambient Light Sensor
|
||||
catalyst,24c32 i2c serial eeprom
|
||||
cirrus,cs42l51 Cirrus Logic CS42L51 audio codec
|
||||
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
|
||||
|
22
Documentation/devicetree/bindings/iio/adc/cc10001_adc.txt
Normal file
22
Documentation/devicetree/bindings/iio/adc/cc10001_adc.txt
Normal file
@ -0,0 +1,22 @@
|
||||
* Cosmic Circuits - Analog to Digital Converter (CC-10001-ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "cosmic,10001-adc"
|
||||
- reg: Should contain adc registers location and length.
|
||||
- clock-names: Should contain "adc".
|
||||
- clocks: Should contain a clock specifier for each entry in clock-names
|
||||
- vref-supply: The regulator supply ADC reference voltage.
|
||||
|
||||
Optional properties:
|
||||
- adc-reserved-channels: Bitmask of reserved channels,
|
||||
i.e. channels that cannot be used by the OS.
|
||||
|
||||
Example:
|
||||
adc: adc@18101600 {
|
||||
compatible = "cosmic,10001-adc";
|
||||
reg = <0x18101600 0x24>;
|
||||
adc-reserved-channels = <0x2>;
|
||||
clocks = <&adc_clk>;
|
||||
clock-names = "adc";
|
||||
vref-supply = <®_1v8>;
|
||||
};
|
129
Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
Normal file
129
Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
Normal file
@ -0,0 +1,129 @@
|
||||
Qualcomm's SPMI PMIC voltage ADC
|
||||
|
||||
SPMI PMIC voltage ADC (VADC) provides interface to clients to read
|
||||
voltage. The VADC is a 15-bit sigma-delta ADC.
|
||||
|
||||
VADC node:
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Should contain "qcom,spmi-vadc".
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: VADC base address and length in the SPMI PMIC register map.
|
||||
|
||||
- #address-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Must be one. Child node 'reg' property should define ADC
|
||||
channel number.
|
||||
|
||||
- #size-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Must be zero.
|
||||
|
||||
- #io-channel-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Must be one. For details about IIO bindings see:
|
||||
Documentation/devicetree/bindings/iio/iio-bindings.txt
|
||||
|
||||
- interrupts:
|
||||
Usage: optional
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: End of conversion interrupt.
|
||||
|
||||
Channel node properties:
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: ADC channel number.
|
||||
See include/dt-bindings/iio/qcom,spmi-vadc.h
|
||||
|
||||
- qcom,decimation:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: This parameter is used to decrease ADC sampling rate.
|
||||
Quicker measurements can be made by reducing decimation ratio.
|
||||
Valid values are 512, 1024, 2048, 4096.
|
||||
If property is not found, default value of 512 will be used.
|
||||
|
||||
- qcom,pre-scaling:
|
||||
Usage: optional
|
||||
Value type: <u32 array>
|
||||
Definition: Used for scaling the channel input signal before the signal is
|
||||
fed to VADC. The configuration for this node is to know the
|
||||
pre-determined ratio and use it for post scaling. Select one from
|
||||
the following options.
|
||||
<1 1>, <1 3>, <1 4>, <1 6>, <1 20>, <1 8>, <10 81>, <1 10>
|
||||
If property is not found default value depending on chip will be used.
|
||||
|
||||
- qcom,ratiometric:
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: Channel calibration type. If this property is specified
|
||||
VADC will use the VDD reference (1.8V) and GND for channel
|
||||
calibration. If property is not found, channel will be
|
||||
calibrated with 0.625V and 1.25V reference channels, also
|
||||
known as absolute calibration.
|
||||
|
||||
- qcom,hw-settle-time:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Time between AMUX getting configured and the ADC starting
|
||||
conversion. Delay = 100us * (value) for value < 11, and
|
||||
2ms * (value - 10) otherwise.
|
||||
Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800,
|
||||
900 us and 1, 2, 4, 6, 8, 10 ms
|
||||
If property is not found, channel will use 0us.
|
||||
|
||||
- qcom,avg-samples:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Number of samples to be used for measurement.
|
||||
Averaging provides the option to obtain a single measurement
|
||||
from the ADC that is an average of multiple samples. The value
|
||||
selected is 2^(value).
|
||||
Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
|
||||
If property is not found, 1 sample will be used.
|
||||
|
||||
NOTE:
|
||||
|
||||
Following channels, also known as reference point channels, are used for
|
||||
result calibration and their channel configuration nodes should be defined:
|
||||
VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
|
||||
VADC_GND_REF and VADC_VDD_VADC.
|
||||
|
||||
Example:
|
||||
|
||||
/* VADC node */
|
||||
pmic_vadc: vadc@3100 {
|
||||
compatible = "qcom,spmi-vadc";
|
||||
reg = <0x3100 0x100>;
|
||||
interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
|
||||
/* Channel node */
|
||||
usb_id_nopull {
|
||||
reg = <VADC_LR_MUX10_USB_ID>;
|
||||
qcom,decimation = <512>;
|
||||
qcom,ratiometric;
|
||||
qcom,hw-settle-time = <200>;
|
||||
qcom,avg-samples = <1>;
|
||||
qcom,pre-scaling = <1 3>;
|
||||
};
|
||||
};
|
||||
|
||||
/* IIO client node */
|
||||
usb {
|
||||
io-channels = <&pmic_vadc VADC_LR_MUX10_USB_ID>;
|
||||
io-channel-names = "vadc";
|
||||
};
|
25
Documentation/devicetree/bindings/iio/sensorhub.txt
Normal file
25
Documentation/devicetree/bindings/iio/sensorhub.txt
Normal file
@ -0,0 +1,25 @@
|
||||
Samsung Sensorhub driver
|
||||
|
||||
Sensorhub is a MCU which manages several sensors and also plays the role
|
||||
of a virtual sensor device.
|
||||
|
||||
Required properties:
|
||||
- compatible: "samsung,sensorhub-rinato" or "samsung,sensorhub-thermostat"
|
||||
- spi-max-frequency: max SPI clock frequency
|
||||
- interrupt-parent: interrupt parent
|
||||
- interrupts: communication interrupt
|
||||
- ap-mcu-gpios: [out] ap to sensorhub line - used during communication
|
||||
- mcu-ap-gpios: [in] sensorhub to ap - used during communication
|
||||
- mcu-reset-gpios: [out] sensorhub reset
|
||||
|
||||
Example:
|
||||
|
||||
shub_spi: shub {
|
||||
compatible = "samsung,sensorhub-rinato";
|
||||
spi-max-frequency = <5000000>;
|
||||
interrupt-parent = <&gpx0>;
|
||||
interrupts = <2 0>;
|
||||
ap-mcu-gpios = <&gpx0 0 0>;
|
||||
mcu-ap-gpios = <&gpx0 4 0>;
|
||||
mcu-reset-gpios = <&gpx0 5 0>;
|
||||
};
|
@ -36,6 +36,7 @@ chunghwa Chunghwa Picture Tubes Ltd.
|
||||
cirrus Cirrus Logic, Inc.
|
||||
cnm Chips&Media, Inc.
|
||||
cortina Cortina Systems, Inc.
|
||||
cosmic Cosmic Circuits
|
||||
crystalfontz Crystalfontz America, Inc.
|
||||
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
|
||||
davicom DAVICOM Semiconductor, Inc.
|
||||
|
@ -2379,6 +2379,12 @@ F: security/capability.c
|
||||
F: security/commoncap.c
|
||||
F: kernel/capability.c
|
||||
|
||||
CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
|
||||
M: Kevin Tsai <ktsai@capellamicro.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/light/cm*
|
||||
F: Documentation/devicetree/bindings/i2c/trivial-devices.txt
|
||||
|
||||
CC2520 IEEE-802.15.4 RADIO DRIVER
|
||||
M: Varka Bhadram <varkabhadram@gmail.com>
|
||||
L: linux-wpan@vger.kernel.org
|
||||
|
@ -27,7 +27,6 @@ boolean "IIO callback buffer used for push in-kernel interfaces"
|
||||
usage. That is, those where the data is pushed to the consumer.
|
||||
|
||||
config IIO_KFIFO_BUF
|
||||
select IIO_TRIGGER
|
||||
tristate "Industrial I/O buffering based on kfifo"
|
||||
help
|
||||
A simple fifo based on kfifo. Note that this currently provides
|
||||
|
@ -111,9 +111,14 @@ config KXCJK1013
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called kxcjk-1013.
|
||||
|
||||
config MMA9551_CORE
|
||||
tristate
|
||||
|
||||
config MMA9551
|
||||
tristate "Freescale MMA9551L Intelligent Motion-Sensing Platform Driver"
|
||||
depends on I2C
|
||||
select MMA9551_CORE
|
||||
|
||||
help
|
||||
Say yes here to build support for the Freescale MMA9551L
|
||||
Intelligent Motion-Sensing Platform Driver.
|
||||
@ -121,4 +126,14 @@ config MMA9551
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma9551.
|
||||
|
||||
config MMA9553
|
||||
tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
|
||||
depends on I2C
|
||||
select MMA9551_CORE
|
||||
help
|
||||
Say yes here to build support for the Freescale MMA9553L
|
||||
Intelligent Pedometer Platform Driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma9553.
|
||||
endmenu
|
||||
|
@ -9,7 +9,12 @@ obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
obj-$(CONFIG_MMA8452) += mma8452.o
|
||||
obj-$(CONFIG_MMA9551) += mma9551.o
|
||||
|
||||
obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
|
||||
obj-$(CONFIG_MMA9551) += mma9551.o
|
||||
obj-$(CONFIG_MMA9553) += mma9553.o
|
||||
|
||||
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
|
||||
st_accel-y := st_accel_core.o
|
||||
|
@ -111,19 +111,12 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&accel_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&accel_state->common_attributes, true);
|
||||
msleep_interruptible(poll_value * 2);
|
||||
report_id = accel_state->accel[chan->scan_index].report_id;
|
||||
address = accel_3d_addresses[chan->scan_index];
|
||||
if (report_id >= 0)
|
||||
@ -419,6 +412,7 @@ static struct platform_driver hid_accel_3d_platform_driver = {
|
||||
.id_table = hid_accel_3d_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_accel_3d_probe,
|
||||
.remove = hid_accel_3d_remove,
|
||||
|
@ -22,62 +22,14 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "mma9551_core.h"
|
||||
|
||||
#define MMA9551_DRV_NAME "mma9551"
|
||||
#define MMA9551_IRQ_NAME "mma9551_event"
|
||||
#define MMA9551_GPIO_NAME "mma9551_int"
|
||||
#define MMA9551_GPIO_COUNT 4
|
||||
|
||||
/* Applications IDs */
|
||||
#define MMA9551_APPID_VERSION 0x00
|
||||
#define MMA9551_APPID_GPIO 0x03
|
||||
#define MMA9551_APPID_AFE 0x06
|
||||
#define MMA9551_APPID_TILT 0x0B
|
||||
#define MMA9551_APPID_SLEEP_WAKE 0x12
|
||||
#define MMA9551_APPID_RESET 0x17
|
||||
#define MMA9551_APPID_NONE 0xff
|
||||
|
||||
/* Command masks for mailbox write command */
|
||||
#define MMA9551_CMD_READ_VERSION_INFO 0x00
|
||||
#define MMA9551_CMD_READ_CONFIG 0x10
|
||||
#define MMA9551_CMD_WRITE_CONFIG 0x20
|
||||
#define MMA9551_CMD_READ_STATUS 0x30
|
||||
|
||||
enum mma9551_gpio_pin {
|
||||
mma9551_gpio6 = 0,
|
||||
mma9551_gpio7,
|
||||
mma9551_gpio8,
|
||||
mma9551_gpio9,
|
||||
mma9551_gpio_max = mma9551_gpio9,
|
||||
};
|
||||
|
||||
/* Mailbox read command */
|
||||
#define MMA9551_RESPONSE_COCO BIT(7)
|
||||
|
||||
/* Error-Status codes returned in mailbox read command */
|
||||
#define MMA9551_MCI_ERROR_NONE 0x00
|
||||
#define MMA9551_MCI_ERROR_PARAM 0x04
|
||||
#define MMA9551_MCI_INVALID_COUNT 0x19
|
||||
#define MMA9551_MCI_ERROR_COMMAND 0x1C
|
||||
#define MMA9551_MCI_ERROR_INVALID_LENGTH 0x21
|
||||
#define MMA9551_MCI_ERROR_FIFO_BUSY 0x22
|
||||
#define MMA9551_MCI_ERROR_FIFO_ALLOCATED 0x23
|
||||
#define MMA9551_MCI_ERROR_FIFO_OVERSIZE 0x24
|
||||
|
||||
/* GPIO Application */
|
||||
#define MMA9551_GPIO_POL_MSB 0x08
|
||||
#define MMA9551_GPIO_POL_LSB 0x09
|
||||
|
||||
/* Sleep/Wake application */
|
||||
#define MMA9551_SLEEP_CFG 0x06
|
||||
#define MMA9551_SLEEP_CFG_SNCEN BIT(0)
|
||||
#define MMA9551_SLEEP_CFG_SCHEN BIT(2)
|
||||
|
||||
/* AFE application */
|
||||
#define MMA9551_AFE_X_ACCEL_REG 0x00
|
||||
#define MMA9551_AFE_Y_ACCEL_REG 0x02
|
||||
#define MMA9551_AFE_Z_ACCEL_REG 0x04
|
||||
|
||||
/* Tilt application (inclination in IIO terms). */
|
||||
#define MMA9551_TILT_XZ_ANG_REG 0x00
|
||||
#define MMA9551_TILT_YZ_ANG_REG 0x01
|
||||
@ -90,6 +42,8 @@ enum mma9551_gpio_pin {
|
||||
#define MMA9551_TILT_CFG_REG 0x01
|
||||
#define MMA9551_TILT_ANG_THRESH_MASK GENMASK(3, 0)
|
||||
|
||||
#define MMA9551_DEFAULT_SAMPLE_RATE 122 /* Hz */
|
||||
|
||||
/* Tilt events are mapped to the first three GPIO pins. */
|
||||
enum mma9551_tilt_axis {
|
||||
mma9551_x = 0,
|
||||
@ -97,61 +51,6 @@ enum mma9551_tilt_axis {
|
||||
mma9551_z,
|
||||
};
|
||||
|
||||
/*
|
||||
* A response is composed of:
|
||||
* - control registers: MB0-3
|
||||
* - data registers: MB4-31
|
||||
*
|
||||
* A request is composed of:
|
||||
* - mbox to write to (always 0)
|
||||
* - control registers: MB1-4
|
||||
* - data registers: MB5-31
|
||||
*/
|
||||
#define MMA9551_MAILBOX_CTRL_REGS 4
|
||||
#define MMA9551_MAX_MAILBOX_DATA_REGS 28
|
||||
#define MMA9551_MAILBOX_REGS 32
|
||||
|
||||
#define MMA9551_I2C_READ_RETRIES 5
|
||||
#define MMA9551_I2C_READ_DELAY 50 /* us */
|
||||
|
||||
struct mma9551_mbox_request {
|
||||
u8 start_mbox; /* Always 0. */
|
||||
u8 app_id;
|
||||
/*
|
||||
* See Section 5.3.1 of the MMA955xL Software Reference Manual.
|
||||
*
|
||||
* Bit 7: reserved, always 0
|
||||
* Bits 6-4: command
|
||||
* Bits 3-0: upper bits of register offset
|
||||
*/
|
||||
u8 cmd_off;
|
||||
u8 lower_off;
|
||||
u8 nbytes;
|
||||
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
|
||||
} __packed;
|
||||
|
||||
struct mma9551_mbox_response {
|
||||
u8 app_id;
|
||||
/*
|
||||
* See Section 5.3.3 of the MMA955xL Software Reference Manual.
|
||||
*
|
||||
* Bit 7: COCO
|
||||
* Bits 6-0: Error code.
|
||||
*/
|
||||
u8 coco_err;
|
||||
u8 nbytes;
|
||||
u8 req_bytes;
|
||||
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
|
||||
} __packed;
|
||||
|
||||
struct mma9551_version_info {
|
||||
__be32 device_id;
|
||||
u8 rom_version[2];
|
||||
u8 fw_version[2];
|
||||
u8 hw_version[2];
|
||||
u8 fw_build[2];
|
||||
};
|
||||
|
||||
struct mma9551_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex mutex;
|
||||
@ -159,246 +58,6 @@ struct mma9551_data {
|
||||
int irqs[MMA9551_GPIO_COUNT];
|
||||
};
|
||||
|
||||
static int mma9551_transfer(struct i2c_client *client,
|
||||
u8 app_id, u8 command, u16 offset,
|
||||
u8 *inbytes, int num_inbytes,
|
||||
u8 *outbytes, int num_outbytes)
|
||||
{
|
||||
struct mma9551_mbox_request req;
|
||||
struct mma9551_mbox_response rsp;
|
||||
struct i2c_msg in, out;
|
||||
u8 req_len, err_code;
|
||||
int ret, retries;
|
||||
|
||||
if (offset >= 1 << 12) {
|
||||
dev_err(&client->dev, "register offset too large\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
|
||||
req.start_mbox = 0;
|
||||
req.app_id = app_id;
|
||||
req.cmd_off = command | (offset >> 8);
|
||||
req.lower_off = offset;
|
||||
|
||||
if (command == MMA9551_CMD_WRITE_CONFIG)
|
||||
req.nbytes = num_inbytes;
|
||||
else
|
||||
req.nbytes = num_outbytes;
|
||||
if (num_inbytes)
|
||||
memcpy(req.buf, inbytes, num_inbytes);
|
||||
|
||||
out.addr = client->addr;
|
||||
out.flags = 0;
|
||||
out.len = req_len;
|
||||
out.buf = (u8 *)&req;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &out, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c write failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
retries = MMA9551_I2C_READ_RETRIES;
|
||||
do {
|
||||
udelay(MMA9551_I2C_READ_DELAY);
|
||||
|
||||
in.addr = client->addr;
|
||||
in.flags = I2C_M_RD;
|
||||
in.len = sizeof(rsp);
|
||||
in.buf = (u8 *)&rsp;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &in, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rsp.coco_err & MMA9551_RESPONSE_COCO)
|
||||
break;
|
||||
} while (--retries > 0);
|
||||
|
||||
if (retries == 0) {
|
||||
dev_err(&client->dev,
|
||||
"timed out while waiting for command response\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (rsp.app_id != app_id) {
|
||||
dev_err(&client->dev,
|
||||
"app_id mismatch in response got %02x expected %02x\n",
|
||||
rsp.app_id, app_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
|
||||
if (err_code != MMA9551_MCI_ERROR_NONE) {
|
||||
dev_err(&client->dev, "read returned error %x\n", err_code);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rsp.nbytes != rsp.req_bytes) {
|
||||
dev_err(&client->dev,
|
||||
"output length mismatch got %d expected %d\n",
|
||||
rsp.nbytes, rsp.req_bytes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (num_outbytes)
|
||||
memcpy(outbytes, rsp.buf, num_outbytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 *val)
|
||||
{
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
|
||||
reg, NULL, 0, val, 1);
|
||||
}
|
||||
|
||||
static int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 val)
|
||||
{
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
|
||||
&val, 1, NULL, 0);
|
||||
}
|
||||
|
||||
static int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 *val)
|
||||
{
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
|
||||
reg, NULL, 0, val, 1);
|
||||
}
|
||||
|
||||
static int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
__be16 v;
|
||||
|
||||
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
|
||||
reg, NULL, 0, (u8 *)&v, 2);
|
||||
*val = be16_to_cpu(v);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret;
|
||||
u8 tmp, orig;
|
||||
|
||||
ret = mma9551_read_config_byte(client, app_id, reg, &orig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = orig & ~mask;
|
||||
tmp |= val & mask;
|
||||
|
||||
if (tmp == orig)
|
||||
return 0;
|
||||
|
||||
return mma9551_write_config_byte(client, app_id, reg, tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* The polarity parameter is described in section 6.2.2, page 66, of the
|
||||
* Software Reference Manual. Basically, polarity=0 means the interrupt
|
||||
* line has the same value as the selected bit, while polarity=1 means
|
||||
* the line is inverted.
|
||||
*/
|
||||
static int mma9551_gpio_config(struct i2c_client *client,
|
||||
enum mma9551_gpio_pin pin,
|
||||
u8 app_id, u8 bitnum, int polarity)
|
||||
{
|
||||
u8 reg, pol_mask, pol_val;
|
||||
int ret;
|
||||
|
||||
if (pin > mma9551_gpio_max) {
|
||||
dev_err(&client->dev, "bad GPIO pin\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
|
||||
* 0x03, and so on.
|
||||
*/
|
||||
reg = pin * 2;
|
||||
|
||||
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
|
||||
reg, app_id);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "error setting GPIO app_id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
|
||||
reg + 1, bitnum);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "error setting GPIO bit number\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (pin) {
|
||||
case mma9551_gpio6:
|
||||
reg = MMA9551_GPIO_POL_LSB;
|
||||
pol_mask = 1 << 6;
|
||||
break;
|
||||
case mma9551_gpio7:
|
||||
reg = MMA9551_GPIO_POL_LSB;
|
||||
pol_mask = 1 << 7;
|
||||
break;
|
||||
case mma9551_gpio8:
|
||||
reg = MMA9551_GPIO_POL_MSB;
|
||||
pol_mask = 1 << 0;
|
||||
break;
|
||||
case mma9551_gpio9:
|
||||
reg = MMA9551_GPIO_POL_MSB;
|
||||
pol_mask = 1 << 1;
|
||||
break;
|
||||
}
|
||||
pol_val = polarity ? pol_mask : 0;
|
||||
|
||||
ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
|
||||
pol_mask, pol_val);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "error setting GPIO polarity\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma9551_read_version(struct i2c_client *client)
|
||||
{
|
||||
struct mma9551_version_info info;
|
||||
int ret;
|
||||
|
||||
ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
|
||||
NULL, 0, (u8 *)&info, sizeof(info));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_info(&client->dev, "Device ID 0x%x, firmware version %02x.%02x\n",
|
||||
be32_to_cpu(info.device_id), info.fw_version[0],
|
||||
info.fw_version[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use 'false' as the second parameter to cause the device to enter
|
||||
* sleep.
|
||||
*/
|
||||
static int mma9551_set_device_state(struct i2c_client *client,
|
||||
bool enable)
|
||||
{
|
||||
return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
|
||||
MMA9551_SLEEP_CFG,
|
||||
MMA9551_SLEEP_CFG_SNCEN,
|
||||
enable ? 0 : MMA9551_SLEEP_CFG_SNCEN);
|
||||
}
|
||||
|
||||
static int mma9551_read_incli_chan(struct i2c_client *client,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
@ -424,15 +83,19 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
|
||||
reg_addr, &angle);
|
||||
ret = mma9551_set_power_state(client, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
|
||||
reg_addr, &angle);
|
||||
if (ret < 0)
|
||||
goto out_poweroff;
|
||||
|
||||
ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
|
||||
MMA9551_TILT_QUAD_REG, &quadrant);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_poweroff;
|
||||
|
||||
angle &= ~MMA9551_TILT_ANGFLG;
|
||||
quadrant = (quadrant >> quad_shift) & 0x03;
|
||||
@ -442,39 +105,11 @@ static int mma9551_read_incli_chan(struct i2c_client *client,
|
||||
else
|
||||
*val = angle + 90 * quadrant;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
ret = IIO_VAL_INT;
|
||||
|
||||
static int mma9551_read_accel_chan(struct i2c_client *client,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2)
|
||||
{
|
||||
u16 reg_addr;
|
||||
s16 raw_accel;
|
||||
int ret;
|
||||
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_X:
|
||||
reg_addr = MMA9551_AFE_X_ACCEL_REG;
|
||||
break;
|
||||
case IIO_MOD_Y:
|
||||
reg_addr = MMA9551_AFE_Y_ACCEL_REG;
|
||||
break;
|
||||
case IIO_MOD_Z:
|
||||
reg_addr = MMA9551_AFE_Z_ACCEL_REG;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
|
||||
reg_addr, &raw_accel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = raw_accel;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
out_poweroff:
|
||||
mma9551_set_power_state(client, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma9551_read_raw(struct iio_dev *indio_dev,
|
||||
@ -509,9 +144,7 @@ static int mma9551_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
*val2 = 2440;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
return mma9551_read_accel_scale(val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -556,6 +189,10 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
|
||||
MMA9551_APPID_NONE, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mma9551_set_power_state(data->client, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
int bitnum;
|
||||
|
||||
@ -574,11 +211,18 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
ret = mma9551_set_power_state(data->client, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mma9551_gpio_config(data->client,
|
||||
(enum mma9551_gpio_pin)mma_axis,
|
||||
MMA9551_APPID_TILT, bitnum, 0);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
mma9551_set_power_state(data->client, false);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
data->event_enabled[mma_axis] = state;
|
||||
@ -669,14 +313,6 @@ static const struct iio_event_spec mma9551_incli_event = {
|
||||
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
|
||||
};
|
||||
|
||||
#define MMA9551_ACCEL_CHANNEL(axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
#define MMA9551_INCLI_CHANNEL(axis) { \
|
||||
.type = IIO_INCLI, \
|
||||
.modified = 1, \
|
||||
@ -771,12 +407,7 @@ static int mma9551_init(struct mma9551_data *data)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Power on chip and enable doze mode. */
|
||||
return mma9551_update_config_bits(data->client,
|
||||
MMA9551_APPID_SLEEP_WAKE,
|
||||
MMA9551_SLEEP_CFG,
|
||||
MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN,
|
||||
MMA9551_SLEEP_CFG_SCHEN);
|
||||
return mma9551_set_device_state(data->client, true);
|
||||
}
|
||||
|
||||
static int mma9551_gpio_probe(struct iio_dev *indio_dev)
|
||||
@ -869,8 +500,19 @@ static int mma9551_probe(struct i2c_client *client,
|
||||
goto out_poweroff;
|
||||
}
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret < 0)
|
||||
goto out_iio_unregister;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||
MMA9551_AUTO_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
out_iio_unregister:
|
||||
iio_device_unregister(indio_dev);
|
||||
out_poweroff:
|
||||
mma9551_set_device_state(client, false);
|
||||
|
||||
@ -882,6 +524,10 @@ static int mma9551_remove(struct i2c_client *client)
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mma9551_data *data = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
mutex_lock(&data->mutex);
|
||||
mma9551_set_device_state(data->client, false);
|
||||
@ -890,37 +536,72 @@ static int mma9551_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mma9551_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mma9551_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = mma9551_set_device_state(data->client, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "powering off device failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mma9551_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mma9551_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = mma9551_set_device_state(data->client, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mma9551_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mma9551_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
mma9551_set_device_state(data->client, false);
|
||||
ret = mma9551_set_device_state(data->client, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma9551_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mma9551_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
mma9551_set_device_state(data->client, true);
|
||||
ret = mma9551_set_device_state(data->client, true);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define mma9551_suspend NULL
|
||||
#define mma9551_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mma9551_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
|
||||
SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
|
||||
mma9551_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id mma9551_acpi_match[] = {
|
||||
|
798
drivers/iio/accel/mma9551_core.c
Normal file
798
drivers/iio/accel/mma9551_core.c
Normal file
@ -0,0 +1,798 @@
|
||||
/*
|
||||
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "mma9551_core.h"
|
||||
|
||||
/* Command masks for mailbox write command */
|
||||
#define MMA9551_CMD_READ_VERSION_INFO 0x00
|
||||
#define MMA9551_CMD_READ_CONFIG 0x10
|
||||
#define MMA9551_CMD_WRITE_CONFIG 0x20
|
||||
#define MMA9551_CMD_READ_STATUS 0x30
|
||||
|
||||
/* Mailbox read command */
|
||||
#define MMA9551_RESPONSE_COCO BIT(7)
|
||||
|
||||
/* Error-Status codes returned in mailbox read command */
|
||||
#define MMA9551_MCI_ERROR_NONE 0x00
|
||||
#define MMA9551_MCI_ERROR_PARAM 0x04
|
||||
#define MMA9551_MCI_INVALID_COUNT 0x19
|
||||
#define MMA9551_MCI_ERROR_COMMAND 0x1C
|
||||
#define MMA9551_MCI_ERROR_INVALID_LENGTH 0x21
|
||||
#define MMA9551_MCI_ERROR_FIFO_BUSY 0x22
|
||||
#define MMA9551_MCI_ERROR_FIFO_ALLOCATED 0x23
|
||||
#define MMA9551_MCI_ERROR_FIFO_OVERSIZE 0x24
|
||||
|
||||
/* GPIO Application */
|
||||
#define MMA9551_GPIO_POL_MSB 0x08
|
||||
#define MMA9551_GPIO_POL_LSB 0x09
|
||||
|
||||
/* Sleep/Wake application */
|
||||
#define MMA9551_SLEEP_CFG 0x06
|
||||
#define MMA9551_SLEEP_CFG_SNCEN BIT(0)
|
||||
#define MMA9551_SLEEP_CFG_FLEEN BIT(1)
|
||||
#define MMA9551_SLEEP_CFG_SCHEN BIT(2)
|
||||
|
||||
/* AFE application */
|
||||
#define MMA9551_AFE_X_ACCEL_REG 0x00
|
||||
#define MMA9551_AFE_Y_ACCEL_REG 0x02
|
||||
#define MMA9551_AFE_Z_ACCEL_REG 0x04
|
||||
|
||||
/* Reset/Suspend/Clear application */
|
||||
#define MMA9551_RSC_RESET 0x00
|
||||
#define MMA9551_RSC_OFFSET(mask) (3 - (ffs(mask) - 1) / 8)
|
||||
#define MMA9551_RSC_VAL(mask) (mask >> (((ffs(mask) - 1) / 8) * 8))
|
||||
|
||||
/*
|
||||
* A response is composed of:
|
||||
* - control registers: MB0-3
|
||||
* - data registers: MB4-31
|
||||
*
|
||||
* A request is composed of:
|
||||
* - mbox to write to (always 0)
|
||||
* - control registers: MB1-4
|
||||
* - data registers: MB5-31
|
||||
*/
|
||||
#define MMA9551_MAILBOX_CTRL_REGS 4
|
||||
#define MMA9551_MAX_MAILBOX_DATA_REGS 28
|
||||
#define MMA9551_MAILBOX_REGS 32
|
||||
|
||||
#define MMA9551_I2C_READ_RETRIES 5
|
||||
#define MMA9551_I2C_READ_DELAY 50 /* us */
|
||||
|
||||
struct mma9551_mbox_request {
|
||||
u8 start_mbox; /* Always 0. */
|
||||
u8 app_id;
|
||||
/*
|
||||
* See Section 5.3.1 of the MMA955xL Software Reference Manual.
|
||||
*
|
||||
* Bit 7: reserved, always 0
|
||||
* Bits 6-4: command
|
||||
* Bits 3-0: upper bits of register offset
|
||||
*/
|
||||
u8 cmd_off;
|
||||
u8 lower_off;
|
||||
u8 nbytes;
|
||||
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
|
||||
} __packed;
|
||||
|
||||
struct mma9551_mbox_response {
|
||||
u8 app_id;
|
||||
/*
|
||||
* See Section 5.3.3 of the MMA955xL Software Reference Manual.
|
||||
*
|
||||
* Bit 7: COCO
|
||||
* Bits 6-0: Error code.
|
||||
*/
|
||||
u8 coco_err;
|
||||
u8 nbytes;
|
||||
u8 req_bytes;
|
||||
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
|
||||
} __packed;
|
||||
|
||||
struct mma9551_version_info {
|
||||
__be32 device_id;
|
||||
u8 rom_version[2];
|
||||
u8 fw_version[2];
|
||||
u8 hw_version[2];
|
||||
u8 fw_build[2];
|
||||
};
|
||||
|
||||
static int mma9551_transfer(struct i2c_client *client,
|
||||
u8 app_id, u8 command, u16 offset,
|
||||
u8 *inbytes, int num_inbytes,
|
||||
u8 *outbytes, int num_outbytes)
|
||||
{
|
||||
struct mma9551_mbox_request req;
|
||||
struct mma9551_mbox_response rsp;
|
||||
struct i2c_msg in, out;
|
||||
u8 req_len, err_code;
|
||||
int ret, retries;
|
||||
|
||||
if (offset >= 1 << 12) {
|
||||
dev_err(&client->dev, "register offset too large\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
|
||||
req.start_mbox = 0;
|
||||
req.app_id = app_id;
|
||||
req.cmd_off = command | (offset >> 8);
|
||||
req.lower_off = offset;
|
||||
|
||||
if (command == MMA9551_CMD_WRITE_CONFIG)
|
||||
req.nbytes = num_inbytes;
|
||||
else
|
||||
req.nbytes = num_outbytes;
|
||||
if (num_inbytes)
|
||||
memcpy(req.buf, inbytes, num_inbytes);
|
||||
|
||||
out.addr = client->addr;
|
||||
out.flags = 0;
|
||||
out.len = req_len;
|
||||
out.buf = (u8 *)&req;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &out, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c write failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
retries = MMA9551_I2C_READ_RETRIES;
|
||||
do {
|
||||
udelay(MMA9551_I2C_READ_DELAY);
|
||||
|
||||
in.addr = client->addr;
|
||||
in.flags = I2C_M_RD;
|
||||
in.len = sizeof(rsp);
|
||||
in.buf = (u8 *)&rsp;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &in, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c read failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rsp.coco_err & MMA9551_RESPONSE_COCO)
|
||||
break;
|
||||
} while (--retries > 0);
|
||||
|
||||
if (retries == 0) {
|
||||
dev_err(&client->dev,
|
||||
"timed out while waiting for command response\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (rsp.app_id != app_id) {
|
||||
dev_err(&client->dev,
|
||||
"app_id mismatch in response got %02x expected %02x\n",
|
||||
rsp.app_id, app_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
|
||||
if (err_code != MMA9551_MCI_ERROR_NONE) {
|
||||
dev_err(&client->dev, "read returned error %x\n", err_code);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rsp.nbytes != rsp.req_bytes) {
|
||||
dev_err(&client->dev,
|
||||
"output length mismatch got %d expected %d\n",
|
||||
rsp.nbytes, rsp.req_bytes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (num_outbytes)
|
||||
memcpy(outbytes, rsp.buf, num_outbytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mma9551_read_config_byte() - read 1 configuration byte
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @val: Pointer to store value read
|
||||
*
|
||||
* Read one configuration byte from the device using MMA955xL command format.
|
||||
* Commands to the MMA955xL platform consist of a write followed
|
||||
* by one or more reads.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 *val)
|
||||
{
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
|
||||
reg, NULL, 0, val, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_config_byte);
|
||||
|
||||
/**
|
||||
* mma9551_write_config_byte() - write 1 configuration byte
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @val: Value to write
|
||||
*
|
||||
* Write one configuration byte from the device using MMA955xL command format.
|
||||
* Commands to the MMA955xL platform consist of a write followed by one or
|
||||
* more reads.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 val)
|
||||
{
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
|
||||
&val, 1, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_write_config_byte);
|
||||
|
||||
/**
|
||||
* mma9551_read_status_byte() - read 1 status byte
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @val: Pointer to store value read
|
||||
*
|
||||
* Read one status byte from the device using MMA955xL command format.
|
||||
* Commands to the MMA955xL platform consist of a write followed by one or
|
||||
* more reads.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 *val)
|
||||
{
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
|
||||
reg, NULL, 0, val, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_status_byte);
|
||||
|
||||
/**
|
||||
* mma9551_read_config_word() - read 1 config word
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @val: Pointer to store value read
|
||||
*
|
||||
* Read one configuration word from the device using MMA955xL command format.
|
||||
* Commands to the MMA955xL platform consist of a write followed by one or
|
||||
* more reads.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
__be16 v;
|
||||
|
||||
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
|
||||
reg, NULL, 0, (u8 *)&v, 2);
|
||||
*val = be16_to_cpu(v);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_config_word);
|
||||
|
||||
/**
|
||||
* mma9551_write_config_word() - write 1 config word
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @val: Value to write
|
||||
*
|
||||
* Write one configuration word from the device using MMA955xL command format.
|
||||
* Commands to the MMA955xL platform consist of a write followed by one or
|
||||
* more reads.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u16 val)
|
||||
{
|
||||
__be16 v = cpu_to_be16(val);
|
||||
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
|
||||
(u8 *) &v, 2, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_write_config_word);
|
||||
|
||||
/**
|
||||
* mma9551_read_status_word() - read 1 status word
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @val: Pointer to store value read
|
||||
*
|
||||
* Read one status word from the device using MMA955xL command format.
|
||||
* Commands to the MMA955xL platform consist of a write followed by one or
|
||||
* more reads.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
__be16 v;
|
||||
|
||||
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
|
||||
reg, NULL, 0, (u8 *)&v, 2);
|
||||
*val = be16_to_cpu(v);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_status_word);
|
||||
|
||||
/**
|
||||
* mma9551_read_config_words() - read multiple config words
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @len: Length of array to read in bytes
|
||||
* @val: Array of words to read
|
||||
*
|
||||
* Read multiple configuration registers (word-sized registers).
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 len, u16 *buf)
|
||||
{
|
||||
int ret, i;
|
||||
int len_words = len / sizeof(u16);
|
||||
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
|
||||
|
||||
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
|
||||
reg, NULL, 0, (u8 *) be_buf, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len_words; i++)
|
||||
buf[i] = be16_to_cpu(be_buf[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_config_words);
|
||||
|
||||
/**
|
||||
* mma9551_read_status_words() - read multiple status words
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @len: Length of array to read in bytes
|
||||
* @val: Array of words to read
|
||||
*
|
||||
* Read multiple status registers (word-sized registers).
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 len, u16 *buf)
|
||||
{
|
||||
int ret, i;
|
||||
int len_words = len / sizeof(u16);
|
||||
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
|
||||
|
||||
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
|
||||
reg, NULL, 0, (u8 *) be_buf, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len_words; i++)
|
||||
buf[i] = be16_to_cpu(be_buf[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_status_words);
|
||||
|
||||
/**
|
||||
* mma9551_write_config_words() - write multiple config words
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @len: Length of array to write in bytes
|
||||
* @val: Array of words to write
|
||||
*
|
||||
* Write multiple configuration registers (word-sized registers).
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 len, u16 *buf)
|
||||
{
|
||||
int i;
|
||||
int len_words = len / sizeof(u16);
|
||||
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
|
||||
|
||||
for (i = 0; i < len_words; i++)
|
||||
be_buf[i] = cpu_to_be16(buf[i]);
|
||||
|
||||
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
|
||||
reg, (u8 *) be_buf, len, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_write_config_words);
|
||||
|
||||
/**
|
||||
* mma9551_update_config_bits() - update bits in register
|
||||
* @client: I2C client
|
||||
* @app_id: Application ID
|
||||
* @reg: Application register
|
||||
* @mask: Mask for the bits to update
|
||||
* @val: Value of the bits to update
|
||||
*
|
||||
* Update bits in the given register using a bit mask.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret;
|
||||
u8 tmp, orig;
|
||||
|
||||
ret = mma9551_read_config_byte(client, app_id, reg, &orig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = orig & ~mask;
|
||||
tmp |= val & mask;
|
||||
|
||||
if (tmp == orig)
|
||||
return 0;
|
||||
|
||||
return mma9551_write_config_byte(client, app_id, reg, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_update_config_bits);
|
||||
|
||||
/**
|
||||
* mma9551_gpio_config() - configure gpio
|
||||
* @client: I2C client
|
||||
* @pin: GPIO pin to configure
|
||||
* @app_id: Application ID
|
||||
* @bitnum: Bit number of status register being assigned to the GPIO pin.
|
||||
* @polarity: The polarity parameter is described in section 6.2.2, page 66,
|
||||
* of the Software Reference Manual. Basically, polarity=0 means
|
||||
* the interrupt line has the same value as the selected bit,
|
||||
* while polarity=1 means the line is inverted.
|
||||
*
|
||||
* Assign a bit from an application’s status register to a specific GPIO pin.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
|
||||
u8 app_id, u8 bitnum, int polarity)
|
||||
{
|
||||
u8 reg, pol_mask, pol_val;
|
||||
int ret;
|
||||
|
||||
if (pin > mma9551_gpio_max) {
|
||||
dev_err(&client->dev, "bad GPIO pin\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
|
||||
* 0x03, and so on.
|
||||
*/
|
||||
reg = pin * 2;
|
||||
|
||||
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
|
||||
reg, app_id);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "error setting GPIO app_id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
|
||||
reg + 1, bitnum);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "error setting GPIO bit number\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (pin) {
|
||||
case mma9551_gpio6:
|
||||
reg = MMA9551_GPIO_POL_LSB;
|
||||
pol_mask = 1 << 6;
|
||||
break;
|
||||
case mma9551_gpio7:
|
||||
reg = MMA9551_GPIO_POL_LSB;
|
||||
pol_mask = 1 << 7;
|
||||
break;
|
||||
case mma9551_gpio8:
|
||||
reg = MMA9551_GPIO_POL_MSB;
|
||||
pol_mask = 1 << 0;
|
||||
break;
|
||||
case mma9551_gpio9:
|
||||
reg = MMA9551_GPIO_POL_MSB;
|
||||
pol_mask = 1 << 1;
|
||||
break;
|
||||
}
|
||||
pol_val = polarity ? pol_mask : 0;
|
||||
|
||||
ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
|
||||
pol_mask, pol_val);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "error setting GPIO polarity\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_gpio_config);
|
||||
|
||||
/**
|
||||
* mma9551_read_version() - read device version information
|
||||
* @client: I2C client
|
||||
*
|
||||
* Read version information and print device id and firmware version.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_version(struct i2c_client *client)
|
||||
{
|
||||
struct mma9551_version_info info;
|
||||
int ret;
|
||||
|
||||
ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
|
||||
NULL, 0, (u8 *)&info, sizeof(info));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_info(&client->dev, "device ID 0x%x, firmware version %02x.%02x\n",
|
||||
be32_to_cpu(info.device_id), info.fw_version[0],
|
||||
info.fw_version[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_version);
|
||||
|
||||
/**
|
||||
* mma9551_set_device_state() - sets HW power mode
|
||||
* @client: I2C client
|
||||
* @enable: Use true to power on device, false to cause the device
|
||||
* to enter sleep.
|
||||
*
|
||||
* Set power on/off for device using the Sleep/Wake Application.
|
||||
* When enable is true, power on chip and enable doze mode.
|
||||
* When enable is false, enter sleep mode (device remains in the
|
||||
* lowest-power mode).
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_set_device_state(struct i2c_client *client, bool enable)
|
||||
{
|
||||
return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
|
||||
MMA9551_SLEEP_CFG,
|
||||
MMA9551_SLEEP_CFG_SNCEN |
|
||||
MMA9551_SLEEP_CFG_FLEEN |
|
||||
MMA9551_SLEEP_CFG_SCHEN,
|
||||
enable ? MMA9551_SLEEP_CFG_SCHEN |
|
||||
MMA9551_SLEEP_CFG_FLEEN :
|
||||
MMA9551_SLEEP_CFG_SNCEN);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_set_device_state);
|
||||
|
||||
/**
|
||||
* mma9551_set_power_state() - sets runtime PM state
|
||||
* @client: I2C client
|
||||
* @on: Use true to power on device, false to power off
|
||||
*
|
||||
* Resume or suspend the device using Runtime PM.
|
||||
* The device will suspend after the autosuspend delay.
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_set_power_state(struct i2c_client *client, bool on)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
int ret;
|
||||
|
||||
if (on)
|
||||
ret = pm_runtime_get_sync(&client->dev);
|
||||
else {
|
||||
pm_runtime_mark_last_busy(&client->dev);
|
||||
ret = pm_runtime_put_autosuspend(&client->dev);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"failed to change power state to %d\n", on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_set_power_state);
|
||||
|
||||
/**
|
||||
* mma9551_sleep() - sleep
|
||||
* @freq: Application frequency
|
||||
*
|
||||
* Firmware applications run at a certain frequency on the
|
||||
* device. Sleep for one application cycle to make sure the
|
||||
* application had time to run once and initialize set values.
|
||||
*/
|
||||
void mma9551_sleep(int freq)
|
||||
{
|
||||
int sleep_val = 1000 / freq;
|
||||
|
||||
if (sleep_val < 20)
|
||||
usleep_range(sleep_val * 1000, 20000);
|
||||
else
|
||||
msleep_interruptible(sleep_val);
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_sleep);
|
||||
|
||||
/**
|
||||
* mma9551_read_accel_chan() - read accelerometer channel
|
||||
* @client: I2C client
|
||||
* @chan: IIO channel
|
||||
* @val: Pointer to the accelerometer value read
|
||||
* @val2: Unused
|
||||
*
|
||||
* Read accelerometer value for the specified channel.
|
||||
*
|
||||
* Locking note: This function must be called with the device lock held.
|
||||
* Locking is not handled inside the function. Callers should ensure they
|
||||
* serialize access to the HW.
|
||||
*
|
||||
* Returns: IIO_VAL_INT on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_read_accel_chan(struct i2c_client *client,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2)
|
||||
{
|
||||
u16 reg_addr;
|
||||
s16 raw_accel;
|
||||
int ret;
|
||||
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_X:
|
||||
reg_addr = MMA9551_AFE_X_ACCEL_REG;
|
||||
break;
|
||||
case IIO_MOD_Y:
|
||||
reg_addr = MMA9551_AFE_Y_ACCEL_REG;
|
||||
break;
|
||||
case IIO_MOD_Z:
|
||||
reg_addr = MMA9551_AFE_Z_ACCEL_REG;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mma9551_set_power_state(client, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
|
||||
reg_addr, &raw_accel);
|
||||
if (ret < 0)
|
||||
goto out_poweroff;
|
||||
|
||||
*val = raw_accel;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
|
||||
out_poweroff:
|
||||
mma9551_set_power_state(client, false);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_accel_chan);
|
||||
|
||||
/**
|
||||
* mma9551_read_accel_scale() - read accelerometer scale
|
||||
* @val: Pointer to the accelerometer scale (int value)
|
||||
* @val2: Pointer to the accelerometer scale (micro value)
|
||||
*
|
||||
* Read accelerometer scale.
|
||||
*
|
||||
* Returns: IIO_VAL_INT_PLUS_MICRO.
|
||||
*/
|
||||
int mma9551_read_accel_scale(int *val, int *val2)
|
||||
{
|
||||
*val = 0;
|
||||
*val2 = 2440;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_read_accel_scale);
|
||||
|
||||
/**
|
||||
* mma9551_app_reset() - reset application
|
||||
* @client: I2C client
|
||||
* @app_mask: Application to reset
|
||||
*
|
||||
* Reset the given application (using the Reset/Suspend/Clear
|
||||
* Control Application)
|
||||
*
|
||||
* Returns: 0 on success, negative value on failure.
|
||||
*/
|
||||
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
|
||||
{
|
||||
return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
|
||||
MMA9551_RSC_RESET +
|
||||
MMA9551_RSC_OFFSET(app_mask),
|
||||
MMA9551_RSC_VAL(app_mask));
|
||||
}
|
||||
EXPORT_SYMBOL(mma9551_app_reset);
|
||||
|
||||
MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
|
||||
MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("MMA955xL sensors core");
|
81
drivers/iio/accel/mma9551_core.h
Normal file
81
drivers/iio/accel/mma9551_core.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*/
|
||||
|
||||
#ifndef _MMA9551_CORE_H_
|
||||
#define _MMA9551_CORE_H_
|
||||
|
||||
/* Applications IDs */
|
||||
#define MMA9551_APPID_VERSION 0x00
|
||||
#define MMA9551_APPID_GPIO 0x03
|
||||
#define MMA9551_APPID_AFE 0x06
|
||||
#define MMA9551_APPID_TILT 0x0B
|
||||
#define MMA9551_APPID_SLEEP_WAKE 0x12
|
||||
#define MMA9551_APPID_PEDOMETER 0x15
|
||||
#define MMA9551_APPID_RCS 0x17
|
||||
#define MMA9551_APPID_NONE 0xff
|
||||
|
||||
/* Reset/Suspend/Clear application app masks */
|
||||
#define MMA9551_RSC_PED BIT(21)
|
||||
|
||||
#define MMA9551_AUTO_SUSPEND_DELAY_MS 2000
|
||||
|
||||
enum mma9551_gpio_pin {
|
||||
mma9551_gpio6 = 0,
|
||||
mma9551_gpio7,
|
||||
mma9551_gpio8,
|
||||
mma9551_gpio9,
|
||||
mma9551_gpio_max = mma9551_gpio9,
|
||||
};
|
||||
|
||||
#define MMA9551_ACCEL_CHANNEL(axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 *val);
|
||||
int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 val);
|
||||
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 *val);
|
||||
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u16 *val);
|
||||
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u16 val);
|
||||
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u16 *val);
|
||||
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 len, u16 *buf);
|
||||
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 len, u16 *buf);
|
||||
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 len, u16 *buf);
|
||||
int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
|
||||
u16 reg, u8 mask, u8 val);
|
||||
int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
|
||||
u8 app_id, u8 bitnum, int polarity);
|
||||
int mma9551_read_version(struct i2c_client *client);
|
||||
int mma9551_set_device_state(struct i2c_client *client, bool enable);
|
||||
int mma9551_set_power_state(struct i2c_client *client, bool on);
|
||||
void mma9551_sleep(int freq);
|
||||
int mma9551_read_accel_chan(struct i2c_client *client,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2);
|
||||
int mma9551_read_accel_scale(int *val, int *val2);
|
||||
int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
|
||||
|
||||
#endif /* _MMA9551_CORE_H_ */
|
1334
drivers/iio/accel/mma9553.c
Normal file
1334
drivers/iio/accel/mma9553.c
Normal file
File diff suppressed because it is too large
Load Diff
169
drivers/iio/accel/ssp_accel_sensor.c
Normal file
169
drivers/iio/accel/ssp_accel_sensor.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include "../common/ssp_sensors/ssp_iio_sensor.h"
|
||||
|
||||
#define SSP_CHANNEL_COUNT 3
|
||||
|
||||
#define SSP_ACCEL_NAME "ssp-accelerometer"
|
||||
static const char ssp_accel_device_name[] = SSP_ACCEL_NAME;
|
||||
|
||||
enum ssp_accel_3d_channel {
|
||||
SSP_CHANNEL_SCAN_INDEX_X,
|
||||
SSP_CHANNEL_SCAN_INDEX_Y,
|
||||
SSP_CHANNEL_SCAN_INDEX_Z,
|
||||
SSP_CHANNEL_SCAN_INDEX_TIME,
|
||||
};
|
||||
|
||||
static int ssp_accel_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
u32 t;
|
||||
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
t = ssp_get_sensor_delay(data, SSP_ACCELEROMETER_SENSOR);
|
||||
ssp_convert_to_freq(t, val, val2);
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ssp_accel_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = ssp_convert_to_time(val, val2);
|
||||
ret = ssp_change_delay(data, SSP_ACCELEROMETER_SENSOR, ret);
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "accel sensor enable fail\n");
|
||||
|
||||
return ret;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct iio_info ssp_accel_iio_info = {
|
||||
.read_raw = &ssp_accel_read_raw,
|
||||
.write_raw = &ssp_accel_write_raw,
|
||||
};
|
||||
|
||||
static const unsigned long ssp_accel_scan_mask[] = { 0x7, 0, };
|
||||
|
||||
static const struct iio_chan_spec ssp_acc_channels[] = {
|
||||
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
|
||||
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
|
||||
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
|
||||
SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
|
||||
};
|
||||
|
||||
static int ssp_process_accel_data(struct iio_dev *indio_dev, void *buf,
|
||||
int64_t timestamp)
|
||||
{
|
||||
return ssp_common_process_data(indio_dev, buf, SSP_ACCELEROMETER_SIZE,
|
||||
timestamp);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ssp_accel_buffer_ops = {
|
||||
.postenable = &ssp_common_buffer_postenable,
|
||||
.postdisable = &ssp_common_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int ssp_accel_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ssp_sensor_data *spd;
|
||||
struct iio_buffer *buffer;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spd = iio_priv(indio_dev);
|
||||
|
||||
spd->process_data = ssp_process_accel_data;
|
||||
spd->type = SSP_ACCELEROMETER_SENSOR;
|
||||
|
||||
indio_dev->name = ssp_accel_device_name;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &ssp_accel_iio_info;
|
||||
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->channels = ssp_acc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
|
||||
indio_dev->available_scan_masks = ssp_accel_scan_mask;
|
||||
|
||||
buffer = devm_iio_kfifo_allocate(&pdev->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
indio_dev->setup_ops = &ssp_accel_buffer_ops;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* ssp registering should be done after all iio setup */
|
||||
ssp_register_consumer(indio_dev, SSP_ACCELEROMETER_SENSOR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_accel_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ssp_accel_driver = {
|
||||
.driver = {
|
||||
.name = SSP_ACCEL_NAME,
|
||||
},
|
||||
.probe = ssp_accel_probe,
|
||||
.remove = ssp_accel_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ssp_accel_driver);
|
||||
|
||||
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -135,6 +135,17 @@ config AXP288_ADC
|
||||
device. Depending on platform configuration, this general purpose ADC can
|
||||
be used for sampling sensors such as thermal resistors.
|
||||
|
||||
config CC10001_ADC
|
||||
tristate "Cosmic Circuits 10001 ADC driver"
|
||||
depends on HAS_IOMEM || HAVE_CLK || REGULATOR
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Cosmic Circuits 10001 ADC.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called cc10001_adc.
|
||||
|
||||
config EXYNOS_ADC
|
||||
tristate "Exynos ADC driver support"
|
||||
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
|
||||
@ -228,6 +239,20 @@ config QCOM_SPMI_IADC
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-iadc.
|
||||
|
||||
config QCOM_SPMI_VADC
|
||||
tristate "Qualcomm SPMI PMIC voltage ADC"
|
||||
depends on SPMI
|
||||
select REGMAP_SPMI
|
||||
help
|
||||
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
|
||||
|
||||
The driver supports multiple channels read. The VADC is a 15-bit
|
||||
sigma-delta ADC. Some of the channels are internally used for
|
||||
calibration.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-vadc.
|
||||
|
||||
config ROCKCHIP_SARADC
|
||||
tristate "Rockchip SARADC driver"
|
||||
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
|
||||
|
@ -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_CC10001_ADC) += cc10001_adc.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
@ -24,6 +25,7 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
|
423
drivers/iio/adc/cc10001_adc.c
Normal file
423
drivers/iio/adc/cc10001_adc.c
Normal file
@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
/* Registers */
|
||||
#define CC10001_ADC_CONFIG 0x00
|
||||
#define CC10001_ADC_START_CONV BIT(4)
|
||||
#define CC10001_ADC_MODE_SINGLE_CONV BIT(5)
|
||||
|
||||
#define CC10001_ADC_DDATA_OUT 0x04
|
||||
#define CC10001_ADC_EOC 0x08
|
||||
#define CC10001_ADC_EOC_SET BIT(0)
|
||||
|
||||
#define CC10001_ADC_CHSEL_SAMPLED 0x0c
|
||||
#define CC10001_ADC_POWER_UP 0x10
|
||||
#define CC10001_ADC_POWER_UP_SET BIT(0)
|
||||
#define CC10001_ADC_DEBUG 0x14
|
||||
#define CC10001_ADC_DATA_COUNT 0x20
|
||||
|
||||
#define CC10001_ADC_DATA_MASK GENMASK(9, 0)
|
||||
#define CC10001_ADC_NUM_CHANNELS 8
|
||||
#define CC10001_ADC_CH_MASK GENMASK(2, 0)
|
||||
|
||||
#define CC10001_INVALID_SAMPLED 0xffff
|
||||
#define CC10001_MAX_POLL_COUNT 20
|
||||
|
||||
/*
|
||||
* As per device specification, wait six clock cycles after power-up to
|
||||
* activate START. Since adding two more clock cycles delay does not
|
||||
* impact the performance too much, we are adding two additional cycles delay
|
||||
* intentionally here.
|
||||
*/
|
||||
#define CC10001_WAIT_CYCLES 8
|
||||
|
||||
struct cc10001_adc_device {
|
||||
void __iomem *reg_base;
|
||||
struct clk *adc_clk;
|
||||
struct regulator *reg;
|
||||
u16 *buf;
|
||||
|
||||
struct mutex lock;
|
||||
unsigned long channel_map;
|
||||
unsigned int start_delay_ns;
|
||||
unsigned int eoc_delay_ns;
|
||||
};
|
||||
|
||||
static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev,
|
||||
u32 reg, u32 val)
|
||||
{
|
||||
writel(val, adc_dev->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev,
|
||||
u32 reg)
|
||||
{
|
||||
return readl(adc_dev->reg_base + reg);
|
||||
}
|
||||
|
||||
static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
|
||||
unsigned int channel)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Channel selection and mode of operation */
|
||||
val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV;
|
||||
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
|
||||
|
||||
val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG);
|
||||
val = val | CC10001_ADC_START_CONV;
|
||||
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
|
||||
}
|
||||
|
||||
static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev,
|
||||
unsigned int channel,
|
||||
unsigned int delay)
|
||||
{
|
||||
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int poll_count = 0;
|
||||
|
||||
while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) &
|
||||
CC10001_ADC_EOC_SET)) {
|
||||
|
||||
ndelay(delay);
|
||||
if (poll_count++ == CC10001_MAX_POLL_COUNT)
|
||||
return CC10001_INVALID_SAMPLED;
|
||||
}
|
||||
|
||||
poll_count = 0;
|
||||
while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) &
|
||||
CC10001_ADC_CH_MASK) != channel) {
|
||||
|
||||
ndelay(delay);
|
||||
if (poll_count++ == CC10001_MAX_POLL_COUNT)
|
||||
return CC10001_INVALID_SAMPLED;
|
||||
}
|
||||
|
||||
/* Read the 10 bit output register */
|
||||
return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) &
|
||||
CC10001_ADC_DATA_MASK;
|
||||
}
|
||||
|
||||
static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
|
||||
{
|
||||
struct cc10001_adc_device *adc_dev;
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned int delay_ns;
|
||||
unsigned int channel;
|
||||
bool sample_invalid;
|
||||
u16 *data;
|
||||
int i;
|
||||
|
||||
indio_dev = pf->indio_dev;
|
||||
adc_dev = iio_priv(indio_dev);
|
||||
data = adc_dev->buf;
|
||||
|
||||
mutex_lock(&adc_dev->lock);
|
||||
|
||||
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
|
||||
CC10001_ADC_POWER_UP_SET);
|
||||
|
||||
/* Wait for 8 (6+2) clock cycles before activating START */
|
||||
ndelay(adc_dev->start_delay_ns);
|
||||
|
||||
/* Calculate delay step for eoc and sampled data */
|
||||
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
|
||||
|
||||
i = 0;
|
||||
sample_invalid = false;
|
||||
for_each_set_bit(channel, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
|
||||
cc10001_adc_start(adc_dev, channel);
|
||||
|
||||
data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns);
|
||||
if (data[i] == CC10001_INVALID_SAMPLED) {
|
||||
dev_warn(&indio_dev->dev,
|
||||
"invalid sample on channel %d\n", channel);
|
||||
sample_invalid = true;
|
||||
goto done;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
done:
|
||||
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
|
||||
|
||||
mutex_unlock(&adc_dev->lock);
|
||||
|
||||
if (!sample_invalid)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
iio_get_time_ns());
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int delay_ns;
|
||||
u16 val;
|
||||
|
||||
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
|
||||
CC10001_ADC_POWER_UP_SET);
|
||||
|
||||
/* Wait for 8 (6+2) clock cycles before activating START */
|
||||
ndelay(adc_dev->start_delay_ns);
|
||||
|
||||
/* Calculate delay step for eoc and sampled data */
|
||||
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
|
||||
|
||||
cc10001_adc_start(adc_dev, chan->channel);
|
||||
|
||||
val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
|
||||
|
||||
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cc10001_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
mutex_lock(&adc_dev->lock);
|
||||
*val = cc10001_adc_read_raw_voltage(indio_dev, chan);
|
||||
mutex_unlock(&adc_dev->lock);
|
||||
|
||||
if (*val == CC10001_INVALID_SAMPLED)
|
||||
return -EIO;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(adc_dev->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cc10001_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
kfree(adc_dev->buf);
|
||||
adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (!adc_dev->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info cc10001_adc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &cc10001_adc_read_raw,
|
||||
.update_scan_mode = &cc10001_update_scan_mode,
|
||||
};
|
||||
|
||||
static int cc10001_adc_channel_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *chan_array, *timestamp;
|
||||
unsigned int bit, idx = 0;
|
||||
|
||||
indio_dev->num_channels = bitmap_weight(&adc_dev->channel_map,
|
||||
CC10001_ADC_NUM_CHANNELS);
|
||||
|
||||
chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels + 1,
|
||||
sizeof(struct iio_chan_spec),
|
||||
GFP_KERNEL);
|
||||
if (!chan_array)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_set_bit(bit, &adc_dev->channel_map, CC10001_ADC_NUM_CHANNELS) {
|
||||
struct iio_chan_spec *chan = &chan_array[idx];
|
||||
|
||||
chan->type = IIO_VOLTAGE;
|
||||
chan->indexed = 1;
|
||||
chan->channel = bit;
|
||||
chan->scan_index = idx;
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 10;
|
||||
chan->scan_type.storagebits = 16;
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
idx++;
|
||||
}
|
||||
|
||||
timestamp = &chan_array[idx];
|
||||
timestamp->type = IIO_TIMESTAMP;
|
||||
timestamp->channel = -1;
|
||||
timestamp->scan_index = idx;
|
||||
timestamp->scan_type.sign = 's';
|
||||
timestamp->scan_type.realbits = 64;
|
||||
timestamp->scan_type.storagebits = 64;
|
||||
|
||||
indio_dev->channels = chan_array;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc10001_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct cc10001_adc_device *adc_dev;
|
||||
unsigned long adc_clk_rate;
|
||||
struct resource *res;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
adc_dev = iio_priv(indio_dev);
|
||||
|
||||
adc_dev->channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
|
||||
if (!of_property_read_u32(node, "adc-reserved-channels", &ret))
|
||||
adc_dev->channel_map &= ~ret;
|
||||
|
||||
adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(adc_dev->reg))
|
||||
return PTR_ERR(adc_dev->reg);
|
||||
|
||||
ret = regulator_enable(adc_dev->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->info = &cc10001_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(adc_dev->reg_base)) {
|
||||
ret = PTR_ERR(adc_dev->reg_base);
|
||||
goto err_disable_reg;
|
||||
}
|
||||
|
||||
adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc");
|
||||
if (IS_ERR(adc_dev->adc_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get the clock\n");
|
||||
ret = PTR_ERR(adc_dev->adc_clk);
|
||||
goto err_disable_reg;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(adc_dev->adc_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable the clock\n");
|
||||
goto err_disable_reg;
|
||||
}
|
||||
|
||||
adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
|
||||
if (!adc_clk_rate) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "null clock rate!\n");
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
|
||||
adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
|
||||
|
||||
/* Setup the ADC channels available on the device */
|
||||
ret = cc10001_adc_channel_init(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_disable_clk;
|
||||
|
||||
mutex_init(&adc_dev->lock);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&cc10001_adc_trigger_h, NULL);
|
||||
if (ret < 0)
|
||||
goto err_disable_clk;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_cleanup_buffer;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(adc_dev->adc_clk);
|
||||
err_disable_reg:
|
||||
regulator_disable(adc_dev->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc10001_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
clk_disable_unprepare(adc_dev->adc_clk);
|
||||
regulator_disable(adc_dev->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cc10001_adc_dt_ids[] = {
|
||||
{ .compatible = "cosmic,10001-adc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids);
|
||||
|
||||
static struct platform_driver cc10001_adc_driver = {
|
||||
.driver = {
|
||||
.name = "cc10001-adc",
|
||||
.of_match_table = cc10001_adc_dt_ids,
|
||||
},
|
||||
.probe = cc10001_adc_probe,
|
||||
.remove = cc10001_adc_remove,
|
||||
};
|
||||
module_platform_driver(cc10001_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Phani Movva <Phani.Movva@imgtec.com>");
|
||||
MODULE_DESCRIPTION("Cosmic Circuits ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
1016
drivers/iio/adc/qcom-spmi-vadc.c
Normal file
1016
drivers/iio/adc/qcom-spmi-vadc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,4 +3,5 @@
|
||||
#
|
||||
|
||||
source "drivers/iio/common/hid-sensors/Kconfig"
|
||||
source "drivers/iio/common/ssp_sensors/Kconfig"
|
||||
source "drivers/iio/common/st_sensors/Kconfig"
|
||||
|
@ -8,4 +8,5 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-y += hid-sensors/
|
||||
obj-y += ssp_sensors/
|
||||
obj-y += st_sensors/
|
||||
|
@ -22,16 +22,18 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include "hid-sensor-trigger.h"
|
||||
|
||||
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
{
|
||||
int state_val;
|
||||
int report_val;
|
||||
s32 poll_value = 0;
|
||||
|
||||
if (state) {
|
||||
if (sensor_hub_device_open(st->hsdev))
|
||||
@ -47,6 +49,8 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
st->report_state.report_id,
|
||||
st->report_state.index,
|
||||
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
|
||||
|
||||
poll_value = hid_sensor_read_poll_value(st);
|
||||
} else {
|
||||
if (!atomic_dec_and_test(&st->data_ready))
|
||||
return 0;
|
||||
@ -78,10 +82,36 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
|
||||
st->power_state.index,
|
||||
&state_val);
|
||||
if (state && poll_value)
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_power_state);
|
||||
|
||||
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
int ret;
|
||||
|
||||
if (state)
|
||||
ret = pm_runtime_get_sync(&st->pdev->dev);
|
||||
else {
|
||||
pm_runtime_mark_last_busy(&st->pdev->dev);
|
||||
ret = pm_runtime_put_autosuspend(&st->pdev->dev);
|
||||
}
|
||||
if (ret < 0) {
|
||||
if (state)
|
||||
pm_runtime_put_noidle(&st->pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return _hid_sensor_power_state(st, state);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
@ -125,8 +155,21 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
||||
attrb->trigger = trig;
|
||||
indio_dev->trig = iio_trigger_get(trig);
|
||||
|
||||
return ret;
|
||||
ret = pm_runtime_set_active(&indio_dev->dev);
|
||||
if (ret)
|
||||
goto error_unreg_trigger;
|
||||
|
||||
iio_device_set_drvdata(indio_dev, attrb);
|
||||
pm_suspend_ignore_children(&attrb->pdev->dev, true);
|
||||
pm_runtime_enable(&attrb->pdev->dev);
|
||||
/* Default to 3 seconds, but can be changed from sysfs */
|
||||
pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
|
||||
3000);
|
||||
pm_runtime_use_autosuspend(&attrb->pdev->dev);
|
||||
|
||||
return ret;
|
||||
error_unreg_trigger:
|
||||
iio_trigger_unregister(trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(trig);
|
||||
error_ret:
|
||||
@ -134,6 +177,34 @@ error_ret:
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_setup_trigger);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hid_sensor_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
return _hid_sensor_power_state(attrb, false);
|
||||
}
|
||||
|
||||
static int hid_sensor_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
return _hid_sensor_power_state(attrb, true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops hid_sensor_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
|
||||
SET_RUNTIME_PM_OPS(hid_sensor_suspend,
|
||||
hid_sensor_resume, NULL)
|
||||
};
|
||||
EXPORT_SYMBOL(hid_sensor_pm_ops);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
|
||||
MODULE_DESCRIPTION("HID Sensor trigger processing");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -19,6 +19,11 @@
|
||||
#ifndef _HID_SENSOR_TRIGGER_H
|
||||
#define _HID_SENSOR_TRIGGER_H
|
||||
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
extern const struct dev_pm_ops hid_sensor_pm_ops;
|
||||
|
||||
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
||||
struct hid_sensor_common *attrb);
|
||||
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb);
|
||||
|
26
drivers/iio/common/ssp_sensors/Kconfig
Normal file
26
drivers/iio/common/ssp_sensors/Kconfig
Normal file
@ -0,0 +1,26 @@
|
||||
#
|
||||
# SSP sensor drivers and commons configuration
|
||||
#
|
||||
menu "SSP Sensor Common"
|
||||
|
||||
config IIO_SSP_SENSORS_COMMONS
|
||||
tristate "Commons for all SSP Sensor IIO drivers"
|
||||
depends on IIO_SSP_SENSORHUB
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say yes here to build commons for SSP sensors.
|
||||
To compile this as a module, choose M here: the module
|
||||
will be called ssp_iio.
|
||||
|
||||
config IIO_SSP_SENSORHUB
|
||||
tristate "Samsung Sensorhub driver"
|
||||
depends on SPI
|
||||
select MFD_CORE
|
||||
help
|
||||
SSP driver for sensorhub.
|
||||
If you say yes here you get ssp support for sensorhub.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sensorhub.
|
||||
|
||||
endmenu
|
8
drivers/iio/common/ssp_sensors/Makefile
Normal file
8
drivers/iio/common/ssp_sensors/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# Makefile for SSP sensor drivers and commons.
|
||||
#
|
||||
|
||||
sensorhub-objs := ssp_dev.o ssp_spi.o
|
||||
obj-$(CONFIG_IIO_SSP_SENSORHUB) += sensorhub.o
|
||||
|
||||
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_iio.o
|
257
drivers/iio/common/ssp_sensors/ssp.h
Normal file
257
drivers/iio/common/ssp_sensors/ssp.h
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SSP_SENSORHUB_H__
|
||||
#define __SSP_SENSORHUB_H__
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define SSP_DEVICE_ID 0x55
|
||||
|
||||
#ifdef SSP_DBG
|
||||
#define ssp_dbg(format, ...) pr_info("[SSP] "format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ssp_dbg(format, ...)
|
||||
#endif
|
||||
|
||||
#define SSP_SW_RESET_TIME 3000
|
||||
/* Sensor polling in ms */
|
||||
#define SSP_DEFAULT_POLLING_DELAY 200
|
||||
#define SSP_DEFAULT_RETRIES 3
|
||||
#define SSP_DATA_PACKET_SIZE 960
|
||||
#define SSP_HEADER_BUFFER_SIZE 4
|
||||
|
||||
enum {
|
||||
SSP_KERNEL_BINARY = 0,
|
||||
SSP_KERNEL_CRASHED_BINARY,
|
||||
};
|
||||
|
||||
enum {
|
||||
SSP_INITIALIZATION_STATE = 0,
|
||||
SSP_NO_SENSOR_STATE,
|
||||
SSP_ADD_SENSOR_STATE,
|
||||
SSP_RUNNING_SENSOR_STATE,
|
||||
};
|
||||
|
||||
/* Firmware download STATE */
|
||||
enum {
|
||||
SSP_FW_DL_STATE_FAIL = -1,
|
||||
SSP_FW_DL_STATE_NONE = 0,
|
||||
SSP_FW_DL_STATE_NEED_TO_SCHEDULE,
|
||||
SSP_FW_DL_STATE_SCHEDULED,
|
||||
SSP_FW_DL_STATE_DOWNLOADING,
|
||||
SSP_FW_DL_STATE_SYNC,
|
||||
SSP_FW_DL_STATE_DONE,
|
||||
};
|
||||
|
||||
#define SSP_INVALID_REVISION 99999
|
||||
#define SSP_INVALID_REVISION2 0xffffff
|
||||
|
||||
/* AP -> SSP Instruction */
|
||||
#define SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD 0xa1
|
||||
#define SSP_MSG2SSP_INST_BYPASS_SENSOR_RM 0xa2
|
||||
#define SSP_MSG2SSP_INST_REMOVE_ALL 0xa3
|
||||
#define SSP_MSG2SSP_INST_CHANGE_DELAY 0xa4
|
||||
#define SSP_MSG2SSP_INST_LIBRARY_ADD 0xb1
|
||||
#define SSP_MSG2SSP_INST_LIBRARY_REMOVE 0xb2
|
||||
#define SSP_MSG2SSP_INST_LIB_NOTI 0xb4
|
||||
#define SSP_MSG2SSP_INST_LIB_DATA 0xc1
|
||||
|
||||
#define SSP_MSG2SSP_AP_MCU_SET_GYRO_CAL 0xcd
|
||||
#define SSP_MSG2SSP_AP_MCU_SET_ACCEL_CAL 0xce
|
||||
#define SSP_MSG2SSP_AP_STATUS_SHUTDOWN 0xd0
|
||||
#define SSP_MSG2SSP_AP_STATUS_WAKEUP 0xd1
|
||||
#define SSP_MSG2SSP_AP_STATUS_SLEEP 0xd2
|
||||
#define SSP_MSG2SSP_AP_STATUS_RESUME 0xd3
|
||||
#define SSP_MSG2SSP_AP_STATUS_SUSPEND 0xd4
|
||||
#define SSP_MSG2SSP_AP_STATUS_RESET 0xd5
|
||||
#define SSP_MSG2SSP_AP_STATUS_POW_CONNECTED 0xd6
|
||||
#define SSP_MSG2SSP_AP_STATUS_POW_DISCONNECTED 0xd7
|
||||
#define SSP_MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE 0xda
|
||||
#define SSP_MSG2SSP_AP_MCU_SET_DUMPMODE 0xdb
|
||||
#define SSP_MSG2SSP_AP_MCU_DUMP_CHECK 0xdc
|
||||
#define SSP_MSG2SSP_AP_MCU_BATCH_FLUSH 0xdd
|
||||
#define SSP_MSG2SSP_AP_MCU_BATCH_COUNT 0xdf
|
||||
|
||||
#define SSP_MSG2SSP_AP_WHOAMI 0x0f
|
||||
#define SSP_MSG2SSP_AP_FIRMWARE_REV 0xf0
|
||||
#define SSP_MSG2SSP_AP_SENSOR_FORMATION 0xf1
|
||||
#define SSP_MSG2SSP_AP_SENSOR_PROXTHRESHOLD 0xf2
|
||||
#define SSP_MSG2SSP_AP_SENSOR_BARCODE_EMUL 0xf3
|
||||
#define SSP_MSG2SSP_AP_SENSOR_SCANNING 0xf4
|
||||
#define SSP_MSG2SSP_AP_SET_MAGNETIC_HWOFFSET 0xf5
|
||||
#define SSP_MSG2SSP_AP_GET_MAGNETIC_HWOFFSET 0xf6
|
||||
#define SSP_MSG2SSP_AP_SENSOR_GESTURE_CURRENT 0xf7
|
||||
#define SSP_MSG2SSP_AP_GET_THERM 0xf8
|
||||
#define SSP_MSG2SSP_AP_GET_BIG_DATA 0xf9
|
||||
#define SSP_MSG2SSP_AP_SET_BIG_DATA 0xfa
|
||||
#define SSP_MSG2SSP_AP_START_BIG_DATA 0xfb
|
||||
#define SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX 0xfd
|
||||
#define SSP_MSG2SSP_AP_SENSOR_TILT 0xea
|
||||
#define SSP_MSG2SSP_AP_MCU_SET_TIME 0xfe
|
||||
#define SSP_MSG2SSP_AP_MCU_GET_TIME 0xff
|
||||
|
||||
#define SSP_MSG2SSP_AP_FUSEROM 0x01
|
||||
|
||||
/* voice data */
|
||||
#define SSP_TYPE_WAKE_UP_VOICE_SERVICE 0x01
|
||||
#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM 0x01
|
||||
#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER 0x02
|
||||
|
||||
/* Factory Test */
|
||||
#define SSP_ACCELEROMETER_FACTORY 0x80
|
||||
#define SSP_GYROSCOPE_FACTORY 0x81
|
||||
#define SSP_GEOMAGNETIC_FACTORY 0x82
|
||||
#define SSP_PRESSURE_FACTORY 0x85
|
||||
#define SSP_GESTURE_FACTORY 0x86
|
||||
#define SSP_TEMPHUMIDITY_CRC_FACTORY 0x88
|
||||
#define SSP_GYROSCOPE_TEMP_FACTORY 0x8a
|
||||
#define SSP_GYROSCOPE_DPS_FACTORY 0x8b
|
||||
#define SSP_MCU_FACTORY 0x8c
|
||||
#define SSP_MCU_SLEEP_FACTORY 0x8d
|
||||
|
||||
/* SSP -> AP ACK about write CMD */
|
||||
#define SSP_MSG_ACK 0x80 /* ACK from SSP to AP */
|
||||
#define SSP_MSG_NAK 0x70 /* NAK from SSP to AP */
|
||||
|
||||
struct ssp_sensorhub_info {
|
||||
char *fw_name;
|
||||
char *fw_crashed_name;
|
||||
unsigned int fw_rev;
|
||||
const u8 * const mag_table;
|
||||
const unsigned int mag_length;
|
||||
};
|
||||
|
||||
/* ssp_msg options bit */
|
||||
#define SSP_RW 0
|
||||
#define SSP_INDEX 3
|
||||
|
||||
#define SSP_AP2HUB_READ 0
|
||||
#define SSP_AP2HUB_WRITE 1
|
||||
#define SSP_HUB2AP_WRITE 2
|
||||
#define SSP_AP2HUB_READY 3
|
||||
#define SSP_AP2HUB_RETURN 4
|
||||
|
||||
/**
|
||||
* struct ssp_data - ssp platformdata structure
|
||||
* @spi: spi device
|
||||
* @sensorhub_info: info about sensorhub board specific features
|
||||
* @wdt_timer: watchdog timer
|
||||
* @work_wdt: watchdog work
|
||||
* @work_firmware: firmware upgrade work queue
|
||||
* @work_refresh: refresh work queue for reset request from MCU
|
||||
* @shut_down: shut down flag
|
||||
* @mcu_dump_mode: mcu dump mode for debug
|
||||
* @time_syncing: time syncing indication flag
|
||||
* @timestamp: previous time in ns calculated for time syncing
|
||||
* @check_status: status table for each sensor
|
||||
* @com_fail_cnt: communication fail count
|
||||
* @reset_cnt: reset count
|
||||
* @timeout_cnt: timeout count
|
||||
* @available_sensors: available sensors seen by sensorhub (bit array)
|
||||
* @cur_firm_rev: cached current firmware revision
|
||||
* @last_resume_state: last AP resume/suspend state used to handle the PM
|
||||
* state of ssp
|
||||
* @last_ap_state: (obsolete) sleep notification for MCU
|
||||
* @sensor_enable: sensor enable mask
|
||||
* @delay_buf: data acquisition intervals table
|
||||
* @batch_latency_buf: yet unknown but existing in communication protocol
|
||||
* @batch_opt_buf: yet unknown but existing in communication protocol
|
||||
* @accel_position: yet unknown but existing in communication protocol
|
||||
* @mag_position: yet unknown but existing in communication protocol
|
||||
* @fw_dl_state: firmware download state
|
||||
* @comm_lock: lock protecting the handshake
|
||||
* @pending_lock: lock protecting pending list and completion
|
||||
* @mcu_reset_gpio: mcu reset line
|
||||
* @ap_mcu_gpio: ap to mcu gpio line
|
||||
* @mcu_ap_gpio: mcu to ap gpio line
|
||||
* @pending_list: pending list for messages queued to be sent/read
|
||||
* @sensor_devs: registered IIO devices table
|
||||
* @enable_refcount: enable reference count for wdt (watchdog timer)
|
||||
* @header_buffer: cache aligned buffer for packet header
|
||||
*/
|
||||
struct ssp_data {
|
||||
struct spi_device *spi;
|
||||
struct ssp_sensorhub_info *sensorhub_info;
|
||||
struct timer_list wdt_timer;
|
||||
struct work_struct work_wdt;
|
||||
struct delayed_work work_refresh;
|
||||
|
||||
bool shut_down;
|
||||
bool mcu_dump_mode;
|
||||
bool time_syncing;
|
||||
int64_t timestamp;
|
||||
|
||||
int check_status[SSP_SENSOR_MAX];
|
||||
|
||||
unsigned int com_fail_cnt;
|
||||
unsigned int reset_cnt;
|
||||
unsigned int timeout_cnt;
|
||||
|
||||
unsigned int available_sensors;
|
||||
unsigned int cur_firm_rev;
|
||||
|
||||
char last_resume_state;
|
||||
char last_ap_state;
|
||||
|
||||
unsigned int sensor_enable;
|
||||
u32 delay_buf[SSP_SENSOR_MAX];
|
||||
s32 batch_latency_buf[SSP_SENSOR_MAX];
|
||||
s8 batch_opt_buf[SSP_SENSOR_MAX];
|
||||
|
||||
int accel_position;
|
||||
int mag_position;
|
||||
int fw_dl_state;
|
||||
|
||||
struct mutex comm_lock;
|
||||
struct mutex pending_lock;
|
||||
|
||||
int mcu_reset_gpio;
|
||||
int ap_mcu_gpio;
|
||||
int mcu_ap_gpio;
|
||||
|
||||
struct list_head pending_list;
|
||||
|
||||
struct iio_dev *sensor_devs[SSP_SENSOR_MAX];
|
||||
atomic_t enable_refcount;
|
||||
|
||||
__le16 header_buffer[SSP_HEADER_BUFFER_SIZE / sizeof(__le16)]
|
||||
____cacheline_aligned;
|
||||
};
|
||||
|
||||
void ssp_clean_pending_list(struct ssp_data *data);
|
||||
|
||||
int ssp_command(struct ssp_data *data, char command, int arg);
|
||||
|
||||
int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
|
||||
u8 *send_buf, u8 length);
|
||||
|
||||
int ssp_irq_msg(struct ssp_data *data);
|
||||
|
||||
int ssp_get_chipid(struct ssp_data *data);
|
||||
|
||||
int ssp_set_magnetic_matrix(struct ssp_data *data);
|
||||
|
||||
unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data);
|
||||
|
||||
unsigned int ssp_get_firmware_rev(struct ssp_data *data);
|
||||
|
||||
int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay);
|
||||
|
||||
#endif /* __SSP_SENSORHUB_H__ */
|
712
drivers/iio/common/ssp_sensors/ssp_dev.c
Normal file
712
drivers/iio/common/ssp_sensors/ssp_dev.c
Normal file
@ -0,0 +1,712 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include "ssp.h"
|
||||
|
||||
#define SSP_WDT_TIME 10000
|
||||
#define SSP_LIMIT_RESET_CNT 20
|
||||
#define SSP_LIMIT_TIMEOUT_CNT 3
|
||||
|
||||
/* It is possible that it is max clk rate for version 1.0 of bootcode */
|
||||
#define SSP_BOOT_SPI_HZ 400000
|
||||
|
||||
/*
|
||||
* These fields can look enigmatic but this structure is used mainly to flat
|
||||
* some values and depends on command type.
|
||||
*/
|
||||
struct ssp_instruction {
|
||||
__le32 a;
|
||||
__le32 b;
|
||||
u8 c;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67,
|
||||
208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171,
|
||||
243, 13, 45, 250};
|
||||
|
||||
static const struct ssp_sensorhub_info ssp_rinato_info = {
|
||||
.fw_name = "ssp_B2.fw",
|
||||
.fw_crashed_name = "ssp_crashed.fw",
|
||||
.fw_rev = 14052300,
|
||||
.mag_table = ssp_magnitude_table,
|
||||
.mag_length = ARRAY_SIZE(ssp_magnitude_table),
|
||||
};
|
||||
|
||||
static const struct ssp_sensorhub_info ssp_thermostat_info = {
|
||||
.fw_name = "thermostat_B2.fw",
|
||||
.fw_crashed_name = "ssp_crashed.fw",
|
||||
.fw_rev = 14080600,
|
||||
.mag_table = ssp_magnitude_table,
|
||||
.mag_length = ARRAY_SIZE(ssp_magnitude_table),
|
||||
};
|
||||
|
||||
static const struct mfd_cell sensorhub_sensor_devs[] = {
|
||||
{
|
||||
.name = "ssp-accelerometer",
|
||||
},
|
||||
{
|
||||
.name = "ssp-gyroscope",
|
||||
},
|
||||
};
|
||||
|
||||
static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)
|
||||
{
|
||||
gpio_set_value(data->mcu_reset_gpio, 0);
|
||||
usleep_range(1000, 1200);
|
||||
gpio_set_value(data->mcu_reset_gpio, 1);
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
static void ssp_sync_available_sensors(struct ssp_data *data)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < SSP_SENSOR_MAX; ++i) {
|
||||
if (data->available_sensors & BIT(i)) {
|
||||
ret = ssp_enable_sensor(data, i, data->delay_buf[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev,
|
||||
"Sync sensor nr: %d fail\n", i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = ssp_command(data, SSP_MSG2SSP_AP_MCU_SET_DUMPMODE,
|
||||
data->mcu_dump_mode);
|
||||
if (ret < 0)
|
||||
dev_err(&data->spi->dev,
|
||||
"SSP_MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");
|
||||
}
|
||||
|
||||
static void ssp_enable_mcu(struct ssp_data *data, bool enable)
|
||||
{
|
||||
dev_info(&data->spi->dev, "current shutdown = %d, old = %d\n", enable,
|
||||
data->shut_down);
|
||||
|
||||
if (enable && data->shut_down) {
|
||||
data->shut_down = false;
|
||||
enable_irq(data->spi->irq);
|
||||
enable_irq_wake(data->spi->irq);
|
||||
} else if (!enable && !data->shut_down) {
|
||||
data->shut_down = true;
|
||||
disable_irq(data->spi->irq);
|
||||
disable_irq_wake(data->spi->irq);
|
||||
} else {
|
||||
dev_warn(&data->spi->dev, "current shutdown = %d, old = %d\n",
|
||||
enable, data->shut_down);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the first one which communicates with the mcu so it is
|
||||
* possible that the first attempt will fail
|
||||
*/
|
||||
static int ssp_check_fwbl(struct ssp_data *data)
|
||||
{
|
||||
int retries = 0;
|
||||
|
||||
while (retries++ < 5) {
|
||||
data->cur_firm_rev = ssp_get_firmware_rev(data);
|
||||
if (data->cur_firm_rev == SSP_INVALID_REVISION ||
|
||||
data->cur_firm_rev == SSP_INVALID_REVISION2) {
|
||||
dev_warn(&data->spi->dev,
|
||||
"Invalid revision, trying %d time\n", retries);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->cur_firm_rev == SSP_INVALID_REVISION ||
|
||||
data->cur_firm_rev == SSP_INVALID_REVISION2) {
|
||||
dev_err(&data->spi->dev, "SSP_INVALID_REVISION\n");
|
||||
return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
|
||||
}
|
||||
|
||||
dev_info(&data->spi->dev,
|
||||
"MCU Firm Rev : Old = %8u, New = %8u\n",
|
||||
data->cur_firm_rev,
|
||||
data->sensorhub_info->fw_rev);
|
||||
|
||||
if (data->cur_firm_rev != data->sensorhub_info->fw_rev)
|
||||
return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
|
||||
|
||||
return SSP_FW_DL_STATE_NONE;
|
||||
}
|
||||
|
||||
static void ssp_reset_mcu(struct ssp_data *data)
|
||||
{
|
||||
ssp_enable_mcu(data, false);
|
||||
ssp_clean_pending_list(data);
|
||||
ssp_toggle_mcu_reset_gpio(data);
|
||||
ssp_enable_mcu(data, true);
|
||||
}
|
||||
|
||||
static void ssp_wdt_work_func(struct work_struct *work)
|
||||
{
|
||||
struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);
|
||||
|
||||
dev_err(&data->spi->dev, "%s - Sensor state: 0x%x, RC: %u, CC: %u\n",
|
||||
__func__, data->available_sensors, data->reset_cnt,
|
||||
data->com_fail_cnt);
|
||||
|
||||
ssp_reset_mcu(data);
|
||||
data->com_fail_cnt = 0;
|
||||
data->timeout_cnt = 0;
|
||||
}
|
||||
|
||||
static void ssp_wdt_timer_func(unsigned long ptr)
|
||||
{
|
||||
struct ssp_data *data = (struct ssp_data *)ptr;
|
||||
|
||||
switch (data->fw_dl_state) {
|
||||
case SSP_FW_DL_STATE_FAIL:
|
||||
case SSP_FW_DL_STATE_DOWNLOADING:
|
||||
case SSP_FW_DL_STATE_SYNC:
|
||||
goto _mod;
|
||||
}
|
||||
|
||||
if (data->timeout_cnt > SSP_LIMIT_TIMEOUT_CNT ||
|
||||
data->com_fail_cnt > SSP_LIMIT_RESET_CNT)
|
||||
queue_work(system_power_efficient_wq, &data->work_wdt);
|
||||
_mod:
|
||||
mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
|
||||
}
|
||||
|
||||
static void ssp_enable_wdt_timer(struct ssp_data *data)
|
||||
{
|
||||
mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
|
||||
}
|
||||
|
||||
static void ssp_disable_wdt_timer(struct ssp_data *data)
|
||||
{
|
||||
del_timer_sync(&data->wdt_timer);
|
||||
cancel_work_sync(&data->work_wdt);
|
||||
}
|
||||
|
||||
/**
|
||||
* ssp_get_sensor_delay() - gets sensor data acquisition period
|
||||
* @data: sensorhub structure
|
||||
* @type: SSP sensor type
|
||||
*
|
||||
* Returns acquisition period in ms
|
||||
*/
|
||||
u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)
|
||||
{
|
||||
return data->delay_buf[type];
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_get_sensor_delay);
|
||||
|
||||
/**
|
||||
* ssp_enable_sensor() - enables data acquisition for sensor
|
||||
* @data: sensorhub structure
|
||||
* @type: SSP sensor type
|
||||
* @delay: delay in ms
|
||||
*
|
||||
* Returns 0 or negative value in case of error
|
||||
*/
|
||||
int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
|
||||
u32 delay)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_instruction to_send;
|
||||
|
||||
to_send.a = cpu_to_le32(delay);
|
||||
to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
|
||||
to_send.c = data->batch_opt_buf[type];
|
||||
|
||||
switch (data->check_status[type]) {
|
||||
case SSP_INITIALIZATION_STATE:
|
||||
/* do calibration step, now just enable */
|
||||
case SSP_ADD_SENSOR_STATE:
|
||||
ret = ssp_send_instruction(data,
|
||||
SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD,
|
||||
type,
|
||||
(u8 *)&to_send, sizeof(to_send));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev, "Enabling sensor failed\n");
|
||||
data->check_status[type] = SSP_NO_SENSOR_STATE;
|
||||
goto derror;
|
||||
}
|
||||
|
||||
data->sensor_enable |= BIT(type);
|
||||
data->check_status[type] = SSP_RUNNING_SENSOR_STATE;
|
||||
break;
|
||||
case SSP_RUNNING_SENSOR_STATE:
|
||||
ret = ssp_send_instruction(data,
|
||||
SSP_MSG2SSP_INST_CHANGE_DELAY, type,
|
||||
(u8 *)&to_send, sizeof(to_send));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev,
|
||||
"Changing sensor delay failed\n");
|
||||
goto derror;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
data->check_status[type] = SSP_ADD_SENSOR_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
data->delay_buf[type] = delay;
|
||||
|
||||
if (atomic_inc_return(&data->enable_refcount) == 1)
|
||||
ssp_enable_wdt_timer(data);
|
||||
|
||||
return 0;
|
||||
|
||||
derror:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_enable_sensor);
|
||||
|
||||
/**
|
||||
* ssp_change_delay() - changes data acquisition for sensor
|
||||
* @data: sensorhub structure
|
||||
* @type: SSP sensor type
|
||||
* @delay: delay in ms
|
||||
*
|
||||
* Returns 0 or negative value in case of error
|
||||
*/
|
||||
int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
|
||||
u32 delay)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_instruction to_send;
|
||||
|
||||
to_send.a = cpu_to_le32(delay);
|
||||
to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
|
||||
to_send.c = data->batch_opt_buf[type];
|
||||
|
||||
ret = ssp_send_instruction(data, SSP_MSG2SSP_INST_CHANGE_DELAY, type,
|
||||
(u8 *)&to_send, sizeof(to_send));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev, "Changing sensor delay failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->delay_buf[type] = delay;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_change_delay);
|
||||
|
||||
/**
|
||||
* ssp_disable_sensor() - disables sensor
|
||||
*
|
||||
* @data: sensorhub structure
|
||||
* @type: SSP sensor type
|
||||
*
|
||||
* Returns 0 or negative value in case of error
|
||||
*/
|
||||
int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
|
||||
{
|
||||
int ret;
|
||||
__le32 command;
|
||||
|
||||
if (data->sensor_enable & BIT(type)) {
|
||||
command = cpu_to_le32(data->delay_buf[type]);
|
||||
|
||||
ret = ssp_send_instruction(data,
|
||||
SSP_MSG2SSP_INST_BYPASS_SENSOR_RM,
|
||||
type, (u8 *)&command,
|
||||
sizeof(command));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev, "Remove sensor fail\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->sensor_enable &= ~BIT(type);
|
||||
}
|
||||
|
||||
data->check_status[type] = SSP_ADD_SENSOR_STATE;
|
||||
|
||||
if (atomic_dec_and_test(&data->enable_refcount))
|
||||
ssp_disable_wdt_timer(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_disable_sensor);
|
||||
|
||||
static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct ssp_data *data = dev_id;
|
||||
|
||||
/*
|
||||
* This wrapper is done to preserve error path for ssp_irq_msg, also
|
||||
* it is defined in different file.
|
||||
*/
|
||||
ssp_irq_msg(data);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ssp_initialize_mcu(struct ssp_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ssp_clean_pending_list(data);
|
||||
|
||||
ret = ssp_get_chipid(data);
|
||||
if (ret != SSP_DEVICE_ID) {
|
||||
dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,
|
||||
ret < 0 ? "is not working" : "identification failed",
|
||||
ret);
|
||||
return ret < 0 ? ret : -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&data->spi->dev, "MCU device ID = %d\n", ret);
|
||||
|
||||
/*
|
||||
* needs clarification, for now do not want to export all transfer
|
||||
* methods to sensors' drivers
|
||||
*/
|
||||
ret = ssp_set_magnetic_matrix(data);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev,
|
||||
"%s - ssp_set_magnetic_matrix failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->available_sensors = ssp_get_sensor_scanning_info(data);
|
||||
if (data->available_sensors == 0) {
|
||||
dev_err(&data->spi->dev,
|
||||
"%s - ssp_get_sensor_scanning_info failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->cur_firm_rev = ssp_get_firmware_rev(data);
|
||||
dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",
|
||||
data->cur_firm_rev);
|
||||
|
||||
return ssp_command(data, SSP_MSG2SSP_AP_MCU_DUMP_CHECK, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* sensorhub can request its reinitialization as some brutal and rare error
|
||||
* handling. It can be requested from the MCU.
|
||||
*/
|
||||
static void ssp_refresh_task(struct work_struct *work)
|
||||
{
|
||||
struct ssp_data *data = container_of((struct delayed_work *)work,
|
||||
struct ssp_data, work_refresh);
|
||||
|
||||
dev_info(&data->spi->dev, "refreshing\n");
|
||||
|
||||
data->reset_cnt++;
|
||||
|
||||
if (ssp_initialize_mcu(data) >= 0) {
|
||||
ssp_sync_available_sensors(data);
|
||||
if (data->last_ap_state != 0)
|
||||
ssp_command(data, data->last_ap_state, 0);
|
||||
|
||||
if (data->last_resume_state != 0)
|
||||
ssp_command(data, data->last_resume_state, 0);
|
||||
|
||||
data->timeout_cnt = 0;
|
||||
data->com_fail_cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
|
||||
{
|
||||
cancel_delayed_work_sync(&data->work_refresh);
|
||||
|
||||
return queue_delayed_work(system_power_efficient_wq,
|
||||
&data->work_refresh,
|
||||
msecs_to_jiffies(delay));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id ssp_of_match[] = {
|
||||
{
|
||||
.compatible = "samsung,sensorhub-rinato",
|
||||
.data = &ssp_rinato_info,
|
||||
}, {
|
||||
.compatible = "samsung,sensorhub-thermostat",
|
||||
.data = &ssp_thermostat_info,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ssp_of_match);
|
||||
|
||||
static struct ssp_data *ssp_parse_dt(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_data *data;
|
||||
struct device_node *node = dev->of_node;
|
||||
const struct of_device_id *match;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
|
||||
if (data->mcu_ap_gpio < 0)
|
||||
goto err_free_pd;
|
||||
|
||||
data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
|
||||
if (data->ap_mcu_gpio < 0)
|
||||
goto err_free_pd;
|
||||
|
||||
data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
|
||||
if (data->mcu_reset_gpio < 0)
|
||||
goto err_free_pd;
|
||||
|
||||
ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
"ap-mcu-gpios");
|
||||
if (ret)
|
||||
goto err_free_pd;
|
||||
|
||||
ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
|
||||
if (ret)
|
||||
goto err_ap_mcu;
|
||||
|
||||
match = of_match_node(ssp_of_match, node);
|
||||
if (!match)
|
||||
goto err_mcu_reset_gpio;
|
||||
|
||||
data->sensorhub_info = (struct ssp_sensorhub_info *)match->data;
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
return data;
|
||||
|
||||
err_mcu_reset_gpio:
|
||||
devm_gpio_free(dev, data->mcu_reset_gpio);
|
||||
err_ap_mcu:
|
||||
devm_gpio_free(dev, data->ap_mcu_gpio);
|
||||
err_free_pd:
|
||||
devm_kfree(dev, data);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static struct ssp_data *ssp_parse_dt(struct device *pdev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ssp_register_consumer() - registers iio consumer in ssp framework
|
||||
*
|
||||
* @indio_dev: consumer iio device
|
||||
* @type: ssp sensor type
|
||||
*/
|
||||
void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type)
|
||||
{
|
||||
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
|
||||
|
||||
data->sensor_devs[type] = indio_dev;
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_register_consumer);
|
||||
|
||||
static int ssp_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret, i;
|
||||
struct ssp_data *data;
|
||||
|
||||
data = ssp_parse_dt(&spi->dev);
|
||||
if (!data) {
|
||||
dev_err(&spi->dev, "Failed to find platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(&spi->dev, -1, sensorhub_sensor_devs,
|
||||
ARRAY_SIZE(sensorhub_sensor_devs), NULL, 0, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "mfd add devices fail\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi->mode = SPI_MODE_1;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Failed to setup spi\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->fw_dl_state = SSP_FW_DL_STATE_NONE;
|
||||
data->spi = spi;
|
||||
spi_set_drvdata(spi, data);
|
||||
|
||||
mutex_init(&data->comm_lock);
|
||||
|
||||
for (i = 0; i < SSP_SENSOR_MAX; ++i) {
|
||||
data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;
|
||||
data->batch_latency_buf[i] = 0;
|
||||
data->batch_opt_buf[i] = 0;
|
||||
data->check_status[i] = SSP_INITIALIZATION_STATE;
|
||||
}
|
||||
|
||||
data->delay_buf[SSP_BIO_HRM_LIB] = 100;
|
||||
|
||||
data->time_syncing = true;
|
||||
|
||||
mutex_init(&data->pending_lock);
|
||||
INIT_LIST_HEAD(&data->pending_list);
|
||||
|
||||
atomic_set(&data->enable_refcount, 0);
|
||||
|
||||
INIT_WORK(&data->work_wdt, ssp_wdt_work_func);
|
||||
INIT_DELAYED_WORK(&data->work_refresh, ssp_refresh_task);
|
||||
|
||||
setup_timer(&data->wdt_timer, ssp_wdt_timer_func, (unsigned long)data);
|
||||
|
||||
ret = request_threaded_irq(data->spi->irq, NULL,
|
||||
ssp_irq_thread_fn,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"SSP_Int", data);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Irq request fail\n");
|
||||
goto err_setup_irq;
|
||||
}
|
||||
|
||||
/* Let's start with enabled one so irq balance could be ok */
|
||||
data->shut_down = false;
|
||||
|
||||
/* just to avoid unbalanced irq set wake up */
|
||||
enable_irq_wake(data->spi->irq);
|
||||
|
||||
data->fw_dl_state = ssp_check_fwbl(data);
|
||||
if (data->fw_dl_state == SSP_FW_DL_STATE_NONE) {
|
||||
ret = ssp_initialize_mcu(data);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Initialize_mcu failed\n");
|
||||
goto err_read_reg;
|
||||
}
|
||||
} else {
|
||||
dev_err(&spi->dev, "Firmware version not supported\n");
|
||||
ret = -EPERM;
|
||||
goto err_read_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_read_reg:
|
||||
free_irq(data->spi->irq, data);
|
||||
err_setup_irq:
|
||||
mutex_destroy(&data->pending_lock);
|
||||
mutex_destroy(&data->comm_lock);
|
||||
|
||||
dev_err(&spi->dev, "Probe failed!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ssp_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ssp_data *data = spi_get_drvdata(spi);
|
||||
|
||||
if (ssp_command(data, SSP_MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
|
||||
dev_err(&data->spi->dev,
|
||||
"SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
|
||||
|
||||
ssp_enable_mcu(data, false);
|
||||
ssp_disable_wdt_timer(data);
|
||||
|
||||
ssp_clean_pending_list(data);
|
||||
|
||||
free_irq(data->spi->irq, data);
|
||||
|
||||
del_timer_sync(&data->wdt_timer);
|
||||
cancel_work_sync(&data->work_wdt);
|
||||
|
||||
mutex_destroy(&data->comm_lock);
|
||||
mutex_destroy(&data->pending_lock);
|
||||
|
||||
mfd_remove_devices(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
|
||||
|
||||
data->last_resume_state = SSP_MSG2SSP_AP_STATUS_SUSPEND;
|
||||
|
||||
if (atomic_read(&data->enable_refcount) > 0)
|
||||
ssp_disable_wdt_timer(data);
|
||||
|
||||
ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_SUSPEND, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev,
|
||||
"%s SSP_MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);
|
||||
|
||||
ssp_enable_wdt_timer(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->time_syncing = false;
|
||||
disable_irq(data->spi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
|
||||
|
||||
enable_irq(data->spi->irq);
|
||||
|
||||
if (atomic_read(&data->enable_refcount) > 0)
|
||||
ssp_enable_wdt_timer(data);
|
||||
|
||||
ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_RESUME, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->spi->dev,
|
||||
"%s SSP_MSG2SSP_AP_STATUS_RESUME failed\n", __func__);
|
||||
ssp_disable_wdt_timer(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* timesyncing is set by MCU */
|
||||
data->last_resume_state = SSP_MSG2SSP_AP_STATUS_RESUME;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ssp_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume)
|
||||
};
|
||||
|
||||
static struct spi_driver ssp_driver = {
|
||||
.probe = ssp_probe,
|
||||
.remove = ssp_remove,
|
||||
.driver = {
|
||||
.pm = &ssp_pm_ops,
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(ssp_of_match),
|
||||
.name = "sensorhub"
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(ssp_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ssp sensorhub driver");
|
||||
MODULE_AUTHOR("Samsung Electronics");
|
||||
MODULE_LICENSE("GPL");
|
107
drivers/iio/common/ssp_sensors/ssp_iio.c
Normal file
107
drivers/iio/common/ssp_sensors/ssp_iio.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include "ssp_iio_sensor.h"
|
||||
|
||||
/**
|
||||
* ssp_common_buffer_postenable() - generic postenable callback for ssp buffer
|
||||
*
|
||||
* @indio_dev: iio device
|
||||
*
|
||||
* Returns 0 or negative value in case of error
|
||||
*/
|
||||
int ssp_common_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ssp_sensor_data *spd = iio_priv(indio_dev);
|
||||
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
|
||||
|
||||
/* the allocation is made in post because scan size is known in this
|
||||
* moment
|
||||
* */
|
||||
spd->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL | GFP_DMA);
|
||||
if (!spd->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
return ssp_enable_sensor(data, spd->type,
|
||||
ssp_get_sensor_delay(data, spd->type));
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_common_buffer_postenable);
|
||||
|
||||
/**
|
||||
* ssp_common_buffer_postdisable() - generic postdisable callback for ssp buffer
|
||||
*
|
||||
* @indio_dev: iio device
|
||||
*
|
||||
* Returns 0 or negative value in case of error
|
||||
*/
|
||||
int ssp_common_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_sensor_data *spd = iio_priv(indio_dev);
|
||||
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
|
||||
|
||||
ret = ssp_disable_sensor(data, spd->type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
kfree(spd->buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_common_buffer_postdisable);
|
||||
|
||||
/**
|
||||
* ssp_common_process_data() - Common process data callback for ssp sensors
|
||||
*
|
||||
* @indio_dev: iio device
|
||||
* @buf: source buffer
|
||||
* @len: sensor data length
|
||||
* @timestamp: system timestamp
|
||||
*
|
||||
* Returns 0 or negative value in case of error
|
||||
*/
|
||||
int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
|
||||
unsigned int len, int64_t timestamp)
|
||||
{
|
||||
__le32 time;
|
||||
int64_t calculated_time;
|
||||
struct ssp_sensor_data *spd = iio_priv(indio_dev);
|
||||
|
||||
if (indio_dev->scan_bytes == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* it always sends full set of samples, remember about available masks
|
||||
*/
|
||||
memcpy(spd->buffer, buf, len);
|
||||
|
||||
if (indio_dev->scan_timestamp) {
|
||||
memcpy(&time, &((char *)buf)[len], SSP_TIME_SIZE);
|
||||
calculated_time =
|
||||
timestamp + (int64_t)le32_to_cpu(time) * 1000000;
|
||||
}
|
||||
|
||||
return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer,
|
||||
calculated_time);
|
||||
}
|
||||
EXPORT_SYMBOL(ssp_common_process_data);
|
||||
|
||||
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung sensorhub commons");
|
||||
MODULE_LICENSE("GPL");
|
71
drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
Normal file
71
drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef __SSP_IIO_SENSOR_H__
|
||||
#define __SSP_IIO_SENSOR_H__
|
||||
|
||||
#define SSP_CHANNEL_AG(_type, _mod, _index) \
|
||||
{ \
|
||||
.type = _type,\
|
||||
.modified = 1,\
|
||||
.channel2 = _mod,\
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
|
||||
.scan_index = _index,\
|
||||
.scan_type = {\
|
||||
.sign = 's',\
|
||||
.realbits = 16,\
|
||||
.storagebits = 16,\
|
||||
.shift = 0,\
|
||||
.endianness = IIO_LE,\
|
||||
},\
|
||||
}
|
||||
|
||||
/* It is defined here as it is a mixed timestamp */
|
||||
#define SSP_CHAN_TIMESTAMP(_si) { \
|
||||
.type = IIO_TIMESTAMP, \
|
||||
.channel = -1, \
|
||||
.scan_index = _si, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 64, \
|
||||
.storagebits = 64, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define SSP_MS_PER_S 1000
|
||||
#define SSP_INVERTED_SCALING_FACTOR 1000000U
|
||||
|
||||
#define SSP_FACTOR_WITH_MS \
|
||||
(SSP_INVERTED_SCALING_FACTOR * SSP_MS_PER_S)
|
||||
|
||||
int ssp_common_buffer_postenable(struct iio_dev *indio_dev);
|
||||
|
||||
int ssp_common_buffer_postdisable(struct iio_dev *indio_dev);
|
||||
|
||||
int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
|
||||
unsigned int len, int64_t timestamp);
|
||||
|
||||
/* Converts time in ms to frequency */
|
||||
static inline void ssp_convert_to_freq(u32 time, int *integer_part,
|
||||
int *fractional)
|
||||
{
|
||||
if (time == 0) {
|
||||
*fractional = 0;
|
||||
*integer_part = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
*integer_part = SSP_FACTOR_WITH_MS / time;
|
||||
*fractional = *integer_part % SSP_INVERTED_SCALING_FACTOR;
|
||||
*integer_part = *integer_part / SSP_INVERTED_SCALING_FACTOR;
|
||||
}
|
||||
|
||||
/* Converts frequency to time in ms */
|
||||
static inline int ssp_convert_to_time(int integer_part, int fractional)
|
||||
{
|
||||
u64 value;
|
||||
|
||||
value = (u64)integer_part * SSP_INVERTED_SCALING_FACTOR + fractional;
|
||||
if (value == 0)
|
||||
return 0;
|
||||
|
||||
return div64_u64((u64)SSP_FACTOR_WITH_MS, value);
|
||||
}
|
||||
#endif /* __SSP_IIO_SENSOR_H__ */
|
608
drivers/iio/common/ssp_sensors/ssp_spi.c
Normal file
608
drivers/iio/common/ssp_sensors/ssp_spi.c
Normal file
@ -0,0 +1,608 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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 "ssp.h"
|
||||
|
||||
#define SSP_DEV (&data->spi->dev)
|
||||
#define SSP_GET_MESSAGE_TYPE(data) (data & (3 << SSP_RW))
|
||||
|
||||
/*
|
||||
* SSP -> AP Instruction
|
||||
* They tell what packet type can be expected. In the future there will
|
||||
* be less of them. BYPASS means common sensor packets with accel, gyro,
|
||||
* hrm etc. data. LIBRARY and META are mock-up's for now.
|
||||
*/
|
||||
#define SSP_MSG2AP_INST_BYPASS_DATA 0x37
|
||||
#define SSP_MSG2AP_INST_LIBRARY_DATA 0x01
|
||||
#define SSP_MSG2AP_INST_DEBUG_DATA 0x03
|
||||
#define SSP_MSG2AP_INST_BIG_DATA 0x04
|
||||
#define SSP_MSG2AP_INST_META_DATA 0x05
|
||||
#define SSP_MSG2AP_INST_TIME_SYNC 0x06
|
||||
#define SSP_MSG2AP_INST_RESET 0x07
|
||||
|
||||
#define SSP_UNIMPLEMENTED -1
|
||||
|
||||
struct ssp_msg_header {
|
||||
u8 cmd;
|
||||
__le16 length;
|
||||
__le16 options;
|
||||
__le32 data;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct ssp_msg {
|
||||
u16 length;
|
||||
u16 options;
|
||||
struct list_head list;
|
||||
struct completion *done;
|
||||
struct ssp_msg_header *h;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
static const int ssp_offset_map[SSP_SENSOR_MAX] = {
|
||||
[SSP_ACCELEROMETER_SENSOR] = SSP_ACCELEROMETER_SIZE +
|
||||
SSP_TIME_SIZE,
|
||||
[SSP_GYROSCOPE_SENSOR] = SSP_GYROSCOPE_SIZE +
|
||||
SSP_TIME_SIZE,
|
||||
[SSP_GEOMAGNETIC_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_GEOMAGNETIC_RAW] = SSP_UNIMPLEMENTED,
|
||||
[SSP_GEOMAGNETIC_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_PRESSURE_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_GESTURE_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_PROXIMITY_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_TEMPERATURE_HUMIDITY_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_LIGHT_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_PROXIMITY_RAW] = SSP_UNIMPLEMENTED,
|
||||
[SSP_ORIENTATION_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_STEP_DETECTOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_SIG_MOTION_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_GYRO_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_GAME_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
|
||||
[SSP_STEP_COUNTER] = SSP_UNIMPLEMENTED,
|
||||
[SSP_BIO_HRM_RAW] = SSP_BIO_HRM_RAW_SIZE +
|
||||
SSP_TIME_SIZE,
|
||||
[SSP_BIO_HRM_RAW_FAC] = SSP_BIO_HRM_RAW_FAC_SIZE +
|
||||
SSP_TIME_SIZE,
|
||||
[SSP_BIO_HRM_LIB] = SSP_BIO_HRM_LIB_SIZE +
|
||||
SSP_TIME_SIZE,
|
||||
};
|
||||
|
||||
#define SSP_HEADER_SIZE (sizeof(struct ssp_msg_header))
|
||||
#define SSP_HEADER_SIZE_ALIGNED (ALIGN(SSP_HEADER_SIZE, 4))
|
||||
|
||||
static struct ssp_msg *ssp_create_msg(u8 cmd, u16 len, u16 opt, u32 data)
|
||||
{
|
||||
struct ssp_msg_header h;
|
||||
struct ssp_msg *msg;
|
||||
|
||||
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
h.cmd = cmd;
|
||||
h.length = cpu_to_le16(len);
|
||||
h.options = cpu_to_le16(opt);
|
||||
h.data = cpu_to_le32(data);
|
||||
|
||||
msg->buffer = kzalloc(SSP_HEADER_SIZE_ALIGNED + len,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!msg->buffer) {
|
||||
kfree(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg->length = len;
|
||||
msg->options = opt;
|
||||
|
||||
memcpy(msg->buffer, &h, SSP_HEADER_SIZE);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is a bit heavy to do it this way but often the function is used to compose
|
||||
* the message from smaller chunks which are placed on the stack. Often the
|
||||
* chunks are small so memcpy should be optimalized.
|
||||
*/
|
||||
static inline void ssp_fill_buffer(struct ssp_msg *m, unsigned int offset,
|
||||
const void *src, unsigned int len)
|
||||
{
|
||||
memcpy(&m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], src, len);
|
||||
}
|
||||
|
||||
static inline void ssp_get_buffer(struct ssp_msg *m, unsigned int offset,
|
||||
void *dest, unsigned int len)
|
||||
{
|
||||
memcpy(dest, &m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], len);
|
||||
}
|
||||
|
||||
#define SSP_GET_BUFFER_AT_INDEX(m, index) \
|
||||
(m->buffer[SSP_HEADER_SIZE_ALIGNED + index])
|
||||
#define SSP_SET_BUFFER_AT_INDEX(m, index, val) \
|
||||
(m->buffer[SSP_HEADER_SIZE_ALIGNED + index] = val)
|
||||
|
||||
static void ssp_clean_msg(struct ssp_msg *m)
|
||||
{
|
||||
kfree(m->buffer);
|
||||
kfree(m);
|
||||
}
|
||||
|
||||
static int ssp_print_mcu_debug(char *data_frame, int *data_index,
|
||||
int received_len)
|
||||
{
|
||||
int length = data_frame[(*data_index)++];
|
||||
|
||||
if (length > received_len - *data_index || length <= 0) {
|
||||
ssp_dbg("[SSP]: MSG From MCU-invalid debug length(%d/%d)\n",
|
||||
length, received_len);
|
||||
return length ? length : -EPROTO;
|
||||
}
|
||||
|
||||
ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
|
||||
|
||||
*data_index += length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* It was designed that way - additional lines to some kind of handshake,
|
||||
* please do not ask why - only the firmware guy can know it.
|
||||
*/
|
||||
static int ssp_check_lines(struct ssp_data *data, bool state)
|
||||
{
|
||||
int delay_cnt = 0;
|
||||
|
||||
gpio_set_value_cansleep(data->ap_mcu_gpio, state);
|
||||
|
||||
while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) {
|
||||
usleep_range(3000, 3500);
|
||||
|
||||
if (data->shut_down || delay_cnt++ > 500) {
|
||||
dev_err(SSP_DEV, "%s:timeout, hw ack wait fail %d\n",
|
||||
__func__, state);
|
||||
|
||||
if (!state)
|
||||
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg,
|
||||
struct completion *done, int timeout)
|
||||
{
|
||||
int status;
|
||||
/*
|
||||
* check if this is a short one way message or the whole transfer has
|
||||
* second part after an interrupt
|
||||
*/
|
||||
const bool use_no_irq = msg->length == 0;
|
||||
|
||||
if (data->shut_down)
|
||||
return -EPERM;
|
||||
|
||||
msg->done = done;
|
||||
|
||||
mutex_lock(&data->comm_lock);
|
||||
|
||||
status = ssp_check_lines(data, false);
|
||||
if (status < 0)
|
||||
goto _error_locked;
|
||||
|
||||
status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
|
||||
if (status < 0) {
|
||||
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
|
||||
dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
|
||||
goto _error_locked;
|
||||
}
|
||||
|
||||
if (!use_no_irq) {
|
||||
mutex_lock(&data->pending_lock);
|
||||
list_add_tail(&msg->list, &data->pending_list);
|
||||
mutex_unlock(&data->pending_lock);
|
||||
}
|
||||
|
||||
status = ssp_check_lines(data, true);
|
||||
if (status < 0) {
|
||||
if (!use_no_irq) {
|
||||
mutex_lock(&data->pending_lock);
|
||||
list_del(&msg->list);
|
||||
mutex_unlock(&data->pending_lock);
|
||||
}
|
||||
goto _error_locked;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->comm_lock);
|
||||
|
||||
if (!use_no_irq && done)
|
||||
if (wait_for_completion_timeout(done,
|
||||
msecs_to_jiffies(timeout)) ==
|
||||
0) {
|
||||
mutex_lock(&data->pending_lock);
|
||||
list_del(&msg->list);
|
||||
mutex_unlock(&data->pending_lock);
|
||||
|
||||
data->timeout_cnt++;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
_error_locked:
|
||||
mutex_unlock(&data->comm_lock);
|
||||
data->timeout_cnt++;
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int ssp_spi_sync_command(struct ssp_data *data,
|
||||
struct ssp_msg *msg)
|
||||
{
|
||||
return ssp_do_transfer(data, msg, NULL, 0);
|
||||
}
|
||||
|
||||
static int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg,
|
||||
int timeout)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
|
||||
if (WARN_ON(!msg->length))
|
||||
return -EPERM;
|
||||
|
||||
return ssp_do_transfer(data, msg, &done, timeout);
|
||||
}
|
||||
|
||||
static int ssp_handle_big_data(struct ssp_data *data, char *dataframe, int *idx)
|
||||
{
|
||||
/* mock-up, it will be changed with adding another sensor types */
|
||||
*idx += 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_parse_dataframe(struct ssp_data *data, char *dataframe, int len)
|
||||
{
|
||||
int idx, sd;
|
||||
struct timespec ts;
|
||||
struct ssp_sensor_data *spd;
|
||||
struct iio_dev **indio_devs = data->sensor_devs;
|
||||
|
||||
getnstimeofday(&ts);
|
||||
|
||||
for (idx = 0; idx < len;) {
|
||||
switch (dataframe[idx++]) {
|
||||
case SSP_MSG2AP_INST_BYPASS_DATA:
|
||||
sd = dataframe[idx++];
|
||||
if (sd < 0 || sd >= SSP_SENSOR_MAX) {
|
||||
dev_err(SSP_DEV,
|
||||
"Mcu data frame1 error %d\n", sd);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (indio_devs[sd]) {
|
||||
spd = iio_priv(indio_devs[sd]);
|
||||
if (spd->process_data)
|
||||
spd->process_data(indio_devs[sd],
|
||||
&dataframe[idx],
|
||||
data->timestamp);
|
||||
} else {
|
||||
dev_err(SSP_DEV, "no client for frame\n");
|
||||
}
|
||||
|
||||
idx += ssp_offset_map[sd];
|
||||
break;
|
||||
case SSP_MSG2AP_INST_DEBUG_DATA:
|
||||
sd = ssp_print_mcu_debug(dataframe, &idx, len);
|
||||
if (sd) {
|
||||
dev_err(SSP_DEV,
|
||||
"Mcu data frame3 error %d\n", sd);
|
||||
return sd;
|
||||
}
|
||||
break;
|
||||
case SSP_MSG2AP_INST_LIBRARY_DATA:
|
||||
idx += len;
|
||||
break;
|
||||
case SSP_MSG2AP_INST_BIG_DATA:
|
||||
ssp_handle_big_data(data, dataframe, &idx);
|
||||
break;
|
||||
case SSP_MSG2AP_INST_TIME_SYNC:
|
||||
data->time_syncing = true;
|
||||
break;
|
||||
case SSP_MSG2AP_INST_RESET:
|
||||
ssp_queue_ssp_refresh_task(data, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->time_syncing)
|
||||
data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* threaded irq */
|
||||
int ssp_irq_msg(struct ssp_data *data)
|
||||
{
|
||||
bool found = false;
|
||||
char *buffer;
|
||||
u8 msg_type;
|
||||
int ret;
|
||||
u16 length, msg_options;
|
||||
struct ssp_msg *msg, *n;
|
||||
|
||||
ret = spi_read(data->spi, data->header_buffer, SSP_HEADER_BUFFER_SIZE);
|
||||
if (ret < 0) {
|
||||
dev_err(SSP_DEV, "header read fail\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
length = le16_to_cpu(data->header_buffer[1]);
|
||||
msg_options = le16_to_cpu(data->header_buffer[0]);
|
||||
|
||||
if (length == 0) {
|
||||
dev_err(SSP_DEV, "length received from mcu is 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg_type = SSP_GET_MESSAGE_TYPE(msg_options);
|
||||
|
||||
switch (msg_type) {
|
||||
case SSP_AP2HUB_READ:
|
||||
case SSP_AP2HUB_WRITE:
|
||||
/*
|
||||
* this is a small list, a few elements - the packets can be
|
||||
* received with no order
|
||||
*/
|
||||
mutex_lock(&data->pending_lock);
|
||||
list_for_each_entry_safe(msg, n, &data->pending_list, list) {
|
||||
if (msg->options == msg_options) {
|
||||
list_del(&msg->list);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
/*
|
||||
* here can be implemented dead messages handling
|
||||
* but the slave should not send such ones - it is to
|
||||
* check but let's handle this
|
||||
*/
|
||||
buffer = kmalloc(length, GFP_KERNEL | GFP_DMA);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto _unlock;
|
||||
}
|
||||
|
||||
/* got dead packet so it is always an error */
|
||||
ret = spi_read(data->spi, buffer, length);
|
||||
if (ret >= 0)
|
||||
ret = -EPROTO;
|
||||
|
||||
kfree(buffer);
|
||||
|
||||
dev_err(SSP_DEV, "No match error %x\n",
|
||||
msg_options);
|
||||
|
||||
goto _unlock;
|
||||
}
|
||||
|
||||
if (msg_type == SSP_AP2HUB_READ)
|
||||
ret = spi_read(data->spi,
|
||||
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
|
||||
msg->length);
|
||||
|
||||
if (msg_type == SSP_AP2HUB_WRITE) {
|
||||
ret = spi_write(data->spi,
|
||||
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
|
||||
msg->length);
|
||||
if (msg_options & SSP_AP2HUB_RETURN) {
|
||||
msg->options =
|
||||
SSP_AP2HUB_READ | SSP_AP2HUB_RETURN;
|
||||
msg->length = 1;
|
||||
|
||||
list_add_tail(&msg->list, &data->pending_list);
|
||||
goto _unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->done)
|
||||
if (!completion_done(msg->done))
|
||||
complete(msg->done);
|
||||
_unlock:
|
||||
mutex_unlock(&data->pending_lock);
|
||||
break;
|
||||
case SSP_HUB2AP_WRITE:
|
||||
buffer = kzalloc(length, GFP_KERNEL | GFP_DMA);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = spi_read(data->spi, buffer, length);
|
||||
if (ret < 0) {
|
||||
dev_err(SSP_DEV, "spi read fail\n");
|
||||
kfree(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ssp_parse_dataframe(data, buffer, length);
|
||||
|
||||
kfree(buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(SSP_DEV, "unknown msg type\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ssp_clean_pending_list(struct ssp_data *data)
|
||||
{
|
||||
struct ssp_msg *msg, *n;
|
||||
|
||||
mutex_lock(&data->pending_lock);
|
||||
list_for_each_entry_safe(msg, n, &data->pending_list, list) {
|
||||
list_del(&msg->list);
|
||||
|
||||
if (msg->done)
|
||||
if (!completion_done(msg->done))
|
||||
complete(msg->done);
|
||||
}
|
||||
mutex_unlock(&data->pending_lock);
|
||||
}
|
||||
|
||||
int ssp_command(struct ssp_data *data, char command, int arg)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_msg *msg;
|
||||
|
||||
msg = ssp_create_msg(command, 0, SSP_AP2HUB_WRITE, arg);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ssp_dbg("%s - command 0x%x %d\n", __func__, command, arg);
|
||||
|
||||
ret = ssp_spi_sync_command(data, msg);
|
||||
ssp_clean_msg(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
|
||||
u8 *send_buf, u8 length)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_msg *msg;
|
||||
|
||||
if (data->fw_dl_state == SSP_FW_DL_STATE_DOWNLOADING) {
|
||||
dev_err(SSP_DEV, "%s - Skip Inst! DL state = %d\n",
|
||||
__func__, data->fw_dl_state);
|
||||
return -EBUSY;
|
||||
} else if (!(data->available_sensors & BIT(sensor_type)) &&
|
||||
(inst <= SSP_MSG2SSP_INST_CHANGE_DELAY)) {
|
||||
dev_err(SSP_DEV, "%s - Bypass Inst Skip! - %u\n",
|
||||
__func__, sensor_type);
|
||||
return -EIO; /* just fail */
|
||||
}
|
||||
|
||||
msg = ssp_create_msg(inst, length + 2, SSP_AP2HUB_WRITE, 0);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ssp_fill_buffer(msg, 0, &sensor_type, 1);
|
||||
ssp_fill_buffer(msg, 1, send_buf, length);
|
||||
|
||||
ssp_dbg("%s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
|
||||
__func__, inst, sensor_type, send_buf[1]);
|
||||
|
||||
ret = ssp_spi_sync(data, msg, 1000);
|
||||
ssp_clean_msg(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ssp_get_chipid(struct ssp_data *data)
|
||||
{
|
||||
int ret;
|
||||
char buffer;
|
||||
struct ssp_msg *msg;
|
||||
|
||||
msg = ssp_create_msg(SSP_MSG2SSP_AP_WHOAMI, 1, SSP_AP2HUB_READ, 0);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ssp_spi_sync(data, msg, 1000);
|
||||
|
||||
buffer = SSP_GET_BUFFER_AT_INDEX(msg, 0);
|
||||
|
||||
ssp_clean_msg(msg);
|
||||
|
||||
return ret < 0 ? ret : buffer;
|
||||
}
|
||||
|
||||
int ssp_set_magnetic_matrix(struct ssp_data *data)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_msg *msg;
|
||||
|
||||
msg = ssp_create_msg(SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX,
|
||||
data->sensorhub_info->mag_length, SSP_AP2HUB_WRITE,
|
||||
0);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ssp_fill_buffer(msg, 0, data->sensorhub_info->mag_table,
|
||||
data->sensorhub_info->mag_length);
|
||||
|
||||
ret = ssp_spi_sync(data, msg, 1000);
|
||||
ssp_clean_msg(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data)
|
||||
{
|
||||
int ret;
|
||||
__le32 result;
|
||||
u32 cpu_result = 0;
|
||||
|
||||
struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_SENSOR_SCANNING, 4,
|
||||
SSP_AP2HUB_READ, 0);
|
||||
if (!msg)
|
||||
return 0;
|
||||
|
||||
ret = ssp_spi_sync(data, msg, 1000);
|
||||
if (ret < 0) {
|
||||
dev_err(SSP_DEV, "%s - spi read fail %d\n", __func__, ret);
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
ssp_get_buffer(msg, 0, &result, 4);
|
||||
cpu_result = le32_to_cpu(result);
|
||||
|
||||
dev_info(SSP_DEV, "%s state: 0x%08x\n", __func__, cpu_result);
|
||||
|
||||
_exit:
|
||||
ssp_clean_msg(msg);
|
||||
return cpu_result;
|
||||
}
|
||||
|
||||
unsigned int ssp_get_firmware_rev(struct ssp_data *data)
|
||||
{
|
||||
int ret;
|
||||
__le32 result;
|
||||
|
||||
struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_FIRMWARE_REV, 4,
|
||||
SSP_AP2HUB_READ, 0);
|
||||
if (!msg)
|
||||
return SSP_INVALID_REVISION;
|
||||
|
||||
ret = ssp_spi_sync(data, msg, 1000);
|
||||
if (ret < 0) {
|
||||
dev_err(SSP_DEV, "%s - transfer fail %d\n", __func__, ret);
|
||||
ret = SSP_INVALID_REVISION;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
ssp_get_buffer(msg, 0, &result, 4);
|
||||
ret = le32_to_cpu(result);
|
||||
|
||||
_exit:
|
||||
ssp_clean_msg(msg);
|
||||
return ret;
|
||||
}
|
@ -16,6 +16,8 @@ itg3200-y := itg3200_core.o
|
||||
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
|
||||
obj-$(CONFIG_ITG3200) += itg3200.o
|
||||
|
||||
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_gyro_sensor.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o
|
||||
st_gyro-y := st_gyro_core.o
|
||||
st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o
|
||||
|
@ -111,19 +111,12 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&gyro_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&gyro_state->common_attributes, true);
|
||||
msleep_interruptible(poll_value * 2);
|
||||
report_id = gyro_state->gyro[chan->scan_index].report_id;
|
||||
address = gyro_3d_addresses[chan->scan_index];
|
||||
if (report_id >= 0)
|
||||
@ -416,6 +409,7 @@ static struct platform_driver hid_gyro_3d_platform_driver = {
|
||||
.id_table = hid_gyro_3d_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_gyro_3d_probe,
|
||||
.remove = hid_gyro_3d_remove,
|
||||
|
168
drivers/iio/gyro/ssp_gyro_sensor.c
Normal file
168
drivers/iio/gyro/ssp_gyro_sensor.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include "../common/ssp_sensors/ssp_iio_sensor.h"
|
||||
|
||||
#define SSP_CHANNEL_COUNT 3
|
||||
|
||||
#define SSP_GYROSCOPE_NAME "ssp-gyroscope"
|
||||
static const char ssp_gyro_name[] = SSP_GYROSCOPE_NAME;
|
||||
|
||||
enum ssp_gyro_3d_channel {
|
||||
SSP_CHANNEL_SCAN_INDEX_X,
|
||||
SSP_CHANNEL_SCAN_INDEX_Y,
|
||||
SSP_CHANNEL_SCAN_INDEX_Z,
|
||||
SSP_CHANNEL_SCAN_INDEX_TIME,
|
||||
};
|
||||
|
||||
static int ssp_gyro_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
u32 t;
|
||||
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
t = ssp_get_sensor_delay(data, SSP_GYROSCOPE_SENSOR);
|
||||
ssp_convert_to_freq(t, val, val2);
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = ssp_convert_to_time(val, val2);
|
||||
ret = ssp_change_delay(data, SSP_GYROSCOPE_SENSOR, ret);
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "gyro sensor enable fail\n");
|
||||
|
||||
return ret;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct iio_info ssp_gyro_iio_info = {
|
||||
.read_raw = &ssp_gyro_read_raw,
|
||||
.write_raw = &ssp_gyro_write_raw,
|
||||
};
|
||||
|
||||
static const unsigned long ssp_gyro_scan_mask[] = { 0x07, 0, };
|
||||
|
||||
static const struct iio_chan_spec ssp_gyro_channels[] = {
|
||||
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
|
||||
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
|
||||
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
|
||||
SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
|
||||
};
|
||||
|
||||
static int ssp_process_gyro_data(struct iio_dev *indio_dev, void *buf,
|
||||
int64_t timestamp)
|
||||
{
|
||||
return ssp_common_process_data(indio_dev, buf, SSP_GYROSCOPE_SIZE,
|
||||
timestamp);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ssp_gyro_buffer_ops = {
|
||||
.postenable = &ssp_common_buffer_postenable,
|
||||
.postdisable = &ssp_common_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int ssp_gyro_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ssp_sensor_data *spd;
|
||||
struct iio_buffer *buffer;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spd = iio_priv(indio_dev);
|
||||
|
||||
spd->process_data = ssp_process_gyro_data;
|
||||
spd->type = SSP_GYROSCOPE_SENSOR;
|
||||
|
||||
indio_dev->name = ssp_gyro_name;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &ssp_gyro_iio_info;
|
||||
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->channels = ssp_gyro_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
|
||||
indio_dev->available_scan_masks = ssp_gyro_scan_mask;
|
||||
|
||||
buffer = devm_iio_kfifo_allocate(&pdev->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
indio_dev->setup_ops = &ssp_gyro_buffer_ops;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* ssp registering should be done after all iio setup */
|
||||
ssp_register_consumer(indio_dev, SSP_GYROSCOPE_SENSOR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_gyro_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ssp_gyro_driver = {
|
||||
.driver = {
|
||||
.name = SSP_GYROSCOPE_NAME,
|
||||
},
|
||||
.probe = ssp_gyro_probe,
|
||||
.remove = ssp_gyro_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ssp_gyro_driver);
|
||||
|
||||
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -116,40 +116,35 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
|
||||
int ret;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"inv_mpu",
|
||||
st->trig);
|
||||
st->trig = devm_iio_trigger_alloc(&indio_dev->dev,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!st->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_request_irq(&indio_dev->dev, st->client->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"inv_mpu",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
return ret;
|
||||
|
||||
st->trig->dev.parent = &st->client->dev;
|
||||
st->trig->ops = &inv_mpu_trigger_ops;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
|
||||
ret = iio_trigger_register(st->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
return ret;
|
||||
|
||||
indio_dev->trig = iio_trigger_get(st->trig);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->client->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st)
|
||||
{
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->client->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
||||
|
@ -639,6 +639,8 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
|
||||
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 */
|
||||
ret = -EINVAL;
|
||||
goto error_run_postdisable;
|
||||
|
@ -72,6 +72,9 @@ static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_HUMIDITYRELATIVE] = "humidityrelative",
|
||||
[IIO_ACTIVITY] = "activity",
|
||||
[IIO_STEPS] = "steps",
|
||||
[IIO_ENERGY] = "energy",
|
||||
[IIO_DISTANCE] = "distance",
|
||||
[IIO_VELOCITY] = "velocity",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
@ -97,6 +100,7 @@ static const char * const iio_modifier_names[] = {
|
||||
[IIO_MOD_JOGGING] = "jogging",
|
||||
[IIO_MOD_WALKING] = "walking",
|
||||
[IIO_MOD_STILL] = "still",
|
||||
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
|
||||
};
|
||||
|
||||
/* relies on pairs of these shared then separate */
|
||||
@ -121,6 +125,9 @@ static const char * const iio_chan_info_postfix[] = {
|
||||
[IIO_CHAN_INFO_INT_TIME] = "integration_time",
|
||||
[IIO_CHAN_INFO_ENABLE] = "en",
|
||||
[IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
|
||||
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
|
||||
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
|
||||
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -197,7 +197,7 @@ static const char * const iio_ev_type_text[] = {
|
||||
[IIO_EV_TYPE_ROC] = "roc",
|
||||
[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
|
||||
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
|
||||
[IIO_EV_TYPE_INSTANCE] = "instance",
|
||||
[IIO_EV_TYPE_CHANGE] = "change",
|
||||
};
|
||||
|
||||
static const char * const iio_ev_dir_text[] = {
|
||||
@ -411,7 +411,7 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
|
||||
{
|
||||
int j, ret, attrcount = 0;
|
||||
|
||||
/* Dynically created from the channels array */
|
||||
/* Dynamically created from the channels array */
|
||||
for (j = 0; j < indio_dev->num_channels; j++) {
|
||||
ret = iio_device_add_event_sysfs(indio_dev,
|
||||
&indio_dev->channels[j]);
|
||||
|
@ -48,6 +48,17 @@ config CM32181
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called cm32181.
|
||||
|
||||
config CM3232
|
||||
depends on I2C
|
||||
tristate "CM3232 ambient light sensor"
|
||||
help
|
||||
Say Y here if you use cm3232.
|
||||
This option enables ambient light sensor using
|
||||
Capella Microsystems cm3232 device driver.
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called cm3232.
|
||||
|
||||
config CM36651
|
||||
depends on I2C
|
||||
tristate "CM36651 driver"
|
||||
|
@ -7,6 +7,7 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_AL3320A) += al3320a.o
|
||||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
@ -169,7 +169,7 @@ static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val)
|
||||
* @cm32181: pointer of struct cm32181.
|
||||
*
|
||||
* Convert sensor raw data to lux. It depends on integration
|
||||
* time and claibscale variable.
|
||||
* time and calibscale variable.
|
||||
*
|
||||
* Return: Positive value is lux, otherwise is error code.
|
||||
*/
|
||||
|
403
drivers/iio/light/cm3232.c
Normal file
403
drivers/iio/light/cm3232.c
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* CM3232 Ambient Light Sensor
|
||||
*
|
||||
* Copyright (C) 2014-2015 Capella Microsystems Inc.
|
||||
* Author: Kevin Tsai <ktsai@capellamicro.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.
|
||||
*
|
||||
* IIO driver for CM3232 (7-bit I2C slave address 0x10).
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
/* Registers Address */
|
||||
#define CM3232_REG_ADDR_CMD 0x00
|
||||
#define CM3232_REG_ADDR_ALS 0x50
|
||||
#define CM3232_REG_ADDR_ID 0x53
|
||||
|
||||
#define CM3232_CMD_ALS_DISABLE BIT(0)
|
||||
|
||||
#define CM3232_CMD_ALS_IT_SHIFT 2
|
||||
#define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4))
|
||||
#define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT)
|
||||
|
||||
#define CM3232_CMD_ALS_RESET BIT(6)
|
||||
|
||||
#define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT
|
||||
|
||||
#define CM3232_HW_ID 0x32
|
||||
#define CM3232_CALIBSCALE_DEFAULT 100000
|
||||
#define CM3232_CALIBSCALE_RESOLUTION 100000
|
||||
#define CM3232_MLUX_PER_LUX 1000
|
||||
|
||||
#define CM3232_MLUX_PER_BIT_DEFAULT 64
|
||||
#define CM3232_MLUX_PER_BIT_BASE_IT 100000
|
||||
|
||||
static const struct {
|
||||
int val;
|
||||
int val2;
|
||||
u8 it;
|
||||
} cm3232_als_it_scales[] = {
|
||||
{0, 100000, 0}, /* 0.100000 */
|
||||
{0, 200000, 1}, /* 0.200000 */
|
||||
{0, 400000, 2}, /* 0.400000 */
|
||||
{0, 800000, 3}, /* 0.800000 */
|
||||
{1, 600000, 4}, /* 1.600000 */
|
||||
{3, 200000, 5}, /* 3.200000 */
|
||||
};
|
||||
|
||||
struct cm3232_als_info {
|
||||
u8 regs_cmd_default;
|
||||
u8 hw_id;
|
||||
int calibscale;
|
||||
int mlux_per_bit;
|
||||
int mlux_per_bit_base_it;
|
||||
};
|
||||
|
||||
static struct cm3232_als_info cm3232_als_info_default = {
|
||||
.regs_cmd_default = CM3232_CMD_DEFAULT,
|
||||
.hw_id = CM3232_HW_ID,
|
||||
.calibscale = CM3232_CALIBSCALE_DEFAULT,
|
||||
.mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
|
||||
.mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
|
||||
};
|
||||
|
||||
struct cm3232_chip {
|
||||
struct i2c_client *client;
|
||||
struct cm3232_als_info *als_info;
|
||||
u8 regs_cmd;
|
||||
u16 regs_als;
|
||||
};
|
||||
|
||||
/**
|
||||
* cm3232_reg_init() - Initialize CM3232
|
||||
* @chip: pointer of struct cm3232_chip.
|
||||
*
|
||||
* Check and initialize CM3232 ambient light sensor.
|
||||
*
|
||||
* Return: 0 for success; otherwise for error code.
|
||||
*/
|
||||
static int cm3232_reg_init(struct cm3232_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
s32 ret;
|
||||
|
||||
chip->als_info = &cm3232_als_info_default;
|
||||
|
||||
/* Identify device */
|
||||
ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "Error reading addr_id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret & 0xFF) != chip->als_info->hw_id)
|
||||
return -ENODEV;
|
||||
|
||||
/* Disable and reset device */
|
||||
chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
|
||||
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
|
||||
chip->regs_cmd);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register default value */
|
||||
chip->regs_cmd = chip->als_info->regs_cmd_default;
|
||||
|
||||
/* Configure register */
|
||||
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
|
||||
chip->regs_cmd);
|
||||
if (ret < 0)
|
||||
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm3232_read_als_it() - Get sensor integration time
|
||||
* @chip: pointer of struct cm3232_chip
|
||||
* @val: pointer of int to load the integration (sec).
|
||||
* @val2: pointer of int to load the integration time (microsecond).
|
||||
*
|
||||
* Report the current integration time.
|
||||
*
|
||||
* Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
|
||||
*/
|
||||
static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
|
||||
{
|
||||
u16 als_it;
|
||||
int i;
|
||||
|
||||
als_it = chip->regs_cmd;
|
||||
als_it &= CM3232_CMD_ALS_IT_MASK;
|
||||
als_it >>= CM3232_CMD_ALS_IT_SHIFT;
|
||||
for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
|
||||
if (als_it == cm3232_als_it_scales[i].it) {
|
||||
*val = cm3232_als_it_scales[i].val;
|
||||
*val2 = cm3232_als_it_scales[i].val2;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm3232_write_als_it() - Write sensor integration time
|
||||
* @chip: pointer of struct cm3232_chip.
|
||||
* @val: integration time in second.
|
||||
* @val2: integration time in microsecond.
|
||||
*
|
||||
* Convert integration time to sensor value.
|
||||
*
|
||||
* Return: i2c_smbus_write_byte_data command return value.
|
||||
*/
|
||||
static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
u16 als_it, cmd;
|
||||
int i;
|
||||
s32 ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
|
||||
if (val == cm3232_als_it_scales[i].val &&
|
||||
val2 == cm3232_als_it_scales[i].val2) {
|
||||
|
||||
als_it = cm3232_als_it_scales[i].it;
|
||||
als_it <<= CM3232_CMD_ALS_IT_SHIFT;
|
||||
|
||||
cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
|
||||
cmd |= als_it;
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
CM3232_REG_ADDR_CMD,
|
||||
cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
chip->regs_cmd = cmd;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm3232_get_lux() - report current lux value
|
||||
* @chip: pointer of struct cm3232_chip.
|
||||
*
|
||||
* Convert sensor data to lux. It depends on integration
|
||||
* time and calibscale variable.
|
||||
*
|
||||
* Return: Zero or positive value is lux, otherwise error code.
|
||||
*/
|
||||
static int cm3232_get_lux(struct cm3232_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
struct cm3232_als_info *als_info = chip->als_info;
|
||||
int ret;
|
||||
int val, val2;
|
||||
int als_it;
|
||||
u64 lux;
|
||||
|
||||
/* Calculate mlux per bit based on als_it */
|
||||
ret = cm3232_read_als_it(chip, &val, &val2);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
als_it = val * 1000000 + val2;
|
||||
lux = (__force u64)als_info->mlux_per_bit;
|
||||
lux *= als_info->mlux_per_bit_base_it;
|
||||
lux = div_u64(lux, als_it);
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading reg_addr_als\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->regs_als = (u16)ret;
|
||||
lux *= chip->regs_als;
|
||||
lux *= als_info->calibscale;
|
||||
lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
|
||||
lux = div_u64(lux, CM3232_MLUX_PER_LUX);
|
||||
|
||||
if (lux > 0xFFFF)
|
||||
lux = 0xFFFF;
|
||||
|
||||
return (int)lux;
|
||||
}
|
||||
|
||||
static int cm3232_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cm3232_chip *chip = iio_priv(indio_dev);
|
||||
struct cm3232_als_info *als_info = chip->als_info;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = cm3232_get_lux(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
*val = als_info->calibscale;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
return cm3232_read_als_it(chip, val, val2);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cm3232_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct cm3232_chip *chip = iio_priv(indio_dev);
|
||||
struct cm3232_als_info *als_info = chip->als_info;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
als_info->calibscale = val;
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
return cm3232_write_als_it(chip, val, val2);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm3232_get_it_available() - Get available ALS IT value
|
||||
* @dev: pointer of struct device.
|
||||
* @attr: pointer of struct device_attribute.
|
||||
* @buf: pointer of return string buffer.
|
||||
*
|
||||
* Display the available integration time in second.
|
||||
*
|
||||
* Return: string length.
|
||||
*/
|
||||
static ssize_t cm3232_get_it_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
|
||||
cm3232_als_it_scales[i].val,
|
||||
cm3232_als_it_scales[i].val2);
|
||||
return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec cm3232_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
}
|
||||
};
|
||||
|
||||
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
|
||||
S_IRUGO, cm3232_get_it_available, NULL, 0);
|
||||
|
||||
static struct attribute *cm3232_attributes[] = {
|
||||
&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group cm3232_attribute_group = {
|
||||
.attrs = cm3232_attributes
|
||||
};
|
||||
|
||||
static const struct iio_info cm3232_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &cm3232_read_raw,
|
||||
.write_raw = &cm3232_write_raw,
|
||||
.attrs = &cm3232_attribute_group,
|
||||
};
|
||||
|
||||
static int cm3232_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct cm3232_chip *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
chip = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
chip->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = cm3232_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
|
||||
indio_dev->info = &cm3232_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = cm3232_reg_init(chip);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"%s: register init failed\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int cm3232_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
|
||||
CM3232_CMD_ALS_DISABLE);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cm3232_id[] = {
|
||||
{"cm3232", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, cm3232_id);
|
||||
|
||||
static const struct of_device_id cm3232_of_match[] = {
|
||||
{.compatible = "capella,cm3232"},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct i2c_driver cm3232_driver = {
|
||||
.driver = {
|
||||
.name = "cm3232",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(cm3232_of_match),
|
||||
},
|
||||
.id_table = cm3232_id,
|
||||
.probe = cm3232_probe,
|
||||
.remove = cm3232_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(cm3232_driver);
|
||||
|
||||
MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
|
||||
MODULE_DESCRIPTION("CM3232 ambient light sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -80,7 +80,6 @@ static int als_read_raw(struct iio_dev *indio_dev,
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
@ -97,15 +96,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
if (report_id >= 0) {
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&als_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&als_state->common_attributes,
|
||||
true);
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
als_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_ALS, address,
|
||||
@ -381,6 +373,7 @@ static struct platform_driver hid_als_platform_driver = {
|
||||
.id_table = hid_als_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_als_probe,
|
||||
.remove = hid_als_remove,
|
||||
|
@ -75,7 +75,6 @@ static int prox_read_raw(struct iio_dev *indio_dev,
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
@ -92,16 +91,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
if (report_id >= 0) {
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&prox_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&prox_state->common_attributes,
|
||||
true);
|
||||
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
prox_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_PROX, address,
|
||||
@ -373,6 +364,7 @@ static struct platform_driver hid_prox_platform_driver = {
|
||||
.id_table = hid_prox_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_prox_probe,
|
||||
.remove = hid_prox_remove,
|
||||
|
@ -149,8 +149,8 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev,
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
|
||||
*val = tcs3414_scales[i][0];
|
||||
i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
|
||||
*val = tcs3414_scales[i][0];
|
||||
*val2 = tcs3414_scales[i][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
|
@ -813,6 +813,7 @@ static int ak8975_probe(struct i2c_client *client,
|
||||
static const struct i2c_device_id ak8975_id[] = {
|
||||
{"ak8975", AK8975},
|
||||
{"ak8963", AK8963},
|
||||
{"AK8963", AK8963},
|
||||
{"ak09911", AK09911},
|
||||
{"ak09912", AK09912},
|
||||
{}
|
||||
|
@ -157,20 +157,12 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&magn_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&magn_state->common_attributes, true);
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
report_id =
|
||||
magn_state->magn[chan->address].report_id;
|
||||
address = magn_3d_addresses[chan->address];
|
||||
@ -530,6 +522,7 @@ static struct platform_driver hid_magn_3d_platform_driver = {
|
||||
.id_table = hid_magn_3d_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_magn_3d_probe,
|
||||
.remove = hid_magn_3d_remove,
|
||||
|
@ -111,20 +111,12 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&incl_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&incl_state->common_attributes, true);
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
report_id =
|
||||
incl_state->incl[chan->scan_index].report_id;
|
||||
address = incl_3d_addresses[chan->scan_index];
|
||||
@ -437,6 +429,7 @@ static struct platform_driver hid_incl_3d_platform_driver = {
|
||||
.id_table = hid_incl_3d_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_incl_3d_probe,
|
||||
.remove = hid_incl_3d_remove,
|
||||
|
@ -79,7 +79,6 @@ static int press_read_raw(struct iio_dev *indio_dev,
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
@ -96,15 +95,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
if (report_id >= 0) {
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&press_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
hid_sensor_power_state(&press_state->common_attributes,
|
||||
true);
|
||||
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
press_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_PRESSURE, address,
|
||||
@ -382,6 +374,7 @@ static struct platform_driver hid_press_platform_driver = {
|
||||
.id_table = hid_press_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_press_probe,
|
||||
.remove = hid_press_remove,
|
||||
|
@ -273,9 +273,9 @@ static void calibrate_as3935(struct as3935_state *st)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int as3935_suspend(struct spi_device *spi, pm_message_t msg)
|
||||
static int as3935_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct as3935_state *st = iio_priv(indio_dev);
|
||||
int val, ret;
|
||||
|
||||
@ -293,9 +293,9 @@ err_suspend:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int as3935_resume(struct spi_device *spi)
|
||||
static int as3935_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct as3935_state *st = iio_priv(indio_dev);
|
||||
int val, ret;
|
||||
|
||||
@ -311,9 +311,12 @@ err_resume:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume);
|
||||
#define AS3935_PM_OPS (&as3935_pm_ops)
|
||||
|
||||
#else
|
||||
#define as3935_suspend NULL
|
||||
#define as3935_resume NULL
|
||||
#define AS3935_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static int as3935_probe(struct spi_device *spi)
|
||||
@ -441,12 +444,11 @@ static struct spi_driver as3935_driver = {
|
||||
.driver = {
|
||||
.name = "as3935",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = AS3935_PM_OPS,
|
||||
},
|
||||
.probe = as3935_probe,
|
||||
.remove = as3935_remove,
|
||||
.id_table = as3935_id,
|
||||
.suspend = as3935_suspend,
|
||||
.resume = as3935_resume,
|
||||
};
|
||||
module_spi_driver(as3935_driver);
|
||||
|
||||
|
@ -59,7 +59,7 @@ static const char * const iio_ev_type_text[] = {
|
||||
[IIO_EV_TYPE_ROC] = "roc",
|
||||
[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
|
||||
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
|
||||
[IIO_EV_TYPE_INSTANCE] = "instance",
|
||||
[IIO_EV_TYPE_CHANGE] = "change",
|
||||
};
|
||||
|
||||
static const char * const iio_ev_dir_text[] = {
|
||||
@ -178,7 +178,7 @@ static bool event_is_known(struct iio_event_data *event)
|
||||
case IIO_EV_TYPE_ROC:
|
||||
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
||||
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
||||
case IIO_EV_TYPE_INSTANCE:
|
||||
case IIO_EV_TYPE_CHANGE:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
@ -73,7 +73,7 @@ static const struct iio_event_spec iio_dummy_event = {
|
||||
* simple step detect event - triggered when a step is detected
|
||||
*/
|
||||
static const struct iio_event_spec step_detect_event = {
|
||||
.type = IIO_EV_TYPE_INSTANCE,
|
||||
.type = IIO_EV_TYPE_CHANGE,
|
||||
.dir = IIO_EV_DIR_NONE,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
};
|
||||
|
@ -86,7 +86,7 @@ int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
|
||||
}
|
||||
case IIO_STEPS:
|
||||
switch (type) {
|
||||
case IIO_EV_TYPE_INSTANCE:
|
||||
case IIO_EV_TYPE_CHANGE:
|
||||
st->event_en = state;
|
||||
break;
|
||||
default:
|
||||
@ -201,7 +201,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
|
||||
IIO_EV_DIR_NONE,
|
||||
IIO_EV_TYPE_INSTANCE, 0, 0, 0),
|
||||
IIO_EV_TYPE_CHANGE, 0, 0, 0),
|
||||
iio_get_time_ns());
|
||||
break;
|
||||
default:
|
||||
|
@ -360,11 +360,11 @@ static ssize_t ad5933_show(struct device *dev,
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
switch ((u32) this_attr->address) {
|
||||
case AD5933_OUT_RANGE:
|
||||
len = sprintf(buf, "%d\n",
|
||||
len = sprintf(buf, "%u\n",
|
||||
st->range_avail[(st->ctrl_hb >> 1) & 0x3]);
|
||||
break;
|
||||
case AD5933_OUT_RANGE_AVAIL:
|
||||
len = sprintf(buf, "%d %d %d %d\n", st->range_avail[0],
|
||||
len = sprintf(buf, "%u %u %u %u\n", st->range_avail[0],
|
||||
st->range_avail[3], st->range_avail[2],
|
||||
st->range_avail[1]);
|
||||
break;
|
||||
|
@ -692,7 +692,7 @@ static ssize_t taos_luxtable_show(struct device *dev,
|
||||
int offset = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(taos_device_lux); i++) {
|
||||
offset += sprintf(buf + offset, "%d,%d,%d,",
|
||||
offset += sprintf(buf + offset, "%u,%u,%u,",
|
||||
taos_device_lux[i].ratio,
|
||||
taos_device_lux[i].ch0,
|
||||
taos_device_lux[i].ch1);
|
||||
|
@ -1147,7 +1147,7 @@ static ssize_t tsl2x7x_luxtable_show(struct device *dev,
|
||||
int offset = 0;
|
||||
|
||||
while (i < (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
|
||||
offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
|
||||
offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,%u,",
|
||||
chip->tsl2x7x_device_lux[i].ratio,
|
||||
chip->tsl2x7x_device_lux[i].ch0,
|
||||
chip->tsl2x7x_device_lux[i].ch1);
|
||||
|
119
include/dt-bindings/iio/qcom,spmi-vadc.h
Normal file
119
include/dt-bindings/iio/qcom,spmi-vadc.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_H
|
||||
#define _DT_BINDINGS_QCOM_SPMI_VADC_H
|
||||
|
||||
/* Voltage ADC channels */
|
||||
#define VADC_USBIN 0x00
|
||||
#define VADC_DCIN 0x01
|
||||
#define VADC_VCHG_SNS 0x02
|
||||
#define VADC_SPARE1_03 0x03
|
||||
#define VADC_USB_ID_MV 0x04
|
||||
#define VADC_VCOIN 0x05
|
||||
#define VADC_VBAT_SNS 0x06
|
||||
#define VADC_VSYS 0x07
|
||||
#define VADC_DIE_TEMP 0x08
|
||||
#define VADC_REF_625MV 0x09
|
||||
#define VADC_REF_1250MV 0x0a
|
||||
#define VADC_CHG_TEMP 0x0b
|
||||
#define VADC_SPARE1 0x0c
|
||||
#define VADC_SPARE2 0x0d
|
||||
#define VADC_GND_REF 0x0e
|
||||
#define VADC_VDD_VADC 0x0f
|
||||
|
||||
#define VADC_P_MUX1_1_1 0x10
|
||||
#define VADC_P_MUX2_1_1 0x11
|
||||
#define VADC_P_MUX3_1_1 0x12
|
||||
#define VADC_P_MUX4_1_1 0x13
|
||||
#define VADC_P_MUX5_1_1 0x14
|
||||
#define VADC_P_MUX6_1_1 0x15
|
||||
#define VADC_P_MUX7_1_1 0x16
|
||||
#define VADC_P_MUX8_1_1 0x17
|
||||
#define VADC_P_MUX9_1_1 0x18
|
||||
#define VADC_P_MUX10_1_1 0x19
|
||||
#define VADC_P_MUX11_1_1 0x1a
|
||||
#define VADC_P_MUX12_1_1 0x1b
|
||||
#define VADC_P_MUX13_1_1 0x1c
|
||||
#define VADC_P_MUX14_1_1 0x1d
|
||||
#define VADC_P_MUX15_1_1 0x1e
|
||||
#define VADC_P_MUX16_1_1 0x1f
|
||||
|
||||
#define VADC_P_MUX1_1_3 0x20
|
||||
#define VADC_P_MUX2_1_3 0x21
|
||||
#define VADC_P_MUX3_1_3 0x22
|
||||
#define VADC_P_MUX4_1_3 0x23
|
||||
#define VADC_P_MUX5_1_3 0x24
|
||||
#define VADC_P_MUX6_1_3 0x25
|
||||
#define VADC_P_MUX7_1_3 0x26
|
||||
#define VADC_P_MUX8_1_3 0x27
|
||||
#define VADC_P_MUX9_1_3 0x28
|
||||
#define VADC_P_MUX10_1_3 0x29
|
||||
#define VADC_P_MUX11_1_3 0x2a
|
||||
#define VADC_P_MUX12_1_3 0x2b
|
||||
#define VADC_P_MUX13_1_3 0x2c
|
||||
#define VADC_P_MUX14_1_3 0x2d
|
||||
#define VADC_P_MUX15_1_3 0x2e
|
||||
#define VADC_P_MUX16_1_3 0x2f
|
||||
|
||||
#define VADC_LR_MUX1_BAT_THERM 0x30
|
||||
#define VADC_LR_MUX2_BAT_ID 0x31
|
||||
#define VADC_LR_MUX3_XO_THERM 0x32
|
||||
#define VADC_LR_MUX4_AMUX_THM1 0x33
|
||||
#define VADC_LR_MUX5_AMUX_THM2 0x34
|
||||
#define VADC_LR_MUX6_AMUX_THM3 0x35
|
||||
#define VADC_LR_MUX7_HW_ID 0x36
|
||||
#define VADC_LR_MUX8_AMUX_THM4 0x37
|
||||
#define VADC_LR_MUX9_AMUX_THM5 0x38
|
||||
#define VADC_LR_MUX10_USB_ID 0x39
|
||||
#define VADC_AMUX_PU1 0x3a
|
||||
#define VADC_AMUX_PU2 0x3b
|
||||
#define VADC_LR_MUX3_BUF_XO_THERM 0x3c
|
||||
|
||||
#define VADC_LR_MUX1_PU1_BAT_THERM 0x70
|
||||
#define VADC_LR_MUX2_PU1_BAT_ID 0x71
|
||||
#define VADC_LR_MUX3_PU1_XO_THERM 0x72
|
||||
#define VADC_LR_MUX4_PU1_AMUX_THM1 0x73
|
||||
#define VADC_LR_MUX5_PU1_AMUX_THM2 0x74
|
||||
#define VADC_LR_MUX6_PU1_AMUX_THM3 0x75
|
||||
#define VADC_LR_MUX7_PU1_AMUX_HW_ID 0x76
|
||||
#define VADC_LR_MUX8_PU1_AMUX_THM4 0x77
|
||||
#define VADC_LR_MUX9_PU1_AMUX_THM5 0x78
|
||||
#define VADC_LR_MUX10_PU1_AMUX_USB_ID 0x79
|
||||
#define VADC_LR_MUX3_BUF_PU1_XO_THERM 0x7c
|
||||
|
||||
#define VADC_LR_MUX1_PU2_BAT_THERM 0xb0
|
||||
#define VADC_LR_MUX2_PU2_BAT_ID 0xb1
|
||||
#define VADC_LR_MUX3_PU2_XO_THERM 0xb2
|
||||
#define VADC_LR_MUX4_PU2_AMUX_THM1 0xb3
|
||||
#define VADC_LR_MUX5_PU2_AMUX_THM2 0xb4
|
||||
#define VADC_LR_MUX6_PU2_AMUX_THM3 0xb5
|
||||
#define VADC_LR_MUX7_PU2_AMUX_HW_ID 0xb6
|
||||
#define VADC_LR_MUX8_PU2_AMUX_THM4 0xb7
|
||||
#define VADC_LR_MUX9_PU2_AMUX_THM5 0xb8
|
||||
#define VADC_LR_MUX10_PU2_AMUX_USB_ID 0xb9
|
||||
#define VADC_LR_MUX3_BUF_PU2_XO_THERM 0xbc
|
||||
|
||||
#define VADC_LR_MUX1_PU1_PU2_BAT_THERM 0xf0
|
||||
#define VADC_LR_MUX2_PU1_PU2_BAT_ID 0xf1
|
||||
#define VADC_LR_MUX3_PU1_PU2_XO_THERM 0xf2
|
||||
#define VADC_LR_MUX4_PU1_PU2_AMUX_THM1 0xf3
|
||||
#define VADC_LR_MUX5_PU1_PU2_AMUX_THM2 0xf4
|
||||
#define VADC_LR_MUX6_PU1_PU2_AMUX_THM3 0xf5
|
||||
#define VADC_LR_MUX7_PU1_PU2_AMUX_HW_ID 0xf6
|
||||
#define VADC_LR_MUX8_PU1_PU2_AMUX_THM4 0xf7
|
||||
#define VADC_LR_MUX9_PU1_PU2_AMUX_THM5 0xf8
|
||||
#define VADC_LR_MUX10_PU1_PU2_AMUX_USB_ID 0xf9
|
||||
#define VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM 0xfc
|
||||
|
||||
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_H */
|
82
include/linux/iio/common/ssp_sensors.h
Normal file
82
include/linux/iio/common/ssp_sensors.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef _SSP_SENSORS_H_
|
||||
#define _SSP_SENSORS_H_
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define SSP_TIME_SIZE 4
|
||||
#define SSP_ACCELEROMETER_SIZE 6
|
||||
#define SSP_GYROSCOPE_SIZE 6
|
||||
#define SSP_BIO_HRM_RAW_SIZE 8
|
||||
#define SSP_BIO_HRM_RAW_FAC_SIZE 36
|
||||
#define SSP_BIO_HRM_LIB_SIZE 8
|
||||
|
||||
/**
|
||||
* enum ssp_sensor_type - SSP sensor type
|
||||
*/
|
||||
enum ssp_sensor_type {
|
||||
SSP_ACCELEROMETER_SENSOR = 0,
|
||||
SSP_GYROSCOPE_SENSOR,
|
||||
SSP_GEOMAGNETIC_UNCALIB_SENSOR,
|
||||
SSP_GEOMAGNETIC_RAW,
|
||||
SSP_GEOMAGNETIC_SENSOR,
|
||||
SSP_PRESSURE_SENSOR,
|
||||
SSP_GESTURE_SENSOR,
|
||||
SSP_PROXIMITY_SENSOR,
|
||||
SSP_TEMPERATURE_HUMIDITY_SENSOR,
|
||||
SSP_LIGHT_SENSOR,
|
||||
SSP_PROXIMITY_RAW,
|
||||
SSP_ORIENTATION_SENSOR,
|
||||
SSP_STEP_DETECTOR,
|
||||
SSP_SIG_MOTION_SENSOR,
|
||||
SSP_GYRO_UNCALIB_SENSOR,
|
||||
SSP_GAME_ROTATION_VECTOR,
|
||||
SSP_ROTATION_VECTOR,
|
||||
SSP_STEP_COUNTER,
|
||||
SSP_BIO_HRM_RAW,
|
||||
SSP_BIO_HRM_RAW_FAC,
|
||||
SSP_BIO_HRM_LIB,
|
||||
SSP_SENSOR_MAX,
|
||||
};
|
||||
|
||||
struct ssp_data;
|
||||
|
||||
/**
|
||||
* struct ssp_sensor_data - Sensor object
|
||||
* @process_data: Callback to feed sensor data.
|
||||
* @type: Used sensor type.
|
||||
* @buffer: Received data buffer.
|
||||
*/
|
||||
struct ssp_sensor_data {
|
||||
int (*process_data)(struct iio_dev *indio_dev, void *buf,
|
||||
int64_t timestamp);
|
||||
enum ssp_sensor_type type;
|
||||
u8 *buffer;
|
||||
};
|
||||
|
||||
void ssp_register_consumer(struct iio_dev *indio_dev,
|
||||
enum ssp_sensor_type type);
|
||||
|
||||
int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
|
||||
u32 delay);
|
||||
|
||||
int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type);
|
||||
|
||||
u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type);
|
||||
|
||||
int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
|
||||
u32 delay);
|
||||
#endif /* _SSP_SENSORS_H_ */
|
@ -40,6 +40,9 @@ enum iio_chan_info_enum {
|
||||
IIO_CHAN_INFO_INT_TIME,
|
||||
IIO_CHAN_INFO_ENABLE,
|
||||
IIO_CHAN_INFO_CALIBHEIGHT,
|
||||
IIO_CHAN_INFO_CALIBWEIGHT,
|
||||
IIO_CHAN_INFO_DEBOUNCE_COUNT,
|
||||
IIO_CHAN_INFO_DEBOUNCE_TIME,
|
||||
};
|
||||
|
||||
enum iio_shared_by {
|
||||
@ -286,10 +289,11 @@ static inline s64 iio_get_time_ns(void)
|
||||
/* Device operating modes */
|
||||
#define INDIO_DIRECT_MODE 0x01
|
||||
#define INDIO_BUFFER_TRIGGERED 0x02
|
||||
#define INDIO_BUFFER_SOFTWARE 0x04
|
||||
#define INDIO_BUFFER_HARDWARE 0x08
|
||||
|
||||
#define INDIO_ALL_BUFFER_MODES \
|
||||
(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE)
|
||||
(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
|
||||
|
||||
#define INDIO_MAX_RAW_ELEMENTS 4
|
||||
|
||||
@ -593,7 +597,8 @@ void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig);
|
||||
static inline bool iio_buffer_enabled(struct iio_dev *indio_dev)
|
||||
{
|
||||
return indio_dev->currentmode
|
||||
& (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE);
|
||||
& (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE |
|
||||
INDIO_BUFFER_SOFTWARE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,9 @@ enum iio_chan_type {
|
||||
IIO_HUMIDITYRELATIVE,
|
||||
IIO_ACTIVITY,
|
||||
IIO_STEPS,
|
||||
IIO_ENERGY,
|
||||
IIO_DISTANCE,
|
||||
IIO_VELOCITY,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
@ -66,6 +69,7 @@ enum iio_modifier {
|
||||
IIO_MOD_JOGGING,
|
||||
IIO_MOD_WALKING,
|
||||
IIO_MOD_STILL,
|
||||
IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
|
||||
};
|
||||
|
||||
enum iio_event_type {
|
||||
@ -74,7 +78,7 @@ enum iio_event_type {
|
||||
IIO_EV_TYPE_ROC,
|
||||
IIO_EV_TYPE_THRESH_ADAPTIVE,
|
||||
IIO_EV_TYPE_MAG_ADAPTIVE,
|
||||
IIO_EV_TYPE_INSTANCE,
|
||||
IIO_EV_TYPE_CHANGE,
|
||||
};
|
||||
|
||||
enum iio_event_info {
|
||||
|
Loading…
Reference in New Issue
Block a user