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