Staging driver patches for 4.2-rc1

Here's the big, really big, staging tree patches for 4.2-rc1.
 
 Loads of stuff in here, almost all just coding style fixes / churn, and
 a few new drivers as well, one of which I just disabled from the build a
 few minutes ago due to way too many build warnings.
 
 Other than the one "disable this driver" patch, all of these have been
 in linux-next for quite a while with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlWNpc0ACgkQMUfUDdst+ym8EgCg0pL1Qcf9Se3jAc96fLt+itpv
 Rd0AoI9uJcq8Qm7d+IXnz3ojLnN9xvN3
 =xt0u
 -----END PGP SIGNATURE-----

Merge tag 'staging-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging driver updates from Greg KH:
 "Here's the big, really big, staging tree patches for 4.2-rc1.

  Loads of stuff in here, almost all just coding style fixes / churn,
  and a few new drivers as well, one of which I just disabled from the
  build a few minutes ago due to way too many build warnings.

  Other than the one "disable this driver" patch, all of these have been
  in linux-next for quite a while with no reported issues"

* tag 'staging-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1163 commits)
  staging: wilc1000: disable driver due to build warnings
  Staging: rts5208: fix CHANGE_LINK_STATE value
  Staging: sm750fb: ddk750_swi2c.c: Insert spaces before parenthesis
  Staging: sm750fb: ddk750_swi2c.c: Place braces on correct lines
  Staging: sm750fb: ddk750_swi2c.c: Insert spaces around operators
  Staging: sm750fb: ddk750_swi2c.c: Replace spaces with tabs
  Staging: sm750fb: ddk750_swi2c.h: Shorten lines to under 80 characters
  Staging: sm750fb: ddk750_swi2c.h: Replace spaces with tabs
  Staging: sm750fb: modedb.h: Shorten lines to under 80 characters
  Staging: sm750fb: modedb.h: Replace spaces with tabs
  staging: comedi: addi_apci_3120: rename 'this_board' variables
  staging: comedi: addi_apci_1516: rename 'this_board' variables
  staging: comedi: ni_atmio: cleanup ni_getboardtype()
  staging: comedi: vmk80xx: sanity check context used to get the boardinfo
  staging: comedi: vmk80xx: rename 'boardinfo' variables
  staging: comedi: dt3000: rename 'this_board' variables
  staging: comedi: adv_pci_dio: rename 'this_board' variables
  staging: comedi: cb_pcidas64: rename 'thisboard' variables
  staging: comedi: cb_pcidas: rename 'thisboard' variables
  staging: comedi: me4000: rename 'thisboard' variables
  ...
This commit is contained in:
Linus Torvalds 2015-06-26 15:46:08 -07:00
commit 23908db413
767 changed files with 64663 additions and 50270 deletions

View file

@ -71,6 +71,8 @@ Description:
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_raw
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@ -81,6 +83,11 @@ Description:
unique to allow association with event codes. Units after
application of scale and offset are millivolts.
Channels with 'i' and 'q' modifiers always exist in pairs and both
channels refer to the same signal. The 'i' channel contains the in-phase
component of the signal while the 'q' channel contains the quadrature
component.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_raw
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
@ -246,8 +253,16 @@ What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_current_offset
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_current_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_current_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
@ -273,14 +288,22 @@ Description:
to the _raw output.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_scale
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale
What: /sys/bus/iio/devices/iio:deviceX/in_current_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_current_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_current_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
@ -328,6 +351,10 @@ Description:
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale
@ -420,6 +447,16 @@ Description:
to the underlying data channel, then this parameter
gives the 3dB frequency of the filter in Hz.
What: /sys/.../in_accel_filter_high_pass_3db_frequency
What: /sys/.../in_anglvel_filter_high_pass_3db_frequency
What: /sys/.../in_magn_filter_high_pass_3db_frequency
KernelVersion: 4.2
Contact: linux-iio@vger.kernel.org
Description:
If a known or controllable high pass filter is applied
to the underlying data channel, then this parameter
gives the 3dB frequency of the filter in Hz.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_raw
KernelVersion: 2.6.37
@ -880,6 +917,26 @@ Description:
met before an event is generated. If direction is not
specified then this period applies to both directions.
What: /sys/.../events/in_accel_thresh_rising_low_pass_filter_3db
What: /sys/.../events/in_anglvel_thresh_rising_low_pass_filter_3db
What: /sys/.../events/in_magn_thresh_rising_low_pass_filter_3db
KernelVersion: 4.2
Contact: linux-iio@vger.kernel.org
Description:
If a low pass filter can be applied to the event generation
this property gives its 3db frequency in Hz.
A value of zero disables the filter.
What: /sys/.../events/in_accel_thresh_rising_high_pass_filter_3db
What: /sys/.../events/in_anglvel_thresh_rising_high_pass_filter_3db
What: /sys/.../events/in_magn_thresh_rising_high_pass_filter_3db
KernelVersion: 4.2
Contact: linux-iio@vger.kernel.org
Description:
If a high pass filter can be applied to the event generation
this property gives its 3db frequency in Hz.
A value of zero disables the filter.
What: /sys/.../events/in_activity_still_thresh_rising_en
What: /sys/.../events/in_activity_still_thresh_falling_en
What: /sys/.../events/in_activity_walking_thresh_rising_en
@ -1016,6 +1073,10 @@ What: /sys/.../iio:deviceX/scan_elements/in_timestamp_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY-voltageZ_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en
@ -1034,6 +1095,10 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_type
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type
What: /sys/.../iio:deviceX/scan_elements/in_pressure_type
@ -1071,6 +1136,10 @@ Description:
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_x_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_y_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_z_index
@ -1230,6 +1299,8 @@ Description:
or without compensation from tilt sensors.
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_i_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_q_raw
KernelVersion: 3.18
Contact: linux-iio@vger.kernel.org
Description:
@ -1238,6 +1309,11 @@ Description:
present, output should be considered as processed with the
unit in milliamps.
Channels with 'i' and 'q' modifiers always exist in pairs and both
channels refer to the same signal. The 'i' channel contains the in-phase
component of the signal while the 'q' channel contains the quadrature
component.
What: /sys/.../iio:deviceX/in_energy_en
What: /sys/.../iio:deviceX/in_distance_en
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
@ -1364,3 +1440,26 @@ Description:
hwfifo_watermak_min but not equal to any of the values in this
list, the driver will chose an appropriate value for the
hardware fifo watermark level.
What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_temp_object_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_object_calibemissivity
KernelVersion: 4.1
Contact: linux-iio@vger.kernel.org
Description:
The emissivity ratio of the surface in the field of view of the
contactless temperature sensor. Emissivity varies from 0 to 1,
with 1 being the emissivity of a black body.
What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_oversampling_ratio
What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_oversampling_ratio
What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_oversampling_ratio
KernelVersion: 4.2
Contact: linux-iio@vger.kernel.org
Description:
Hardware applied number of measurements for acquiring one
data point. The HW will do <type>[_name]_oversampling_ratio
measurements and return the average value as output data. Each
value resulted from <type>[_name]_oversampling_ratio measurements
is considered as one sample for <type>[_name]_sampling_frequency.

View file

@ -0,0 +1,7 @@
What: /sys/bus/iio/devices/iio:deviceX/conversion_mode
KernelVersion: 4.2
Contact: linux-iio@vger.kernel.org
Description:
Specifies the hardware conversion mode used. The three
available modes are "normal", "high-speed" and "low-power",
where the last is the default mode.

View file

@ -0,0 +1,19 @@
* Berlin Analog to Digital Converter (ADC)
The Berlin ADC has 8 channels, with one connected to a temperature sensor.
It is part of the system controller register set. The ADC node should be a
sub-node of the system controller node.
Required properties:
- compatible: must be "marvell,berlin2-adc"
- interrupts: the interrupts for the ADC and the temperature sensor
- interrupt-names: should be "adc" and "tsen"
Example:
adc: adc {
compatible = "marvell,berlin2-adc";
interrupt-parent = <&sic>;
interrupts = <12>, <14>;
interrupt-names = "adc", "tsen";
};

View file

@ -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

View file

@ -11,6 +11,13 @@ Required properties:
- clock-names: Must contain "adc", matching entry in the clocks property.
- vref-supply: The regulator supply ADC reference voltage.
Recommended properties:
- fsl,adck-max-frequency: Maximum frequencies according to datasheets operating
requirements. Three values are required, depending on conversion mode:
- Frequency in normal mode (ADLPC=0, ADHSC=0)
- Frequency in high-speed mode (ADLPC=0, ADHSC=1)
- Frequency in low-power mode (ADLPC=1, ADHSC=0)
Example:
adc0: adc@4003b000 {
compatible = "fsl,vf610-adc";
@ -18,5 +25,7 @@ adc0: adc@4003b000 {
interrupts = <0 53 0x04>;
clocks = <&clks VF610_CLK_ADC0>;
clock-names = "adc";
fsl,adck-max-frequency = <30000000>, <40000000>,
<20000000>;
vref-supply = <&reg_vcc_3v3_mcu>;
};

View file

@ -0,0 +1,17 @@
InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
http://www.invensense.com/mems/gyro/mpu6050.html
Required properties:
- compatible : should be "invensense,mpu6050"
- reg : the I2C address of the sensor
- interrupt-parent : should be the phandle for the interrupt controller
- interrupts : interrupt mapping for GPIO IRQ
Example:
mpu6050@68 {
compatible = "invensense,mpu6050";
reg = <0x68>;
interrupt-parent = <&gpio1>;
interrupts = <18 1>;
};

View file

@ -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>;
};

View file

@ -30,6 +30,7 @@ Accelerometers:
- st,lsm330d-accel
- st,lsm330dl-accel
- st,lsm330dlc-accel
- st,lis331dl-accel
- st,lis331dlh-accel
- st,lsm303dl-accel
- st,lsm303dlm-accel
@ -45,6 +46,7 @@ Gyroscopes:
- st,lsm330-gyro
Magnetometers:
- st,lsm303dlh-magn
- st,lsm303dlhc-magn
- st,lsm303dlm-magn
- st,lis3mdl-magn

View file

@ -0,0 +1,24 @@
* Melexis MLX90614 contactless IR temperature sensor
http://melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90614-615.aspx
Required properties:
- compatible: should be "melexis,mlx90614"
- reg: the I2C address of the sensor
Optional properties:
- wakeup-gpios: device tree identifier of the GPIO connected to the SDA line
to hold low in order to wake up the device. In normal operation, the
GPIO is set as input and will not interfere in I2C communication. There
is no need for a GPIO driving the SCL line. If no GPIO is given, power
management is disabled.
Example:
mlx90614@5a {
compatible = "melexis,mlx90614";
reg = <0x5a>;
wakeup-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
};

View file

@ -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>;
};
}

View file

@ -123,6 +123,7 @@ lltc Linear Technology Corporation
marvell Marvell Technology Group Ltd.
maxim Maxim Integrated Products
mediatek MediaTek Inc.
melexis Melexis N.V.
merrii Merrii Technology Co., Ltd.
micrel Micrel Inc.
microchip Microchip Technology Inc.

View file

@ -171,6 +171,12 @@ This functions by reading the offset, applying the mask. If the bits selected in
the mask match with the values of the corresponding bits in the compare field,
the value of swap is written the specified offset.
Parts of a VME window can be mapped into user space memory using the following
function:
int vme_master_mmap(struct vme_resource *resource,
struct vm_area_struct *vma)
Slave windows
=============

View file

@ -9696,6 +9696,15 @@ M: Forest Bond <forest@alittletooquiet.net>
S: Odd Fixes
F: drivers/staging/vt665?/
STAGING - WILC1000 WIFI DRIVER
M: Johnny Kim <johnny.kim@atmel.com>
M: Rachel Kim <rachel.kim@atmel.com>
M: Dean Lee <dean.lee@atmel.com>
M: Chris Park <chris.park@atmel.com>
L: linux-wireless@vger.kernel.org
S: Supported
F: drivers/staging/wilc1000/
STAGING - XGI Z7,Z9,Z11 PCI DISPLAY DRIVER
M: Arnaud Patard <arnaud.patard@rtp-net.org>
S: Odd Fixes

View file

@ -98,7 +98,6 @@ obj-$(CONFIG_USB_GADGET) += usb/
obj-$(CONFIG_SERIO) += input/serio/
obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_INPUT) += input/
obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_RTC_LIB) += rtc/
obj-y += i2c/ media/
obj-$(CONFIG_PPS) += pps/

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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",

View file

@ -875,15 +875,18 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
return 0;
}
static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev,
struct iio_trigger *trig)
static int kxcjk1013_buffer_preenable(struct iio_dev *indio_dev)
{
struct kxcjk1013_data *data = iio_priv(indio_dev);
if (data->dready_trig != trig && data->motion_trig != trig)
return -EINVAL;
return kxcjk1013_set_power_state(data, true);
}
return 0;
static int kxcjk1013_buffer_postdisable(struct iio_dev *indio_dev)
{
struct kxcjk1013_data *data = iio_priv(indio_dev);
return kxcjk1013_set_power_state(data, false);
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
@ -935,6 +938,13 @@ static const struct iio_chan_spec kxcjk1013_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = {
.preenable = kxcjk1013_buffer_preenable,
.postenable = iio_triggered_buffer_postenable,
.postdisable = kxcjk1013_buffer_postdisable,
.predisable = iio_triggered_buffer_predisable,
};
static const struct iio_info kxcjk1013_info = {
.attrs = &kxcjk1013_attrs_group,
.read_raw = kxcjk1013_read_raw,
@ -943,7 +953,6 @@ static const struct iio_info kxcjk1013_info = {
.write_event_value = kxcjk1013_write_event,
.write_event_config = kxcjk1013_write_event_config,
.read_event_config = kxcjk1013_read_event_config,
.validate_trigger = kxcjk1013_validate_trigger,
.driver_module = THIS_MODULE,
};
@ -1147,8 +1156,10 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
if (strcmp(id->id, "SMO8500") == 0)
*is_smo8500_device = true;
*chipset = (enum kx_chipset)id->driver_data;
return dev_name(dev);
@ -1163,6 +1174,7 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
if (!client)
return -EINVAL;
if (data->is_smo8500_device)
return -ENOTSUPP;
@ -1276,16 +1288,15 @@ static int kxcjk1013_probe(struct i2c_client *client,
data->motion_trig = NULL;
goto err_trigger_unregister;
}
}
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
kxcjk1013_trigger_handler,
NULL);
if (ret < 0) {
dev_err(&client->dev,
"iio triggered buffer setup failed\n");
goto err_trigger_unregister;
}
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
kxcjk1013_trigger_handler,
&kxcjk1013_buffer_setup_ops);
if (ret < 0) {
dev_err(&client->dev, "iio triggered buffer setup failed\n");
goto err_trigger_unregister;
}
ret = iio_device_register(indio_dev);
@ -1418,6 +1429,7 @@ static const struct dev_pm_ops kxcjk1013_pm_ops = {
static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXCJ9000", KXCJ91008},
{"KXTJ1009", KXTJ21009},
{"SMO8500", KXCJ91008},
{ },

View file

@ -9,7 +9,7 @@
*
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
*
* TODO: interrupt, thresholding, orientation / freefall events, autosleep
* TODO: orientation / freefall events, autosleep
*/
#include <linux/module.h>
@ -18,20 +18,42 @@
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/events.h>
#include <linux/delay.h>
#define MMA8452_STATUS 0x00
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
#define MMA8452_OUT_Y 0x03
#define MMA8452_OUT_Z 0x05
#define MMA8452_INT_SRC 0x0c
#define MMA8452_WHO_AM_I 0x0d
#define MMA8452_DATA_CFG 0x0e
#define MMA8452_HP_FILTER_CUTOFF 0x0f
#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK (BIT(0) | BIT(1))
#define MMA8452_TRANSIENT_CFG 0x1d
#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
#define MMA8452_TRANSIENT_SRC 0x1e
#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3)
#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5)
#define MMA8452_TRANSIENT_THS 0x1f
#define MMA8452_TRANSIENT_THS_MASK 0x7f
#define MMA8452_TRANSIENT_COUNT 0x20
#define MMA8452_OFF_X 0x2f
#define MMA8452_OFF_Y 0x30
#define MMA8452_OFF_Z 0x31
#define MMA8452_CTRL_REG1 0x2a
#define MMA8452_CTRL_REG2 0x2b
#define MMA8452_CTRL_REG2_RST BIT(6)
#define MMA8452_CTRL_REG4 0x2d
#define MMA8452_CTRL_REG5 0x2e
#define MMA8452_MAX_REG 0x31
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
@ -44,6 +66,10 @@
#define MMA8452_DATA_CFG_FS_2G 0
#define MMA8452_DATA_CFG_FS_4G 1
#define MMA8452_DATA_CFG_FS_8G 2
#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
#define MMA8452_INT_DRDY BIT(0)
#define MMA8452_INT_TRANS BIT(5)
#define MMA8452_DEVICE_ID 0x2a
@ -106,6 +132,12 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
return -EINVAL;
}
static int mma8452_get_odr_index(struct mma8452_data *data)
{
return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
MMA8452_CTRL_DR_SHIFT;
}
static const int mma8452_samp_freq[8][2] = {
{800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
{6, 250000}, {1, 560000}
@ -121,6 +153,30 @@ static const int mma8452_scales[3][2] = {
{0, 9577}, {0, 19154}, {0, 38307}
};
/* Datasheet table 35 (step time vs sample frequency) */
static const int mma8452_transient_time_step_us[8] = {
1250,
2500,
5000,
10000,
20000,
20000,
20000,
20000
};
/* Datasheet table 18 (normal mode) */
static const int mma8452_hp_filter_cutoff[8][4][2] = {
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */
{ {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */
{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, /* 100 Hz sample */
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 50 Hz sample */
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */
};
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -135,9 +191,23 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
ARRAY_SIZE(mma8452_scales));
}
static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct mma8452_data *data = iio_priv(indio_dev);
int i = mma8452_get_odr_index(data);
return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i],
ARRAY_SIZE(mma8452_hp_filter_cutoff[0]));
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
mma8452_show_scale_avail, NULL, 0);
static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0);
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
int val, int val2)
@ -153,6 +223,31 @@ static int mma8452_get_scale_index(struct mma8452_data *data,
ARRAY_SIZE(mma8452_scales), val, val2);
}
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
int val, int val2)
{
int i = mma8452_get_odr_index(data);
return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i],
ARRAY_SIZE(mma8452_scales[0]), val, val2);
}
static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
{
int i, ret;
ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF);
if (ret < 0)
return ret;
i = mma8452_get_odr_index(data);
ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
*hz = mma8452_hp_filter_cutoff[i][ret][0];
*uHz = mma8452_hp_filter_cutoff[i][ret][1];
return 0;
}
static int mma8452_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@ -180,8 +275,7 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
*val2 = mma8452_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
MMA8452_CTRL_DR_SHIFT;
i = mma8452_get_odr_index(data);
*val = mma8452_samp_freq[i][0];
*val2 = mma8452_samp_freq[i][1];
return IIO_VAL_INT_PLUS_MICRO;
@ -192,6 +286,16 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
return ret;
*val = sign_extend32(ret, 7);
return IIO_VAL_INT;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
ret = mma8452_read_hp_filter(data, val, val2);
if (ret < 0)
return ret;
} else {
*val = 0;
*val2 = 0;
}
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
@ -233,12 +337,31 @@ fail:
return ret;
}
static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
int val, int val2)
{
int i, reg;
i = mma8452_get_hp_filter_index(data, val, val2);
if (i < 0)
return -EINVAL;
reg = i2c_smbus_read_byte_data(data->client,
MMA8452_HP_FILTER_CUTOFF);
if (reg < 0)
return reg;
reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
reg |= i;
return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg);
}
static int mma8452_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct mma8452_data *data = iio_priv(indio_dev);
int i;
int i, ret;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
@ -266,11 +389,217 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
return mma8452_change_config(data, MMA8452_OFF_X +
chan->scan_index, val);
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
if (val == 0 && val2 == 0) {
data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK;
} else {
data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
ret = mma8452_set_hp_filter_frequency(data, val, val2);
if (ret < 0)
return ret;
}
return mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
default:
return -EINVAL;
}
}
static int mma8452_read_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
struct mma8452_data *data = iio_priv(indio_dev);
int ret, us;
switch (info) {
case IIO_EV_INFO_VALUE:
ret = i2c_smbus_read_byte_data(data->client,
MMA8452_TRANSIENT_THS);
if (ret < 0)
return ret;
*val = ret & MMA8452_TRANSIENT_THS_MASK;
return IIO_VAL_INT;
case IIO_EV_INFO_PERIOD:
ret = i2c_smbus_read_byte_data(data->client,
MMA8452_TRANSIENT_COUNT);
if (ret < 0)
return ret;
us = ret * mma8452_transient_time_step_us[
mma8452_get_odr_index(data)];
*val = us / USEC_PER_SEC;
*val2 = us % USEC_PER_SEC;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
ret = i2c_smbus_read_byte_data(data->client,
MMA8452_TRANSIENT_CFG);
if (ret < 0)
return ret;
if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) {
*val = 0;
*val2 = 0;
} else {
ret = mma8452_read_hp_filter(data, val, val2);
if (ret < 0)
return ret;
}
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int mma8452_write_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
struct mma8452_data *data = iio_priv(indio_dev);
int ret, reg, steps;
switch (info) {
case IIO_EV_INFO_VALUE:
return mma8452_change_config(data, MMA8452_TRANSIENT_THS,
val & MMA8452_TRANSIENT_THS_MASK);
case IIO_EV_INFO_PERIOD:
steps = (val * USEC_PER_SEC + val2) /
mma8452_transient_time_step_us[
mma8452_get_odr_index(data)];
if (steps > 0xff)
return -EINVAL;
return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
steps);
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
reg = i2c_smbus_read_byte_data(data->client,
MMA8452_TRANSIENT_CFG);
if (reg < 0)
return reg;
if (val == 0 && val2 == 0) {
reg |= MMA8452_TRANSIENT_CFG_HPF_BYP;
} else {
reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP;
ret = mma8452_set_hp_filter_frequency(data, val, val2);
if (ret < 0)
return ret;
}
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
default:
return -EINVAL;
}
}
static int mma8452_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct mma8452_data *data = iio_priv(indio_dev);
int ret;
ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
if (ret < 0)
return ret;
return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0;
}
static int mma8452_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
int state)
{
struct mma8452_data *data = iio_priv(indio_dev);
int val;
val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
if (val < 0)
return val;
if (state)
val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
else
val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
val |= MMA8452_TRANSIENT_CFG_ELE;
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val);
}
static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
{
struct mma8452_data *data = iio_priv(indio_dev);
s64 ts = iio_get_time_ns();
int src;
src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
if (src < 0)
return;
if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
ts);
if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
ts);
if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
ts);
}
static irqreturn_t mma8452_interrupt(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct mma8452_data *data = iio_priv(indio_dev);
int ret = IRQ_NONE;
int src;
src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC);
if (src < 0)
return IRQ_NONE;
if (src & MMA8452_INT_DRDY) {
iio_trigger_poll_chained(indio_dev->trig);
ret = IRQ_HANDLED;
}
if (src & MMA8452_INT_TRANS) {
mma8452_transient_interrupt(indio_dev);
ret = IRQ_HANDLED;
}
return ret;
}
static irqreturn_t mma8452_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
@ -291,6 +620,55 @@ done:
return IRQ_HANDLED;
}
static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
{
int ret;
struct mma8452_data *data = iio_priv(indio_dev);
if (reg > MMA8452_MAX_REG)
return -EINVAL;
if (!readval)
return mma8452_change_config(data, reg, writeval);
ret = i2c_smbus_read_byte_data(data->client, reg);
if (ret < 0)
return ret;
*readval = ret;
return 0;
}
static const struct iio_event_spec mma8452_transient_event[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD) |
BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
},
};
/*
* Threshold is configured in fixed 8G/127 steps regardless of
* currently selected scale for measurement.
*/
static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742");
static struct attribute *mma8452_event_attributes[] = {
&iio_const_attr_accel_transient_scale.dev_attr.attr,
NULL,
};
static struct attribute_group mma8452_event_attribute_group = {
.attrs = mma8452_event_attributes,
.name = "events",
};
#define MMA8452_CHANNEL(axis, idx) { \
.type = IIO_ACCEL, \
.modified = 1, \
@ -298,7 +676,8 @@ done:
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
@ -307,6 +686,8 @@ done:
.shift = 4, \
.endianness = IIO_BE, \
}, \
.event_spec = mma8452_transient_event, \
.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
}
static const struct iio_chan_spec mma8452_channels[] = {
@ -319,6 +700,7 @@ static const struct iio_chan_spec mma8452_channels[] = {
static struct attribute *mma8452_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
&iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr,
NULL
};
@ -330,11 +712,107 @@ static const struct iio_info mma8452_info = {
.attrs = &mma8452_group,
.read_raw = &mma8452_read_raw,
.write_raw = &mma8452_write_raw,
.event_attrs = &mma8452_event_attribute_group,
.read_event_value = &mma8452_read_thresh,
.write_event_value = &mma8452_write_thresh,
.read_event_config = &mma8452_read_event_config,
.write_event_config = &mma8452_write_event_config,
.debugfs_reg_access = &mma8452_reg_access_dbg,
.driver_module = THIS_MODULE,
};
static const unsigned long mma8452_scan_masks[] = {0x7, 0};
static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct mma8452_data *data = iio_priv(indio_dev);
int reg;
reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
if (reg < 0)
return reg;
if (state)
reg |= MMA8452_INT_DRDY;
else
reg &= ~MMA8452_INT_DRDY;
return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
}
static int mma8452_validate_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
{
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
if (indio != indio_dev)
return -EINVAL;
return 0;
}
static const struct iio_trigger_ops mma8452_trigger_ops = {
.set_trigger_state = mma8452_data_rdy_trigger_set_state,
.validate_device = mma8452_validate_device,
.owner = THIS_MODULE,
};
static int mma8452_trigger_setup(struct iio_dev *indio_dev)
{
struct mma8452_data *data = iio_priv(indio_dev);
struct iio_trigger *trig;
int ret;
trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!trig)
return -ENOMEM;
trig->dev.parent = &data->client->dev;
trig->ops = &mma8452_trigger_ops;
iio_trigger_set_drvdata(trig, indio_dev);
ret = iio_trigger_register(trig);
if (ret)
return ret;
indio_dev->trig = trig;
return 0;
}
static void mma8452_trigger_cleanup(struct iio_dev *indio_dev)
{
if (indio_dev->trig)
iio_trigger_unregister(indio_dev->trig);
}
static int mma8452_reset(struct i2c_client *client)
{
int i;
int ret;
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2,
MMA8452_CTRL_REG2_RST);
if (ret < 0)
return ret;
for (i = 0; i < 10; i++) {
usleep_range(100, 200);
ret = i2c_smbus_read_byte_data(client, MMA8452_CTRL_REG2);
if (ret == -EIO)
continue; /* I2C comm reset */
if (ret < 0)
return ret;
if (!(ret & MMA8452_CTRL_REG2_RST))
return 0;
}
return -ETIMEDOUT;
}
static int mma8452_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -365,10 +843,7 @@ static int mma8452_probe(struct i2c_client *client,
indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
indio_dev->available_scan_masks = mma8452_scan_masks;
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
data->ctrl_reg1);
ret = mma8452_reset(client);
if (ret < 0)
return ret;
@ -378,18 +853,77 @@ static int mma8452_probe(struct i2c_client *client,
if (ret < 0)
return ret;
/*
* By default set transient threshold to max to avoid events if
* enabling without configuring threshold.
*/
ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
MMA8452_TRANSIENT_THS_MASK);
if (ret < 0)
return ret;
if (client->irq) {
/*
* Although we enable the transient interrupt source once and
* for all here the transient event detection itself is not
* enabled until userspace asks for it by
* mma8452_write_event_config()
*/
int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS;
int enabled_interrupts = MMA8452_INT_TRANS;
/* Assume wired to INT1 pin */
ret = i2c_smbus_write_byte_data(client,
MMA8452_CTRL_REG5,
supported_interrupts);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client,
MMA8452_CTRL_REG4,
enabled_interrupts);
if (ret < 0)
return ret;
ret = mma8452_trigger_setup(indio_dev);
if (ret < 0)
return ret;
}
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
data->ctrl_reg1);
if (ret < 0)
goto trigger_cleanup;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
mma8452_trigger_handler, NULL);
if (ret < 0)
return ret;
goto trigger_cleanup;
if (client->irq) {
ret = devm_request_threaded_irq(&client->dev,
client->irq,
NULL, mma8452_interrupt,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
client->name, indio_dev);
if (ret)
goto buffer_cleanup;
}
ret = iio_device_register(indio_dev);
if (ret < 0)
goto buffer_cleanup;
return 0;
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
trigger_cleanup:
mma8452_trigger_cleanup(indio_dev);
return ret;
}
@ -399,6 +933,7 @@ static int mma8452_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
mma8452_trigger_cleanup(indio_dev);
mma8452_standby(iio_priv(indio_dev));
return 0;

View file

@ -374,7 +374,7 @@ EXPORT_SYMBOL(mma9551_read_status_word);
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
* @val: Array of words to read
* @buf: Array of words to read
*
* Read multiple configuration registers (word-sized registers).
*
@ -414,7 +414,7 @@ EXPORT_SYMBOL(mma9551_read_config_words);
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
* @val: Array of words to read
* @buf: Array of words to read
*
* Read multiple status registers (word-sized registers).
*
@ -454,7 +454,7 @@ EXPORT_SYMBOL(mma9551_read_status_words);
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to write in bytes
* @val: Array of words to write
* @buf: Array of words to write
*
* Write multiple configuration registers (word-sized registers).
*
@ -800,7 +800,7 @@ EXPORT_SYMBOL(mma9551_read_accel_scale);
*/
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
{
return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
return mma9551_write_config_byte(client, MMA9551_APPID_RSC,
MMA9551_RSC_RESET +
MMA9551_RSC_OFFSET(app_mask),
MMA9551_RSC_VAL(app_mask));

View file

@ -22,7 +22,7 @@
#define MMA9551_APPID_TILT 0x0B
#define MMA9551_APPID_SLEEP_WAKE 0x12
#define MMA9551_APPID_PEDOMETER 0x15
#define MMA9551_APPID_RCS 0x17
#define MMA9551_APPID_RSC 0x17
#define MMA9551_APPID_NONE 0xff
/* Reset/Suspend/Clear application app masks */

View file

@ -63,8 +63,8 @@
#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
#define MMA9553_MASK_STATUS_SUSP BIT(11)
#define MMA9553_MASK_STATUS_ACTIVITY (BIT(10) | BIT(9) | BIT(8))
#define MMA9553_MASK_STATUS_VERSION 0x00FF
#define MMA9553_MASK_STATUS_ACTIVITY GENMASK(10, 8)
#define MMA9553_MASK_STATUS_VERSION GENMASK(7, 0)
#define MMA9553_REG_STEPCNT 0x02
#define MMA9553_REG_DISTANCE 0x04
@ -76,14 +76,15 @@
#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
#define MMA9553_DEFAULT_GPIO_POLARITY 0
/* Bitnum used for gpio configuration = bit number in high status byte */
#define STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
/* Bitnum used for GPIO configuration = bit number in high status byte */
#define MMA9553_STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
#define MMA9553_MAX_BITNUM MMA9553_STATUS_TO_BITNUM(BIT(16))
#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
/*
* The internal activity level must be stable for ACTTHD samples before
* ACTIVITY is updated.The ACTIVITY variable contains the current activity
* ACTIVITY is updated. The ACTIVITY variable contains the current activity
* level and is updated every time a step is detected or once a second
* if there are no steps.
*/
@ -351,11 +352,11 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
* This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
*/
if (activity_enabled && ev_step_detect->enabled)
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
else if (ev_step_detect->enabled)
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
else if (activity_enabled)
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
else /* Reset */
appid = MMA9551_APPID_NONE;
@ -363,9 +364,12 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
return 0;
/* Save initial values for activity and stepcnt */
if (activity_enabled || ev_step_detect->enabled)
mma9553_read_activity_stepcnt(data, &data->activity,
&data->stepcnt);
if (activity_enabled || ev_step_detect->enabled) {
ret = mma9553_read_activity_stepcnt(data, &data->activity,
&data->stepcnt);
if (ret < 0)
return ret;
}
ret = mma9551_gpio_config(data->client,
MMA9553_DEFAULT_GPIO_PIN,
@ -396,13 +400,13 @@ static int mma9553_init(struct mma9553_data *data)
sizeof(data->conf), (u16 *) &data->conf);
if (ret < 0) {
dev_err(&data->client->dev,
"device is not MMA9553L: failed to read cfg regs\n");
"failed to read configuration registers\n");
return ret;
}
/* Reset gpio */
data->gpio_bitnum = -1;
/* Reset GPIO */
data->gpio_bitnum = MMA9553_MAX_BITNUM;
ret = mma9553_conf_gpio(data);
if (ret < 0)
return ret;
@ -436,6 +440,32 @@ static int mma9553_init(struct mma9553_data *data)
return mma9551_set_device_state(data->client, true);
}
static int mma9553_read_status_word(struct mma9553_data *data, u16 reg,
u16 *tmp)
{
bool powered_on;
int ret;
/*
* The HW only counts steps and other dependent
* parameters (speed, distance, calories, activity)
* if power is on (from enabling an event or the
* step counter).
*/
powered_on = mma9553_is_any_event_enabled(data, false, 0) ||
data->stepcnt_enabled;
if (!powered_on) {
dev_err(&data->client->dev, "No channels enabled\n");
return -EINVAL;
}
mutex_lock(&data->mutex);
ret = mma9551_read_status_word(data->client, MMA9551_APPID_PEDOMETER,
reg, tmp);
mutex_unlock(&data->mutex);
return ret;
}
static int mma9553_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@ -444,69 +474,30 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
int ret;
u16 tmp;
u8 activity;
bool powered_on;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
/*
* The HW only counts steps and other dependent
* parameters (speed, distance, calories, activity)
* if power is on (from enabling an event or the
* step counter */
powered_on =
mma9553_is_any_event_enabled(data, false, 0) ||
data->stepcnt_enabled;
if (!powered_on) {
dev_err(&data->client->dev,
"No channels enabled\n");
return -EINVAL;
}
mutex_lock(&data->mutex);
ret = mma9551_read_status_word(data->client,
MMA9551_APPID_PEDOMETER,
ret = mma9553_read_status_word(data,
MMA9553_REG_STEPCNT,
&tmp);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_DISTANCE:
powered_on =
mma9553_is_any_event_enabled(data, false, 0) ||
data->stepcnt_enabled;
if (!powered_on) {
dev_err(&data->client->dev,
"No channels enabled\n");
return -EINVAL;
}
mutex_lock(&data->mutex);
ret = mma9551_read_status_word(data->client,
MMA9551_APPID_PEDOMETER,
ret = mma9553_read_status_word(data,
MMA9553_REG_DISTANCE,
&tmp);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_ACTIVITY:
powered_on =
mma9553_is_any_event_enabled(data, false, 0) ||
data->stepcnt_enabled;
if (!powered_on) {
dev_err(&data->client->dev,
"No channels enabled\n");
return -EINVAL;
}
mutex_lock(&data->mutex);
ret = mma9551_read_status_word(data->client,
MMA9551_APPID_PEDOMETER,
ret = mma9553_read_status_word(data,
MMA9553_REG_STATUS,
&tmp);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
@ -531,38 +522,17 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
case IIO_VELOCITY: /* m/h */
if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
return -EINVAL;
powered_on =
mma9553_is_any_event_enabled(data, false, 0) ||
data->stepcnt_enabled;
if (!powered_on) {
dev_err(&data->client->dev,
"No channels enabled\n");
return -EINVAL;
}
mutex_lock(&data->mutex);
ret = mma9551_read_status_word(data->client,
MMA9551_APPID_PEDOMETER,
MMA9553_REG_SPEED, &tmp);
mutex_unlock(&data->mutex);
ret = mma9553_read_status_word(data,
MMA9553_REG_SPEED,
&tmp);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_ENERGY: /* Cal or kcal */
powered_on =
mma9553_is_any_event_enabled(data, false, 0) ||
data->stepcnt_enabled;
if (!powered_on) {
dev_err(&data->client->dev,
"No channels enabled\n");
return -EINVAL;
}
mutex_lock(&data->mutex);
ret = mma9551_read_status_word(data->client,
MMA9551_APPID_PEDOMETER,
ret = mma9553_read_status_word(data,
MMA9553_REG_CALORIES,
&tmp);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
@ -789,7 +759,7 @@ static int mma9553_write_event_config(struct iio_dev *indio_dev,
mutex_unlock(&data->mutex);
return ret;
return 0;
err_conf_gpio:
if (state) {
@ -897,7 +867,7 @@ static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
/*
* HW expects 0 for female and 1 for male,
* while iio index is 0 for male and 1 for female
* while iio index is 0 for male and 1 for female.
*/
return !gender;
}
@ -944,11 +914,11 @@ static const struct iio_event_spec mma9553_activity_events[] = {
},
};
static const char * const calibgender_modes[] = { "male", "female" };
static const char * const mma9553_calibgender_modes[] = { "male", "female" };
static const struct iio_enum mma9553_calibgender_enum = {
.items = calibgender_modes,
.num_items = ARRAY_SIZE(calibgender_modes),
.items = mma9553_calibgender_modes,
.num_items = ARRAY_SIZE(mma9553_calibgender_modes),
.get = mma9553_get_calibgender_mode,
.set = mma9553_set_calibgender_mode,
};
@ -1110,16 +1080,16 @@ static int mma9553_gpio_probe(struct i2c_client *client)
dev = &client->dev;
/* data ready gpio interrupt pin */
/* data ready GPIO interrupt pin */
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
if (IS_ERR(gpio)) {
dev_err(dev, "acpi gpio get index failed\n");
dev_err(dev, "ACPI GPIO get index failed\n");
return PTR_ERR(gpio);
}
ret = gpiod_to_irq(gpio);
dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
return ret;
}

View file

@ -20,6 +20,7 @@
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel"
#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel"
#define LIS331DL_ACCEL_DEV_NAME "lis331dl_accel"
#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh"
#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel"
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"

View file

@ -153,6 +153,44 @@
#define ST_ACCEL_4_IG1_EN_MASK 0x08
#define ST_ACCEL_4_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 5 */
#define ST_ACCEL_5_WAI_EXP 0x3b
#define ST_ACCEL_5_ODR_ADDR 0x20
#define ST_ACCEL_5_ODR_MASK 0x80
#define ST_ACCEL_5_ODR_AVL_100HZ_VAL 0x00
#define ST_ACCEL_5_ODR_AVL_400HZ_VAL 0x01
#define ST_ACCEL_5_PW_ADDR 0x20
#define ST_ACCEL_5_PW_MASK 0x40
#define ST_ACCEL_5_FS_ADDR 0x20
#define ST_ACCEL_5_FS_MASK 0x20
#define ST_ACCEL_5_FS_AVL_2_VAL 0X00
#define ST_ACCEL_5_FS_AVL_8_VAL 0X01
/* TODO: check these resulting gain settings, these are not in the datsheet */
#define ST_ACCEL_5_FS_AVL_2_GAIN IIO_G_TO_M_S_2(18000)
#define ST_ACCEL_5_FS_AVL_8_GAIN IIO_G_TO_M_S_2(72000)
#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
#define ST_ACCEL_5_IG1_EN_ADDR 0x21
#define ST_ACCEL_5_IG1_EN_MASK 0x08
#define ST_ACCEL_5_MULTIREAD_BIT false
static const struct iio_chan_spec st_accel_8bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8,
ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1),
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8,
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1),
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8,
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_accel_12bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@ -454,6 +492,54 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
.bootime = 2, /* guess */
},
{
.wai = ST_ACCEL_5_WAI_EXP,
.sensors_supported = {
[0] = LIS331DL_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
.odr = {
.addr = ST_ACCEL_5_ODR_ADDR,
.mask = ST_ACCEL_5_ODR_MASK,
.odr_avl = {
{ 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL },
{ 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, },
},
},
.pw = {
.addr = ST_ACCEL_5_PW_ADDR,
.mask = ST_ACCEL_5_PW_MASK,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_ACCEL_5_FS_ADDR,
.mask = ST_ACCEL_5_FS_MASK,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = ST_ACCEL_5_FS_AVL_2_VAL,
.gain = ST_ACCEL_5_FS_AVL_2_GAIN,
},
[1] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = ST_ACCEL_5_FS_AVL_8_VAL,
.gain = ST_ACCEL_5_FS_AVL_8_GAIN,
},
},
},
.drdy_irq = {
.addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
},
.multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
.bootime = 2, /* guess */
},
};
static int st_accel_read_raw(struct iio_dev *indio_dev,

View file

@ -48,6 +48,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lsm330dlc-accel",
.data = LSM330DLC_ACCEL_DEV_NAME,
},
{
.compatible = "st,lis331dl-accel",
.data = LIS331DL_ACCEL_DEV_NAME,
},
{
.compatible = "st,lis331dlh-accel",
.data = LIS331DLH_ACCEL_DEV_NAME,

390
drivers/iio/accel/stk8312.c Normal file
View file

@ -0,0 +1,390 @@
/**
* Sensortek STK8312 3-Axis Accelerometer
*
* Copyright (c) 2015, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* IIO driver for STK8312; 7-bit I2C address: 0x3D.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define STK8312_REG_XOUT 0x00
#define STK8312_REG_YOUT 0x01
#define STK8312_REG_ZOUT 0x02
#define STK8312_REG_MODE 0x07
#define STK8312_REG_STH 0x13
#define STK8312_REG_RESET 0x20
#define STK8312_REG_AFECTRL 0x24
#define STK8312_REG_OTPADDR 0x3D
#define STK8312_REG_OTPDATA 0x3E
#define STK8312_REG_OTPCTRL 0x3F
#define STK8312_MODE_ACTIVE 1
#define STK8312_MODE_STANDBY 0
#define STK8312_MODE_MASK 0x01
#define STK8312_RNG_MASK 0xC0
#define STK8312_RNG_SHIFT 6
#define STK8312_READ_RETRIES 16
#define STK8312_DRIVER_NAME "stk8312"
/*
* The accelerometer has two measurement ranges:
*
* -6g - +6g (8-bit, signed)
* -16g - +16g (8-bit, signed)
*
* scale1 = (6 + 6) * 9.81 / (2^8 - 1) = 0.4616
* scale2 = (16 + 16) * 9.81 / (2^8 - 1) = 1.2311
*/
#define STK8312_SCALE_AVAIL "0.4616 1.2311"
static const int stk8312_scale_table[][2] = {
{0, 461600}, {1, 231100}
};
#define STK8312_ACCEL_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec stk8312_channels[] = {
STK8312_ACCEL_CHANNEL(STK8312_REG_XOUT, X),
STK8312_ACCEL_CHANNEL(STK8312_REG_YOUT, Y),
STK8312_ACCEL_CHANNEL(STK8312_REG_ZOUT, Z),
};
struct stk8312_data {
struct i2c_client *client;
struct mutex lock;
int range;
u8 mode;
};
static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
static struct attribute *stk8312_attributes[] = {
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group stk8312_attribute_group = {
.attrs = stk8312_attributes
};
static int stk8312_otp_init(struct stk8312_data *data)
{
int ret;
int count = 10;
struct i2c_client *client = data->client;
ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPADDR, 0x70);
if (ret < 0)
goto exit_err;
ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPCTRL, 0x02);
if (ret < 0)
goto exit_err;
do {
usleep_range(1000, 5000);
ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPCTRL);
if (ret < 0)
goto exit_err;
count--;
} while (!(ret & 0x80) && count > 0);
if (count == 0)
goto exit_err;
ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPDATA);
if (ret < 0)
goto exit_err;
ret = i2c_smbus_write_byte_data(data->client,
STK8312_REG_AFECTRL, ret);
if (ret < 0)
goto exit_err;
msleep(150);
return ret;
exit_err:
dev_err(&client->dev, "failed to initialize sensor\n");
return ret;
}
static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
{
int ret;
u8 masked_reg;
struct i2c_client *client = data->client;
if (mode > 1)
return -EINVAL;
else if (mode == data->mode)
return 0;
ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
if (ret < 0) {
dev_err(&client->dev, "failed to change sensor mode\n");
return ret;
}
masked_reg = ret & (~STK8312_MODE_MASK);
masked_reg |= mode;
ret = i2c_smbus_write_byte_data(client,
STK8312_REG_MODE, masked_reg);
if (ret < 0) {
dev_err(&client->dev, "failed to change sensor mode\n");
return ret;
}
data->mode = mode;
if (mode == STK8312_MODE_ACTIVE) {
/* Need to run OTP sequence before entering active mode */
usleep_range(1000, 5000);
ret = stk8312_otp_init(data);
}
return ret;
}
static int stk8312_set_range(struct stk8312_data *data, u8 range)
{
int ret;
u8 masked_reg;
u8 mode;
struct i2c_client *client = data->client;
if (range != 1 && range != 2)
return -EINVAL;
else if (range == data->range)
return 0;
mode = data->mode;
/* We need to go in standby mode to modify registers */
ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
if (ret < 0)
return ret;
ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
if (ret < 0) {
dev_err(&client->dev, "failed to change sensor range\n");
return ret;
}
masked_reg = ret & (~STK8312_RNG_MASK);
masked_reg |= range << STK8312_RNG_SHIFT;
ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
if (ret < 0)
dev_err(&client->dev, "failed to change sensor range\n");
else
data->range = range;
return stk8312_set_mode(data, mode);
}
static int stk8312_read_accel(struct stk8312_data *data, u8 address)
{
int ret;
struct i2c_client *client = data->client;
if (address > 2)
return -EINVAL;
ret = i2c_smbus_read_byte_data(client, address);
if (ret < 0) {
dev_err(&client->dev, "register read failed\n");
return ret;
}
return sign_extend32(ret, 7);
}
static int stk8312_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct stk8312_data *data = iio_priv(indio_dev);
if (chan->type != IIO_ACCEL)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->lock);
*val = stk8312_read_accel(data, chan->address);
mutex_unlock(&data->lock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = stk8312_scale_table[data->range - 1][0];
*val2 = stk8312_scale_table[data->range - 1][1];
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
static int stk8312_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int i;
int index = -1;
int ret;
struct stk8312_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
if (val == stk8312_scale_table[i][0] &&
val2 == stk8312_scale_table[i][1]) {
index = i + 1;
break;
}
if (index < 0)
return -EINVAL;
mutex_lock(&data->lock);
ret = stk8312_set_range(data, index);
mutex_unlock(&data->lock);
return ret;
}
return -EINVAL;
}
static const struct iio_info stk8312_info = {
.driver_module = THIS_MODULE,
.read_raw = stk8312_read_raw,
.write_raw = stk8312_write_raw,
.attrs = &stk8312_attribute_group,
};
static int stk8312_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct stk8312_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &stk8312_info;
indio_dev->name = STK8312_DRIVER_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = stk8312_channels;
indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
/* A software reset is recommended at power-on */
ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET, 0x00);
if (ret < 0) {
dev_err(&client->dev, "failed to reset sensor\n");
return ret;
}
ret = stk8312_set_range(data, 1);
if (ret < 0)
return ret;
ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
stk8312_set_mode(data, STK8312_MODE_STANDBY);
}
return ret;
}
static int stk8312_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
}
#ifdef CONFIG_PM_SLEEP
static int stk8312_suspend(struct device *dev)
{
struct stk8312_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return stk8312_set_mode(data, STK8312_MODE_STANDBY);
}
static int stk8312_resume(struct device *dev)
{
struct stk8312_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
}
static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
#define STK8312_PM_OPS (&stk8312_pm_ops)
#else
#define STK8312_PM_OPS NULL
#endif
static const struct i2c_device_id stk8312_i2c_id[] = {
{"STK8312", 0},
{}
};
static const struct acpi_device_id stk8312_acpi_id[] = {
{"STK8312", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
static struct i2c_driver stk8312_driver = {
.driver = {
.name = "stk8312",
.pm = STK8312_PM_OPS,
.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
},
.probe = stk8312_probe,
.remove = stk8312_remove,
.id_table = stk8312_i2c_id,
};
module_i2c_driver(stk8312_driver);
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,302 @@
/**
* Sensortek STK8BA50 3-Axis Accelerometer
*
* Copyright (c) 2015, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* STK8BA50 7-bit I2C address: 0x18.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define STK8BA50_REG_XOUT 0x02
#define STK8BA50_REG_YOUT 0x04
#define STK8BA50_REG_ZOUT 0x06
#define STK8BA50_REG_RANGE 0x0F
#define STK8BA50_REG_POWMODE 0x11
#define STK8BA50_REG_SWRST 0x14
#define STK8BA50_MODE_NORMAL 0
#define STK8BA50_MODE_SUSPEND 1
#define STK8BA50_MODE_POWERBIT BIT(7)
#define STK8BA50_DATA_SHIFT 6
#define STK8BA50_RESET_CMD 0xB6
#define STK8BA50_DRIVER_NAME "stk8ba50"
#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
/*
* The accelerometer has four measurement ranges:
* +/-2g; +/-4g; +/-8g; +/-16g
*
* Acceleration values are 10-bit, 2's complement.
* Scales are calculated as following:
*
* scale1 = (2 + 2) * 9.81 / (2^10 - 1) = 0.0384
* scale2 = (4 + 4) * 9.81 / (2^10 - 1) = 0.0767
* etc.
*
* Scales are stored in this format:
* { <register value>, <scale value> }
*
* Locally, the range is stored as a table index.
*/
static const int stk8ba50_scale_table[][2] = {
{3, 38400}, {5, 76700}, {8, 153400}, {12, 306900}
};
struct stk8ba50_data {
struct i2c_client *client;
struct mutex lock;
int range;
};
#define STK8BA50_ACCEL_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec stk8ba50_channels[] = {
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_XOUT, X),
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_YOUT, Y),
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_ZOUT, Z),
};
static IIO_CONST_ATTR(in_accel_scale_available, STK8BA50_SCALE_AVAIL);
static struct attribute *stk8ba50_attributes[] = {
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group stk8ba50_attribute_group = {
.attrs = stk8ba50_attributes
};
static int stk8ba50_read_accel(struct stk8ba50_data *data, u8 reg)
{
int ret;
struct i2c_client *client = data->client;
ret = i2c_smbus_read_word_data(client, reg);
if (ret < 0) {
dev_err(&client->dev, "register read failed\n");
return ret;
}
return sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9);
}
static int stk8ba50_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct stk8ba50_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->lock);
*val = stk8ba50_read_accel(data, chan->address);
mutex_unlock(&data->lock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = stk8ba50_scale_table[data->range][1];
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
static int stk8ba50_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
int i;
int index = -1;
struct stk8ba50_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
if (val != 0)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(stk8ba50_scale_table); i++)
if (val2 == stk8ba50_scale_table[i][1]) {
index = i;
break;
}
if (index < 0)
return -EINVAL;
ret = i2c_smbus_write_byte_data(data->client,
STK8BA50_REG_RANGE,
stk8ba50_scale_table[index][0]);
if (ret < 0)
dev_err(&data->client->dev,
"failed to set measurement range\n");
else
data->range = index;
return ret;
}
return -EINVAL;
}
static const struct iio_info stk8ba50_info = {
.driver_module = THIS_MODULE,
.read_raw = stk8ba50_read_raw,
.write_raw = stk8ba50_write_raw,
.attrs = &stk8ba50_attribute_group,
};
static int stk8ba50_set_power(struct stk8ba50_data *data, bool mode)
{
int ret;
u8 masked_reg;
struct i2c_client *client = data->client;
ret = i2c_smbus_read_byte_data(client, STK8BA50_REG_POWMODE);
if (ret < 0)
goto exit_err;
if (mode)
masked_reg = ret | STK8BA50_MODE_POWERBIT;
else
masked_reg = ret & (~STK8BA50_MODE_POWERBIT);
ret = i2c_smbus_write_byte_data(client, STK8BA50_REG_POWMODE,
masked_reg);
if (ret < 0)
goto exit_err;
return ret;
exit_err:
dev_err(&client->dev, "failed to change sensor mode\n");
return ret;
}
static int stk8ba50_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct stk8ba50_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &stk8ba50_info;
indio_dev->name = STK8BA50_DRIVER_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = stk8ba50_channels;
indio_dev->num_channels = ARRAY_SIZE(stk8ba50_channels);
/* Reset all registers on startup */
ret = i2c_smbus_write_byte_data(client,
STK8BA50_REG_SWRST, STK8BA50_RESET_CMD);
if (ret < 0) {
dev_err(&client->dev, "failed to reset sensor\n");
return ret;
}
/* The default range is +/-2g */
data->range = 0;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
}
return ret;
}
static int stk8ba50_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return stk8ba50_set_power(iio_priv(indio_dev), STK8BA50_MODE_SUSPEND);
}
#ifdef CONFIG_PM_SLEEP
static int stk8ba50_suspend(struct device *dev)
{
struct stk8ba50_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
}
static int stk8ba50_resume(struct device *dev)
{
struct stk8ba50_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
}
static SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume);
#define STK8BA50_PM_OPS (&stk8ba50_pm_ops)
#else
#define STK8BA50_PM_OPS NULL
#endif
static const struct i2c_device_id stk8ba50_i2c_id[] = {
{"stk8ba50", 0},
{}
};
static const struct acpi_device_id stk8ba50_acpi_id[] = {
{"STK8BA50", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id);
static struct i2c_driver stk8ba50_driver = {
.driver = {
.name = "stk8ba50",
.pm = STK8BA50_PM_OPS,
.acpi_match_table = ACPI_PTR(stk8ba50_acpi_id),
},
.probe = stk8ba50_probe,
.remove = stk8ba50_remove,
.id_table = stk8ba50_i2c_id,
};
module_i2c_driver(stk8ba50_driver);
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
MODULE_DESCRIPTION("STK8BA50 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

View file

@ -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.

View file

@ -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

View file

@ -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" },
{},
};

View file

@ -0,0 +1,378 @@
/*
* Marvell Berlin2 ADC driver
*
* Copyright (C) 2015 Marvell Technology Group Ltd.
*
* Antoine Tenart <antoine.tenart@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/machine.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/wait.h>
#define BERLIN2_SM_CTRL 0x14
#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
#define BERLIN2_SM_CTRL_ADC_SEL(x) (BIT(x) << 5) /* 0-15 */
#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5)
#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK (0x3 << 10)
#define BERLIN2_SM_CTRL_ADC_START BIT(12)
#define BERLIN2_SM_CTRL_ADC_RESET BIT(13)
#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14)
#define BERLIN2_SM_CTRL_ADC_CONT_SINGLE (0x0 << 15)
#define BERLIN2_SM_CTRL_ADC_CONT_CONTINUOUS (0x1 << 15)
#define BERLIN2_SM_CTRL_ADC_BUFFER_EN BIT(16)
#define BERLIN2_SM_CTRL_ADC_VREF_EXT (0x0 << 17)
#define BERLIN2_SM_CTRL_ADC_VREF_INT (0x1 << 17)
#define BERLIN2_SM_CTRL_ADC_ROTATE BIT(19)
#define BERLIN2_SM_CTRL_TSEN_EN BIT(20)
#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_125 (0x0 << 21) /* 1.25 MHz */
#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_250 (0x1 << 21) /* 2.5 MHz */
#define BERLIN2_SM_CTRL_TSEN_MODE_0_125 (0x0 << 22) /* 0-125 C */
#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */
#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29)
#define BERLIN2_SM_ADC_DATA 0x20
#define BERLIN2_SM_ADC_MASK 0x3ff
#define BERLIN2_SM_ADC_STATUS 0x1c
#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK 0xf
#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */
#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK (0xf << 16)
#define BERLIN2_SM_TSEN_STATUS 0x24
#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0)
#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1)
#define BERLIN2_SM_TSEN_DATA 0x28
#define BERLIN2_SM_TSEN_MASK 0xfff
#define BERLIN2_SM_TSEN_CTRL 0x74
#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */
#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK (0x1 << 21)
#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22)
#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK (0xf << 22)
struct berlin2_adc_priv {
struct regmap *regmap;
struct mutex lock;
wait_queue_head_t wq;
bool data_available;
int data;
};
#define BERLIN2_ADC_CHANNEL(n, t) \
{ \
.channel = n, \
.datasheet_name = "channel"#n, \
.type = t, \
.indexed = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
}
static struct iio_chan_spec berlin2_adc_channels[] = {
BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */
BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */
BERLIN2_ADC_CHANNEL(3, IIO_VOLTAGE), /* external input */
BERLIN2_ADC_CHANNEL(4, IIO_VOLTAGE), /* reserved */
BERLIN2_ADC_CHANNEL(5, IIO_VOLTAGE), /* reserved */
{ /* temperature sensor */
.channel = 6,
.datasheet_name = "channel6",
.type = IIO_TEMP,
.indexed = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
},
BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */
IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */
};
#define BERLIN2_N_CHANNELS ARRAY_SIZE(berlin2_adc_channels)
static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
{
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
int data, ret;
mutex_lock(&priv->lock);
/* Configure the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK
| BERLIN2_SM_CTRL_ADC_START,
BERLIN2_SM_CTRL_ADC_SEL(channel) | BERLIN2_SM_CTRL_ADC_START);
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
msecs_to_jiffies(1000));
/* Disable the interrupts */
regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS,
BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
if (ret == 0)
ret = -ETIMEDOUT;
if (ret < 0) {
mutex_unlock(&priv->lock);
return ret;
}
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
BERLIN2_SM_CTRL_ADC_START, 0);
data = priv->data;
priv->data_available = false;
mutex_unlock(&priv->lock);
return data;
}
static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
{
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
int data, ret;
mutex_lock(&priv->lock);
/* Configure the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE,
BERLIN2_SM_CTRL_ADC_ROTATE);
/* Configure the temperature sensor */
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
BERLIN2_SM_TSEN_CTRL_TRIM_MASK | BERLIN2_SM_TSEN_CTRL_SETTLING_MASK
| BERLIN2_SM_TSEN_CTRL_START,
BERLIN2_SM_TSEN_CTRL_TRIM(3) | BERLIN2_SM_TSEN_CTRL_SETTLING_12
| BERLIN2_SM_TSEN_CTRL_START);
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
msecs_to_jiffies(1000));
/* Disable interrupts */
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS,
BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
if (ret == 0)
ret = -ETIMEDOUT;
if (ret < 0) {
mutex_unlock(&priv->lock);
return ret;
}
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
BERLIN2_SM_TSEN_CTRL_START, 0);
data = priv->data;
priv->data_available = false;
mutex_unlock(&priv->lock);
return data;
}
static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
{
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
int temp;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_VOLTAGE)
return -EINVAL;
/* Enable the interrupts */
regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
BERLIN2_SM_ADC_STATUS_INT_EN(chan->channel));
*val = berlin2_adc_read(indio_dev, chan->channel);
if (*val < 0)
return *val;
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
if (chan->type != IIO_TEMP)
return -EINVAL;
/* Enable interrupts */
regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
BERLIN2_SM_TSEN_STATUS_INT_EN);
temp = berlin2_adc_tsen_read(indio_dev);
if (temp < 0)
return temp;
if (temp > 2047)
temp = -(4096 - temp);
/* Convert to milli Celsius */
*val = ((temp * 100000) / 264 - 270000);
return IIO_VAL_INT;
default:
break;
}
return -EINVAL;
}
static irqreturn_t berlin2_adc_irq(int irq, void *private)
{
struct berlin2_adc_priv *priv = iio_priv(private);
unsigned val;
regmap_read(priv->regmap, BERLIN2_SM_ADC_STATUS, &val);
if (val & BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK) {
regmap_read(priv->regmap, BERLIN2_SM_ADC_DATA, &priv->data);
priv->data &= BERLIN2_SM_ADC_MASK;
val &= ~BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK;
regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, val);
priv->data_available = true;
wake_up_interruptible(&priv->wq);
}
return IRQ_HANDLED;
}
static irqreturn_t berlin2_adc_tsen_irq(int irq, void *private)
{
struct berlin2_adc_priv *priv = iio_priv(private);
unsigned val;
regmap_read(priv->regmap, BERLIN2_SM_TSEN_STATUS, &val);
if (val & BERLIN2_SM_TSEN_STATUS_DATA_RDY) {
regmap_read(priv->regmap, BERLIN2_SM_TSEN_DATA, &priv->data);
priv->data &= BERLIN2_SM_TSEN_MASK;
val &= ~BERLIN2_SM_TSEN_STATUS_DATA_RDY;
regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, val);
priv->data_available = true;
wake_up_interruptible(&priv->wq);
}
return IRQ_HANDLED;
}
static const struct iio_info berlin2_adc_info = {
.driver_module = THIS_MODULE,
.read_raw = berlin2_adc_read_raw,
};
static int berlin2_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct berlin2_adc_priv *priv;
struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
int irq, tsen_irq;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct berlin2_adc_priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
platform_set_drvdata(pdev, indio_dev);
priv->regmap = syscon_node_to_regmap(parent_np);
of_node_put(parent_np);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
irq = platform_get_irq_byname(pdev, "adc");
if (irq < 0)
return -ENODEV;
tsen_irq = platform_get_irq_byname(pdev, "tsen");
if (tsen_irq < 0)
return -ENODEV;
ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
pdev->dev.driver->name, indio_dev);
if (ret)
return ret;
ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq,
0, pdev->dev.driver->name, indio_dev);
if (ret)
return ret;
init_waitqueue_head(&priv->wq);
mutex_init(&priv->lock);
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &berlin2_adc_info;
indio_dev->num_channels = BERLIN2_N_CHANNELS;
indio_dev->channels = berlin2_adc_channels;
/* Power up the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER);
ret = iio_device_register(indio_dev);
if (ret) {
/* Power down the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
BERLIN2_SM_CTRL_ADC_POWER, 0);
return ret;
}
return 0;
}
static int berlin2_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
/* Power down the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
BERLIN2_SM_CTRL_ADC_POWER, 0);
return 0;
}
static const struct of_device_id berlin2_adc_match[] = {
{ .compatible = "marvell,berlin2-adc", },
{ },
};
MODULE_DEVICE_TABLE(of, berlin2_adc_match);
static struct platform_driver berlin2_adc_driver = {
.driver = {
.name = "berlin2-adc",
.of_match_table = berlin2_adc_match,
},
.probe = berlin2_adc_probe,
.remove = berlin2_adc_remove,
};
module_platform_driver(berlin2_adc_driver);
MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
MODULE_DESCRIPTION("Marvell Berlin2 ADC driver");
MODULE_LICENSE("GPL v2");

View file

@ -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);

View file

@ -37,6 +37,7 @@ struct tiadc_device {
u8 channel_step[8];
int buffer_en_ch_steps;
u16 data[8];
u32 open_delay[8], sample_delay[8], step_avg[8];
};
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
@ -85,6 +86,7 @@ static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
static void tiadc_step_config(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct device *dev = adc_dev->mfd_tscadc->dev;
unsigned int stepconfig;
int i, steps = 0;
@ -98,20 +100,47 @@ static void tiadc_step_config(struct iio_dev *indio_dev)
* needs to be given to ADC to digitalize data.
*/
if (iio_buffer_enabled(indio_dev))
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
| STEPCONFIG_MODE_SWCNT;
else
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
for (i = 0; i < adc_dev->channels; i++) {
int chan;
chan = adc_dev->channel_line[i];
if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) {
dev_warn(dev, "chan %d step_avg truncating to %d\n",
chan, STEPCONFIG_AVG_16);
adc_dev->step_avg[i] = STEPCONFIG_AVG_16;
}
if (adc_dev->step_avg[i])
stepconfig =
STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) |
STEPCONFIG_FIFO1;
else
stepconfig = STEPCONFIG_FIFO1;
if (iio_buffer_enabled(indio_dev))
stepconfig |= STEPCONFIG_MODE_SWCNT;
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
stepconfig | STEPCONFIG_INP(chan));
if (adc_dev->open_delay[i] > STEPDELAY_OPEN_MASK) {
dev_warn(dev, "chan %d open delay truncating to 0x3FFFF\n",
chan);
adc_dev->open_delay[i] = STEPDELAY_OPEN_MASK;
}
if (adc_dev->sample_delay[i] > 0xFF) {
dev_warn(dev, "chan %d sample delay truncating to 0xFF\n",
chan);
adc_dev->sample_delay[i] = 0xFF;
}
tiadc_writel(adc_dev, REG_STEPDELAY(steps),
STEPCONFIG_OPENDLY);
STEPDELAY_OPEN(adc_dev->open_delay[i]) |
STEPDELAY_SAMPLE(adc_dev->sample_delay[i]));
adc_dev->channel_step[i] = steps;
steps++;
}
@ -395,16 +424,43 @@ static const struct iio_info tiadc_info = {
.driver_module = THIS_MODULE,
};
static int tiadc_parse_dt(struct platform_device *pdev,
struct tiadc_device *adc_dev)
{
struct device_node *node = pdev->dev.of_node;
struct property *prop;
const __be32 *cur;
int channels = 0;
u32 val;
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
adc_dev->channel_line[channels] = val;
/* Set Default values for optional DT parameters */
adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY;
adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY;
adc_dev->step_avg[channels] = 16;
channels++;
}
of_property_read_u32_array(node, "ti,chan-step-avg",
adc_dev->step_avg, channels);
of_property_read_u32_array(node, "ti,chan-step-opendelay",
adc_dev->open_delay, channels);
of_property_read_u32_array(node, "ti,chan-step-sampledelay",
adc_dev->sample_delay, channels);
adc_dev->channels = channels;
return 0;
}
static int tiadc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct tiadc_device *adc_dev;
struct device_node *node = pdev->dev.of_node;
struct property *prop;
const __be32 *cur;
int err;
u32 val;
int channels = 0;
if (!node) {
dev_err(&pdev->dev, "Could not find valid DT data.\n");
@ -420,12 +476,7 @@ static int tiadc_probe(struct platform_device *pdev)
adc_dev = iio_priv(indio_dev);
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
adc_dev->channel_line[channels] = val;
channels++;
}
adc_dev->channels = channels;
tiadc_parse_dt(pdev, adc_dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);

View file

@ -235,7 +235,7 @@ static int twl4030battery_temperature(int raw_volt)
if (ret < 0)
return ret;
curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10;
/* Getting and calculating the thermistor resistance in ohms */
res = volt * 1000 / curr;
/* calculating temperature */
@ -662,10 +662,8 @@ EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
*
* @madc: pointer to twl4030_madc_data struct
* @chan: can be one of the two values:
* TWL4030_BCI_ITHEN
* Enables bias current for main battery type reading
* TWL4030_BCI_TYPEN
* Enables bias current for main battery temperature sensing
* 0 - Enables bias current for main battery type reading
* 1 - Enables bias current for main battery temperature sensing
* @on: enable or disable chan.
*
* Function to enable or disable bias current for

View file

@ -118,15 +118,21 @@ enum average_sel {
VF610_ADC_SAMPLE_32,
};
enum conversion_mode_sel {
VF610_ADC_CONV_NORMAL,
VF610_ADC_CONV_HIGH_SPEED,
VF610_ADC_CONV_LOW_POWER,
};
struct vf610_adc_feature {
enum clk_sel clk_sel;
enum vol_ref vol_ref;
enum conversion_mode_sel conv_mode;
int clk_div;
int sample_rate;
int res_mode;
bool lpm;
bool calibration;
bool ovwren;
};
@ -139,6 +145,8 @@ struct vf610_adc {
u32 vref_uv;
u32 value;
struct regulator *vref;
u32 max_adck_rate[3];
struct vf610_adc_feature adc_feature;
u32 sample_freq_avail[5];
@ -148,46 +156,22 @@ struct vf610_adc {
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
#define VF610_ADC_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.indexed = 1, \
.channel = (_idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.channel = (_idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
}
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
VF610_ADC_CHAN(0, IIO_VOLTAGE),
VF610_ADC_CHAN(1, IIO_VOLTAGE),
VF610_ADC_CHAN(2, IIO_VOLTAGE),
VF610_ADC_CHAN(3, IIO_VOLTAGE),
VF610_ADC_CHAN(4, IIO_VOLTAGE),
VF610_ADC_CHAN(5, IIO_VOLTAGE),
VF610_ADC_CHAN(6, IIO_VOLTAGE),
VF610_ADC_CHAN(7, IIO_VOLTAGE),
VF610_ADC_CHAN(8, IIO_VOLTAGE),
VF610_ADC_CHAN(9, IIO_VOLTAGE),
VF610_ADC_CHAN(10, IIO_VOLTAGE),
VF610_ADC_CHAN(11, IIO_VOLTAGE),
VF610_ADC_CHAN(12, IIO_VOLTAGE),
VF610_ADC_CHAN(13, IIO_VOLTAGE),
VF610_ADC_CHAN(14, IIO_VOLTAGE),
VF610_ADC_CHAN(15, IIO_VOLTAGE),
VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
/* sentinel */
};
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
{
struct vf610_adc_feature *adc_feature = &info->adc_feature;
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
int i;
int divisor, i;
adck_rate = info->max_adck_rate[adc_feature->conv_mode];
if (adck_rate) {
/* calculate clk divider which is within specification */
divisor = ipg_rate / adck_rate;
adc_feature->clk_div = 1 << fls(divisor + 1);
} else {
/* fall-back value using a safe divisor */
adc_feature->clk_div = 8;
}
/*
* Calculate ADC sample frequencies
@ -219,10 +203,8 @@ static inline void vf610_adc_cfg_init(struct vf610_adc *info)
adc_feature->res_mode = 12;
adc_feature->sample_rate = 1;
adc_feature->lpm = true;
/* Use a save ADCK which is below 20MHz on all devices */
adc_feature->clk_div = 8;
adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER;
vf610_adc_calculate_rates(info);
}
@ -304,10 +286,12 @@ static void vf610_adc_cfg_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
cfg_data &= ~VF610_ADC_ADLPC_EN;
if (adc_feature->lpm)
if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER)
cfg_data |= VF610_ADC_ADLPC_EN;
cfg_data &= ~VF610_ADC_ADHSC_EN;
if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED)
cfg_data |= VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
}
@ -409,6 +393,81 @@ static void vf610_adc_hw_init(struct vf610_adc *info)
vf610_adc_cfg_set(info);
}
static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct vf610_adc *info = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
info->adc_feature.conv_mode = mode;
vf610_adc_calculate_rates(info);
vf610_adc_hw_init(info);
mutex_unlock(&indio_dev->mlock);
return 0;
}
static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct vf610_adc *info = iio_priv(indio_dev);
return info->adc_feature.conv_mode;
}
static const char * const vf610_conv_modes[] = { "normal", "high-speed",
"low-power" };
static const struct iio_enum vf610_conversion_mode = {
.items = vf610_conv_modes,
.num_items = ARRAY_SIZE(vf610_conv_modes),
.get = vf610_get_conversion_mode,
.set = vf610_set_conversion_mode,
};
static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode),
{},
};
#define VF610_ADC_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.indexed = 1, \
.channel = (_idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.ext_info = vf610_ext_info, \
}
#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.channel = (_idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
}
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
VF610_ADC_CHAN(0, IIO_VOLTAGE),
VF610_ADC_CHAN(1, IIO_VOLTAGE),
VF610_ADC_CHAN(2, IIO_VOLTAGE),
VF610_ADC_CHAN(3, IIO_VOLTAGE),
VF610_ADC_CHAN(4, IIO_VOLTAGE),
VF610_ADC_CHAN(5, IIO_VOLTAGE),
VF610_ADC_CHAN(6, IIO_VOLTAGE),
VF610_ADC_CHAN(7, IIO_VOLTAGE),
VF610_ADC_CHAN(8, IIO_VOLTAGE),
VF610_ADC_CHAN(9, IIO_VOLTAGE),
VF610_ADC_CHAN(10, IIO_VOLTAGE),
VF610_ADC_CHAN(11, IIO_VOLTAGE),
VF610_ADC_CHAN(12, IIO_VOLTAGE),
VF610_ADC_CHAN(13, IIO_VOLTAGE),
VF610_ADC_CHAN(14, IIO_VOLTAGE),
VF610_ADC_CHAN(15, IIO_VOLTAGE),
VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
/* sentinel */
};
static int vf610_adc_read_data(struct vf610_adc *info)
{
int result;
@ -651,6 +710,9 @@ static int vf610_adc_probe(struct platform_device *pdev)
info->vref_uv = regulator_get_voltage(info->vref);
of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency",
info->max_adck_rate, 3);
platform_set_drvdata(pdev, indio_dev);
init_completion(&info->completion);

View file

@ -33,6 +33,8 @@ static void iio_buffer_cb_release(struct iio_buffer *buffer)
static const struct iio_buffer_access_funcs iio_cb_access = {
.store_to = &iio_buffer_cb_store_to,
.release = &iio_buffer_cb_release,
.modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
};
struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,

View file

@ -245,6 +245,16 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
{
struct st_sensor_data *sdata = iio_priv(indio_dev);
/* Sensor does not support interrupts */
if (sdata->sensor_settings->drdy_irq.addr == 0) {
if (pdata->drdy_int_pin)
dev_info(&indio_dev->dev,
"DRDY on pin INT%d specified, but sensor "
"does not support interrupts\n",
pdata->drdy_int_pin);
return 0;
}
switch (pdata->drdy_int_pin) {
case 1:
if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
@ -285,7 +295,7 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
pdata->drdy_int_pin = (u8) val;
else
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 1;
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
return pdata;
}
@ -332,11 +342,13 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
return err;
/* set BDU */
err = st_sensors_write_data_with_mask(indio_dev,
if (sdata->sensor_settings->bdu.addr) {
err = st_sensors_write_data_with_mask(indio_dev,
sdata->sensor_settings->bdu.addr,
sdata->sensor_settings->bdu.mask, true);
if (err < 0)
return err;
if (err < 0)
return err;
}
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
@ -419,7 +431,9 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
if (err < 0)
goto st_sensors_free_memory;
if (byte_for_channel == 2)
if (byte_for_channel == 1)
*data = (s8)*outdata;
else if (byte_for_channel == 2)
*data = (s16)get_unaligned_le16(outdata);
else if (byte_for_channel == 3)
*data = (s32)st_sensors_get_unaligned_le24(outdata);
@ -489,7 +503,8 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
break;
}
if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
dev_err(&indio_dev->dev, "device name \"%s\" and WhoAmI (0x%02x) mismatch",
indio_dev->name, wai);
goto sensor_name_mismatch;
}

View file

@ -37,8 +37,10 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
IRQF_TRIGGER_RISING,
sdata->trig->name,
sdata->trig);
if (err)
if (err) {
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
goto request_irq_error;
}
iio_trigger_set_drvdata(sdata->trig, indio_dev);
sdata->trig->ops = trigger_ops;

View file

@ -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

View file

@ -16,6 +16,7 @@ obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o

269
drivers/iio/dac/m62332.c Normal file
View file

@ -0,0 +1,269 @@
/*
* m62332.c - Support for Mitsubishi m62332 DAC
*
* Copyright (c) 2014 Dmitry Eremin-Solenikov
*
* Based on max517 driver:
* Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/regulator/consumer.h>
#define M62332_CHANNELS 2
struct m62332_data {
struct i2c_client *client;
u16 vref_mv;
struct regulator *vcc;
struct mutex mutex;
u8 raw[M62332_CHANNELS];
#ifdef CONFIG_PM_SLEEP
u8 save[M62332_CHANNELS];
#endif
};
static int m62332_set_value(struct iio_dev *indio_dev,
u8 val, int channel)
{
struct m62332_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
u8 outbuf[2];
int res;
if (val == data->raw[channel])
return 0;
outbuf[0] = channel;
outbuf[1] = val;
mutex_lock(&data->mutex);
if (val) {
res = regulator_enable(data->vcc);
if (res)
goto out;
}
res = i2c_master_send(client, outbuf, 2);
if (res >= 0 && res != 2)
res = -EIO;
if (res < 0)
goto out;
data->raw[channel] = val;
if (!val)
regulator_disable(data->vcc);
mutex_unlock(&data->mutex);
return 0;
out:
mutex_unlock(&data->mutex);
return res;
}
static int m62332_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long m)
{
struct m62332_data *data = iio_priv(indio_dev);
switch (m) {
case IIO_CHAN_INFO_SCALE:
/* Corresponds to Vref / 2^(bits) */
*val = data->vref_mv;
*val2 = 8;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_RAW:
*val = data->raw[chan->channel];
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
*val = 1;
return IIO_VAL_INT;
default:
break;
}
return -EINVAL;
}
static int m62332_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (val < 0 || val > 255)
return -EINVAL;
ret = m62332_set_value(indio_dev, val, chan->channel);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int m62332_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct m62332_data *data = iio_priv(indio_dev);
int ret;
data->save[0] = data->raw[0];
data->save[1] = data->raw[1];
ret = m62332_set_value(indio_dev, 0, 0);
if (ret < 0)
return ret;
return m62332_set_value(indio_dev, 0, 1);
}
static int m62332_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct m62332_data *data = iio_priv(indio_dev);
int ret;
ret = m62332_set_value(indio_dev, data->save[0], 0);
if (ret < 0)
return ret;
return m62332_set_value(indio_dev, data->save[1], 1);
}
static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
#define M62332_PM_OPS (&m62332_pm_ops)
#else
#define M62332_PM_OPS NULL
#endif
static const struct iio_info m62332_info = {
.read_raw = m62332_read_raw,
.write_raw = m62332_write_raw,
.driver_module = THIS_MODULE,
};
#define M62332_CHANNEL(chan) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.output = 1, \
.channel = (chan), \
.datasheet_name = "CH" #chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
}
static const struct iio_chan_spec m62332_channels[M62332_CHANNELS] = {
M62332_CHANNEL(0),
M62332_CHANNEL(1)
};
static int m62332_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct m62332_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
mutex_init(&data->mutex);
data->vcc = devm_regulator_get(&client->dev, "VCC");
if (IS_ERR(data->vcc))
return PTR_ERR(data->vcc);
/* establish that the iio_dev is a child of the i2c device */
indio_dev->dev.parent = &client->dev;
indio_dev->num_channels = M62332_CHANNELS;
indio_dev->channels = m62332_channels;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &m62332_info;
ret = regulator_get_voltage(data->vcc);
if (ret < 0)
return ret;
data->vref_mv = ret / 1000; /* mV */
ret = iio_map_array_register(indio_dev, client->dev.platform_data);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto err;
return 0;
err:
iio_map_array_unregister(indio_dev);
return ret;
}
static int m62332_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
iio_map_array_unregister(indio_dev);
return 0;
}
static const struct i2c_device_id m62332_id[] = {
{ "m62332", },
{ }
};
MODULE_DEVICE_TABLE(i2c, m62332_id);
static struct i2c_driver m62332_driver = {
.driver = {
.name = "m62332",
.pm = M62332_PM_OPS,
},
.probe = m62332_probe,
.remove = m62332_remove,
.id_table = m62332_id,
};
module_i2c_driver(m62332_driver);
MODULE_AUTHOR("Dmitry Eremin-Solenikov");
MODULE_DESCRIPTION("M62332 8-bit DAC");
MODULE_LICENSE("GPL v2");

View file

@ -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);
}

View file

@ -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",

View file

@ -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.

View file

@ -239,13 +239,19 @@ static ssize_t iio_scan_el_show(struct device *dev,
/* Note NULL used as error indicator as it doesn't make sense. */
static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
unsigned int masklength,
const unsigned long *mask)
const unsigned long *mask,
bool strict)
{
if (bitmap_empty(mask, masklength))
return NULL;
while (*av_masks) {
if (bitmap_subset(mask, av_masks, masklength))
return av_masks;
if (strict) {
if (bitmap_equal(mask, av_masks, masklength))
return av_masks;
} else {
if (bitmap_subset(mask, av_masks, masklength))
return av_masks;
}
av_masks += BITS_TO_LONGS(masklength);
}
return NULL;
@ -295,7 +301,7 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
if (indio_dev->available_scan_masks) {
mask = iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
trialmask);
trialmask, false);
if (!mask)
goto err_invalid_mask;
}
@ -539,26 +545,13 @@ static void iio_buffer_deactivate(struct iio_buffer *buffer)
iio_buffer_put(buffer);
}
void iio_disable_all_buffers(struct iio_dev *indio_dev)
static void iio_buffer_deactivate_all(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer, *_buffer;
if (list_empty(&indio_dev->buffer_list))
return;
if (indio_dev->setup_ops->predisable)
indio_dev->setup_ops->predisable(indio_dev);
list_for_each_entry_safe(buffer, _buffer,
&indio_dev->buffer_list, buffer_list)
iio_buffer_deactivate(buffer);
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable)
indio_dev->setup_ops->postdisable(indio_dev);
if (indio_dev->available_scan_masks == NULL)
kfree(indio_dev->active_scan_mask);
}
static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
@ -575,167 +568,277 @@ static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
buffer->access->set_bytes_per_datum(buffer, bytes);
}
static int __iio_update_buffers(struct iio_dev *indio_dev,
struct iio_buffer *insert_buffer,
struct iio_buffer *remove_buffer)
static int iio_buffer_request_update(struct iio_dev *indio_dev,
struct iio_buffer *buffer)
{
int ret;
int success = 0;
struct iio_buffer *buffer;
unsigned long *compound_mask;
const unsigned long *old_mask;
/* Wind down existing buffers - iff there are any */
if (!list_empty(&indio_dev->buffer_list)) {
if (indio_dev->setup_ops->predisable) {
ret = indio_dev->setup_ops->predisable(indio_dev);
if (ret)
return ret;
}
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable) {
ret = indio_dev->setup_ops->postdisable(indio_dev);
if (ret)
return ret;
iio_buffer_update_bytes_per_datum(indio_dev, buffer);
if (buffer->access->request_update) {
ret = buffer->access->request_update(buffer);
if (ret) {
dev_dbg(&indio_dev->dev,
"Buffer not started: buffer parameter update failed (%d)\n",
ret);
return ret;
}
}
/* Keep a copy of current setup to allow roll back */
old_mask = indio_dev->active_scan_mask;
return 0;
}
static void iio_free_scan_mask(struct iio_dev *indio_dev,
const unsigned long *mask)
{
/* If the mask is dynamically allocated free it, otherwise do nothing */
if (!indio_dev->available_scan_masks)
indio_dev->active_scan_mask = NULL;
kfree(mask);
}
struct iio_device_config {
unsigned int mode;
const unsigned long *scan_mask;
unsigned int scan_bytes;
bool scan_timestamp;
};
static int iio_verify_update(struct iio_dev *indio_dev,
struct iio_buffer *insert_buffer, struct iio_buffer *remove_buffer,
struct iio_device_config *config)
{
unsigned long *compound_mask;
const unsigned long *scan_mask;
bool strict_scanmask = false;
struct iio_buffer *buffer;
bool scan_timestamp;
unsigned int modes;
memset(config, 0, sizeof(*config));
/*
* If there is just one buffer and we are removing it there is nothing
* to verify.
*/
if (remove_buffer && !insert_buffer &&
list_is_singular(&indio_dev->buffer_list))
return 0;
modes = indio_dev->modes;
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
if (buffer == remove_buffer)
continue;
modes &= buffer->access->modes;
}
if (remove_buffer)
iio_buffer_deactivate(remove_buffer);
if (insert_buffer)
iio_buffer_activate(indio_dev, insert_buffer);
modes &= insert_buffer->access->modes;
/* If no buffers in list, we are done */
if (list_empty(&indio_dev->buffer_list)) {
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->available_scan_masks == NULL)
kfree(old_mask);
return 0;
/* Definitely possible for devices to support both of these. */
if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
config->mode = INDIO_BUFFER_TRIGGERED;
} else if (modes & INDIO_BUFFER_HARDWARE) {
/*
* Keep things simple for now and only allow a single buffer to
* be connected in hardware mode.
*/
if (insert_buffer && !list_empty(&indio_dev->buffer_list))
return -EINVAL;
config->mode = INDIO_BUFFER_HARDWARE;
strict_scanmask = true;
} else if (modes & INDIO_BUFFER_SOFTWARE) {
config->mode = INDIO_BUFFER_SOFTWARE;
} else {
/* Can only occur on first buffer */
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
dev_dbg(&indio_dev->dev, "Buffer not started: no trigger\n");
return -EINVAL;
}
/* What scan mask do we actually have? */
compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
sizeof(long), GFP_KERNEL);
if (compound_mask == NULL) {
if (indio_dev->available_scan_masks == NULL)
kfree(old_mask);
if (compound_mask == NULL)
return -ENOMEM;
}
indio_dev->scan_timestamp = 0;
scan_timestamp = false;
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
if (buffer == remove_buffer)
continue;
bitmap_or(compound_mask, compound_mask, buffer->scan_mask,
indio_dev->masklength);
indio_dev->scan_timestamp |= buffer->scan_timestamp;
scan_timestamp |= buffer->scan_timestamp;
}
if (insert_buffer) {
bitmap_or(compound_mask, compound_mask,
insert_buffer->scan_mask, indio_dev->masklength);
scan_timestamp |= insert_buffer->scan_timestamp;
}
if (indio_dev->available_scan_masks) {
indio_dev->active_scan_mask =
iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
compound_mask);
if (indio_dev->active_scan_mask == NULL) {
/*
* Roll back.
* Note can only occur when adding a buffer.
*/
iio_buffer_deactivate(insert_buffer);
if (old_mask) {
indio_dev->active_scan_mask = old_mask;
success = -EINVAL;
}
else {
kfree(compound_mask);
ret = -EINVAL;
return ret;
}
}
scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
compound_mask,
strict_scanmask);
kfree(compound_mask);
if (scan_mask == NULL)
return -EINVAL;
} else {
indio_dev->active_scan_mask = compound_mask;
scan_mask = compound_mask;
}
config->scan_bytes = iio_compute_scan_bytes(indio_dev,
scan_mask, scan_timestamp);
config->scan_mask = scan_mask;
config->scan_timestamp = scan_timestamp;
return 0;
}
static int iio_enable_buffers(struct iio_dev *indio_dev,
struct iio_device_config *config)
{
int ret;
indio_dev->active_scan_mask = config->scan_mask;
indio_dev->scan_timestamp = config->scan_timestamp;
indio_dev->scan_bytes = config->scan_bytes;
iio_update_demux(indio_dev);
/* Wind up again */
if (indio_dev->setup_ops->preenable) {
ret = indio_dev->setup_ops->preenable(indio_dev);
if (ret) {
printk(KERN_ERR
dev_dbg(&indio_dev->dev,
"Buffer not started: buffer preenable failed (%d)\n", ret);
goto error_remove_inserted;
}
}
indio_dev->scan_bytes =
iio_compute_scan_bytes(indio_dev,
indio_dev->active_scan_mask,
indio_dev->scan_timestamp);
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
iio_buffer_update_bytes_per_datum(indio_dev, buffer);
if (buffer->access->request_update) {
ret = buffer->access->request_update(buffer);
if (ret) {
printk(KERN_INFO
"Buffer not started: buffer parameter update failed (%d)\n", ret);
goto error_run_postdisable;
}
goto err_undo_config;
}
}
if (indio_dev->info->update_scan_mode) {
ret = indio_dev->info
->update_scan_mode(indio_dev,
indio_dev->active_scan_mask);
if (ret < 0) {
printk(KERN_INFO "Buffer not started: update scan mode failed (%d)\n", ret);
goto error_run_postdisable;
dev_dbg(&indio_dev->dev,
"Buffer not started: update scan mode failed (%d)\n",
ret);
goto err_run_postdisable;
}
}
/* Definitely possible for devices to support both of these. */
if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
} else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
} else { /* Should never be reached */
/* Can only occur on first buffer */
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
pr_info("Buffer not started: no trigger\n");
ret = -EINVAL;
goto error_run_postdisable;
}
indio_dev->currentmode = config->mode;
if (indio_dev->setup_ops->postenable) {
ret = indio_dev->setup_ops->postenable(indio_dev);
if (ret) {
printk(KERN_INFO
dev_dbg(&indio_dev->dev,
"Buffer not started: postenable failed (%d)\n", ret);
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable)
indio_dev->setup_ops->postdisable(indio_dev);
goto error_disable_all_buffers;
goto err_run_postdisable;
}
}
if (indio_dev->available_scan_masks)
kfree(compound_mask);
else
kfree(old_mask);
return 0;
return success;
error_disable_all_buffers:
err_run_postdisable:
indio_dev->currentmode = INDIO_DIRECT_MODE;
error_run_postdisable:
if (indio_dev->setup_ops->postdisable)
indio_dev->setup_ops->postdisable(indio_dev);
error_remove_inserted:
err_undo_config:
indio_dev->active_scan_mask = NULL;
return ret;
}
static int iio_disable_buffers(struct iio_dev *indio_dev)
{
int ret = 0;
int ret2;
/* Wind down existing buffers - iff there are any */
if (list_empty(&indio_dev->buffer_list))
return 0;
/*
* If things go wrong at some step in disable we still need to continue
* to perform the other steps, otherwise we leave the device in a
* inconsistent state. We return the error code for the first error we
* encountered.
*/
if (indio_dev->setup_ops->predisable) {
ret2 = indio_dev->setup_ops->predisable(indio_dev);
if (ret2 && !ret)
ret = ret2;
}
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable) {
ret2 = indio_dev->setup_ops->postdisable(indio_dev);
if (ret2 && !ret)
ret = ret2;
}
iio_free_scan_mask(indio_dev, indio_dev->active_scan_mask);
indio_dev->active_scan_mask = NULL;
return ret;
}
static int __iio_update_buffers(struct iio_dev *indio_dev,
struct iio_buffer *insert_buffer,
struct iio_buffer *remove_buffer)
{
struct iio_device_config new_config;
int ret;
ret = iio_verify_update(indio_dev, insert_buffer, remove_buffer,
&new_config);
if (ret)
return ret;
if (insert_buffer) {
ret = iio_buffer_request_update(indio_dev, insert_buffer);
if (ret)
goto err_free_config;
}
ret = iio_disable_buffers(indio_dev);
if (ret)
goto err_deactivate_all;
if (remove_buffer)
iio_buffer_deactivate(remove_buffer);
if (insert_buffer)
iio_buffer_deactivate(insert_buffer);
indio_dev->active_scan_mask = old_mask;
kfree(compound_mask);
iio_buffer_activate(indio_dev, insert_buffer);
/* If no buffers in list, we are done */
if (list_empty(&indio_dev->buffer_list))
return 0;
ret = iio_enable_buffers(indio_dev, &new_config);
if (ret)
goto err_deactivate_all;
return 0;
err_deactivate_all:
/*
* We've already verified that the config is valid earlier. If things go
* wrong in either enable or disable the most likely reason is an IO
* error from the device. In this case there is no good recovery
* strategy. Just make sure to disable everything and leave the device
* in a sane state. With a bit of luck the device might come back to
* life again later and userspace can try again.
*/
iio_buffer_deactivate_all(indio_dev);
err_free_config:
iio_free_scan_mask(indio_dev, new_config.scan_mask);
return ret;
}
@ -777,6 +880,12 @@ out_unlock:
}
EXPORT_SYMBOL_GPL(iio_update_buffers);
void iio_disable_all_buffers(struct iio_dev *indio_dev)
{
iio_disable_buffers(indio_dev);
iio_buffer_deactivate_all(indio_dev);
}
static ssize_t iio_buffer_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
@ -806,8 +915,6 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
ret = __iio_update_buffers(indio_dev,
NULL, indio_dev->buffer);
if (ret < 0)
goto done;
done:
mutex_unlock(&indio_dev->mlock);
return (ret < 0) ? ret : len;
@ -886,6 +993,15 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
int ret, i, attrn, attrcount, attrcount_orig = 0;
const struct iio_chan_spec *channels;
channels = indio_dev->channels;
if (channels) {
int ml = indio_dev->masklength;
for (i = 0; i < indio_dev->num_channels; i++)
ml = max(ml, channels[i].scan_index + 1);
indio_dev->masklength = ml;
}
if (!buffer)
return 0;
@ -929,12 +1045,6 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
if (channels[i].scan_index < 0)
continue;
/* Establish necessary mask length */
if (channels[i].scan_index >
(int)indio_dev->masklength - 1)
indio_dev->masklength
= channels[i].scan_index + 1;
ret = iio_buffer_add_channel_sysfs(indio_dev,
&channels[i]);
if (ret < 0)

View file

@ -101,6 +101,8 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_WALKING] = "walking",
[IIO_MOD_STILL] = "still",
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
[IIO_MOD_I] = "i",
[IIO_MOD_Q] = "q",
};
/* relies on pairs of these shared then separate */
@ -117,6 +119,8 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_AVERAGE_RAW] = "mean_raw",
[IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY]
= "filter_low_pass_3db_frequency",
[IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY]
= "filter_high_pass_3db_frequency",
[IIO_CHAN_INFO_SAMP_FREQ] = "sampling_frequency",
[IIO_CHAN_INFO_FREQUENCY] = "frequency",
[IIO_CHAN_INFO_PHASE] = "phase",
@ -128,6 +132,8 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
[IIO_CHAN_INFO_CALIBEMISSIVITY] = "calibemissivity",
[IIO_CHAN_INFO_OVERSAMPLING_RATIO] = "oversampling_ratio",
};
/**

View file

@ -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)

View file

@ -136,6 +136,8 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
.set_length = &iio_set_length_kfifo,
.release = &iio_kfifo_buffer_release,
.modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
};
struct iio_buffer *iio_kfifo_allocate(void)

View file

@ -5,6 +5,19 @@
menu "Light sensors"
config ACPI_ALS
tristate "ACPI Ambient Light Sensor"
depends on ACPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IIO_KFIFO_BUF
help
Say Y here if you want to build a driver for the ACPI0008
Ambient Light Sensor.
To compile this driver as a module, choose M here: the module will
be called acpi-als.
config ADJD_S311
tristate "ADJD-S311-CR999 digital color sensor"
select IIO_BUFFER
@ -37,6 +50,16 @@ config APDS9300
To compile this driver as a module, choose M here: the
module will be called apds9300.
config BH1750
tristate "ROHM BH1750 ambient light sensor"
depends on I2C
help
Say Y here to build support for the ROHM BH1710, BH1715, BH1721,
BH1750, BH1751 ambient light sensors.
To compile this driver as a module, choose M here: the module will
be called bh1750.
config CM32181
depends on I2C
tristate "CM32181 driver"
@ -169,11 +192,23 @@ config LTR501
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for the Lite-On LTR-501ALS-01
ambient light and proximity sensor.
ambient light and proximity sensor. This driver also supports LTR-559
ALS/PS or LTR-301 ALS sensors.
This driver can also be built as a module. If so, the module
will be called ltr501.
config STK3310
tristate "STK3310 ALS and proximity sensor"
depends on I2C
help
Say yes here to get support for the Sensortek STK3310 ambient light
and proximity sensor. The STK3311 model is also supported by this
driver.
Choosing M will build the driver as a module. If so, the module
will be called stk3310.
config TCS3414
tristate "TAOS TCS3414 digital color sensor"
depends on I2C

View file

@ -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

View file

@ -0,0 +1,231 @@
/*
* ACPI Ambient Light Sensor Driver
*
* Based on ALS driver:
* Copyright (C) 2009 Zhang Rui <rui.zhang@intel.com>
*
* Rework for IIO subsystem:
* Copyright (C) 2012-2013 Martin Liska <marxin.liska@gmail.com>
*
* Final cleanup and debugging:
* Copyright (C) 2013-2014 Marek Vasut <marex@denx.de>
* Copyright (C) 2015 Gabriele Mazzotta <gabriele.mzt@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#define ACPI_ALS_CLASS "als"
#define ACPI_ALS_DEVICE_NAME "acpi-als"
#define ACPI_ALS_NOTIFY_ILLUMINANCE 0x80
ACPI_MODULE_NAME("acpi-als");
/*
* So far, there's only one channel in here, but the specification for
* ACPI0008 says there can be more to what the block can report. Like
* chromaticity and such. We are ready for incoming additions!
*/
static const struct iio_chan_spec acpi_als_channels[] = {
{
.type = IIO_LIGHT,
.scan_type = {
.sign = 's',
.realbits = 32,
.storagebits = 32,
},
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
};
/*
* The event buffer contains timestamp and all the data from
* the ACPI0008 block. There are multiple, but so far we only
* support _ALI (illuminance). Once someone adds new channels
* to acpi_als_channels[], the evt_buffer below will grow
* automatically.
*/
#define EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels)
#define EVT_BUFFER_SIZE \
(sizeof(s64) + (EVT_NR_SOURCES * sizeof(s32)))
struct acpi_als {
struct acpi_device *device;
struct mutex lock;
s32 evt_buffer[EVT_BUFFER_SIZE];
};
/*
* All types of properties the ACPI0008 block can report. The ALI, ALC, ALT
* and ALP can all be handled by als_read_value() below, while the ALR is
* special.
*
* The _ALR property returns tables that can be used to fine-tune the values
* reported by the other props based on the particular hardware type and it's
* location (it contains tables for "rainy", "bright inhouse lighting" etc.).
*
* So far, we support only ALI (illuminance).
*/
#define ACPI_ALS_ILLUMINANCE "_ALI"
#define ACPI_ALS_CHROMATICITY "_ALC"
#define ACPI_ALS_COLOR_TEMP "_ALT"
#define ACPI_ALS_POLLING "_ALP"
#define ACPI_ALS_TABLES "_ALR"
static int als_read_value(struct acpi_als *als, char *prop, s32 *val)
{
unsigned long long temp_val;
acpi_status status;
status = acpi_evaluate_integer(als->device->handle, prop, NULL,
&temp_val);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS %s", prop));
return -EIO;
}
*val = temp_val;
return 0;
}
static void acpi_als_notify(struct acpi_device *device, u32 event)
{
struct iio_dev *indio_dev = acpi_driver_data(device);
struct acpi_als *als = iio_priv(indio_dev);
s32 *buffer = als->evt_buffer;
s64 time_ns = iio_get_time_ns();
s32 val;
int ret;
mutex_lock(&als->lock);
memset(buffer, 0, EVT_BUFFER_SIZE);
switch (event) {
case ACPI_ALS_NOTIFY_ILLUMINANCE:
ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
if (ret < 0)
goto out;
*buffer++ = val;
break;
default:
/* Unhandled event */
dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n",
event);
goto out;
}
iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns);
out:
mutex_unlock(&als->lock);
}
static int acpi_als_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct acpi_als *als = iio_priv(indio_dev);
s32 temp_val;
int ret;
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
/* we support only illumination (_ALI) so far. */
if (chan->type != IIO_LIGHT)
return -EINVAL;
ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val);
if (ret < 0)
return ret;
*val = temp_val;
return IIO_VAL_INT;
}
static const struct iio_info acpi_als_info = {
.driver_module = THIS_MODULE,
.read_raw = acpi_als_read_raw,
};
static int acpi_als_add(struct acpi_device *device)
{
struct acpi_als *als;
struct iio_dev *indio_dev;
struct iio_buffer *buffer;
indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
if (!indio_dev)
return -ENOMEM;
als = iio_priv(indio_dev);
device->driver_data = indio_dev;
als->device = device;
mutex_init(&als->lock);
indio_dev->name = ACPI_ALS_DEVICE_NAME;
indio_dev->dev.parent = &device->dev;
indio_dev->info = &acpi_als_info;
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = acpi_als_channels;
indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
buffer = devm_iio_kfifo_allocate(&device->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
return devm_iio_device_register(&device->dev, indio_dev);
}
static const struct acpi_device_id acpi_als_device_ids[] = {
{"ACPI0008", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids);
static struct acpi_driver acpi_als_driver = {
.name = "acpi_als",
.class = ACPI_ALS_CLASS,
.ids = acpi_als_device_ids,
.ops = {
.add = acpi_als_add,
.notify = acpi_als_notify,
},
};
module_acpi_driver(acpi_als_driver);
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_AUTHOR("Martin Liska <marxin.liska@gmail.com>");
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("ACPI Ambient Light Sensor Driver");
MODULE_LICENSE("GPL");

334
drivers/iio/light/bh1750.c Normal file
View file

@ -0,0 +1,334 @@
/*
* ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Data sheets:
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1721fvc-e.pdf
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1750fvi-e.pdf
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1751fvi-e.pdf
*
* 7-bit I2C slave addresses:
* 0x23 (ADDR pin low)
* 0x5C (ADDR pin high)
*
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
#define BH1750_POWER_DOWN 0x00
#define BH1750_ONE_TIME_H_RES_MODE 0x20 /* auto-mode for BH1721 */
#define BH1750_CHANGE_INT_TIME_H_BIT 0x40
#define BH1750_CHANGE_INT_TIME_L_BIT 0x60
enum {
BH1710,
BH1721,
BH1750,
};
struct bh1750_chip_info;
struct bh1750_data {
struct i2c_client *client;
struct mutex lock;
const struct bh1750_chip_info *chip_info;
u16 mtreg;
};
struct bh1750_chip_info {
u16 mtreg_min;
u16 mtreg_max;
u16 mtreg_default;
int mtreg_to_usec;
int mtreg_to_scale;
/*
* For BH1710/BH1721 all possible integration time values won't fit
* into one page so displaying is limited to every second one.
* Note, that user can still write proper values which were not
* listed.
*/
int inc;
u16 int_time_low_mask;
u16 int_time_high_mask;
}
static const bh1750_chip_info_tbl[] = {
[BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 },
[BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 },
[BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 0x00E0 },
};
static int bh1750_change_int_time(struct bh1750_data *data, int usec)
{
int ret;
u16 val;
u8 regval;
const struct bh1750_chip_info *chip_info = data->chip_info;
if ((usec % chip_info->mtreg_to_usec) != 0)
return -EINVAL;
val = usec / chip_info->mtreg_to_usec;
if (val < chip_info->mtreg_min || val > chip_info->mtreg_max)
return -EINVAL;
ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN);
if (ret < 0)
return ret;
regval = (val & chip_info->int_time_high_mask) >> 5;
ret = i2c_smbus_write_byte(data->client,
BH1750_CHANGE_INT_TIME_H_BIT | regval);
if (ret < 0)
return ret;
regval = val & chip_info->int_time_low_mask;
ret = i2c_smbus_write_byte(data->client,
BH1750_CHANGE_INT_TIME_L_BIT | regval);
if (ret < 0)
return ret;
data->mtreg = val;
return 0;
}
static int bh1750_read(struct bh1750_data *data, int *val)
{
int ret;
__be16 result;
const struct bh1750_chip_info *chip_info = data->chip_info;
unsigned long delay = chip_info->mtreg_to_usec * data->mtreg;
/*
* BH1721 will enter continuous mode on receiving this command.
* Note, that this eliminates need for bh1750_resume().
*/
ret = i2c_smbus_write_byte(data->client, BH1750_ONE_TIME_H_RES_MODE);
if (ret < 0)
return ret;
usleep_range(delay + 15000, delay + 40000);
ret = i2c_master_recv(data->client, (char *)&result, 2);
if (ret < 0)
return ret;
*val = be16_to_cpu(result);
return 0;
}
static int bh1750_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret, tmp;
struct bh1750_data *data = iio_priv(indio_dev);
const struct bh1750_chip_info *chip_info = data->chip_info;
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_LIGHT:
mutex_lock(&data->lock);
ret = bh1750_read(data, val);
mutex_unlock(&data->lock);
if (ret < 0)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
tmp = chip_info->mtreg_to_scale / data->mtreg;
*val = tmp / 1000000;
*val2 = tmp % 1000000;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_INT_TIME:
*val = 0;
*val2 = chip_info->mtreg_to_usec * data->mtreg;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int bh1750_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
struct bh1750_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
if (val != 0)
return -EINVAL;
mutex_lock(&data->lock);
ret = bh1750_change_int_time(data, val2);
mutex_unlock(&data->lock);
return ret;
default:
return -EINVAL;
}
}
static ssize_t bh1750_show_int_time_available(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i;
size_t len = 0;
struct bh1750_data *data = iio_priv(dev_to_iio_dev(dev));
const struct bh1750_chip_info *chip_info = data->chip_info;
for (i = chip_info->mtreg_min; i <= chip_info->mtreg_max; i += chip_info->inc)
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06d ",
chip_info->mtreg_to_usec * i);
buf[len - 1] = '\n';
return len;
}
static IIO_DEV_ATTR_INT_TIME_AVAIL(bh1750_show_int_time_available);
static struct attribute *bh1750_attributes[] = {
&iio_dev_attr_integration_time_available.dev_attr.attr,
NULL,
};
static struct attribute_group bh1750_attribute_group = {
.attrs = bh1750_attributes,
};
static const struct iio_info bh1750_info = {
.driver_module = THIS_MODULE,
.attrs = &bh1750_attribute_group,
.read_raw = bh1750_read_raw,
.write_raw = bh1750_write_raw,
};
static const struct iio_chan_spec bh1750_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME)
}
};
static int bh1750_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret, usec;
struct bh1750_data *data;
struct iio_dev *indio_dev;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WRITE_BYTE))
return -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->chip_info = &bh1750_chip_info_tbl[id->driver_data];
usec = data->chip_info->mtreg_to_usec * data->chip_info->mtreg_default;
ret = bh1750_change_int_time(data, usec);
if (ret < 0)
return ret;
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &bh1750_info;
indio_dev->name = id->name;
indio_dev->channels = bh1750_channels;
indio_dev->num_channels = ARRAY_SIZE(bh1750_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
return iio_device_register(indio_dev);
}
static int bh1750_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct bh1750_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
mutex_lock(&data->lock);
i2c_smbus_write_byte(client, BH1750_POWER_DOWN);
mutex_unlock(&data->lock);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int bh1750_suspend(struct device *dev)
{
int ret;
struct bh1750_data *data =
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
/*
* This is mainly for BH1721 which doesn't enter power down
* mode automatically.
*/
mutex_lock(&data->lock);
ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN);
mutex_unlock(&data->lock);
return ret;
}
static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL);
#define BH1750_PM_OPS (&bh1750_pm_ops)
#else
#define BH1750_PM_OPS NULL
#endif
static const struct i2c_device_id bh1750_id[] = {
{ "bh1710", BH1710 },
{ "bh1715", BH1750 },
{ "bh1721", BH1721 },
{ "bh1750", BH1750 },
{ "bh1751", BH1750 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bh1750_id);
static struct i2c_driver bh1750_driver = {
.driver = {
.name = "bh1750",
.owner = THIS_MODULE,
.pm = BH1750_PM_OPS,
},
.probe = bh1750_probe,
.remove = bh1750_remove,
.id_table = bh1750_id,
};
module_i2c_driver(bh1750_driver);
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("ROHM BH1710/BH1715/BH1721/BH1750/BH1751 als driver");
MODULE_LICENSE("GPL v2");

View file

@ -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",

View file

@ -350,7 +350,7 @@ static int hid_prox_remove(struct platform_device *pdev)
return 0;
}
static struct platform_device_id hid_prox_ids[] = {
static const struct platform_device_id hid_prox_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200011",

File diff suppressed because it is too large Load diff

722
drivers/iio/light/stk3310.c Normal file
View file

@ -0,0 +1,722 @@
/**
* Sensortek STK3310/STK3311 Ambient Light and Proximity Sensor
*
* Copyright (c) 2015, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define STK3310_REG_STATE 0x00
#define STK3310_REG_PSCTRL 0x01
#define STK3310_REG_ALSCTRL 0x02
#define STK3310_REG_INT 0x04
#define STK3310_REG_THDH_PS 0x06
#define STK3310_REG_THDL_PS 0x08
#define STK3310_REG_FLAG 0x10
#define STK3310_REG_PS_DATA_MSB 0x11
#define STK3310_REG_PS_DATA_LSB 0x12
#define STK3310_REG_ALS_DATA_MSB 0x13
#define STK3310_REG_ALS_DATA_LSB 0x14
#define STK3310_REG_ID 0x3E
#define STK3310_MAX_REG 0x80
#define STK3310_STATE_EN_PS 0x01
#define STK3310_STATE_EN_ALS 0x02
#define STK3310_STATE_STANDBY 0x00
#define STK3310_CHIP_ID_VAL 0x13
#define STK3311_CHIP_ID_VAL 0x1D
#define STK3310_PSINT_EN 0x01
#define STK3310_PS_MAX_VAL 0xFFFF
#define STK3310_THRESH_MAX 0xFFFF
#define STK3310_DRIVER_NAME "stk3310"
#define STK3310_REGMAP_NAME "stk3310_regmap"
#define STK3310_EVENT "stk3310_event"
#define STK3310_GPIO "stk3310_gpio"
#define STK3310_SCALE_AVAILABLE "6.4 1.6 0.4 0.1"
#define STK3310_IT_AVAILABLE \
"0.000185 0.000370 0.000741 0.001480 0.002960 0.005920 0.011840 " \
"0.023680 0.047360 0.094720 0.189440 0.378880 0.757760 1.515520 " \
"3.031040 6.062080"
#define STK3310_REGFIELD(name) \
do { \
data->reg_##name = \
devm_regmap_field_alloc(&client->dev, regmap, \
stk3310_reg_field_##name); \
if (IS_ERR(data->reg_##name)) { \
dev_err(&client->dev, "reg field alloc failed.\n"); \
return PTR_ERR(data->reg_##name); \
} \
} while (0)
static const struct reg_field stk3310_reg_field_state =
REG_FIELD(STK3310_REG_STATE, 0, 2);
static const struct reg_field stk3310_reg_field_als_gain =
REG_FIELD(STK3310_REG_ALSCTRL, 4, 5);
static const struct reg_field stk3310_reg_field_ps_gain =
REG_FIELD(STK3310_REG_PSCTRL, 4, 5);
static const struct reg_field stk3310_reg_field_als_it =
REG_FIELD(STK3310_REG_ALSCTRL, 0, 3);
static const struct reg_field stk3310_reg_field_ps_it =
REG_FIELD(STK3310_REG_PSCTRL, 0, 3);
static const struct reg_field stk3310_reg_field_int_ps =
REG_FIELD(STK3310_REG_INT, 0, 2);
static const struct reg_field stk3310_reg_field_flag_psint =
REG_FIELD(STK3310_REG_FLAG, 4, 4);
static const struct reg_field stk3310_reg_field_flag_nf =
REG_FIELD(STK3310_REG_FLAG, 0, 0);
/*
* Maximum PS values with regard to scale. Used to export the 'inverse'
* PS value (high values for far objects, low values for near objects).
*/
static const int stk3310_ps_max[4] = {
STK3310_PS_MAX_VAL / 64,
STK3310_PS_MAX_VAL / 16,
STK3310_PS_MAX_VAL / 4,
STK3310_PS_MAX_VAL,
};
static const int stk3310_scale_table[][2] = {
{6, 400000}, {1, 600000}, {0, 400000}, {0, 100000}
};
/* Integration time in seconds, microseconds */
static const int stk3310_it_table[][2] = {
{0, 185}, {0, 370}, {0, 741}, {0, 1480},
{0, 2960}, {0, 5920}, {0, 11840}, {0, 23680},
{0, 47360}, {0, 94720}, {0, 189440}, {0, 378880},
{0, 757760}, {1, 515520}, {3, 31040}, {6, 62080},
};
struct stk3310_data {
struct i2c_client *client;
struct mutex lock;
bool als_enabled;
bool ps_enabled;
u64 timestamp;
struct regmap *regmap;
struct regmap_field *reg_state;
struct regmap_field *reg_als_gain;
struct regmap_field *reg_ps_gain;
struct regmap_field *reg_als_it;
struct regmap_field *reg_ps_it;
struct regmap_field *reg_int_ps;
struct regmap_field *reg_flag_psint;
struct regmap_field *reg_flag_nf;
};
static const struct iio_event_spec stk3310_events[] = {
/* Proximity event */
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
/* Out-of-proximity event */
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_chan_spec stk3310_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME),
},
{
.type = IIO_PROXIMITY,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME),
.event_spec = stk3310_events,
.num_event_specs = ARRAY_SIZE(stk3310_events),
}
};
static IIO_CONST_ATTR(in_illuminance_scale_available, STK3310_SCALE_AVAILABLE);
static IIO_CONST_ATTR(in_proximity_scale_available, STK3310_SCALE_AVAILABLE);
static IIO_CONST_ATTR(in_illuminance_integration_time_available,
STK3310_IT_AVAILABLE);
static IIO_CONST_ATTR(in_proximity_integration_time_available,
STK3310_IT_AVAILABLE);
static struct attribute *stk3310_attributes[] = {
&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
&iio_const_attr_in_proximity_scale_available.dev_attr.attr,
&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
&iio_const_attr_in_proximity_integration_time_available.dev_attr.attr,
NULL,
};
static const struct attribute_group stk3310_attribute_group = {
.attrs = stk3310_attributes
};
static int stk3310_get_index(const int table[][2], int table_size,
int val, int val2)
{
int i;
for (i = 0; i < table_size; i++) {
if (val == table[i][0] && val2 == table[i][1])
return i;
}
return -EINVAL;
}
static int stk3310_read_event(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
u8 reg;
u16 buf;
int ret;
unsigned int index;
struct stk3310_data *data = iio_priv(indio_dev);
if (info != IIO_EV_INFO_VALUE)
return -EINVAL;
/*
* Only proximity interrupts are implemented at the moment.
* Since we're inverting proximity values, the sensor's 'high'
* threshold will become our 'low' threshold, associated with
* 'near' events. Similarly, the sensor's 'low' threshold will
* be our 'high' threshold, associated with 'far' events.
*/
if (dir == IIO_EV_DIR_RISING)
reg = STK3310_REG_THDL_PS;
else if (dir == IIO_EV_DIR_FALLING)
reg = STK3310_REG_THDH_PS;
else
return -EINVAL;
mutex_lock(&data->lock);
ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
mutex_unlock(&data->lock);
if (ret < 0) {
dev_err(&data->client->dev, "register read failed\n");
return ret;
}
regmap_field_read(data->reg_ps_gain, &index);
*val = swab16(stk3310_ps_max[index] - buf);
return IIO_VAL_INT;
}
static int stk3310_write_event(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
u8 reg;
u16 buf;
int ret;
unsigned int index;
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
regmap_field_read(data->reg_ps_gain, &index);
if (val > stk3310_ps_max[index])
return -EINVAL;
if (dir == IIO_EV_DIR_RISING)
reg = STK3310_REG_THDL_PS;
else if (dir == IIO_EV_DIR_FALLING)
reg = STK3310_REG_THDH_PS;
else
return -EINVAL;
buf = swab16(stk3310_ps_max[index] - val);
ret = regmap_bulk_write(data->regmap, reg, &buf, 2);
if (ret < 0)
dev_err(&client->dev, "failed to set PS threshold!\n");
return ret;
}
static int stk3310_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
unsigned int event_val;
struct stk3310_data *data = iio_priv(indio_dev);
regmap_field_read(data->reg_int_ps, &event_val);
return event_val;
}
static int stk3310_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
int state)
{
int ret;
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
if (state < 0 || state > 7)
return -EINVAL;
/* Set INT_PS value */
mutex_lock(&data->lock);
ret = regmap_field_write(data->reg_int_ps, state);
if (ret < 0)
dev_err(&client->dev, "failed to set interrupt mode\n");
mutex_unlock(&data->lock);
return ret;
}
static int stk3310_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
u8 reg;
u16 buf;
int ret;
unsigned int index;
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_LIGHT)
reg = STK3310_REG_ALS_DATA_MSB;
else if (chan->type == IIO_PROXIMITY)
reg = STK3310_REG_PS_DATA_MSB;
else
return -EINVAL;
mutex_lock(&data->lock);
ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
if (ret < 0) {
dev_err(&client->dev, "register read failed\n");
mutex_unlock(&data->lock);
return ret;
}
*val = swab16(buf);
if (chan->type == IIO_PROXIMITY) {
/*
* Invert the proximity data so we return low values
* for close objects and high values for far ones.
*/
regmap_field_read(data->reg_ps_gain, &index);
*val = stk3310_ps_max[index] - *val;
}
mutex_unlock(&data->lock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type == IIO_LIGHT)
regmap_field_read(data->reg_als_it, &index);
else
regmap_field_read(data->reg_ps_it, &index);
*val = stk3310_it_table[index][0];
*val2 = stk3310_it_table[index][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_LIGHT)
regmap_field_read(data->reg_als_gain, &index);
else
regmap_field_read(data->reg_ps_gain, &index);
*val = stk3310_scale_table[index][0];
*val2 = stk3310_scale_table[index][1];
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
static int stk3310_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
int index;
struct stk3310_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
index = stk3310_get_index(stk3310_it_table,
ARRAY_SIZE(stk3310_it_table),
val, val2);
if (index < 0)
return -EINVAL;
mutex_lock(&data->lock);
if (chan->type == IIO_LIGHT)
ret = regmap_field_write(data->reg_als_it, index);
else
ret = regmap_field_write(data->reg_ps_it, index);
if (ret < 0)
dev_err(&data->client->dev,
"sensor configuration failed\n");
mutex_unlock(&data->lock);
return ret;
case IIO_CHAN_INFO_SCALE:
index = stk3310_get_index(stk3310_scale_table,
ARRAY_SIZE(stk3310_scale_table),
val, val2);
if (index < 0)
return -EINVAL;
mutex_lock(&data->lock);
if (chan->type == IIO_LIGHT)
ret = regmap_field_write(data->reg_als_gain, index);
else
ret = regmap_field_write(data->reg_ps_gain, index);
if (ret < 0)
dev_err(&data->client->dev,
"sensor configuration failed\n");
mutex_unlock(&data->lock);
return ret;
}
return -EINVAL;
}
static const struct iio_info stk3310_info = {
.driver_module = THIS_MODULE,
.read_raw = stk3310_read_raw,
.write_raw = stk3310_write_raw,
.attrs = &stk3310_attribute_group,
.read_event_value = stk3310_read_event,
.write_event_value = stk3310_write_event,
.read_event_config = stk3310_read_event_config,
.write_event_config = stk3310_write_event_config,
};
static int stk3310_set_state(struct stk3310_data *data, u8 state)
{
int ret;
struct i2c_client *client = data->client;
/* 3-bit state; 0b100 is not supported. */
if (state > 7 || state == 4)
return -EINVAL;
mutex_lock(&data->lock);
ret = regmap_field_write(data->reg_state, state);
if (ret < 0) {
dev_err(&client->dev, "failed to change sensor state\n");
} else if (state != STK3310_STATE_STANDBY) {
/* Don't reset the 'enabled' flags if we're going in standby */
data->ps_enabled = !!(state & 0x01);
data->als_enabled = !!(state & 0x02);
}
mutex_unlock(&data->lock);
return ret;
}
static int stk3310_init(struct iio_dev *indio_dev)
{
int ret;
int chipid;
u8 state;
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
regmap_read(data->regmap, STK3310_REG_ID, &chipid);
if (chipid != STK3310_CHIP_ID_VAL &&
chipid != STK3311_CHIP_ID_VAL) {
dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
return -ENODEV;
}
state = STK3310_STATE_EN_ALS | STK3310_STATE_EN_PS;
ret = stk3310_set_state(data, state);
if (ret < 0) {
dev_err(&client->dev, "failed to enable sensor");
return ret;
}
/* Enable PS interrupts */
ret = regmap_field_write(data->reg_int_ps, STK3310_PSINT_EN);
if (ret < 0)
dev_err(&client->dev, "failed to enable interrupts!\n");
return ret;
}
static int stk3310_gpio_probe(struct i2c_client *client)
{
struct device *dev;
struct gpio_desc *gpio;
int ret;
if (!client)
return -EINVAL;
dev = &client->dev;
/* gpio interrupt pin */
gpio = devm_gpiod_get_index(dev, STK3310_GPIO, 0);
if (IS_ERR(gpio)) {
dev_err(dev, "acpi gpio get index failed\n");
return PTR_ERR(gpio);
}
ret = gpiod_direction_input(gpio);
if (ret)
return ret;
ret = gpiod_to_irq(gpio);
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
return ret;
}
static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case STK3310_REG_ALS_DATA_MSB:
case STK3310_REG_ALS_DATA_LSB:
case STK3310_REG_PS_DATA_LSB:
case STK3310_REG_PS_DATA_MSB:
case STK3310_REG_FLAG:
return true;
default:
return false;
}
}
static struct regmap_config stk3310_regmap_config = {
.name = STK3310_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
.max_register = STK3310_MAX_REG,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = stk3310_is_volatile_reg,
};
static int stk3310_regmap_init(struct stk3310_data *data)
{
struct regmap *regmap;
struct i2c_client *client;
client = data->client;
regmap = devm_regmap_init_i2c(client, &stk3310_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "regmap initialization failed.\n");
return PTR_ERR(regmap);
}
data->regmap = regmap;
STK3310_REGFIELD(state);
STK3310_REGFIELD(als_gain);
STK3310_REGFIELD(ps_gain);
STK3310_REGFIELD(als_it);
STK3310_REGFIELD(ps_it);
STK3310_REGFIELD(int_ps);
STK3310_REGFIELD(flag_psint);
STK3310_REGFIELD(flag_nf);
return 0;
}
static irqreturn_t stk3310_irq_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct stk3310_data *data = iio_priv(indio_dev);
data->timestamp = iio_get_time_ns();
return IRQ_WAKE_THREAD;
}
static irqreturn_t stk3310_irq_event_handler(int irq, void *private)
{
int ret;
unsigned int dir;
u64 event;
struct iio_dev *indio_dev = private;
struct stk3310_data *data = iio_priv(indio_dev);
/* Read FLAG_NF to figure out what threshold has been met. */
mutex_lock(&data->lock);
ret = regmap_field_read(data->reg_flag_nf, &dir);
if (ret < 0) {
dev_err(&data->client->dev, "register read failed\n");
mutex_unlock(&data->lock);
return ret;
}
event = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
IIO_EV_TYPE_THRESH,
(dir ? IIO_EV_DIR_RISING :
IIO_EV_DIR_FALLING));
iio_push_event(indio_dev, event, data->timestamp);
/* Reset the interrupt flag */
ret = regmap_field_write(data->reg_flag_psint, 0);
if (ret < 0)
dev_err(&data->client->dev, "failed to reset interrupts\n");
mutex_unlock(&data->lock);
return IRQ_HANDLED;
}
static int stk3310_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct stk3310_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
mutex_init(&data->lock);
ret = stk3310_regmap_init(data);
if (ret < 0)
return ret;
indio_dev->dev.parent = &client->dev;
indio_dev->info = &stk3310_info;
indio_dev->name = STK3310_DRIVER_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = stk3310_channels;
indio_dev->num_channels = ARRAY_SIZE(stk3310_channels);
ret = stk3310_init(indio_dev);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
stk3310_set_state(data, STK3310_STATE_STANDBY);
}
if (client->irq <= 0)
client->irq = stk3310_gpio_probe(client);
if (client->irq >= 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
stk3310_irq_handler,
stk3310_irq_event_handler,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
STK3310_EVENT, indio_dev);
if (ret < 0)
dev_err(&client->dev, "request irq %d failed\n",
client->irq);
}
return ret;
}
static int stk3310_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return stk3310_set_state(iio_priv(indio_dev), STK3310_STATE_STANDBY);
}
#ifdef CONFIG_PM_SLEEP
static int stk3310_suspend(struct device *dev)
{
struct stk3310_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return stk3310_set_state(data, STK3310_STATE_STANDBY);
}
static int stk3310_resume(struct device *dev)
{
int state = 0;
struct stk3310_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
if (data->ps_enabled)
state |= STK3310_STATE_EN_PS;
if (data->als_enabled)
state |= STK3310_STATE_EN_ALS;
return stk3310_set_state(data, state);
}
static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume);
#define STK3310_PM_OPS (&stk3310_pm_ops)
#else
#define STK3310_PM_OPS NULL
#endif
static const struct i2c_device_id stk3310_i2c_id[] = {
{"STK3310", 0},
{"STK3311", 0},
{}
};
static const struct acpi_device_id stk3310_acpi_id[] = {
{"STK3310", 0},
{"STK3311", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id);
static struct i2c_driver stk3310_driver = {
.driver = {
.name = "stk3310",
.pm = STK3310_PM_OPS,
.acpi_match_table = ACPI_PTR(stk3310_acpi_id),
},
.probe = stk3310_probe,
.remove = stk3310_remove,
.id_table = stk3310_i2c_id,
};
module_i2c_driver(stk3310_driver);
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
MODULE_DESCRIPTION("STK3310 Ambient Light and Proximity Sensor driver");
MODULE_LICENSE("GPL v2");

View file

@ -240,7 +240,7 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
* convert between normalized values and HW values obtained using given
* timing and gain settings.
*/
static int adc_shiftbits(u8 timing)
static int tsl2563_adc_shiftbits(u8 timing)
{
int shift = 0;
@ -263,9 +263,9 @@ static int adc_shiftbits(u8 timing)
}
/* Convert a HW ADC value to normalized scale. */
static u32 normalize_adc(u16 adc, u8 timing)
static u32 tsl2563_normalize_adc(u16 adc, u8 timing)
{
return adc << adc_shiftbits(timing);
return adc << tsl2563_adc_shiftbits(timing);
}
static void tsl2563_wait_adc(struct tsl2563_chip *chip)
@ -350,8 +350,8 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip)
retry = tsl2563_adjust_gainlevel(chip, adc0);
}
chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
chip->data0 = tsl2563_normalize_adc(adc0, chip->gainlevel->gaintime);
chip->data1 = tsl2563_normalize_adc(adc1, chip->gainlevel->gaintime);
if (!chip->int_enabled)
schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
@ -361,13 +361,13 @@ out:
return ret;
}
static inline int calib_to_sysfs(u32 calib)
static inline int tsl2563_calib_to_sysfs(u32 calib)
{
return (int) (((calib * CALIB_BASE_SYSFS) +
CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
}
static inline u32 calib_from_sysfs(int value)
static inline u32 tsl2563_calib_from_sysfs(int value)
{
return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
}
@ -426,7 +426,7 @@ static const struct tsl2563_lux_coeff lux_table[] = {
};
/* Convert normalized, scaled ADC values to lux. */
static unsigned int adc_to_lux(u32 adc0, u32 adc1)
static unsigned int tsl2563_adc_to_lux(u32 adc0, u32 adc1)
{
const struct tsl2563_lux_coeff *lp = lux_table;
unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
@ -442,7 +442,7 @@ static unsigned int adc_to_lux(u32 adc0, u32 adc1)
}
/* Apply calibration coefficient to ADC count. */
static u32 calib_adc(u32 adc, u32 calib)
static u32 tsl2563_calib_adc(u32 adc, u32 calib)
{
unsigned long scaled = adc;
@ -463,9 +463,9 @@ static int tsl2563_write_raw(struct iio_dev *indio_dev,
if (mask != IIO_CHAN_INFO_CALIBSCALE)
return -EINVAL;
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
chip->calib0 = calib_from_sysfs(val);
chip->calib0 = tsl2563_calib_from_sysfs(val);
else if (chan->channel2 == IIO_MOD_LIGHT_IR)
chip->calib1 = calib_from_sysfs(val);
chip->calib1 = tsl2563_calib_from_sysfs(val);
else
return -EINVAL;
@ -491,11 +491,11 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
ret = tsl2563_get_adc(chip);
if (ret)
goto error_ret;
calib0 = calib_adc(chip->data0, chip->calib0) *
calib0 = tsl2563_calib_adc(chip->data0, chip->calib0) *
chip->cover_comp_gain;
calib1 = calib_adc(chip->data1, chip->calib1) *
calib1 = tsl2563_calib_adc(chip->data1, chip->calib1) *
chip->cover_comp_gain;
*val = adc_to_lux(calib0, calib1);
*val = tsl2563_adc_to_lux(calib0, calib1);
ret = IIO_VAL_INT;
break;
case IIO_INTENSITY:
@ -515,9 +515,9 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
*val = calib_to_sysfs(chip->calib0);
*val = tsl2563_calib_to_sysfs(chip->calib0);
else
*val = calib_to_sysfs(chip->calib1);
*val = tsl2563_calib_to_sysfs(chip->calib1);
ret = IIO_VAL_INT;
break;
default:
@ -750,8 +750,8 @@ static int tsl2563_probe(struct i2c_client *client,
chip->high_thres = 0xffff;
chip->gainlevel = tsl2563_gainlevel_table;
chip->intr = TSL2563_INT_PERSIST(4);
chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
if (pdata)
chip->cover_comp_gain = pdata->cover_comp_gain;

View file

@ -24,12 +24,12 @@
#define TSL4531_DRV_NAME "tsl4531"
#define TCS3472_COMMAND BIT(7)
#define TSL4531_COMMAND BIT(7)
#define TSL4531_CONTROL (TCS3472_COMMAND | 0x00)
#define TSL4531_CONFIG (TCS3472_COMMAND | 0x01)
#define TSL4531_DATA (TCS3472_COMMAND | 0x04)
#define TSL4531_ID (TCS3472_COMMAND | 0x0a)
#define TSL4531_CONTROL (TSL4531_COMMAND | 0x00)
#define TSL4531_CONFIG (TSL4531_COMMAND | 0x01)
#define TSL4531_DATA (TSL4531_COMMAND | 0x04)
#define TSL4531_ID (TSL4531_COMMAND | 0x0a)
/* operating modes in control register */
#define TSL4531_MODE_POWERDOWN 0x00

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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",

View file

@ -0,0 +1,579 @@
/*
* MMC35240 - MEMSIC 3-axis Magnetic Sensor
*
* Copyright (c) 2015, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* IIO driver for MMC35240 (7-bit I2C slave address 0x30).
*
* TODO: offset, ACPI, continuous measurement mode, PM
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/acpi.h>
#include <linux/pm.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define MMC35240_DRV_NAME "mmc35240"
#define MMC35240_REGMAP_NAME "mmc35240_regmap"
#define MMC35240_REG_XOUT_L 0x00
#define MMC35240_REG_XOUT_H 0x01
#define MMC35240_REG_YOUT_L 0x02
#define MMC35240_REG_YOUT_H 0x03
#define MMC35240_REG_ZOUT_L 0x04
#define MMC35240_REG_ZOUT_H 0x05
#define MMC35240_REG_STATUS 0x06
#define MMC35240_REG_CTRL0 0x07
#define MMC35240_REG_CTRL1 0x08
#define MMC35240_REG_ID 0x20
#define MMC35240_STATUS_MEAS_DONE_BIT BIT(0)
#define MMC35240_CTRL0_REFILL_BIT BIT(7)
#define MMC35240_CTRL0_RESET_BIT BIT(6)
#define MMC35240_CTRL0_SET_BIT BIT(5)
#define MMC35240_CTRL0_CMM_BIT BIT(1)
#define MMC35240_CTRL0_TM_BIT BIT(0)
/* output resolution bits */
#define MMC35240_CTRL1_BW0_BIT BIT(0)
#define MMC35240_CTRL1_BW1_BIT BIT(1)
#define MMC35240_CTRL1_BW_MASK (MMC35240_CTRL1_BW0_BIT | \
MMC35240_CTRL1_BW1_BIT)
#define MMC35240_CTRL1_BW_SHIFT 0
#define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */
#define MMC53240_WAIT_SET_RESET 1000 /* us */
/*
* Memsic OTP process code piece is put here for reference:
*
* #define OTP_CONVERT(REG) ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006
* 1) For X axis, the COEFFICIENT is always 1.
* 2) For Y axis, the COEFFICIENT is as below:
* f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) |
* (reg_data[2] >> 4)) + 1.0;
* 3) For Z axis, the COEFFICIENT is as below:
* f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35;
* We implemented the OTP logic into driver.
*/
/* scale = 1000 here for Y otp */
#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6)
/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */
#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81)
#define MMC35240_X_COEFF(x) (x)
#define MMC35240_Y_COEFF(y) (y + 1000)
#define MMC35240_Z_COEFF(z) (z + 13500)
#define MMC35240_OTP_START_ADDR 0x1B
enum mmc35240_resolution {
MMC35240_16_BITS_SLOW = 0, /* 100 Hz */
MMC35240_16_BITS_FAST, /* 200 Hz */
MMC35240_14_BITS, /* 333 Hz */
MMC35240_12_BITS, /* 666 Hz */
};
enum mmc35240_axis {
AXIS_X = 0,
AXIS_Y,
AXIS_Z,
};
static const struct {
int sens[3]; /* sensitivity per X, Y, Z axis */
int nfo; /* null field output */
} mmc35240_props_table[] = {
/* 16 bits, 100Hz ODR */
{
{1024, 1024, 1024},
32768,
},
/* 16 bits, 200Hz ODR */
{
{1024, 1024, 770},
32768,
},
/* 14 bits, 333Hz ODR */
{
{256, 256, 193},
8192,
},
/* 12 bits, 666Hz ODR */
{
{64, 64, 48},
2048,
},
};
struct mmc35240_data {
struct i2c_client *client;
struct mutex mutex;
struct regmap *regmap;
enum mmc35240_resolution res;
/* OTP compensation */
int axis_coef[3];
int axis_scale[3];
};
static const int mmc35240_samp_freq[] = {100, 200, 333, 666};
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 333 666");
#define MMC35240_CHANNEL(_axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _axis, \
.address = AXIS_ ## _axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec mmc35240_channels[] = {
MMC35240_CHANNEL(X),
MMC35240_CHANNEL(Y),
MMC35240_CHANNEL(Z),
};
static struct attribute *mmc35240_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group mmc35240_attribute_group = {
.attrs = mmc35240_attributes,
};
static int mmc35240_get_samp_freq_index(struct mmc35240_data *data,
int val, int val2)
{
int i;
for (i = 0; i < ARRAY_SIZE(mmc35240_samp_freq); i++)
if (mmc35240_samp_freq[i] == val)
return i;
return -EINVAL;
}
static int mmc35240_hw_set(struct mmc35240_data *data, bool set)
{
int ret;
u8 coil_bit;
/*
* Recharge the capacitor at VCAP pin, requested to be issued
* before a SET/RESET command.
*/
ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
MMC35240_CTRL0_REFILL_BIT,
MMC35240_CTRL0_REFILL_BIT);
if (ret < 0)
return ret;
usleep_range(MMC35240_WAIT_CHARGE_PUMP, MMC35240_WAIT_CHARGE_PUMP + 1);
if (set)
coil_bit = MMC35240_CTRL0_SET_BIT;
else
coil_bit = MMC35240_CTRL0_RESET_BIT;
return regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
MMC35240_CTRL0_REFILL_BIT,
coil_bit);
}
static int mmc35240_init(struct mmc35240_data *data)
{
int ret, y_convert, z_convert;
unsigned int reg_id;
u8 otp_data[6];
ret = regmap_read(data->regmap, MMC35240_REG_ID, &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 */
ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
MMC35240_CTRL1_BW_MASK,
data->res << MMC35240_CTRL1_BW_SHIFT);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR,
(u8 *)otp_data, sizeof(otp_data));
if (ret < 0)
return ret;
y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) |
(otp_data[2] >> 4));
z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f);
data->axis_coef[0] = MMC35240_X_COEFF(1);
data->axis_coef[1] = MMC35240_Y_COEFF(y_convert);
data->axis_coef[2] = MMC35240_Z_COEFF(z_convert);
data->axis_scale[0] = 1;
data->axis_scale[1] = 1000;
data->axis_scale[2] = 10000;
return 0;
}
static int mmc35240_take_measurement(struct mmc35240_data *data)
{
int ret, tries = 100;
unsigned int reg_status;
ret = regmap_write(data->regmap, MMC35240_REG_CTRL0,
MMC35240_CTRL0_TM_BIT);
if (ret < 0)
return ret;
while (tries-- > 0) {
ret = regmap_read(data->regmap, MMC35240_REG_STATUS,
&reg_status);
if (ret < 0)
return ret;
if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT)
break;
/* minimum wait time to complete measurement is 10 ms */
usleep_range(10000, 11000);
}
if (tries < 0) {
dev_err(&data->client->dev, "data not ready\n");
return -EIO;
}
return 0;
}
static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3])
{
int ret;
ret = mmc35240_take_measurement(data);
if (ret < 0)
return ret;
return regmap_bulk_read(data->regmap, MMC35240_REG_XOUT_L, (u8 *)buf,
3 * sizeof(__le16));
}
/**
* mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply
compensation for output value.
*
* @data: device private data
* @index: axis index for which we want the conversion
* @buf: raw data to be converted, 2 bytes in little endian format
* @val: compensated output reading (unit is milli gauss)
*
* Returns: 0 in case of success, -EINVAL when @index is not valid
*/
static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
__le16 buf[], int *val)
{
int raw_x, raw_y, raw_z;
int sens_x, sens_y, sens_z;
int nfo;
raw_x = le16_to_cpu(buf[AXIS_X]);
raw_y = le16_to_cpu(buf[AXIS_Y]);
raw_z = le16_to_cpu(buf[AXIS_Z]);
sens_x = mmc35240_props_table[data->res].sens[AXIS_X];
sens_y = mmc35240_props_table[data->res].sens[AXIS_Y];
sens_z = mmc35240_props_table[data->res].sens[AXIS_Z];
nfo = mmc35240_props_table[data->res].nfo;
switch (index) {
case AXIS_X:
*val = (raw_x - nfo) * 1000 / sens_x;
break;
case AXIS_Y:
*val = (raw_y - nfo) * 1000 / sens_y -
(raw_z - nfo) * 1000 / sens_z;
break;
case AXIS_Z:
*val = (raw_y - nfo) * 1000 / sens_y +
(raw_z - nfo) * 1000 / sens_z;
break;
default:
return -EINVAL;
}
/* apply OTP compensation */
*val = (*val) * data->axis_coef[index] / data->axis_scale[index];
return 0;
}
static int mmc35240_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct mmc35240_data *data = iio_priv(indio_dev);
int ret, i;
unsigned int reg;
__le16 buf[3];
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->mutex);
ret = mmc35240_read_measurement(data, buf);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = 1000;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, MMC35240_REG_CTRL1, &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");

View file

@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/iio/common/st_sensors.h>
#define LSM303DLH_MAGN_DEV_NAME "lsm303dlh_magn"
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"

View file

@ -45,6 +45,46 @@
#define ST_MAGN_FS_AVL_12000MG 12000
#define ST_MAGN_FS_AVL_16000MG 16000
/* CUSTOM VALUES FOR SENSOR 0 */
#define ST_MAGN_0_ODR_ADDR 0x00
#define ST_MAGN_0_ODR_MASK 0x1c
#define ST_MAGN_0_ODR_AVL_1HZ_VAL 0x00
#define ST_MAGN_0_ODR_AVL_2HZ_VAL 0x01
#define ST_MAGN_0_ODR_AVL_3HZ_VAL 0x02
#define ST_MAGN_0_ODR_AVL_8HZ_VAL 0x03
#define ST_MAGN_0_ODR_AVL_15HZ_VAL 0x04
#define ST_MAGN_0_ODR_AVL_30HZ_VAL 0x05
#define ST_MAGN_0_ODR_AVL_75HZ_VAL 0x06
#define ST_MAGN_0_ODR_AVL_220HZ_VAL 0x07
#define ST_MAGN_0_PW_ADDR 0x02
#define ST_MAGN_0_PW_MASK 0x03
#define ST_MAGN_0_PW_ON 0x00
#define ST_MAGN_0_PW_OFF 0x03
#define ST_MAGN_0_FS_ADDR 0x01
#define ST_MAGN_0_FS_MASK 0xe0
#define ST_MAGN_0_FS_AVL_1300_VAL 0x01
#define ST_MAGN_0_FS_AVL_1900_VAL 0x02
#define ST_MAGN_0_FS_AVL_2500_VAL 0x03
#define ST_MAGN_0_FS_AVL_4000_VAL 0x04
#define ST_MAGN_0_FS_AVL_4700_VAL 0x05
#define ST_MAGN_0_FS_AVL_5600_VAL 0x06
#define ST_MAGN_0_FS_AVL_8100_VAL 0x07
#define ST_MAGN_0_FS_AVL_1300_GAIN_XY 1100
#define ST_MAGN_0_FS_AVL_1900_GAIN_XY 855
#define ST_MAGN_0_FS_AVL_2500_GAIN_XY 670
#define ST_MAGN_0_FS_AVL_4000_GAIN_XY 450
#define ST_MAGN_0_FS_AVL_4700_GAIN_XY 400
#define ST_MAGN_0_FS_AVL_5600_GAIN_XY 330
#define ST_MAGN_0_FS_AVL_8100_GAIN_XY 230
#define ST_MAGN_0_FS_AVL_1300_GAIN_Z 980
#define ST_MAGN_0_FS_AVL_1900_GAIN_Z 760
#define ST_MAGN_0_FS_AVL_2500_GAIN_Z 600
#define ST_MAGN_0_FS_AVL_4000_GAIN_Z 400
#define ST_MAGN_0_FS_AVL_4700_GAIN_Z 355
#define ST_MAGN_0_FS_AVL_5600_GAIN_Z 295
#define ST_MAGN_0_FS_AVL_8100_GAIN_Z 205
#define ST_MAGN_0_MULTIREAD_BIT false
/* CUSTOM VALUES FOR SENSOR 1 */
#define ST_MAGN_1_WAI_EXP 0x3c
#define ST_MAGN_1_ODR_ADDR 0x00
@ -150,6 +190,82 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
};
static const struct st_sensor_settings st_magn_sensors_settings[] = {
{
.wai = 0, /* This sensor has no valid WhoAmI report 0 */
.sensors_supported = {
[0] = LSM303DLH_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
.odr = {
.addr = ST_MAGN_0_ODR_ADDR,
.mask = ST_MAGN_0_ODR_MASK,
.odr_avl = {
{ 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
{ 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
{ 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
{ 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
{ 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
{ 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
{ 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
},
},
.pw = {
.addr = ST_MAGN_0_PW_ADDR,
.mask = ST_MAGN_0_PW_MASK,
.value_on = ST_MAGN_0_PW_ON,
.value_off = ST_MAGN_0_PW_OFF,
},
.fs = {
.addr = ST_MAGN_0_FS_ADDR,
.mask = ST_MAGN_0_FS_MASK,
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_1300MG,
.value = ST_MAGN_0_FS_AVL_1300_VAL,
.gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
},
[1] = {
.num = ST_MAGN_FS_AVL_1900MG,
.value = ST_MAGN_0_FS_AVL_1900_VAL,
.gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
},
[2] = {
.num = ST_MAGN_FS_AVL_2500MG,
.value = ST_MAGN_0_FS_AVL_2500_VAL,
.gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
},
[3] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = ST_MAGN_0_FS_AVL_4000_VAL,
.gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
},
[4] = {
.num = ST_MAGN_FS_AVL_4700MG,
.value = ST_MAGN_0_FS_AVL_4700_VAL,
.gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
},
[5] = {
.num = ST_MAGN_FS_AVL_5600MG,
.value = ST_MAGN_0_FS_AVL_5600_VAL,
.gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
},
[6] = {
.num = ST_MAGN_FS_AVL_8100MG,
.value = ST_MAGN_0_FS_AVL_8100_VAL,
.gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
},
},
},
.multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
.bootime = 2,
},
{
.wai = ST_MAGN_1_WAI_EXP,
.sensors_supported = {

View file

@ -20,6 +20,10 @@
#ifdef CONFIG_OF
static const struct of_device_id st_magn_of_match[] = {
{
.compatible = "st,lsm303dlh-magn",
.data = LSM303DLH_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm303dlhc-magn",
.data = LSM303DLHC_MAGN_DEV_NAME,
@ -71,6 +75,7 @@ static int st_magn_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id st_magn_id_table[] = {
{ LSM303DLH_MAGN_DEV_NAME },
{ LSM303DLHC_MAGN_DEV_NAME },
{ LSM303DLM_MAGN_DEV_NAME },
{ LIS3MDL_MAGN_DEV_NAME },

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -18,6 +18,8 @@
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/pm.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@ -29,7 +31,9 @@
#define SX9500_DRIVER_NAME "sx9500"
#define SX9500_IRQ_NAME "sx9500_event"
#define SX9500_GPIO_NAME "sx9500_gpio"
#define SX9500_GPIO_INT "interrupt"
#define SX9500_GPIO_RESET "reset"
/* Register definitions. */
#define SX9500_REG_IRQ_SRC 0x00
@ -73,6 +77,7 @@
#define SX9500_CONVDONE_IRQ BIT(3)
#define SX9500_PROXSTAT_SHIFT 4
#define SX9500_COMPSTAT_MASK GENMASK(3, 0)
#define SX9500_NUM_CHANNELS 4
@ -81,6 +86,7 @@ struct sx9500_data {
struct i2c_client *client;
struct iio_trigger *trig;
struct regmap *regmap;
struct gpio_desc *gpiod_rst;
/*
* Last reading of the proximity status for each channel. We
* only send an event to user space when this changes.
@ -89,6 +95,11 @@ struct sx9500_data {
bool event_enabled[SX9500_NUM_CHANNELS];
bool trigger_enabled;
u16 *buffer;
/* Remember enabled channels and sample rate during suspend. */
unsigned int suspend_ctrl0;
struct completion completion;
int data_rdy_users, close_far_users;
int channel_users[SX9500_NUM_CHANNELS];
};
static const struct iio_event_spec sx9500_events[] = {
@ -139,6 +150,10 @@ static const struct {
{2, 500000},
};
static const unsigned int sx9500_scan_period_table[] = {
30, 60, 90, 120, 150, 200, 300, 400,
};
static const struct regmap_range sx9500_writable_reg_ranges[] = {
regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
@ -191,7 +206,67 @@ static const struct regmap_config sx9500_regmap_config = {
.volatile_table = &sx9500_volatile_regs,
};
static int sx9500_read_proximity(struct sx9500_data *data,
static int sx9500_inc_users(struct sx9500_data *data, int *counter,
unsigned int reg, unsigned int bitmask)
{
(*counter)++;
if (*counter != 1)
/* Bit is already active, nothing to do. */
return 0;
return regmap_update_bits(data->regmap, reg, bitmask, bitmask);
}
static int sx9500_dec_users(struct sx9500_data *data, int *counter,
unsigned int reg, unsigned int bitmask)
{
(*counter)--;
if (*counter != 0)
/* There are more users, do not deactivate. */
return 0;
return regmap_update_bits(data->regmap, reg, bitmask, 0);
}
static int sx9500_inc_chan_users(struct sx9500_data *data, int chan)
{
return sx9500_inc_users(data, &data->channel_users[chan],
SX9500_REG_PROX_CTRL0, BIT(chan));
}
static int sx9500_dec_chan_users(struct sx9500_data *data, int chan)
{
return sx9500_dec_users(data, &data->channel_users[chan],
SX9500_REG_PROX_CTRL0, BIT(chan));
}
static int sx9500_inc_data_rdy_users(struct sx9500_data *data)
{
return sx9500_inc_users(data, &data->data_rdy_users,
SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
}
static int sx9500_dec_data_rdy_users(struct sx9500_data *data)
{
return sx9500_dec_users(data, &data->data_rdy_users,
SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
}
static int sx9500_inc_close_far_users(struct sx9500_data *data)
{
return sx9500_inc_users(data, &data->close_far_users,
SX9500_REG_IRQ_MSK,
SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
}
static int sx9500_dec_close_far_users(struct sx9500_data *data)
{
return sx9500_dec_users(data, &data->close_far_users,
SX9500_REG_IRQ_MSK,
SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
}
static int sx9500_read_prox_data(struct sx9500_data *data,
const struct iio_chan_spec *chan,
int *val)
{
@ -211,6 +286,79 @@ static int sx9500_read_proximity(struct sx9500_data *data,
return IIO_VAL_INT;
}
/*
* If we have no interrupt support, we have to wait for a scan period
* after enabling a channel to get a result.
*/
static int sx9500_wait_for_sample(struct sx9500_data *data)
{
int ret;
unsigned int val;
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &val);
if (ret < 0)
return ret;
val = (val & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
msleep(sx9500_scan_period_table[val]);
return 0;
}
static int sx9500_read_proximity(struct sx9500_data *data,
const struct iio_chan_spec *chan,
int *val)
{
int ret;
mutex_lock(&data->mutex);
ret = sx9500_inc_chan_users(data, chan->channel);
if (ret < 0)
goto out;
ret = sx9500_inc_data_rdy_users(data);
if (ret < 0)
goto out_dec_chan;
mutex_unlock(&data->mutex);
if (data->client->irq > 0)
ret = wait_for_completion_interruptible(&data->completion);
else
ret = sx9500_wait_for_sample(data);
if (ret < 0)
return ret;
mutex_lock(&data->mutex);
ret = sx9500_read_prox_data(data, chan, val);
if (ret < 0)
goto out;
ret = sx9500_dec_chan_users(data, chan->channel);
if (ret < 0)
goto out;
ret = sx9500_dec_data_rdy_users(data);
if (ret < 0)
goto out;
ret = IIO_VAL_INT;
goto out;
out_dec_chan:
sx9500_dec_chan_users(data, chan->channel);
out:
mutex_unlock(&data->mutex);
reinit_completion(&data->completion);
return ret;
}
static int sx9500_read_samp_freq(struct sx9500_data *data,
int *val, int *val2)
{
@ -236,7 +384,6 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
switch (chan->type) {
case IIO_PROXIMITY:
@ -244,10 +391,7 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
mutex_lock(&data->mutex);
ret = sx9500_read_proximity(data, chan, val);
mutex_unlock(&data->mutex);
return ret;
return sx9500_read_proximity(data, chan, val);
case IIO_CHAN_INFO_SAMP_FREQ:
return sx9500_read_samp_freq(data, val, val2);
default:
@ -318,28 +462,16 @@ static irqreturn_t sx9500_irq_handler(int irq, void *private)
return IRQ_WAKE_THREAD;
}
static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
static void sx9500_push_events(struct iio_dev *indio_dev)
{
struct iio_dev *indio_dev = private;
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
unsigned int val, chan;
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
if (ret < 0) {
dev_err(&data->client->dev, "i2c transfer error in irq\n");
goto out;
}
if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ)))
goto out;
struct sx9500_data *data = iio_priv(indio_dev);
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
if (ret < 0) {
dev_err(&data->client->dev, "i2c transfer error in irq\n");
goto out;
return;
}
val >>= SX9500_PROXSTAT_SHIFT;
@ -354,15 +486,34 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
/* No change on this channel. */
continue;
dir = new_prox ? IIO_EV_DIR_FALLING :
IIO_EV_DIR_RISING;
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
chan,
IIO_EV_TYPE_THRESH,
dir);
dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
IIO_EV_TYPE_THRESH, dir);
iio_push_event(indio_dev, ev, iio_get_time_ns());
data->prox_stat[chan] = new_prox;
}
}
static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
unsigned int val;
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
if (ret < 0) {
dev_err(&data->client->dev, "i2c transfer error in irq\n");
goto out;
}
if (val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ))
sx9500_push_events(indio_dev);
if (val & SX9500_CONVDONE_IRQ)
complete_all(&data->completion);
out:
mutex_unlock(&data->mutex);
@ -391,9 +542,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
int state)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
bool any_active = false;
unsigned int irqmask;
int ret;
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
dir != IIO_EV_DIR_EITHER)
@ -401,24 +550,32 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
mutex_lock(&data->mutex);
if (state == 1) {
ret = sx9500_inc_chan_users(data, chan->channel);
if (ret < 0)
goto out_unlock;
ret = sx9500_inc_close_far_users(data);
if (ret < 0)
goto out_undo_chan;
} else {
ret = sx9500_dec_chan_users(data, chan->channel);
if (ret < 0)
goto out_unlock;
ret = sx9500_dec_close_far_users(data);
if (ret < 0)
goto out_undo_chan;
}
data->event_enabled[chan->channel] = state;
goto out_unlock;
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
if (data->event_enabled[i]) {
any_active = true;
break;
}
irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ;
if (any_active)
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
irqmask, irqmask);
out_undo_chan:
if (state == 1)
sx9500_dec_chan_users(data, chan->channel);
else
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
irqmask, 0);
sx9500_inc_chan_users(data, chan->channel);
out_unlock:
mutex_unlock(&data->mutex);
return ret;
}
@ -469,12 +626,16 @@ static int sx9500_set_trigger_state(struct iio_trigger *trig,
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
SX9500_CONVDONE_IRQ,
state ? SX9500_CONVDONE_IRQ : 0);
if (ret == 0)
data->trigger_enabled = state;
if (state)
ret = sx9500_inc_data_rdy_users(data);
else
ret = sx9500_dec_data_rdy_users(data);
if (ret < 0)
goto out;
data->trigger_enabled = state;
out:
mutex_unlock(&data->mutex);
return ret;
@ -496,7 +657,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private)
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
ret = sx9500_read_prox_data(data, &indio_dev->channels[bit],
&val);
if (ret < 0)
goto out;
@ -515,6 +676,62 @@ out:
return IRQ_HANDLED;
}
static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
mutex_lock(&data->mutex);
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
if (test_bit(i, indio_dev->active_scan_mask)) {
ret = sx9500_inc_chan_users(data, i);
if (ret)
break;
}
if (ret)
for (i = i - 1; i >= 0; i--)
if (test_bit(i, indio_dev->active_scan_mask))
sx9500_dec_chan_users(data, i);
mutex_unlock(&data->mutex);
return ret;
}
static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
iio_triggered_buffer_predisable(indio_dev);
mutex_lock(&data->mutex);
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
if (test_bit(i, indio_dev->active_scan_mask)) {
ret = sx9500_dec_chan_users(data, i);
if (ret)
break;
}
if (ret)
for (i = i - 1; i >= 0; i--)
if (test_bit(i, indio_dev->active_scan_mask))
sx9500_inc_chan_users(data, i);
mutex_unlock(&data->mutex);
return ret;
}
static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = {
.preenable = sx9500_buffer_preenable,
.postenable = iio_triggered_buffer_postenable,
.predisable = sx9500_buffer_predisable,
};
struct sx9500_reg_default {
u8 reg;
u8 def;
@ -570,17 +787,57 @@ static const struct sx9500_reg_default sx9500_default_regs[] = {
},
{
.reg = SX9500_REG_PROX_CTRL0,
/* Scan period: 30ms, all sensors enabled. */
.def = 0x0f,
/* Scan period: 30ms, all sensors disabled. */
.def = 0x00,
},
};
/* Activate all channels and perform an initial compensation. */
static int sx9500_init_compensation(struct iio_dev *indio_dev)
{
struct sx9500_data *data = iio_priv(indio_dev);
int i, ret;
unsigned int val;
ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
GENMASK(SX9500_NUM_CHANNELS, 0),
GENMASK(SX9500_NUM_CHANNELS, 0));
if (ret < 0)
return ret;
for (i = 10; i >= 0; i--) {
usleep_range(10000, 20000);
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
if (ret < 0)
goto out;
if (!(val & SX9500_COMPSTAT_MASK))
break;
}
if (i < 0) {
dev_err(&data->client->dev, "initial compensation timed out");
ret = -ETIMEDOUT;
}
out:
regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
GENMASK(SX9500_NUM_CHANNELS, 0), 0);
return ret;
}
static int sx9500_init_device(struct iio_dev *indio_dev)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
unsigned int val;
if (data->gpiod_rst) {
gpiod_set_value_cansleep(data->gpiod_rst, 0);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(data->gpiod_rst, 1);
usleep_range(1000, 2000);
}
ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
if (ret < 0)
return ret;
@ -602,33 +859,34 @@ static int sx9500_init_device(struct iio_dev *indio_dev)
return ret;
}
return 0;
return sx9500_init_compensation(indio_dev);
}
static int sx9500_gpio_probe(struct i2c_client *client,
struct sx9500_data *data)
static void sx9500_gpio_probe(struct i2c_client *client,
struct sx9500_data *data)
{
struct device *dev;
struct gpio_desc *gpio;
int ret;
if (!client)
return -EINVAL;
return;
dev = &client->dev;
/* data ready gpio interrupt pin */
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
if (IS_ERR(gpio)) {
dev_err(dev, "acpi gpio get index failed\n");
return PTR_ERR(gpio);
if (client->irq <= 0) {
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_INT, 0, GPIOD_IN);
if (IS_ERR(gpio))
dev_err(dev, "gpio get irq failed\n");
else
client->irq = gpiod_to_irq(gpio);
}
ret = gpiod_to_irq(gpio);
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
return ret;
data->gpiod_rst = devm_gpiod_get_index(dev, SX9500_GPIO_RESET,
0, GPIOD_OUT_HIGH);
if (IS_ERR(data->gpiod_rst)) {
dev_warn(dev, "gpio get reset pin failed\n");
data->gpiod_rst = NULL;
}
}
static int sx9500_probe(struct i2c_client *client,
@ -645,14 +903,13 @@ static int sx9500_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->mutex);
init_completion(&data->completion);
data->trigger_enabled = false;
data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
sx9500_init_device(indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->name = SX9500_DRIVER_NAME;
indio_dev->channels = sx9500_channels;
@ -661,10 +918,15 @@ static int sx9500_probe(struct i2c_client *client,
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);
if (client->irq <= 0)
client->irq = sx9500_gpio_probe(client, data);
sx9500_gpio_probe(client, data);
if (client->irq > 0) {
ret = sx9500_init_device(indio_dev);
if (ret < 0)
return ret;
if (client->irq <= 0)
dev_warn(&client->dev, "no valid irq found\n");
else {
ret = devm_request_threaded_irq(&client->dev, client->irq,
sx9500_irq_handler, sx9500_irq_thread_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@ -687,7 +949,8 @@ static int sx9500_probe(struct i2c_client *client,
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
sx9500_trigger_handler, NULL);
sx9500_trigger_handler,
&sx9500_buffer_setup_ops);
if (ret < 0)
goto out_trigger_unregister;
@ -720,6 +983,49 @@ static int sx9500_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int sx9500_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0,
&data->suspend_ctrl0);
if (ret < 0)
goto out;
/*
* Scan period doesn't matter because when all the sensors are
* deactivated the device is in sleep mode.
*/
ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0, 0);
out:
mutex_unlock(&data->mutex);
return ret;
}
static int sx9500_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0,
data->suspend_ctrl0);
mutex_unlock(&data->mutex);
return ret;
}
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops sx9500_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume)
};
static const struct acpi_device_id sx9500_acpi_match[] = {
{"SSX9500", 0},
{ },
@ -728,7 +1034,7 @@ MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
static const struct i2c_device_id sx9500_id[] = {
{"sx9500", 0},
{}
{ },
};
MODULE_DEVICE_TABLE(i2c, sx9500_id);
@ -736,6 +1042,7 @@ static struct i2c_driver sx9500_driver = {
.driver = {
.name = SX9500_DRIVER_NAME,
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
.pm = &sx9500_pm_ops,
},
.probe = sx9500_probe,
.remove = sx9500_remove,

View file

@ -12,12 +12,24 @@
*
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
*
* TODO: sleep mode, configuration EEPROM
* To wake up from sleep mode, the SDA line must be held low while SCL is high
* for at least 33ms. This is achieved with an extra GPIO that can be connected
* directly to the SDA line. In normal operation, the GPIO is set as input and
* will not interfere in I2C communication. While the GPIO is driven low, the
* i2c adapter is locked since it cannot be used by other clients. The SCL line
* always has a pull-up so we do not need an extra GPIO to drive it high. If
* the "wakeup" GPIO is not given, power management will be disabled.
*
* TODO: filter configuration
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/gpio/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
@ -51,10 +63,101 @@
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
struct mlx90614_data {
struct i2c_client *client;
struct mutex lock; /* for EEPROM access only */
struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
unsigned long ready_timestamp; /* in jiffies */
};
/*
* Erase an address and write word.
* The mutex must be locked before calling.
*/
static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
u16 value)
{
/*
* Note: The mlx90614 requires a PEC on writing but does not send us a
* valid PEC on reading. Hence, we cannot set I2C_CLIENT_PEC in
* i2c_client.flags. As a workaround, we use i2c_smbus_xfer here.
*/
union i2c_smbus_data data;
s32 ret;
dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command);
data.word = 0x0000; /* erase command */
ret = i2c_smbus_xfer(client->adapter, client->addr,
client->flags | I2C_CLIENT_PEC,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_WORD_DATA, &data);
if (ret < 0)
return ret;
msleep(MLX90614_TIMING_EEPROM);
data.word = value; /* actual write */
ret = i2c_smbus_xfer(client->adapter, client->addr,
client->flags | I2C_CLIENT_PEC,
I2C_SMBUS_WRITE, command,
I2C_SMBUS_WORD_DATA, &data);
msleep(MLX90614_TIMING_EEPROM);
return ret;
}
#ifdef CONFIG_PM
/*
* If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
* the last wake-up. This is normally only needed to get a valid temperature
* reading. EEPROM access does not need such delay.
* Return 0 on success, <0 on error.
*/
static int mlx90614_power_get(struct mlx90614_data *data, bool startup)
{
unsigned long now;
if (!data->wakeup_gpio)
return 0;
pm_runtime_get_sync(&data->client->dev);
if (startup) {
now = jiffies;
if (time_before(now, data->ready_timestamp) &&
msleep_interruptible(jiffies_to_msecs(
data->ready_timestamp - now)) != 0) {
pm_runtime_put_autosuspend(&data->client->dev);
return -EINTR;
}
}
return 0;
}
static void mlx90614_power_put(struct mlx90614_data *data)
{
if (!data->wakeup_gpio)
return;
pm_runtime_mark_last_busy(&data->client->dev);
pm_runtime_put_autosuspend(&data->client->dev);
}
#else
static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
{
return 0;
}
static inline void mlx90614_power_put(struct mlx90614_data *data)
{
}
#endif
static int mlx90614_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long mask)
@ -85,9 +188,19 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
ret = i2c_smbus_read_word_data(data->client, cmd);
ret = mlx90614_power_get(data, true);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(data->client, cmd);
mlx90614_power_put(data);
if (ret < 0)
return ret;
/* MSB is an error flag */
if (ret & 0x8000)
return -EIO;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
@ -97,6 +210,63 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
*val = 20;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
mlx90614_power_get(data, false);
mutex_lock(&data->lock);
ret = i2c_smbus_read_word_data(data->client,
MLX90614_EMISSIVITY);
mutex_unlock(&data->lock);
mlx90614_power_put(data);
if (ret < 0)
return ret;
if (ret == 65535) {
*val = 1;
*val2 = 0;
} else {
*val = 0;
*val2 = ret * 15259; /* 1/65535 ~ 0.000015259 */
}
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static int mlx90614_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int val,
int val2, long mask)
{
struct mlx90614_data *data = iio_priv(indio_dev);
s32 ret;
switch (mask) {
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
return -EINVAL;
val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
mlx90614_power_get(data, false);
mutex_lock(&data->lock);
ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
val);
mutex_unlock(&data->lock);
mlx90614_power_put(data);
return ret;
default:
return -EINVAL;
}
}
static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_CALIBEMISSIVITY:
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
@ -115,7 +285,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.type = IIO_TEMP,
.modified = 1,
.channel2 = IIO_MOD_TEMP_OBJECT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@ -125,7 +296,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.modified = 1,
.channel = 1,
.channel2 = IIO_MOD_TEMP_OBJECT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@ -133,9 +305,103 @@ static const struct iio_chan_spec mlx90614_channels[] = {
static const struct iio_info mlx90614_info = {
.read_raw = mlx90614_read_raw,
.write_raw = mlx90614_write_raw,
.write_raw_get_fmt = mlx90614_write_raw_get_fmt,
.driver_module = THIS_MODULE,
};
#ifdef CONFIG_PM
static int mlx90614_sleep(struct mlx90614_data *data)
{
s32 ret;
if (!data->wakeup_gpio) {
dev_dbg(&data->client->dev, "Sleep disabled");
return -ENOSYS;
}
dev_dbg(&data->client->dev, "Requesting sleep");
mutex_lock(&data->lock);
ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
data->client->flags | I2C_CLIENT_PEC,
I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
I2C_SMBUS_BYTE, NULL);
mutex_unlock(&data->lock);
return ret;
}
static int mlx90614_wakeup(struct mlx90614_data *data)
{
if (!data->wakeup_gpio) {
dev_dbg(&data->client->dev, "Wake-up disabled");
return -ENOSYS;
}
dev_dbg(&data->client->dev, "Requesting wake-up");
i2c_lock_adapter(data->client->adapter);
gpiod_direction_output(data->wakeup_gpio, 0);
msleep(MLX90614_TIMING_WAKEUP);
gpiod_direction_input(data->wakeup_gpio);
i2c_unlock_adapter(data->client->adapter);
data->ready_timestamp = jiffies +
msecs_to_jiffies(MLX90614_TIMING_STARTUP);
/*
* Quirk: the i2c controller may get confused right after the
* wake-up signal has been sent. As a workaround, do a dummy read.
* If the read fails, the controller will probably be reset so that
* further reads will work.
*/
i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
return 0;
}
/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
static struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
{
struct gpio_desc *gpio;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE)) {
dev_info(&client->dev,
"i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
return NULL;
}
gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
if (IS_ERR(gpio)) {
dev_warn(&client->dev,
"gpio acquisition failed with error %ld, sleep disabled",
PTR_ERR(gpio));
return NULL;
} else if (!gpio) {
dev_info(&client->dev,
"wakeup-gpio not found, sleep disabled");
}
return gpio;
}
#else
static inline int mlx90614_sleep(struct mlx90614_data *data)
{
return -ENOSYS;
}
static inline int mlx90614_wakeup(struct mlx90614_data *data)
{
return -ENOSYS;
}
static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
{
return NULL;
}
#endif
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
{
@ -166,6 +432,10 @@ static int mlx90614_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
mutex_init(&data->lock);
data->wakeup_gpio = mlx90614_probe_wakeup(client);
mlx90614_wakeup(data);
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
@ -188,12 +458,30 @@ static int mlx90614_probe(struct i2c_client *client,
return ret;
}
if (data->wakeup_gpio) {
pm_runtime_set_autosuspend_delay(&client->dev,
MLX90614_AUTOSLEEP_DELAY);
pm_runtime_use_autosuspend(&client->dev);
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
}
return iio_device_register(indio_dev);
}
static int mlx90614_remove(struct i2c_client *client)
{
iio_device_unregister(i2c_get_clientdata(client));
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mlx90614_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
if (data->wakeup_gpio) {
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev))
mlx90614_sleep(data);
pm_runtime_set_suspended(&client->dev);
}
return 0;
}
@ -204,10 +492,67 @@ static const struct i2c_device_id mlx90614_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mlx90614_id);
#ifdef CONFIG_PM_SLEEP
static int mlx90614_pm_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mlx90614_data *data = iio_priv(indio_dev);
if (data->wakeup_gpio && pm_runtime_active(dev))
return mlx90614_sleep(data);
return 0;
}
static int mlx90614_pm_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mlx90614_data *data = iio_priv(indio_dev);
int err;
if (data->wakeup_gpio) {
err = mlx90614_wakeup(data);
if (err < 0)
return err;
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
return 0;
}
#endif
#ifdef CONFIG_PM
static int mlx90614_pm_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mlx90614_data *data = iio_priv(indio_dev);
return mlx90614_sleep(data);
}
static int mlx90614_pm_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mlx90614_data *data = iio_priv(indio_dev);
return mlx90614_wakeup(data);
}
#endif
static const struct dev_pm_ops mlx90614_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
mlx90614_pm_runtime_resume, NULL)
};
static struct i2c_driver mlx90614_driver = {
.driver = {
.name = "mlx90614",
.owner = THIS_MODULE,
.pm = &mlx90614_pm_ops,
},
.probe = mlx90614_probe,
.remove = mlx90614_remove,

View file

@ -41,8 +41,8 @@
#define TMP006_CONFIG_CR_MASK 0x0e00
#define TMP006_CONFIG_CR_SHIFT 9
#define MANUFACTURER_MAGIC 0x5449
#define DEVICE_MAGIC 0x0067
#define TMP006_MANUFACTURER_MAGIC 0x5449
#define TMP006_DEVICE_MAGIC 0x0067
struct tmp006_data {
struct i2c_client *client;
@ -191,7 +191,7 @@ static bool tmp006_check_identification(struct i2c_client *client)
if (did < 0)
return false;
return mid == MANUFACTURER_MAGIC && did == DEVICE_MAGIC;
return mid == TMP006_MANUFACTURER_MAGIC && did == TMP006_DEVICE_MAGIC;
}
static int tmp006_probe(struct i2c_client *client,

View file

@ -15,7 +15,6 @@
***************************************************************************/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/stringify.h>
#include <linux/delay.h>
#include <linux/kthread.h>

View file

@ -158,8 +158,8 @@ static u32 goldfish_cmd_status(struct goldfish_pipe *pipe, u32 cmd)
struct goldfish_pipe_dev *dev = pipe->dev;
spin_lock_irqsave(&dev->lock, flags);
gf_write64((u64)(unsigned long)pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
writel(cmd, dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);
spin_unlock_irqrestore(&dev->lock, flags);
@ -172,8 +172,8 @@ static void goldfish_cmd(struct goldfish_pipe *pipe, u32 cmd)
struct goldfish_pipe_dev *dev = pipe->dev;
spin_lock_irqsave(&dev->lock, flags);
gf_write64((u64)(unsigned long)pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
writel(cmd, dev->base + PIPE_REG_COMMAND);
spin_unlock_irqrestore(&dev->lock, flags);
}
@ -327,12 +327,12 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
spin_lock_irqsave(&dev->lock, irq_flags);
if (access_with_param(dev, CMD_WRITE_BUFFER + cmd_offset,
address, avail, pipe, &status)) {
gf_write64((u64)(unsigned long)pipe,
dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
writel(avail, dev->base + PIPE_REG_SIZE);
gf_write64(address, dev->base + PIPE_REG_ADDRESS,
dev->base + PIPE_REG_ADDRESS_HIGH);
gf_write_ptr((void *)address,
dev->base + PIPE_REG_ADDRESS,
dev->base + PIPE_REG_ADDRESS_HIGH);
writel(CMD_WRITE_BUFFER + cmd_offset,
dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);

View file

@ -108,8 +108,8 @@ source "drivers/staging/clocking-wizard/Kconfig"
source "drivers/staging/fbtft/Kconfig"
source "drivers/staging/i2o/Kconfig"
source "drivers/staging/fsl-mc/Kconfig"
source "drivers/staging/wilc1000/Kconfig"
endif # STAGING

View file

@ -23,7 +23,7 @@ obj-$(CONFIG_VT6656) += vt6656/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
obj-$(CONFIG_FB_SM7XX) += sm750fb/
obj-$(CONFIG_FB_SM750) += sm750fb/
obj-$(CONFIG_FB_XGI) += xgifb/
obj-$(CONFIG_USB_EMXX) += emxx_udc/
obj-$(CONFIG_FT1000) += ft1000/
@ -46,5 +46,5 @@ obj-$(CONFIG_CRYPTO_SKEIN) += skein/
obj-$(CONFIG_UNISYSSPAR) += unisys/
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
obj-$(CONFIG_FB_TFT) += fbtft/
obj-$(CONFIG_I2O) += i2o/
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
obj-$(CONFIG_WILC1000) += wilc1000/

View file

@ -22,11 +22,20 @@ config ANDROID_TIMED_GPIO
tristate "Android timed gpio driver"
depends on GPIOLIB && ANDROID_TIMED_OUTPUT
default n
---help---
Unlike generic gpio is to allow programs to access and manipulate gpio
registers from user space, timed output/gpio is a system to allow changing
a gpio pin and restore it automatically after a specified timeout.
config ANDROID_LOW_MEMORY_KILLER
bool "Android Low Memory Killer"
---help---
Registers processes to be killed when memory is low
Registers processes to be killed when low memory conditions, this is useful
as there is no particular swap space on android.
The registered process will kills according to the priorities in android init
scripts (/init.rc), and it defines priority values with minimum free memory size
for each priority.
config SYNC
bool "Synchronization framework"

View file

@ -1579,6 +1579,7 @@ struct ion_device *ion_device_create(long (*custom_ioctl)
ret = misc_register(&idev->dev);
if (ret) {
pr_err("ion: failed to register misc device.\n");
kfree(idev);
return ERR_PTR(ret);
}

View file

@ -173,7 +173,7 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
chunk_heap->heap.ops = &chunk_heap_ops;
chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
pr_info("%s: base %lu size %zu align %ld\n", __func__, chunk_heap->base,
pr_debug("%s: base %lu size %zu align %ld\n", __func__, chunk_heap->base,
heap_data->size, heap_data->align);
return &chunk_heap->heap;

View file

@ -33,7 +33,7 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
/**
* struct ion_buffer - metadata for a particular buffer
* @ref: refernce count
* @ref: reference count
* @node: node in the ion_device buffers tree
* @dev: back pointer to the ion_device
* @heap: back pointer to the heap the buffer came from
@ -46,7 +46,7 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
* an ion_phys_addr_t (and someday a phys_addr_t)
* @lock: protects the buffers cnt fields
* @kmap_cnt: number of times the buffer is mapped to the kernel
* @vaddr: the kenrel mapping if kmap_cnt is not zero
* @vaddr: the kernel mapping if kmap_cnt is not zero
* @dmap_cnt: number of times the buffer is mapped for dma
* @sg_table: the sg table for the buffer if dmap_cnt is not zero
* @pages: flat array of pages in the buffer -- used by fault
@ -266,7 +266,7 @@ void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer);
/**
* ion_heap_freelist_drain - drain the deferred free list
* @heap: the heap
* @size: ammount of memory to drain in bytes
* @size: amount of memory to drain in bytes
*
* Drains the indicated amount of memory from the deferred freelist immediately.
* Returns the total amount freed. The total freed may be higher depending

View file

@ -261,7 +261,20 @@ static int __init ion_test_probe(struct platform_device *pdev)
return 0;
}
static int ion_test_remove(struct platform_device *pdev)
{
struct ion_test_device *testdev;
testdev = platform_get_drvdata(pdev);
if (!testdev)
return -ENODATA;
return misc_deregister(&testdev->misc);
}
static struct platform_device *ion_test_pdev;
static struct platform_driver ion_test_platform_driver = {
.remove = ion_test_remove,
.driver = {
.name = "ion-test",
},
@ -269,13 +282,18 @@ static struct platform_driver ion_test_platform_driver = {
static int __init ion_test_init(void)
{
platform_device_register_simple("ion-test", -1, NULL, 0);
ion_test_pdev = platform_device_register_simple("ion-test",
-1, NULL, 0);
if (!ion_test_pdev)
return -ENODEV;
return platform_driver_probe(&ion_test_platform_driver, ion_test_probe);
}
static void __exit ion_test_exit(void)
{
platform_driver_unregister(&ion_test_platform_driver);
platform_device_unregister(ion_test_pdev);
}
module_init(ion_test_init);

View file

@ -15,6 +15,7 @@
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../ion.h"

View file

@ -156,20 +156,27 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
p->pid, p->comm, oom_score_adj, tasksize);
}
if (selected) {
lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n",
selected->pid, selected->comm,
selected_oom_score_adj, selected_tasksize);
lowmem_deathpending_timeout = jiffies + HZ;
task_lock(selected);
if (!selected->mm) {
/* Already exited, cannot do mark_tsk_oom_victim() */
task_unlock(selected);
goto out;
}
/*
* FIXME: lowmemorykiller shouldn't abuse global OOM killer
* infrastructure. There is no real reason why the selected
* task should have access to the memory reserves.
*/
mark_oom_victim(selected);
task_unlock(selected);
lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n",
selected->pid, selected->comm,
selected_oom_score_adj, selected_tasksize);
lowmem_deathpending_timeout = jiffies + HZ;
send_sig(SIGKILL, selected, 0);
rem += selected_tasksize;
}
out:
lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
sc->nr_to_scan, sc->gfp_mask, rem);
rcu_read_unlock();

View file

@ -179,7 +179,7 @@ struct ion_custom_data {
* DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory
*
* Deprecated in favor of using the dma_buf api's correctly (syncing
* will happend automatically when the buffer is mapped to a device).
* will happen automatically when the buffer is mapped to a device).
* If necessary should be used after touching a cached buffer from the cpu,
* this will make the buffer in memory coherent.
*/

View file

@ -1,7 +1,6 @@
config STAGING_BOARD
bool "Staging Board Support"
depends on OF_ADDRESS
depends on BROKEN
help
Select to enable per-board staging support code.

View file

@ -1,2 +1,3 @@
obj-y := board.o
obj-$(CONFIG_ARCH_EMEV2) += kzm9d.o
obj-$(CONFIG_ARCH_EMEV2) += kzm9d.o
obj-$(CONFIG_ARCH_R8A7740) += armadillo800eva.o

View file

@ -0,0 +1,105 @@
/*
* Staging board support for Armadillo 800 eva.
* Enable not-yet-DT-capable devices here.
*
* Based on board-armadillo800eva.c
*
* Copyright (C) 2012 Renesas Solutions Corp.
* Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/dma-mapping.h>
#include <linux/fb.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <video/sh_mobile_lcdc.h>
#include "board.h"
static struct fb_videomode lcdc0_mode = {
.name = "AMPIER/AM-800480",
.xres = 800,
.yres = 480,
.left_margin = 88,
.right_margin = 40,
.hsync_len = 128,
.upper_margin = 20,
.lower_margin = 5,
.vsync_len = 5,
.sync = 0,
};
static struct sh_mobile_lcdc_info lcdc0_info = {
.clock_source = LCDC_CLK_BUS,
.ch[0] = {
.chan = LCDC_CHAN_MAINLCD,
.fourcc = V4L2_PIX_FMT_RGB565,
.interface_type = RGB24,
.clock_divider = 5,
.flags = 0,
.lcd_modes = &lcdc0_mode,
.num_modes = 1,
.panel_cfg = {
.width = 111,
.height = 68,
},
},
};
static struct resource lcdc0_resources[] = {
[0] = {
.name = "LCD0",
.start = 0xfe940000,
.end = 0xfe943fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 177 + 32,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device lcdc0_device = {
.name = "sh_mobile_lcdc_fb",
.num_resources = ARRAY_SIZE(lcdc0_resources),
.resource = lcdc0_resources,
.id = 0,
.dev = {
.platform_data = &lcdc0_info,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
};
static const struct board_staging_clk lcdc0_clocks[] __initconst = {
{ "lcdc0", NULL, "sh_mobile_lcdc_fb.0" },
};
static const struct board_staging_dev armadillo800eva_devices[] __initconst = {
{
.pdev = &lcdc0_device,
.clocks = lcdc0_clocks,
.nclocks = ARRAY_SIZE(lcdc0_clocks),
.domain = "a4lc",
},
};
static void __init armadillo800eva_init(void)
{
board_staging_gic_setup_xlate("arm,cortex-a9-gic", 32);
board_staging_register_devices(armadillo800eva_devices,
ARRAY_SIZE(armadillo800eva_devices));
}
board_staging("renesas,armadillo800eva", armadillo800eva_init);

View file

@ -1,10 +1,30 @@
/*
* Copyright (C) 2014 Magnus Damm
* Copyright (C) 2015 Glider bvba
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#define pr_fmt(fmt) "board_staging: " fmt
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include "board.h"
static struct device_node *irqc_node __initdata;
static unsigned int irqc_base __initdata;
static bool find_by_address(u64 base_address)
{
struct device_node *dn = of_find_all_nodes(NULL);
@ -38,3 +58,119 @@ bool __init board_staging_dt_node_available(const struct resource *resource,
return false; /* Nothing found */
}
int __init board_staging_gic_setup_xlate(const char *gic_match,
unsigned int base)
{
WARN_ON(irqc_node);
irqc_node = of_find_compatible_node(NULL, NULL, gic_match);
WARN_ON(!irqc_node);
if (!irqc_node)
return -ENOENT;
irqc_base = base;
return 0;
}
static void __init gic_fixup_resource(struct resource *res)
{
struct of_phandle_args irq_data;
unsigned int hwirq = res->start;
unsigned int virq;
if (resource_type(res) != IORESOURCE_IRQ || !irqc_node)
return;
irq_data.np = irqc_node;
irq_data.args_count = 3;
irq_data.args[0] = 0;
irq_data.args[1] = hwirq - irqc_base;
switch (res->flags &
(IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE |
IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL)) {
case IORESOURCE_IRQ_LOWEDGE:
irq_data.args[2] = IRQ_TYPE_EDGE_FALLING;
break;
case IORESOURCE_IRQ_HIGHEDGE:
irq_data.args[2] = IRQ_TYPE_EDGE_RISING;
break;
case IORESOURCE_IRQ_LOWLEVEL:
irq_data.args[2] = IRQ_TYPE_LEVEL_LOW;
break;
case IORESOURCE_IRQ_HIGHLEVEL:
default:
irq_data.args[2] = IRQ_TYPE_LEVEL_HIGH;
break;
}
virq = irq_create_of_mapping(&irq_data);
if (WARN_ON(!virq))
return;
pr_debug("hwirq %u -> virq %u\n", hwirq, virq);
res->start = virq;
}
void __init board_staging_gic_fixup_resources(struct resource *res,
unsigned int nres)
{
unsigned int i;
for (i = 0; i < nres; i++)
gic_fixup_resource(&res[i]);
}
int __init board_staging_register_clock(const struct board_staging_clk *bsc)
{
int error;
pr_debug("Aliasing clock %s for con_id %s dev_id %s\n", bsc->clk,
bsc->con_id, bsc->dev_id);
error = clk_add_alias(bsc->con_id, bsc->dev_id, bsc->clk, NULL);
if (error)
pr_err("Failed to alias clock %s (%d)\n", bsc->clk, error);
return error;
}
int __init board_staging_register_device(const struct board_staging_dev *dev)
{
struct platform_device *pdev = dev->pdev;
unsigned int i;
int error;
pr_debug("Trying to register device %s\n", pdev->name);
if (board_staging_dt_node_available(pdev->resource,
pdev->num_resources)) {
pr_warn("Skipping %s, already in DT\n", pdev->name);
return -EEXIST;
}
board_staging_gic_fixup_resources(pdev->resource, pdev->num_resources);
for (i = 0; i < dev->nclocks; i++)
board_staging_register_clock(&dev->clocks[i]);
error = platform_device_register(pdev);
if (error) {
pr_err("Failed to register device %s (%d)\n", pdev->name,
error);
return error;
}
if (dev->domain)
__pm_genpd_name_add_device(dev->domain, &pdev->dev, NULL);
return error;
}
void __init board_staging_register_devices(const struct board_staging_dev *devs,
unsigned int ndevs)
{
unsigned int i;
for (i = 0; i < ndevs; i++)
board_staging_register_device(&devs[i]);
}

View file

@ -1,10 +1,35 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include <linux/init.h>
#include <linux/of.h>
struct board_staging_clk {
const char *clk;
const char *con_id;
const char *dev_id;
};
struct board_staging_dev {
/* Platform Device */
struct platform_device *pdev;
/* Clocks (optional) */
const struct board_staging_clk *clocks;
unsigned int nclocks;
/* Generic PM Domain (optional) */
const char *domain;
};
struct resource;
bool board_staging_dt_node_available(const struct resource *resource,
unsigned int num_resources);
int board_staging_gic_setup_xlate(const char *gic_match, unsigned int base);
void board_staging_gic_fixup_resources(struct resource *res, unsigned int nres);
int board_staging_register_clock(const struct board_staging_clk *bsc);
int board_staging_register_device(const struct board_staging_dev *dev);
void board_staging_register_devices(const struct board_staging_dev *devs,
unsigned int ndevs);
#define board_staging(str, fn) \
static int __init runtime_board_check(void) \
@ -15,6 +40,6 @@ static int __init runtime_board_check(void) \
return 0; \
} \
\
late_initcall(runtime_board_check)
device_initcall(runtime_board_check)
#endif /* __BOARD_H__ */

View file

@ -4,16 +4,22 @@
#include <linux/platform_device.h>
#include "board.h"
static const struct resource usbs1_res[] __initconst = {
static struct resource usbs1_res[] __initdata = {
DEFINE_RES_MEM(0xe2800000, 0x2000),
DEFINE_RES_IRQ(159),
};
static void __init kzm9d_init(void)
{
if (!board_staging_dt_node_available(usbs1_res, ARRAY_SIZE(usbs1_res)))
board_staging_gic_setup_xlate("arm,cortex-a9-gic", 32);
if (!board_staging_dt_node_available(usbs1_res,
ARRAY_SIZE(usbs1_res))) {
board_staging_gic_fixup_resources(usbs1_res,
ARRAY_SIZE(usbs1_res));
platform_device_register_simple("emxx_udc", -1, usbs1_res,
ARRAY_SIZE(usbs1_res));
}
}
board_staging("renesas,kzm9d", kzm9d_init);

View file

@ -1247,16 +1247,22 @@ config COMEDI_8254
tristate
config COMEDI_8255
tristate "Generic 8255 support"
tristate
config COMEDI_8255_SA
tristate "Standalone 8255 support"
select COMEDI_8255
---help---
Enable generic 8255 support.
Enable support for 8255 digital I/O as a standalone driver.
You should enable compilation this driver if you plan to use a board
that has an 8255 chip. For multifunction boards, the main driver will
configure the 8255 subdevice automatically.
that has an 8255 chip at a known I/O base address and there are no
other Comedi drivers for the board.
Note that most PCI based 8255 boards use the 8255_pci driver as a
wrapper around this driver.
Note that Comedi drivers for most multi-function boards incorporating
an 8255 chip use the 'comedi_8255' module. Most PCI-based 8255
boards use the 8255_pci driver as a wrapper around the 'comedi_8255'
module.
To compile this driver as a module, choose M here: the module will be
called 8255.

View file

@ -217,7 +217,7 @@
#define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */
#define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */
#define SDF_PACKED 0x20000000 /* subdevice can do packed DIO */
/* re recyle these flags for PWM */
/* re recycle these flags for PWM */
#define SDF_PWM_COUNTER SDF_MODE0 /* PWM can automatically switch off */
#define SDF_PWM_HBRIDGE SDF_MODE1 /* PWM is signed (H-bridge) */

View file

@ -43,6 +43,23 @@
#include "comedi_internal.h"
/**
* comedi_subdevice "runflags"
* @COMEDI_SRF_RT: DEPRECATED: command is running real-time
* @COMEDI_SRF_ERROR: indicates an COMEDI_CB_ERROR event has occurred
* since the last command was started
* @COMEDI_SRF_RUNNING: command is running
* @COMEDI_SRF_FREE_SPRIV: free s->private on detach
*
* @COMEDI_SRF_BUSY_MASK: runflags that indicate the subdevice is "busy"
*/
#define COMEDI_SRF_RT BIT(1)
#define COMEDI_SRF_ERROR BIT(2)
#define COMEDI_SRF_RUNNING BIT(27)
#define COMEDI_SRF_FREE_SPRIV BIT(31)
#define COMEDI_SRF_BUSY_MASK (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING)
/**
* struct comedi_file - per-file private data for comedi device
* @dev: comedi_device struct
@ -679,8 +696,28 @@ static bool comedi_is_subdevice_idle(struct comedi_subdevice *s)
return !(runflags & COMEDI_SRF_BUSY_MASK);
}
bool comedi_can_auto_free_spriv(struct comedi_subdevice *s)
{
unsigned runflags = __comedi_get_subdevice_runflags(s);
return runflags & COMEDI_SRF_FREE_SPRIV;
}
/**
* comedi_alloc_spriv() - Allocate memory for the subdevice private data.
* comedi_set_spriv_auto_free - mark subdevice private data as freeable
* @s: comedi_subdevice struct
*
* Mark the subdevice as having a pointer to private data that can be
* automatically freed by the comedi core during the detach.
*/
void comedi_set_spriv_auto_free(struct comedi_subdevice *s)
{
__comedi_set_subdevice_runflags(s, COMEDI_SRF_FREE_SPRIV);
}
EXPORT_SYMBOL_GPL(comedi_set_spriv_auto_free);
/**
* comedi_alloc_spriv - Allocate memory for the subdevice private data.
* @s: comedi_subdevice struct
* @size: size of the memory to allocate
*
@ -691,7 +728,7 @@ void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size)
{
s->private = kzalloc(size, GFP_KERNEL);
if (s->private)
s->runflags |= COMEDI_SRF_FREE_SPRIV;
comedi_set_spriv_auto_free(s);
return s->private;
}
EXPORT_SYMBOL_GPL(comedi_alloc_spriv);
@ -1048,11 +1085,6 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
if (put_user(x, it.rangelist + i))
return -EFAULT;
}
#if 0
if (copy_to_user(it.rangelist, s->range_type_list,
s->n_chan * sizeof(unsigned int)))
return -EFAULT;
#endif
}
return 0;
@ -1725,7 +1757,7 @@ cleanup:
/*
* COMEDI_CMDTEST ioctl
* asynchronous aquisition command testing
* asynchronous acquisition command testing
*
* arg:
* pointer to comedi_cmd structure

View file

@ -33,6 +33,7 @@ struct comedi_buf_map *comedi_buf_map_from_subdev_get(
struct comedi_subdevice *s);
unsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s);
void comedi_device_cancel_all(struct comedi_device *dev);
bool comedi_can_auto_free_spriv(struct comedi_subdevice *s);
extern unsigned int comedi_default_buf_size_kb;
extern unsigned int comedi_default_buf_maxsize_kb;

View file

@ -227,12 +227,12 @@ struct comedi_async {
* @COMEDI_CB_ERROR_MASK: events that indicate an error has occurred
* @COMEDI_CB_CANCEL_MASK: events that will cancel an async command
*/
#define COMEDI_CB_EOS (1 << 0)
#define COMEDI_CB_EOA (1 << 1)
#define COMEDI_CB_BLOCK (1 << 2)
#define COMEDI_CB_EOBUF (1 << 3)
#define COMEDI_CB_ERROR (1 << 4)
#define COMEDI_CB_OVERFLOW (1 << 5)
#define COMEDI_CB_EOS BIT(0)
#define COMEDI_CB_EOA BIT(1)
#define COMEDI_CB_BLOCK BIT(2)
#define COMEDI_CB_EOBUF BIT(3)
#define COMEDI_CB_ERROR BIT(4)
#define COMEDI_CB_OVERFLOW BIT(5)
#define COMEDI_CB_ERROR_MASK (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)
#define COMEDI_CB_CANCEL_MASK (COMEDI_CB_EOA | COMEDI_CB_ERROR_MASK)
@ -303,26 +303,10 @@ void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s);
struct comedi_device *comedi_dev_get_from_minor(unsigned minor);
int comedi_dev_put(struct comedi_device *dev);
/**
* comedi_subdevice "runflags"
* @COMEDI_SRF_RT: DEPRECATED: command is running real-time
* @COMEDI_SRF_ERROR: indicates an COMEDI_CB_ERROR event has occurred
* since the last command was started
* @COMEDI_SRF_RUNNING: command is running
* @COMEDI_SRF_FREE_SPRIV: free s->private on detach
*
* @COMEDI_SRF_BUSY_MASK: runflags that indicate the subdevice is "busy"
*/
#define COMEDI_SRF_RT BIT(1)
#define COMEDI_SRF_ERROR BIT(2)
#define COMEDI_SRF_RUNNING BIT(27)
#define COMEDI_SRF_FREE_SPRIV BIT(31)
#define COMEDI_SRF_BUSY_MASK (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING)
bool comedi_is_subdevice_running(struct comedi_subdevice *s);
void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size);
void comedi_set_spriv_auto_free(struct comedi_subdevice *s);
int comedi_check_chanlist(struct comedi_subdevice *s,
int n,

View file

@ -125,7 +125,7 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
s = &dev->subdevices[i];
if (s->runflags & COMEDI_SRF_FREE_SPRIV)
if (comedi_can_auto_free_spriv(s))
kfree(s->private);
comedi_free_subdevice_minor(s);
if (s->async) {

View file

@ -53,221 +53,6 @@
#include "8255.h"
struct subdev_8255_private {
unsigned long regbase;
int (*io)(struct comedi_device *, int, int, int, unsigned long);
};
static int subdev_8255_io(struct comedi_device *dev,
int dir, int port, int data, unsigned long regbase)
{
if (dir) {
outb(data, dev->iobase + regbase + port);
return 0;
}
return inb(dev->iobase + regbase + port);
}
static int subdev_8255_mmio(struct comedi_device *dev,
int dir, int port, int data, unsigned long regbase)
{
if (dir) {
writeb(data, dev->mmio + regbase + port);
return 0;
}
return readb(dev->mmio + regbase + port);
}
static int subdev_8255_insn(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct subdev_8255_private *spriv = s->private;
unsigned long regbase = spriv->regbase;
unsigned int mask;
unsigned int v;
mask = comedi_dio_update_state(s, data);
if (mask) {
if (mask & 0xff)
spriv->io(dev, 1, I8255_DATA_A_REG,
s->state & 0xff, regbase);
if (mask & 0xff00)
spriv->io(dev, 1, I8255_DATA_B_REG,
(s->state >> 8) & 0xff, regbase);
if (mask & 0xff0000)
spriv->io(dev, 1, I8255_DATA_C_REG,
(s->state >> 16) & 0xff, regbase);
}
v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, regbase);
v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, regbase) << 8);
v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, regbase) << 16);
data[1] = v;
return insn->n;
}
static void subdev_8255_do_config(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct subdev_8255_private *spriv = s->private;
unsigned long regbase = spriv->regbase;
int config;
config = I8255_CTRL_CW;
/* 1 in io_bits indicates output, 1 in config indicates input */
if (!(s->io_bits & 0x0000ff))
config |= I8255_CTRL_A_IO;
if (!(s->io_bits & 0x00ff00))
config |= I8255_CTRL_B_IO;
if (!(s->io_bits & 0x0f0000))
config |= I8255_CTRL_C_LO_IO;
if (!(s->io_bits & 0xf00000))
config |= I8255_CTRL_C_HI_IO;
spriv->io(dev, 1, I8255_CTRL_REG, config, regbase);
}
static int subdev_8255_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
unsigned int chan = CR_CHAN(insn->chanspec);
unsigned int mask;
int ret;
if (chan < 8)
mask = 0x0000ff;
else if (chan < 16)
mask = 0x00ff00;
else if (chan < 20)
mask = 0x0f0000;
else
mask = 0xf00000;
ret = comedi_dio_insn_config(dev, s, insn, data, mask);
if (ret)
return ret;
subdev_8255_do_config(dev, s);
return insn->n;
}
static int __subdev_8255_init(struct comedi_device *dev,
struct comedi_subdevice *s,
int (*io)(struct comedi_device *,
int, int, int, unsigned long),
unsigned long regbase,
bool is_mmio)
{
struct subdev_8255_private *spriv;
spriv = comedi_alloc_spriv(s, sizeof(*spriv));
if (!spriv)
return -ENOMEM;
if (io)
spriv->io = io;
else if (is_mmio)
spriv->io = subdev_8255_mmio;
else
spriv->io = subdev_8255_io;
spriv->regbase = regbase;
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 24;
s->range_table = &range_digital;
s->maxdata = 1;
s->insn_bits = subdev_8255_insn;
s->insn_config = subdev_8255_insn_config;
subdev_8255_do_config(dev, s);
return 0;
}
/**
* subdev_8255_init - initialize DIO subdevice for driving I/O mapped 8255
* @dev: comedi device owning subdevice
* @s: comedi subdevice to initialize
* @io: (optional) register I/O call-back function
* @regbase: offset of 8255 registers from dev->iobase, or call-back context
*
* Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
*
* If the optional I/O call-back function is provided, its prototype is of
* the following form:
*
* int my_8255_callback(struct comedi_device *dev,
* struct comedi_subdevice *s, int dir, int port,
* int data, unsigned long regbase);
*
* where 'dev', 's', and 'regbase' match the values passed to this function,
* 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
* is the direction (0 for read, 1 for write) and 'data' is the value to be
* written. It should return 0 if writing or the value read if reading.
*
* If the optional I/O call-back function is not provided, an internal
* call-back function is used which uses consecutive I/O port addresses
* starting at dev->iobase + regbase.
*
* Return: -ENOMEM if failed to allocate memory, zero on success.
*/
int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
int (*io)(struct comedi_device *,
int, int, int, unsigned long),
unsigned long regbase)
{
return __subdev_8255_init(dev, s, io, regbase, false);
}
EXPORT_SYMBOL_GPL(subdev_8255_init);
/**
* subdev_8255_mm_init - initialize DIO subdevice for driving mmio-mapped 8255
* @dev: comedi device owning subdevice
* @s: comedi subdevice to initialize
* @io: (optional) register I/O call-back function
* @regbase: offset of 8255 registers from dev->mmio, or call-back context
*
* Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
*
* If the optional I/O call-back function is provided, its prototype is of
* the following form:
*
* int my_8255_callback(struct comedi_device *dev,
* struct comedi_subdevice *s, int dir, int port,
* int data, unsigned long regbase);
*
* where 'dev', 's', and 'regbase' match the values passed to this function,
* 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
* is the direction (0 for read, 1 for write) and 'data' is the value to be
* written. It should return 0 if writing or the value read if reading.
*
* If the optional I/O call-back function is not provided, an internal
* call-back function is used which uses consecutive MMIO virtual addresses
* starting at dev->mmio + regbase.
*
* Return: -ENOMEM if failed to allocate memory, zero on success.
*/
int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s,
int (*io)(struct comedi_device *,
int, int, int, unsigned long),
unsigned long regbase)
{
return __subdev_8255_init(dev, s, io, regbase, true);
}
EXPORT_SYMBOL_GPL(subdev_8255_mm_init);
/*
* Start of the 8255 standalone device
*/
static int dev_8255_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
@ -306,8 +91,15 @@ static int dev_8255_attach(struct comedi_device *dev,
s->type = COMEDI_SUBD_UNUSED;
} else {
ret = subdev_8255_init(dev, s, NULL, iobase);
if (ret)
if (ret) {
/*
* Release the I/O port region here, as the
* "detach" handler cannot find it.
*/
release_region(iobase, I8255_SIZE);
s->type = COMEDI_SUBD_UNUSED;
return ret;
}
}
}
@ -317,14 +109,14 @@ static int dev_8255_attach(struct comedi_device *dev,
static void dev_8255_detach(struct comedi_device *dev)
{
struct comedi_subdevice *s;
struct subdev_8255_private *spriv;
int i;
for (i = 0; i < dev->n_subdevices; i++) {
s = &dev->subdevices[i];
if (s->type != COMEDI_SUBD_UNUSED) {
spriv = s->private;
release_region(spriv->regbase, I8255_SIZE);
unsigned long regbase = subdev_8255_regbase(s);
release_region(regbase, I8255_SIZE);
}
}
}
@ -338,5 +130,5 @@ static struct comedi_driver dev_8255_driver = {
module_comedi_driver(dev_8255_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
MODULE_DESCRIPTION("Comedi driver for standalone 8255 devices");
MODULE_LICENSE("GPL");

View file

@ -19,8 +19,6 @@
#ifndef _8255_H
#define _8255_H
#include "../comedidev.h"
#define I8255_SIZE 0x04
#define I8255_DATA_A_REG 0x00
@ -35,14 +33,19 @@
#define I8255_CTRL_A_MODE(x) ((x) << 5)
#define I8255_CTRL_CW (1 << 7)
int subdev_8255_init(struct comedi_device *, struct comedi_subdevice *,
int (*io)(struct comedi_device *,
int, int, int, unsigned long),
struct comedi_device;
struct comedi_subdevice;
int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
int (*io)(struct comedi_device *dev, int dir, int port,
int data, unsigned long regbase),
unsigned long regbase);
int subdev_8255_mm_init(struct comedi_device *, struct comedi_subdevice *,
int (*io)(struct comedi_device *,
int, int, int, unsigned long),
int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s,
int (*io)(struct comedi_device *dev, int dir, int port,
int data, unsigned long regbase),
unsigned long regbase);
unsigned long subdev_8255_regbase(struct comedi_subdevice *s);
#endif

View file

@ -139,7 +139,8 @@ obj-$(CONFIG_COMEDI_NI_TIOCMD) += ni_tiocmd.o
obj-$(CONFIG_COMEDI_NI_LABPC) += ni_labpc_common.o
obj-$(CONFIG_COMEDI_NI_LABPC_ISADMA) += ni_labpc_isadma.o
obj-$(CONFIG_COMEDI_8255) += 8255.o
obj-$(CONFIG_COMEDI_8255) += comedi_8255.o
obj-$(CONFIG_COMEDI_8255_SA) += 8255.o
obj-$(CONFIG_COMEDI_AMPLC_DIO200) += amplc_dio200_common.o
obj-$(CONFIG_COMEDI_AMPLC_PC236) += amplc_pc236_common.o
obj-$(CONFIG_COMEDI_DAS08) += das08.o

Some files were not shown because too many files have changed in this diff Show more