Staging drivers patches for 3.20-rc1

Here's the big staging driver tree update for 3.20-rc1.  Lots of little
 things in here, adding up to lots of overall cleanups.  The IIO driver
 updates are also in here as they cross the staging tree boundry a lot.
 I2O has moved into staging as well, as a plan to drop it from the tree
 eventually as that's a dead subsystem.
 
 All of this has been in linux-next with no reported issues for a while.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlTgtVQACgkQMUfUDdst+yk4mACgshYZ1fOQDoPR+BXd+QD1HXfh
 GosAoICXkSjDQjwVo13W6QHIVMsUezY+
 =4jHr
 -----END PGP SIGNATURE-----

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

Pull staging drivers patches from Greg KH:
 "Here's the big staging driver tree update for 3.20-rc1.

  Lots of little things in here, adding up to lots of overall cleanups.
  The IIO driver updates are also in here as they cross the staging tree
  boundry a lot.  I2O has moved into staging as well, as a plan to drop
  it from the tree eventually as that's a dead subsystem.

  All of this has been in linux-next with no reported issues for a
  while"

* tag 'staging-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (740 commits)
  staging: lustre: lustre: libcfs: define symbols as static
  staging: rtl8712: Do coding style cleanup
  staging: lustre: make obd_updatemax_lock static
  staging: rtl8188eu: core: switch with redundant cases
  staging: rtl8188eu: odm: conditional setting with no effect
  staging: rtl8188eu: odm: condition with no effect
  staging: ft1000: fix braces warning
  staging: sm7xxfb: fix remaining CamelCase
  staging: sm7xxfb: fix CamelCase
  staging: rtl8723au: multiple condition with no effect - if identical to else
  staging: sm7xxfb: make smtc_scr_info static
  staging/lustre/mdc: Initialize req in mdc_enqueue for !it case
  staging/lustre/clio: Do not allow group locks with gid 0
  staging/lustre/llite: don't add to page cache upon failure
  staging/lustre/llite: Add exception entry check after radix_tree
  staging/lustre/libcfs: protect kkuc_groups from write access
  staging/lustre/fld: refer to MDT0 for fld lookup in some cases
  staging/lustre/llite: Solve a race to access lli_has_smd in read case
  staging/lustre/ptlrpc: hold rq_lock when modify rq_flags
  staging/lustre/lnet: portal spreading rotor should be unsigned
  ...
This commit is contained in:
Linus Torvalds 2015-02-15 11:30:39 -08:00
commit 46f7b63556
525 changed files with 31882 additions and 15079 deletions

View File

@ -92,6 +92,18 @@ Description:
is required is a consistent labeling. Units after application
of scale and offset are millivolts.
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw
KernelVersion: 3.17
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no bias removal etc.) current measurement from
channel Y. In special cases where the channel does not
correspond to externally available input one of the named
versions may be used. The number must always be specified and
unique to allow association with event codes. Units after
application of scale and offset are milliamps.
What: /sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
KernelVersion: 3.2
Contact: linux-iio@vger.kernel.org
@ -234,6 +246,8 @@ 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_currentY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_current_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
@ -262,9 +276,14 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltage_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_accel_scale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_scale
What: /sys/bus/iio/devices/iio:deviceX/in_energy_scale
What: /sys/bus/iio/devices/iio:deviceX/in_distance_scale
What: /sys/bus/iio/devices/iio:deviceX/in_magn_scale
What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
@ -276,6 +295,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_rot_from_north_true_tilt_comp_scale
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@ -323,6 +343,44 @@ Description:
production inaccuracies). If shared across all channels,
<type>_calibscale is used.
What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Gender of the user (e.g.: male, female) used by some pedometers
to compute the stride length, distance, speed and activity
type.
What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Lists all available gender values (e.g.: male, female).
What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibheight
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibheight
What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibheight
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibheight
KernelVersion: 3.19
Contact: linux-iio@vger.kernel.org
Description:
Height of the user (in meters) used by some pedometers
to compute the stride length, distance, speed and activity
type.
What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibweight
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Weight of the user (in kg). It is needed by some pedometers
to compute the calories burnt by the user.
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
What: /sys/.../iio:deviceX/in_voltageX_scale_available
What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available
@ -783,6 +841,14 @@ What: /sys/.../events/in_tempY_roc_falling_period
What: /sys/.../events/in_accel_x&y&z_mag_falling_period
What: /sys/.../events/in_intensity0_thresh_period
What: /sys/.../events/in_proximity0_thresh_period
What: /sys/.../events/in_activity_still_thresh_rising_period
What: /sys/.../events/in_activity_still_thresh_falling_period
What: /sys/.../events/in_activity_walking_thresh_rising_period
What: /sys/.../events/in_activity_walking_thresh_falling_period
What: /sys/.../events/in_activity_jogging_thresh_rising_period
What: /sys/.../events/in_activity_jogging_thresh_falling_period
What: /sys/.../events/in_activity_running_thresh_rising_period
What: /sys/.../events/in_activity_running_thresh_falling_period
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
@ -790,6 +856,40 @@ Description:
met before an event is generated. If direction is not
specified then this period applies to both directions.
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
What: /sys/.../events/in_activity_walking_thresh_falling_en
What: /sys/.../events/in_activity_jogging_thresh_rising_en
What: /sys/.../events/in_activity_jogging_thresh_falling_en
What: /sys/.../events/in_activity_running_thresh_rising_en
What: /sys/.../events/in_activity_running_thresh_falling_en
KernelVersion: 3.19
Contact: linux-iio@vger.kernel.org
Description:
Enables or disables activitity events. Depending on direction
an event is generated when sensor ENTERS or LEAVES a given state.
What: /sys/.../events/in_activity_still_thresh_rising_value
What: /sys/.../events/in_activity_still_thresh_falling_value
What: /sys/.../events/in_activity_walking_thresh_rising_value
What: /sys/.../events/in_activity_walking_thresh_falling_value
What: /sys/.../events/in_activity_jogging_thresh_rising_value
What: /sys/.../events/in_activity_jogging_thresh_falling_value
What: /sys/.../events/in_activity_running_thresh_rising_value
What: /sys/.../events/in_activity_running_thresh_falling_value
KernelVersion: 3.19
Contact: linux-iio@vger.kernel.org
Description:
Confidence value (in units as percentage) to be used
for deciding when an event should be generated. E.g for
running: If the confidence value reported by the sensor
is greater than in_activity_running_thresh_rising_value
then the sensor ENTERS running state. Conversely, if the
confidence value reported by the sensor is lower than
in_activity_running_thresh_falling_value then the sensor
is LEAVING running state.
What: /sys/.../iio:deviceX/events/in_accel_mag_en
What: /sys/.../iio:deviceX/events/in_accel_mag_rising_en
What: /sys/.../iio:deviceX/events/in_accel_mag_falling_en
@ -822,6 +922,25 @@ Description:
number or direction is not specified, applies to all channels of
this type.
What: /sys/.../events/in_steps_change_en
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Event generated when channel passes a threshold on the absolute
change in value. E.g. for steps: a step change event is
generated each time the user takes N steps, where N is set using
in_steps_change_value.
What: /sys/.../events/in_steps_change_value
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Specifies the value of change threshold that the
device is comparing against for the events enabled by
<type>[Y][_name]_roc[_rising|falling|]_en. E.g. for steps:
if set to 3, a step change event will be generated every 3
steps.
What: /sys/bus/iio/devices/iio:deviceX/trigger/current_trigger
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
@ -956,6 +1075,16 @@ Description:
and the relevant _type attributes to establish the data storage
format.
What: /sys/.../iio:deviceX/in_activity_still_input
What: /sys/.../iio:deviceX/in_activity_walking_input
What: /sys/.../iio:deviceX/in_activity_jogging_input
What: /sys/.../iio:deviceX/in_activity_running_input
KernelVersion: 3.19
Contact: linux-iio@vger.kernel.org
Description:
This attribute is used to read the confidence for an activity
expressed in units as percentage.
What: /sys/.../iio:deviceX/in_anglvel_z_quadrature_correction_raw
KernelVersion: 2.6.38
Contact: linux-iio@vger.kernel.org
@ -973,6 +1102,24 @@ Description:
For a list of available output power modes read
in_accel_power_mode_available.
What: /sys/.../iio:deviceX/in_energy_input
What: /sys/.../iio:deviceX/in_energy_raw
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
This attribute is used to read the energy value reported by the
device (e.g.: human activity sensors report energy burnt by the
user). Units after application of scale are Joules.
What: /sys/.../iio:deviceX/in_distance_input
What: /sys/.../iio:deviceX/in_distance_raw
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
This attribute is used to read the distance covered by the user
since the last reboot while activated. Units after application
of scale are meters.
What: /sys/bus/iio/devices/iio:deviceX/store_eeprom
KernelVersion: 3.4.0
Contact: linux-iio@vger.kernel.org
@ -992,7 +1139,9 @@ Description:
reflectivity of infrared or ultrasound emitted.
Often these sensors are unit less and as such conversion
to SI units is not possible. Where it is, the units should
be meters.
be meters. If such a conversion is not possible, the reported
values should behave in the same way as a distance, i.e. lower
values indicate something is closer to the sensor.
What: /sys/.../iio:deviceX/in_illuminanceY_input
What: /sys/.../iio:deviceX/in_illuminanceY_raw
@ -1024,6 +1173,12 @@ Description:
This attribute is used to get/set the integration time in
seconds.
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Number of seconds in which to compute speed.
What: /sys/bus/iio/devices/iio:deviceX/in_rot_quaternion_raw
KernelVersion: 3.15
Contact: linux-iio@vger.kernel.org
@ -1051,3 +1206,46 @@ Description:
after application of scale and offset. If no offset or scale is
present, output should be considered as processed with the
unit in milliamps.
What: /sys/.../iio:deviceX/in_energy_en
What: /sys/.../iio:deviceX/in_distance_en
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
What: /sys/.../iio:deviceX/in_steps_en
KernelVersion: 3.19
Contact: linux-iio@vger.kernel.org
Description:
Activates a device feature that runs in firmware/hardware.
E.g. for steps: the pedometer saves power while not used;
when activated, it will count the steps taken by the user in
firmware and export them through in_steps_input.
What: /sys/.../iio:deviceX/in_steps_input
KernelVersion: 3.19
Contact: linux-iio@vger.kernel.org
Description:
This attribute is used to read the number of steps taken by the user
since the last reboot while activated.
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_input
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_raw
KernelVersion: 3.19
Contact: linux-iio@vger.kernel.org
Description:
This attribute is used to read the current speed value of the
user (which is the norm or magnitude of the velocity vector).
Units after application of scale are m/s.
What: /sys/.../iio:deviceX/in_steps_debounce_count
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Specifies the number of steps that must occur within
in_steps_filter_debounce_time for the pedometer to decide the
consumer is making steps.
What: /sys/.../iio:deviceX/in_steps_debounce_time
KernelVersion: 3.20
Contact: linux-iio@vger.kernel.org
Description:
Specifies number of seconds in which we compute the steps
that occur in order to decide if the consumer is making steps.

View File

@ -35,6 +35,7 @@ atmel,24c512 i2c serial eeprom (24cxx)
atmel,24c1024 i2c serial eeprom (24cxx)
atmel,at97sc3204t i2c trusted platform module (TPM)
capella,cm32181 CM32181: Ambient Light Sensor
capella,cm3232 CM3232: Ambient Light Sensor
catalyst,24c32 i2c serial eeprom
cirrus,cs42l51 Cirrus Logic CS42L51 audio codec
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock

View File

@ -0,0 +1,22 @@
* Cosmic Circuits - Analog to Digital Converter (CC-10001-ADC)
Required properties:
- compatible: Should be "cosmic,10001-adc"
- reg: Should contain adc registers location and length.
- clock-names: Should contain "adc".
- clocks: Should contain a clock specifier for each entry in clock-names
- vref-supply: The regulator supply ADC reference voltage.
Optional properties:
- adc-reserved-channels: Bitmask of reserved channels,
i.e. channels that cannot be used by the OS.
Example:
adc: adc@18101600 {
compatible = "cosmic,10001-adc";
reg = <0x18101600 0x24>;
adc-reserved-channels = <0x2>;
clocks = <&adc_clk>;
clock-names = "adc";
vref-supply = <&reg_1v8>;
};

View File

@ -0,0 +1,129 @@
Qualcomm's SPMI PMIC voltage ADC
SPMI PMIC voltage ADC (VADC) provides interface to clients to read
voltage. The VADC is a 15-bit sigma-delta ADC.
VADC node:
- compatible:
Usage: required
Value type: <string>
Definition: Should contain "qcom,spmi-vadc".
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: VADC base address and length in the SPMI PMIC register map.
- #address-cells:
Usage: required
Value type: <u32>
Definition: Must be one. Child node 'reg' property should define ADC
channel number.
- #size-cells:
Usage: required
Value type: <u32>
Definition: Must be zero.
- #io-channel-cells:
Usage: required
Value type: <u32>
Definition: Must be one. For details about IIO bindings see:
Documentation/devicetree/bindings/iio/iio-bindings.txt
- interrupts:
Usage: optional
Value type: <prop-encoded-array>
Definition: End of conversion interrupt.
Channel node properties:
- reg:
Usage: required
Value type: <u32>
Definition: ADC channel number.
See include/dt-bindings/iio/qcom,spmi-vadc.h
- qcom,decimation:
Usage: optional
Value type: <u32>
Definition: This parameter is used to decrease ADC sampling rate.
Quicker measurements can be made by reducing decimation ratio.
Valid values are 512, 1024, 2048, 4096.
If property is not found, default value of 512 will be used.
- qcom,pre-scaling:
Usage: optional
Value type: <u32 array>
Definition: Used for scaling the channel input signal before the signal is
fed to VADC. The configuration for this node is to know the
pre-determined ratio and use it for post scaling. Select one from
the following options.
<1 1>, <1 3>, <1 4>, <1 6>, <1 20>, <1 8>, <10 81>, <1 10>
If property is not found default value depending on chip will be used.
- qcom,ratiometric:
Usage: optional
Value type: <empty>
Definition: Channel calibration type. If this property is specified
VADC will use the VDD reference (1.8V) and GND for channel
calibration. If property is not found, channel will be
calibrated with 0.625V and 1.25V reference channels, also
known as absolute calibration.
- qcom,hw-settle-time:
Usage: optional
Value type: <u32>
Definition: Time between AMUX getting configured and the ADC starting
conversion. Delay = 100us * (value) for value < 11, and
2ms * (value - 10) otherwise.
Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800,
900 us and 1, 2, 4, 6, 8, 10 ms
If property is not found, channel will use 0us.
- qcom,avg-samples:
Usage: optional
Value type: <u32>
Definition: Number of samples to be used for measurement.
Averaging provides the option to obtain a single measurement
from the ADC that is an average of multiple samples. The value
selected is 2^(value).
Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
If property is not found, 1 sample will be used.
NOTE:
Following channels, also known as reference point channels, are used for
result calibration and their channel configuration nodes should be defined:
VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
VADC_GND_REF and VADC_VDD_VADC.
Example:
/* VADC node */
pmic_vadc: vadc@3100 {
compatible = "qcom,spmi-vadc";
reg = <0x3100 0x100>;
interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
io-channel-ranges;
/* Channel node */
usb_id_nopull {
reg = <VADC_LR_MUX10_USB_ID>;
qcom,decimation = <512>;
qcom,ratiometric;
qcom,hw-settle-time = <200>;
qcom,avg-samples = <1>;
qcom,pre-scaling = <1 3>;
};
};
/* IIO client node */
usb {
io-channels = <&pmic_vadc VADC_LR_MUX10_USB_ID>;
io-channel-names = "vadc";
};

View File

@ -0,0 +1,25 @@
Samsung Sensorhub driver
Sensorhub is a MCU which manages several sensors and also plays the role
of a virtual sensor device.
Required properties:
- compatible: "samsung,sensorhub-rinato" or "samsung,sensorhub-thermostat"
- spi-max-frequency: max SPI clock frequency
- interrupt-parent: interrupt parent
- interrupts: communication interrupt
- ap-mcu-gpios: [out] ap to sensorhub line - used during communication
- mcu-ap-gpios: [in] sensorhub to ap - used during communication
- mcu-reset-gpios: [out] sensorhub reset
Example:
shub_spi: shub {
compatible = "samsung,sensorhub-rinato";
spi-max-frequency = <5000000>;
interrupt-parent = <&gpx0>;
interrupts = <2 0>;
ap-mcu-gpios = <&gpx0 0 0>;
mcu-ap-gpios = <&gpx0 4 0>;
mcu-reset-gpios = <&gpx0 5 0>;
};

View File

@ -12,9 +12,9 @@ Optional properties:
property is not present, then the touchscreen is
disabled. 5 wires is valid for i.MX28 SoC only.
- fsl,ave-ctrl: number of samples per direction to calculate an average value.
Allowed value is 1 ... 31, default is 4
Allowed value is 1 ... 32, default is 4
- fsl,ave-delay: delay between consecutive samples. Allowed value is
1 ... 2047. It is used if 'fsl,ave-ctrl' > 1, counts at
2 ... 2048. It is used if 'fsl,ave-ctrl' > 1, counts at
2 kHz and its default is 2 (= 1 ms)
- fsl,settling: delay between plate switch to next sample. Allowed value is
1 ... 2047. It counts at 2 kHz and its default is

View File

@ -38,6 +38,7 @@ chunghwa Chunghwa Picture Tubes Ltd.
cirrus Cirrus Logic, Inc.
cnm Chips&Media, Inc.
cortina Cortina Systems, Inc.
cosmic Cosmic Circuits
crystalfontz Crystalfontz America, Inc.
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
davicom DAVICOM Semiconductor, Inc.

View File

@ -258,6 +258,8 @@ IIO
devm_iio_device_free()
devm_iio_device_register()
devm_iio_device_unregister()
devm_iio_kfifo_allocate()
devm_iio_kfifo_free()
devm_iio_trigger_alloc()
devm_iio_trigger_free()

View File

@ -2399,6 +2399,12 @@ F: security/capability.c
F: security/commoncap.c
F: kernel/capability.c
CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
M: Kevin Tsai <ktsai@capellamicro.com>
S: Maintained
F: drivers/iio/light/cm*
F: Documentation/devicetree/bindings/i2c/trivial-devices.txt
CC2520 IEEE-802.15.4 RADIO DRIVER
M: Varka Bhadram <varkabhadram@gmail.com>
L: linux-wpan@vger.kernel.org
@ -3904,6 +3910,12 @@ S: Supported
F: Documentation/fault-injection/
F: lib/fault-inject.c
FBTFT Framebuffer drivers
M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
M: Noralf Trønnes <noralf@tronnes.org>
S: Maintained
F: drivers/staging/fbtft/
FCOE SUBSYSTEM (libfc, libfcoe, fcoe)
M: Robert Love <robert.w.love@intel.com>
L: fcoe-devel@open-fcoe.org
@ -9262,6 +9274,14 @@ L: linux-wireless@vger.kernel.org
S: Maintained
F: drivers/staging/rtl8723au/
STAGING - SILICON MOTION SM7XX FRAME BUFFER DRIVER
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
M: Teddy Wang <teddy.wang@siliconmotion.com>
M: Sudip Mukherjee <sudip@vectorindia.org>
L: linux-fbdev@vger.kernel.org
S: Maintained
F: drivers/staging/sm7xxfb/
STAGING - SLICOSS
M: Lior Dotan <liodot@gmail.com>
M: Christopher Harrer <charrer@alacritech.com>

View File

@ -36,8 +36,6 @@ source "drivers/message/fusion/Kconfig"
source "drivers/firewire/Kconfig"
source "drivers/message/i2o/Kconfig"
source "drivers/macintosh/Kconfig"
source "drivers/net/Kconfig"

View File

@ -27,7 +27,6 @@ boolean "IIO callback buffer used for push in-kernel interfaces"
usage. That is, those where the data is pushed to the consumer.
config IIO_KFIFO_BUF
select IIO_TRIGGER
tristate "Industrial I/O buffering based on kfifo"
help
A simple fifo based on kfifo. Note that this currently provides

View File

@ -43,6 +43,9 @@ config HID_SENSOR_ACCEL_3D
Say yes here to build support for the HID SENSOR
accelerometers 3D.
To compile this driver as a module, choose M here: the
module will be called hid-sensor-accel-3d.
config IIO_ST_ACCEL_3AXIS
tristate "STMicroelectronics accelerometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
@ -80,6 +83,9 @@ config KXSD9
Say yes here to build support for the Kionix KXSD9 accelerometer.
Currently this only supports the device via an SPI interface.
To compile this driver as a module, choose M here: the module
will be called kxsd9.
config MMA8452
tristate "Freescale MMA8452Q Accelerometer Driver"
depends on I2C
@ -105,4 +111,29 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will
be called kxcjk-1013.
config MMA9551_CORE
tristate
config MMA9551
tristate "Freescale MMA9551L Intelligent Motion-Sensing Platform Driver"
depends on I2C
select MMA9551_CORE
help
Say yes here to build support for the Freescale MMA9551L
Intelligent Motion-Sensing Platform Driver.
To compile this driver as a module, choose M here: the module
will be called mma9551.
config MMA9553
tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
depends on I2C
select MMA9551_CORE
help
Say yes here to build support for the Freescale MMA9553L
Intelligent Pedometer Platform Driver.
To compile this driver as a module, choose M here: the module
will be called mma9553.
endmenu

View File

@ -10,6 +10,12 @@ obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
obj-$(CONFIG_MMA8452) += mma8452.o
obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
st_accel-y := st_accel_core.o
st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o

View File

@ -111,19 +111,12 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case 0:
poll_value = hid_sensor_read_poll_value(
&accel_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&accel_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id = accel_state->accel[chan->scan_index].report_id;
address = accel_3d_addresses[chan->scan_index];
if (report_id >= 0)
@ -419,6 +412,7 @@ static struct platform_driver hid_accel_3d_platform_driver = {
.id_table = hid_accel_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_accel_3d_probe,
.remove = hid_accel_3d_remove,

View File

@ -108,6 +108,7 @@ struct kxcjk1013_data {
bool motion_trigger_on;
int64_t timestamp;
enum kx_chipset chipset;
bool is_smo8500_device;
};
enum kxcjk1013_axis {
@ -377,6 +378,7 @@ static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data)
static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
{
#ifdef CONFIG_PM
int ret;
if (on)
@ -388,8 +390,11 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: kxcjk1013_set_power_state for %d\n", on);
if (on)
pm_runtime_put_noidle(&data->client->dev);
return ret;
}
#endif
return 0;
}
@ -858,6 +863,8 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
ret = kxcjk1013_setup_any_motion_interrupt(data, state);
if (ret < 0) {
kxcjk1013_set_power_state(data, false);
data->ev_enable_state = 0;
mutex_unlock(&data->mutex);
return ret;
}
@ -1008,6 +1015,7 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig,
else
ret = kxcjk1013_setup_new_data_interrupt(data, state);
if (ret < 0) {
kxcjk1013_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
@ -1131,12 +1139,16 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private)
}
static const char *kxcjk1013_match_acpi_device(struct device *dev,
enum kx_chipset *chipset)
enum kx_chipset *chipset,
bool *is_smo8500_device)
{
const struct acpi_device_id *id;
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);
@ -1151,6 +1163,8 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
if (!client)
return -EINVAL;
if (data->is_smo8500_device)
return -ENOTSUPP;
dev = &client->dev;
@ -1200,7 +1214,8 @@ static int kxcjk1013_probe(struct i2c_client *client,
name = id->name;
} else if (ACPI_HANDLE(&client->dev)) {
name = kxcjk1013_match_acpi_device(&client->dev,
&data->chipset);
&data->chipset,
&data->is_smo8500_device);
} else
return -ENODEV;
@ -1228,21 +1243,25 @@ static int kxcjk1013_probe(struct i2c_client *client,
KXCJK1013_IRQ_NAME,
indio_dev);
if (ret)
return ret;
goto err_poweroff;
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!data->dready_trig)
return -ENOMEM;
if (!data->dready_trig) {
ret = -ENOMEM;
goto err_poweroff;
}
data->motion_trig = devm_iio_trigger_alloc(&client->dev,
"%s-any-motion-dev%d",
indio_dev->name,
indio_dev->id);
if (!data->motion_trig)
return -ENOMEM;
if (!data->motion_trig) {
ret = -ENOMEM;
goto err_poweroff;
}
data->dready_trig->dev.parent = &client->dev;
data->dready_trig->ops = &kxcjk1013_trigger_ops;
@ -1251,7 +1270,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
iio_trigger_get(indio_dev->trig);
ret = iio_trigger_register(data->dready_trig);
if (ret)
return ret;
goto err_poweroff;
data->motion_trig->dev.parent = &client->dev;
data->motion_trig->ops = &kxcjk1013_trigger_ops;
@ -1300,6 +1319,8 @@ static int kxcjk1013_probe(struct i2c_client *client,
iio_trigger_unregister(data->dready_trig);
if (data->motion_trig)
iio_trigger_unregister(data->motion_trig);
err_poweroff:
kxcjk1013_set_mode(data, STANDBY);
return ret;
}
@ -1349,10 +1370,7 @@ static int kxcjk1013_resume(struct device *dev)
int ret = 0;
mutex_lock(&data->mutex);
/* Check, if the suspend occured while active */
if (data->dready_trigger_on || data->motion_trigger_on ||
data->ev_enable_state)
ret = kxcjk1013_set_mode(data, OPERATION);
ret = kxcjk1013_set_mode(data, OPERATION);
mutex_unlock(&data->mutex);
return ret;
@ -1364,8 +1382,14 @@ static int kxcjk1013_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct kxcjk1013_data *data = iio_priv(indio_dev);
int ret;
return kxcjk1013_set_mode(data, STANDBY);
ret = kxcjk1013_set_mode(data, STANDBY);
if (ret < 0) {
dev_err(&data->client->dev, "powering off device failed\n");
return -EAGAIN;
}
return 0;
}
static int kxcjk1013_runtime_resume(struct device *dev)
@ -1399,6 +1423,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXTJ1009", KXTJ21009},
{"SMO8500", KXCJ91008},
{ },
};
MODULE_DEVICE_TABLE(acpi, kx_acpi_match);
@ -1407,6 +1432,7 @@ static const struct i2c_device_id kxcjk1013_id[] = {
{"kxcjk1013", KXCJK1013},
{"kxcj91008", KXCJ91008},
{"kxtj21009", KXTJ21009},
{"SMO8500", KXCJ91008},
{}
};

View File

@ -111,7 +111,7 @@ static const int mma8452_samp_freq[8][2] = {
{6, 250000}, {1, 560000}
};
/*
/*
* Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
* The userspace interface uses m/s^2 and we declare micro units
* So scale factor is given by:

637
drivers/iio/accel/mma9551.c Normal file
View File

@ -0,0 +1,637 @@
/*
* Freescale MMA9551L Intelligent Motion-Sensing Platform driver
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/pm_runtime.h>
#include "mma9551_core.h"
#define MMA9551_DRV_NAME "mma9551"
#define MMA9551_IRQ_NAME "mma9551_event"
#define MMA9551_GPIO_NAME "mma9551_int"
#define MMA9551_GPIO_COUNT 4
/* Tilt application (inclination in IIO terms). */
#define MMA9551_TILT_XZ_ANG_REG 0x00
#define MMA9551_TILT_YZ_ANG_REG 0x01
#define MMA9551_TILT_XY_ANG_REG 0x02
#define MMA9551_TILT_ANGFLG BIT(7)
#define MMA9551_TILT_QUAD_REG 0x03
#define MMA9551_TILT_XY_QUAD_SHIFT 0
#define MMA9551_TILT_YZ_QUAD_SHIFT 2
#define MMA9551_TILT_XZ_QUAD_SHIFT 4
#define MMA9551_TILT_CFG_REG 0x01
#define MMA9551_TILT_ANG_THRESH_MASK GENMASK(3, 0)
#define MMA9551_DEFAULT_SAMPLE_RATE 122 /* Hz */
/* Tilt events are mapped to the first three GPIO pins. */
enum mma9551_tilt_axis {
mma9551_x = 0,
mma9551_y,
mma9551_z,
};
struct mma9551_data {
struct i2c_client *client;
struct mutex mutex;
int event_enabled[3];
int irqs[MMA9551_GPIO_COUNT];
};
static int mma9551_read_incli_chan(struct i2c_client *client,
const struct iio_chan_spec *chan,
int *val)
{
u8 quad_shift, angle, quadrant;
u16 reg_addr;
int ret;
switch (chan->channel2) {
case IIO_MOD_X:
reg_addr = MMA9551_TILT_YZ_ANG_REG;
quad_shift = MMA9551_TILT_YZ_QUAD_SHIFT;
break;
case IIO_MOD_Y:
reg_addr = MMA9551_TILT_XZ_ANG_REG;
quad_shift = MMA9551_TILT_XZ_QUAD_SHIFT;
break;
case IIO_MOD_Z:
reg_addr = MMA9551_TILT_XY_ANG_REG;
quad_shift = MMA9551_TILT_XY_QUAD_SHIFT;
break;
default:
return -EINVAL;
}
ret = mma9551_set_power_state(client, true);
if (ret < 0)
return ret;
ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
reg_addr, &angle);
if (ret < 0)
goto out_poweroff;
ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
MMA9551_TILT_QUAD_REG, &quadrant);
if (ret < 0)
goto out_poweroff;
angle &= ~MMA9551_TILT_ANGFLG;
quadrant = (quadrant >> quad_shift) & 0x03;
if (quadrant == 1 || quadrant == 3)
*val = 90 * (quadrant + 1) - angle;
else
*val = angle + 90 * quadrant;
ret = IIO_VAL_INT;
out_poweroff:
mma9551_set_power_state(client, false);
return ret;
}
static int mma9551_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_INCLI:
mutex_lock(&data->mutex);
ret = mma9551_read_incli_chan(data->client, chan, val);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_ACCEL:
mutex_lock(&data->mutex);
ret = mma9551_read_accel_chan(data->client,
chan, val, val2);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ACCEL:
return mma9551_read_accel_scale(val, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int mma9551_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 mma9551_data *data = iio_priv(indio_dev);
switch (chan->type) {
case IIO_INCLI:
/* IIO counts axes from 1, because IIO_NO_MOD is 0. */
return data->event_enabled[chan->channel2 - 1];
default:
return -EINVAL;
}
}
static int mma9551_config_incli_event(struct iio_dev *indio_dev,
enum iio_modifier axis,
int state)
{
struct mma9551_data *data = iio_priv(indio_dev);
enum mma9551_tilt_axis mma_axis;
int ret;
/* IIO counts axes from 1, because IIO_NO_MOD is 0. */
mma_axis = axis - 1;
if (data->event_enabled[mma_axis] == state)
return 0;
if (state == 0) {
ret = mma9551_gpio_config(data->client,
(enum mma9551_gpio_pin)mma_axis,
MMA9551_APPID_NONE, 0, 0);
if (ret < 0)
return ret;
ret = mma9551_set_power_state(data->client, false);
if (ret < 0)
return ret;
} else {
int bitnum;
/* Bit 7 of each angle register holds the angle flag. */
switch (axis) {
case IIO_MOD_X:
bitnum = 7 + 8 * MMA9551_TILT_YZ_ANG_REG;
break;
case IIO_MOD_Y:
bitnum = 7 + 8 * MMA9551_TILT_XZ_ANG_REG;
break;
case IIO_MOD_Z:
bitnum = 7 + 8 * MMA9551_TILT_XY_ANG_REG;
break;
default:
return -EINVAL;
}
ret = mma9551_set_power_state(data->client, true);
if (ret < 0)
return ret;
ret = mma9551_gpio_config(data->client,
(enum mma9551_gpio_pin)mma_axis,
MMA9551_APPID_TILT, bitnum, 0);
if (ret < 0) {
mma9551_set_power_state(data->client, false);
return ret;
}
}
data->event_enabled[mma_axis] = state;
return ret;
}
static int mma9551_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 mma9551_data *data = iio_priv(indio_dev);
int ret;
switch (chan->type) {
case IIO_INCLI:
mutex_lock(&data->mutex);
ret = mma9551_config_incli_event(indio_dev,
chan->channel2, state);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
}
static int mma9551_write_event_value(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 mma9551_data *data = iio_priv(indio_dev);
int ret;
switch (chan->type) {
case IIO_INCLI:
if (val2 != 0 || val < 1 || val > 10)
return -EINVAL;
mutex_lock(&data->mutex);
ret = mma9551_update_config_bits(data->client,
MMA9551_APPID_TILT,
MMA9551_TILT_CFG_REG,
MMA9551_TILT_ANG_THRESH_MASK,
val);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
}
static int mma9551_read_event_value(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 mma9551_data *data = iio_priv(indio_dev);
int ret;
u8 tmp;
switch (chan->type) {
case IIO_INCLI:
mutex_lock(&data->mutex);
ret = mma9551_read_config_byte(data->client,
MMA9551_APPID_TILT,
MMA9551_TILT_CFG_REG, &tmp);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp & MMA9551_TILT_ANG_THRESH_MASK;
*val2 = 0;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static const struct iio_event_spec mma9551_incli_event = {
.type = IIO_EV_TYPE_ROC,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
};
#define MMA9551_INCLI_CHANNEL(axis) { \
.type = IIO_INCLI, \
.modified = 1, \
.channel2 = axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
.event_spec = &mma9551_incli_event, \
.num_event_specs = 1, \
}
static const struct iio_chan_spec mma9551_channels[] = {
MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
MMA9551_INCLI_CHANNEL(IIO_MOD_X),
MMA9551_INCLI_CHANNEL(IIO_MOD_Y),
MMA9551_INCLI_CHANNEL(IIO_MOD_Z),
};
static const struct iio_info mma9551_info = {
.driver_module = THIS_MODULE,
.read_raw = mma9551_read_raw,
.read_event_config = mma9551_read_event_config,
.write_event_config = mma9551_write_event_config,
.read_event_value = mma9551_read_event_value,
.write_event_value = mma9551_write_event_value,
};
static irqreturn_t mma9551_event_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct mma9551_data *data = iio_priv(indio_dev);
int i, ret, mma_axis = -1;
u16 reg;
u8 val;
mutex_lock(&data->mutex);
for (i = 0; i < 3; i++)
if (irq == data->irqs[i]) {
mma_axis = i;
break;
}
if (mma_axis == -1) {
/* IRQ was triggered on 4th line, which we don't use. */
dev_warn(&data->client->dev,
"irq triggered on unused line %d\n", data->irqs[3]);
goto out;
}
switch (mma_axis) {
case mma9551_x:
reg = MMA9551_TILT_YZ_ANG_REG;
break;
case mma9551_y:
reg = MMA9551_TILT_XZ_ANG_REG;
break;
case mma9551_z:
reg = MMA9551_TILT_XY_ANG_REG;
break;
}
/*
* Read the angle even though we don't use it, otherwise we
* won't get any further interrupts.
*/
ret = mma9551_read_status_byte(data->client, MMA9551_APPID_TILT,
reg, &val);
if (ret < 0) {
dev_err(&data->client->dev,
"error %d reading tilt register in IRQ\n", ret);
goto out;
}
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_INCLI, 0, (mma_axis + 1),
IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING),
iio_get_time_ns());
out:
mutex_unlock(&data->mutex);
return IRQ_HANDLED;
}
static int mma9551_init(struct mma9551_data *data)
{
int ret;
ret = mma9551_read_version(data->client);
if (ret)
return ret;
return mma9551_set_device_state(data->client, true);
}
static int mma9551_gpio_probe(struct iio_dev *indio_dev)
{
struct gpio_desc *gpio;
int i, ret;
struct mma9551_data *data = iio_priv(indio_dev);
struct device *dev = &data->client->dev;
for (i = 0; i < MMA9551_GPIO_COUNT; i++) {
gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i);
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;
data->irqs[i] = gpiod_to_irq(gpio);
ret = devm_request_threaded_irq(dev, data->irqs[i],
NULL, mma9551_event_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
MMA9551_IRQ_NAME, indio_dev);
if (ret < 0) {
dev_err(dev, "request irq %d failed\n", data->irqs[i]);
return ret;
}
dev_dbg(dev, "gpio resource, no:%d irq:%d\n",
desc_to_gpio(gpio), data->irqs[i]);
}
return 0;
}
static const char *mma9551_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
return dev_name(dev);
}
static int mma9551_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mma9551_data *data;
struct iio_dev *indio_dev;
const char *name = NULL;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
if (id)
name = id->name;
else if (ACPI_HANDLE(&client->dev))
name = mma9551_match_acpi_device(&client->dev);
ret = mma9551_init(data);
if (ret < 0)
return ret;
mutex_init(&data->mutex);
indio_dev->dev.parent = &client->dev;
indio_dev->channels = mma9551_channels;
indio_dev->num_channels = ARRAY_SIZE(mma9551_channels);
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mma9551_info;
ret = mma9551_gpio_probe(indio_dev);
if (ret < 0)
goto out_poweroff;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto out_poweroff;
}
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto out_iio_unregister;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
MMA9551_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
return 0;
out_iio_unregister:
iio_device_unregister(indio_dev);
out_poweroff:
mma9551_set_device_state(client, false);
return ret;
}
static int mma9551_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma9551_data *data = iio_priv(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
return 0;
}
#ifdef CONFIG_PM
static int mma9551_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
if (ret < 0) {
dev_err(&data->client->dev, "powering off device failed\n");
return -EAGAIN;
}
return 0;
}
static int mma9551_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
ret = mma9551_set_device_state(data->client, true);
if (ret < 0)
return ret;
mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
return 0;
}
#endif
#ifdef CONFIG_PM_SLEEP
static int mma9551_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
return ret;
}
static int mma9551_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mma9551_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
ret = mma9551_set_device_state(data->client, true);
mutex_unlock(&data->mutex);
return ret;
}
#endif
static const struct dev_pm_ops mma9551_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
mma9551_runtime_resume, NULL)
};
static const struct acpi_device_id mma9551_acpi_match[] = {
{"MMA9551", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, mma9551_acpi_match);
static const struct i2c_device_id mma9551_id[] = {
{"mma9551", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mma9551_id);
static struct i2c_driver mma9551_driver = {
.driver = {
.name = MMA9551_DRV_NAME,
.acpi_match_table = ACPI_PTR(mma9551_acpi_match),
.pm = &mma9551_pm_ops,
},
.probe = mma9551_probe,
.remove = mma9551_remove,
.id_table = mma9551_id,
};
module_i2c_driver(mma9551_driver);
MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MMA9551L motion-sensing platform driver");

View File

@ -0,0 +1,798 @@
/*
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/pm_runtime.h>
#include "mma9551_core.h"
/* Command masks for mailbox write command */
#define MMA9551_CMD_READ_VERSION_INFO 0x00
#define MMA9551_CMD_READ_CONFIG 0x10
#define MMA9551_CMD_WRITE_CONFIG 0x20
#define MMA9551_CMD_READ_STATUS 0x30
/* Mailbox read command */
#define MMA9551_RESPONSE_COCO BIT(7)
/* Error-Status codes returned in mailbox read command */
#define MMA9551_MCI_ERROR_NONE 0x00
#define MMA9551_MCI_ERROR_PARAM 0x04
#define MMA9551_MCI_INVALID_COUNT 0x19
#define MMA9551_MCI_ERROR_COMMAND 0x1C
#define MMA9551_MCI_ERROR_INVALID_LENGTH 0x21
#define MMA9551_MCI_ERROR_FIFO_BUSY 0x22
#define MMA9551_MCI_ERROR_FIFO_ALLOCATED 0x23
#define MMA9551_MCI_ERROR_FIFO_OVERSIZE 0x24
/* GPIO Application */
#define MMA9551_GPIO_POL_MSB 0x08
#define MMA9551_GPIO_POL_LSB 0x09
/* Sleep/Wake application */
#define MMA9551_SLEEP_CFG 0x06
#define MMA9551_SLEEP_CFG_SNCEN BIT(0)
#define MMA9551_SLEEP_CFG_FLEEN BIT(1)
#define MMA9551_SLEEP_CFG_SCHEN BIT(2)
/* AFE application */
#define MMA9551_AFE_X_ACCEL_REG 0x00
#define MMA9551_AFE_Y_ACCEL_REG 0x02
#define MMA9551_AFE_Z_ACCEL_REG 0x04
/* Reset/Suspend/Clear application */
#define MMA9551_RSC_RESET 0x00
#define MMA9551_RSC_OFFSET(mask) (3 - (ffs(mask) - 1) / 8)
#define MMA9551_RSC_VAL(mask) (mask >> (((ffs(mask) - 1) / 8) * 8))
/*
* A response is composed of:
* - control registers: MB0-3
* - data registers: MB4-31
*
* A request is composed of:
* - mbox to write to (always 0)
* - control registers: MB1-4
* - data registers: MB5-31
*/
#define MMA9551_MAILBOX_CTRL_REGS 4
#define MMA9551_MAX_MAILBOX_DATA_REGS 28
#define MMA9551_MAILBOX_REGS 32
#define MMA9551_I2C_READ_RETRIES 5
#define MMA9551_I2C_READ_DELAY 50 /* us */
struct mma9551_mbox_request {
u8 start_mbox; /* Always 0. */
u8 app_id;
/*
* See Section 5.3.1 of the MMA955xL Software Reference Manual.
*
* Bit 7: reserved, always 0
* Bits 6-4: command
* Bits 3-0: upper bits of register offset
*/
u8 cmd_off;
u8 lower_off;
u8 nbytes;
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
} __packed;
struct mma9551_mbox_response {
u8 app_id;
/*
* See Section 5.3.3 of the MMA955xL Software Reference Manual.
*
* Bit 7: COCO
* Bits 6-0: Error code.
*/
u8 coco_err;
u8 nbytes;
u8 req_bytes;
u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
} __packed;
struct mma9551_version_info {
__be32 device_id;
u8 rom_version[2];
u8 fw_version[2];
u8 hw_version[2];
u8 fw_build[2];
};
static int mma9551_transfer(struct i2c_client *client,
u8 app_id, u8 command, u16 offset,
u8 *inbytes, int num_inbytes,
u8 *outbytes, int num_outbytes)
{
struct mma9551_mbox_request req;
struct mma9551_mbox_response rsp;
struct i2c_msg in, out;
u8 req_len, err_code;
int ret, retries;
if (offset >= 1 << 12) {
dev_err(&client->dev, "register offset too large\n");
return -EINVAL;
}
req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
req.start_mbox = 0;
req.app_id = app_id;
req.cmd_off = command | (offset >> 8);
req.lower_off = offset;
if (command == MMA9551_CMD_WRITE_CONFIG)
req.nbytes = num_inbytes;
else
req.nbytes = num_outbytes;
if (num_inbytes)
memcpy(req.buf, inbytes, num_inbytes);
out.addr = client->addr;
out.flags = 0;
out.len = req_len;
out.buf = (u8 *)&req;
ret = i2c_transfer(client->adapter, &out, 1);
if (ret < 0) {
dev_err(&client->dev, "i2c write failed\n");
return ret;
}
retries = MMA9551_I2C_READ_RETRIES;
do {
udelay(MMA9551_I2C_READ_DELAY);
in.addr = client->addr;
in.flags = I2C_M_RD;
in.len = sizeof(rsp);
in.buf = (u8 *)&rsp;
ret = i2c_transfer(client->adapter, &in, 1);
if (ret < 0) {
dev_err(&client->dev, "i2c read failed\n");
return ret;
}
if (rsp.coco_err & MMA9551_RESPONSE_COCO)
break;
} while (--retries > 0);
if (retries == 0) {
dev_err(&client->dev,
"timed out while waiting for command response\n");
return -ETIMEDOUT;
}
if (rsp.app_id != app_id) {
dev_err(&client->dev,
"app_id mismatch in response got %02x expected %02x\n",
rsp.app_id, app_id);
return -EINVAL;
}
err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
if (err_code != MMA9551_MCI_ERROR_NONE) {
dev_err(&client->dev, "read returned error %x\n", err_code);
return -EINVAL;
}
if (rsp.nbytes != rsp.req_bytes) {
dev_err(&client->dev,
"output length mismatch got %d expected %d\n",
rsp.nbytes, rsp.req_bytes);
return -EINVAL;
}
if (num_outbytes)
memcpy(outbytes, rsp.buf, num_outbytes);
return 0;
}
/**
* mma9551_read_config_byte() - read 1 configuration byte
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one configuration byte from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed
* by one or more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val)
{
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, val, 1);
}
EXPORT_SYMBOL(mma9551_read_config_byte);
/**
* mma9551_write_config_byte() - write 1 configuration byte
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Value to write
*
* Write one configuration byte from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 val)
{
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
&val, 1, NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_byte);
/**
* mma9551_read_status_byte() - read 1 status byte
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one status byte from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val)
{
return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, val, 1);
}
EXPORT_SYMBOL(mma9551_read_status_byte);
/**
* mma9551_read_config_word() - read 1 config word
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one configuration word from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val)
{
int ret;
__be16 v;
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, (u8 *)&v, 2);
*val = be16_to_cpu(v);
return ret;
}
EXPORT_SYMBOL(mma9551_read_config_word);
/**
* mma9551_write_config_word() - write 1 config word
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Value to write
*
* Write one configuration word from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 val)
{
__be16 v = cpu_to_be16(val);
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
(u8 *) &v, 2, NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_word);
/**
* mma9551_read_status_word() - read 1 status word
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @val: Pointer to store value read
*
* Read one status word from the device using MMA955xL command format.
* Commands to the MMA955xL platform consist of a write followed by one or
* more reads.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val)
{
int ret;
__be16 v;
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, (u8 *)&v, 2);
*val = be16_to_cpu(v);
return ret;
}
EXPORT_SYMBOL(mma9551_read_status_word);
/**
* mma9551_read_config_words() - read multiple config words
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
* @val: Array of words to read
*
* Read multiple configuration registers (word-sized registers).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int ret, i;
int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, (u8 *) be_buf, len);
if (ret < 0)
return ret;
for (i = 0; i < len_words; i++)
buf[i] = be16_to_cpu(be_buf[i]);
return 0;
}
EXPORT_SYMBOL(mma9551_read_config_words);
/**
* mma9551_read_status_words() - read multiple status words
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
* @val: Array of words to read
*
* Read multiple status registers (word-sized registers).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int ret, i;
int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, (u8 *) be_buf, len);
if (ret < 0)
return ret;
for (i = 0; i < len_words; i++)
buf[i] = be16_to_cpu(be_buf[i]);
return 0;
}
EXPORT_SYMBOL(mma9551_read_status_words);
/**
* mma9551_write_config_words() - write multiple config words
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to write in bytes
* @val: Array of words to write
*
* Write multiple configuration registers (word-sized registers).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int i;
int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
for (i = 0; i < len_words; i++)
be_buf[i] = cpu_to_be16(buf[i]);
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
reg, (u8 *) be_buf, len, NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_words);
/**
* mma9551_update_config_bits() - update bits in register
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
* @mask: Mask for the bits to update
* @val: Value of the bits to update
*
* Update bits in the given register using a bit mask.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
u16 reg, u8 mask, u8 val)
{
int ret;
u8 tmp, orig;
ret = mma9551_read_config_byte(client, app_id, reg, &orig);
if (ret < 0)
return ret;
tmp = orig & ~mask;
tmp |= val & mask;
if (tmp == orig)
return 0;
return mma9551_write_config_byte(client, app_id, reg, tmp);
}
EXPORT_SYMBOL(mma9551_update_config_bits);
/**
* mma9551_gpio_config() - configure gpio
* @client: I2C client
* @pin: GPIO pin to configure
* @app_id: Application ID
* @bitnum: Bit number of status register being assigned to the GPIO pin.
* @polarity: The polarity parameter is described in section 6.2.2, page 66,
* of the Software Reference Manual. Basically, polarity=0 means
* the interrupt line has the same value as the selected bit,
* while polarity=1 means the line is inverted.
*
* Assign a bit from an applications status register to a specific GPIO pin.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
u8 app_id, u8 bitnum, int polarity)
{
u8 reg, pol_mask, pol_val;
int ret;
if (pin > mma9551_gpio_max) {
dev_err(&client->dev, "bad GPIO pin\n");
return -EINVAL;
}
/*
* Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
* 0x03, and so on.
*/
reg = pin * 2;
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
reg, app_id);
if (ret < 0) {
dev_err(&client->dev, "error setting GPIO app_id\n");
return ret;
}
ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
reg + 1, bitnum);
if (ret < 0) {
dev_err(&client->dev, "error setting GPIO bit number\n");
return ret;
}
switch (pin) {
case mma9551_gpio6:
reg = MMA9551_GPIO_POL_LSB;
pol_mask = 1 << 6;
break;
case mma9551_gpio7:
reg = MMA9551_GPIO_POL_LSB;
pol_mask = 1 << 7;
break;
case mma9551_gpio8:
reg = MMA9551_GPIO_POL_MSB;
pol_mask = 1 << 0;
break;
case mma9551_gpio9:
reg = MMA9551_GPIO_POL_MSB;
pol_mask = 1 << 1;
break;
}
pol_val = polarity ? pol_mask : 0;
ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
pol_mask, pol_val);
if (ret < 0)
dev_err(&client->dev, "error setting GPIO polarity\n");
return ret;
}
EXPORT_SYMBOL(mma9551_gpio_config);
/**
* mma9551_read_version() - read device version information
* @client: I2C client
*
* Read version information and print device id and firmware version.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_version(struct i2c_client *client)
{
struct mma9551_version_info info;
int ret;
ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
NULL, 0, (u8 *)&info, sizeof(info));
if (ret < 0)
return ret;
dev_info(&client->dev, "device ID 0x%x, firmware version %02x.%02x\n",
be32_to_cpu(info.device_id), info.fw_version[0],
info.fw_version[1]);
return 0;
}
EXPORT_SYMBOL(mma9551_read_version);
/**
* mma9551_set_device_state() - sets HW power mode
* @client: I2C client
* @enable: Use true to power on device, false to cause the device
* to enter sleep.
*
* Set power on/off for device using the Sleep/Wake Application.
* When enable is true, power on chip and enable doze mode.
* When enable is false, enter sleep mode (device remains in the
* lowest-power mode).
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_set_device_state(struct i2c_client *client, bool enable)
{
return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
MMA9551_SLEEP_CFG,
MMA9551_SLEEP_CFG_SNCEN |
MMA9551_SLEEP_CFG_FLEEN |
MMA9551_SLEEP_CFG_SCHEN,
enable ? MMA9551_SLEEP_CFG_SCHEN |
MMA9551_SLEEP_CFG_FLEEN :
MMA9551_SLEEP_CFG_SNCEN);
}
EXPORT_SYMBOL(mma9551_set_device_state);
/**
* mma9551_set_power_state() - sets runtime PM state
* @client: I2C client
* @on: Use true to power on device, false to power off
*
* Resume or suspend the device using Runtime PM.
* The device will suspend after the autosuspend delay.
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_set_power_state(struct i2c_client *client, bool on)
{
#ifdef CONFIG_PM
int ret;
if (on)
ret = pm_runtime_get_sync(&client->dev);
else {
pm_runtime_mark_last_busy(&client->dev);
ret = pm_runtime_put_autosuspend(&client->dev);
}
if (ret < 0) {
dev_err(&client->dev,
"failed to change power state to %d\n", on);
if (on)
pm_runtime_put_noidle(&client->dev);
return ret;
}
#endif
return 0;
}
EXPORT_SYMBOL(mma9551_set_power_state);
/**
* mma9551_sleep() - sleep
* @freq: Application frequency
*
* Firmware applications run at a certain frequency on the
* device. Sleep for one application cycle to make sure the
* application had time to run once and initialize set values.
*/
void mma9551_sleep(int freq)
{
int sleep_val = 1000 / freq;
if (sleep_val < 20)
usleep_range(sleep_val * 1000, 20000);
else
msleep_interruptible(sleep_val);
}
EXPORT_SYMBOL(mma9551_sleep);
/**
* mma9551_read_accel_chan() - read accelerometer channel
* @client: I2C client
* @chan: IIO channel
* @val: Pointer to the accelerometer value read
* @val2: Unused
*
* Read accelerometer value for the specified channel.
*
* Locking note: This function must be called with the device lock held.
* Locking is not handled inside the function. Callers should ensure they
* serialize access to the HW.
*
* Returns: IIO_VAL_INT on success, negative value on failure.
*/
int mma9551_read_accel_chan(struct i2c_client *client,
const struct iio_chan_spec *chan,
int *val, int *val2)
{
u16 reg_addr;
s16 raw_accel;
int ret;
switch (chan->channel2) {
case IIO_MOD_X:
reg_addr = MMA9551_AFE_X_ACCEL_REG;
break;
case IIO_MOD_Y:
reg_addr = MMA9551_AFE_Y_ACCEL_REG;
break;
case IIO_MOD_Z:
reg_addr = MMA9551_AFE_Z_ACCEL_REG;
break;
default:
return -EINVAL;
}
ret = mma9551_set_power_state(client, true);
if (ret < 0)
return ret;
ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
reg_addr, &raw_accel);
if (ret < 0)
goto out_poweroff;
*val = raw_accel;
ret = IIO_VAL_INT;
out_poweroff:
mma9551_set_power_state(client, false);
return ret;
}
EXPORT_SYMBOL(mma9551_read_accel_chan);
/**
* mma9551_read_accel_scale() - read accelerometer scale
* @val: Pointer to the accelerometer scale (int value)
* @val2: Pointer to the accelerometer scale (micro value)
*
* Read accelerometer scale.
*
* Returns: IIO_VAL_INT_PLUS_MICRO.
*/
int mma9551_read_accel_scale(int *val, int *val2)
{
*val = 0;
*val2 = 2440;
return IIO_VAL_INT_PLUS_MICRO;
}
EXPORT_SYMBOL(mma9551_read_accel_scale);
/**
* mma9551_app_reset() - reset application
* @client: I2C client
* @app_mask: Application to reset
*
* Reset the given application (using the Reset/Suspend/Clear
* Control Application)
*
* Returns: 0 on success, negative value on failure.
*/
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
{
return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
MMA9551_RSC_RESET +
MMA9551_RSC_OFFSET(app_mask),
MMA9551_RSC_VAL(app_mask));
}
EXPORT_SYMBOL(mma9551_app_reset);
MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MMA955xL sensors core");

View File

@ -0,0 +1,81 @@
/*
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef _MMA9551_CORE_H_
#define _MMA9551_CORE_H_
/* Applications IDs */
#define MMA9551_APPID_VERSION 0x00
#define MMA9551_APPID_GPIO 0x03
#define MMA9551_APPID_AFE 0x06
#define MMA9551_APPID_TILT 0x0B
#define MMA9551_APPID_SLEEP_WAKE 0x12
#define MMA9551_APPID_PEDOMETER 0x15
#define MMA9551_APPID_RCS 0x17
#define MMA9551_APPID_NONE 0xff
/* Reset/Suspend/Clear application app masks */
#define MMA9551_RSC_PED BIT(21)
#define MMA9551_AUTO_SUSPEND_DELAY_MS 2000
enum mma9551_gpio_pin {
mma9551_gpio6 = 0,
mma9551_gpio7,
mma9551_gpio8,
mma9551_gpio9,
mma9551_gpio_max = mma9551_gpio9,
};
#define MMA9551_ACCEL_CHANNEL(axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val);
int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 val);
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val);
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val);
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 val);
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val);
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf);
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf);
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf);
int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
u16 reg, u8 mask, u8 val);
int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
u8 app_id, u8 bitnum, int polarity);
int mma9551_read_version(struct i2c_client *client);
int mma9551_set_device_state(struct i2c_client *client, bool enable);
int mma9551_set_power_state(struct i2c_client *client, bool on);
void mma9551_sleep(int freq);
int mma9551_read_accel_chan(struct i2c_client *client,
const struct iio_chan_spec *chan,
int *val, int *val2);
int mma9551_read_accel_scale(int *val, int *val2);
int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
#endif /* _MMA9551_CORE_H_ */

1334
drivers/iio/accel/mma9553.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../common/ssp_sensors/ssp_iio_sensor.h"
#define SSP_CHANNEL_COUNT 3
#define SSP_ACCEL_NAME "ssp-accelerometer"
static const char ssp_accel_device_name[] = SSP_ACCEL_NAME;
enum ssp_accel_3d_channel {
SSP_CHANNEL_SCAN_INDEX_X,
SSP_CHANNEL_SCAN_INDEX_Y,
SSP_CHANNEL_SCAN_INDEX_Z,
SSP_CHANNEL_SCAN_INDEX_TIME,
};
static int ssp_accel_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
u32 t;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
t = ssp_get_sensor_delay(data, SSP_ACCELEROMETER_SENSOR);
ssp_convert_to_freq(t, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
default:
break;
}
return -EINVAL;
}
static int ssp_accel_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
int ret;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ssp_convert_to_time(val, val2);
ret = ssp_change_delay(data, SSP_ACCELEROMETER_SENSOR, ret);
if (ret < 0)
dev_err(&indio_dev->dev, "accel sensor enable fail\n");
return ret;
default:
break;
}
return -EINVAL;
}
static struct iio_info ssp_accel_iio_info = {
.read_raw = &ssp_accel_read_raw,
.write_raw = &ssp_accel_write_raw,
};
static const unsigned long ssp_accel_scan_mask[] = { 0x7, 0, };
static const struct iio_chan_spec ssp_acc_channels[] = {
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
};
static int ssp_process_accel_data(struct iio_dev *indio_dev, void *buf,
int64_t timestamp)
{
return ssp_common_process_data(indio_dev, buf, SSP_ACCELEROMETER_SIZE,
timestamp);
}
static const struct iio_buffer_setup_ops ssp_accel_buffer_ops = {
.postenable = &ssp_common_buffer_postenable,
.postdisable = &ssp_common_buffer_postdisable,
};
static int ssp_accel_probe(struct platform_device *pdev)
{
int ret;
struct iio_dev *indio_dev;
struct ssp_sensor_data *spd;
struct iio_buffer *buffer;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
if (!indio_dev)
return -ENOMEM;
spd = iio_priv(indio_dev);
spd->process_data = ssp_process_accel_data;
spd->type = SSP_ACCELEROMETER_SENSOR;
indio_dev->name = ssp_accel_device_name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &ssp_accel_iio_info;
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = ssp_acc_channels;
indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
indio_dev->available_scan_masks = ssp_accel_scan_mask;
buffer = devm_iio_kfifo_allocate(&pdev->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->setup_ops = &ssp_accel_buffer_ops;
platform_set_drvdata(pdev, indio_dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
return ret;
/* ssp registering should be done after all iio setup */
ssp_register_consumer(indio_dev, SSP_ACCELEROMETER_SENSOR);
return 0;
}
static int ssp_accel_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
return 0;
}
static struct platform_driver ssp_accel_driver = {
.driver = {
.name = SSP_ACCEL_NAME,
},
.probe = ssp_accel_probe,
.remove = ssp_accel_remove,
};
module_platform_driver(ssp_accel_driver);
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver");
MODULE_LICENSE("GPL");

View File

@ -135,6 +135,17 @@ config AXP288_ADC
device. Depending on platform configuration, this general purpose ADC can
be used for sampling sensors such as thermal resistors.
config CC10001_ADC
tristate "Cosmic Circuits 10001 ADC driver"
depends on HAS_IOMEM || HAVE_CLK || REGULATOR
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Cosmic Circuits 10001 ADC.
This driver can also be built as a module. If so, the module will be
called cc10001_adc.
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@ -228,6 +239,20 @@ config QCOM_SPMI_IADC
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-iadc.
config QCOM_SPMI_VADC
tristate "Qualcomm SPMI PMIC voltage ADC"
depends on SPMI
select REGMAP_SPMI
help
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
The driver supports multiple channels read. The VADC is a 15-bit
sigma-delta ADC. Some of the channels are internally used for
calibration.
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-vadc.
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)

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_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
@ -24,6 +25,7 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o

View File

@ -0,0 +1,423 @@
/*
* Copyright (c) 2014-2015 Imagination Technologies Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
/* Registers */
#define CC10001_ADC_CONFIG 0x00
#define CC10001_ADC_START_CONV BIT(4)
#define CC10001_ADC_MODE_SINGLE_CONV BIT(5)
#define CC10001_ADC_DDATA_OUT 0x04
#define CC10001_ADC_EOC 0x08
#define CC10001_ADC_EOC_SET BIT(0)
#define CC10001_ADC_CHSEL_SAMPLED 0x0c
#define CC10001_ADC_POWER_UP 0x10
#define CC10001_ADC_POWER_UP_SET BIT(0)
#define CC10001_ADC_DEBUG 0x14
#define CC10001_ADC_DATA_COUNT 0x20
#define CC10001_ADC_DATA_MASK GENMASK(9, 0)
#define CC10001_ADC_NUM_CHANNELS 8
#define CC10001_ADC_CH_MASK GENMASK(2, 0)
#define CC10001_INVALID_SAMPLED 0xffff
#define CC10001_MAX_POLL_COUNT 20
/*
* As per device specification, wait six clock cycles after power-up to
* activate START. Since adding two more clock cycles delay does not
* impact the performance too much, we are adding two additional cycles delay
* intentionally here.
*/
#define CC10001_WAIT_CYCLES 8
struct cc10001_adc_device {
void __iomem *reg_base;
struct clk *adc_clk;
struct regulator *reg;
u16 *buf;
struct mutex lock;
unsigned long channel_map;
unsigned int start_delay_ns;
unsigned int eoc_delay_ns;
};
static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev,
u32 reg, u32 val)
{
writel(val, adc_dev->reg_base + reg);
}
static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev,
u32 reg)
{
return readl(adc_dev->reg_base + reg);
}
static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
unsigned int channel)
{
u32 val;
/* Channel selection and mode of operation */
val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG);
val = val | CC10001_ADC_START_CONV;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
}
static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev,
unsigned int channel,
unsigned int delay)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
unsigned int poll_count = 0;
while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) &
CC10001_ADC_EOC_SET)) {
ndelay(delay);
if (poll_count++ == CC10001_MAX_POLL_COUNT)
return CC10001_INVALID_SAMPLED;
}
poll_count = 0;
while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) &
CC10001_ADC_CH_MASK) != channel) {
ndelay(delay);
if (poll_count++ == CC10001_MAX_POLL_COUNT)
return CC10001_INVALID_SAMPLED;
}
/* Read the 10 bit output register */
return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) &
CC10001_ADC_DATA_MASK;
}
static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
{
struct cc10001_adc_device *adc_dev;
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev;
unsigned int delay_ns;
unsigned int channel;
bool sample_invalid;
u16 *data;
int i;
indio_dev = pf->indio_dev;
adc_dev = iio_priv(indio_dev);
data = adc_dev->buf;
mutex_lock(&adc_dev->lock);
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
CC10001_ADC_POWER_UP_SET);
/* Wait for 8 (6+2) clock cycles before activating START */
ndelay(adc_dev->start_delay_ns);
/* Calculate delay step for eoc and sampled data */
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
i = 0;
sample_invalid = false;
for_each_set_bit(channel, indio_dev->active_scan_mask,
indio_dev->masklength) {
cc10001_adc_start(adc_dev, channel);
data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns);
if (data[i] == CC10001_INVALID_SAMPLED) {
dev_warn(&indio_dev->dev,
"invalid sample on channel %d\n", channel);
sample_invalid = true;
goto done;
}
i++;
}
done:
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
mutex_unlock(&adc_dev->lock);
if (!sample_invalid)
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns());
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
unsigned int delay_ns;
u16 val;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
CC10001_ADC_POWER_UP_SET);
/* Wait for 8 (6+2) clock cycles before activating START */
ndelay(adc_dev->start_delay_ns);
/* Calculate delay step for eoc and sampled data */
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
cc10001_adc_start(adc_dev, chan->channel);
val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
return val;
}
static int cc10001_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
mutex_lock(&adc_dev->lock);
*val = cc10001_adc_read_raw_voltage(indio_dev, chan);
mutex_unlock(&adc_dev->lock);
if (*val == CC10001_INVALID_SAMPLED)
return -EIO;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(adc_dev->reg);
if (ret)
return ret;
*val = ret / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int cc10001_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
kfree(adc_dev->buf);
adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
if (!adc_dev->buf)
return -ENOMEM;
return 0;
}
static const struct iio_info cc10001_adc_info = {
.driver_module = THIS_MODULE,
.read_raw = &cc10001_adc_read_raw,
.update_scan_mode = &cc10001_update_scan_mode,
};
static int cc10001_adc_channel_init(struct iio_dev *indio_dev)
{
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
struct iio_chan_spec *chan_array, *timestamp;
unsigned int bit, idx = 0;
indio_dev->num_channels = bitmap_weight(&adc_dev->channel_map,
CC10001_ADC_NUM_CHANNELS);
chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels + 1,
sizeof(struct iio_chan_spec),
GFP_KERNEL);
if (!chan_array)
return -ENOMEM;
for_each_set_bit(bit, &adc_dev->channel_map, CC10001_ADC_NUM_CHANNELS) {
struct iio_chan_spec *chan = &chan_array[idx];
chan->type = IIO_VOLTAGE;
chan->indexed = 1;
chan->channel = bit;
chan->scan_index = idx;
chan->scan_type.sign = 'u';
chan->scan_type.realbits = 10;
chan->scan_type.storagebits = 16;
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
idx++;
}
timestamp = &chan_array[idx];
timestamp->type = IIO_TIMESTAMP;
timestamp->channel = -1;
timestamp->scan_index = idx;
timestamp->scan_type.sign = 's';
timestamp->scan_type.realbits = 64;
timestamp->scan_type.storagebits = 64;
indio_dev->channels = chan_array;
return 0;
}
static int cc10001_adc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct cc10001_adc_device *adc_dev;
unsigned long adc_clk_rate;
struct resource *res;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
if (indio_dev == NULL)
return -ENOMEM;
adc_dev = iio_priv(indio_dev);
adc_dev->channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
if (!of_property_read_u32(node, "adc-reserved-channels", &ret))
adc_dev->channel_map &= ~ret;
adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(adc_dev->reg))
return PTR_ERR(adc_dev->reg);
ret = regulator_enable(adc_dev->reg);
if (ret)
return ret;
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->info = &cc10001_adc_info;
indio_dev->modes = INDIO_DIRECT_MODE;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(adc_dev->reg_base)) {
ret = PTR_ERR(adc_dev->reg_base);
goto err_disable_reg;
}
adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(adc_dev->adc_clk)) {
dev_err(&pdev->dev, "failed to get the clock\n");
ret = PTR_ERR(adc_dev->adc_clk);
goto err_disable_reg;
}
ret = clk_prepare_enable(adc_dev->adc_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable the clock\n");
goto err_disable_reg;
}
adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
if (!adc_clk_rate) {
ret = -EINVAL;
dev_err(&pdev->dev, "null clock rate!\n");
goto err_disable_clk;
}
adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
/* Setup the ADC channels available on the device */
ret = cc10001_adc_channel_init(indio_dev);
if (ret < 0)
goto err_disable_clk;
mutex_init(&adc_dev->lock);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
&cc10001_adc_trigger_h, NULL);
if (ret < 0)
goto err_disable_clk;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto err_cleanup_buffer;
platform_set_drvdata(pdev, indio_dev);
return 0;
err_cleanup_buffer:
iio_triggered_buffer_cleanup(indio_dev);
err_disable_clk:
clk_disable_unprepare(adc_dev->adc_clk);
err_disable_reg:
regulator_disable(adc_dev->reg);
return ret;
}
static int cc10001_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
clk_disable_unprepare(adc_dev->adc_clk);
regulator_disable(adc_dev->reg);
return 0;
}
static const struct of_device_id cc10001_adc_dt_ids[] = {
{ .compatible = "cosmic,10001-adc", },
{ }
};
MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids);
static struct platform_driver cc10001_adc_driver = {
.driver = {
.name = "cc10001-adc",
.of_match_table = cc10001_adc_dt_ids,
},
.probe = cc10001_adc_probe,
.remove = cc10001_adc_remove,
};
module_platform_driver(cc10001_adc_driver);
MODULE_AUTHOR("Phani Movva <Phani.Movva@imgtec.com>");
MODULE_DESCRIPTION("Cosmic Circuits ADC driver");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load Diff

View File

@ -249,7 +249,7 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
struct iio_buffer *buffer;
int ret;
buffer = iio_kfifo_allocate(indio_dev);
buffer = iio_kfifo_allocate();
if (!buffer)
return -ENOMEM;
@ -263,16 +263,8 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
indio_dev->setup_ops = setup_ops;
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
ret = iio_buffer_register(indio_dev,
indio_dev->channels,
indio_dev->num_channels);
if (ret)
goto error_free_irq;
return 0;
error_free_irq:
free_irq(irq, indio_dev);
error_kfifo_free:
iio_kfifo_free(indio_dev->buffer);
return ret;
@ -284,7 +276,6 @@ static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev)
free_irq(adc_dev->mfd_tscadc->irq, indio_dev);
iio_kfifo_free(indio_dev->buffer);
iio_buffer_unregister(indio_dev);
}

View File

@ -31,7 +31,7 @@ struct ad8366_state {
};
static int ad8366_write(struct iio_dev *indio_dev,
unsigned char ch_a, char unsigned ch_b)
unsigned char ch_a, unsigned char ch_b)
{
struct ad8366_state *st = iio_priv(indio_dev);
int ret;
@ -166,7 +166,7 @@ static int ad8366_probe(struct spi_device *spi)
if (ret)
goto error_disable_reg;
ad8366_write(indio_dev, 0 , 0);
ad8366_write(indio_dev, 0, 0);
return 0;

View File

@ -3,4 +3,5 @@
#
source "drivers/iio/common/hid-sensors/Kconfig"
source "drivers/iio/common/ssp_sensors/Kconfig"
source "drivers/iio/common/st_sensors/Kconfig"

View File

@ -8,4 +8,5 @@
# When adding new entries keep the list in alphabetical order
obj-y += hid-sensors/
obj-y += ssp_sensors/
obj-y += st_sensors/

View File

@ -22,16 +22,18 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/sysfs.h>
#include "hid-sensor-trigger.h"
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
{
int state_val;
int report_val;
s32 poll_value = 0;
if (state) {
if (sensor_hub_device_open(st->hsdev))
@ -47,6 +49,8 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
poll_value = hid_sensor_read_poll_value(st);
} else {
if (!atomic_dec_and_test(&st->data_ready))
return 0;
@ -78,10 +82,36 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
&state_val);
if (state && poll_value)
msleep_interruptible(poll_value * 2);
return 0;
}
EXPORT_SYMBOL(hid_sensor_power_state);
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
{
#ifdef CONFIG_PM
int ret;
if (state)
ret = pm_runtime_get_sync(&st->pdev->dev);
else {
pm_runtime_mark_last_busy(&st->pdev->dev);
ret = pm_runtime_put_autosuspend(&st->pdev->dev);
}
if (ret < 0) {
if (state)
pm_runtime_put_noidle(&st->pdev->dev);
return ret;
}
return 0;
#else
return _hid_sensor_power_state(st, state);
#endif
}
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
@ -125,8 +155,21 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
attrb->trigger = trig;
indio_dev->trig = iio_trigger_get(trig);
return ret;
ret = pm_runtime_set_active(&indio_dev->dev);
if (ret)
goto error_unreg_trigger;
iio_device_set_drvdata(indio_dev, attrb);
pm_suspend_ignore_children(&attrb->pdev->dev, true);
pm_runtime_enable(&attrb->pdev->dev);
/* Default to 3 seconds, but can be changed from sysfs */
pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
3000);
pm_runtime_use_autosuspend(&attrb->pdev->dev);
return ret;
error_unreg_trigger:
iio_trigger_unregister(trig);
error_free_trig:
iio_trigger_free(trig);
error_ret:
@ -134,6 +177,34 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
}
EXPORT_SYMBOL(hid_sensor_setup_trigger);
#ifdef CONFIG_PM
static int hid_sensor_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
return _hid_sensor_power_state(attrb, false);
}
static int hid_sensor_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
return _hid_sensor_power_state(attrb, true);
}
#endif
const struct dev_pm_ops hid_sensor_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
SET_RUNTIME_PM_OPS(hid_sensor_suspend,
hid_sensor_resume, NULL)
};
EXPORT_SYMBOL(hid_sensor_pm_ops);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
MODULE_DESCRIPTION("HID Sensor trigger processing");
MODULE_LICENSE("GPL");

View File

@ -19,6 +19,11 @@
#ifndef _HID_SENSOR_TRIGGER_H
#define _HID_SENSOR_TRIGGER_H
#include <linux/pm.h>
#include <linux/pm_runtime.h>
extern const struct dev_pm_ops hid_sensor_pm_ops;
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
struct hid_sensor_common *attrb);
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb);

View File

@ -0,0 +1,26 @@
#
# SSP sensor drivers and commons configuration
#
menu "SSP Sensor Common"
config IIO_SSP_SENSORS_COMMONS
tristate "Commons for all SSP Sensor IIO drivers"
depends on IIO_SSP_SENSORHUB
select IIO_BUFFER
select IIO_KFIFO_BUF
help
Say yes here to build commons for SSP sensors.
To compile this as a module, choose M here: the module
will be called ssp_iio.
config IIO_SSP_SENSORHUB
tristate "Samsung Sensorhub driver"
depends on SPI
select MFD_CORE
help
SSP driver for sensorhub.
If you say yes here you get ssp support for sensorhub.
To compile this driver as a module, choose M here: the
module will be called sensorhub.
endmenu

View File

@ -0,0 +1,8 @@
#
# Makefile for SSP sensor drivers and commons.
#
sensorhub-objs := ssp_dev.o ssp_spi.o
obj-$(CONFIG_IIO_SSP_SENSORHUB) += sensorhub.o
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_iio.o

View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SSP_SENSORHUB_H__
#define __SSP_SENSORHUB_H__
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/iio.h>
#include <linux/spi/spi.h>
#define SSP_DEVICE_ID 0x55
#ifdef SSP_DBG
#define ssp_dbg(format, ...) pr_info("[SSP] "format, ##__VA_ARGS__)
#else
#define ssp_dbg(format, ...)
#endif
#define SSP_SW_RESET_TIME 3000
/* Sensor polling in ms */
#define SSP_DEFAULT_POLLING_DELAY 200
#define SSP_DEFAULT_RETRIES 3
#define SSP_DATA_PACKET_SIZE 960
#define SSP_HEADER_BUFFER_SIZE 4
enum {
SSP_KERNEL_BINARY = 0,
SSP_KERNEL_CRASHED_BINARY,
};
enum {
SSP_INITIALIZATION_STATE = 0,
SSP_NO_SENSOR_STATE,
SSP_ADD_SENSOR_STATE,
SSP_RUNNING_SENSOR_STATE,
};
/* Firmware download STATE */
enum {
SSP_FW_DL_STATE_FAIL = -1,
SSP_FW_DL_STATE_NONE = 0,
SSP_FW_DL_STATE_NEED_TO_SCHEDULE,
SSP_FW_DL_STATE_SCHEDULED,
SSP_FW_DL_STATE_DOWNLOADING,
SSP_FW_DL_STATE_SYNC,
SSP_FW_DL_STATE_DONE,
};
#define SSP_INVALID_REVISION 99999
#define SSP_INVALID_REVISION2 0xffffff
/* AP -> SSP Instruction */
#define SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD 0xa1
#define SSP_MSG2SSP_INST_BYPASS_SENSOR_RM 0xa2
#define SSP_MSG2SSP_INST_REMOVE_ALL 0xa3
#define SSP_MSG2SSP_INST_CHANGE_DELAY 0xa4
#define SSP_MSG2SSP_INST_LIBRARY_ADD 0xb1
#define SSP_MSG2SSP_INST_LIBRARY_REMOVE 0xb2
#define SSP_MSG2SSP_INST_LIB_NOTI 0xb4
#define SSP_MSG2SSP_INST_LIB_DATA 0xc1
#define SSP_MSG2SSP_AP_MCU_SET_GYRO_CAL 0xcd
#define SSP_MSG2SSP_AP_MCU_SET_ACCEL_CAL 0xce
#define SSP_MSG2SSP_AP_STATUS_SHUTDOWN 0xd0
#define SSP_MSG2SSP_AP_STATUS_WAKEUP 0xd1
#define SSP_MSG2SSP_AP_STATUS_SLEEP 0xd2
#define SSP_MSG2SSP_AP_STATUS_RESUME 0xd3
#define SSP_MSG2SSP_AP_STATUS_SUSPEND 0xd4
#define SSP_MSG2SSP_AP_STATUS_RESET 0xd5
#define SSP_MSG2SSP_AP_STATUS_POW_CONNECTED 0xd6
#define SSP_MSG2SSP_AP_STATUS_POW_DISCONNECTED 0xd7
#define SSP_MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE 0xda
#define SSP_MSG2SSP_AP_MCU_SET_DUMPMODE 0xdb
#define SSP_MSG2SSP_AP_MCU_DUMP_CHECK 0xdc
#define SSP_MSG2SSP_AP_MCU_BATCH_FLUSH 0xdd
#define SSP_MSG2SSP_AP_MCU_BATCH_COUNT 0xdf
#define SSP_MSG2SSP_AP_WHOAMI 0x0f
#define SSP_MSG2SSP_AP_FIRMWARE_REV 0xf0
#define SSP_MSG2SSP_AP_SENSOR_FORMATION 0xf1
#define SSP_MSG2SSP_AP_SENSOR_PROXTHRESHOLD 0xf2
#define SSP_MSG2SSP_AP_SENSOR_BARCODE_EMUL 0xf3
#define SSP_MSG2SSP_AP_SENSOR_SCANNING 0xf4
#define SSP_MSG2SSP_AP_SET_MAGNETIC_HWOFFSET 0xf5
#define SSP_MSG2SSP_AP_GET_MAGNETIC_HWOFFSET 0xf6
#define SSP_MSG2SSP_AP_SENSOR_GESTURE_CURRENT 0xf7
#define SSP_MSG2SSP_AP_GET_THERM 0xf8
#define SSP_MSG2SSP_AP_GET_BIG_DATA 0xf9
#define SSP_MSG2SSP_AP_SET_BIG_DATA 0xfa
#define SSP_MSG2SSP_AP_START_BIG_DATA 0xfb
#define SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX 0xfd
#define SSP_MSG2SSP_AP_SENSOR_TILT 0xea
#define SSP_MSG2SSP_AP_MCU_SET_TIME 0xfe
#define SSP_MSG2SSP_AP_MCU_GET_TIME 0xff
#define SSP_MSG2SSP_AP_FUSEROM 0x01
/* voice data */
#define SSP_TYPE_WAKE_UP_VOICE_SERVICE 0x01
#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM 0x01
#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER 0x02
/* Factory Test */
#define SSP_ACCELEROMETER_FACTORY 0x80
#define SSP_GYROSCOPE_FACTORY 0x81
#define SSP_GEOMAGNETIC_FACTORY 0x82
#define SSP_PRESSURE_FACTORY 0x85
#define SSP_GESTURE_FACTORY 0x86
#define SSP_TEMPHUMIDITY_CRC_FACTORY 0x88
#define SSP_GYROSCOPE_TEMP_FACTORY 0x8a
#define SSP_GYROSCOPE_DPS_FACTORY 0x8b
#define SSP_MCU_FACTORY 0x8c
#define SSP_MCU_SLEEP_FACTORY 0x8d
/* SSP -> AP ACK about write CMD */
#define SSP_MSG_ACK 0x80 /* ACK from SSP to AP */
#define SSP_MSG_NAK 0x70 /* NAK from SSP to AP */
struct ssp_sensorhub_info {
char *fw_name;
char *fw_crashed_name;
unsigned int fw_rev;
const u8 * const mag_table;
const unsigned int mag_length;
};
/* ssp_msg options bit */
#define SSP_RW 0
#define SSP_INDEX 3
#define SSP_AP2HUB_READ 0
#define SSP_AP2HUB_WRITE 1
#define SSP_HUB2AP_WRITE 2
#define SSP_AP2HUB_READY 3
#define SSP_AP2HUB_RETURN 4
/**
* struct ssp_data - ssp platformdata structure
* @spi: spi device
* @sensorhub_info: info about sensorhub board specific features
* @wdt_timer: watchdog timer
* @work_wdt: watchdog work
* @work_firmware: firmware upgrade work queue
* @work_refresh: refresh work queue for reset request from MCU
* @shut_down: shut down flag
* @mcu_dump_mode: mcu dump mode for debug
* @time_syncing: time syncing indication flag
* @timestamp: previous time in ns calculated for time syncing
* @check_status: status table for each sensor
* @com_fail_cnt: communication fail count
* @reset_cnt: reset count
* @timeout_cnt: timeout count
* @available_sensors: available sensors seen by sensorhub (bit array)
* @cur_firm_rev: cached current firmware revision
* @last_resume_state: last AP resume/suspend state used to handle the PM
* state of ssp
* @last_ap_state: (obsolete) sleep notification for MCU
* @sensor_enable: sensor enable mask
* @delay_buf: data acquisition intervals table
* @batch_latency_buf: yet unknown but existing in communication protocol
* @batch_opt_buf: yet unknown but existing in communication protocol
* @accel_position: yet unknown but existing in communication protocol
* @mag_position: yet unknown but existing in communication protocol
* @fw_dl_state: firmware download state
* @comm_lock: lock protecting the handshake
* @pending_lock: lock protecting pending list and completion
* @mcu_reset_gpio: mcu reset line
* @ap_mcu_gpio: ap to mcu gpio line
* @mcu_ap_gpio: mcu to ap gpio line
* @pending_list: pending list for messages queued to be sent/read
* @sensor_devs: registered IIO devices table
* @enable_refcount: enable reference count for wdt (watchdog timer)
* @header_buffer: cache aligned buffer for packet header
*/
struct ssp_data {
struct spi_device *spi;
struct ssp_sensorhub_info *sensorhub_info;
struct timer_list wdt_timer;
struct work_struct work_wdt;
struct delayed_work work_refresh;
bool shut_down;
bool mcu_dump_mode;
bool time_syncing;
int64_t timestamp;
int check_status[SSP_SENSOR_MAX];
unsigned int com_fail_cnt;
unsigned int reset_cnt;
unsigned int timeout_cnt;
unsigned int available_sensors;
unsigned int cur_firm_rev;
char last_resume_state;
char last_ap_state;
unsigned int sensor_enable;
u32 delay_buf[SSP_SENSOR_MAX];
s32 batch_latency_buf[SSP_SENSOR_MAX];
s8 batch_opt_buf[SSP_SENSOR_MAX];
int accel_position;
int mag_position;
int fw_dl_state;
struct mutex comm_lock;
struct mutex pending_lock;
int mcu_reset_gpio;
int ap_mcu_gpio;
int mcu_ap_gpio;
struct list_head pending_list;
struct iio_dev *sensor_devs[SSP_SENSOR_MAX];
atomic_t enable_refcount;
__le16 header_buffer[SSP_HEADER_BUFFER_SIZE / sizeof(__le16)]
____cacheline_aligned;
};
void ssp_clean_pending_list(struct ssp_data *data);
int ssp_command(struct ssp_data *data, char command, int arg);
int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
u8 *send_buf, u8 length);
int ssp_irq_msg(struct ssp_data *data);
int ssp_get_chipid(struct ssp_data *data);
int ssp_set_magnetic_matrix(struct ssp_data *data);
unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data);
unsigned int ssp_get_firmware_rev(struct ssp_data *data);
int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay);
#endif /* __SSP_SENSORHUB_H__ */

View File

@ -0,0 +1,712 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include "ssp.h"
#define SSP_WDT_TIME 10000
#define SSP_LIMIT_RESET_CNT 20
#define SSP_LIMIT_TIMEOUT_CNT 3
/* It is possible that it is max clk rate for version 1.0 of bootcode */
#define SSP_BOOT_SPI_HZ 400000
/*
* These fields can look enigmatic but this structure is used mainly to flat
* some values and depends on command type.
*/
struct ssp_instruction {
__le32 a;
__le32 b;
u8 c;
} __attribute__((__packed__));
static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67,
208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171,
243, 13, 45, 250};
static const struct ssp_sensorhub_info ssp_rinato_info = {
.fw_name = "ssp_B2.fw",
.fw_crashed_name = "ssp_crashed.fw",
.fw_rev = 14052300,
.mag_table = ssp_magnitude_table,
.mag_length = ARRAY_SIZE(ssp_magnitude_table),
};
static const struct ssp_sensorhub_info ssp_thermostat_info = {
.fw_name = "thermostat_B2.fw",
.fw_crashed_name = "ssp_crashed.fw",
.fw_rev = 14080600,
.mag_table = ssp_magnitude_table,
.mag_length = ARRAY_SIZE(ssp_magnitude_table),
};
static const struct mfd_cell sensorhub_sensor_devs[] = {
{
.name = "ssp-accelerometer",
},
{
.name = "ssp-gyroscope",
},
};
static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)
{
gpio_set_value(data->mcu_reset_gpio, 0);
usleep_range(1000, 1200);
gpio_set_value(data->mcu_reset_gpio, 1);
msleep(50);
}
static void ssp_sync_available_sensors(struct ssp_data *data)
{
int i, ret;
for (i = 0; i < SSP_SENSOR_MAX; ++i) {
if (data->available_sensors & BIT(i)) {
ret = ssp_enable_sensor(data, i, data->delay_buf[i]);
if (ret < 0) {
dev_err(&data->spi->dev,
"Sync sensor nr: %d fail\n", i);
continue;
}
}
}
ret = ssp_command(data, SSP_MSG2SSP_AP_MCU_SET_DUMPMODE,
data->mcu_dump_mode);
if (ret < 0)
dev_err(&data->spi->dev,
"SSP_MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");
}
static void ssp_enable_mcu(struct ssp_data *data, bool enable)
{
dev_info(&data->spi->dev, "current shutdown = %d, old = %d\n", enable,
data->shut_down);
if (enable && data->shut_down) {
data->shut_down = false;
enable_irq(data->spi->irq);
enable_irq_wake(data->spi->irq);
} else if (!enable && !data->shut_down) {
data->shut_down = true;
disable_irq(data->spi->irq);
disable_irq_wake(data->spi->irq);
} else {
dev_warn(&data->spi->dev, "current shutdown = %d, old = %d\n",
enable, data->shut_down);
}
}
/*
* This function is the first one which communicates with the mcu so it is
* possible that the first attempt will fail
*/
static int ssp_check_fwbl(struct ssp_data *data)
{
int retries = 0;
while (retries++ < 5) {
data->cur_firm_rev = ssp_get_firmware_rev(data);
if (data->cur_firm_rev == SSP_INVALID_REVISION ||
data->cur_firm_rev == SSP_INVALID_REVISION2) {
dev_warn(&data->spi->dev,
"Invalid revision, trying %d time\n", retries);
} else {
break;
}
}
if (data->cur_firm_rev == SSP_INVALID_REVISION ||
data->cur_firm_rev == SSP_INVALID_REVISION2) {
dev_err(&data->spi->dev, "SSP_INVALID_REVISION\n");
return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
}
dev_info(&data->spi->dev,
"MCU Firm Rev : Old = %8u, New = %8u\n",
data->cur_firm_rev,
data->sensorhub_info->fw_rev);
if (data->cur_firm_rev != data->sensorhub_info->fw_rev)
return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
return SSP_FW_DL_STATE_NONE;
}
static void ssp_reset_mcu(struct ssp_data *data)
{
ssp_enable_mcu(data, false);
ssp_clean_pending_list(data);
ssp_toggle_mcu_reset_gpio(data);
ssp_enable_mcu(data, true);
}
static void ssp_wdt_work_func(struct work_struct *work)
{
struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);
dev_err(&data->spi->dev, "%s - Sensor state: 0x%x, RC: %u, CC: %u\n",
__func__, data->available_sensors, data->reset_cnt,
data->com_fail_cnt);
ssp_reset_mcu(data);
data->com_fail_cnt = 0;
data->timeout_cnt = 0;
}
static void ssp_wdt_timer_func(unsigned long ptr)
{
struct ssp_data *data = (struct ssp_data *)ptr;
switch (data->fw_dl_state) {
case SSP_FW_DL_STATE_FAIL:
case SSP_FW_DL_STATE_DOWNLOADING:
case SSP_FW_DL_STATE_SYNC:
goto _mod;
}
if (data->timeout_cnt > SSP_LIMIT_TIMEOUT_CNT ||
data->com_fail_cnt > SSP_LIMIT_RESET_CNT)
queue_work(system_power_efficient_wq, &data->work_wdt);
_mod:
mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
}
static void ssp_enable_wdt_timer(struct ssp_data *data)
{
mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
}
static void ssp_disable_wdt_timer(struct ssp_data *data)
{
del_timer_sync(&data->wdt_timer);
cancel_work_sync(&data->work_wdt);
}
/**
* ssp_get_sensor_delay() - gets sensor data acquisition period
* @data: sensorhub structure
* @type: SSP sensor type
*
* Returns acquisition period in ms
*/
u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)
{
return data->delay_buf[type];
}
EXPORT_SYMBOL(ssp_get_sensor_delay);
/**
* ssp_enable_sensor() - enables data acquisition for sensor
* @data: sensorhub structure
* @type: SSP sensor type
* @delay: delay in ms
*
* Returns 0 or negative value in case of error
*/
int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
u32 delay)
{
int ret;
struct ssp_instruction to_send;
to_send.a = cpu_to_le32(delay);
to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
to_send.c = data->batch_opt_buf[type];
switch (data->check_status[type]) {
case SSP_INITIALIZATION_STATE:
/* do calibration step, now just enable */
case SSP_ADD_SENSOR_STATE:
ret = ssp_send_instruction(data,
SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD,
type,
(u8 *)&to_send, sizeof(to_send));
if (ret < 0) {
dev_err(&data->spi->dev, "Enabling sensor failed\n");
data->check_status[type] = SSP_NO_SENSOR_STATE;
goto derror;
}
data->sensor_enable |= BIT(type);
data->check_status[type] = SSP_RUNNING_SENSOR_STATE;
break;
case SSP_RUNNING_SENSOR_STATE:
ret = ssp_send_instruction(data,
SSP_MSG2SSP_INST_CHANGE_DELAY, type,
(u8 *)&to_send, sizeof(to_send));
if (ret < 0) {
dev_err(&data->spi->dev,
"Changing sensor delay failed\n");
goto derror;
}
break;
default:
data->check_status[type] = SSP_ADD_SENSOR_STATE;
break;
}
data->delay_buf[type] = delay;
if (atomic_inc_return(&data->enable_refcount) == 1)
ssp_enable_wdt_timer(data);
return 0;
derror:
return ret;
}
EXPORT_SYMBOL(ssp_enable_sensor);
/**
* ssp_change_delay() - changes data acquisition for sensor
* @data: sensorhub structure
* @type: SSP sensor type
* @delay: delay in ms
*
* Returns 0 or negative value in case of error
*/
int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
u32 delay)
{
int ret;
struct ssp_instruction to_send;
to_send.a = cpu_to_le32(delay);
to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
to_send.c = data->batch_opt_buf[type];
ret = ssp_send_instruction(data, SSP_MSG2SSP_INST_CHANGE_DELAY, type,
(u8 *)&to_send, sizeof(to_send));
if (ret < 0) {
dev_err(&data->spi->dev, "Changing sensor delay failed\n");
return ret;
}
data->delay_buf[type] = delay;
return 0;
}
EXPORT_SYMBOL(ssp_change_delay);
/**
* ssp_disable_sensor() - disables sensor
*
* @data: sensorhub structure
* @type: SSP sensor type
*
* Returns 0 or negative value in case of error
*/
int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
{
int ret;
__le32 command;
if (data->sensor_enable & BIT(type)) {
command = cpu_to_le32(data->delay_buf[type]);
ret = ssp_send_instruction(data,
SSP_MSG2SSP_INST_BYPASS_SENSOR_RM,
type, (u8 *)&command,
sizeof(command));
if (ret < 0) {
dev_err(&data->spi->dev, "Remove sensor fail\n");
return ret;
}
data->sensor_enable &= ~BIT(type);
}
data->check_status[type] = SSP_ADD_SENSOR_STATE;
if (atomic_dec_and_test(&data->enable_refcount))
ssp_disable_wdt_timer(data);
return 0;
}
EXPORT_SYMBOL(ssp_disable_sensor);
static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)
{
struct ssp_data *data = dev_id;
/*
* This wrapper is done to preserve error path for ssp_irq_msg, also
* it is defined in different file.
*/
ssp_irq_msg(data);
return IRQ_HANDLED;
}
static int ssp_initialize_mcu(struct ssp_data *data)
{
int ret;
ssp_clean_pending_list(data);
ret = ssp_get_chipid(data);
if (ret != SSP_DEVICE_ID) {
dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,
ret < 0 ? "is not working" : "identification failed",
ret);
return ret < 0 ? ret : -ENODEV;
}
dev_info(&data->spi->dev, "MCU device ID = %d\n", ret);
/*
* needs clarification, for now do not want to export all transfer
* methods to sensors' drivers
*/
ret = ssp_set_magnetic_matrix(data);
if (ret < 0) {
dev_err(&data->spi->dev,
"%s - ssp_set_magnetic_matrix failed\n", __func__);
return ret;
}
data->available_sensors = ssp_get_sensor_scanning_info(data);
if (data->available_sensors == 0) {
dev_err(&data->spi->dev,
"%s - ssp_get_sensor_scanning_info failed\n", __func__);
return -EIO;
}
data->cur_firm_rev = ssp_get_firmware_rev(data);
dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",
data->cur_firm_rev);
return ssp_command(data, SSP_MSG2SSP_AP_MCU_DUMP_CHECK, 0);
}
/*
* sensorhub can request its reinitialization as some brutal and rare error
* handling. It can be requested from the MCU.
*/
static void ssp_refresh_task(struct work_struct *work)
{
struct ssp_data *data = container_of((struct delayed_work *)work,
struct ssp_data, work_refresh);
dev_info(&data->spi->dev, "refreshing\n");
data->reset_cnt++;
if (ssp_initialize_mcu(data) >= 0) {
ssp_sync_available_sensors(data);
if (data->last_ap_state != 0)
ssp_command(data, data->last_ap_state, 0);
if (data->last_resume_state != 0)
ssp_command(data, data->last_resume_state, 0);
data->timeout_cnt = 0;
data->com_fail_cnt = 0;
}
}
int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
{
cancel_delayed_work_sync(&data->work_refresh);
return queue_delayed_work(system_power_efficient_wq,
&data->work_refresh,
msecs_to_jiffies(delay));
}
#ifdef CONFIG_OF
static struct of_device_id ssp_of_match[] = {
{
.compatible = "samsung,sensorhub-rinato",
.data = &ssp_rinato_info,
}, {
.compatible = "samsung,sensorhub-thermostat",
.data = &ssp_thermostat_info,
},
{},
};
MODULE_DEVICE_TABLE(of, ssp_of_match);
static struct ssp_data *ssp_parse_dt(struct device *dev)
{
int ret;
struct ssp_data *data;
struct device_node *node = dev->of_node;
const struct of_device_id *match;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return NULL;
data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
if (data->mcu_ap_gpio < 0)
goto err_free_pd;
data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
if (data->ap_mcu_gpio < 0)
goto err_free_pd;
data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
if (data->mcu_reset_gpio < 0)
goto err_free_pd;
ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
"ap-mcu-gpios");
if (ret)
goto err_free_pd;
ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
if (ret)
goto err_ap_mcu;
match = of_match_node(ssp_of_match, node);
if (!match)
goto err_mcu_reset_gpio;
data->sensorhub_info = (struct ssp_sensorhub_info *)match->data;
dev_set_drvdata(dev, data);
return data;
err_mcu_reset_gpio:
devm_gpio_free(dev, data->mcu_reset_gpio);
err_ap_mcu:
devm_gpio_free(dev, data->ap_mcu_gpio);
err_free_pd:
devm_kfree(dev, data);
return NULL;
}
#else
static struct ssp_data *ssp_parse_dt(struct device *pdev)
{
return NULL;
}
#endif
/**
* ssp_register_consumer() - registers iio consumer in ssp framework
*
* @indio_dev: consumer iio device
* @type: ssp sensor type
*/
void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type)
{
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
data->sensor_devs[type] = indio_dev;
}
EXPORT_SYMBOL(ssp_register_consumer);
static int ssp_probe(struct spi_device *spi)
{
int ret, i;
struct ssp_data *data;
data = ssp_parse_dt(&spi->dev);
if (!data) {
dev_err(&spi->dev, "Failed to find platform data\n");
return -ENODEV;
}
ret = mfd_add_devices(&spi->dev, -1, sensorhub_sensor_devs,
ARRAY_SIZE(sensorhub_sensor_devs), NULL, 0, NULL);
if (ret < 0) {
dev_err(&spi->dev, "mfd add devices fail\n");
return ret;
}
spi->mode = SPI_MODE_1;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "Failed to setup spi\n");
return ret;
}
data->fw_dl_state = SSP_FW_DL_STATE_NONE;
data->spi = spi;
spi_set_drvdata(spi, data);
mutex_init(&data->comm_lock);
for (i = 0; i < SSP_SENSOR_MAX; ++i) {
data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;
data->batch_latency_buf[i] = 0;
data->batch_opt_buf[i] = 0;
data->check_status[i] = SSP_INITIALIZATION_STATE;
}
data->delay_buf[SSP_BIO_HRM_LIB] = 100;
data->time_syncing = true;
mutex_init(&data->pending_lock);
INIT_LIST_HEAD(&data->pending_list);
atomic_set(&data->enable_refcount, 0);
INIT_WORK(&data->work_wdt, ssp_wdt_work_func);
INIT_DELAYED_WORK(&data->work_refresh, ssp_refresh_task);
setup_timer(&data->wdt_timer, ssp_wdt_timer_func, (unsigned long)data);
ret = request_threaded_irq(data->spi->irq, NULL,
ssp_irq_thread_fn,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"SSP_Int", data);
if (ret < 0) {
dev_err(&spi->dev, "Irq request fail\n");
goto err_setup_irq;
}
/* Let's start with enabled one so irq balance could be ok */
data->shut_down = false;
/* just to avoid unbalanced irq set wake up */
enable_irq_wake(data->spi->irq);
data->fw_dl_state = ssp_check_fwbl(data);
if (data->fw_dl_state == SSP_FW_DL_STATE_NONE) {
ret = ssp_initialize_mcu(data);
if (ret < 0) {
dev_err(&spi->dev, "Initialize_mcu failed\n");
goto err_read_reg;
}
} else {
dev_err(&spi->dev, "Firmware version not supported\n");
ret = -EPERM;
goto err_read_reg;
}
return 0;
err_read_reg:
free_irq(data->spi->irq, data);
err_setup_irq:
mutex_destroy(&data->pending_lock);
mutex_destroy(&data->comm_lock);
dev_err(&spi->dev, "Probe failed!\n");
return ret;
}
static int ssp_remove(struct spi_device *spi)
{
struct ssp_data *data = spi_get_drvdata(spi);
if (ssp_command(data, SSP_MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
dev_err(&data->spi->dev,
"SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
ssp_enable_mcu(data, false);
ssp_disable_wdt_timer(data);
ssp_clean_pending_list(data);
free_irq(data->spi->irq, data);
del_timer_sync(&data->wdt_timer);
cancel_work_sync(&data->work_wdt);
mutex_destroy(&data->comm_lock);
mutex_destroy(&data->pending_lock);
mfd_remove_devices(&spi->dev);
return 0;
}
static int ssp_suspend(struct device *dev)
{
int ret;
struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
data->last_resume_state = SSP_MSG2SSP_AP_STATUS_SUSPEND;
if (atomic_read(&data->enable_refcount) > 0)
ssp_disable_wdt_timer(data);
ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_SUSPEND, 0);
if (ret < 0) {
dev_err(&data->spi->dev,
"%s SSP_MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);
ssp_enable_wdt_timer(data);
return ret;
}
data->time_syncing = false;
disable_irq(data->spi->irq);
return 0;
}
static int ssp_resume(struct device *dev)
{
int ret;
struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
enable_irq(data->spi->irq);
if (atomic_read(&data->enable_refcount) > 0)
ssp_enable_wdt_timer(data);
ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_RESUME, 0);
if (ret < 0) {
dev_err(&data->spi->dev,
"%s SSP_MSG2SSP_AP_STATUS_RESUME failed\n", __func__);
ssp_disable_wdt_timer(data);
return ret;
}
/* timesyncing is set by MCU */
data->last_resume_state = SSP_MSG2SSP_AP_STATUS_RESUME;
return 0;
}
static const struct dev_pm_ops ssp_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume)
};
static struct spi_driver ssp_driver = {
.probe = ssp_probe,
.remove = ssp_remove,
.driver = {
.pm = &ssp_pm_ops,
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ssp_of_match),
.name = "sensorhub"
},
};
module_spi_driver(ssp_driver);
MODULE_DESCRIPTION("ssp sensorhub driver");
MODULE_AUTHOR("Samsung Electronics");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,107 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "ssp_iio_sensor.h"
/**
* ssp_common_buffer_postenable() - generic postenable callback for ssp buffer
*
* @indio_dev: iio device
*
* Returns 0 or negative value in case of error
*/
int ssp_common_buffer_postenable(struct iio_dev *indio_dev)
{
struct ssp_sensor_data *spd = iio_priv(indio_dev);
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
/* the allocation is made in post because scan size is known in this
* moment
* */
spd->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL | GFP_DMA);
if (!spd->buffer)
return -ENOMEM;
return ssp_enable_sensor(data, spd->type,
ssp_get_sensor_delay(data, spd->type));
}
EXPORT_SYMBOL(ssp_common_buffer_postenable);
/**
* ssp_common_buffer_postdisable() - generic postdisable callback for ssp buffer
*
* @indio_dev: iio device
*
* Returns 0 or negative value in case of error
*/
int ssp_common_buffer_postdisable(struct iio_dev *indio_dev)
{
int ret;
struct ssp_sensor_data *spd = iio_priv(indio_dev);
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
ret = ssp_disable_sensor(data, spd->type);
if (ret < 0)
return ret;
kfree(spd->buffer);
return ret;
}
EXPORT_SYMBOL(ssp_common_buffer_postdisable);
/**
* ssp_common_process_data() - Common process data callback for ssp sensors
*
* @indio_dev: iio device
* @buf: source buffer
* @len: sensor data length
* @timestamp: system timestamp
*
* Returns 0 or negative value in case of error
*/
int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
unsigned int len, int64_t timestamp)
{
__le32 time;
int64_t calculated_time;
struct ssp_sensor_data *spd = iio_priv(indio_dev);
if (indio_dev->scan_bytes == 0)
return 0;
/*
* it always sends full set of samples, remember about available masks
*/
memcpy(spd->buffer, buf, len);
if (indio_dev->scan_timestamp) {
memcpy(&time, &((char *)buf)[len], SSP_TIME_SIZE);
calculated_time =
timestamp + (int64_t)le32_to_cpu(time) * 1000000;
}
return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer,
calculated_time);
}
EXPORT_SYMBOL(ssp_common_process_data);
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
MODULE_DESCRIPTION("Samsung sensorhub commons");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,71 @@
#ifndef __SSP_IIO_SENSOR_H__
#define __SSP_IIO_SENSOR_H__
#define SSP_CHANNEL_AG(_type, _mod, _index) \
{ \
.type = _type,\
.modified = 1,\
.channel2 = _mod,\
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
.scan_index = _index,\
.scan_type = {\
.sign = 's',\
.realbits = 16,\
.storagebits = 16,\
.shift = 0,\
.endianness = IIO_LE,\
},\
}
/* It is defined here as it is a mixed timestamp */
#define SSP_CHAN_TIMESTAMP(_si) { \
.type = IIO_TIMESTAMP, \
.channel = -1, \
.scan_index = _si, \
.scan_type = { \
.sign = 's', \
.realbits = 64, \
.storagebits = 64, \
}, \
}
#define SSP_MS_PER_S 1000
#define SSP_INVERTED_SCALING_FACTOR 1000000U
#define SSP_FACTOR_WITH_MS \
(SSP_INVERTED_SCALING_FACTOR * SSP_MS_PER_S)
int ssp_common_buffer_postenable(struct iio_dev *indio_dev);
int ssp_common_buffer_postdisable(struct iio_dev *indio_dev);
int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
unsigned int len, int64_t timestamp);
/* Converts time in ms to frequency */
static inline void ssp_convert_to_freq(u32 time, int *integer_part,
int *fractional)
{
if (time == 0) {
*fractional = 0;
*integer_part = 0;
return;
}
*integer_part = SSP_FACTOR_WITH_MS / time;
*fractional = *integer_part % SSP_INVERTED_SCALING_FACTOR;
*integer_part = *integer_part / SSP_INVERTED_SCALING_FACTOR;
}
/* Converts frequency to time in ms */
static inline int ssp_convert_to_time(int integer_part, int fractional)
{
u64 value;
value = (u64)integer_part * SSP_INVERTED_SCALING_FACTOR + fractional;
if (value == 0)
return 0;
return div64_u64((u64)SSP_FACTOR_WITH_MS, value);
}
#endif /* __SSP_IIO_SENSOR_H__ */

View File

@ -0,0 +1,608 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "ssp.h"
#define SSP_DEV (&data->spi->dev)
#define SSP_GET_MESSAGE_TYPE(data) (data & (3 << SSP_RW))
/*
* SSP -> AP Instruction
* They tell what packet type can be expected. In the future there will
* be less of them. BYPASS means common sensor packets with accel, gyro,
* hrm etc. data. LIBRARY and META are mock-up's for now.
*/
#define SSP_MSG2AP_INST_BYPASS_DATA 0x37
#define SSP_MSG2AP_INST_LIBRARY_DATA 0x01
#define SSP_MSG2AP_INST_DEBUG_DATA 0x03
#define SSP_MSG2AP_INST_BIG_DATA 0x04
#define SSP_MSG2AP_INST_META_DATA 0x05
#define SSP_MSG2AP_INST_TIME_SYNC 0x06
#define SSP_MSG2AP_INST_RESET 0x07
#define SSP_UNIMPLEMENTED -1
struct ssp_msg_header {
u8 cmd;
__le16 length;
__le16 options;
__le32 data;
} __attribute__((__packed__));
struct ssp_msg {
u16 length;
u16 options;
struct list_head list;
struct completion *done;
struct ssp_msg_header *h;
char *buffer;
};
static const int ssp_offset_map[SSP_SENSOR_MAX] = {
[SSP_ACCELEROMETER_SENSOR] = SSP_ACCELEROMETER_SIZE +
SSP_TIME_SIZE,
[SSP_GYROSCOPE_SENSOR] = SSP_GYROSCOPE_SIZE +
SSP_TIME_SIZE,
[SSP_GEOMAGNETIC_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GEOMAGNETIC_RAW] = SSP_UNIMPLEMENTED,
[SSP_GEOMAGNETIC_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_PRESSURE_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GESTURE_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_PROXIMITY_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_TEMPERATURE_HUMIDITY_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_LIGHT_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_PROXIMITY_RAW] = SSP_UNIMPLEMENTED,
[SSP_ORIENTATION_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_STEP_DETECTOR] = SSP_UNIMPLEMENTED,
[SSP_SIG_MOTION_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GYRO_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
[SSP_GAME_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
[SSP_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
[SSP_STEP_COUNTER] = SSP_UNIMPLEMENTED,
[SSP_BIO_HRM_RAW] = SSP_BIO_HRM_RAW_SIZE +
SSP_TIME_SIZE,
[SSP_BIO_HRM_RAW_FAC] = SSP_BIO_HRM_RAW_FAC_SIZE +
SSP_TIME_SIZE,
[SSP_BIO_HRM_LIB] = SSP_BIO_HRM_LIB_SIZE +
SSP_TIME_SIZE,
};
#define SSP_HEADER_SIZE (sizeof(struct ssp_msg_header))
#define SSP_HEADER_SIZE_ALIGNED (ALIGN(SSP_HEADER_SIZE, 4))
static struct ssp_msg *ssp_create_msg(u8 cmd, u16 len, u16 opt, u32 data)
{
struct ssp_msg_header h;
struct ssp_msg *msg;
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
if (!msg)
return NULL;
h.cmd = cmd;
h.length = cpu_to_le16(len);
h.options = cpu_to_le16(opt);
h.data = cpu_to_le32(data);
msg->buffer = kzalloc(SSP_HEADER_SIZE_ALIGNED + len,
GFP_KERNEL | GFP_DMA);
if (!msg->buffer) {
kfree(msg);
return NULL;
}
msg->length = len;
msg->options = opt;
memcpy(msg->buffer, &h, SSP_HEADER_SIZE);
return msg;
}
/*
* It is a bit heavy to do it this way but often the function is used to compose
* the message from smaller chunks which are placed on the stack. Often the
* chunks are small so memcpy should be optimalized.
*/
static inline void ssp_fill_buffer(struct ssp_msg *m, unsigned int offset,
const void *src, unsigned int len)
{
memcpy(&m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], src, len);
}
static inline void ssp_get_buffer(struct ssp_msg *m, unsigned int offset,
void *dest, unsigned int len)
{
memcpy(dest, &m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], len);
}
#define SSP_GET_BUFFER_AT_INDEX(m, index) \
(m->buffer[SSP_HEADER_SIZE_ALIGNED + index])
#define SSP_SET_BUFFER_AT_INDEX(m, index, val) \
(m->buffer[SSP_HEADER_SIZE_ALIGNED + index] = val)
static void ssp_clean_msg(struct ssp_msg *m)
{
kfree(m->buffer);
kfree(m);
}
static int ssp_print_mcu_debug(char *data_frame, int *data_index,
int received_len)
{
int length = data_frame[(*data_index)++];
if (length > received_len - *data_index || length <= 0) {
ssp_dbg("[SSP]: MSG From MCU-invalid debug length(%d/%d)\n",
length, received_len);
return length ? length : -EPROTO;
}
ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
*data_index += length;
return 0;
}
/*
* It was designed that way - additional lines to some kind of handshake,
* please do not ask why - only the firmware guy can know it.
*/
static int ssp_check_lines(struct ssp_data *data, bool state)
{
int delay_cnt = 0;
gpio_set_value_cansleep(data->ap_mcu_gpio, state);
while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) {
usleep_range(3000, 3500);
if (data->shut_down || delay_cnt++ > 500) {
dev_err(SSP_DEV, "%s:timeout, hw ack wait fail %d\n",
__func__, state);
if (!state)
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
return -ETIMEDOUT;
}
}
return 0;
}
static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg,
struct completion *done, int timeout)
{
int status;
/*
* check if this is a short one way message or the whole transfer has
* second part after an interrupt
*/
const bool use_no_irq = msg->length == 0;
if (data->shut_down)
return -EPERM;
msg->done = done;
mutex_lock(&data->comm_lock);
status = ssp_check_lines(data, false);
if (status < 0)
goto _error_locked;
status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
if (status < 0) {
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
goto _error_locked;
}
if (!use_no_irq) {
mutex_lock(&data->pending_lock);
list_add_tail(&msg->list, &data->pending_list);
mutex_unlock(&data->pending_lock);
}
status = ssp_check_lines(data, true);
if (status < 0) {
if (!use_no_irq) {
mutex_lock(&data->pending_lock);
list_del(&msg->list);
mutex_unlock(&data->pending_lock);
}
goto _error_locked;
}
mutex_unlock(&data->comm_lock);
if (!use_no_irq && done)
if (wait_for_completion_timeout(done,
msecs_to_jiffies(timeout)) ==
0) {
mutex_lock(&data->pending_lock);
list_del(&msg->list);
mutex_unlock(&data->pending_lock);
data->timeout_cnt++;
return -ETIMEDOUT;
}
return 0;
_error_locked:
mutex_unlock(&data->comm_lock);
data->timeout_cnt++;
return status;
}
static inline int ssp_spi_sync_command(struct ssp_data *data,
struct ssp_msg *msg)
{
return ssp_do_transfer(data, msg, NULL, 0);
}
static int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg,
int timeout)
{
DECLARE_COMPLETION_ONSTACK(done);
if (WARN_ON(!msg->length))
return -EPERM;
return ssp_do_transfer(data, msg, &done, timeout);
}
static int ssp_handle_big_data(struct ssp_data *data, char *dataframe, int *idx)
{
/* mock-up, it will be changed with adding another sensor types */
*idx += 8;
return 0;
}
static int ssp_parse_dataframe(struct ssp_data *data, char *dataframe, int len)
{
int idx, sd;
struct timespec ts;
struct ssp_sensor_data *spd;
struct iio_dev **indio_devs = data->sensor_devs;
getnstimeofday(&ts);
for (idx = 0; idx < len;) {
switch (dataframe[idx++]) {
case SSP_MSG2AP_INST_BYPASS_DATA:
sd = dataframe[idx++];
if (sd < 0 || sd >= SSP_SENSOR_MAX) {
dev_err(SSP_DEV,
"Mcu data frame1 error %d\n", sd);
return -EPROTO;
}
if (indio_devs[sd]) {
spd = iio_priv(indio_devs[sd]);
if (spd->process_data)
spd->process_data(indio_devs[sd],
&dataframe[idx],
data->timestamp);
} else {
dev_err(SSP_DEV, "no client for frame\n");
}
idx += ssp_offset_map[sd];
break;
case SSP_MSG2AP_INST_DEBUG_DATA:
sd = ssp_print_mcu_debug(dataframe, &idx, len);
if (sd) {
dev_err(SSP_DEV,
"Mcu data frame3 error %d\n", sd);
return sd;
}
break;
case SSP_MSG2AP_INST_LIBRARY_DATA:
idx += len;
break;
case SSP_MSG2AP_INST_BIG_DATA:
ssp_handle_big_data(data, dataframe, &idx);
break;
case SSP_MSG2AP_INST_TIME_SYNC:
data->time_syncing = true;
break;
case SSP_MSG2AP_INST_RESET:
ssp_queue_ssp_refresh_task(data, 0);
break;
}
}
if (data->time_syncing)
data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
return 0;
}
/* threaded irq */
int ssp_irq_msg(struct ssp_data *data)
{
bool found = false;
char *buffer;
u8 msg_type;
int ret;
u16 length, msg_options;
struct ssp_msg *msg, *n;
ret = spi_read(data->spi, data->header_buffer, SSP_HEADER_BUFFER_SIZE);
if (ret < 0) {
dev_err(SSP_DEV, "header read fail\n");
return ret;
}
length = le16_to_cpu(data->header_buffer[1]);
msg_options = le16_to_cpu(data->header_buffer[0]);
if (length == 0) {
dev_err(SSP_DEV, "length received from mcu is 0\n");
return -EINVAL;
}
msg_type = SSP_GET_MESSAGE_TYPE(msg_options);
switch (msg_type) {
case SSP_AP2HUB_READ:
case SSP_AP2HUB_WRITE:
/*
* this is a small list, a few elements - the packets can be
* received with no order
*/
mutex_lock(&data->pending_lock);
list_for_each_entry_safe(msg, n, &data->pending_list, list) {
if (msg->options == msg_options) {
list_del(&msg->list);
found = true;
break;
}
}
if (!found) {
/*
* here can be implemented dead messages handling
* but the slave should not send such ones - it is to
* check but let's handle this
*/
buffer = kmalloc(length, GFP_KERNEL | GFP_DMA);
if (!buffer) {
ret = -ENOMEM;
goto _unlock;
}
/* got dead packet so it is always an error */
ret = spi_read(data->spi, buffer, length);
if (ret >= 0)
ret = -EPROTO;
kfree(buffer);
dev_err(SSP_DEV, "No match error %x\n",
msg_options);
goto _unlock;
}
if (msg_type == SSP_AP2HUB_READ)
ret = spi_read(data->spi,
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
msg->length);
if (msg_type == SSP_AP2HUB_WRITE) {
ret = spi_write(data->spi,
&msg->buffer[SSP_HEADER_SIZE_ALIGNED],
msg->length);
if (msg_options & SSP_AP2HUB_RETURN) {
msg->options =
SSP_AP2HUB_READ | SSP_AP2HUB_RETURN;
msg->length = 1;
list_add_tail(&msg->list, &data->pending_list);
goto _unlock;
}
}
if (msg->done)
if (!completion_done(msg->done))
complete(msg->done);
_unlock:
mutex_unlock(&data->pending_lock);
break;
case SSP_HUB2AP_WRITE:
buffer = kzalloc(length, GFP_KERNEL | GFP_DMA);
if (!buffer)
return -ENOMEM;
ret = spi_read(data->spi, buffer, length);
if (ret < 0) {
dev_err(SSP_DEV, "spi read fail\n");
kfree(buffer);
break;
}
ret = ssp_parse_dataframe(data, buffer, length);
kfree(buffer);
break;
default:
dev_err(SSP_DEV, "unknown msg type\n");
return -EPROTO;
}
return ret;
}
void ssp_clean_pending_list(struct ssp_data *data)
{
struct ssp_msg *msg, *n;
mutex_lock(&data->pending_lock);
list_for_each_entry_safe(msg, n, &data->pending_list, list) {
list_del(&msg->list);
if (msg->done)
if (!completion_done(msg->done))
complete(msg->done);
}
mutex_unlock(&data->pending_lock);
}
int ssp_command(struct ssp_data *data, char command, int arg)
{
int ret;
struct ssp_msg *msg;
msg = ssp_create_msg(command, 0, SSP_AP2HUB_WRITE, arg);
if (!msg)
return -ENOMEM;
ssp_dbg("%s - command 0x%x %d\n", __func__, command, arg);
ret = ssp_spi_sync_command(data, msg);
ssp_clean_msg(msg);
return ret;
}
int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
u8 *send_buf, u8 length)
{
int ret;
struct ssp_msg *msg;
if (data->fw_dl_state == SSP_FW_DL_STATE_DOWNLOADING) {
dev_err(SSP_DEV, "%s - Skip Inst! DL state = %d\n",
__func__, data->fw_dl_state);
return -EBUSY;
} else if (!(data->available_sensors & BIT(sensor_type)) &&
(inst <= SSP_MSG2SSP_INST_CHANGE_DELAY)) {
dev_err(SSP_DEV, "%s - Bypass Inst Skip! - %u\n",
__func__, sensor_type);
return -EIO; /* just fail */
}
msg = ssp_create_msg(inst, length + 2, SSP_AP2HUB_WRITE, 0);
if (!msg)
return -ENOMEM;
ssp_fill_buffer(msg, 0, &sensor_type, 1);
ssp_fill_buffer(msg, 1, send_buf, length);
ssp_dbg("%s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
__func__, inst, sensor_type, send_buf[1]);
ret = ssp_spi_sync(data, msg, 1000);
ssp_clean_msg(msg);
return ret;
}
int ssp_get_chipid(struct ssp_data *data)
{
int ret;
char buffer;
struct ssp_msg *msg;
msg = ssp_create_msg(SSP_MSG2SSP_AP_WHOAMI, 1, SSP_AP2HUB_READ, 0);
if (!msg)
return -ENOMEM;
ret = ssp_spi_sync(data, msg, 1000);
buffer = SSP_GET_BUFFER_AT_INDEX(msg, 0);
ssp_clean_msg(msg);
return ret < 0 ? ret : buffer;
}
int ssp_set_magnetic_matrix(struct ssp_data *data)
{
int ret;
struct ssp_msg *msg;
msg = ssp_create_msg(SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX,
data->sensorhub_info->mag_length, SSP_AP2HUB_WRITE,
0);
if (!msg)
return -ENOMEM;
ssp_fill_buffer(msg, 0, data->sensorhub_info->mag_table,
data->sensorhub_info->mag_length);
ret = ssp_spi_sync(data, msg, 1000);
ssp_clean_msg(msg);
return ret;
}
unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data)
{
int ret;
__le32 result;
u32 cpu_result = 0;
struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_SENSOR_SCANNING, 4,
SSP_AP2HUB_READ, 0);
if (!msg)
return 0;
ret = ssp_spi_sync(data, msg, 1000);
if (ret < 0) {
dev_err(SSP_DEV, "%s - spi read fail %d\n", __func__, ret);
goto _exit;
}
ssp_get_buffer(msg, 0, &result, 4);
cpu_result = le32_to_cpu(result);
dev_info(SSP_DEV, "%s state: 0x%08x\n", __func__, cpu_result);
_exit:
ssp_clean_msg(msg);
return cpu_result;
}
unsigned int ssp_get_firmware_rev(struct ssp_data *data)
{
int ret;
__le32 result;
struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_FIRMWARE_REV, 4,
SSP_AP2HUB_READ, 0);
if (!msg)
return SSP_INVALID_REVISION;
ret = ssp_spi_sync(data, msg, 1000);
if (ret < 0) {
dev_err(SSP_DEV, "%s - transfer fail %d\n", __func__, ret);
ret = SSP_INVALID_REVISION;
goto _exit;
}
ssp_get_buffer(msg, 0, &result, 4);
ret = le32_to_cpu(result);
_exit:
ssp_clean_msg(msg);
return ret;
}

View File

@ -54,7 +54,7 @@ static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
if (err)
goto acc_spi_read_error;
memcpy(data, tb->rx_buf, len*sizeof(u8));
memcpy(data, tb->rx_buf, len);
mutex_unlock(&tb->buf_lock);
return len;

View File

@ -445,7 +445,7 @@ static int ad9523_store_eeprom(struct iio_dev *indio_dev)
tmp = 4;
do {
msleep(16);
msleep(20);
ret = ad9523_read(indio_dev,
AD9523_EEPROM_DATA_XFER_STATUS);
if (ret < 0)

View File

@ -387,10 +387,8 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
int ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "could not allocate memory for platform data\n");
if (!pdata)
return NULL;
}
strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1);
@ -613,9 +611,8 @@ static int adf4350_remove(struct spi_device *spi)
if (st->clk)
clk_disable_unprepare(st->clk);
if (!IS_ERR(reg)) {
if (!IS_ERR(reg))
regulator_disable(reg);
}
return 0;
}

View File

@ -16,6 +16,8 @@ itg3200-y := itg3200_core.o
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
obj-$(CONFIG_ITG3200) += itg3200.o
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_gyro_sensor.o
obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o
st_gyro-y := st_gyro_core.o
st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o

View File

@ -111,19 +111,12 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case 0:
poll_value = hid_sensor_read_poll_value(
&gyro_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&gyro_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id = gyro_state->gyro[chan->scan_index].report_id;
address = gyro_3d_addresses[chan->scan_index];
if (report_id >= 0)
@ -416,6 +409,7 @@ static struct platform_driver hid_gyro_3d_platform_driver = {
.id_table = hid_gyro_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_gyro_3d_probe,
.remove = hid_gyro_3d_remove,

View File

@ -0,0 +1,168 @@
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../common/ssp_sensors/ssp_iio_sensor.h"
#define SSP_CHANNEL_COUNT 3
#define SSP_GYROSCOPE_NAME "ssp-gyroscope"
static const char ssp_gyro_name[] = SSP_GYROSCOPE_NAME;
enum ssp_gyro_3d_channel {
SSP_CHANNEL_SCAN_INDEX_X,
SSP_CHANNEL_SCAN_INDEX_Y,
SSP_CHANNEL_SCAN_INDEX_Z,
SSP_CHANNEL_SCAN_INDEX_TIME,
};
static int ssp_gyro_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
u32 t;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
t = ssp_get_sensor_delay(data, SSP_GYROSCOPE_SENSOR);
ssp_convert_to_freq(t, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
default:
break;
}
return -EINVAL;
}
static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
int ret;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ssp_convert_to_time(val, val2);
ret = ssp_change_delay(data, SSP_GYROSCOPE_SENSOR, ret);
if (ret < 0)
dev_err(&indio_dev->dev, "gyro sensor enable fail\n");
return ret;
default:
break;
}
return -EINVAL;
}
static struct iio_info ssp_gyro_iio_info = {
.read_raw = &ssp_gyro_read_raw,
.write_raw = &ssp_gyro_write_raw,
};
static const unsigned long ssp_gyro_scan_mask[] = { 0x07, 0, };
static const struct iio_chan_spec ssp_gyro_channels[] = {
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
};
static int ssp_process_gyro_data(struct iio_dev *indio_dev, void *buf,
int64_t timestamp)
{
return ssp_common_process_data(indio_dev, buf, SSP_GYROSCOPE_SIZE,
timestamp);
}
static const struct iio_buffer_setup_ops ssp_gyro_buffer_ops = {
.postenable = &ssp_common_buffer_postenable,
.postdisable = &ssp_common_buffer_postdisable,
};
static int ssp_gyro_probe(struct platform_device *pdev)
{
int ret;
struct iio_dev *indio_dev;
struct ssp_sensor_data *spd;
struct iio_buffer *buffer;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
if (!indio_dev)
return -ENOMEM;
spd = iio_priv(indio_dev);
spd->process_data = ssp_process_gyro_data;
spd->type = SSP_GYROSCOPE_SENSOR;
indio_dev->name = ssp_gyro_name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &ssp_gyro_iio_info;
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = ssp_gyro_channels;
indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
indio_dev->available_scan_masks = ssp_gyro_scan_mask;
buffer = devm_iio_kfifo_allocate(&pdev->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->setup_ops = &ssp_gyro_buffer_ops;
platform_set_drvdata(pdev, indio_dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
return ret;
/* ssp registering should be done after all iio setup */
ssp_register_consumer(indio_dev, SSP_GYROSCOPE_SENSOR);
return 0;
}
static int ssp_gyro_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
return 0;
}
static struct platform_driver ssp_gyro_driver = {
.driver = {
.name = SSP_GYROSCOPE_NAME,
},
.probe = ssp_gyro_probe,
.remove = ssp_gyro_remove,
};
module_platform_driver(ssp_gyro_driver);
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
MODULE_LICENSE("GPL");

View File

@ -48,6 +48,8 @@ unsigned int iio_buffer_poll(struct file *filp,
ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps);
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr (&iio_buffer_poll)
#define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)
@ -60,6 +62,13 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr NULL
#define iio_buffer_read_first_n_outer_addr NULL
static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
return 0;
}
static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {}
static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}

View File

@ -25,6 +25,17 @@ config ADIS16480
Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
ADIS16485, ADIS16488 inertial sensors.
config KMX61
tristate "Kionix KMX61 6-axis accelerometer and magnetometer"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here if you want to build a driver for Kionix KMX61 6-axis
accelerometer and magnetometer.
To compile this driver as module, choose M here: the module will
be called kmx61.
source "drivers/iio/imu/inv_mpu6050/Kconfig"
endmenu

View File

@ -14,3 +14,5 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
obj-y += inv_mpu6050/
obj-$(CONFIG_KMX61) += kmx61.o

View File

@ -7,6 +7,7 @@ config INV_MPU6050_IIO
depends on I2C && SYSFS
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select I2C_MUX
help
This driver supports the Invensense MPU6050 devices.
This driver can also support MPU6500 in MPU6050 compatibility mode

View File

@ -23,6 +23,8 @@
#include <linux/kfifo.h>
#include <linux/spinlock.h>
#include <linux/iio/iio.h>
#include <linux/i2c-mux.h>
#include <linux/acpi.h>
#include "inv_mpu_iio.h"
/*
@ -52,6 +54,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
.int_enable = INV_MPU6050_REG_INT_ENABLE,
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
};
static const struct inv_mpu6050_chip_config chip_config_6050 = {
@ -77,6 +80,83 @@ int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d)
return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d);
}
/*
* The i2c read/write needs to happen in unlocked mode. As the parent
* adapter is common. If we use locked versions, it will fail as
* the mux adapter will lock the parent i2c adapter, while calling
* select/deselect functions.
*/
static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st,
u8 reg, u8 d)
{
int ret;
u8 buf[2];
struct i2c_msg msg[1] = {
{
.addr = st->client->addr,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
}
};
buf[0] = reg;
buf[1] = d;
ret = __i2c_transfer(st->client->adapter, msg, 1);
if (ret != 1)
return ret;
return 0;
}
static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
u32 chan_id)
{
struct iio_dev *indio_dev = mux_priv;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
int ret = 0;
/* Use the same mutex which was used everywhere to protect power-op */
mutex_lock(&indio_dev->mlock);
if (!st->powerup_count) {
ret = inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
0);
if (ret)
goto write_error;
msleep(INV_MPU6050_REG_UP_TIME);
}
if (!ret) {
st->powerup_count++;
ret = inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
st->client->irq |
INV_MPU6050_BIT_BYPASS_EN);
}
write_error:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap,
void *mux_priv, u32 chan_id)
{
struct iio_dev *indio_dev = mux_priv;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
/* It doesn't really mattter, if any of the calls fails */
inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
st->client->irq);
st->powerup_count--;
if (!st->powerup_count)
inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
mutex_unlock(&indio_dev->mlock);
return 0;
}
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
{
u8 d, mgmt_1;
@ -133,13 +213,22 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
{
int result;
int result = 0;
if (power_on) {
/* Already under indio-dev->mlock mutex */
if (!st->powerup_count)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
0);
if (!result)
st->powerup_count++;
} else {
st->powerup_count--;
if (!st->powerup_count)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
}
if (power_on)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, 0);
else
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
if (result)
return result;
@ -673,6 +762,7 @@ static int inv_mpu_probe(struct i2c_client *client,
st = iio_priv(indio_dev);
st->client = client;
st->powerup_count = 0;
pdata = dev_get_platdata(&client->dev);
if (pdata)
st->plat_data = *pdata;
@ -720,8 +810,21 @@ static int inv_mpu_probe(struct i2c_client *client,
goto out_remove_trigger;
}
st->mux_adapter = i2c_add_mux_adapter(client->adapter,
&client->dev,
indio_dev,
0, 0, 0,
inv_mpu6050_select_bypass,
inv_mpu6050_deselect_bypass);
if (!st->mux_adapter) {
result = -ENODEV;
goto out_unreg_device;
}
return 0;
out_unreg_device:
iio_device_unregister(indio_dev);
out_remove_trigger:
inv_mpu6050_remove_trigger(st);
out_unreg_ring:
@ -734,6 +837,7 @@ static int inv_mpu_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
i2c_del_mux_adapter(st->mux_adapter);
iio_device_unregister(indio_dev);
inv_mpu6050_remove_trigger(st);
iio_triggered_buffer_cleanup(indio_dev);
@ -772,6 +876,13 @@ static const struct i2c_device_id inv_mpu_id[] = {
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
static const struct acpi_device_id inv_acpi_match[] = {
{"INVN6500", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
static struct i2c_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
@ -780,6 +891,7 @@ static struct i2c_driver inv_mpu_driver = {
.owner = THIS_MODULE,
.name = "inv-mpu6050",
.pm = INV_MPU6050_PMOPS,
.acpi_match_table = ACPI_PTR(inv_acpi_match),
},
};

View File

@ -54,6 +54,7 @@ struct inv_mpu6050_reg_map {
u8 int_enable;
u8 pwr_mgmt_1;
u8 pwr_mgmt_2;
u8 int_pin_cfg;
};
/*device enum */
@ -119,6 +120,8 @@ struct inv_mpu6050_state {
enum inv_devices chip_type;
spinlock_t time_stamp_lock;
struct i2c_client *client;
struct i2c_adapter *mux_adapter;
unsigned int powerup_count;
struct inv_mpu6050_platform_data plat_data;
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
};
@ -179,6 +182,9 @@ struct inv_mpu6050_state {
/* 6 + 6 round up and plus 8 */
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
#define INV_MPU6050_REG_INT_PIN_CFG 0x37
#define INV_MPU6050_BIT_BYPASS_EN 0x2
/* init parameters */
#define INV_MPU6050_INIT_FIFO_RATE 50
#define INV_MPU6050_TIME_STAMP_TOR 5

View File

@ -116,40 +116,35 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
int ret;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
st->trig = iio_trigger_alloc("%s-dev%d",
indio_dev->name,
indio_dev->id);
if (st->trig == NULL) {
ret = -ENOMEM;
goto error_ret;
}
ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
"inv_mpu",
st->trig);
st->trig = devm_iio_trigger_alloc(&indio_dev->dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!st->trig)
return -ENOMEM;
ret = devm_request_irq(&indio_dev->dev, st->client->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
"inv_mpu",
st->trig);
if (ret)
goto error_free_trig;
return ret;
st->trig->dev.parent = &st->client->dev;
st->trig->ops = &inv_mpu_trigger_ops;
iio_trigger_set_drvdata(st->trig, indio_dev);
ret = iio_trigger_register(st->trig);
if (ret)
goto error_free_irq;
return ret;
indio_dev->trig = iio_trigger_get(st->trig);
return 0;
error_free_irq:
free_irq(st->client->irq, st->trig);
error_free_trig:
iio_trigger_free(st->trig);
error_ret:
return ret;
}
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st)
{
iio_trigger_unregister(st->trig);
free_irq(st->client->irq, st->trig);
iio_trigger_free(st->trig);
}

1595
drivers/iio/imu/kmx61.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -178,6 +178,80 @@ static ssize_t iio_scan_el_show(struct device *dev,
return sprintf(buf, "%d\n", ret);
}
/* 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)
{
if (bitmap_empty(mask, masklength))
return NULL;
while (*av_masks) {
if (bitmap_subset(mask, av_masks, masklength))
return av_masks;
av_masks += BITS_TO_LONGS(masklength);
}
return NULL;
}
static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
const unsigned long *mask)
{
if (!indio_dev->setup_ops->validate_scan_mask)
return true;
return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask);
}
/**
* iio_scan_mask_set() - set particular bit in the scan mask
* @indio_dev: the iio device
* @buffer: the buffer whose scan mask we are interested in
* @bit: the bit to be set.
*
* Note that at this point we have no way of knowing what other
* buffers might request, hence this code only verifies that the
* individual buffers request is plausible.
*/
static int iio_scan_mask_set(struct iio_dev *indio_dev,
struct iio_buffer *buffer, int bit)
{
const unsigned long *mask;
unsigned long *trialmask;
trialmask = kmalloc(sizeof(*trialmask)*
BITS_TO_LONGS(indio_dev->masklength),
GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {
WARN_ON("Trying to set scanmask prior to registering buffer\n");
goto err_invalid_mask;
}
bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
set_bit(bit, trialmask);
if (!iio_validate_scan_mask(indio_dev, trialmask))
goto err_invalid_mask;
if (indio_dev->available_scan_masks) {
mask = iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
trialmask);
if (!mask)
goto err_invalid_mask;
}
bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
kfree(trialmask);
return 0;
err_invalid_mask:
kfree(trialmask);
return -EINVAL;
}
static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit)
{
clear_bit(bit, buffer->scan_mask);
@ -309,115 +383,19 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
}
static const char * const iio_scan_elements_group_name = "scan_elements";
int iio_buffer_register(struct iio_dev *indio_dev,
const struct iio_chan_spec *channels,
int num_channels)
{
struct iio_dev_attr *p;
struct attribute **attr;
struct iio_buffer *buffer = indio_dev->buffer;
int ret, i, attrn, attrcount, attrcount_orig = 0;
if (buffer->attrs)
indio_dev->groups[indio_dev->groupcounter++] = buffer->attrs;
if (buffer->scan_el_attrs != NULL) {
attr = buffer->scan_el_attrs->attrs;
while (*attr++ != NULL)
attrcount_orig++;
}
attrcount = attrcount_orig;
INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
if (channels) {
/* new magic */
for (i = 0; i < num_channels; i++) {
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)
goto error_cleanup_dynamic;
attrcount += ret;
if (channels[i].type == IIO_TIMESTAMP)
indio_dev->scan_index_timestamp =
channels[i].scan_index;
}
if (indio_dev->masklength && buffer->scan_mask == NULL) {
buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
sizeof(*buffer->scan_mask),
GFP_KERNEL);
if (buffer->scan_mask == NULL) {
ret = -ENOMEM;
goto error_cleanup_dynamic;
}
}
}
buffer->scan_el_group.name = iio_scan_elements_group_name;
buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
sizeof(buffer->scan_el_group.attrs[0]),
GFP_KERNEL);
if (buffer->scan_el_group.attrs == NULL) {
ret = -ENOMEM;
goto error_free_scan_mask;
}
if (buffer->scan_el_attrs)
memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
attrn = attrcount_orig;
list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
return 0;
error_free_scan_mask:
kfree(buffer->scan_mask);
error_cleanup_dynamic:
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
return ret;
}
EXPORT_SYMBOL(iio_buffer_register);
void iio_buffer_unregister(struct iio_dev *indio_dev)
{
kfree(indio_dev->buffer->scan_mask);
kfree(indio_dev->buffer->scan_el_group.attrs);
iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
}
EXPORT_SYMBOL(iio_buffer_unregister);
ssize_t iio_buffer_read_length(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t iio_buffer_read_length(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_buffer *buffer = indio_dev->buffer;
if (buffer->access->get_length)
return sprintf(buf, "%d\n",
buffer->access->get_length(buffer));
return 0;
return sprintf(buf, "%d\n", buffer->length);
}
EXPORT_SYMBOL(iio_buffer_read_length);
ssize_t iio_buffer_write_length(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
static ssize_t iio_buffer_write_length(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_buffer *buffer = indio_dev->buffer;
@ -428,47 +406,28 @@ ssize_t iio_buffer_write_length(struct device *dev,
if (ret)
return ret;
if (buffer->access->get_length)
if (val == buffer->access->get_length(buffer))
return len;
if (val == buffer->length)
return len;
mutex_lock(&indio_dev->mlock);
if (iio_buffer_is_active(indio_dev->buffer)) {
ret = -EBUSY;
} else {
if (buffer->access->set_length)
buffer->access->set_length(buffer, val);
buffer->access->set_length(buffer, val);
ret = 0;
}
mutex_unlock(&indio_dev->mlock);
return ret ? ret : len;
}
EXPORT_SYMBOL(iio_buffer_write_length);
ssize_t iio_buffer_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t iio_buffer_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer));
}
EXPORT_SYMBOL(iio_buffer_show_enable);
/* 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)
{
if (bitmap_empty(mask, masklength))
return NULL;
while (*av_masks) {
if (bitmap_subset(mask, av_masks, masklength))
return av_masks;
av_masks += BITS_TO_LONGS(masklength);
}
return NULL;
}
static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
const unsigned long *mask, bool timestamp)
@ -680,6 +639,8 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
} else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
} else { /* Should never be reached */
ret = -EINVAL;
goto error_run_postdisable;
@ -755,10 +716,10 @@ int iio_update_buffers(struct iio_dev *indio_dev,
}
EXPORT_SYMBOL_GPL(iio_update_buffers);
ssize_t iio_buffer_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
static ssize_t iio_buffer_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
{
int ret;
bool requested_state;
@ -790,7 +751,130 @@ ssize_t iio_buffer_store_enable(struct device *dev,
mutex_unlock(&indio_dev->mlock);
return (ret < 0) ? ret : len;
}
EXPORT_SYMBOL(iio_buffer_store_enable);
static const char * const iio_scan_elements_group_name = "scan_elements";
static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length,
iio_buffer_write_length);
static struct device_attribute dev_attr_length_ro = __ATTR(length,
S_IRUGO, iio_buffer_read_length, NULL);
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
iio_buffer_show_enable, iio_buffer_store_enable);
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_dev_attr *p;
struct attribute **attr;
struct iio_buffer *buffer = indio_dev->buffer;
int ret, i, attrn, attrcount, attrcount_orig = 0;
const struct iio_chan_spec *channels;
if (!buffer)
return 0;
attrcount = 0;
if (buffer->attrs) {
while (buffer->attrs[attrcount] != NULL)
attrcount++;
}
buffer->buffer_group.name = "buffer";
buffer->buffer_group.attrs = kcalloc(attrcount + 3,
sizeof(*buffer->buffer_group.attrs), GFP_KERNEL);
if (!buffer->buffer_group.attrs)
return -ENOMEM;
if (buffer->access->set_length)
buffer->buffer_group.attrs[0] = &dev_attr_length.attr;
else
buffer->buffer_group.attrs[0] = &dev_attr_length_ro.attr;
buffer->buffer_group.attrs[1] = &dev_attr_enable.attr;
if (buffer->attrs)
memcpy(&buffer->buffer_group.attrs[2], buffer->attrs,
sizeof(*&buffer->buffer_group.attrs) * attrcount);
buffer->buffer_group.attrs[attrcount+2] = NULL;
indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group;
if (buffer->scan_el_attrs != NULL) {
attr = buffer->scan_el_attrs->attrs;
while (*attr++ != NULL)
attrcount_orig++;
}
attrcount = attrcount_orig;
INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
channels = indio_dev->channels;
if (channels) {
/* new magic */
for (i = 0; i < indio_dev->num_channels; i++) {
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)
goto error_cleanup_dynamic;
attrcount += ret;
if (channels[i].type == IIO_TIMESTAMP)
indio_dev->scan_index_timestamp =
channels[i].scan_index;
}
if (indio_dev->masklength && buffer->scan_mask == NULL) {
buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
sizeof(*buffer->scan_mask),
GFP_KERNEL);
if (buffer->scan_mask == NULL) {
ret = -ENOMEM;
goto error_cleanup_dynamic;
}
}
}
buffer->scan_el_group.name = iio_scan_elements_group_name;
buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
sizeof(buffer->scan_el_group.attrs[0]),
GFP_KERNEL);
if (buffer->scan_el_group.attrs == NULL) {
ret = -ENOMEM;
goto error_free_scan_mask;
}
if (buffer->scan_el_attrs)
memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
attrn = attrcount_orig;
list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
return 0;
error_free_scan_mask:
kfree(buffer->scan_mask);
error_cleanup_dynamic:
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
kfree(indio_dev->buffer->buffer_group.attrs);
return ret;
}
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
{
if (!indio_dev->buffer)
return;
kfree(indio_dev->buffer->scan_mask);
kfree(indio_dev->buffer->buffer_group.attrs);
kfree(indio_dev->buffer->scan_el_group.attrs);
iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
}
/**
* iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
@ -808,66 +892,6 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
}
EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot);
static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
const unsigned long *mask)
{
if (!indio_dev->setup_ops->validate_scan_mask)
return true;
return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask);
}
/**
* iio_scan_mask_set() - set particular bit in the scan mask
* @indio_dev: the iio device
* @buffer: the buffer whose scan mask we are interested in
* @bit: the bit to be set.
*
* Note that at this point we have no way of knowing what other
* buffers might request, hence this code only verifies that the
* individual buffers request is plausible.
*/
int iio_scan_mask_set(struct iio_dev *indio_dev,
struct iio_buffer *buffer, int bit)
{
const unsigned long *mask;
unsigned long *trialmask;
trialmask = kmalloc(sizeof(*trialmask)*
BITS_TO_LONGS(indio_dev->masklength),
GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {
WARN_ON("Trying to set scanmask prior to registering buffer\n");
goto err_invalid_mask;
}
bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
set_bit(bit, trialmask);
if (!iio_validate_scan_mask(indio_dev, trialmask))
goto err_invalid_mask;
if (indio_dev->available_scan_masks) {
mask = iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
trialmask);
if (!mask)
goto err_invalid_mask;
}
bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
kfree(trialmask);
return 0;
err_invalid_mask:
kfree(trialmask);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(iio_scan_mask_set);
int iio_scan_mask_query(struct iio_dev *indio_dev,
struct iio_buffer *buffer, int bit)
{

View File

@ -70,6 +70,11 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_CCT] = "cct",
[IIO_PRESSURE] = "pressure",
[IIO_HUMIDITYRELATIVE] = "humidityrelative",
[IIO_ACTIVITY] = "activity",
[IIO_STEPS] = "steps",
[IIO_ENERGY] = "energy",
[IIO_DISTANCE] = "distance",
[IIO_VELOCITY] = "velocity",
};
static const char * const iio_modifier_names[] = {
@ -91,6 +96,11 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_NORTH_TRUE] = "from_north_true",
[IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp",
[IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp",
[IIO_MOD_RUNNING] = "running",
[IIO_MOD_JOGGING] = "jogging",
[IIO_MOD_WALKING] = "walking",
[IIO_MOD_STILL] = "still",
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
};
/* relies on pairs of these shared then separate */
@ -113,6 +123,11 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",
[IIO_CHAN_INFO_HYSTERESIS] = "hysteresis",
[IIO_CHAN_INFO_INT_TIME] = "integration_time",
[IIO_CHAN_INFO_ENABLE] = "en",
[IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
};
/**
@ -1035,7 +1050,6 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
if (!ptr)
return NULL;
/* use raw alloc_dr for kmalloc caller tracing */
iio_dev = iio_device_alloc(sizeof_priv);
if (iio_dev) {
*ptr = iio_dev;
@ -1127,6 +1141,29 @@ static const struct file_operations iio_buffer_fileops = {
.compat_ioctl = iio_ioctl,
};
static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
{
int i, j;
const struct iio_chan_spec *channels = indio_dev->channels;
if (!(indio_dev->modes & INDIO_ALL_BUFFER_MODES))
return 0;
for (i = 0; i < indio_dev->num_channels - 1; i++) {
if (channels[i].scan_index < 0)
continue;
for (j = i + 1; j < indio_dev->num_channels; j++)
if (channels[i].scan_index == channels[j].scan_index) {
dev_err(&indio_dev->dev,
"Duplicate scan index %d\n",
channels[i].scan_index);
return -EINVAL;
}
}
return 0;
}
static const struct iio_buffer_setup_ops noop_ring_setup_ops;
/**
@ -1141,6 +1178,10 @@ int iio_device_register(struct iio_dev *indio_dev)
if (!indio_dev->dev.of_node && indio_dev->dev.parent)
indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
ret = iio_check_unique_scan_index(indio_dev);
if (ret < 0)
return ret;
/* configure elements for the chrdev */
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
@ -1150,11 +1191,19 @@ int iio_device_register(struct iio_dev *indio_dev)
"Failed to register debugfs interfaces\n");
return ret;
}
ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to create buffer sysfs interfaces\n");
goto error_unreg_debugfs;
}
ret = iio_device_register_sysfs(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to register sysfs interfaces\n");
goto error_unreg_debugfs;
goto error_buffer_free_sysfs;
}
ret = iio_device_register_eventset(indio_dev);
if (ret) {
@ -1187,6 +1236,8 @@ int iio_device_register(struct iio_dev *indio_dev)
iio_device_unregister_eventset(indio_dev);
error_free_sysfs:
iio_device_unregister_sysfs(indio_dev);
error_buffer_free_sysfs:
iio_buffer_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev);
return ret;
@ -1215,6 +1266,8 @@ void iio_device_unregister(struct iio_dev *indio_dev)
iio_buffer_wakeup_poll(indio_dev);
mutex_unlock(&indio_dev->info_exist_lock);
iio_buffer_free_sysfs_and_mask(indio_dev);
}
EXPORT_SYMBOL(iio_device_unregister);

View File

@ -197,6 +197,7 @@ static const char * const iio_ev_type_text[] = {
[IIO_EV_TYPE_ROC] = "roc",
[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
[IIO_EV_TYPE_CHANGE] = "change",
};
static const char * const iio_ev_dir_text[] = {
@ -327,9 +328,15 @@ static int iio_device_add_event(struct iio_dev *indio_dev,
for_each_set_bit(i, mask, sizeof(*mask)*8) {
if (i >= ARRAY_SIZE(iio_ev_info_text))
return -EINVAL;
postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_ev_type_text[type], iio_ev_dir_text[dir],
iio_ev_info_text[i]);
if (dir != IIO_EV_DIR_NONE)
postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_ev_type_text[type],
iio_ev_dir_text[dir],
iio_ev_info_text[i]);
else
postfix = kasprintf(GFP_KERNEL, "%s_%s",
iio_ev_type_text[type],
iio_ev_info_text[i]);
if (postfix == NULL)
return -ENOMEM;
@ -404,7 +411,7 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
{
int j, ret, attrcount = 0;
/* Dynically created from the channels array */
/* Dynamically created from the channels array */
for (j = 0; j < indio_dev->num_channels; j++) {
ret = iio_device_add_event_sysfs(indio_dev,
&indio_dev->channels[j]);

View File

@ -32,7 +32,7 @@ static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
*
* This function combines some common tasks which will normally be performed
* when setting up a triggered buffer. It will allocate the buffer and the
* pollfunc, as well as register the buffer with the IIO core.
* pollfunc.
*
* Before calling this function the indio_dev structure should already be
* completely initialized, but not yet registered. In practice this means that
@ -49,7 +49,7 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
struct iio_buffer *buffer;
int ret;
buffer = iio_kfifo_allocate(indio_dev);
buffer = iio_kfifo_allocate();
if (!buffer) {
ret = -ENOMEM;
goto error_ret;
@ -78,16 +78,8 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
/* Flag that polled ring buffering is possible */
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
ret = iio_buffer_register(indio_dev,
indio_dev->channels,
indio_dev->num_channels);
if (ret)
goto error_dealloc_pollfunc;
return 0;
error_dealloc_pollfunc:
iio_dealloc_pollfunc(indio_dev->pollfunc);
error_kfifo_free:
iio_kfifo_free(indio_dev->buffer);
error_ret:
@ -101,7 +93,6 @@ EXPORT_SYMBOL(iio_triggered_buffer_setup);
*/
void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
{
iio_buffer_unregister(indio_dev);
iio_dealloc_pollfunc(indio_dev->pollfunc);
iio_kfifo_free(indio_dev->buffer);
}

View File

@ -116,8 +116,11 @@ static int __of_iio_simple_xlate(struct iio_dev *indio_dev,
if (!iiospec->args_count)
return 0;
if (iiospec->args[0] >= indio_dev->num_channels)
if (iiospec->args[0] >= indio_dev->num_channels) {
dev_err(&indio_dev->dev, "invalid channel index %u\n",
iiospec->args[0]);
return -EINVAL;
}
return iiospec->args[0];
}
@ -634,3 +637,28 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
return ret;
}
EXPORT_SYMBOL_GPL(iio_get_channel_type);
static int iio_channel_write(struct iio_channel *chan, int val, int val2,
enum iio_chan_info_enum info)
{
return chan->indio_dev->info->write_raw(chan->indio_dev,
chan->channel, val, val2, info);
}
int iio_write_channel_raw(struct iio_channel *chan, int val)
{
int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_RAW);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
return ret;
}
EXPORT_SYMBOL_GPL(iio_write_channel_raw);

View File

@ -47,30 +47,6 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
return ret;
}
static int iio_get_length_kfifo(struct iio_buffer *r)
{
return r->length;
}
static IIO_BUFFER_ENABLE_ATTR;
static IIO_BUFFER_LENGTH_ATTR;
static struct attribute *iio_kfifo_attributes[] = {
&dev_attr_length.attr,
&dev_attr_enable.attr,
NULL,
};
static struct attribute_group iio_kfifo_attribute_group = {
.attrs = iio_kfifo_attributes,
.name = "buffer",
};
static int iio_get_bytes_per_datum_kfifo(struct iio_buffer *r)
{
return r->bytes_per_datum;
}
static int iio_mark_update_needed_kfifo(struct iio_buffer *r)
{
struct iio_kfifo *kf = iio_to_kfifo(r);
@ -159,26 +135,25 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
.read_first_n = &iio_read_first_n_kfifo,
.data_available = iio_kfifo_buf_data_available,
.request_update = &iio_request_update_kfifo,
.get_bytes_per_datum = &iio_get_bytes_per_datum_kfifo,
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
.get_length = &iio_get_length_kfifo,
.set_length = &iio_set_length_kfifo,
.release = &iio_kfifo_buffer_release,
};
struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
struct iio_buffer *iio_kfifo_allocate(void)
{
struct iio_kfifo *kf;
kf = kzalloc(sizeof *kf, GFP_KERNEL);
kf = kzalloc(sizeof(*kf), GFP_KERNEL);
if (!kf)
return NULL;
kf->update_needed = true;
iio_buffer_init(&kf->buffer);
kf->buffer.attrs = &iio_kfifo_attribute_group;
kf->buffer.access = &kfifo_access_funcs;
kf->buffer.length = 2;
mutex_init(&kf->user_lock);
return &kf->buffer;
}
EXPORT_SYMBOL(iio_kfifo_allocate);
@ -189,4 +164,58 @@ void iio_kfifo_free(struct iio_buffer *r)
}
EXPORT_SYMBOL(iio_kfifo_free);
static void devm_iio_kfifo_release(struct device *dev, void *res)
{
iio_kfifo_free(*(struct iio_buffer **)res);
}
static int devm_iio_kfifo_match(struct device *dev, void *res, void *data)
{
struct iio_buffer **r = res;
if (WARN_ON(!r || !*r))
return 0;
return *r == data;
}
/**
* devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate()
* @dev: Device to allocate kfifo buffer for
*
* RETURNS:
* Pointer to allocated iio_buffer on success, NULL on failure.
*/
struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)
{
struct iio_buffer **ptr, *r;
ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return NULL;
r = iio_kfifo_allocate();
if (r) {
*ptr = r;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return r;
}
EXPORT_SYMBOL(devm_iio_kfifo_allocate);
/**
* devm_iio_fifo_free - Resource-managed iio_kfifo_free()
* @dev: Device the buffer belongs to
* @r: The buffer associated with the device
*/
void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r)
{
WARN_ON(devres_release(dev, devm_iio_kfifo_release,
devm_iio_kfifo_match, r));
}
EXPORT_SYMBOL(devm_iio_kfifo_free);
MODULE_LICENSE("GPL");

View File

@ -48,6 +48,17 @@ config CM32181
To compile this driver as a module, choose M here:
the module will be called cm32181.
config CM3232
depends on I2C
tristate "CM3232 ambient light sensor"
help
Say Y here if you use cm3232.
This option enables ambient light sensor using
Capella Microsystems cm3232 device driver.
To compile this driver as a module, choose M here:
the module will be called cm3232.
config CM36651
depends on I2C
tristate "CM36651 driver"
@ -95,6 +106,9 @@ config HID_SENSOR_ALS
Say yes here to build support for the HID SENSOR
Ambient light sensor.
To compile this driver as a module, choose M here: the
module will be called hid-sensor-als.
config HID_SENSOR_PROX
depends on HID_SENSOR_HUB
select IIO_BUFFER
@ -109,6 +123,16 @@ config HID_SENSOR_PROX
To compile this driver as a module, choose M here: the
module will be called hid-sensor-prox.
config JSA1212
tristate "JSA1212 ALS and proximity sensor driver"
depends on I2C
help
Say Y here if you want to build a IIO driver for JSA1212
proximity & ALS sensor device.
To compile this driver as a module, choose M here:
the module will be called jsa1212.
config SENSORS_LM3533
tristate "LM3533 ambient light sensor"
depends on MFD_LM3533

View File

@ -7,11 +7,13 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
obj-$(CONFIG_CM32181) += cm32181.o
obj-$(CONFIG_CM3232) += cm3232.o
obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o

View File

@ -169,7 +169,7 @@ static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val)
* @cm32181: pointer of struct cm32181.
*
* Convert sensor raw data to lux. It depends on integration
* time and claibscale variable.
* time and calibscale variable.
*
* Return: Positive value is lux, otherwise is error code.
*/

403
drivers/iio/light/cm3232.c Normal file
View File

@ -0,0 +1,403 @@
/*
* CM3232 Ambient Light Sensor
*
* Copyright (C) 2014-2015 Capella Microsystems Inc.
* Author: Kevin Tsai <ktsai@capellamicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2, as published
* by the Free Software Foundation.
*
* IIO driver for CM3232 (7-bit I2C slave address 0x10).
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/init.h>
/* Registers Address */
#define CM3232_REG_ADDR_CMD 0x00
#define CM3232_REG_ADDR_ALS 0x50
#define CM3232_REG_ADDR_ID 0x53
#define CM3232_CMD_ALS_DISABLE BIT(0)
#define CM3232_CMD_ALS_IT_SHIFT 2
#define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4))
#define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT)
#define CM3232_CMD_ALS_RESET BIT(6)
#define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT
#define CM3232_HW_ID 0x32
#define CM3232_CALIBSCALE_DEFAULT 100000
#define CM3232_CALIBSCALE_RESOLUTION 100000
#define CM3232_MLUX_PER_LUX 1000
#define CM3232_MLUX_PER_BIT_DEFAULT 64
#define CM3232_MLUX_PER_BIT_BASE_IT 100000
static const struct {
int val;
int val2;
u8 it;
} cm3232_als_it_scales[] = {
{0, 100000, 0}, /* 0.100000 */
{0, 200000, 1}, /* 0.200000 */
{0, 400000, 2}, /* 0.400000 */
{0, 800000, 3}, /* 0.800000 */
{1, 600000, 4}, /* 1.600000 */
{3, 200000, 5}, /* 3.200000 */
};
struct cm3232_als_info {
u8 regs_cmd_default;
u8 hw_id;
int calibscale;
int mlux_per_bit;
int mlux_per_bit_base_it;
};
static struct cm3232_als_info cm3232_als_info_default = {
.regs_cmd_default = CM3232_CMD_DEFAULT,
.hw_id = CM3232_HW_ID,
.calibscale = CM3232_CALIBSCALE_DEFAULT,
.mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
.mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
};
struct cm3232_chip {
struct i2c_client *client;
struct cm3232_als_info *als_info;
u8 regs_cmd;
u16 regs_als;
};
/**
* cm3232_reg_init() - Initialize CM3232
* @chip: pointer of struct cm3232_chip.
*
* Check and initialize CM3232 ambient light sensor.
*
* Return: 0 for success; otherwise for error code.
*/
static int cm3232_reg_init(struct cm3232_chip *chip)
{
struct i2c_client *client = chip->client;
s32 ret;
chip->als_info = &cm3232_als_info_default;
/* Identify device */
ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
if (ret < 0) {
dev_err(&chip->client->dev, "Error reading addr_id\n");
return ret;
}
if ((ret & 0xFF) != chip->als_info->hw_id)
return -ENODEV;
/* Disable and reset device */
chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
chip->regs_cmd);
if (ret < 0) {
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
return ret;
}
/* Register default value */
chip->regs_cmd = chip->als_info->regs_cmd_default;
/* Configure register */
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
chip->regs_cmd);
if (ret < 0)
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
return 0;
}
/**
* cm3232_read_als_it() - Get sensor integration time
* @chip: pointer of struct cm3232_chip
* @val: pointer of int to load the integration (sec).
* @val2: pointer of int to load the integration time (microsecond).
*
* Report the current integration time.
*
* Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
*/
static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
{
u16 als_it;
int i;
als_it = chip->regs_cmd;
als_it &= CM3232_CMD_ALS_IT_MASK;
als_it >>= CM3232_CMD_ALS_IT_SHIFT;
for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
if (als_it == cm3232_als_it_scales[i].it) {
*val = cm3232_als_it_scales[i].val;
*val2 = cm3232_als_it_scales[i].val2;
return IIO_VAL_INT_PLUS_MICRO;
}
}
return -EINVAL;
}
/**
* cm3232_write_als_it() - Write sensor integration time
* @chip: pointer of struct cm3232_chip.
* @val: integration time in second.
* @val2: integration time in microsecond.
*
* Convert integration time to sensor value.
*
* Return: i2c_smbus_write_byte_data command return value.
*/
static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
{
struct i2c_client *client = chip->client;
u16 als_it, cmd;
int i;
s32 ret;
for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
if (val == cm3232_als_it_scales[i].val &&
val2 == cm3232_als_it_scales[i].val2) {
als_it = cm3232_als_it_scales[i].it;
als_it <<= CM3232_CMD_ALS_IT_SHIFT;
cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
cmd |= als_it;
ret = i2c_smbus_write_byte_data(client,
CM3232_REG_ADDR_CMD,
cmd);
if (ret < 0)
return ret;
chip->regs_cmd = cmd;
return 0;
}
}
return -EINVAL;
}
/**
* cm3232_get_lux() - report current lux value
* @chip: pointer of struct cm3232_chip.
*
* Convert sensor data to lux. It depends on integration
* time and calibscale variable.
*
* Return: Zero or positive value is lux, otherwise error code.
*/
static int cm3232_get_lux(struct cm3232_chip *chip)
{
struct i2c_client *client = chip->client;
struct cm3232_als_info *als_info = chip->als_info;
int ret;
int val, val2;
int als_it;
u64 lux;
/* Calculate mlux per bit based on als_it */
ret = cm3232_read_als_it(chip, &val, &val2);
if (ret < 0)
return -EINVAL;
als_it = val * 1000000 + val2;
lux = (__force u64)als_info->mlux_per_bit;
lux *= als_info->mlux_per_bit_base_it;
lux = div_u64(lux, als_it);
ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
if (ret < 0) {
dev_err(&client->dev, "Error reading reg_addr_als\n");
return ret;
}
chip->regs_als = (u16)ret;
lux *= chip->regs_als;
lux *= als_info->calibscale;
lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
lux = div_u64(lux, CM3232_MLUX_PER_LUX);
if (lux > 0xFFFF)
lux = 0xFFFF;
return (int)lux;
}
static int cm3232_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct cm3232_chip *chip = iio_priv(indio_dev);
struct cm3232_als_info *als_info = chip->als_info;
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
ret = cm3232_get_lux(chip);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBSCALE:
*val = als_info->calibscale;
return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
return cm3232_read_als_it(chip, val, val2);
}
return -EINVAL;
}
static int cm3232_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct cm3232_chip *chip = iio_priv(indio_dev);
struct cm3232_als_info *als_info = chip->als_info;
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
als_info->calibscale = val;
return 0;
case IIO_CHAN_INFO_INT_TIME:
return cm3232_write_als_it(chip, val, val2);
}
return -EINVAL;
}
/**
* cm3232_get_it_available() - Get available ALS IT value
* @dev: pointer of struct device.
* @attr: pointer of struct device_attribute.
* @buf: pointer of return string buffer.
*
* Display the available integration time in second.
*
* Return: string length.
*/
static ssize_t cm3232_get_it_available(struct device *dev,
struct device_attribute *attr, char *buf)
{
int i, len;
for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
cm3232_als_it_scales[i].val,
cm3232_als_it_scales[i].val2);
return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
}
static const struct iio_chan_spec cm3232_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate =
BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_CALIBSCALE) |
BIT(IIO_CHAN_INFO_INT_TIME),
}
};
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
S_IRUGO, cm3232_get_it_available, NULL, 0);
static struct attribute *cm3232_attributes[] = {
&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
NULL,
};
static const struct attribute_group cm3232_attribute_group = {
.attrs = cm3232_attributes
};
static const struct iio_info cm3232_info = {
.driver_module = THIS_MODULE,
.read_raw = &cm3232_read_raw,
.write_raw = &cm3232_write_raw,
.attrs = &cm3232_attribute_group,
};
static int cm3232_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cm3232_chip *chip;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
chip->client = client;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = cm3232_channels;
indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
indio_dev->info = &cm3232_info;
indio_dev->name = id->name;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = cm3232_reg_init(chip);
if (ret) {
dev_err(&client->dev,
"%s: register init failed\n",
__func__);
return ret;
}
return iio_device_register(indio_dev);
}
static int cm3232_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
CM3232_CMD_ALS_DISABLE);
iio_device_unregister(indio_dev);
return 0;
}
static const struct i2c_device_id cm3232_id[] = {
{"cm3232", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cm3232_id);
static const struct of_device_id cm3232_of_match[] = {
{.compatible = "capella,cm3232"},
{}
};
static struct i2c_driver cm3232_driver = {
.driver = {
.name = "cm3232",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(cm3232_of_match),
},
.id_table = cm3232_id,
.probe = cm3232_probe,
.remove = cm3232_remove,
};
module_i2c_driver(cm3232_driver);
MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
MODULE_DESCRIPTION("CM3232 ambient light sensor driver");
MODULE_LICENSE("GPL");

View File

@ -80,7 +80,6 @@ static int als_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
@ -97,15 +96,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
poll_value = hid_sensor_read_poll_value(
&als_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&als_state->common_attributes,
true);
msleep_interruptible(poll_value * 2);
*val = sensor_hub_input_attr_get_raw_value(
als_state->common_attributes.hsdev,
HID_USAGE_SENSOR_ALS, address,
@ -381,6 +373,7 @@ static struct platform_driver hid_als_platform_driver = {
.id_table = hid_als_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_als_probe,
.remove = hid_als_remove,

View File

@ -75,7 +75,6 @@ static int prox_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
@ -92,16 +91,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
poll_value = hid_sensor_read_poll_value(
&prox_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&prox_state->common_attributes,
true);
msleep_interruptible(poll_value * 2);
*val = sensor_hub_input_attr_get_raw_value(
prox_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PROX, address,
@ -373,6 +364,7 @@ static struct platform_driver hid_prox_platform_driver = {
.id_table = hid_prox_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_prox_probe,
.remove = hid_prox_remove,

471
drivers/iio/light/jsa1212.c Normal file
View File

@ -0,0 +1,471 @@
/*
* JSA1212 Ambient Light & Proximity Sensor Driver
*
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* JSA1212 I2C slave address: 0x44(ADDR tied to GND), 0x45(ADDR tied to VDD)
*
* TODO: Interrupt support, thresholds, range support.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
/* JSA1212 reg address */
#define JSA1212_CONF_REG 0x01
#define JSA1212_INT_REG 0x02
#define JSA1212_PXS_LT_REG 0x03
#define JSA1212_PXS_HT_REG 0x04
#define JSA1212_ALS_TH1_REG 0x05
#define JSA1212_ALS_TH2_REG 0x06
#define JSA1212_ALS_TH3_REG 0x07
#define JSA1212_PXS_DATA_REG 0x08
#define JSA1212_ALS_DT1_REG 0x09
#define JSA1212_ALS_DT2_REG 0x0A
#define JSA1212_ALS_RNG_REG 0x0B
#define JSA1212_MAX_REG 0x0C
/* JSA1212 reg masks */
#define JSA1212_CONF_MASK 0xFF
#define JSA1212_INT_MASK 0xFF
#define JSA1212_PXS_LT_MASK 0xFF
#define JSA1212_PXS_HT_MASK 0xFF
#define JSA1212_ALS_TH1_MASK 0xFF
#define JSA1212_ALS_TH2_LT_MASK 0x0F
#define JSA1212_ALS_TH2_HT_MASK 0xF0
#define JSA1212_ALS_TH3_MASK 0xFF
#define JSA1212_PXS_DATA_MASK 0xFF
#define JSA1212_ALS_DATA_MASK 0x0FFF
#define JSA1212_ALS_DT1_MASK 0xFF
#define JSA1212_ALS_DT2_MASK 0x0F
#define JSA1212_ALS_RNG_MASK 0x07
/* JSA1212 CONF REG bits */
#define JSA1212_CONF_PXS_MASK 0x80
#define JSA1212_CONF_PXS_ENABLE 0x80
#define JSA1212_CONF_PXS_DISABLE 0x00
#define JSA1212_CONF_ALS_MASK 0x04
#define JSA1212_CONF_ALS_ENABLE 0x04
#define JSA1212_CONF_ALS_DISABLE 0x00
#define JSA1212_CONF_IRDR_MASK 0x08
/* Proxmity sensing IRDR current sink settings */
#define JSA1212_CONF_IRDR_200MA 0x08
#define JSA1212_CONF_IRDR_100MA 0x00
#define JSA1212_CONF_PXS_SLP_MASK 0x70
#define JSA1212_CONF_PXS_SLP_0MS 0x70
#define JSA1212_CONF_PXS_SLP_12MS 0x60
#define JSA1212_CONF_PXS_SLP_50MS 0x50
#define JSA1212_CONF_PXS_SLP_75MS 0x40
#define JSA1212_CONF_PXS_SLP_100MS 0x30
#define JSA1212_CONF_PXS_SLP_200MS 0x20
#define JSA1212_CONF_PXS_SLP_400MS 0x10
#define JSA1212_CONF_PXS_SLP_800MS 0x00
/* JSA1212 INT REG bits */
#define JSA1212_INT_CTRL_MASK 0x01
#define JSA1212_INT_CTRL_EITHER 0x00
#define JSA1212_INT_CTRL_BOTH 0x01
#define JSA1212_INT_ALS_PRST_MASK 0x06
#define JSA1212_INT_ALS_PRST_1CONV 0x00
#define JSA1212_INT_ALS_PRST_4CONV 0x02
#define JSA1212_INT_ALS_PRST_8CONV 0x04
#define JSA1212_INT_ALS_PRST_16CONV 0x06
#define JSA1212_INT_ALS_FLAG_MASK 0x08
#define JSA1212_INT_ALS_FLAG_CLR 0x00
#define JSA1212_INT_PXS_PRST_MASK 0x60
#define JSA1212_INT_PXS_PRST_1CONV 0x00
#define JSA1212_INT_PXS_PRST_4CONV 0x20
#define JSA1212_INT_PXS_PRST_8CONV 0x40
#define JSA1212_INT_PXS_PRST_16CONV 0x60
#define JSA1212_INT_PXS_FLAG_MASK 0x80
#define JSA1212_INT_PXS_FLAG_CLR 0x00
/* JSA1212 ALS RNG REG bits */
#define JSA1212_ALS_RNG_0_2048 0x00
#define JSA1212_ALS_RNG_0_1024 0x01
#define JSA1212_ALS_RNG_0_512 0x02
#define JSA1212_ALS_RNG_0_256 0x03
#define JSA1212_ALS_RNG_0_128 0x04
/* JSA1212 INT threshold range */
#define JSA1212_ALS_TH_MIN 0x0000
#define JSA1212_ALS_TH_MAX 0x0FFF
#define JSA1212_PXS_TH_MIN 0x00
#define JSA1212_PXS_TH_MAX 0xFF
#define JSA1212_ALS_DELAY_MS 200
#define JSA1212_PXS_DELAY_MS 100
#define JSA1212_DRIVER_NAME "jsa1212"
#define JSA1212_REGMAP_NAME "jsa1212_regmap"
enum jsa1212_op_mode {
JSA1212_OPMODE_ALS_EN,
JSA1212_OPMODE_PXS_EN,
};
struct jsa1212_data {
struct i2c_client *client;
struct mutex lock;
u8 als_rng_idx;
bool als_en; /* ALS enable status */
bool pxs_en; /* proximity enable status */
struct regmap *regmap;
};
/* ALS range idx to val mapping */
static const int jsa1212_als_range_val[] = {2048, 1024, 512, 256, 128,
128, 128, 128};
/* Enables or disables ALS function based on status */
static int jsa1212_als_enable(struct jsa1212_data *data, u8 status)
{
int ret;
ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
JSA1212_CONF_ALS_MASK,
status);
if (ret < 0)
return ret;
data->als_en = !!status;
return 0;
}
/* Enables or disables PXS function based on status */
static int jsa1212_pxs_enable(struct jsa1212_data *data, u8 status)
{
int ret;
ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
JSA1212_CONF_PXS_MASK,
status);
if (ret < 0)
return ret;
data->pxs_en = !!status;
return 0;
}
static int jsa1212_read_als_data(struct jsa1212_data *data,
unsigned int *val)
{
int ret;
__le16 als_data;
ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
if (ret < 0)
return ret;
/* Delay for data output */
msleep(JSA1212_ALS_DELAY_MS);
/* Read 12 bit data */
ret = regmap_bulk_read(data->regmap, JSA1212_ALS_DT1_REG, &als_data, 2);
if (ret < 0) {
dev_err(&data->client->dev, "als data read err\n");
goto als_data_read_err;
}
*val = le16_to_cpu(als_data);
als_data_read_err:
return jsa1212_als_enable(data, JSA1212_CONF_ALS_DISABLE);
}
static int jsa1212_read_pxs_data(struct jsa1212_data *data,
unsigned int *val)
{
int ret;
unsigned int pxs_data;
ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
if (ret < 0)
return ret;
/* Delay for data output */
msleep(JSA1212_PXS_DELAY_MS);
/* Read out all data */
ret = regmap_read(data->regmap, JSA1212_PXS_DATA_REG, &pxs_data);
if (ret < 0) {
dev_err(&data->client->dev, "pxs data read err\n");
goto pxs_data_read_err;
}
*val = pxs_data & JSA1212_PXS_DATA_MASK;
pxs_data_read_err:
return jsa1212_pxs_enable(data, JSA1212_CONF_PXS_DISABLE);
}
static int jsa1212_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
struct jsa1212_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->lock);
switch (chan->type) {
case IIO_LIGHT:
ret = jsa1212_read_als_data(data, val);
break;
case IIO_PROXIMITY:
ret = jsa1212_read_pxs_data(data, val);
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&data->lock);
return ret < 0 ? ret : IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_LIGHT:
*val = jsa1212_als_range_val[data->als_rng_idx];
*val2 = BIT(12); /* Max 12 bit value */
return IIO_VAL_FRACTIONAL;
default:
break;
}
break;
default:
break;
}
return -EINVAL;
}
static const struct iio_chan_spec jsa1212_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
},
{
.type = IIO_PROXIMITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}
};
static const struct iio_info jsa1212_info = {
.driver_module = THIS_MODULE,
.read_raw = &jsa1212_read_raw,
};
static int jsa1212_chip_init(struct jsa1212_data *data)
{
int ret;
ret = regmap_write(data->regmap, JSA1212_CONF_REG,
(JSA1212_CONF_PXS_SLP_50MS |
JSA1212_CONF_IRDR_200MA));
if (ret < 0)
return ret;
ret = regmap_write(data->regmap, JSA1212_INT_REG,
JSA1212_INT_ALS_PRST_4CONV);
if (ret < 0)
return ret;
data->als_rng_idx = JSA1212_ALS_RNG_0_2048;
return 0;
}
static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case JSA1212_PXS_DATA_REG:
case JSA1212_ALS_DT1_REG:
case JSA1212_ALS_DT2_REG:
case JSA1212_INT_REG:
return true;
default:
return false;
}
}
static struct regmap_config jsa1212_regmap_config = {
.name = JSA1212_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
.max_register = JSA1212_MAX_REG,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = jsa1212_is_volatile_reg,
};
static int jsa1212_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct jsa1212_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &jsa1212_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Regmap initialization failed.\n");
return PTR_ERR(regmap);
}
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->regmap = regmap;
mutex_init(&data->lock);
ret = jsa1212_chip_init(data);
if (ret < 0)
return ret;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = jsa1212_channels;
indio_dev->num_channels = ARRAY_SIZE(jsa1212_channels);
indio_dev->name = JSA1212_DRIVER_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &jsa1212_info;
ret = iio_device_register(indio_dev);
if (ret < 0)
dev_err(&client->dev, "%s: register device failed\n", __func__);
return ret;
}
/* power off the device */
static int jsa1212_power_off(struct jsa1212_data *data)
{
int ret;
mutex_lock(&data->lock);
ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
JSA1212_CONF_ALS_MASK |
JSA1212_CONF_PXS_MASK,
JSA1212_CONF_ALS_DISABLE |
JSA1212_CONF_PXS_DISABLE);
if (ret < 0)
dev_err(&data->client->dev, "power off cmd failed\n");
mutex_unlock(&data->lock);
return ret;
}
static int jsa1212_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct jsa1212_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
return jsa1212_power_off(data);
}
#ifdef CONFIG_PM_SLEEP
static int jsa1212_suspend(struct device *dev)
{
struct jsa1212_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return jsa1212_power_off(data);
}
static int jsa1212_resume(struct device *dev)
{
int ret = 0;
struct jsa1212_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
mutex_lock(&data->lock);
if (data->als_en) {
ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
if (ret < 0) {
dev_err(dev, "als resume failed\n");
goto unlock_and_ret;
}
}
if (data->pxs_en) {
ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
if (ret < 0)
dev_err(dev, "pxs resume failed\n");
}
unlock_and_ret:
mutex_unlock(&data->lock);
return ret;
}
static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume);
#define JSA1212_PM_OPS (&jsa1212_pm_ops)
#else
#define JSA1212_PM_OPS NULL
#endif
static const struct acpi_device_id jsa1212_acpi_match[] = {
{"JSA1212", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match);
static const struct i2c_device_id jsa1212_id[] = {
{ JSA1212_DRIVER_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, jsa1212_id);
static struct i2c_driver jsa1212_driver = {
.driver = {
.name = JSA1212_DRIVER_NAME,
.pm = JSA1212_PM_OPS,
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
},
.probe = jsa1212_probe,
.remove = jsa1212_remove,
.id_table = jsa1212_id,
};
module_i2c_driver(jsa1212_driver);
MODULE_AUTHOR("Sathya Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com>");
MODULE_DESCRIPTION("JSA1212 proximity/ambient light sensor driver");
MODULE_LICENSE("GPL v2");

View File

@ -657,7 +657,7 @@ static ALS_HYSTERESIS_ATTR_RO(3);
#define ILLUMINANCE_ATTR_RO(_name) \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL)
#define ILLUMINANCE_ATTR_RW(_name) \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \
show_##_name, store_##_name)
/*
* ALS Zone threshold-event enable

View File

@ -149,8 +149,8 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev,
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
*val = tcs3414_scales[i][0];
i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
*val = tcs3414_scales[i][0];
*val2 = tcs3414_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_INT_TIME:

View File

@ -6,26 +6,21 @@
menu "Magnetometer sensors"
config AK8975
tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
tristate "Asahi Kasei AK 3-Axis Magnetometer"
depends on I2C
depends on GPIOLIB
help
Say yes here to build support for Asahi Kasei AK8975 3-Axis
Magnetometer. This driver can also support AK8963, if i2c
device name is identified as ak8963.
Say yes here to build support for Asahi Kasei AK8975, AK8963,
AK09911 or AK09912 3-Axis Magnetometer.
To compile this driver as a module, choose M here: the module
will be called ak8975.
config AK09911
tristate "Asahi Kasei AK09911 3-axis Compass"
depends on I2C
select AK8975
help
Say yes here to build support for Asahi Kasei AK09911 3-Axis
Magnetometer.
To compile this driver as a module, choose M here: the module
will be called ak09911.
Deprecated: AK09911 is now supported by AK8975 driver.
config MAG3110
tristate "Freescale MAG3110 3-Axis Magnetometer"

View File

@ -3,7 +3,6 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AK09911) += ak09911.o
obj-$(CONFIG_AK8975) += ak8975.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o

View File

@ -1,326 +0,0 @@
/*
* AK09911 3-axis compass driver
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/iio/iio.h>
#define AK09911_REG_WIA1 0x00
#define AK09911_REG_WIA2 0x01
#define AK09911_WIA1_VALUE 0x48
#define AK09911_WIA2_VALUE 0x05
#define AK09911_REG_ST1 0x10
#define AK09911_REG_HXL 0x11
#define AK09911_REG_HXH 0x12
#define AK09911_REG_HYL 0x13
#define AK09911_REG_HYH 0x14
#define AK09911_REG_HZL 0x15
#define AK09911_REG_HZH 0x16
#define AK09911_REG_ASAX 0x60
#define AK09911_REG_ASAY 0x61
#define AK09911_REG_ASAZ 0x62
#define AK09911_REG_CNTL1 0x30
#define AK09911_REG_CNTL2 0x31
#define AK09911_REG_CNTL3 0x32
#define AK09911_MODE_SNG_MEASURE 0x01
#define AK09911_MODE_SELF_TEST 0x10
#define AK09911_MODE_FUSE_ACCESS 0x1F
#define AK09911_MODE_POWERDOWN 0x00
#define AK09911_RESET_DATA 0x01
#define AK09911_REG_CNTL1 0x30
#define AK09911_REG_CNTL2 0x31
#define AK09911_REG_CNTL3 0x32
#define AK09911_RAW_TO_GAUSS(asa) ((((asa) + 128) * 6000) / 256)
#define AK09911_MAX_CONVERSION_TIMEOUT_MS 500
#define AK09911_CONVERSION_DONE_POLL_TIME_MS 10
struct ak09911_data {
struct i2c_client *client;
struct mutex lock;
u8 asa[3];
long raw_to_gauss[3];
};
static const int ak09911_index_to_reg[] = {
AK09911_REG_HXL, AK09911_REG_HYL, AK09911_REG_HZL,
};
static int ak09911_set_mode(struct i2c_client *client, u8 mode)
{
int ret;
switch (mode) {
case AK09911_MODE_SNG_MEASURE:
case AK09911_MODE_SELF_TEST:
case AK09911_MODE_FUSE_ACCESS:
case AK09911_MODE_POWERDOWN:
ret = i2c_smbus_write_byte_data(client,
AK09911_REG_CNTL2, mode);
if (ret < 0) {
dev_err(&client->dev, "set_mode error\n");
return ret;
}
/* After mode change wait atleast 100us */
usleep_range(100, 500);
break;
default:
dev_err(&client->dev,
"%s: Unknown mode(%d).", __func__, mode);
return -EINVAL;
}
return ret;
}
/* Get Sensitivity Adjustment value */
static int ak09911_get_asa(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ak09911_data *data = iio_priv(indio_dev);
int ret;
ret = ak09911_set_mode(client, AK09911_MODE_FUSE_ACCESS);
if (ret < 0)
return ret;
/* Get asa data and store in the device data. */
ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_ASAX,
3, data->asa);
if (ret < 0) {
dev_err(&client->dev, "Not able to read asa data\n");
return ret;
}
ret = ak09911_set_mode(client, AK09911_MODE_POWERDOWN);
if (ret < 0)
return ret;
data->raw_to_gauss[0] = AK09911_RAW_TO_GAUSS(data->asa[0]);
data->raw_to_gauss[1] = AK09911_RAW_TO_GAUSS(data->asa[1]);
data->raw_to_gauss[2] = AK09911_RAW_TO_GAUSS(data->asa[2]);
return 0;
}
static int ak09911_verify_chip_id(struct i2c_client *client)
{
u8 wia_val[2];
int ret;
ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_WIA1,
2, wia_val);
if (ret < 0) {
dev_err(&client->dev, "Error reading WIA\n");
return ret;
}
dev_dbg(&client->dev, "WIA %02x %02x\n", wia_val[0], wia_val[1]);
if (wia_val[0] != AK09911_WIA1_VALUE ||
wia_val[1] != AK09911_WIA2_VALUE) {
dev_err(&client->dev, "Device ak09911 not found\n");
return -ENODEV;
}
return 0;
}
static int wait_conversion_complete_polled(struct ak09911_data *data)
{
struct i2c_client *client = data->client;
u8 read_status;
u32 timeout_ms = AK09911_MAX_CONVERSION_TIMEOUT_MS;
int ret;
/* Wait for the conversion to complete. */
while (timeout_ms) {
msleep_interruptible(AK09911_CONVERSION_DONE_POLL_TIME_MS);
ret = i2c_smbus_read_byte_data(client, AK09911_REG_ST1);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST1\n");
return ret;
}
read_status = ret & 0x01;
if (read_status)
break;
timeout_ms -= AK09911_CONVERSION_DONE_POLL_TIME_MS;
}
if (!timeout_ms) {
dev_err(&client->dev, "Conversion timeout happened\n");
return -EIO;
}
return read_status;
}
static int ak09911_read_axis(struct iio_dev *indio_dev, int index, int *val)
{
struct ak09911_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
int ret;
mutex_lock(&data->lock);
ret = ak09911_set_mode(client, AK09911_MODE_SNG_MEASURE);
if (ret < 0)
goto fn_exit;
ret = wait_conversion_complete_polled(data);
if (ret < 0)
goto fn_exit;
/* Read data */
ret = i2c_smbus_read_word_data(client, ak09911_index_to_reg[index]);
if (ret < 0) {
dev_err(&client->dev, "Read axis data fails\n");
goto fn_exit;
}
mutex_unlock(&data->lock);
/* Clamp to valid range. */
*val = sign_extend32(clamp_t(s16, ret, -8192, 8191), 13);
return IIO_VAL_INT;
fn_exit:
mutex_unlock(&data->lock);
return ret;
}
static int ak09911_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2,
long mask)
{
struct ak09911_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return ak09911_read_axis(indio_dev, chan->address, val);
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = data->raw_to_gauss[chan->address];
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
#define AK09911_CHANNEL(axis, index) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.address = index, \
}
static const struct iio_chan_spec ak09911_channels[] = {
AK09911_CHANNEL(X, 0), AK09911_CHANNEL(Y, 1), AK09911_CHANNEL(Z, 2),
};
static const struct iio_info ak09911_info = {
.read_raw = &ak09911_read_raw,
.driver_module = THIS_MODULE,
};
static const struct acpi_device_id ak_acpi_match[] = {
{"AK009911", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
static int ak09911_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct ak09911_data *data;
const char *name;
int ret;
ret = ak09911_verify_chip_id(client);
if (ret) {
dev_err(&client->dev, "AK00911 not detected\n");
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (indio_dev == NULL)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
mutex_init(&data->lock);
ret = ak09911_get_asa(client);
if (ret)
return ret;
if (id)
name = id->name;
else if (ACPI_HANDLE(&client->dev))
name = dev_name(&client->dev);
else
return -ENODEV;
dev_dbg(&client->dev, "Asahi compass chip %s\n", name);
indio_dev->dev.parent = &client->dev;
indio_dev->channels = ak09911_channels;
indio_dev->num_channels = ARRAY_SIZE(ak09911_channels);
indio_dev->info = &ak09911_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = name;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id ak09911_id[] = {
{"ak09911", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ak09911_id);
static struct i2c_driver ak09911_driver = {
.driver = {
.name = "ak09911",
.acpi_match_table = ACPI_PTR(ak_acpi_match),
},
.probe = ak09911_probe,
.id_table = ak09911_id,
};
module_i2c_driver(ak09911_driver);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("AK09911 Compass driver");

View File

@ -64,10 +64,10 @@
#define AK8975_REG_CNTL 0x0A
#define AK8975_REG_CNTL_MODE_SHIFT 0
#define AK8975_REG_CNTL_MODE_MASK (0xF << AK8975_REG_CNTL_MODE_SHIFT)
#define AK8975_REG_CNTL_MODE_POWER_DOWN 0
#define AK8975_REG_CNTL_MODE_ONCE 1
#define AK8975_REG_CNTL_MODE_SELF_TEST 8
#define AK8975_REG_CNTL_MODE_FUSE_ROM 0xF
#define AK8975_REG_CNTL_MODE_POWER_DOWN 0x00
#define AK8975_REG_CNTL_MODE_ONCE 0x01
#define AK8975_REG_CNTL_MODE_SELF_TEST 0x08
#define AK8975_REG_CNTL_MODE_FUSE_ROM 0x0F
#define AK8975_REG_RSVC 0x0B
#define AK8975_REG_ASTC 0x0C
@ -80,19 +80,279 @@
#define AK8975_MAX_REGS AK8975_REG_ASAZ
/*
* AK09912 Register definitions
*/
#define AK09912_REG_WIA1 0x00
#define AK09912_REG_WIA2 0x01
#define AK09912_DEVICE_ID 0x04
#define AK09911_DEVICE_ID 0x05
#define AK09911_REG_INFO1 0x02
#define AK09911_REG_INFO2 0x03
#define AK09912_REG_ST1 0x10
#define AK09912_REG_ST1_DRDY_SHIFT 0
#define AK09912_REG_ST1_DRDY_MASK (1 << AK09912_REG_ST1_DRDY_SHIFT)
#define AK09912_REG_HXL 0x11
#define AK09912_REG_HXH 0x12
#define AK09912_REG_HYL 0x13
#define AK09912_REG_HYH 0x14
#define AK09912_REG_HZL 0x15
#define AK09912_REG_HZH 0x16
#define AK09912_REG_TMPS 0x17
#define AK09912_REG_ST2 0x18
#define AK09912_REG_ST2_HOFL_SHIFT 3
#define AK09912_REG_ST2_HOFL_MASK (1 << AK09912_REG_ST2_HOFL_SHIFT)
#define AK09912_REG_CNTL1 0x30
#define AK09912_REG_CNTL2 0x31
#define AK09912_REG_CNTL_MODE_POWER_DOWN 0x00
#define AK09912_REG_CNTL_MODE_ONCE 0x01
#define AK09912_REG_CNTL_MODE_SELF_TEST 0x10
#define AK09912_REG_CNTL_MODE_FUSE_ROM 0x1F
#define AK09912_REG_CNTL2_MODE_SHIFT 0
#define AK09912_REG_CNTL2_MODE_MASK (0x1F << AK09912_REG_CNTL2_MODE_SHIFT)
#define AK09912_REG_CNTL3 0x32
#define AK09912_REG_TS1 0x33
#define AK09912_REG_TS2 0x34
#define AK09912_REG_TS3 0x35
#define AK09912_REG_I2CDIS 0x36
#define AK09912_REG_TS4 0x37
#define AK09912_REG_ASAX 0x60
#define AK09912_REG_ASAY 0x61
#define AK09912_REG_ASAZ 0x62
#define AK09912_MAX_REGS AK09912_REG_ASAZ
/*
* Miscellaneous values.
*/
#define AK8975_MAX_CONVERSION_TIMEOUT 500
#define AK8975_CONVERSION_DONE_POLL_TIME 10
#define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000)
#define RAW_TO_GAUSS_8975(asa) ((((asa) + 128) * 3000) / 256)
#define RAW_TO_GAUSS_8963(asa) ((((asa) + 128) * 6000) / 256)
/*
* Precalculate scale factor (in Gauss units) for each axis and
* store in the device data.
*
* This scale factor is axis-dependent, and is derived from 3 calibration
* factors ASA(x), ASA(y), and ASA(z).
*
* These ASA values are read from the sensor device at start of day, and
* cached in the device context struct.
*
* Adjusting the flux value with the sensitivity adjustment value should be
* done via the following formula:
*
* Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
* where H is the raw value, ASA is the sensitivity adjustment, and Hadj
* is the resultant adjusted value.
*
* We reduce the formula to:
*
* Hadj = H * (ASA + 128) / 256
*
* H is in the range of -4096 to 4095. The magnetometer has a range of
* +-1229uT. To go from the raw value to uT is:
*
* HuT = H * 1229/4096, or roughly, 3/10.
*
* Since 1uT = 0.01 gauss, our final scale factor becomes:
*
* Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100
* Hadj = H * ((ASA + 128) * 0.003) / 256
*
* Since ASA doesn't change, we cache the resultant scale factor into the
* device context in ak8975_setup().
*
* Given we use IIO_VAL_INT_PLUS_MICRO bit when displaying the scale, we
* multiply the stored scale value by 1e6.
*/
static long ak8975_raw_to_gauss(u16 data)
{
return (((long)data + 128) * 3000) / 256;
}
/*
* For AK8963 and AK09911, same calculation, but the device is less sensitive:
*
* H is in the range of +-8190. The magnetometer has a range of
* +-4912uT. To go from the raw value to uT is:
*
* HuT = H * 4912/8190, or roughly, 6/10, instead of 3/10.
*/
static long ak8963_09911_raw_to_gauss(u16 data)
{
return (((long)data + 128) * 6000) / 256;
}
/*
* For AK09912, same calculation, except the device is more sensitive:
*
* H is in the range of -32752 to 32752. The magnetometer has a range of
* +-4912uT. To go from the raw value to uT is:
*
* HuT = H * 4912/32752, or roughly, 3/20, instead of 3/10.
*/
static long ak09912_raw_to_gauss(u16 data)
{
return (((long)data + 128) * 1500) / 256;
}
/* Compatible Asahi Kasei Compass parts */
enum asahi_compass_chipset {
AK8975,
AK8963,
AK09911,
AK09912,
AK_MAX_TYPE
};
enum ak_ctrl_reg_addr {
ST1,
ST2,
CNTL,
ASA_BASE,
MAX_REGS,
REGS_END,
};
enum ak_ctrl_reg_mask {
ST1_DRDY,
ST2_HOFL,
ST2_DERR,
CNTL_MODE,
MASK_END,
};
enum ak_ctrl_mode {
POWER_DOWN,
MODE_ONCE,
SELF_TEST,
FUSE_ROM,
MODE_END,
};
struct ak_def {
enum asahi_compass_chipset type;
long (*raw_to_gauss)(u16 data);
u16 range;
u8 ctrl_regs[REGS_END];
u8 ctrl_masks[MASK_END];
u8 ctrl_modes[MODE_END];
u8 data_regs[3];
};
static struct ak_def ak_def_array[AK_MAX_TYPE] = {
{
.type = AK8975,
.raw_to_gauss = ak8975_raw_to_gauss,
.range = 4096,
.ctrl_regs = {
AK8975_REG_ST1,
AK8975_REG_ST2,
AK8975_REG_CNTL,
AK8975_REG_ASAX,
AK8975_MAX_REGS},
.ctrl_masks = {
AK8975_REG_ST1_DRDY_MASK,
AK8975_REG_ST2_HOFL_MASK,
AK8975_REG_ST2_DERR_MASK,
AK8975_REG_CNTL_MODE_MASK},
.ctrl_modes = {
AK8975_REG_CNTL_MODE_POWER_DOWN,
AK8975_REG_CNTL_MODE_ONCE,
AK8975_REG_CNTL_MODE_SELF_TEST,
AK8975_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK8975_REG_HXL,
AK8975_REG_HYL,
AK8975_REG_HZL},
},
{
.type = AK8963,
.raw_to_gauss = ak8963_09911_raw_to_gauss,
.range = 8190,
.ctrl_regs = {
AK8975_REG_ST1,
AK8975_REG_ST2,
AK8975_REG_CNTL,
AK8975_REG_ASAX,
AK8975_MAX_REGS},
.ctrl_masks = {
AK8975_REG_ST1_DRDY_MASK,
AK8975_REG_ST2_HOFL_MASK,
0,
AK8975_REG_CNTL_MODE_MASK},
.ctrl_modes = {
AK8975_REG_CNTL_MODE_POWER_DOWN,
AK8975_REG_CNTL_MODE_ONCE,
AK8975_REG_CNTL_MODE_SELF_TEST,
AK8975_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK8975_REG_HXL,
AK8975_REG_HYL,
AK8975_REG_HZL},
},
{
.type = AK09911,
.raw_to_gauss = ak8963_09911_raw_to_gauss,
.range = 8192,
.ctrl_regs = {
AK09912_REG_ST1,
AK09912_REG_ST2,
AK09912_REG_CNTL2,
AK09912_REG_ASAX,
AK09912_MAX_REGS},
.ctrl_masks = {
AK09912_REG_ST1_DRDY_MASK,
AK09912_REG_ST2_HOFL_MASK,
0,
AK09912_REG_CNTL2_MODE_MASK},
.ctrl_modes = {
AK09912_REG_CNTL_MODE_POWER_DOWN,
AK09912_REG_CNTL_MODE_ONCE,
AK09912_REG_CNTL_MODE_SELF_TEST,
AK09912_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK09912_REG_HXL,
AK09912_REG_HYL,
AK09912_REG_HZL},
},
{
.type = AK09912,
.raw_to_gauss = ak09912_raw_to_gauss,
.range = 32752,
.ctrl_regs = {
AK09912_REG_ST1,
AK09912_REG_ST2,
AK09912_REG_CNTL2,
AK09912_REG_ASAX,
AK09912_MAX_REGS},
.ctrl_masks = {
AK09912_REG_ST1_DRDY_MASK,
AK09912_REG_ST2_HOFL_MASK,
0,
AK09912_REG_CNTL2_MODE_MASK},
.ctrl_modes = {
AK09912_REG_CNTL_MODE_POWER_DOWN,
AK09912_REG_CNTL_MODE_ONCE,
AK09912_REG_CNTL_MODE_SELF_TEST,
AK09912_REG_CNTL_MODE_FUSE_ROM},
.data_regs = {
AK09912_REG_HXL,
AK09912_REG_HYL,
AK09912_REG_HZL},
}
};
/*
@ -100,40 +360,82 @@ enum asahi_compass_chipset {
*/
struct ak8975_data {
struct i2c_client *client;
struct ak_def *def;
struct attribute_group attrs;
struct mutex lock;
u8 asa[3];
long raw_to_gauss[3];
u8 reg_cache[AK8975_MAX_REGS];
int eoc_gpio;
int eoc_irq;
wait_queue_head_t data_ready_queue;
unsigned long flags;
enum asahi_compass_chipset chipset;
};
static const int ak8975_index_to_reg[] = {
AK8975_REG_HXL, AK8975_REG_HYL, AK8975_REG_HZL,
u8 cntl_cache;
};
/*
* Helper function to write to the I2C device's registers.
* Return 0 if the i2c device is the one we expect.
* return a negative error number otherwise
*/
static int ak8975_write_data(struct i2c_client *client,
u8 reg, u8 val, u8 mask, u8 shift)
static int ak8975_who_i_am(struct i2c_client *client,
enum asahi_compass_chipset type)
{
u8 wia_val[2];
int ret;
/*
* Signature for each device:
* Device | WIA1 | WIA2
* AK09912 | DEVICE_ID | AK09912_DEVICE_ID
* AK09911 | DEVICE_ID | AK09911_DEVICE_ID
* AK8975 | DEVICE_ID | NA
* AK8963 | DEVICE_ID | NA
*/
ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1,
2, wia_val);
if (ret < 0) {
dev_err(&client->dev, "Error reading WIA\n");
return ret;
}
if (wia_val[0] != AK8975_DEVICE_ID)
return -ENODEV;
switch (type) {
case AK8975:
case AK8963:
return 0;
case AK09911:
if (wia_val[1] == AK09911_DEVICE_ID)
return 0;
break;
case AK09912:
if (wia_val[1] == AK09912_DEVICE_ID)
return 0;
break;
default:
dev_err(&client->dev, "Type %d unknown\n", type);
}
return -ENODEV;
}
/*
* Helper function to write to CNTL register.
*/
static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ak8975_data *data = iio_priv(indio_dev);
u8 regval;
int ret;
regval = (data->reg_cache[reg] & ~mask) | (val << shift);
ret = i2c_smbus_write_byte_data(client, reg, regval);
regval = (data->cntl_cache & ~data->def->ctrl_masks[CNTL_MODE]) |
data->def->ctrl_modes[mode];
ret = i2c_smbus_write_byte_data(data->client,
data->def->ctrl_regs[CNTL], regval);
if (ret < 0) {
dev_err(&client->dev, "Write to device fails status %x\n", ret);
return ret;
}
data->reg_cache[reg] = regval;
data->cntl_cache = regval;
/* After mode change wait atleast 100us */
usleep_range(100, 500);
return 0;
}
@ -166,8 +468,8 @@ static int ak8975_setup_irq(struct ak8975_data *data)
irq = gpio_to_irq(data->eoc_gpio);
rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
dev_name(&client->dev), data);
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
dev_name(&client->dev), data);
if (rc < 0) {
dev_err(&client->dev,
"irq %d request failed, (gpio %d): %d\n",
@ -191,34 +493,18 @@ static int ak8975_setup(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ak8975_data *data = iio_priv(indio_dev);
u8 device_id;
int ret;
/* Confirm that the device we're talking to is really an AK8975. */
ret = i2c_smbus_read_byte_data(client, AK8975_REG_WIA);
if (ret < 0) {
dev_err(&client->dev, "Error reading WIA\n");
return ret;
}
device_id = ret;
if (device_id != AK8975_DEVICE_ID) {
dev_err(&client->dev, "Device ak8975 not found\n");
return -ENODEV;
}
/* Write the fused rom access mode. */
ret = ak8975_write_data(client,
AK8975_REG_CNTL,
AK8975_REG_CNTL_MODE_FUSE_ROM,
AK8975_REG_CNTL_MODE_MASK,
AK8975_REG_CNTL_MODE_SHIFT);
ret = ak8975_set_mode(data, FUSE_ROM);
if (ret < 0) {
dev_err(&client->dev, "Error in setting fuse access mode\n");
return ret;
}
/* Get asa data and store in the device data. */
ret = i2c_smbus_read_i2c_block_data(client, AK8975_REG_ASAX,
ret = i2c_smbus_read_i2c_block_data(client,
data->def->ctrl_regs[ASA_BASE],
3, data->asa);
if (ret < 0) {
dev_err(&client->dev, "Not able to read asa data\n");
@ -226,13 +512,13 @@ static int ak8975_setup(struct i2c_client *client)
}
/* After reading fuse ROM data set power-down mode */
ret = ak8975_write_data(client,
AK8975_REG_CNTL,
AK8975_REG_CNTL_MODE_POWER_DOWN,
AK8975_REG_CNTL_MODE_MASK,
AK8975_REG_CNTL_MODE_SHIFT);
ret = ak8975_set_mode(data, POWER_DOWN);
if (ret < 0) {
dev_err(&client->dev, "Error in setting power-down mode\n");
return ret;
}
if (data->eoc_gpio > 0 || client->irq) {
if (data->eoc_gpio > 0 || client->irq > 0) {
ret = ak8975_setup_irq(data);
if (ret < 0) {
dev_err(&client->dev,
@ -241,61 +527,9 @@ static int ak8975_setup(struct i2c_client *client)
}
}
if (ret < 0) {
dev_err(&client->dev, "Error in setting power-down mode\n");
return ret;
}
/*
* Precalculate scale factor (in Gauss units) for each axis and
* store in the device data.
*
* This scale factor is axis-dependent, and is derived from 3 calibration
* factors ASA(x), ASA(y), and ASA(z).
*
* These ASA values are read from the sensor device at start of day, and
* cached in the device context struct.
*
* Adjusting the flux value with the sensitivity adjustment value should be
* done via the following formula:
*
* Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
*
* where H is the raw value, ASA is the sensitivity adjustment, and Hadj
* is the resultant adjusted value.
*
* We reduce the formula to:
*
* Hadj = H * (ASA + 128) / 256
*
* H is in the range of -4096 to 4095. The magnetometer has a range of
* +-1229uT. To go from the raw value to uT is:
*
* HuT = H * 1229/4096, or roughly, 3/10.
*
* Since 1uT = 0.01 gauss, our final scale factor becomes:
*
* Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100
* Hadj = H * ((ASA + 128) * 0.003) / 256
*
* Since ASA doesn't change, we cache the resultant scale factor into the
* device context in ak8975_setup().
*/
if (data->chipset == AK8963) {
/*
* H range is +-8190 and magnetometer range is +-4912.
* So HuT using the above explanation for 8975,
* 4912/8190 = ~ 6/10.
* So the Hadj should use 6/10 instead of 3/10.
*/
data->raw_to_gauss[0] = RAW_TO_GAUSS_8963(data->asa[0]);
data->raw_to_gauss[1] = RAW_TO_GAUSS_8963(data->asa[1]);
data->raw_to_gauss[2] = RAW_TO_GAUSS_8963(data->asa[2]);
} else {
data->raw_to_gauss[0] = RAW_TO_GAUSS_8975(data->asa[0]);
data->raw_to_gauss[1] = RAW_TO_GAUSS_8975(data->asa[1]);
data->raw_to_gauss[2] = RAW_TO_GAUSS_8975(data->asa[2]);
}
data->raw_to_gauss[0] = data->def->raw_to_gauss(data->asa[0]);
data->raw_to_gauss[1] = data->def->raw_to_gauss(data->asa[1]);
data->raw_to_gauss[2] = data->def->raw_to_gauss(data->asa[2]);
return 0;
}
@ -318,7 +552,7 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data)
return -EINVAL;
}
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]);
if (ret < 0)
dev_err(&client->dev, "Error in reading ST1\n");
@ -335,7 +569,8 @@ static int wait_conversion_complete_polled(struct ak8975_data *data)
/* Wait for the conversion to complete. */
while (timeout_ms) {
msleep(AK8975_CONVERSION_DONE_POLL_TIME);
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
ret = i2c_smbus_read_byte_data(client,
data->def->ctrl_regs[ST1]);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST1\n");
return ret;
@ -378,11 +613,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
mutex_lock(&data->lock);
/* Set up the device for taking a sample. */
ret = ak8975_write_data(client,
AK8975_REG_CNTL,
AK8975_REG_CNTL_MODE_ONCE,
AK8975_REG_CNTL_MODE_MASK,
AK8975_REG_CNTL_MODE_SHIFT);
ret = ak8975_set_mode(data, MODE_ONCE);
if (ret < 0) {
dev_err(&client->dev, "Error in setting operating mode\n");
goto exit;
@ -399,14 +630,15 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
goto exit;
/* This will be executed only for non-interrupt based waiting case */
if (ret & AK8975_REG_ST1_DRDY_MASK) {
ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST2);
if (ret & data->def->ctrl_masks[ST1_DRDY]) {
ret = i2c_smbus_read_byte_data(client,
data->def->ctrl_regs[ST2]);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST2\n");
goto exit;
}
if (ret & (AK8975_REG_ST2_DERR_MASK |
AK8975_REG_ST2_HOFL_MASK)) {
if (ret & (data->def->ctrl_masks[ST2_DERR] |
data->def->ctrl_masks[ST2_HOFL])) {
dev_err(&client->dev, "ST2 status error 0x%x\n", ret);
ret = -EINVAL;
goto exit;
@ -415,7 +647,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
/* Read the flux value from the appropriate register
(the register is specified in the iio device attributes). */
ret = i2c_smbus_read_word_data(client, ak8975_index_to_reg[index]);
ret = i2c_smbus_read_word_data(client, data->def->data_regs[index]);
if (ret < 0) {
dev_err(&client->dev, "Read axis data fails\n");
goto exit;
@ -424,7 +656,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
mutex_unlock(&data->lock);
/* Clamp to valid range. */
*val = clamp_t(s16, ret, -4096, 4095);
*val = clamp_t(s16, ret, -data->def->range, data->def->range);
return IIO_VAL_INT;
exit:
@ -473,6 +705,8 @@ static const struct acpi_device_id ak_acpi_match[] = {
{"AK8975", AK8975},
{"AK8963", AK8963},
{"INVN6500", AK8963},
{"AK09911", AK09911},
{"AK09912", AK09912},
{ },
};
MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
@ -498,6 +732,7 @@ static int ak8975_probe(struct i2c_client *client,
int eoc_gpio;
int err;
const char *name = NULL;
enum asahi_compass_chipset chipset;
/* Grab and set up the supplied GPIO. */
if (client->dev.platform_data)
@ -537,42 +772,50 @@ static int ak8975_probe(struct i2c_client *client,
/* id will be NULL when enumerated via ACPI */
if (id) {
data->chipset =
(enum asahi_compass_chipset)(id->driver_data);
chipset = (enum asahi_compass_chipset)(id->driver_data);
name = id->name;
} else if (ACPI_HANDLE(&client->dev))
name = ak8975_match_acpi_device(&client->dev, &data->chipset);
name = ak8975_match_acpi_device(&client->dev, &chipset);
else
return -ENOSYS;
if (chipset >= AK_MAX_TYPE) {
dev_err(&client->dev, "AKM device type unsupported: %d\n",
chipset);
return -ENODEV;
}
data->def = &ak_def_array[chipset];
err = ak8975_who_i_am(client, data->def->type);
if (err < 0) {
dev_err(&client->dev, "Unexpected device\n");
return err;
}
dev_dbg(&client->dev, "Asahi compass chip %s\n", name);
/* Perform some basic start-of-day setup of the device. */
err = ak8975_setup(client);
if (err < 0) {
dev_err(&client->dev, "AK8975 initialization fails\n");
dev_err(&client->dev, "%s initialization fails\n", name);
return err;
}
data->client = client;
mutex_init(&data->lock);
data->eoc_gpio = eoc_gpio;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = ak8975_channels;
indio_dev->num_channels = ARRAY_SIZE(ak8975_channels);
indio_dev->info = &ak8975_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = name;
err = devm_iio_device_register(&client->dev, indio_dev);
if (err < 0)
return err;
return 0;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id ak8975_id[] = {
{"ak8975", AK8975},
{"ak8963", AK8963},
{"AK8963", AK8963},
{"ak09911", AK09911},
{"ak09912", AK09912},
{}
};
@ -581,14 +824,20 @@ MODULE_DEVICE_TABLE(i2c, ak8975_id);
static const struct of_device_id ak8975_of_match[] = {
{ .compatible = "asahi-kasei,ak8975", },
{ .compatible = "ak8975", },
{ }
{ .compatible = "asahi-kasei,ak8963", },
{ .compatible = "ak8963", },
{ .compatible = "asahi-kasei,ak09911", },
{ .compatible = "ak09911", },
{ .compatible = "asahi-kasei,ak09912", },
{ .compatible = "ak09912", },
{}
};
MODULE_DEVICE_TABLE(of, ak8975_of_match);
static struct i2c_driver ak8975_driver = {
.driver = {
.name = "ak8975",
.of_match_table = ak8975_of_match,
.of_match_table = of_match_ptr(ak8975_of_match),
.acpi_match_table = ACPI_PTR(ak_acpi_match),
},
.probe = ak8975_probe,

View File

@ -157,20 +157,12 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case 0:
poll_value = hid_sensor_read_poll_value(
&magn_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&magn_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id =
magn_state->magn[chan->address].report_id;
address = magn_3d_addresses[chan->address];
@ -530,6 +522,7 @@ static struct platform_driver hid_magn_3d_platform_driver = {
.id_table = hid_magn_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_magn_3d_probe,
.remove = hid_magn_3d_remove,

View File

@ -111,20 +111,12 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
poll_value = hid_sensor_read_poll_value(
&incl_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&incl_state->common_attributes, true);
msleep_interruptible(poll_value * 2);
report_id =
incl_state->incl[chan->scan_index].report_id;
address = incl_3d_addresses[chan->scan_index];
@ -437,6 +429,7 @@ static struct platform_driver hid_incl_3d_platform_driver = {
.id_table = hid_incl_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_incl_3d_probe,
.remove = hid_incl_3d_remove,

View File

@ -80,16 +80,12 @@ struct bmp280_data {
s32 t_fine;
};
/* Compensation parameters. */
struct bmp280_comp_temp {
u16 dig_t1;
s16 dig_t2, dig_t3;
};
struct bmp280_comp_press {
u16 dig_p1;
s16 dig_p2, dig_p3, dig_p4, dig_p5, dig_p6, dig_p7, dig_p8, dig_p9;
};
/*
* These enums are used for indexing into the array of compensation
* parameters.
*/
enum { T1, T2, T3 };
enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 };
static const struct iio_chan_spec bmp280_channels[] = {
{
@ -141,10 +137,18 @@ static const struct regmap_config bmp280_regmap_config = {
.volatile_reg = bmp280_is_volatile_reg,
};
static int bmp280_read_compensation_temp(struct bmp280_data *data,
struct bmp280_comp_temp *comp)
/*
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
* "5123" equals 51.23 DegC. t_fine carries fine temperature as global
* value.
*
* Taken from datasheet, Section 3.11.3, "Compensation formula".
*/
static s32 bmp280_compensate_temp(struct bmp280_data *data,
s32 adc_temp)
{
int ret;
s32 var1, var2;
__le16 buf[BMP280_COMP_TEMP_REG_COUNT / 2];
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START,
@ -155,63 +159,21 @@ static int bmp280_read_compensation_temp(struct bmp280_data *data,
return ret;
}
comp->dig_t1 = (u16) le16_to_cpu(buf[0]);
comp->dig_t2 = (s16) le16_to_cpu(buf[1]);
comp->dig_t3 = (s16) le16_to_cpu(buf[2]);
/*
* The double casts are necessary because le16_to_cpu returns an
* unsigned 16-bit value. Casting that value directly to a
* signed 32-bit will not do proper sign extension.
*
* Conversely, T1 and P1 are unsigned values, so they can be
* cast straight to the larger type.
*/
var1 = (((adc_temp >> 3) - ((s32)le16_to_cpu(buf[T1]) << 1)) *
((s32)(s16)le16_to_cpu(buf[T2]))) >> 11;
var2 = (((((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1]))) *
((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1])))) >> 12) *
((s32)(s16)le16_to_cpu(buf[T3]))) >> 14;
return 0;
}
static int bmp280_read_compensation_press(struct bmp280_data *data,
struct bmp280_comp_press *comp)
{
int ret;
__le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2];
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START,
buf, BMP280_COMP_PRESS_REG_COUNT);
if (ret < 0) {
dev_err(&data->client->dev,
"failed to read pressure calibration parameters\n");
return ret;
}
comp->dig_p1 = (u16) le16_to_cpu(buf[0]);
comp->dig_p2 = (s16) le16_to_cpu(buf[1]);
comp->dig_p3 = (s16) le16_to_cpu(buf[2]);
comp->dig_p4 = (s16) le16_to_cpu(buf[3]);
comp->dig_p5 = (s16) le16_to_cpu(buf[4]);
comp->dig_p6 = (s16) le16_to_cpu(buf[5]);
comp->dig_p7 = (s16) le16_to_cpu(buf[6]);
comp->dig_p8 = (s16) le16_to_cpu(buf[7]);
comp->dig_p9 = (s16) le16_to_cpu(buf[8]);
return 0;
}
/*
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
* "5123" equals 51.23 DegC. t_fine carries fine temperature as global
* value.
*
* Taken from datasheet, Section 3.11.3, "Compensation formula".
*/
static s32 bmp280_compensate_temp(struct bmp280_data *data,
struct bmp280_comp_temp *comp,
s32 adc_temp)
{
s32 var1, var2, t;
var1 = (((adc_temp >> 3) - ((s32) comp->dig_t1 << 1)) *
((s32) comp->dig_t2)) >> 11;
var2 = (((((adc_temp >> 4) - ((s32) comp->dig_t1)) *
((adc_temp >> 4) - ((s32) comp->dig_t1))) >> 12) *
((s32) comp->dig_t3)) >> 14;
data->t_fine = var1 + var2;
t = (data->t_fine * 5 + 128) >> 8;
return t;
return (data->t_fine * 5 + 128) >> 8;
}
/*
@ -222,29 +184,38 @@ static s32 bmp280_compensate_temp(struct bmp280_data *data,
* Taken from datasheet, Section 3.11.3, "Compensation formula".
*/
static u32 bmp280_compensate_press(struct bmp280_data *data,
struct bmp280_comp_press *comp,
s32 adc_press)
{
int ret;
s64 var1, var2, p;
__le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2];
var1 = ((s64) data->t_fine) - 128000;
var2 = var1 * var1 * (s64) comp->dig_p6;
var2 = var2 + ((var1 * (s64) comp->dig_p5) << 17);
var2 = var2 + (((s64) comp->dig_p4) << 35);
var1 = ((var1 * var1 * (s64) comp->dig_p3) >> 8) +
((var1 * (s64) comp->dig_p2) << 12);
var1 = (((((s64) 1) << 47) + var1)) * ((s64) comp->dig_p1) >> 33;
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START,
buf, BMP280_COMP_PRESS_REG_COUNT);
if (ret < 0) {
dev_err(&data->client->dev,
"failed to read pressure calibration parameters\n");
return ret;
}
var1 = ((s64)data->t_fine) - 128000;
var2 = var1 * var1 * (s64)(s16)le16_to_cpu(buf[P6]);
var2 += (var1 * (s64)(s16)le16_to_cpu(buf[P5])) << 17;
var2 += ((s64)(s16)le16_to_cpu(buf[P4])) << 35;
var1 = ((var1 * var1 * (s64)(s16)le16_to_cpu(buf[P3])) >> 8) +
((var1 * (s64)(s16)le16_to_cpu(buf[P2])) << 12);
var1 = ((((s64)1) << 47) + var1) * ((s64)le16_to_cpu(buf[P1])) >> 33;
if (var1 == 0)
return 0;
p = ((((s64) 1048576 - adc_press) << 31) - var2) * 3125;
p = ((((s64)1048576 - adc_press) << 31) - var2) * 3125;
p = div64_s64(p, var1);
var1 = (((s64) comp->dig_p9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((s64) comp->dig_p8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((s64) comp->dig_p7) << 4);
var1 = (((s64)(s16)le16_to_cpu(buf[P9])) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((s64)(s16)le16_to_cpu(buf[P8])) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((s64)(s16)le16_to_cpu(buf[P7])) << 4);
return (u32) p;
return (u32)p;
}
static int bmp280_read_temp(struct bmp280_data *data,
@ -253,11 +224,6 @@ static int bmp280_read_temp(struct bmp280_data *data,
int ret;
__be32 tmp = 0;
s32 adc_temp, comp_temp;
struct bmp280_comp_temp comp;
ret = bmp280_read_compensation_temp(data, &comp);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB,
(u8 *) &tmp, 3);
@ -267,7 +233,7 @@ static int bmp280_read_temp(struct bmp280_data *data,
}
adc_temp = be32_to_cpu(tmp) >> 12;
comp_temp = bmp280_compensate_temp(data, &comp, adc_temp);
comp_temp = bmp280_compensate_temp(data, adc_temp);
/*
* val might be NULL if we're called by the read_press routine,
@ -288,11 +254,6 @@ static int bmp280_read_press(struct bmp280_data *data,
__be32 tmp = 0;
s32 adc_press;
u32 comp_press;
struct bmp280_comp_press comp;
ret = bmp280_read_compensation_press(data, &comp);
if (ret < 0)
return ret;
/* Read and compensate temperature so we get a reading of t_fine. */
ret = bmp280_read_temp(data, NULL);
@ -307,7 +268,7 @@ static int bmp280_read_press(struct bmp280_data *data,
}
adc_press = be32_to_cpu(tmp) >> 12;
comp_press = bmp280_compensate_press(data, &comp, adc_press);
comp_press = bmp280_compensate_press(data, adc_press);
*val = comp_press;
*val2 = 256000;
@ -366,7 +327,7 @@ static int bmp280_chip_init(struct bmp280_data *data)
BMP280_MODE_NORMAL);
if (ret < 0) {
dev_err(&data->client->dev,
"failed to write config register\n");
"failed to write ctrl_meas register\n");
return ret;
}
@ -394,7 +355,6 @@ static int bmp280_probe(struct i2c_client *client,
if (!indio_dev)
return -ENOMEM;
i2c_set_clientdata(client, indio_dev);
data = iio_priv(indio_dev);
mutex_init(&data->lock);
data->client = client;

View File

@ -79,7 +79,6 @@ static int press_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
s32 poll_value;
*val = 0;
*val2 = 0;
@ -96,15 +95,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
poll_value = hid_sensor_read_poll_value(
&press_state->common_attributes);
if (poll_value < 0)
return -EINVAL;
hid_sensor_power_state(&press_state->common_attributes,
true);
msleep_interruptible(poll_value * 2);
*val = sensor_hub_input_attr_get_raw_value(
press_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PRESSURE, address,
@ -382,6 +374,7 @@ static struct platform_driver hid_press_platform_driver = {
.id_table = hid_press_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_press_probe,
.remove = hid_press_remove,

View File

@ -17,3 +17,20 @@ config AS3935
module will be called as3935
endmenu
menu "Proximity sensors"
config SX9500
tristate "SX9500 Semtech proximity sensor"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP_I2C
depends on I2C
help
Say Y here to build a driver for Semtech's SX9500 capacitive
proximity/button sensor.
To compile this driver as a module, choose M here: the
module will be called sx9500.
endmenu

View File

@ -4,3 +4,4 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_SX9500) += sx9500.o

View File

@ -273,9 +273,9 @@ static void calibrate_as3935(struct as3935_state *st)
}
#ifdef CONFIG_PM_SLEEP
static int as3935_suspend(struct spi_device *spi, pm_message_t msg)
static int as3935_suspend(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct as3935_state *st = iio_priv(indio_dev);
int val, ret;
@ -293,9 +293,9 @@ static int as3935_suspend(struct spi_device *spi, pm_message_t msg)
return ret;
}
static int as3935_resume(struct spi_device *spi)
static int as3935_resume(struct device *dev)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct as3935_state *st = iio_priv(indio_dev);
int val, ret;
@ -311,9 +311,12 @@ static int as3935_resume(struct spi_device *spi)
return ret;
}
static SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume);
#define AS3935_PM_OPS (&as3935_pm_ops)
#else
#define as3935_suspend NULL
#define as3935_resume NULL
#define AS3935_PM_OPS NULL
#endif
static int as3935_probe(struct spi_device *spi)
@ -441,12 +444,11 @@ static struct spi_driver as3935_driver = {
.driver = {
.name = "as3935",
.owner = THIS_MODULE,
.pm = AS3935_PM_OPS,
},
.probe = as3935_probe,
.remove = as3935_remove,
.id_table = as3935_id,
.suspend = as3935_suspend,
.resume = as3935_resume,
};
module_spi_driver(as3935_driver);

View File

@ -0,0 +1,752 @@
/*
* Copyright (c) 2014 Intel Corporation
*
* Driver for Semtech's SX9500 capacitive proximity/button solution.
* Datasheet available at
* <http://www.semtech.com/images/datasheet/sx9500.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 published by
* the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define SX9500_DRIVER_NAME "sx9500"
#define SX9500_IRQ_NAME "sx9500_event"
#define SX9500_GPIO_NAME "sx9500_gpio"
/* Register definitions. */
#define SX9500_REG_IRQ_SRC 0x00
#define SX9500_REG_STAT 0x01
#define SX9500_REG_IRQ_MSK 0x03
#define SX9500_REG_PROX_CTRL0 0x06
#define SX9500_REG_PROX_CTRL1 0x07
#define SX9500_REG_PROX_CTRL2 0x08
#define SX9500_REG_PROX_CTRL3 0x09
#define SX9500_REG_PROX_CTRL4 0x0a
#define SX9500_REG_PROX_CTRL5 0x0b
#define SX9500_REG_PROX_CTRL6 0x0c
#define SX9500_REG_PROX_CTRL7 0x0d
#define SX9500_REG_PROX_CTRL8 0x0e
#define SX9500_REG_SENSOR_SEL 0x20
#define SX9500_REG_USE_MSB 0x21
#define SX9500_REG_USE_LSB 0x22
#define SX9500_REG_AVG_MSB 0x23
#define SX9500_REG_AVG_LSB 0x24
#define SX9500_REG_DIFF_MSB 0x25
#define SX9500_REG_DIFF_LSB 0x26
#define SX9500_REG_OFFSET_MSB 0x27
#define SX9500_REG_OFFSET_LSB 0x28
#define SX9500_REG_RESET 0x7f
/* Write this to REG_RESET to do a soft reset. */
#define SX9500_SOFT_RESET 0xde
#define SX9500_SCAN_PERIOD_MASK GENMASK(6, 4)
#define SX9500_SCAN_PERIOD_SHIFT 4
/*
* These serve for identifying IRQ source in the IRQ_SRC register, and
* also for masking the IRQs in the IRQ_MSK register.
*/
#define SX9500_CLOSE_IRQ BIT(6)
#define SX9500_FAR_IRQ BIT(5)
#define SX9500_CONVDONE_IRQ BIT(3)
#define SX9500_PROXSTAT_SHIFT 4
#define SX9500_NUM_CHANNELS 4
struct sx9500_data {
struct mutex mutex;
struct i2c_client *client;
struct iio_trigger *trig;
struct regmap *regmap;
/*
* Last reading of the proximity status for each channel. We
* only send an event to user space when this changes.
*/
bool prox_stat[SX9500_NUM_CHANNELS];
bool event_enabled[SX9500_NUM_CHANNELS];
bool trigger_enabled;
u16 *buffer;
};
static const struct iio_event_spec sx9500_events[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
},
};
#define SX9500_CHANNEL(idx) \
{ \
.type = IIO_PROXIMITY, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.indexed = 1, \
.channel = idx, \
.event_spec = sx9500_events, \
.num_event_specs = ARRAY_SIZE(sx9500_events), \
.scan_index = idx, \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.shift = 0, \
}, \
}
static const struct iio_chan_spec sx9500_channels[] = {
SX9500_CHANNEL(0),
SX9500_CHANNEL(1),
SX9500_CHANNEL(2),
SX9500_CHANNEL(3),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct {
int val;
int val2;
} sx9500_samp_freq_table[] = {
{33, 333333},
{16, 666666},
{11, 111111},
{8, 333333},
{6, 666666},
{5, 0},
{3, 333333},
{2, 500000},
};
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),
regmap_reg_range(SX9500_REG_SENSOR_SEL, SX9500_REG_SENSOR_SEL),
regmap_reg_range(SX9500_REG_OFFSET_MSB, SX9500_REG_OFFSET_LSB),
regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
};
static const struct regmap_access_table sx9500_writeable_regs = {
.yes_ranges = sx9500_writable_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(sx9500_writable_reg_ranges),
};
/*
* All allocated registers are readable, so we just list unallocated
* ones.
*/
static const struct regmap_range sx9500_non_readable_reg_ranges[] = {
regmap_reg_range(SX9500_REG_STAT + 1, SX9500_REG_STAT + 1),
regmap_reg_range(SX9500_REG_IRQ_MSK + 1, SX9500_REG_PROX_CTRL0 - 1),
regmap_reg_range(SX9500_REG_PROX_CTRL8 + 1, SX9500_REG_SENSOR_SEL - 1),
regmap_reg_range(SX9500_REG_OFFSET_LSB + 1, SX9500_REG_RESET - 1),
};
static const struct regmap_access_table sx9500_readable_regs = {
.no_ranges = sx9500_non_readable_reg_ranges,
.n_no_ranges = ARRAY_SIZE(sx9500_non_readable_reg_ranges),
};
static const struct regmap_range sx9500_volatile_reg_ranges[] = {
regmap_reg_range(SX9500_REG_IRQ_SRC, SX9500_REG_STAT),
regmap_reg_range(SX9500_REG_USE_MSB, SX9500_REG_OFFSET_LSB),
regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
};
static const struct regmap_access_table sx9500_volatile_regs = {
.yes_ranges = sx9500_volatile_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(sx9500_volatile_reg_ranges),
};
static const struct regmap_config sx9500_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SX9500_REG_RESET,
.cache_type = REGCACHE_RBTREE,
.wr_table = &sx9500_writeable_regs,
.rd_table = &sx9500_readable_regs,
.volatile_table = &sx9500_volatile_regs,
};
static int sx9500_read_proximity(struct sx9500_data *data,
const struct iio_chan_spec *chan,
int *val)
{
int ret;
__be16 regval;
ret = regmap_write(data->regmap, SX9500_REG_SENSOR_SEL, chan->channel);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, SX9500_REG_USE_MSB, &regval, 2);
if (ret < 0)
return ret;
*val = 32767 - (s16)be16_to_cpu(regval);
return IIO_VAL_INT;
}
static int sx9500_read_samp_freq(struct sx9500_data *data,
int *val, int *val2)
{
int ret;
unsigned int regval;
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &regval);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
regval = (regval & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
*val = sx9500_samp_freq_table[regval].val;
*val2 = sx9500_samp_freq_table[regval].val2;
return IIO_VAL_INT_PLUS_MICRO;
}
static int sx9500_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
switch (chan->type) {
case IIO_PROXIMITY:
switch (mask) {
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;
case IIO_CHAN_INFO_SAMP_FREQ:
return sx9500_read_samp_freq(data, val, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int sx9500_set_samp_freq(struct sx9500_data *data,
int val, int val2)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(sx9500_samp_freq_table); i++)
if (val == sx9500_samp_freq_table[i].val &&
val2 == sx9500_samp_freq_table[i].val2)
break;
if (i == ARRAY_SIZE(sx9500_samp_freq_table))
return -EINVAL;
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
SX9500_SCAN_PERIOD_MASK,
i << SX9500_SCAN_PERIOD_SHIFT);
mutex_unlock(&data->mutex);
return ret;
}
static int sx9500_write_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int val, int val2, long mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
switch (chan->type) {
case IIO_PROXIMITY:
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return sx9500_set_samp_freq(data, val, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static irqreturn_t sx9500_irq_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct sx9500_data *data = iio_priv(indio_dev);
if (data->trigger_enabled)
iio_trigger_poll(data->trig);
/*
* Even if no event is enabled, we need to wake the thread to
* clear the interrupt state by reading SX9500_REG_IRQ_SRC. It
* is not possible to do that here because regmap_read takes a
* mutex.
*/
return IRQ_WAKE_THREAD;
}
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, 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;
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;
}
val >>= SX9500_PROXSTAT_SHIFT;
for (chan = 0; chan < SX9500_NUM_CHANNELS; chan++) {
int dir;
u64 ev;
bool new_prox = val & BIT(chan);
if (!data->event_enabled[chan])
continue;
if (new_prox == data->prox_stat[chan])
/* 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);
iio_push_event(indio_dev, ev, iio_get_time_ns());
data->prox_stat[chan] = new_prox;
}
out:
mutex_unlock(&data->mutex);
return IRQ_HANDLED;
}
static int sx9500_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 sx9500_data *data = iio_priv(indio_dev);
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
dir != IIO_EV_DIR_EITHER)
return -EINVAL;
return data->event_enabled[chan->channel];
}
static int sx9500_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 sx9500_data *data = iio_priv(indio_dev);
int ret, i;
bool any_active = false;
unsigned int irqmask;
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
dir != IIO_EV_DIR_EITHER)
return -EINVAL;
mutex_lock(&data->mutex);
data->event_enabled[chan->channel] = state;
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);
else
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
irqmask, 0);
mutex_unlock(&data->mutex);
return ret;
}
static int sx9500_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
kfree(data->buffer);
data->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
mutex_unlock(&data->mutex);
if (data->buffer == NULL)
return -ENOMEM;
return 0;
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
"2.500000 3.333333 5 6.666666 8.333333 11.111111 16.666666 33.333333");
static struct attribute *sx9500_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group sx9500_attribute_group = {
.attrs = sx9500_attributes,
};
static const struct iio_info sx9500_info = {
.driver_module = THIS_MODULE,
.attrs = &sx9500_attribute_group,
.read_raw = &sx9500_read_raw,
.write_raw = &sx9500_write_raw,
.read_event_config = &sx9500_read_event_config,
.write_event_config = &sx9500_write_event_config,
.update_scan_mode = &sx9500_update_scan_mode,
};
static int sx9500_set_trigger_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct sx9500_data *data = iio_priv(indio_dev);
int ret;
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;
mutex_unlock(&data->mutex);
return ret;
}
static const struct iio_trigger_ops sx9500_trigger_ops = {
.set_trigger_state = sx9500_set_trigger_state,
.owner = THIS_MODULE,
};
static irqreturn_t sx9500_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct sx9500_data *data = iio_priv(indio_dev);
int val, bit, ret, i = 0;
mutex_lock(&data->mutex);
for_each_set_bit(bit, indio_dev->buffer->scan_mask,
indio_dev->masklength) {
ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
&val);
if (ret < 0)
goto out;
data->buffer[i++] = val;
}
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
iio_get_time_ns());
out:
mutex_unlock(&data->mutex);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
struct sx9500_reg_default {
u8 reg;
u8 def;
};
static const struct sx9500_reg_default sx9500_default_regs[] = {
{
.reg = SX9500_REG_PROX_CTRL1,
/* Shield enabled, small range. */
.def = 0x43,
},
{
.reg = SX9500_REG_PROX_CTRL2,
/* x8 gain, 167kHz frequency, finest resolution. */
.def = 0x77,
},
{
.reg = SX9500_REG_PROX_CTRL3,
/* Doze enabled, 2x scan period doze, no raw filter. */
.def = 0x40,
},
{
.reg = SX9500_REG_PROX_CTRL4,
/* Average threshold. */
.def = 0x30,
},
{
.reg = SX9500_REG_PROX_CTRL5,
/*
* Debouncer off, lowest average negative filter,
* highest average postive filter.
*/
.def = 0x0f,
},
{
.reg = SX9500_REG_PROX_CTRL6,
/* Proximity detection threshold: 280 */
.def = 0x0e,
},
{
.reg = SX9500_REG_PROX_CTRL7,
/*
* No automatic compensation, compensate each pin
* independently, proximity hysteresis: 32, close
* debouncer off, far debouncer off.
*/
.def = 0x00,
},
{
.reg = SX9500_REG_PROX_CTRL8,
/* No stuck timeout, no periodic compensation. */
.def = 0x00,
},
{
.reg = SX9500_REG_PROX_CTRL0,
/* Scan period: 30ms, all sensors enabled. */
.def = 0x0f,
},
};
static int sx9500_init_device(struct iio_dev *indio_dev)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
unsigned int val;
ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
if (ret < 0)
return ret;
ret = regmap_write(data->regmap, SX9500_REG_RESET,
SX9500_SOFT_RESET);
if (ret < 0)
return ret;
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(sx9500_default_regs); i++) {
ret = regmap_write(data->regmap,
sx9500_default_regs[i].reg,
sx9500_default_regs[i].def);
if (ret < 0)
return ret;
}
return 0;
}
static int sx9500_gpio_probe(struct i2c_client *client,
struct sx9500_data *data)
{
struct device *dev;
struct gpio_desc *gpio;
int ret;
if (!client)
return -EINVAL;
dev = &client->dev;
/* data ready gpio interrupt pin */
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 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 int sx9500_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct sx9500_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (indio_dev == NULL)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->mutex);
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;
indio_dev->num_channels = ARRAY_SIZE(sx9500_channels);
indio_dev->info = &sx9500_info;
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);
if (client->irq <= 0)
client->irq = sx9500_gpio_probe(client, data);
if (client->irq > 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
sx9500_irq_handler, sx9500_irq_thread_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
SX9500_IRQ_NAME, indio_dev);
if (ret < 0)
return ret;
data->trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d", indio_dev->name, indio_dev->id);
if (!data->trig)
return -ENOMEM;
data->trig->dev.parent = &client->dev;
data->trig->ops = &sx9500_trigger_ops;
iio_trigger_set_drvdata(data->trig, indio_dev);
ret = iio_trigger_register(data->trig);
if (ret)
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
sx9500_trigger_handler, NULL);
if (ret < 0)
goto out_trigger_unregister;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto out_buffer_cleanup;
return 0;
out_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
out_trigger_unregister:
if (client->irq > 0)
iio_trigger_unregister(data->trig);
return ret;
}
static int sx9500_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct sx9500_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (client->irq > 0)
iio_trigger_unregister(data->trig);
kfree(data->buffer);
return 0;
}
static const struct acpi_device_id sx9500_acpi_match[] = {
{"SSX9500", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
static const struct i2c_device_id sx9500_id[] = {
{"sx9500", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, sx9500_id);
static struct i2c_driver sx9500_driver = {
.driver = {
.name = SX9500_DRIVER_NAME,
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
},
.probe = sx9500_probe,
.remove = sx9500_remove,
.id_table = sx9500_id,
};
module_i2c_driver(sx9500_driver);
MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
MODULE_DESCRIPTION("Driver for Semtech SX9500 proximity sensor");
MODULE_LICENSE("GPL v2");

View File

@ -135,6 +135,7 @@ static int iio_sysfs_trigger_probe(int id)
struct iio_sysfs_trig *t;
int ret;
bool foundit = false;
mutex_lock(&iio_sysfs_trig_list_mut);
list_for_each_entry(t, &iio_sysfs_trig_list, l)
if (id == t->id) {
@ -185,6 +186,7 @@ static int iio_sysfs_trigger_remove(int id)
{
bool foundit = false;
struct iio_sysfs_trig *t;
mutex_lock(&iio_sysfs_trig_list_mut);
list_for_each_entry(t, &iio_sysfs_trig_list, l)
if (id == t->id) {

View File

@ -2,5 +2,4 @@
# Makefile for MPT based block devices
#
obj-$(CONFIG_I2O) += i2o/
obj-$(CONFIG_FUSION) += fusion/

View File

@ -56,6 +56,8 @@ source "drivers/staging/vt6656/Kconfig"
source "drivers/staging/iio/Kconfig"
source "drivers/staging/sm7xxfb/Kconfig"
source "drivers/staging/xgifb/Kconfig"
source "drivers/staging/emxx_udc/Kconfig"
@ -64,8 +66,6 @@ source "drivers/staging/ft1000/Kconfig"
source "drivers/staging/speakup/Kconfig"
source "drivers/staging/cptm1217/Kconfig"
source "drivers/staging/ste_rmi4/Kconfig"
source "drivers/staging/nvec/Kconfig"
@ -104,4 +104,8 @@ source "drivers/staging/unisys/Kconfig"
source "drivers/staging/clocking-wizard/Kconfig"
source "drivers/staging/fbtft/Kconfig"
source "drivers/staging/i2o/Kconfig"
endif # STAGING

View File

@ -22,11 +22,11 @@ obj-$(CONFIG_VT6655) += vt6655/
obj-$(CONFIG_VT6656) += vt6656/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
obj-$(CONFIG_FB_XGI) += xgifb/
obj-$(CONFIG_USB_EMXX) += emxx_udc/
obj-$(CONFIG_FT1000) += ft1000/
obj-$(CONFIG_SPEAKUP) += speakup/
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
obj-$(CONFIG_MFD_NVEC) += nvec/
obj-$(CONFIG_ANDROID) += android/
@ -44,3 +44,5 @@ obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
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/

View File

@ -14,23 +14,6 @@ config ASHMEM
It is, in theory, a good memory allocator for low-memory devices,
because it can discard shared memory units when under memory pressure.
config ANDROID_LOGGER
tristate "Android log driver"
default n
---help---
This adds support for system-wide logging using four log buffers.
These are:
1: main
2: events
3: radio
4: system
Log reading and writing is performed via normal Linux reads and
optimized writes. This optimization avoids logging having too
much overhead in the system.
config ANDROID_TIMED_OUTPUT
bool "Timed output class driver"
default y
@ -45,15 +28,6 @@ config ANDROID_LOW_MEMORY_KILLER
---help---
Registers processes to be killed when memory is low
config ANDROID_INTF_ALARM_DEV
tristate "Android alarm driver"
depends on RTC_CLASS
default n
---help---
Provides non-wakeup and rtc backed wakeup alarms based on rtc or
elapsed realtime, and a non-wakeup alarm on the monotonic clock.
Also exports the alarm interface to user-space.
config SYNC
bool "Synchronization framework"
default n

View File

@ -3,10 +3,8 @@ ccflags-y += -I$(src) # needed for trace events
obj-y += ion/
obj-$(CONFIG_ASHMEM) += ashmem.o
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o
obj-$(CONFIG_SYNC) += sync.o sync_debug.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o

View File

@ -1,446 +0,0 @@
/* drivers/rtc/alarm-dev.c
*
* Copyright (C) 2007-2009 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/time.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/alarmtimer.h>
#include "android_alarm.h"
#define ANDROID_ALARM_PRINT_INFO (1U << 0)
#define ANDROID_ALARM_PRINT_IO (1U << 1)
#define ANDROID_ALARM_PRINT_INT (1U << 2)
static int debug_mask = ANDROID_ALARM_PRINT_INFO;
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
#define alarm_dbg(debug_level_mask, fmt, ...) \
do { \
if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) \
pr_info(fmt, ##__VA_ARGS__); \
} while (0)
#define ANDROID_ALARM_WAKEUP_MASK ( \
ANDROID_ALARM_RTC_WAKEUP_MASK | \
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
static int alarm_opened;
static DEFINE_SPINLOCK(alarm_slock);
static struct wakeup_source alarm_wake_lock;
static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue);
static uint32_t alarm_pending;
static uint32_t alarm_enabled;
static uint32_t wait_pending;
struct devalarm {
union {
struct hrtimer hrt;
struct alarm alrm;
} u;
enum android_alarm_type type;
};
static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT];
/**
* is_wakeup() - Checks to see if this alarm can wake the device
* @type: The type of alarm being checked
*
* Return: 1 if this is a wakeup alarm, otherwise 0
*/
static int is_wakeup(enum android_alarm_type type)
{
return type == ANDROID_ALARM_RTC_WAKEUP ||
type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP;
}
static void devalarm_start(struct devalarm *alrm, ktime_t exp)
{
if (is_wakeup(alrm->type))
alarm_start(&alrm->u.alrm, exp);
else
hrtimer_start(&alrm->u.hrt, exp, HRTIMER_MODE_ABS);
}
static int devalarm_try_to_cancel(struct devalarm *alrm)
{
if (is_wakeup(alrm->type))
return alarm_try_to_cancel(&alrm->u.alrm);
return hrtimer_try_to_cancel(&alrm->u.hrt);
}
static void devalarm_cancel(struct devalarm *alrm)
{
if (is_wakeup(alrm->type))
alarm_cancel(&alrm->u.alrm);
else
hrtimer_cancel(&alrm->u.hrt);
}
static void alarm_clear(enum android_alarm_type alarm_type)
{
uint32_t alarm_type_mask = 1U << alarm_type;
unsigned long flags;
spin_lock_irqsave(&alarm_slock, flags);
alarm_dbg(IO, "alarm %d clear\n", alarm_type);
devalarm_try_to_cancel(&alarms[alarm_type]);
if (alarm_pending) {
alarm_pending &= ~alarm_type_mask;
if (!alarm_pending && !wait_pending)
__pm_relax(&alarm_wake_lock);
}
alarm_enabled &= ~alarm_type_mask;
spin_unlock_irqrestore(&alarm_slock, flags);
}
static void alarm_set(enum android_alarm_type alarm_type,
struct timespec *ts)
{
uint32_t alarm_type_mask = 1U << alarm_type;
unsigned long flags;
spin_lock_irqsave(&alarm_slock, flags);
alarm_dbg(IO, "alarm %d set %ld.%09ld\n",
alarm_type, ts->tv_sec, ts->tv_nsec);
alarm_enabled |= alarm_type_mask;
devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts));
spin_unlock_irqrestore(&alarm_slock, flags);
}
static int alarm_wait(void)
{
unsigned long flags;
int rv = 0;
spin_lock_irqsave(&alarm_slock, flags);
alarm_dbg(IO, "alarm wait\n");
if (!alarm_pending && wait_pending) {
__pm_relax(&alarm_wake_lock);
wait_pending = 0;
}
spin_unlock_irqrestore(&alarm_slock, flags);
rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
if (rv)
return rv;
spin_lock_irqsave(&alarm_slock, flags);
rv = alarm_pending;
wait_pending = 1;
alarm_pending = 0;
spin_unlock_irqrestore(&alarm_slock, flags);
return rv;
}
static int alarm_set_rtc(struct timespec *ts)
{
struct rtc_time new_rtc_tm;
struct rtc_device *rtc_dev;
unsigned long flags;
int rv = 0;
rtc_time_to_tm(ts->tv_sec, &new_rtc_tm);
rtc_dev = alarmtimer_get_rtcdev();
rv = do_settimeofday(ts);
if (rv < 0)
return rv;
if (rtc_dev)
rv = rtc_set_time(rtc_dev, &new_rtc_tm);
spin_lock_irqsave(&alarm_slock, flags);
alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
wake_up(&alarm_wait_queue);
spin_unlock_irqrestore(&alarm_slock, flags);
return rv;
}
static int alarm_get_time(enum android_alarm_type alarm_type,
struct timespec *ts)
{
int rv = 0;
switch (alarm_type) {
case ANDROID_ALARM_RTC_WAKEUP:
case ANDROID_ALARM_RTC:
getnstimeofday(ts);
break;
case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
case ANDROID_ALARM_ELAPSED_REALTIME:
get_monotonic_boottime(ts);
break;
case ANDROID_ALARM_SYSTEMTIME:
ktime_get_ts(ts);
break;
default:
rv = -EINVAL;
}
return rv;
}
static long alarm_do_ioctl(struct file *file, unsigned int cmd,
struct timespec *ts)
{
int rv = 0;
unsigned long flags;
enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
return -EINVAL;
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return -EPERM;
if (file->private_data == NULL &&
cmd != ANDROID_ALARM_SET_RTC) {
spin_lock_irqsave(&alarm_slock, flags);
if (alarm_opened) {
spin_unlock_irqrestore(&alarm_slock, flags);
return -EBUSY;
}
alarm_opened = 1;
file->private_data = (void *)1;
spin_unlock_irqrestore(&alarm_slock, flags);
}
}
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_CLEAR(0):
alarm_clear(alarm_type);
break;
case ANDROID_ALARM_SET(0):
alarm_set(alarm_type, ts);
break;
case ANDROID_ALARM_SET_AND_WAIT(0):
alarm_set(alarm_type, ts);
/* fall though */
case ANDROID_ALARM_WAIT:
rv = alarm_wait();
break;
case ANDROID_ALARM_SET_RTC:
rv = alarm_set_rtc(ts);
break;
case ANDROID_ALARM_GET_TIME(0):
rv = alarm_get_time(alarm_type, ts);
break;
default:
rv = -EINVAL;
}
return rv;
}
static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct timespec ts;
int rv;
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_SET_AND_WAIT(0):
case ANDROID_ALARM_SET(0):
case ANDROID_ALARM_SET_RTC:
if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
return -EFAULT;
break;
}
rv = alarm_do_ioctl(file, cmd, &ts);
if (rv)
return rv;
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_GET_TIME(0):
if (copy_to_user((void __user *)arg, &ts, sizeof(ts)))
return -EFAULT;
break;
}
return 0;
}
#ifdef CONFIG_COMPAT
static long alarm_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct timespec ts;
int rv;
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_SET_AND_WAIT_COMPAT(0):
case ANDROID_ALARM_SET_COMPAT(0):
case ANDROID_ALARM_SET_RTC_COMPAT:
if (compat_get_timespec(&ts, (void __user *)arg))
return -EFAULT;
/* fall through */
case ANDROID_ALARM_GET_TIME_COMPAT(0):
cmd = ANDROID_ALARM_COMPAT_TO_NORM(cmd);
break;
}
rv = alarm_do_ioctl(file, cmd, &ts);
if (rv)
return rv;
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_GET_TIME(0): /* NOTE: we modified cmd above */
if (compat_put_timespec(&ts, (void __user *)arg))
return -EFAULT;
break;
}
return 0;
}
#endif
static int alarm_open(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static int alarm_release(struct inode *inode, struct file *file)
{
int i;
unsigned long flags;
spin_lock_irqsave(&alarm_slock, flags);
if (file->private_data) {
for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
uint32_t alarm_type_mask = 1U << i;
if (alarm_enabled & alarm_type_mask) {
alarm_dbg(INFO,
"%s: clear alarm, pending %d\n",
__func__,
!!(alarm_pending & alarm_type_mask));
alarm_enabled &= ~alarm_type_mask;
}
spin_unlock_irqrestore(&alarm_slock, flags);
devalarm_cancel(&alarms[i]);
spin_lock_irqsave(&alarm_slock, flags);
}
if (alarm_pending | wait_pending) {
if (alarm_pending)
alarm_dbg(INFO, "%s: clear pending alarms %x\n",
__func__, alarm_pending);
__pm_relax(&alarm_wake_lock);
wait_pending = 0;
alarm_pending = 0;
}
alarm_opened = 0;
}
spin_unlock_irqrestore(&alarm_slock, flags);
return 0;
}
static void devalarm_triggered(struct devalarm *alarm)
{
unsigned long flags;
uint32_t alarm_type_mask = 1U << alarm->type;
alarm_dbg(INT, "%s: type %d\n", __func__, alarm->type);
spin_lock_irqsave(&alarm_slock, flags);
if (alarm_enabled & alarm_type_mask) {
__pm_wakeup_event(&alarm_wake_lock, 5000); /* 5secs */
alarm_enabled &= ~alarm_type_mask;
alarm_pending |= alarm_type_mask;
wake_up(&alarm_wait_queue);
}
spin_unlock_irqrestore(&alarm_slock, flags);
}
static enum hrtimer_restart devalarm_hrthandler(struct hrtimer *hrt)
{
struct devalarm *devalrm = container_of(hrt, struct devalarm, u.hrt);
devalarm_triggered(devalrm);
return HRTIMER_NORESTART;
}
static enum alarmtimer_restart devalarm_alarmhandler(struct alarm *alrm,
ktime_t now)
{
struct devalarm *devalrm = container_of(alrm, struct devalarm, u.alrm);
devalarm_triggered(devalrm);
return ALARMTIMER_NORESTART;
}
static const struct file_operations alarm_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = alarm_ioctl,
.open = alarm_open,
.release = alarm_release,
#ifdef CONFIG_COMPAT
.compat_ioctl = alarm_compat_ioctl,
#endif
};
static struct miscdevice alarm_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "alarm",
.fops = &alarm_fops,
};
static int __init alarm_dev_init(void)
{
int err;
int i;
err = misc_register(&alarm_device);
if (err)
return err;
alarm_init(&alarms[ANDROID_ALARM_RTC_WAKEUP].u.alrm,
ALARM_REALTIME, devalarm_alarmhandler);
hrtimer_init(&alarms[ANDROID_ALARM_RTC].u.hrt,
CLOCK_REALTIME, HRTIMER_MODE_ABS);
alarm_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].u.alrm,
ALARM_BOOTTIME, devalarm_alarmhandler);
hrtimer_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME].u.hrt,
CLOCK_BOOTTIME, HRTIMER_MODE_ABS);
hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt,
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
alarms[i].type = i;
if (!is_wakeup(i))
alarms[i].u.hrt.function = devalarm_hrthandler;
}
wakeup_source_init(&alarm_wake_lock, "alarm");
return 0;
}
static void __exit alarm_dev_exit(void)
{
misc_deregister(&alarm_device);
wakeup_source_trash(&alarm_wake_lock);
}
module_init(alarm_dev_init);
module_exit(alarm_dev_exit);
MODULE_LICENSE("GPL");

View File

@ -1,41 +0,0 @@
/* include/linux/android_alarm.h
*
* Copyright (C) 2006-2007 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _LINUX_ANDROID_ALARM_H
#define _LINUX_ANDROID_ALARM_H
#include <linux/compat.h>
#include <linux/ioctl.h>
#include "uapi/android_alarm.h"
#ifdef CONFIG_COMPAT
#define ANDROID_ALARM_SET_COMPAT(type) ALARM_IOW(2, type, \
struct compat_timespec)
#define ANDROID_ALARM_SET_AND_WAIT_COMPAT(type) ALARM_IOW(3, type, \
struct compat_timespec)
#define ANDROID_ALARM_GET_TIME_COMPAT(type) ALARM_IOW(4, type, \
struct compat_timespec)
#define ANDROID_ALARM_SET_RTC_COMPAT _IOW('a', 5, \
struct compat_timespec)
#define ANDROID_ALARM_IOCTL_NR(cmd) (_IOC_NR(cmd) & ((1<<4)-1))
#define ANDROID_ALARM_COMPAT_TO_NORM(cmd) \
ALARM_IOW(ANDROID_ALARM_IOCTL_NR(cmd), \
ANDROID_ALARM_IOCTL_TO_TYPE(cmd), \
struct timespec)
#endif
#endif

View File

@ -447,8 +447,8 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
loff_t end = (range->pgend + 1) * PAGE_SIZE;
vfs_fallocate(range->asma->file,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
start, end - start);
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
start, end - start);
range->purged = ASHMEM_WAS_PURGED;
lru_del(range);
@ -549,7 +549,6 @@ static int get_name(struct ashmem_area *asma, void __user *name)
mutex_lock(&ashmem_mutex);
if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0') {
/*
* Copying only `len', instead of ASHMEM_NAME_LEN, bytes
* prevents us from revealing one user's stack to another.
@ -751,10 +750,10 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) {
case ASHMEM_SET_NAME:
ret = set_name(asma, (void __user *) arg);
ret = set_name(asma, (void __user *)arg);
break;
case ASHMEM_GET_NAME:
ret = get_name(asma, (void __user *) arg);
ret = get_name(asma, (void __user *)arg);
break;
case ASHMEM_SET_SIZE:
ret = -EINVAL;
@ -775,7 +774,7 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case ASHMEM_PIN:
case ASHMEM_UNPIN:
case ASHMEM_GET_PIN_STATUS:
ret = ashmem_pin_unpin(asma, cmd, (void __user *) arg);
ret = ashmem_pin_unpin(asma, cmd, (void __user *)arg);
break;
case ASHMEM_PURGE_ALL_CACHES:
ret = -EPERM;
@ -798,7 +797,6 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
static long compat_ashmem_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case COMPAT_ASHMEM_SET_SIZE:
cmd = ASHMEM_SET_SIZE;

View File

@ -1508,6 +1508,9 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
pr_err("%s: can not add heap with invalid ops struct.\n",
__func__);
spin_lock_init(&heap->free_lock);
heap->free_list_size = 0;
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
ion_heap_init_deferred_free(heap);

View File

@ -39,24 +39,6 @@ struct ion_cma_buffer_info {
struct sg_table *table;
};
/*
* Create scatter-list for the already allocated DMA buffer.
* This function could be replaced by dma_common_get_sgtable
* as soon as it will avalaible.
*/
static int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt,
void *cpu_addr, dma_addr_t handle, size_t size)
{
struct page *page = virt_to_page(cpu_addr);
int ret;
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
if (unlikely(ret))
return ret;
sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
return 0;
}
/* ION CMA heap operations functions */
static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
@ -91,7 +73,7 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
if (!info->table)
goto free_mem;
if (ion_cma_get_sgtable
if (dma_common_get_sgtable
(dev, info->table, info->cpu_addr, info->handle, len))
goto free_table;
/* keep this for memory release */

View File

@ -253,8 +253,6 @@ int ion_heap_init_deferred_free(struct ion_heap *heap)
struct sched_param param = { .sched_priority = 0 };
INIT_LIST_HEAD(&heap->free_list);
heap->free_list_size = 0;
spin_lock_init(&heap->free_lock);
init_waitqueue_head(&heap->waitqueue);
heap->task = kthread_run(ion_heap_deferred_free, heap,
"%s", heap->name);

View File

@ -1,808 +0,0 @@
/*
* drivers/misc/logger.c
*
* A Logging Subsystem
*
* Copyright (C) 2007-2008 Google, Inc.
*
* Robert Love <rlove@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#define pr_fmt(fmt) "logger: " fmt
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
#include <linux/aio.h>
#include "logger.h"
#include <asm/ioctls.h>
/**
* struct logger_log - represents a specific log, such as 'main' or 'radio'
* @buffer: The actual ring buffer
* @misc: The "misc" device representing the log
* @wq: The wait queue for @readers
* @readers: This log's readers
* @mutex: The mutex that protects the @buffer
* @w_off: The current write head offset
* @head: The head, or location that readers start reading at.
* @size: The size of the log
* @logs: The list of log channels
*
* This structure lives from module insertion until module removal, so it does
* not need additional reference counting. The structure is protected by the
* mutex 'mutex'.
*/
struct logger_log {
unsigned char *buffer;
struct miscdevice misc;
wait_queue_head_t wq;
struct list_head readers;
struct mutex mutex;
size_t w_off;
size_t head;
size_t size;
struct list_head logs;
};
static LIST_HEAD(log_list);
/**
* struct logger_reader - a logging device open for reading
* @log: The associated log
* @list: The associated entry in @logger_log's list
* @r_off: The current read head offset.
* @r_all: Reader can read all entries
* @r_ver: Reader ABI version
*
* This object lives from open to release, so we don't need additional
* reference counting. The structure is protected by log->mutex.
*/
struct logger_reader {
struct logger_log *log;
struct list_head list;
size_t r_off;
bool r_all;
int r_ver;
};
/* logger_offset - returns index 'n' into the log via (optimized) modulus */
static size_t logger_offset(struct logger_log *log, size_t n)
{
return n & (log->size - 1);
}
/*
* file_get_log - Given a file structure, return the associated log
*
* This isn't aesthetic. We have several goals:
*
* 1) Need to quickly obtain the associated log during an I/O operation
* 2) Readers need to maintain state (logger_reader)
* 3) Writers need to be very fast (open() should be a near no-op)
*
* In the reader case, we can trivially go file->logger_reader->logger_log.
* For a writer, we don't want to maintain a logger_reader, so we just go
* file->logger_log. Thus what file->private_data points at depends on whether
* or not the file was opened for reading. This function hides that dirtiness.
*/
static inline struct logger_log *file_get_log(struct file *file)
{
if (file->f_mode & FMODE_READ) {
struct logger_reader *reader = file->private_data;
return reader->log;
}
return file->private_data;
}
/*
* get_entry_header - returns a pointer to the logger_entry header within
* 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must
* be provided. Typically the return value will be a pointer within
* 'logger->buf'. However, a pointer to 'scratch' may be returned if
* the log entry spans the end and beginning of the circular buffer.
*/
static struct logger_entry *get_entry_header(struct logger_log *log,
size_t off, struct logger_entry *scratch)
{
size_t len = min(sizeof(struct logger_entry), log->size - off);
if (len != sizeof(struct logger_entry)) {
memcpy(((void *) scratch), log->buffer + off, len);
memcpy(((void *) scratch) + len, log->buffer,
sizeof(struct logger_entry) - len);
return scratch;
}
return (struct logger_entry *) (log->buffer + off);
}
/*
* get_entry_msg_len - Grabs the length of the message of the entry
* starting from from 'off'.
*
* An entry length is 2 bytes (16 bits) in host endian order.
* In the log, the length does not include the size of the log entry structure.
* This function returns the size including the log entry structure.
*
* Caller needs to hold log->mutex.
*/
static __u32 get_entry_msg_len(struct logger_log *log, size_t off)
{
struct logger_entry scratch;
struct logger_entry *entry;
entry = get_entry_header(log, off, &scratch);
return entry->len;
}
static size_t get_user_hdr_len(int ver)
{
if (ver < 2)
return sizeof(struct user_logger_entry_compat);
return sizeof(struct logger_entry);
}
static ssize_t copy_header_to_user(int ver, struct logger_entry *entry,
char __user *buf)
{
void *hdr;
size_t hdr_len;
struct user_logger_entry_compat v1;
if (ver < 2) {
v1.len = entry->len;
v1.__pad = 0;
v1.pid = entry->pid;
v1.tid = entry->tid;
v1.sec = entry->sec;
v1.nsec = entry->nsec;
hdr = &v1;
hdr_len = sizeof(struct user_logger_entry_compat);
} else {
hdr = entry;
hdr_len = sizeof(struct logger_entry);
}
return copy_to_user(buf, hdr, hdr_len);
}
/*
* do_read_log_to_user - reads exactly 'count' bytes from 'log' into the
* user-space buffer 'buf'. Returns 'count' on success.
*
* Caller must hold log->mutex.
*/
static ssize_t do_read_log_to_user(struct logger_log *log,
struct logger_reader *reader,
char __user *buf,
size_t count)
{
struct logger_entry scratch;
struct logger_entry *entry;
size_t len;
size_t msg_start;
/*
* First, copy the header to userspace, using the version of
* the header requested
*/
entry = get_entry_header(log, reader->r_off, &scratch);
if (copy_header_to_user(reader->r_ver, entry, buf))
return -EFAULT;
count -= get_user_hdr_len(reader->r_ver);
buf += get_user_hdr_len(reader->r_ver);
msg_start = logger_offset(log,
reader->r_off + sizeof(struct logger_entry));
/*
* We read from the msg in two disjoint operations. First, we read from
* the current msg head offset up to 'count' bytes or to the end of
* the log, whichever comes first.
*/
len = min(count, log->size - msg_start);
if (copy_to_user(buf, log->buffer + msg_start, len))
return -EFAULT;
/*
* Second, we read any remaining bytes, starting back at the head of
* the log.
*/
if (count != len)
if (copy_to_user(buf + len, log->buffer, count - len))
return -EFAULT;
reader->r_off = logger_offset(log, reader->r_off +
sizeof(struct logger_entry) + count);
return count + get_user_hdr_len(reader->r_ver);
}
/*
* get_next_entry_by_uid - Starting at 'off', returns an offset into
* 'log->buffer' which contains the first entry readable by 'euid'
*/
static size_t get_next_entry_by_uid(struct logger_log *log,
size_t off, kuid_t euid)
{
while (off != log->w_off) {
struct logger_entry *entry;
struct logger_entry scratch;
size_t next_len;
entry = get_entry_header(log, off, &scratch);
if (uid_eq(entry->euid, euid))
return off;
next_len = sizeof(struct logger_entry) + entry->len;
off = logger_offset(log, off + next_len);
}
return off;
}
/*
* logger_read - our log's read() method
*
* Behavior:
*
* - O_NONBLOCK works
* - If there are no log entries to read, blocks until log is written to
* - Atomically reads exactly one log entry
*
* Will set errno to EINVAL if read
* buffer is insufficient to hold next entry.
*/
static ssize_t logger_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
struct logger_reader *reader = file->private_data;
struct logger_log *log = reader->log;
ssize_t ret;
DEFINE_WAIT(wait);
start:
while (1) {
mutex_lock(&log->mutex);
prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
ret = (log->w_off == reader->r_off);
mutex_unlock(&log->mutex);
if (!ret)
break;
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -EINTR;
break;
}
schedule();
}
finish_wait(&log->wq, &wait);
if (ret)
return ret;
mutex_lock(&log->mutex);
if (!reader->r_all)
reader->r_off = get_next_entry_by_uid(log,
reader->r_off, current_euid());
/* is there still something to read or did we race? */
if (unlikely(log->w_off == reader->r_off)) {
mutex_unlock(&log->mutex);
goto start;
}
/* get the size of the next entry */
ret = get_user_hdr_len(reader->r_ver) +
get_entry_msg_len(log, reader->r_off);
if (count < ret) {
ret = -EINVAL;
goto out;
}
/* get exactly one entry from the log */
ret = do_read_log_to_user(log, reader, buf, ret);
out:
mutex_unlock(&log->mutex);
return ret;
}
/*
* get_next_entry - return the offset of the first valid entry at least 'len'
* bytes after 'off'.
*
* Caller must hold log->mutex.
*/
static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)
{
size_t count = 0;
do {
size_t nr = sizeof(struct logger_entry) +
get_entry_msg_len(log, off);
off = logger_offset(log, off + nr);
count += nr;
} while (count < len);
return off;
}
/*
* is_between - is a < c < b, accounting for wrapping of a, b, and c
* positions in the buffer
*
* That is, if a<b, check for c between a and b
* and if a>b, check for c outside (not between) a and b
*
* |------- a xxxxxxxx b --------|
* c^
*
* |xxxxx b --------- a xxxxxxxxx|
* c^
* or c^
*/
static inline int is_between(size_t a, size_t b, size_t c)
{
if (a < b) {
/* is c between a and b? */
if (a < c && c <= b)
return 1;
} else {
/* is c outside of b through a? */
if (c <= b || a < c)
return 1;
}
return 0;
}
/*
* fix_up_readers - walk the list of all readers and "fix up" any who were
* lapped by the writer; also do the same for the default "start head".
* We do this by "pulling forward" the readers and start head to the first
* entry after the new write head.
*
* The caller needs to hold log->mutex.
*/
static void fix_up_readers(struct logger_log *log, size_t len)
{
size_t old = log->w_off;
size_t new = logger_offset(log, old + len);
struct logger_reader *reader;
if (is_between(old, new, log->head))
log->head = get_next_entry(log, log->head, len);
list_for_each_entry(reader, &log->readers, list)
if (is_between(old, new, reader->r_off))
reader->r_off = get_next_entry(log, reader->r_off, len);
}
/*
* logger_write_iter - our write method, implementing support for write(),
* writev(), and aio_write(). Writes are our fast path, and we try to optimize
* them above all else.
*/
static ssize_t logger_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct logger_log *log = file_get_log(iocb->ki_filp);
struct logger_entry header;
struct timespec now;
size_t len, count, w_off;
count = min_t(size_t, iocb->ki_nbytes, LOGGER_ENTRY_MAX_PAYLOAD);
now = current_kernel_time();
header.pid = current->tgid;
header.tid = current->pid;
header.sec = now.tv_sec;
header.nsec = now.tv_nsec;
header.euid = current_euid();
header.len = count;
header.hdr_size = sizeof(struct logger_entry);
/* null writes succeed, return zero */
if (unlikely(!header.len))
return 0;
mutex_lock(&log->mutex);
/*
* Fix up any readers, pulling them forward to the first readable
* entry after (what will be) the new write offset. We do this now
* because if we partially fail, we can end up with clobbered log
* entries that encroach on readable buffer.
*/
fix_up_readers(log, sizeof(struct logger_entry) + header.len);
len = min(sizeof(header), log->size - log->w_off);
memcpy(log->buffer + log->w_off, &header, len);
memcpy(log->buffer, (char *)&header + len, sizeof(header) - len);
/* Work with a copy until we are ready to commit the whole entry */
w_off = logger_offset(log, log->w_off + sizeof(struct logger_entry));
len = min(count, log->size - w_off);
if (copy_from_iter(log->buffer + w_off, len, from) != len) {
/*
* Note that by not updating log->w_off, this abandons the
* portion of the new entry that *was* successfully
* copied, just above. This is intentional to avoid
* message corruption from missing fragments.
*/
mutex_unlock(&log->mutex);
return -EFAULT;
}
if (copy_from_iter(log->buffer, count - len, from) != count - len) {
mutex_unlock(&log->mutex);
return -EFAULT;
}
log->w_off = logger_offset(log, w_off + count);
mutex_unlock(&log->mutex);
/* wake up any blocked readers */
wake_up_interruptible(&log->wq);
return len;
}
static struct logger_log *get_log_from_minor(int minor)
{
struct logger_log *log;
list_for_each_entry(log, &log_list, logs)
if (log->misc.minor == minor)
return log;
return NULL;
}
/*
* logger_open - the log's open() file operation
*
* Note how near a no-op this is in the write-only case. Keep it that way!
*/
static int logger_open(struct inode *inode, struct file *file)
{
struct logger_log *log;
int ret;
ret = nonseekable_open(inode, file);
if (ret)
return ret;
log = get_log_from_minor(MINOR(inode->i_rdev));
if (!log)
return -ENODEV;
if (file->f_mode & FMODE_READ) {
struct logger_reader *reader;
reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);
if (!reader)
return -ENOMEM;
reader->log = log;
reader->r_ver = 1;
reader->r_all = in_egroup_p(inode->i_gid) ||
capable(CAP_SYSLOG);
INIT_LIST_HEAD(&reader->list);
mutex_lock(&log->mutex);
reader->r_off = log->head;
list_add_tail(&reader->list, &log->readers);
mutex_unlock(&log->mutex);
file->private_data = reader;
} else
file->private_data = log;
return 0;
}
/*
* logger_release - the log's release file operation
*
* Note this is a total no-op in the write-only case. Keep it that way!
*/
static int logger_release(struct inode *ignored, struct file *file)
{
if (file->f_mode & FMODE_READ) {
struct logger_reader *reader = file->private_data;
struct logger_log *log = reader->log;
mutex_lock(&log->mutex);
list_del(&reader->list);
mutex_unlock(&log->mutex);
kfree(reader);
}
return 0;
}
/*
* logger_poll - the log's poll file operation, for poll/select/epoll
*
* Note we always return POLLOUT, because you can always write() to the log.
* Note also that, strictly speaking, a return value of POLLIN does not
* guarantee that the log is readable without blocking, as there is a small
* chance that the writer can lap the reader in the interim between poll()
* returning and the read() request.
*/
static unsigned int logger_poll(struct file *file, poll_table *wait)
{
struct logger_reader *reader;
struct logger_log *log;
unsigned int ret = POLLOUT | POLLWRNORM;
if (!(file->f_mode & FMODE_READ))
return ret;
reader = file->private_data;
log = reader->log;
poll_wait(file, &log->wq, wait);
mutex_lock(&log->mutex);
if (!reader->r_all)
reader->r_off = get_next_entry_by_uid(log,
reader->r_off, current_euid());
if (log->w_off != reader->r_off)
ret |= POLLIN | POLLRDNORM;
mutex_unlock(&log->mutex);
return ret;
}
static long logger_set_version(struct logger_reader *reader, void __user *arg)
{
int version;
if (copy_from_user(&version, arg, sizeof(int)))
return -EFAULT;
if ((version < 1) || (version > 2))
return -EINVAL;
reader->r_ver = version;
return 0;
}
static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct logger_log *log = file_get_log(file);
struct logger_reader *reader;
long ret = -EINVAL;
void __user *argp = (void __user *) arg;
mutex_lock(&log->mutex);
switch (cmd) {
case LOGGER_GET_LOG_BUF_SIZE:
ret = log->size;
break;
case LOGGER_GET_LOG_LEN:
if (!(file->f_mode & FMODE_READ)) {
ret = -EBADF;
break;
}
reader = file->private_data;
if (log->w_off >= reader->r_off)
ret = log->w_off - reader->r_off;
else
ret = (log->size - reader->r_off) + log->w_off;
break;
case LOGGER_GET_NEXT_ENTRY_LEN:
if (!(file->f_mode & FMODE_READ)) {
ret = -EBADF;
break;
}
reader = file->private_data;
if (!reader->r_all)
reader->r_off = get_next_entry_by_uid(log,
reader->r_off, current_euid());
if (log->w_off != reader->r_off)
ret = get_user_hdr_len(reader->r_ver) +
get_entry_msg_len(log, reader->r_off);
else
ret = 0;
break;
case LOGGER_FLUSH_LOG:
if (!(file->f_mode & FMODE_WRITE)) {
ret = -EBADF;
break;
}
if (!(in_egroup_p(file_inode(file)->i_gid) ||
capable(CAP_SYSLOG))) {
ret = -EPERM;
break;
}
list_for_each_entry(reader, &log->readers, list)
reader->r_off = log->w_off;
log->head = log->w_off;
ret = 0;
break;
case LOGGER_GET_VERSION:
if (!(file->f_mode & FMODE_READ)) {
ret = -EBADF;
break;
}
reader = file->private_data;
ret = reader->r_ver;
break;
case LOGGER_SET_VERSION:
if (!(file->f_mode & FMODE_READ)) {
ret = -EBADF;
break;
}
reader = file->private_data;
ret = logger_set_version(reader, argp);
break;
}
mutex_unlock(&log->mutex);
return ret;
}
static const struct file_operations logger_fops = {
.owner = THIS_MODULE,
.read = logger_read,
.write_iter = logger_write_iter,
.poll = logger_poll,
.unlocked_ioctl = logger_ioctl,
.compat_ioctl = logger_ioctl,
.open = logger_open,
.release = logger_release,
};
/*
* Log size must must be a power of two, and greater than
* (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)).
*/
static int __init create_log(char *log_name, int size)
{
int ret = 0;
struct logger_log *log;
unsigned char *buffer;
buffer = vmalloc(size);
if (buffer == NULL)
return -ENOMEM;
log = kzalloc(sizeof(struct logger_log), GFP_KERNEL);
if (log == NULL) {
ret = -ENOMEM;
goto out_free_buffer;
}
log->buffer = buffer;
log->misc.minor = MISC_DYNAMIC_MINOR;
log->misc.name = kstrdup(log_name, GFP_KERNEL);
if (log->misc.name == NULL) {
ret = -ENOMEM;
goto out_free_log;
}
log->misc.fops = &logger_fops;
log->misc.parent = NULL;
init_waitqueue_head(&log->wq);
INIT_LIST_HEAD(&log->readers);
mutex_init(&log->mutex);
log->w_off = 0;
log->head = 0;
log->size = size;
INIT_LIST_HEAD(&log->logs);
list_add_tail(&log->logs, &log_list);
/* finally, initialize the misc device for this log */
ret = misc_register(&log->misc);
if (unlikely(ret)) {
pr_err("failed to register misc device for log '%s'!\n",
log->misc.name);
goto out_free_misc_name;
}
pr_info("created %luK log '%s'\n",
(unsigned long) log->size >> 10, log->misc.name);
return 0;
out_free_misc_name:
kfree(log->misc.name);
out_free_log:
kfree(log);
out_free_buffer:
vfree(buffer);
return ret;
}
static int __init logger_init(void)
{
int ret;
ret = create_log(LOGGER_LOG_MAIN, 256*1024);
if (unlikely(ret))
goto out;
ret = create_log(LOGGER_LOG_EVENTS, 256*1024);
if (unlikely(ret))
goto out;
ret = create_log(LOGGER_LOG_RADIO, 256*1024);
if (unlikely(ret))
goto out;
ret = create_log(LOGGER_LOG_SYSTEM, 256*1024);
if (unlikely(ret))
goto out;
out:
return ret;
}
static void __exit logger_exit(void)
{
struct logger_log *current_log, *next_log;
list_for_each_entry_safe(current_log, next_log, &log_list, logs) {
/* we have to delete all the entry inside log_list */
misc_deregister(&current_log->misc);
vfree(current_log->buffer);
kfree(current_log->misc.name);
list_del(&current_log->logs);
kfree(current_log);
}
}
device_initcall(logger_init);
module_exit(logger_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert Love, <rlove@google.com>");
MODULE_DESCRIPTION("Android Logger");

View File

@ -1,89 +0,0 @@
/* include/linux/logger.h
*
* Copyright (C) 2007-2008 Google, Inc.
* Author: Robert Love <rlove@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _LINUX_LOGGER_H
#define _LINUX_LOGGER_H
#include <linux/types.h>
#include <linux/ioctl.h>
/**
* struct user_logger_entry_compat - defines a single entry that is given to a logger
* @len: The length of the payload
* @__pad: Two bytes of padding that appear to be required
* @pid: The generating process' process ID
* @tid: The generating process' thread ID
* @sec: The number of seconds that have elapsed since the Epoch
* @nsec: The number of nanoseconds that have elapsed since @sec
* @msg: The message that is to be logged
*
* The userspace structure for version 1 of the logger_entry ABI.
* This structure is returned to userspace unless the caller requests
* an upgrade to a newer ABI version.
*/
struct user_logger_entry_compat {
__u16 len;
__u16 __pad;
__s32 pid;
__s32 tid;
__s32 sec;
__s32 nsec;
char msg[0];
};
/**
* struct logger_entry - defines a single entry that is given to a logger
* @len: The length of the payload
* @hdr_size: sizeof(struct logger_entry_v2)
* @pid: The generating process' process ID
* @tid: The generating process' thread ID
* @sec: The number of seconds that have elapsed since the Epoch
* @nsec: The number of nanoseconds that have elapsed since @sec
* @euid: Effective UID of logger
* @msg: The message that is to be logged
*
* The structure for version 2 of the logger_entry ABI.
* This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
* is called with version >= 2
*/
struct logger_entry {
__u16 len;
__u16 hdr_size;
__s32 pid;
__s32 tid;
__s32 sec;
__s32 nsec;
kuid_t euid;
char msg[0];
};
#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */
#define LOGGER_LOG_MAIN "log_main" /* everything else */
#define LOGGER_ENTRY_MAX_PAYLOAD 4076
#define __LOGGERIO 0xAE
#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */
#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */
#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */
#endif /* _LINUX_LOGGER_H */

View File

@ -96,7 +96,8 @@ static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
sync_status_str(status));
if (status <= 0) {
struct timespec64 ts64 = ktime_to_timespec64(pt->base.timestamp);
struct timespec64 ts64 =
ktime_to_timespec64(pt->base.timestamp);
seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
}

View File

@ -1,62 +0,0 @@
/* drivers/staging/android/uapi/android_alarm.h
*
* Copyright (C) 2006-2007 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _UAPI_LINUX_ANDROID_ALARM_H
#define _UAPI_LINUX_ANDROID_ALARM_H
#include <linux/ioctl.h>
#include <linux/time.h>
enum android_alarm_type {
/* return code bit numbers or set alarm arg */
ANDROID_ALARM_RTC_WAKEUP,
ANDROID_ALARM_RTC,
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
ANDROID_ALARM_ELAPSED_REALTIME,
ANDROID_ALARM_SYSTEMTIME,
ANDROID_ALARM_TYPE_COUNT,
/* return code bit numbers */
/* ANDROID_ALARM_TIME_CHANGE = 16 */
};
enum android_alarm_return_flags {
ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP,
ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC,
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK =
1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
ANDROID_ALARM_ELAPSED_REALTIME_MASK =
1U << ANDROID_ALARM_ELAPSED_REALTIME,
ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME,
ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16
};
/* Disable alarm */
#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4))
/* Ack last alarm and wait for next */
#define ANDROID_ALARM_WAIT _IO('a', 1)
#define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size)
/* Set alarm */
#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec)
#define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec)
#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec)
#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec)
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)
#endif

View File

@ -11,8 +11,7 @@ static bool find_by_address(u64 base_address)
struct resource res;
while (dn) {
if (of_can_translate_address(dn)
&& !of_address_to_resource(dn, 0, &res)) {
if (!of_address_to_resource(dn, 0, &res)) {
if (res.start == base_address) {
of_node_put(dn);
return true;

View File

@ -91,8 +91,10 @@ static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event,
if (ndata->clk == clk_wzrd->clk_in1)
max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1];
if (ndata->clk == clk_wzrd->axi_clk)
else if (ndata->clk == clk_wzrd->axi_clk)
max = WZRD_ACLK_MAX_FREQ;
else
return NOTIFY_DONE; /* should never happen */
switch (event) {
case PRE_RATE_CHANGE:
@ -239,6 +241,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
/* register div per output */
for (i = WZRD_NUM_OUTPUTS - 1; i >= 0 ; i--) {
const char *clkout_name;
if (of_property_read_string_index(np, "clock-output-names", i,
&clkout_name)) {
dev_err(&pdev->dev,

View File

@ -168,7 +168,7 @@ config COMEDI_PCL730
config COMEDI_PCL812
tristate "Advantech PCL-812/813 and ADlink ACL-8112/8113/8113/8216"
depends on VIRT_TO_BUS && ISA_DMA_API
select COMEDI_ISADMA if ISA_DMA_API
---help---
Enable support for Advantech PCL-812/PG, PCL-813/B, ADLink
ACL-8112DG/HG/PG, ACL-8113, ACL-8216, ICP DAS A-821PGH/PGL/PGL-NDA,
@ -179,7 +179,7 @@ config COMEDI_PCL812
config COMEDI_PCL816
tristate "Advantech PCL-814 and PCL-816 ISA card support"
depends on VIRT_TO_BUS && ISA_DMA_API
select COMEDI_ISADMA if ISA_DMA_API
---help---
Enable support for Advantech PCL-814 and PCL-816 ISA cards
@ -188,7 +188,7 @@ config COMEDI_PCL816
config COMEDI_PCL818
tristate "Advantech PCL-718 and PCL-818 ISA card support"
depends on VIRT_TO_BUS && ISA_DMA_API
select COMEDI_ISADMA if ISA_DMA_API
---help---
Enable support for Advantech PCL-818 ISA cards
PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818 and PCL-718
@ -281,7 +281,7 @@ config COMEDI_DAS08_ISA
config COMEDI_DAS16
tristate "DAS-16 compatible ISA and PC/104 card support"
depends on ISA_DMA_API
select COMEDI_ISADMA if ISA_DMA_API
select COMEDI_8255
---help---
Enable support for Keithley Metrabyte/ComputerBoards DAS16
@ -309,7 +309,7 @@ config COMEDI_DAS800
config COMEDI_DAS1800
tristate "DAS1800 and compatible ISA card support"
depends on VIRT_TO_BUS && ISA_DMA_API
select COMEDI_ISADMA if ISA_DMA_API
---help---
Enable support for DAS1800 and compatible ISA cards
Keithley Metrabyte DAS-1701ST, DAS-1701ST-DA, DAS-1701/AO,
@ -372,7 +372,7 @@ config COMEDI_DT2817
config COMEDI_DT282X
tristate "Data Translation DT2821 series and DT-EZ ISA card support"
depends on VIRT_TO_BUS && ISA_DMA_API
select COMEDI_ISADMA if ISA_DMA_API
---help---
Enable support for Data Translation DT2821 series including DT-EZ
DT2821, DT2821-F-16SE, DT2821-F-8DI, DT2821-G-16SE, DT2821-G-8DI,
@ -462,7 +462,7 @@ config COMEDI_ADQ12B
config COMEDI_NI_AT_A2150
tristate "NI AT-A2150 ISA card support"
depends on VIRT_TO_BUS && ISA_DMA_API
select COMEDI_ISADMA if ISA_DMA_API
---help---
Enable support for National Instruments AT-A2150 cards
@ -502,7 +502,7 @@ config COMEDI_NI_ATMIO16D
config COMEDI_NI_LABPC_ISA
tristate "NI Lab-PC and compatibles ISA support"
select COMEDI_NI_LABPC
select COMEDI_NI_LABPC_ISADMA if ISA_DMA_API && VIRT_TO_BUS
select COMEDI_NI_LABPC_ISADMA if ISA_DMA_API
---help---
Enable support for National Instruments Lab-PC and compatibles
Lab-PC-1200, Lab-PC-1200AI, Lab-PC+.
@ -724,7 +724,6 @@ config COMEDI_ADL_PCI9111
config COMEDI_ADL_PCI9118
tristate "ADLink PCI-9118DG, PCI-9118HG, PCI-9118HR support"
depends on HAS_DMA
depends on VIRT_TO_BUS
---help---
Enable support for ADlink PCI-9118DG, PCI-9118HG, PCI-9118HR cards
@ -1263,12 +1262,16 @@ config COMEDI_DAS08
tristate
select COMEDI_8255
config COMEDI_ISADMA
tristate
config COMEDI_NI_LABPC
tristate
select COMEDI_8255
config COMEDI_NI_LABPC_ISADMA
tristate
select COMEDI_ISADMA
config COMEDI_NI_TIO
tristate

View File

@ -1,23 +1,23 @@
/*
comedi/comedi_compat32.c
32-bit ioctl compatibility for 64-bit comedi kernel module.
Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
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.
*/
* comedi/comedi_compat32.c
* 32-bit ioctl compatibility for 64-bit comedi kernel module.
*
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
* Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
*
* 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/uaccess.h>
#include <linux/compat.h>
@ -27,11 +27,15 @@
#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
/* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number. */
/*
* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number.
*/
#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
/* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number. */
/*
* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number.
*/
#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
@ -39,7 +43,7 @@
struct comedi32_chaninfo_struct {
unsigned int subdev;
compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */
compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
unsigned int unused[4];
};
@ -62,16 +66,16 @@ struct comedi32_cmd_struct {
unsigned int scan_end_arg;
unsigned int stop_src;
unsigned int stop_arg;
compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
unsigned int chanlist_len;
compat_uptr_t data; /* 32-bit 'short *' */
compat_uptr_t data; /* 32-bit 'short *' */
unsigned int data_len;
};
struct comedi32_insn_struct {
unsigned int insn;
unsigned int n;
compat_uptr_t data; /* 32-bit 'unsigned int *' */
compat_uptr_t data; /* 32-bit 'unsigned int *' */
unsigned int subdev;
unsigned int chanspec;
unsigned int unused[3];
@ -79,7 +83,7 @@ struct comedi32_insn_struct {
struct comedi32_insnlist_struct {
unsigned int n_insns;
compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
};
/* Handle translated ioctl. */
@ -215,10 +219,12 @@ static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
int err;
unsigned int temp;
/* Copy back most of cmd structure. */
/* Assume the pointer values are already valid. */
/* (Could use ptr_to_compat() to set them, but that wasn't implemented
* until kernel version 2.6.11.) */
/*
* Copy back most of cmd structure.
*
* Assume the pointer values are already valid.
* (Could use ptr_to_compat() to set them.)
*/
if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd)) ||
!access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32)))
return -EFAULT;
@ -262,7 +268,7 @@ static int compat_cmd(struct file *file, unsigned long arg)
{
struct comedi_cmd __user *cmd;
struct comedi32_cmd_struct __user *cmd32;
int rc;
int rc, err;
cmd32 = compat_ptr(arg);
cmd = compat_alloc_user_space(sizeof(*cmd));
@ -271,7 +277,15 @@ static int compat_cmd(struct file *file, unsigned long arg)
if (rc)
return rc;
return translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
if (rc == -EAGAIN) {
/* Special case: copy cmd back to user. */
err = put_compat_cmd(cmd32, cmd);
if (err)
rc = err;
}
return rc;
}
/* Handle 32-bit COMEDI_CMDTEST ioctl. */
@ -395,10 +409,12 @@ static int compat_insn(struct file *file, unsigned long arg)
return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
}
/* Process untranslated ioctl. */
/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
static inline int raw_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
/*
* compat_ioctl file operation.
*
* Returns -ENOIOCTLCMD for unrecognised ioctl codes.
*/
long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc;
@ -445,10 +461,3 @@ static inline int raw_ioctl(struct file *file, unsigned int cmd,
}
return rc;
}
/* compat_ioctl file operation. */
/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return raw_ioctl(file, cmd, arg);
}

View File

@ -1,23 +1,23 @@
/*
comedi/comedi_compat32.h
32-bit ioctl compatibility for 64-bit comedi kernel module.
Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
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.
*/
* comedi/comedi_compat32.h
* 32-bit ioctl compatibility for 64-bit comedi kernel module.
*
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
* Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _COMEDI_COMPAT32_H
#define _COMEDI_COMPAT32_H

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