Staging driver patches for 4.3-rc1
Here is the big staging driver updates for 4.3-rc1. Lots of things all over the place, almost all of them trivial fixups and changes. The usual IIO updates and new drivers and we have added the MOST driver subsystem which is getting cleaned up in the tree. The ozwpan driver is finally being deleted as it is obviously abandoned and no one cares about it. Full details are in the shortlog, and all of these have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlXV9S8ACgkQMUfUDdst+ykAwQCfSOc+y5AG7+63cIdnLwRS3MdZ p3EAoLoJXl8+5hfIsRBlwXyLEkN33f/7 =dD9z -----END PGP SIGNATURE----- Merge tag 'staging-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver updates from Greg KH: "Here is the big staging driver updates for 4.3-rc1. Lots of things all over the place, almost all of them trivial fixups and changes. The usual IIO updates and new drivers and we have added the MOST driver subsystem which is getting cleaned up in the tree. The ozwpan driver is finally being deleted as it is obviously abandoned and no one cares about it. Full details are in the shortlog, and all of these have been in linux-next with no reported issues" * tag 'staging-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (912 commits) staging/lustre/o2iblnd: remove references to ib_reg_phsy_mr() staging: wilc1000: fix build warning with setup_timer() staging: wilc1000: remove DECLARE_WILC_BUFFER() staging: wilc1000: remove void function return statements that are not useful staging: wilc1000: coreconfigurator.c: fix kmalloc error check staging: wilc1000: coreconfigurator.c: use kmalloc instead of WILC_MALLOC staging: wilc1000: remove unused codes of gps8ConfigPacket staging: wilc1000: remove unnecessary void pointer cast staging: wilc1000: remove WILC_NEW and WILC_NEW_EX staging: wilc1000: use kmalloc instead of WILC_NEW staging: wilc1000: Process WARN, INFO options of debug levels from user staging: wilc1000: remove unneeded tstrWILC_MsgQueueAttrs typedef staging: wilc1000: delete wilc_osconfig.h staging: wilc1000: delete wilc_log.h staging: wilc1000: delete wilc_timer.h staging: wilc1000: remove WILC_TimerStart() staging: wilc1000: remove WILC_TimerCreate() staging: wilc1000: remove WILC_TimerDestroy() staging: wilc1000: remove WILC_TimerStop() staging: wilc1000: remove tstrWILC_TimerAttrs typedef ...
This commit is contained in:
commit
2f37d65a6a
|
@ -413,6 +413,11 @@ Description:
|
||||||
to compute the calories burnt by the user.
|
to compute the calories burnt by the user.
|
||||||
|
|
||||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
|
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
|
||||||
|
What: /sys/.../iio:deviceX/in_anglvel_scale_available
|
||||||
|
What: /sys/.../iio:deviceX/in_magn_scale_available
|
||||||
|
What: /sys/.../iio:deviceX/in_illuminance_scale_available
|
||||||
|
What: /sys/.../iio:deviceX/in_intensity_scale_available
|
||||||
|
What: /sys/.../iio:deviceX/in_proximity_scale_available
|
||||||
What: /sys/.../iio:deviceX/in_voltageX_scale_available
|
What: /sys/.../iio:deviceX/in_voltageX_scale_available
|
||||||
What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available
|
What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available
|
||||||
What: /sys/.../iio:deviceX/out_voltageX_scale_available
|
What: /sys/.../iio:deviceX/out_voltageX_scale_available
|
||||||
|
@ -488,7 +493,7 @@ Contact: linux-iio@vger.kernel.org
|
||||||
Description:
|
Description:
|
||||||
Specifies the output powerdown mode.
|
Specifies the output powerdown mode.
|
||||||
DAC output stage is disconnected from the amplifier and
|
DAC output stage is disconnected from the amplifier and
|
||||||
1kohm_to_gnd: connected to ground via an 1kOhm resistor,
|
1kohm_to_gnd: connected to ground via an 1kOhm resistor,
|
||||||
6kohm_to_gnd: connected to ground via a 6kOhm resistor,
|
6kohm_to_gnd: connected to ground via a 6kOhm resistor,
|
||||||
20kohm_to_gnd: connected to ground via a 20kOhm resistor,
|
20kohm_to_gnd: connected to ground via a 20kOhm resistor,
|
||||||
100kohm_to_gnd: connected to ground via an 100kOhm resistor,
|
100kohm_to_gnd: connected to ground via an 100kOhm resistor,
|
||||||
|
@ -498,9 +503,9 @@ Description:
|
||||||
outX_powerdown_mode_available. If Y is not present the
|
outX_powerdown_mode_available. If Y is not present the
|
||||||
mode is shared across all outputs.
|
mode is shared across all outputs.
|
||||||
|
|
||||||
What: /sys/.../iio:deviceX/out_votlageY_powerdown_mode_available
|
What: /sys/.../iio:deviceX/out_voltageY_powerdown_mode_available
|
||||||
What: /sys/.../iio:deviceX/out_voltage_powerdown_mode_available
|
What: /sys/.../iio:deviceX/out_voltage_powerdown_mode_available
|
||||||
What: /sys/.../iio:deviceX/out_altvotlageY_powerdown_mode_available
|
What: /sys/.../iio:deviceX/out_altvoltageY_powerdown_mode_available
|
||||||
What: /sys/.../iio:deviceX/out_altvoltage_powerdown_mode_available
|
What: /sys/.../iio:deviceX/out_altvoltage_powerdown_mode_available
|
||||||
KernelVersion: 2.6.38
|
KernelVersion: 2.6.38
|
||||||
Contact: linux-iio@vger.kernel.org
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
@ -1035,13 +1040,6 @@ Contact: linux-iio@vger.kernel.org
|
||||||
Description:
|
Description:
|
||||||
Number of scans contained by the buffer.
|
Number of scans contained by the buffer.
|
||||||
|
|
||||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/bytes_per_datum
|
|
||||||
KernelVersion: 2.6.37
|
|
||||||
Contact: linux-iio@vger.kernel.org
|
|
||||||
Description:
|
|
||||||
Bytes per scan. Due to alignment fun, the scan may be larger
|
|
||||||
than implied directly by the scan_element parameters.
|
|
||||||
|
|
||||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/enable
|
What: /sys/bus/iio/devices/iio:deviceX/buffer/enable
|
||||||
KernelVersion: 2.6.35
|
KernelVersion: 2.6.35
|
||||||
Contact: linux-iio@vger.kernel.org
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
|
|
@ -9,3 +9,12 @@ Description:
|
||||||
automated testing or in situations, where other trigger methods
|
automated testing or in situations, where other trigger methods
|
||||||
are not applicable. For example no RTC or spare GPIOs.
|
are not applicable. For example no RTC or spare GPIOs.
|
||||||
X is the IIO index of the trigger.
|
X is the IIO index of the trigger.
|
||||||
|
|
||||||
|
What: /sys/bus/iio/devices/triggerX/name
|
||||||
|
KernelVersion: 2.6.39
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
The name attribute holds a description string for the current
|
||||||
|
trigger. In order to associate the trigger with an IIO device
|
||||||
|
one should write this name string to
|
||||||
|
/sys/bus/iio/devices/iio:deviceY/trigger/current_trigger.
|
||||||
|
|
|
@ -15,7 +15,7 @@ DOCBOOKS := z8530book.xml device-drivers.xml \
|
||||||
80211.xml debugobjects.xml sh.xml regulator.xml \
|
80211.xml debugobjects.xml sh.xml regulator.xml \
|
||||||
alsa-driver-api.xml writing-an-alsa-driver.xml \
|
alsa-driver-api.xml writing-an-alsa-driver.xml \
|
||||||
tracepoint.xml drm.xml media_api.xml w1.xml \
|
tracepoint.xml drm.xml media_api.xml w1.xml \
|
||||||
writing_musb_glue_layer.xml crypto-API.xml
|
writing_musb_glue_layer.xml crypto-API.xml iio.xml
|
||||||
|
|
||||||
include Documentation/DocBook/media/Makefile
|
include Documentation/DocBook/media/Makefile
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,697 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
|
||||||
|
|
||||||
|
<book id="iioid">
|
||||||
|
<bookinfo>
|
||||||
|
<title>Industrial I/O driver developer's guide </title>
|
||||||
|
|
||||||
|
<authorgroup>
|
||||||
|
<author>
|
||||||
|
<firstname>Daniel</firstname>
|
||||||
|
<surname>Baluta</surname>
|
||||||
|
<affiliation>
|
||||||
|
<address>
|
||||||
|
<email>daniel.baluta@intel.com</email>
|
||||||
|
</address>
|
||||||
|
</affiliation>
|
||||||
|
</author>
|
||||||
|
</authorgroup>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2015</year>
|
||||||
|
<holder>Intel Corporation</holder>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>
|
||||||
|
This documentation is free software; you can redistribute
|
||||||
|
it and/or modify it under the terms of the GNU General Public
|
||||||
|
License version 2.
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
|
</bookinfo>
|
||||||
|
|
||||||
|
<toc></toc>
|
||||||
|
|
||||||
|
<chapter id="intro">
|
||||||
|
<title>Introduction</title>
|
||||||
|
<para>
|
||||||
|
The main purpose of the Industrial I/O subsystem (IIO) is to provide
|
||||||
|
support for devices that in some sense perform either analog-to-digital
|
||||||
|
conversion (ADC) or digital-to-analog conversion (DAC) or both. The aim
|
||||||
|
is to fill the gap between the somewhat similar hwmon and input
|
||||||
|
subsystems.
|
||||||
|
Hwmon is directed at low sample rate sensors used to monitor and
|
||||||
|
control the system itself, like fan speed control or temperature
|
||||||
|
measurement. Input is, as its name suggests, focused on human interaction
|
||||||
|
input devices (keyboard, mouse, touchscreen). In some cases there is
|
||||||
|
considerable overlap between these and IIO.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Devices that fall into this category include:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
analog to digital converters (ADCs)
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
accelerometers
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
capacitance to digital converters (CDCs)
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
digital to analog converters (DACs)
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
gyroscopes
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
inertial measurement units (IMUs)
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
color and light sensors
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
magnetometers
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
pressure sensors
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
proximity sensors
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
temperature sensors
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
Usually these sensors are connected via SPI or I2C. A common use case of the
|
||||||
|
sensors devices is to have combined functionality (e.g. light plus proximity
|
||||||
|
sensor).
|
||||||
|
</para>
|
||||||
|
</chapter>
|
||||||
|
<chapter id='iiosubsys'>
|
||||||
|
<title>Industrial I/O core</title>
|
||||||
|
<para>
|
||||||
|
The Industrial I/O core offers:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
a unified framework for writing drivers for many different types of
|
||||||
|
embedded sensors.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
a standard interface to user space applications manipulating sensors.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
The implementation can be found under <filename>
|
||||||
|
drivers/iio/industrialio-*</filename>
|
||||||
|
</para>
|
||||||
|
<sect1 id="iiodevice">
|
||||||
|
<title> Industrial I/O devices </title>
|
||||||
|
|
||||||
|
!Finclude/linux/iio/iio.h iio_dev
|
||||||
|
!Fdrivers/iio/industrialio-core.c iio_device_alloc
|
||||||
|
!Fdrivers/iio/industrialio-core.c iio_device_free
|
||||||
|
!Fdrivers/iio/industrialio-core.c iio_device_register
|
||||||
|
!Fdrivers/iio/industrialio-core.c iio_device_unregister
|
||||||
|
|
||||||
|
<para>
|
||||||
|
An IIO device usually corresponds to a single hardware sensor and it
|
||||||
|
provides all the information needed by a driver handling a device.
|
||||||
|
Let's first have a look at the functionality embedded in an IIO
|
||||||
|
device then we will show how a device driver makes use of an IIO
|
||||||
|
device.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
There are two ways for a user space application to interact
|
||||||
|
with an IIO driver.
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/iio:deviceX/</filename>, this
|
||||||
|
represents a hardware sensor and groups together the data
|
||||||
|
channels of the same chip.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>/dev/iio:deviceX</filename>, character device node
|
||||||
|
interface used for buffered data transfer and for events information
|
||||||
|
retrieval.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
A typical IIO driver will register itself as an I2C or SPI driver and will
|
||||||
|
create two routines, <function> probe </function> and <function> remove
|
||||||
|
</function>. At <function>probe</function>:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>call <function>iio_device_alloc</function>, which allocates memory
|
||||||
|
for an IIO device.
|
||||||
|
</listitem>
|
||||||
|
<listitem> initialize IIO device fields with driver specific information
|
||||||
|
(e.g. device name, device channels).
|
||||||
|
</listitem>
|
||||||
|
<listitem>call <function> iio_device_register</function>, this registers the
|
||||||
|
device with the IIO core. After this call the device is ready to accept
|
||||||
|
requests from user space applications.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
At <function>remove</function>, we free the resources allocated in
|
||||||
|
<function>probe</function> in reverse order:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><function>iio_device_unregister</function>, unregister the device
|
||||||
|
from the IIO core.
|
||||||
|
</listitem>
|
||||||
|
<listitem><function>iio_device_free</function>, free the memory allocated
|
||||||
|
for the IIO device.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
<sect2 id="iioattr"> <title> IIO device sysfs interface </title>
|
||||||
|
<para>
|
||||||
|
Attributes are sysfs files used to expose chip info and also allowing
|
||||||
|
applications to set various configuration parameters. For device
|
||||||
|
with index X, attributes can be found under
|
||||||
|
<filename>/sys/bus/iio/iio:deviceX/ </filename> directory.
|
||||||
|
Common attributes are:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><filename>name</filename>, description of the physical
|
||||||
|
chip.
|
||||||
|
</listitem>
|
||||||
|
<listitem><filename>dev</filename>, shows the major:minor pair
|
||||||
|
associated with <filename>/dev/iio:deviceX</filename> node.
|
||||||
|
</listitem>
|
||||||
|
<listitem><filename>sampling_frequency_available</filename>,
|
||||||
|
available discrete set of sampling frequency values for
|
||||||
|
device.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
Available standard attributes for IIO devices are described in the
|
||||||
|
<filename>Documentation/ABI/testing/sysfs-bus-iio </filename> file
|
||||||
|
in the Linux kernel sources.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
<sect2 id="iiochannel"> <title> IIO device channels </title>
|
||||||
|
!Finclude/linux/iio/iio.h iio_chan_spec structure.
|
||||||
|
<para>
|
||||||
|
An IIO device channel is a representation of a data channel. An
|
||||||
|
IIO device can have one or multiple channels. For example:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
a thermometer sensor has one channel representing the
|
||||||
|
temperature measurement.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
a light sensor with two channels indicating the measurements in
|
||||||
|
the visible and infrared spectrum.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
an accelerometer can have up to 3 channels representing
|
||||||
|
acceleration on X, Y and Z axes.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
An IIO channel is described by the <type> struct iio_chan_spec
|
||||||
|
</type>. A thermometer driver for the temperature sensor in the
|
||||||
|
example above would have to describe its channel as follows:
|
||||||
|
<programlisting>
|
||||||
|
static const struct iio_chan_spec temp_channel[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_TEMP,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
</programlisting>
|
||||||
|
Channel sysfs attributes exposed to userspace are specified in
|
||||||
|
the form of <emphasis>bitmasks</emphasis>. Depending on their
|
||||||
|
shared info, attributes can be set in one of the following masks:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><emphasis>info_mask_separate</emphasis>, attributes will
|
||||||
|
be specific to this channel</listitem>
|
||||||
|
<listitem><emphasis>info_mask_shared_by_type</emphasis>,
|
||||||
|
attributes are shared by all channels of the same type</listitem>
|
||||||
|
<listitem><emphasis>info_mask_shared_by_dir</emphasis>, attributes
|
||||||
|
are shared by all channels of the same direction </listitem>
|
||||||
|
<listitem><emphasis>info_mask_shared_by_all</emphasis>,
|
||||||
|
attributes are shared by all channels</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
When there are multiple data channels per channel type we have two
|
||||||
|
ways to distinguish between them:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem> set <emphasis> .modified</emphasis> field of <type>
|
||||||
|
iio_chan_spec</type> to 1. Modifiers are specified using
|
||||||
|
<emphasis>.channel2</emphasis> field of the same
|
||||||
|
<type>iio_chan_spec</type> structure and are used to indicate a
|
||||||
|
physically unique characteristic of the channel such as its direction
|
||||||
|
or spectral response. For example, a light sensor can have two channels,
|
||||||
|
one for infrared light and one for both infrared and visible light.
|
||||||
|
</listitem>
|
||||||
|
<listitem> set <emphasis>.indexed </emphasis> field of
|
||||||
|
<type>iio_chan_spec</type> to 1. In this case the channel is
|
||||||
|
simply another instance with an index specified by the
|
||||||
|
<emphasis>.channel</emphasis> field.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
Here is how we can make use of the channel's modifiers:
|
||||||
|
<programlisting>
|
||||||
|
static const struct iio_chan_spec light_channels[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_INTENSITY,
|
||||||
|
.modified = 1,
|
||||||
|
.channel2 = IIO_MOD_LIGHT_IR,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||||
|
.info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IIO_INTENSITY,
|
||||||
|
.modified = 1,
|
||||||
|
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||||
|
.info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IIO_LIGHT,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||||
|
.info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
This channel's definition will generate two separate sysfs files
|
||||||
|
for raw data retrieval:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/iio:deviceX/in_intensity_ir_raw</filename>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/iio:deviceX/in_intensity_both_raw</filename>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
one file for processed data:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/iio:deviceX/in_illuminance_input
|
||||||
|
</filename>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
and one shared sysfs file for sampling frequency:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/iio:deviceX/sampling_frequency.
|
||||||
|
</filename>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Here is how we can make use of the channel's indexing:
|
||||||
|
<programlisting>
|
||||||
|
static const struct iio_chan_spec light_channels[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_VOLTAGE,
|
||||||
|
.indexed = 1,
|
||||||
|
.channel = 0,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IIO_VOLTAGE,
|
||||||
|
.indexed = 1,
|
||||||
|
.channel = 1,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
This will generate two separate attributes files for raw data
|
||||||
|
retrieval:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/devices/iio:deviceX/in_voltage0_raw</filename>,
|
||||||
|
representing voltage measurement for channel 0.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/devices/iio:deviceX/in_voltage1_raw</filename>,
|
||||||
|
representing voltage measurement for channel 1.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="iiobuffer"> <title> Industrial I/O buffers </title>
|
||||||
|
!Finclude/linux/iio/buffer.h iio_buffer
|
||||||
|
!Edrivers/iio/industrialio-buffer.c
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The Industrial I/O core offers a way for continuous data capture
|
||||||
|
based on a trigger source. Multiple data channels can be read at once
|
||||||
|
from <filename>/dev/iio:deviceX</filename> character device node,
|
||||||
|
thus reducing the CPU load.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="iiobuffersysfs">
|
||||||
|
<title>IIO buffer sysfs interface </title>
|
||||||
|
<para>
|
||||||
|
An IIO buffer has an associated attributes directory under <filename>
|
||||||
|
/sys/bus/iio/iio:deviceX/buffer/</filename>. Here are the existing
|
||||||
|
attributes:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<emphasis>length</emphasis>, the total number of data samples
|
||||||
|
(capacity) that can be stored by the buffer.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<emphasis>enable</emphasis>, activate buffer capture.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
<sect2 id="iiobuffersetup"> <title> IIO buffer setup </title>
|
||||||
|
<para>The meta information associated with a channel reading
|
||||||
|
placed in a buffer is called a <emphasis> scan element </emphasis>.
|
||||||
|
The important bits configuring scan elements are exposed to
|
||||||
|
userspace applications via the <filename>
|
||||||
|
/sys/bus/iio/iio:deviceX/scan_elements/</filename> directory. This
|
||||||
|
file contains attributes of the following form:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><emphasis>enable</emphasis>, used for enabling a channel.
|
||||||
|
If and only if its attribute is non zero, then a triggered capture
|
||||||
|
will contain data samples for this channel.
|
||||||
|
</listitem>
|
||||||
|
<listitem><emphasis>type</emphasis>, description of the scan element
|
||||||
|
data storage within the buffer and hence the form in which it is
|
||||||
|
read from user space. Format is <emphasis>
|
||||||
|
[be|le]:[s|u]bits/storagebitsXrepeat[>>shift] </emphasis>.
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem> <emphasis>be</emphasis> or <emphasis>le</emphasis>, specifies
|
||||||
|
big or little endian.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<emphasis>s </emphasis>or <emphasis>u</emphasis>, specifies if
|
||||||
|
signed (2's complement) or unsigned.
|
||||||
|
</listitem>
|
||||||
|
<listitem><emphasis>bits</emphasis>, is the number of valid data
|
||||||
|
bits.
|
||||||
|
</listitem>
|
||||||
|
<listitem><emphasis>storagebits</emphasis>, is the number of bits
|
||||||
|
(after padding) that it occupies in the buffer.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<emphasis>shift</emphasis>, if specified, is the shift that needs
|
||||||
|
to be applied prior to masking out unused bits.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<emphasis>repeat</emphasis>, specifies the number of bits/storagebits
|
||||||
|
repetitions. When the repeat element is 0 or 1, then the repeat
|
||||||
|
value is omitted.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
For example, a driver for a 3-axis accelerometer with 12 bit
|
||||||
|
resolution where data is stored in two 8-bits registers as
|
||||||
|
follows:
|
||||||
|
<programlisting>
|
||||||
|
7 6 5 4 3 2 1 0
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
|D3 |D2 |D1 |D0 | X | X | X | X | (LOW byte, address 0x06)
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
|
||||||
|
7 6 5 4 3 2 1 0
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
|D11|D10|D9 |D8 |D7 |D6 |D5 |D4 | (HIGH byte, address 0x07)
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
will have the following scan element type for each axis:
|
||||||
|
<programlisting>
|
||||||
|
$ cat /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_y_type
|
||||||
|
le:s12/16>>4
|
||||||
|
</programlisting>
|
||||||
|
A user space application will interpret data samples read from the
|
||||||
|
buffer as two byte little endian signed data, that needs a 4 bits
|
||||||
|
right shift before masking out the 12 valid bits of data.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For implementing buffer support a driver should initialize the following
|
||||||
|
fields in <type>iio_chan_spec</type> definition:
|
||||||
|
<programlisting>
|
||||||
|
struct iio_chan_spec {
|
||||||
|
/* other members */
|
||||||
|
int scan_index
|
||||||
|
struct {
|
||||||
|
char sign;
|
||||||
|
u8 realbits;
|
||||||
|
u8 storagebits;
|
||||||
|
u8 shift;
|
||||||
|
u8 repeat;
|
||||||
|
enum iio_endian endianness;
|
||||||
|
} scan_type;
|
||||||
|
};
|
||||||
|
</programlisting>
|
||||||
|
The driver implementing the accelerometer described above will
|
||||||
|
have the following channel definition:
|
||||||
|
<programlisting>
|
||||||
|
struct struct iio_chan_spec accel_channels[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_ACCEL,
|
||||||
|
.modified = 1,
|
||||||
|
.channel2 = IIO_MOD_X,
|
||||||
|
/* other stuff here */
|
||||||
|
.scan_index = 0,
|
||||||
|
.scan_type = {
|
||||||
|
.sign = 's',
|
||||||
|
.realbits = 12,
|
||||||
|
.storgebits = 16,
|
||||||
|
.shift = 4,
|
||||||
|
.endianness = IIO_LE,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
/* similar for Y (with channel2 = IIO_MOD_Y, scan_index = 1)
|
||||||
|
* and Z (with channel2 = IIO_MOD_Z, scan_index = 2) axis
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Here <emphasis> scan_index </emphasis> defines the order in which
|
||||||
|
the enabled channels are placed inside the buffer. Channels with a lower
|
||||||
|
scan_index will be placed before channels with a higher index. Each
|
||||||
|
channel needs to have a unique scan_index.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Setting scan_index to -1 can be used to indicate that the specific
|
||||||
|
channel does not support buffered capture. In this case no entries will
|
||||||
|
be created for the channel in the scan_elements directory.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="iiotrigger"> <title> Industrial I/O triggers </title>
|
||||||
|
!Finclude/linux/iio/trigger.h iio_trigger
|
||||||
|
!Edrivers/iio/industrialio-trigger.c
|
||||||
|
<para>
|
||||||
|
In many situations it is useful for a driver to be able to
|
||||||
|
capture data based on some external event (trigger) as opposed
|
||||||
|
to periodically polling for data. An IIO trigger can be provided
|
||||||
|
by a device driver that also has an IIO device based on hardware
|
||||||
|
generated events (e.g. data ready or threshold exceeded) or
|
||||||
|
provided by a separate driver from an independent interrupt
|
||||||
|
source (e.g. GPIO line connected to some external system, timer
|
||||||
|
interrupt or user space writing a specific file in sysfs). A
|
||||||
|
trigger may initiate data capture for a number of sensors and
|
||||||
|
also it may be completely unrelated to the sensor itself.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="iiotrigsysfs"> <title> IIO trigger sysfs interface </title>
|
||||||
|
There are two locations in sysfs related to triggers:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><filename>/sys/bus/iio/devices/triggerY</filename>,
|
||||||
|
this file is created once an IIO trigger is registered with
|
||||||
|
the IIO core and corresponds to trigger with index Y. Because
|
||||||
|
triggers can be very different depending on type there are few
|
||||||
|
standard attributes that we can describe here:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<emphasis>name</emphasis>, trigger name that can be later
|
||||||
|
used for association with a device.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<emphasis>sampling_frequency</emphasis>, some timer based
|
||||||
|
triggers use this attribute to specify the frequency for
|
||||||
|
trigger calls.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>/sys/bus/iio/devices/iio:deviceX/trigger/</filename>, this
|
||||||
|
directory is created once the device supports a triggered
|
||||||
|
buffer. We can associate a trigger with our device by writing
|
||||||
|
the trigger's name in the <filename>current_trigger</filename> file.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="iiotrigattr"> <title> IIO trigger setup</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Let's see a simple example of how to setup a trigger to be used
|
||||||
|
by a driver.
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
struct iio_trigger_ops trigger_ops = {
|
||||||
|
.set_trigger_state = sample_trigger_state,
|
||||||
|
.validate_device = sample_validate_device,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iio_trigger *trig;
|
||||||
|
|
||||||
|
/* first, allocate memory for our trigger */
|
||||||
|
trig = iio_trigger_alloc(dev, "trig-%s-%d", name, idx);
|
||||||
|
|
||||||
|
/* setup trigger operations field */
|
||||||
|
trig->ops = &trigger_ops;
|
||||||
|
|
||||||
|
/* now register the trigger with the IIO core */
|
||||||
|
iio_trigger_register(trig);
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="iiotrigsetup"> <title> IIO trigger ops</title>
|
||||||
|
!Finclude/linux/iio/trigger.h iio_trigger_ops
|
||||||
|
<para>
|
||||||
|
Notice that a trigger has a set of operations attached:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<function>set_trigger_state</function>, switch the trigger on/off
|
||||||
|
on demand.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<function>validate_device</function>, function to validate the
|
||||||
|
device when the current trigger gets changed.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
<sect1 id="iiotriggered_buffer">
|
||||||
|
<title> Industrial I/O triggered buffers </title>
|
||||||
|
<para>
|
||||||
|
Now that we know what buffers and triggers are let's see how they
|
||||||
|
work together.
|
||||||
|
</para>
|
||||||
|
<sect2 id="iiotrigbufsetup"> <title> IIO triggered buffer setup</title>
|
||||||
|
!Edrivers/iio/industrialio-triggered-buffer.c
|
||||||
|
!Finclude/linux/iio/iio.h iio_buffer_setup_ops
|
||||||
|
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A typical triggered buffer setup looks like this:
|
||||||
|
<programlisting>
|
||||||
|
const struct iio_buffer_setup_ops sensor_buffer_setup_ops = {
|
||||||
|
.preenable = sensor_buffer_preenable,
|
||||||
|
.postenable = sensor_buffer_postenable,
|
||||||
|
.postdisable = sensor_buffer_postdisable,
|
||||||
|
.predisable = sensor_buffer_predisable,
|
||||||
|
};
|
||||||
|
|
||||||
|
irqreturn_t sensor_iio_pollfunc(int irq, void *p)
|
||||||
|
{
|
||||||
|
pf->timestamp = iio_get_time_ns();
|
||||||
|
return IRQ_WAKE_THREAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
irqreturn_t sensor_trigger_handler(int irq, void *p)
|
||||||
|
{
|
||||||
|
u16 buf[8];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
/* read data for each active channel */
|
||||||
|
for_each_set_bit(bit, active_scan_mask, masklength)
|
||||||
|
buf[i++] = sensor_get_data(bit)
|
||||||
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, buf, timestamp);
|
||||||
|
|
||||||
|
iio_trigger_notify_done(trigger);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup triggered buffer, usually in probe function */
|
||||||
|
iio_triggered_buffer_setup(indio_dev, sensor_iio_polfunc,
|
||||||
|
sensor_trigger_handler,
|
||||||
|
sensor_buffer_setup_ops);
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
The important things to notice here are:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><function> iio_buffer_setup_ops</function>, the buffer setup
|
||||||
|
functions to be called at predefined points in the buffer configuration
|
||||||
|
sequence (e.g. before enable, after disable). If not specified, the
|
||||||
|
IIO core uses the default <type>iio_triggered_buffer_setup_ops</type>.
|
||||||
|
</listitem>
|
||||||
|
<listitem><function>sensor_iio_pollfunc</function>, the function that
|
||||||
|
will be used as top half of poll function. It should do as little
|
||||||
|
processing as possible, because it runs in interrupt context. The most
|
||||||
|
common operation is recording of the current timestamp and for this reason
|
||||||
|
one can use the IIO core defined <function>iio_pollfunc_store_time
|
||||||
|
</function> function.
|
||||||
|
</listitem>
|
||||||
|
<listitem><function>sensor_trigger_handler</function>, the function that
|
||||||
|
will be used as bottom half of the poll function. This runs in the
|
||||||
|
context of a kernel thread and all the processing takes place here.
|
||||||
|
It usually reads data from the device and stores it in the internal
|
||||||
|
buffer together with the timestamp recorded in the top half.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
||||||
|
<chapter id='iioresources'>
|
||||||
|
<title> Resources </title>
|
||||||
|
IIO core may change during time so the best documentation to read is the
|
||||||
|
source code. There are several locations where you should look:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<filename>drivers/iio/</filename>, contains the IIO core plus
|
||||||
|
and directories for each sensor type (e.g. accel, magnetometer,
|
||||||
|
etc.)
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>include/linux/iio/</filename>, contains the header
|
||||||
|
files, nice to read for the internal kernel interfaces.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>include/uapi/linux/iio/</filename>, contains files to be
|
||||||
|
used by user space applications.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>tools/iio/</filename>, contains tools for rapidly
|
||||||
|
testing buffers, events and device creation.
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<filename>drivers/staging/iio/</filename>, contains code for some
|
||||||
|
drivers or experimental features that are not yet mature enough
|
||||||
|
to be moved out.
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
<para>
|
||||||
|
Besides the code, there are some good online documentation sources:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<ulink url="http://marc.info/?l=linux-iio"> Industrial I/O mailing
|
||||||
|
list </ulink>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<ulink url="http://wiki.analog.com/software/linux/docs/iio/iio">
|
||||||
|
Analog Device IIO wiki page </ulink>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<ulink url="https://fosdem.org/2015/schedule/event/iiosdr/">
|
||||||
|
Using the Linux IIO framework for SDR, Lars-Peter Clausen's
|
||||||
|
presentation at FOSDEM </ulink>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
</chapter>
|
||||||
|
</book>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
vim: softtabstop=2:shiftwidth=2:expandtab:textwidth=72
|
||||||
|
-->
|
|
@ -18,6 +18,7 @@ Required properties:
|
||||||
"mcp3202"
|
"mcp3202"
|
||||||
"mcp3204"
|
"mcp3204"
|
||||||
"mcp3208"
|
"mcp3208"
|
||||||
|
"mcp3301"
|
||||||
|
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
|
@ -17,6 +17,11 @@ Recommended properties:
|
||||||
- Frequency in normal mode (ADLPC=0, ADHSC=0)
|
- Frequency in normal mode (ADLPC=0, ADHSC=0)
|
||||||
- Frequency in high-speed mode (ADLPC=0, ADHSC=1)
|
- Frequency in high-speed mode (ADLPC=0, ADHSC=1)
|
||||||
- Frequency in low-power mode (ADLPC=1, ADHSC=0)
|
- Frequency in low-power mode (ADLPC=1, ADHSC=0)
|
||||||
|
- min-sample-time: Minimum sampling time in nanoseconds. This value has
|
||||||
|
to be chosen according to the conversion mode and the connected analog
|
||||||
|
source resistance (R_as) and capacitance (C_as). Refer the datasheet's
|
||||||
|
operating requirements. A safe default across a wide range of R_as and
|
||||||
|
C_as as well as conversion modes is 1000ns.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
adc0: adc@4003b000 {
|
adc0: adc@4003b000 {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
* MEMSIC MMC35240 magnetometer sensor
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible : should be "memsic,mmc35240"
|
||||||
|
- reg : the I2C address of the magnetometer
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mmc35240@30 {
|
||||||
|
compatible = "memsic,mmc35240";
|
||||||
|
reg = <0x30>;
|
||||||
|
};
|
|
@ -35,6 +35,7 @@ Accelerometers:
|
||||||
- st,lsm303dl-accel
|
- st,lsm303dl-accel
|
||||||
- st,lsm303dlm-accel
|
- st,lsm303dlm-accel
|
||||||
- st,lsm330-accel
|
- st,lsm330-accel
|
||||||
|
- st,lsm303agr-accel
|
||||||
|
|
||||||
Gyroscopes:
|
Gyroscopes:
|
||||||
- st,l3g4200d-gyro
|
- st,l3g4200d-gyro
|
||||||
|
@ -46,6 +47,7 @@ Gyroscopes:
|
||||||
- st,lsm330-gyro
|
- st,lsm330-gyro
|
||||||
|
|
||||||
Magnetometers:
|
Magnetometers:
|
||||||
|
- st,lsm303agr-magn
|
||||||
- st,lsm303dlh-magn
|
- st,lsm303dlh-magn
|
||||||
- st,lsm303dlhc-magn
|
- st,lsm303dlhc-magn
|
||||||
- st,lsm303dlm-magn
|
- st,lsm303dlm-magn
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
* Freescale i.MX28 LRADC device driver
|
* Freescale MXS LRADC device driver
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "fsl,imx23-lradc" for i.MX23 SoC and "fsl,imx28-lradc"
|
- compatible: Should be "fsl,imx23-lradc" for i.MX23 SoC and "fsl,imx28-lradc"
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
What is sm712fb?
|
||||||
|
=================
|
||||||
|
|
||||||
|
This is a graphics framebuffer driver for Silicon Motion SM712 based processors.
|
||||||
|
|
||||||
|
How to use it?
|
||||||
|
==============
|
||||||
|
|
||||||
|
Switching modes is done using the video=sm712fb:... boot parameter.
|
||||||
|
|
||||||
|
If you want, for example, enable a resolution of 1280x1024x24bpp you should
|
||||||
|
pass to the kernel this command line: "video=sm712fb:0x31B".
|
||||||
|
|
||||||
|
You should not compile-in vesafb.
|
||||||
|
|
||||||
|
Currently supported video modes are:
|
||||||
|
|
||||||
|
[Graphic modes]
|
||||||
|
|
||||||
|
bpp | 640x480 800x600 1024x768 1280x1024
|
||||||
|
----+--------------------------------------------
|
||||||
|
8 | 0x301 0x303 0x305 0x307
|
||||||
|
16 | 0x311 0x314 0x317 0x31A
|
||||||
|
24 | 0x312 0x315 0x318 0x31B
|
||||||
|
|
||||||
|
Missing Features
|
||||||
|
================
|
||||||
|
(alias TODO list)
|
||||||
|
|
||||||
|
* 2D acceleratrion
|
||||||
|
* dual-head support
|
22
MAINTAINERS
22
MAINTAINERS
|
@ -9348,6 +9348,15 @@ S: Maintained
|
||||||
F: drivers/media/i2c/ov2659.c
|
F: drivers/media/i2c/ov2659.c
|
||||||
F: include/media/ov2659.h
|
F: include/media/ov2659.h
|
||||||
|
|
||||||
|
SILICON MOTION SM712 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/video/fbdev/sm712*
|
||||||
|
F: Documentation/fb/sm712fb.txt
|
||||||
|
|
||||||
SIS 190 ETHERNET DRIVER
|
SIS 190 ETHERNET DRIVER
|
||||||
M: Francois Romieu <romieu@fr.zoreil.com>
|
M: Francois Romieu <romieu@fr.zoreil.com>
|
||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
|
@ -9745,11 +9754,6 @@ W: http://wiki.laptop.org/go/DCON
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/staging/olpc_dcon/
|
F: drivers/staging/olpc_dcon/
|
||||||
|
|
||||||
STAGING - OZMO DEVICES USB OVER WIFI DRIVER
|
|
||||||
M: Shigekatsu Tateno <shigekatsu.tateno@atmel.com>
|
|
||||||
S: Maintained
|
|
||||||
F: drivers/staging/ozwpan/
|
|
||||||
|
|
||||||
STAGING - PARALLEL LCD/KEYPAD PANEL DRIVER
|
STAGING - PARALLEL LCD/KEYPAD PANEL DRIVER
|
||||||
M: Willy Tarreau <willy@meta-x.org>
|
M: Willy Tarreau <willy@meta-x.org>
|
||||||
S: Odd Fixes
|
S: Odd Fixes
|
||||||
|
@ -9768,14 +9772,6 @@ L: linux-wireless@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/staging/rtl8723au/
|
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 - SILICON MOTION SM750 FRAME BUFFER DRIVER
|
STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
|
||||||
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
|
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
|
||||||
M: Teddy Wang <teddy.wang@siliconmotion.com>
|
M: Teddy Wang <teddy.wang@siliconmotion.com>
|
||||||
|
|
|
@ -86,18 +86,6 @@ config KXSD9
|
||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called kxsd9.
|
will be called kxsd9.
|
||||||
|
|
||||||
config MMA8452
|
|
||||||
tristate "Freescale MMA8452Q Accelerometer Driver"
|
|
||||||
depends on I2C
|
|
||||||
select IIO_BUFFER
|
|
||||||
select IIO_TRIGGERED_BUFFER
|
|
||||||
help
|
|
||||||
Say yes here to build support for the Freescale MMA8452Q 3-axis
|
|
||||||
accelerometer.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module
|
|
||||||
will be called mma8452.
|
|
||||||
|
|
||||||
config KXCJK1013
|
config KXCJK1013
|
||||||
tristate "Kionix 3-Axis Accelerometer Driver"
|
tristate "Kionix 3-Axis Accelerometer Driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -111,6 +99,18 @@ config KXCJK1013
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called kxcjk-1013.
|
be called kxcjk-1013.
|
||||||
|
|
||||||
|
config MMA8452
|
||||||
|
tristate "Freescale MMA8452Q Accelerometer Driver"
|
||||||
|
depends on I2C
|
||||||
|
select IIO_BUFFER
|
||||||
|
select IIO_TRIGGERED_BUFFER
|
||||||
|
help
|
||||||
|
Say yes here to build support for the Freescale MMA8452Q 3-axis
|
||||||
|
accelerometer.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called mma8452.
|
||||||
|
|
||||||
config MMA9551_CORE
|
config MMA9551_CORE
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
@ -140,6 +140,8 @@ config MMA9553
|
||||||
config STK8312
|
config STK8312
|
||||||
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
|
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
select IIO_BUFFER
|
||||||
|
select IIO_TRIGGERED_BUFFER
|
||||||
help
|
help
|
||||||
Say yes here to get support for the Sensortek STK8312 3-axis
|
Say yes here to get support for the Sensortek STK8312 3-axis
|
||||||
accelerometer.
|
accelerometer.
|
||||||
|
|
|
@ -846,7 +846,6 @@ MODULE_DEVICE_TABLE(i2c, bma180_ids);
|
||||||
static struct i2c_driver bma180_driver = {
|
static struct i2c_driver bma180_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "bma180",
|
.name = "bma180",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = BMA180_PM_OPS,
|
.pm = BMA180_PM_OPS,
|
||||||
},
|
},
|
||||||
.probe = bma180_probe,
|
.probe = bma180_probe,
|
||||||
|
|
|
@ -151,6 +151,7 @@ struct bmc150_scale_info {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bmc150_accel_chip_info {
|
struct bmc150_accel_chip_info {
|
||||||
|
const char *name;
|
||||||
u8 chip_id;
|
u8 chip_id;
|
||||||
const struct iio_chan_spec *channels;
|
const struct iio_chan_spec *channels;
|
||||||
int num_channels;
|
int num_channels;
|
||||||
|
@ -241,7 +242,6 @@ static const struct {
|
||||||
{500000, BMC150_ACCEL_SLEEP_500_MS},
|
{500000, BMC150_ACCEL_SLEEP_500_MS},
|
||||||
{1000000, BMC150_ACCEL_SLEEP_1_SEC} };
|
{1000000, BMC150_ACCEL_SLEEP_1_SEC} };
|
||||||
|
|
||||||
|
|
||||||
static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
|
static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
|
||||||
enum bmc150_power_modes mode,
|
enum bmc150_power_modes mode,
|
||||||
int dur_us)
|
int dur_us)
|
||||||
|
@ -259,8 +259,9 @@ static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
|
||||||
dur_val =
|
dur_val =
|
||||||
bmc150_accel_sleep_value_table[i].reg_value;
|
bmc150_accel_sleep_value_table[i].reg_value;
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
dur_val = 0;
|
dur_val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (dur_val < 0)
|
if (dur_val < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -288,7 +289,7 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(bmc150_accel_samp_freq_table); ++i) {
|
for (i = 0; i < ARRAY_SIZE(bmc150_accel_samp_freq_table); ++i) {
|
||||||
if (bmc150_accel_samp_freq_table[i].val == val &&
|
if (bmc150_accel_samp_freq_table[i].val == val &&
|
||||||
bmc150_accel_samp_freq_table[i].val2 == val2) {
|
bmc150_accel_samp_freq_table[i].val2 == val2) {
|
||||||
ret = i2c_smbus_write_byte_data(
|
ret = i2c_smbus_write_byte_data(
|
||||||
data->client,
|
data->client,
|
||||||
BMC150_ACCEL_REG_PMU_BW,
|
BMC150_ACCEL_REG_PMU_BW,
|
||||||
|
@ -345,65 +346,6 @@ static int bmc150_accel_any_motion_setup(struct bmc150_accel_trigger *t,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_CHIP_ID);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"Error: Reading chip id\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(&data->client->dev, "Chip Id %x\n", ret);
|
|
||||||
if (ret != data->chip_info->chip_id) {
|
|
||||||
dev_err(&data->client->dev, "Invalid chip %x\n", ret);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Set Bandwidth */
|
|
||||||
ret = bmc150_accel_set_bw(data, BMC150_ACCEL_DEF_BW, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Set Default Range */
|
|
||||||
ret = i2c_smbus_write_byte_data(data->client,
|
|
||||||
BMC150_ACCEL_REG_PMU_RANGE,
|
|
||||||
BMC150_ACCEL_DEF_RANGE_4G);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"Error writing reg_pmu_range\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->range = BMC150_ACCEL_DEF_RANGE_4G;
|
|
||||||
|
|
||||||
/* Set default slope duration and thresholds */
|
|
||||||
data->slope_thres = BMC150_ACCEL_DEF_SLOPE_THRESHOLD;
|
|
||||||
data->slope_dur = BMC150_ACCEL_DEF_SLOPE_DURATION;
|
|
||||||
ret = bmc150_accel_update_slope(data);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Set default as latched interrupts */
|
|
||||||
ret = i2c_smbus_write_byte_data(data->client,
|
|
||||||
BMC150_ACCEL_REG_INT_RST_LATCH,
|
|
||||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
|
||||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"Error writing reg_int_rst_latch\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bmc150_accel_get_bw(struct bmc150_accel_data *data, int *val,
|
static int bmc150_accel_get_bw(struct bmc150_accel_data *data, int *val,
|
||||||
int *val2)
|
int *val2)
|
||||||
{
|
{
|
||||||
|
@ -437,12 +379,13 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (on)
|
if (on) {
|
||||||
ret = pm_runtime_get_sync(&data->client->dev);
|
ret = pm_runtime_get_sync(&data->client->dev);
|
||||||
else {
|
} else {
|
||||||
pm_runtime_mark_last_busy(&data->client->dev);
|
pm_runtime_mark_last_busy(&data->client->dev);
|
||||||
ret = pm_runtime_put_autosuspend(&data->client->dev);
|
ret = pm_runtime_put_autosuspend(&data->client->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&data->client->dev,
|
dev_err(&data->client->dev,
|
||||||
"Failed: bmc150_accel_set_power_state for %d\n", on);
|
"Failed: bmc150_accel_set_power_state for %d\n", on);
|
||||||
|
@ -514,13 +457,13 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We will expect the enable and disable to do operation in
|
* We will expect the enable and disable to do operation in reverse
|
||||||
* in reverse order. This will happen here anyway as our
|
* order. This will happen here anyway, as our resume operation uses
|
||||||
* resume operation uses sync mode runtime pm calls, the
|
* sync mode runtime pm calls. The suspend operation will be delayed
|
||||||
* suspend operation will be delayed by autosuspend delay
|
* by autosuspend delay.
|
||||||
* So the disable operation will still happen in reverse of
|
* So the disable operation will still happen in reverse order of
|
||||||
* enable operation. When runtime pm is disabled the mode
|
* enable operation. When runtime pm is disabled the mode is always on,
|
||||||
* is always on so sequence doesn't matter
|
* so sequence doesn't matter.
|
||||||
*/
|
*/
|
||||||
ret = bmc150_accel_set_power_state(data, state);
|
ret = bmc150_accel_set_power_state(data, state);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -574,7 +517,6 @@ out_fix_power_state:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
|
static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
@ -674,8 +616,9 @@ static int bmc150_accel_read_raw(struct iio_dev *indio_dev,
|
||||||
if (chan->type == IIO_TEMP) {
|
if (chan->type == IIO_TEMP) {
|
||||||
*val = BMC150_ACCEL_TEMP_CENTER_VAL;
|
*val = BMC150_ACCEL_TEMP_CENTER_VAL;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
} else
|
} else {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
*val = 0;
|
*val = 0;
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
|
@ -776,7 +719,7 @@ static int bmc150_accel_write_event(struct iio_dev *indio_dev,
|
||||||
|
|
||||||
switch (info) {
|
switch (info) {
|
||||||
case IIO_EV_INFO_VALUE:
|
case IIO_EV_INFO_VALUE:
|
||||||
data->slope_thres = val & 0xFF;
|
data->slope_thres = val & BMC150_ACCEL_SLOPE_THRES_MASK;
|
||||||
break;
|
break;
|
||||||
case IIO_EV_INFO_PERIOD:
|
case IIO_EV_INFO_PERIOD:
|
||||||
data->slope_dur = val & BMC150_ACCEL_SLOPE_DUR_MASK;
|
data->slope_dur = val & BMC150_ACCEL_SLOPE_DUR_MASK;
|
||||||
|
@ -793,7 +736,6 @@ static int bmc150_accel_read_event_config(struct iio_dev *indio_dev,
|
||||||
enum iio_event_type type,
|
enum iio_event_type type,
|
||||||
enum iio_event_direction dir)
|
enum iio_event_direction dir)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
return data->ev_enable_state;
|
return data->ev_enable_state;
|
||||||
|
@ -827,7 +769,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev,
|
static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev,
|
||||||
struct iio_trigger *trig)
|
struct iio_trigger *trig)
|
||||||
{
|
{
|
||||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||||
int i;
|
int i;
|
||||||
|
@ -963,6 +905,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
|
||||||
u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3];
|
u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3];
|
||||||
int64_t tstamp;
|
int64_t tstamp;
|
||||||
uint64_t sample_period;
|
uint64_t sample_period;
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(data->client,
|
ret = i2c_smbus_read_byte_data(data->client,
|
||||||
BMC150_ACCEL_REG_FIFO_STATUS);
|
BMC150_ACCEL_REG_FIFO_STATUS);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -1120,6 +1063,7 @@ enum {
|
||||||
|
|
||||||
static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
||||||
[bmc150] = {
|
[bmc150] = {
|
||||||
|
.name = "BMC150A",
|
||||||
.chip_id = 0xFA,
|
.chip_id = 0xFA,
|
||||||
.channels = bmc150_accel_channels,
|
.channels = bmc150_accel_channels,
|
||||||
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
|
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
|
||||||
|
@ -1129,6 +1073,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
||||||
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
|
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
|
||||||
},
|
},
|
||||||
[bmi055] = {
|
[bmi055] = {
|
||||||
|
.name = "BMI055A",
|
||||||
.chip_id = 0xFA,
|
.chip_id = 0xFA,
|
||||||
.channels = bmc150_accel_channels,
|
.channels = bmc150_accel_channels,
|
||||||
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
|
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
|
||||||
|
@ -1138,6 +1083,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
||||||
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
|
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
|
||||||
},
|
},
|
||||||
[bma255] = {
|
[bma255] = {
|
||||||
|
.name = "BMA0255",
|
||||||
.chip_id = 0xFA,
|
.chip_id = 0xFA,
|
||||||
.channels = bmc150_accel_channels,
|
.channels = bmc150_accel_channels,
|
||||||
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
|
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
|
||||||
|
@ -1147,6 +1093,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
||||||
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
|
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
|
||||||
},
|
},
|
||||||
[bma250e] = {
|
[bma250e] = {
|
||||||
|
.name = "BMA250E",
|
||||||
.chip_id = 0xF9,
|
.chip_id = 0xF9,
|
||||||
.channels = bma250e_accel_channels,
|
.channels = bma250e_accel_channels,
|
||||||
.num_channels = ARRAY_SIZE(bma250e_accel_channels),
|
.num_channels = ARRAY_SIZE(bma250e_accel_channels),
|
||||||
|
@ -1156,6 +1103,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
||||||
{306457, BMC150_ACCEL_DEF_RANGE_16G} },
|
{306457, BMC150_ACCEL_DEF_RANGE_16G} },
|
||||||
},
|
},
|
||||||
[bma222e] = {
|
[bma222e] = {
|
||||||
|
.name = "BMA222E",
|
||||||
.chip_id = 0xF8,
|
.chip_id = 0xF8,
|
||||||
.channels = bma222e_accel_channels,
|
.channels = bma222e_accel_channels,
|
||||||
.num_channels = ARRAY_SIZE(bma222e_accel_channels),
|
.num_channels = ARRAY_SIZE(bma222e_accel_channels),
|
||||||
|
@ -1165,6 +1113,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
||||||
{1225831, BMC150_ACCEL_DEF_RANGE_16G} },
|
{1225831, BMC150_ACCEL_DEF_RANGE_16G} },
|
||||||
},
|
},
|
||||||
[bma280] = {
|
[bma280] = {
|
||||||
|
.name = "BMA0280",
|
||||||
.chip_id = 0xFB,
|
.chip_id = 0xFB,
|
||||||
.channels = bma280_accel_channels,
|
.channels = bma280_accel_channels,
|
||||||
.num_channels = ARRAY_SIZE(bma280_accel_channels),
|
.num_channels = ARRAY_SIZE(bma280_accel_channels),
|
||||||
|
@ -1255,7 +1204,7 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bmc150_accel_trigger_set_state(struct iio_trigger *trig,
|
static int bmc150_accel_trigger_set_state(struct iio_trigger *trig,
|
||||||
bool state)
|
bool state)
|
||||||
{
|
{
|
||||||
struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig);
|
struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig);
|
||||||
struct bmc150_accel_data *data = t->data;
|
struct bmc150_accel_data *data = t->data;
|
||||||
|
@ -1314,26 +1263,32 @@ static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev)
|
||||||
dir = IIO_EV_DIR_RISING;
|
dir = IIO_EV_DIR_RISING;
|
||||||
|
|
||||||
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X)
|
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X)
|
||||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
iio_push_event(indio_dev,
|
||||||
0,
|
IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
||||||
IIO_MOD_X,
|
0,
|
||||||
IIO_EV_TYPE_ROC,
|
IIO_MOD_X,
|
||||||
dir),
|
IIO_EV_TYPE_ROC,
|
||||||
data->timestamp);
|
dir),
|
||||||
|
data->timestamp);
|
||||||
|
|
||||||
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y)
|
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y)
|
||||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
iio_push_event(indio_dev,
|
||||||
0,
|
IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
||||||
IIO_MOD_Y,
|
0,
|
||||||
IIO_EV_TYPE_ROC,
|
IIO_MOD_Y,
|
||||||
dir),
|
IIO_EV_TYPE_ROC,
|
||||||
data->timestamp);
|
dir),
|
||||||
|
data->timestamp);
|
||||||
|
|
||||||
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z)
|
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z)
|
||||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
iio_push_event(indio_dev,
|
||||||
0,
|
IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
||||||
IIO_MOD_Z,
|
0,
|
||||||
IIO_EV_TYPE_ROC,
|
IIO_MOD_Z,
|
||||||
dir),
|
IIO_EV_TYPE_ROC,
|
||||||
data->timestamp);
|
dir),
|
||||||
|
data->timestamp);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1365,7 +1320,9 @@ static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private)
|
||||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
|
dev_err(&data->client->dev,
|
||||||
|
"Error writing reg_int_rst_latch\n");
|
||||||
|
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
} else {
|
} else {
|
||||||
ret = IRQ_NONE;
|
ret = IRQ_NONE;
|
||||||
|
@ -1403,22 +1360,8 @@ static irqreturn_t bmc150_accel_irq_handler(int irq, void *private)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *bmc150_accel_match_acpi_device(struct device *dev, int *data)
|
|
||||||
{
|
|
||||||
const struct acpi_device_id *id;
|
|
||||||
|
|
||||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
|
||||||
|
|
||||||
if (!id)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
*data = (int) id->driver_data;
|
|
||||||
|
|
||||||
return dev_name(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bmc150_accel_gpio_probe(struct i2c_client *client,
|
static int bmc150_accel_gpio_probe(struct i2c_client *client,
|
||||||
struct bmc150_accel_data *data)
|
struct bmc150_accel_data *data)
|
||||||
{
|
{
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct gpio_desc *gpio;
|
struct gpio_desc *gpio;
|
||||||
|
@ -1611,6 +1554,70 @@ static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = {
|
||||||
.postdisable = bmc150_accel_buffer_postdisable,
|
.postdisable = bmc150_accel_buffer_postdisable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_CHIP_ID);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&data->client->dev, "Error: Reading chip id\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&data->client->dev, "Chip Id %x\n", ret);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(bmc150_accel_chip_info_tbl); i++) {
|
||||||
|
if (bmc150_accel_chip_info_tbl[i].chip_id == ret) {
|
||||||
|
data->chip_info = &bmc150_accel_chip_info_tbl[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data->chip_info) {
|
||||||
|
dev_err(&data->client->dev, "Unsupported chip %x\n", ret);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Set Bandwidth */
|
||||||
|
ret = bmc150_accel_set_bw(data, BMC150_ACCEL_DEF_BW, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Set Default Range */
|
||||||
|
ret = i2c_smbus_write_byte_data(data->client,
|
||||||
|
BMC150_ACCEL_REG_PMU_RANGE,
|
||||||
|
BMC150_ACCEL_DEF_RANGE_4G);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&data->client->dev, "Error writing reg_pmu_range\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->range = BMC150_ACCEL_DEF_RANGE_4G;
|
||||||
|
|
||||||
|
/* Set default slope duration and thresholds */
|
||||||
|
data->slope_thres = BMC150_ACCEL_DEF_SLOPE_THRESHOLD;
|
||||||
|
data->slope_dur = BMC150_ACCEL_DEF_SLOPE_DURATION;
|
||||||
|
ret = bmc150_accel_update_slope(data);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Set default as latched interrupts */
|
||||||
|
ret = i2c_smbus_write_byte_data(data->client,
|
||||||
|
BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||||
|
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||||
|
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&data->client->dev,
|
||||||
|
"Error writing reg_int_rst_latch\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int bmc150_accel_probe(struct i2c_client *client,
|
static int bmc150_accel_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -1618,7 +1625,6 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
int ret;
|
int ret;
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
int chip_id = 0;
|
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
|
@ -1628,15 +1634,8 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||||
i2c_set_clientdata(client, indio_dev);
|
i2c_set_clientdata(client, indio_dev);
|
||||||
data->client = client;
|
data->client = client;
|
||||||
|
|
||||||
if (id) {
|
if (id)
|
||||||
name = id->name;
|
name = id->name;
|
||||||
chip_id = id->driver_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ACPI_HANDLE(&client->dev))
|
|
||||||
name = bmc150_accel_match_acpi_device(&client->dev, &chip_id);
|
|
||||||
|
|
||||||
data->chip_info = &bmc150_accel_chip_info_tbl[chip_id];
|
|
||||||
|
|
||||||
ret = bmc150_accel_chip_init(data);
|
ret = bmc150_accel_chip_init(data);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -1647,7 +1646,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||||
indio_dev->dev.parent = &client->dev;
|
indio_dev->dev.parent = &client->dev;
|
||||||
indio_dev->channels = data->chip_info->channels;
|
indio_dev->channels = data->chip_info->channels;
|
||||||
indio_dev->num_channels = data->chip_info->num_channels;
|
indio_dev->num_channels = data->chip_info->num_channels;
|
||||||
indio_dev->name = name;
|
indio_dev->name = name ? name : data->chip_info->name;
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
indio_dev->info = &bmc150_accel_info;
|
indio_dev->info = &bmc150_accel_info;
|
||||||
|
|
||||||
|
@ -1663,7 +1662,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||||
if (client->irq < 0)
|
if (client->irq < 0)
|
||||||
client->irq = bmc150_accel_gpio_probe(client, data);
|
client->irq = bmc150_accel_gpio_probe(client, data);
|
||||||
|
|
||||||
if (client->irq >= 0) {
|
if (client->irq > 0) {
|
||||||
ret = devm_request_threaded_irq(
|
ret = devm_request_threaded_irq(
|
||||||
&client->dev, client->irq,
|
&client->dev, client->irq,
|
||||||
bmc150_accel_irq_handler,
|
bmc150_accel_irq_handler,
|
||||||
|
|
|
@ -658,10 +658,8 @@ static int kxcjk1013_set_scale(struct kxcjk1013_data *data, int val)
|
||||||
int ret, i;
|
int ret, i;
|
||||||
enum kxcjk1013_mode store_mode;
|
enum kxcjk1013_mode store_mode;
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(KXCJK1013_scale_table); ++i) {
|
for (i = 0; i < ARRAY_SIZE(KXCJK1013_scale_table); ++i) {
|
||||||
if (KXCJK1013_scale_table[i].scale == val) {
|
if (KXCJK1013_scale_table[i].scale == val) {
|
||||||
|
|
||||||
ret = kxcjk1013_get_mode(data, &store_mode);
|
ret = kxcjk1013_get_mode(data, &store_mode);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -820,7 +818,6 @@ static int kxcjk1013_read_event_config(struct iio_dev *indio_dev,
|
||||||
enum iio_event_type type,
|
enum iio_event_type type,
|
||||||
enum iio_event_direction dir)
|
enum iio_event_direction dir)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
return data->ev_enable_state;
|
return data->ev_enable_state;
|
||||||
|
@ -1243,7 +1240,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
||||||
if (client->irq < 0)
|
if (client->irq < 0)
|
||||||
client->irq = kxcjk1013_gpio_probe(client, data);
|
client->irq = kxcjk1013_gpio_probe(client, data);
|
||||||
|
|
||||||
if (client->irq >= 0) {
|
if (client->irq > 0) {
|
||||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
kxcjk1013_data_rdy_trig_poll,
|
kxcjk1013_data_rdy_trig_poll,
|
||||||
kxcjk1013_event_handler,
|
kxcjk1013_event_handler,
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
#include <linux/iio/trigger_consumer.h>
|
|
||||||
#include <linux/iio/buffer.h>
|
#include <linux/iio/buffer.h>
|
||||||
#include <linux/iio/trigger.h>
|
#include <linux/iio/trigger.h>
|
||||||
#include <linux/iio/trigger_consumer.h>
|
#include <linux/iio/trigger_consumer.h>
|
||||||
|
@ -24,54 +23,51 @@
|
||||||
#include <linux/iio/events.h>
|
#include <linux/iio/events.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
|
||||||
#define MMA8452_STATUS 0x00
|
#define MMA8452_STATUS 0x00
|
||||||
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
|
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||||
#define MMA8452_OUT_Y 0x03
|
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
|
||||||
#define MMA8452_OUT_Z 0x05
|
#define MMA8452_OUT_Y 0x03
|
||||||
#define MMA8452_INT_SRC 0x0c
|
#define MMA8452_OUT_Z 0x05
|
||||||
#define MMA8452_WHO_AM_I 0x0d
|
#define MMA8452_INT_SRC 0x0c
|
||||||
#define MMA8452_DATA_CFG 0x0e
|
#define MMA8452_WHO_AM_I 0x0d
|
||||||
#define MMA8452_HP_FILTER_CUTOFF 0x0f
|
#define MMA8452_DATA_CFG 0x0e
|
||||||
#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK (BIT(0) | BIT(1))
|
#define MMA8452_DATA_CFG_FS_MASK GENMASK(1, 0)
|
||||||
#define MMA8452_TRANSIENT_CFG 0x1d
|
#define MMA8452_DATA_CFG_FS_2G 0
|
||||||
#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
|
#define MMA8452_DATA_CFG_FS_4G 1
|
||||||
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
|
#define MMA8452_DATA_CFG_FS_8G 2
|
||||||
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
|
#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
|
||||||
#define MMA8452_TRANSIENT_SRC 0x1e
|
#define MMA8452_HP_FILTER_CUTOFF 0x0f
|
||||||
#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
|
#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK GENMASK(1, 0)
|
||||||
#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3)
|
#define MMA8452_TRANSIENT_CFG 0x1d
|
||||||
#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5)
|
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
|
||||||
#define MMA8452_TRANSIENT_THS 0x1f
|
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
|
||||||
#define MMA8452_TRANSIENT_THS_MASK 0x7f
|
#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
|
||||||
#define MMA8452_TRANSIENT_COUNT 0x20
|
#define MMA8452_TRANSIENT_SRC 0x1e
|
||||||
#define MMA8452_OFF_X 0x2f
|
#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
|
||||||
#define MMA8452_OFF_Y 0x30
|
#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3)
|
||||||
#define MMA8452_OFF_Z 0x31
|
#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5)
|
||||||
#define MMA8452_CTRL_REG1 0x2a
|
#define MMA8452_TRANSIENT_THS 0x1f
|
||||||
#define MMA8452_CTRL_REG2 0x2b
|
#define MMA8452_TRANSIENT_THS_MASK GENMASK(6, 0)
|
||||||
#define MMA8452_CTRL_REG2_RST BIT(6)
|
#define MMA8452_TRANSIENT_COUNT 0x20
|
||||||
#define MMA8452_CTRL_REG4 0x2d
|
#define MMA8452_CTRL_REG1 0x2a
|
||||||
#define MMA8452_CTRL_REG5 0x2e
|
#define MMA8452_CTRL_ACTIVE BIT(0)
|
||||||
|
#define MMA8452_CTRL_DR_MASK GENMASK(5, 3)
|
||||||
|
#define MMA8452_CTRL_DR_SHIFT 3
|
||||||
|
#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */
|
||||||
|
#define MMA8452_CTRL_REG2 0x2b
|
||||||
|
#define MMA8452_CTRL_REG2_RST BIT(6)
|
||||||
|
#define MMA8452_CTRL_REG4 0x2d
|
||||||
|
#define MMA8452_CTRL_REG5 0x2e
|
||||||
|
#define MMA8452_OFF_X 0x2f
|
||||||
|
#define MMA8452_OFF_Y 0x30
|
||||||
|
#define MMA8452_OFF_Z 0x31
|
||||||
|
|
||||||
#define MMA8452_MAX_REG 0x31
|
#define MMA8452_MAX_REG 0x31
|
||||||
|
|
||||||
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
#define MMA8452_INT_DRDY BIT(0)
|
||||||
|
#define MMA8452_INT_TRANS BIT(5)
|
||||||
|
|
||||||
#define MMA8452_CTRL_DR_MASK (BIT(5) | BIT(4) | BIT(3))
|
#define MMA8452_DEVICE_ID 0x2a
|
||||||
#define MMA8452_CTRL_DR_SHIFT 3
|
|
||||||
#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */
|
|
||||||
#define MMA8452_CTRL_ACTIVE BIT(0)
|
|
||||||
|
|
||||||
#define MMA8452_DATA_CFG_FS_MASK (BIT(1) | BIT(0))
|
|
||||||
#define MMA8452_DATA_CFG_FS_2G 0
|
|
||||||
#define MMA8452_DATA_CFG_FS_4G 1
|
|
||||||
#define MMA8452_DATA_CFG_FS_8G 2
|
|
||||||
#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
|
|
||||||
|
|
||||||
#define MMA8452_INT_DRDY BIT(0)
|
|
||||||
#define MMA8452_INT_TRANS BIT(5)
|
|
||||||
|
|
||||||
#define MMA8452_DEVICE_ID 0x2a
|
|
||||||
|
|
||||||
struct mma8452_data {
|
struct mma8452_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
@ -91,30 +87,34 @@ static int mma8452_drdy(struct mma8452_data *data)
|
||||||
return ret;
|
return ret;
|
||||||
if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
|
if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
msleep(20);
|
msleep(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_err(&data->client->dev, "data not ready\n");
|
dev_err(&data->client->dev, "data not ready\n");
|
||||||
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
|
static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
|
||||||
{
|
{
|
||||||
int ret = mma8452_drdy(data);
|
int ret = mma8452_drdy(data);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
return i2c_smbus_read_i2c_block_data(data->client,
|
|
||||||
MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
|
return i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
|
||||||
|
3 * sizeof(__be16), (u8 *)buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t mma8452_show_int_plus_micros(char *buf,
|
static ssize_t mma8452_show_int_plus_micros(char *buf, const int (*vals)[2],
|
||||||
const int (*vals)[2], int n)
|
int n)
|
||||||
{
|
{
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
||||||
while (n-- > 0)
|
while (n-- > 0)
|
||||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
|
||||||
"%d.%06d ", vals[n][0], vals[n][1]);
|
vals[n][0], vals[n][1]);
|
||||||
|
|
||||||
/* replace trailing space by newline */
|
/* replace trailing space by newline */
|
||||||
buf[len - 1] = '\n';
|
buf[len - 1] = '\n';
|
||||||
|
@ -123,7 +123,7 @@ static ssize_t mma8452_show_int_plus_micros(char *buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
|
static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
|
||||||
int val, int val2)
|
int val, int val2)
|
||||||
{
|
{
|
||||||
while (n-- > 0)
|
while (n-- > 0)
|
||||||
if (val == vals[n][0] && val2 == vals[n][1])
|
if (val == vals[n][0] && val2 == vals[n][1])
|
||||||
|
@ -147,7 +147,7 @@ static const int mma8452_samp_freq[8][2] = {
|
||||||
* Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
|
* Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
|
||||||
* The userspace interface uses m/s^2 and we declare micro units
|
* The userspace interface uses m/s^2 and we declare micro units
|
||||||
* So scale factor is given by:
|
* So scale factor is given by:
|
||||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
|
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g = 9.80665
|
||||||
*/
|
*/
|
||||||
static const int mma8452_scales[3][2] = {
|
static const int mma8452_scales[3][2] = {
|
||||||
{0, 9577}, {0, 19154}, {0, 38307}
|
{0, 9577}, {0, 19154}, {0, 38307}
|
||||||
|
@ -178,17 +178,19 @@ static const int mma8452_hp_filter_cutoff[8][4][2] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
|
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
{
|
{
|
||||||
return mma8452_show_int_plus_micros(buf, mma8452_samp_freq,
|
return mma8452_show_int_plus_micros(buf, mma8452_samp_freq,
|
||||||
ARRAY_SIZE(mma8452_samp_freq));
|
ARRAY_SIZE(mma8452_samp_freq));
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t mma8452_show_scale_avail(struct device *dev,
|
static ssize_t mma8452_show_scale_avail(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
{
|
{
|
||||||
return mma8452_show_int_plus_micros(buf, mma8452_scales,
|
return mma8452_show_int_plus_micros(buf, mma8452_scales,
|
||||||
ARRAY_SIZE(mma8452_scales));
|
ARRAY_SIZE(mma8452_scales));
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
|
static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
|
||||||
|
@ -205,22 +207,23 @@ static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
|
||||||
|
|
||||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
|
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
|
||||||
static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
|
static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
|
||||||
mma8452_show_scale_avail, NULL, 0);
|
mma8452_show_scale_avail, NULL, 0);
|
||||||
static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
|
static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
|
||||||
S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0);
|
S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0);
|
||||||
|
|
||||||
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
|
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
|
||||||
int val, int val2)
|
int val, int val2)
|
||||||
{
|
{
|
||||||
return mma8452_get_int_plus_micros_index(mma8452_samp_freq,
|
return mma8452_get_int_plus_micros_index(mma8452_samp_freq,
|
||||||
ARRAY_SIZE(mma8452_samp_freq), val, val2);
|
ARRAY_SIZE(mma8452_samp_freq),
|
||||||
|
val, val2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_get_scale_index(struct mma8452_data *data,
|
static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
|
||||||
int val, int val2)
|
|
||||||
{
|
{
|
||||||
return mma8452_get_int_plus_micros_index(mma8452_scales,
|
return mma8452_get_int_plus_micros_index(mma8452_scales,
|
||||||
ARRAY_SIZE(mma8452_scales), val, val2);
|
ARRAY_SIZE(mma8452_scales),
|
||||||
|
val, val2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
|
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
|
||||||
|
@ -229,7 +232,7 @@ static int mma8452_get_hp_filter_index(struct mma8452_data *data,
|
||||||
int i = mma8452_get_odr_index(data);
|
int i = mma8452_get_odr_index(data);
|
||||||
|
|
||||||
return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i],
|
return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i],
|
||||||
ARRAY_SIZE(mma8452_scales[0]), val, val2);
|
ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
|
static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
|
||||||
|
@ -266,25 +269,31 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
||||||
mutex_unlock(&data->lock);
|
mutex_unlock(&data->lock);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = sign_extend32(
|
|
||||||
be16_to_cpu(buffer[chan->scan_index]) >> 4, 11);
|
*val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]) >> 4,
|
||||||
|
11);
|
||||||
|
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
|
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
|
||||||
*val = mma8452_scales[i][0];
|
*val = mma8452_scales[i][0];
|
||||||
*val2 = mma8452_scales[i][1];
|
*val2 = mma8452_scales[i][1];
|
||||||
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
i = mma8452_get_odr_index(data);
|
i = mma8452_get_odr_index(data);
|
||||||
*val = mma8452_samp_freq[i][0];
|
*val = mma8452_samp_freq[i][0];
|
||||||
*val2 = mma8452_samp_freq[i][1];
|
*val2 = mma8452_samp_freq[i][1];
|
||||||
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
case IIO_CHAN_INFO_CALIBBIAS:
|
case IIO_CHAN_INFO_CALIBBIAS:
|
||||||
ret = i2c_smbus_read_byte_data(data->client, MMA8452_OFF_X +
|
ret = i2c_smbus_read_byte_data(data->client,
|
||||||
chan->scan_index);
|
MMA8452_OFF_X + chan->scan_index);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*val = sign_extend32(ret, 7);
|
*val = sign_extend32(ret, 7);
|
||||||
|
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||||
if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
|
if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
|
||||||
|
@ -295,21 +304,23 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
||||||
*val = 0;
|
*val = 0;
|
||||||
*val2 = 0;
|
*val2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_standby(struct mma8452_data *data)
|
static int mma8452_standby(struct mma8452_data *data)
|
||||||
{
|
{
|
||||||
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
|
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
|
||||||
data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
|
data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_active(struct mma8452_data *data)
|
static int mma8452_active(struct mma8452_data *data)
|
||||||
{
|
{
|
||||||
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
|
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
|
||||||
data->ctrl_reg1);
|
data->ctrl_reg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
||||||
|
@ -334,6 +345,7 @@ static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
mutex_unlock(&data->lock);
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,12 +356,13 @@ static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
|
||||||
|
|
||||||
i = mma8452_get_hp_filter_index(data, val, val2);
|
i = mma8452_get_hp_filter_index(data, val, val2);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return -EINVAL;
|
return i;
|
||||||
|
|
||||||
reg = i2c_smbus_read_byte_data(data->client,
|
reg = i2c_smbus_read_byte_data(data->client,
|
||||||
MMA8452_HP_FILTER_CUTOFF);
|
MMA8452_HP_FILTER_CUTOFF);
|
||||||
if (reg < 0)
|
if (reg < 0)
|
||||||
return reg;
|
return reg;
|
||||||
|
|
||||||
reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
|
reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
|
||||||
reg |= i;
|
reg |= i;
|
||||||
|
|
||||||
|
@ -370,25 +383,30 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
|
||||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
i = mma8452_get_samp_freq_index(data, val, val2);
|
i = mma8452_get_samp_freq_index(data, val, val2);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return -EINVAL;
|
return i;
|
||||||
|
|
||||||
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
|
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
|
||||||
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
|
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
|
||||||
|
|
||||||
return mma8452_change_config(data, MMA8452_CTRL_REG1,
|
return mma8452_change_config(data, MMA8452_CTRL_REG1,
|
||||||
data->ctrl_reg1);
|
data->ctrl_reg1);
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
i = mma8452_get_scale_index(data, val, val2);
|
i = mma8452_get_scale_index(data, val, val2);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return -EINVAL;
|
return i;
|
||||||
|
|
||||||
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
|
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
|
||||||
data->data_cfg |= i;
|
data->data_cfg |= i;
|
||||||
|
|
||||||
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||||
data->data_cfg);
|
data->data_cfg);
|
||||||
case IIO_CHAN_INFO_CALIBBIAS:
|
case IIO_CHAN_INFO_CALIBBIAS:
|
||||||
if (val < -128 || val > 127)
|
if (val < -128 || val > 127)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
return mma8452_change_config(data, MMA8452_OFF_X +
|
|
||||||
chan->scan_index, val);
|
return mma8452_change_config(data,
|
||||||
|
MMA8452_OFF_X + chan->scan_index,
|
||||||
|
val);
|
||||||
|
|
||||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||||
if (val == 0 && val2 == 0) {
|
if (val == 0 && val2 == 0) {
|
||||||
|
@ -399,8 +417,9 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||||
data->data_cfg);
|
data->data_cfg);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -425,6 +444,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*val = ret & MMA8452_TRANSIENT_THS_MASK;
|
*val = ret & MMA8452_TRANSIENT_THS_MASK;
|
||||||
|
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
|
|
||||||
case IIO_EV_INFO_PERIOD:
|
case IIO_EV_INFO_PERIOD:
|
||||||
|
@ -437,6 +457,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
||||||
mma8452_get_odr_index(data)];
|
mma8452_get_odr_index(data)];
|
||||||
*val = us / USEC_PER_SEC;
|
*val = us / USEC_PER_SEC;
|
||||||
*val2 = us % USEC_PER_SEC;
|
*val2 = us % USEC_PER_SEC;
|
||||||
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
|
||||||
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
|
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
|
||||||
|
@ -453,6 +474,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -472,19 +494,22 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
|
||||||
|
|
||||||
switch (info) {
|
switch (info) {
|
||||||
case IIO_EV_INFO_VALUE:
|
case IIO_EV_INFO_VALUE:
|
||||||
return mma8452_change_config(data, MMA8452_TRANSIENT_THS,
|
if (val < 0 || val > MMA8452_TRANSIENT_THS_MASK)
|
||||||
val & MMA8452_TRANSIENT_THS_MASK);
|
return -EINVAL;
|
||||||
|
|
||||||
|
return mma8452_change_config(data, MMA8452_TRANSIENT_THS, val);
|
||||||
|
|
||||||
case IIO_EV_INFO_PERIOD:
|
case IIO_EV_INFO_PERIOD:
|
||||||
steps = (val * USEC_PER_SEC + val2) /
|
steps = (val * USEC_PER_SEC + val2) /
|
||||||
mma8452_transient_time_step_us[
|
mma8452_transient_time_step_us[
|
||||||
mma8452_get_odr_index(data)];
|
mma8452_get_odr_index(data)];
|
||||||
|
|
||||||
if (steps > 0xff)
|
if (steps < 0 || steps > 0xff)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
|
return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
|
||||||
steps);
|
steps);
|
||||||
|
|
||||||
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
|
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
|
||||||
reg = i2c_smbus_read_byte_data(data->client,
|
reg = i2c_smbus_read_byte_data(data->client,
|
||||||
MMA8452_TRANSIENT_CFG);
|
MMA8452_TRANSIENT_CFG);
|
||||||
|
@ -499,6 +524,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
|
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -608,15 +634,16 @@ static irqreturn_t mma8452_trigger_handler(int irq, void *p)
|
||||||
u8 buffer[16]; /* 3 16-bit channels + padding + ts */
|
u8 buffer[16]; /* 3 16-bit channels + padding + ts */
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = mma8452_read(data, (__be16 *) buffer);
|
ret = mma8452_read(data, (__be16 *)buffer);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||||
iio_get_time_ns());
|
iio_get_time_ns());
|
||||||
|
|
||||||
done:
|
done:
|
||||||
iio_trigger_notify_done(indio_dev->trig);
|
iio_trigger_notify_done(indio_dev->trig);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,10 +701,10 @@ static struct attribute_group mma8452_event_attribute_group = {
|
||||||
.modified = 1, \
|
.modified = 1, \
|
||||||
.channel2 = IIO_MOD_##axis, \
|
.channel2 = IIO_MOD_##axis, \
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||||
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
|
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
|
||||||
.scan_index = idx, \
|
.scan_index = idx, \
|
||||||
.scan_type = { \
|
.scan_type = { \
|
||||||
.sign = 's', \
|
.sign = 's', \
|
||||||
|
@ -780,6 +807,7 @@ static int mma8452_trigger_setup(struct iio_dev *indio_dev)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
indio_dev->trig = trig;
|
indio_dev->trig = trig;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,7 +877,7 @@ static int mma8452_probe(struct i2c_client *client,
|
||||||
|
|
||||||
data->data_cfg = MMA8452_DATA_CFG_FS_2G;
|
data->data_cfg = MMA8452_DATA_CFG_FS_2G;
|
||||||
ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
|
ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
|
||||||
data->data_cfg);
|
data->data_cfg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -891,14 +919,14 @@ static int mma8452_probe(struct i2c_client *client,
|
||||||
}
|
}
|
||||||
|
|
||||||
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
|
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
|
||||||
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
|
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
|
||||||
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
|
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
|
||||||
data->ctrl_reg1);
|
data->ctrl_reg1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto trigger_cleanup;
|
goto trigger_cleanup;
|
||||||
|
|
||||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||||
mma8452_trigger_handler, NULL);
|
mma8452_trigger_handler, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto trigger_cleanup;
|
goto trigger_cleanup;
|
||||||
|
|
||||||
|
@ -968,6 +996,7 @@ static const struct of_device_id mma8452_dt_ids[] = {
|
||||||
{ .compatible = "fsl,mma8452" },
|
{ .compatible = "fsl,mma8452" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
|
||||||
|
|
||||||
static struct i2c_driver mma8452_driver = {
|
static struct i2c_driver mma8452_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
|
|
|
@ -297,7 +297,7 @@ EXPORT_SYMBOL(mma9551_read_status_byte);
|
||||||
* Returns: 0 on success, negative value on failure.
|
* Returns: 0 on success, negative value on failure.
|
||||||
*/
|
*/
|
||||||
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
|
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u16 *val)
|
u16 reg, u16 *val)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
__be16 v;
|
__be16 v;
|
||||||
|
@ -328,12 +328,12 @@ EXPORT_SYMBOL(mma9551_read_config_word);
|
||||||
* Returns: 0 on success, negative value on failure.
|
* Returns: 0 on success, negative value on failure.
|
||||||
*/
|
*/
|
||||||
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
|
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u16 val)
|
u16 reg, u16 val)
|
||||||
{
|
{
|
||||||
__be16 v = cpu_to_be16(val);
|
__be16 v = cpu_to_be16(val);
|
||||||
|
|
||||||
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
|
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
|
||||||
(u8 *) &v, 2, NULL, 0);
|
(u8 *)&v, 2, NULL, 0);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mma9551_write_config_word);
|
EXPORT_SYMBOL(mma9551_write_config_word);
|
||||||
|
|
||||||
|
@ -373,7 +373,7 @@ EXPORT_SYMBOL(mma9551_read_status_word);
|
||||||
* @client: I2C client
|
* @client: I2C client
|
||||||
* @app_id: Application ID
|
* @app_id: Application ID
|
||||||
* @reg: Application register
|
* @reg: Application register
|
||||||
* @len: Length of array to read in bytes
|
* @len: Length of array to read (in words)
|
||||||
* @buf: Array of words to read
|
* @buf: Array of words to read
|
||||||
*
|
*
|
||||||
* Read multiple configuration registers (word-sized registers).
|
* Read multiple configuration registers (word-sized registers).
|
||||||
|
@ -385,23 +385,22 @@ EXPORT_SYMBOL(mma9551_read_status_word);
|
||||||
* Returns: 0 on success, negative value on failure.
|
* Returns: 0 on success, negative value on failure.
|
||||||
*/
|
*/
|
||||||
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
|
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u8 len, u16 *buf)
|
u16 reg, u8 len, u16 *buf)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
int len_words = len / sizeof(u16);
|
|
||||||
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
|
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
|
||||||
|
|
||||||
if (len_words > ARRAY_SIZE(be_buf)) {
|
if (len > ARRAY_SIZE(be_buf)) {
|
||||||
dev_err(&client->dev, "Invalid buffer size %d\n", len);
|
dev_err(&client->dev, "Invalid buffer size %d\n", len);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
|
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
|
||||||
reg, NULL, 0, (u8 *) be_buf, len);
|
reg, NULL, 0, (u8 *)be_buf, len * sizeof(u16));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
for (i = 0; i < len_words; i++)
|
for (i = 0; i < len; i++)
|
||||||
buf[i] = be16_to_cpu(be_buf[i]);
|
buf[i] = be16_to_cpu(be_buf[i]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -413,7 +412,7 @@ EXPORT_SYMBOL(mma9551_read_config_words);
|
||||||
* @client: I2C client
|
* @client: I2C client
|
||||||
* @app_id: Application ID
|
* @app_id: Application ID
|
||||||
* @reg: Application register
|
* @reg: Application register
|
||||||
* @len: Length of array to read in bytes
|
* @len: Length of array to read (in words)
|
||||||
* @buf: Array of words to read
|
* @buf: Array of words to read
|
||||||
*
|
*
|
||||||
* Read multiple status registers (word-sized registers).
|
* Read multiple status registers (word-sized registers).
|
||||||
|
@ -428,20 +427,19 @@ int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u8 len, u16 *buf)
|
u16 reg, u8 len, u16 *buf)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
int len_words = len / sizeof(u16);
|
|
||||||
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
|
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
|
||||||
|
|
||||||
if (len_words > ARRAY_SIZE(be_buf)) {
|
if (len > ARRAY_SIZE(be_buf)) {
|
||||||
dev_err(&client->dev, "Invalid buffer size %d\n", len);
|
dev_err(&client->dev, "Invalid buffer size %d\n", len);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
|
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
|
||||||
reg, NULL, 0, (u8 *) be_buf, len);
|
reg, NULL, 0, (u8 *)be_buf, len * sizeof(u16));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
for (i = 0; i < len_words; i++)
|
for (i = 0; i < len; i++)
|
||||||
buf[i] = be16_to_cpu(be_buf[i]);
|
buf[i] = be16_to_cpu(be_buf[i]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -453,7 +451,7 @@ EXPORT_SYMBOL(mma9551_read_status_words);
|
||||||
* @client: I2C client
|
* @client: I2C client
|
||||||
* @app_id: Application ID
|
* @app_id: Application ID
|
||||||
* @reg: Application register
|
* @reg: Application register
|
||||||
* @len: Length of array to write in bytes
|
* @len: Length of array to write (in words)
|
||||||
* @buf: Array of words to write
|
* @buf: Array of words to write
|
||||||
*
|
*
|
||||||
* Write multiple configuration registers (word-sized registers).
|
* Write multiple configuration registers (word-sized registers).
|
||||||
|
@ -468,19 +466,18 @@ int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u8 len, u16 *buf)
|
u16 reg, u8 len, u16 *buf)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int len_words = len / sizeof(u16);
|
|
||||||
__be16 be_buf[(MMA9551_MAX_MAILBOX_DATA_REGS - 1) / 2];
|
__be16 be_buf[(MMA9551_MAX_MAILBOX_DATA_REGS - 1) / 2];
|
||||||
|
|
||||||
if (len_words > ARRAY_SIZE(be_buf)) {
|
if (len > ARRAY_SIZE(be_buf)) {
|
||||||
dev_err(&client->dev, "Invalid buffer size %d\n", len);
|
dev_err(&client->dev, "Invalid buffer size %d\n", len);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < len_words; i++)
|
for (i = 0; i < len; i++)
|
||||||
be_buf[i] = cpu_to_be16(buf[i]);
|
be_buf[i] = cpu_to_be16(buf[i]);
|
||||||
|
|
||||||
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
|
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
|
||||||
reg, (u8 *) be_buf, len, NULL, 0);
|
reg, (u8 *)be_buf, len * sizeof(u16), NULL, 0);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mma9551_write_config_words);
|
EXPORT_SYMBOL(mma9551_write_config_words);
|
||||||
|
|
||||||
|
|
|
@ -53,13 +53,13 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
|
||||||
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
|
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u8 *val);
|
u16 reg, u8 *val);
|
||||||
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
|
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u16 *val);
|
u16 reg, u16 *val);
|
||||||
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
|
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u16 val);
|
u16 reg, u16 val);
|
||||||
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
|
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u16 *val);
|
u16 reg, u16 *val);
|
||||||
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
|
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u8 len, u16 *buf);
|
u16 reg, u8 len, u16 *buf);
|
||||||
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
|
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
|
||||||
u16 reg, u8 len, u16 *buf);
|
u16 reg, u8 len, u16 *buf);
|
||||||
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
|
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
|
||||||
|
|
|
@ -182,6 +182,10 @@ struct mma9553_conf_regs {
|
||||||
|
|
||||||
struct mma9553_data {
|
struct mma9553_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
/*
|
||||||
|
* 1. Serialize access to HW (requested by mma9551_core API).
|
||||||
|
* 2. Serialize sequences that power on/off the device and access HW.
|
||||||
|
*/
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
struct mma9553_conf_regs conf;
|
struct mma9553_conf_regs conf;
|
||||||
struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
|
struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
|
||||||
|
@ -322,7 +326,8 @@ static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
|
ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
|
||||||
MMA9553_REG_STATUS, sizeof(u32), buf);
|
MMA9553_REG_STATUS, ARRAY_SIZE(buf),
|
||||||
|
buf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&data->client->dev,
|
dev_err(&data->client->dev,
|
||||||
"error reading status and stepcnt\n");
|
"error reading status and stepcnt\n");
|
||||||
|
@ -342,10 +347,10 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
|
||||||
struct mma9553_event *ev_step_detect;
|
struct mma9553_event *ev_step_detect;
|
||||||
bool activity_enabled;
|
bool activity_enabled;
|
||||||
|
|
||||||
activity_enabled =
|
activity_enabled = mma9553_is_any_event_enabled(data, true,
|
||||||
mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
|
IIO_ACTIVITY);
|
||||||
ev_step_detect =
|
ev_step_detect = mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
|
||||||
mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
|
IIO_EV_DIR_NONE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If both step detector and activity are enabled, use the MRGFL bit.
|
* If both step detector and activity are enabled, use the MRGFL bit.
|
||||||
|
@ -371,9 +376,8 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mma9551_gpio_config(data->client,
|
ret = mma9551_gpio_config(data->client, MMA9553_DEFAULT_GPIO_PIN, appid,
|
||||||
MMA9553_DEFAULT_GPIO_PIN,
|
bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
|
||||||
appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
data->gpio_bitnum = bitnum;
|
data->gpio_bitnum = bitnum;
|
||||||
|
@ -394,17 +398,16 @@ static int mma9553_init(struct mma9553_data *data)
|
||||||
* a device identification command to differentiate the MMA9553L
|
* a device identification command to differentiate the MMA9553L
|
||||||
* from the MMA9550L.
|
* from the MMA9550L.
|
||||||
*/
|
*/
|
||||||
ret =
|
ret = mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
|
||||||
mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
|
MMA9553_REG_CONF_SLEEPMIN,
|
||||||
MMA9553_REG_CONF_SLEEPMIN,
|
sizeof(data->conf) / sizeof(u16),
|
||||||
sizeof(data->conf), (u16 *) &data->conf);
|
(u16 *)&data->conf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&data->client->dev,
|
dev_err(&data->client->dev,
|
||||||
"failed to read configuration registers\n");
|
"failed to read configuration registers\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Reset GPIO */
|
/* Reset GPIO */
|
||||||
data->gpio_bitnum = MMA9553_MAX_BITNUM;
|
data->gpio_bitnum = MMA9553_MAX_BITNUM;
|
||||||
ret = mma9553_conf_gpio(data);
|
ret = mma9553_conf_gpio(data);
|
||||||
|
@ -419,18 +422,18 @@ static int mma9553_init(struct mma9553_data *data)
|
||||||
data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
|
data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
|
||||||
data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
|
data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
|
||||||
data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
|
data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
|
||||||
data->conf.config =
|
data->conf.config = mma9553_set_bits(data->conf.config, 1,
|
||||||
mma9553_set_bits(data->conf.config, 1, MMA9553_MASK_CONF_CONFIG);
|
MMA9553_MASK_CONF_CONFIG);
|
||||||
/*
|
/*
|
||||||
* Clear the activity debounce counter when the activity level changes,
|
* Clear the activity debounce counter when the activity level changes,
|
||||||
* so that the confidence level applies for any activity level.
|
* so that the confidence level applies for any activity level.
|
||||||
*/
|
*/
|
||||||
data->conf.config = mma9553_set_bits(data->conf.config, 1,
|
data->conf.config = mma9553_set_bits(data->conf.config, 1,
|
||||||
MMA9553_MASK_CONF_ACT_DBCNTM);
|
MMA9553_MASK_CONF_ACT_DBCNTM);
|
||||||
ret =
|
ret = mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
|
||||||
mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
|
MMA9553_REG_CONF_SLEEPMIN,
|
||||||
MMA9553_REG_CONF_SLEEPMIN,
|
sizeof(data->conf) / sizeof(u16),
|
||||||
sizeof(data->conf), (u16 *) &data->conf);
|
(u16 *)&data->conf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&data->client->dev,
|
dev_err(&data->client->dev,
|
||||||
"failed to write configuration registers\n");
|
"failed to write configuration registers\n");
|
||||||
|
@ -567,7 +570,7 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_CALIBHEIGHT:
|
case IIO_CHAN_INFO_CALIBHEIGHT:
|
||||||
tmp = mma9553_get_bits(data->conf.height_weight,
|
tmp = mma9553_get_bits(data->conf.height_weight,
|
||||||
MMA9553_MASK_CONF_HEIGHT);
|
MMA9553_MASK_CONF_HEIGHT);
|
||||||
*val = tmp / 100; /* cm to m */
|
*val = tmp / 100; /* cm to m */
|
||||||
*val2 = (tmp % 100) * 10000;
|
*val2 = (tmp % 100) * 10000;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
@ -719,7 +722,6 @@ static int mma9553_read_event_config(struct iio_dev *indio_dev,
|
||||||
enum iio_event_type type,
|
enum iio_event_type type,
|
||||||
enum iio_event_direction dir)
|
enum iio_event_direction dir)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct mma9553_data *data = iio_priv(indio_dev);
|
struct mma9553_data *data = iio_priv(indio_dev);
|
||||||
struct mma9553_event *event;
|
struct mma9553_event *event;
|
||||||
|
|
||||||
|
@ -1026,22 +1028,22 @@ static irqreturn_t mma9553_event_handler(int irq, void *private)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
ev_prev_activity =
|
ev_prev_activity = mma9553_get_event(data, IIO_ACTIVITY,
|
||||||
mma9553_get_event(data, IIO_ACTIVITY,
|
mma9553_activity_to_mod(
|
||||||
mma9553_activity_to_mod(data->activity),
|
data->activity),
|
||||||
IIO_EV_DIR_FALLING);
|
IIO_EV_DIR_FALLING);
|
||||||
ev_activity =
|
ev_activity = mma9553_get_event(data, IIO_ACTIVITY,
|
||||||
mma9553_get_event(data, IIO_ACTIVITY,
|
mma9553_activity_to_mod(activity),
|
||||||
mma9553_activity_to_mod(activity),
|
IIO_EV_DIR_RISING);
|
||||||
IIO_EV_DIR_RISING);
|
ev_step_detect = mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
|
||||||
ev_step_detect =
|
IIO_EV_DIR_NONE);
|
||||||
mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
|
|
||||||
|
|
||||||
if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
|
if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
|
||||||
data->stepcnt = stepcnt;
|
data->stepcnt = stepcnt;
|
||||||
iio_push_event(indio_dev,
|
iio_push_event(indio_dev,
|
||||||
IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
|
IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
|
||||||
IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
|
IIO_EV_DIR_NONE,
|
||||||
|
IIO_EV_TYPE_CHANGE, 0, 0, 0),
|
||||||
data->timestamp);
|
data->timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1051,17 +1053,19 @@ static irqreturn_t mma9553_event_handler(int irq, void *private)
|
||||||
if (ev_prev_activity && ev_prev_activity->enabled)
|
if (ev_prev_activity && ev_prev_activity->enabled)
|
||||||
iio_push_event(indio_dev,
|
iio_push_event(indio_dev,
|
||||||
IIO_EVENT_CODE(IIO_ACTIVITY, 0,
|
IIO_EVENT_CODE(IIO_ACTIVITY, 0,
|
||||||
ev_prev_activity->info->mod,
|
ev_prev_activity->info->mod,
|
||||||
IIO_EV_DIR_FALLING,
|
IIO_EV_DIR_FALLING,
|
||||||
IIO_EV_TYPE_THRESH, 0, 0, 0),
|
IIO_EV_TYPE_THRESH, 0, 0,
|
||||||
|
0),
|
||||||
data->timestamp);
|
data->timestamp);
|
||||||
|
|
||||||
if (ev_activity && ev_activity->enabled)
|
if (ev_activity && ev_activity->enabled)
|
||||||
iio_push_event(indio_dev,
|
iio_push_event(indio_dev,
|
||||||
IIO_EVENT_CODE(IIO_ACTIVITY, 0,
|
IIO_EVENT_CODE(IIO_ACTIVITY, 0,
|
||||||
ev_activity->info->mod,
|
ev_activity->info->mod,
|
||||||
IIO_EV_DIR_RISING,
|
IIO_EV_DIR_RISING,
|
||||||
IIO_EV_TYPE_THRESH, 0, 0, 0),
|
IIO_EV_TYPE_THRESH, 0, 0,
|
||||||
|
0),
|
||||||
data->timestamp);
|
data->timestamp);
|
||||||
}
|
}
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
@ -1145,7 +1149,7 @@ static int mma9553_probe(struct i2c_client *client,
|
||||||
if (client->irq < 0)
|
if (client->irq < 0)
|
||||||
client->irq = mma9553_gpio_probe(client);
|
client->irq = mma9553_gpio_probe(client);
|
||||||
|
|
||||||
if (client->irq >= 0) {
|
if (client->irq > 0) {
|
||||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
mma9553_irq_handler,
|
mma9553_irq_handler,
|
||||||
mma9553_event_handler,
|
mma9553_event_handler,
|
||||||
|
@ -1156,7 +1160,6 @@ static int mma9553_probe(struct i2c_client *client,
|
||||||
client->irq);
|
client->irq);
|
||||||
goto out_poweroff;
|
goto out_poweroff;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"
|
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"
|
||||||
#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel"
|
#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel"
|
||||||
#define LSM330_ACCEL_DEV_NAME "lsm330_accel"
|
#define LSM330_ACCEL_DEV_NAME "lsm330_accel"
|
||||||
|
#define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct st_sensors_platform_data - default accel platform data
|
* struct st_sensors_platform_data - default accel platform data
|
||||||
|
|
|
@ -226,12 +226,14 @@ static const struct iio_chan_spec st_accel_16bit_channels[] = {
|
||||||
static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||||
{
|
{
|
||||||
.wai = ST_ACCEL_1_WAI_EXP,
|
.wai = ST_ACCEL_1_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LIS3DH_ACCEL_DEV_NAME,
|
[0] = LIS3DH_ACCEL_DEV_NAME,
|
||||||
[1] = LSM303DLHC_ACCEL_DEV_NAME,
|
[1] = LSM303DLHC_ACCEL_DEV_NAME,
|
||||||
[2] = LSM330D_ACCEL_DEV_NAME,
|
[2] = LSM330D_ACCEL_DEV_NAME,
|
||||||
[3] = LSM330DL_ACCEL_DEV_NAME,
|
[3] = LSM330DL_ACCEL_DEV_NAME,
|
||||||
[4] = LSM330DLC_ACCEL_DEV_NAME,
|
[4] = LSM330DLC_ACCEL_DEV_NAME,
|
||||||
|
[5] = LSM303AGR_ACCEL_DEV_NAME,
|
||||||
},
|
},
|
||||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||||
.odr = {
|
.odr = {
|
||||||
|
@ -297,6 +299,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_ACCEL_2_WAI_EXP,
|
.wai = ST_ACCEL_2_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LIS331DLH_ACCEL_DEV_NAME,
|
[0] = LIS331DLH_ACCEL_DEV_NAME,
|
||||||
[1] = LSM303DL_ACCEL_DEV_NAME,
|
[1] = LSM303DL_ACCEL_DEV_NAME,
|
||||||
|
@ -359,6 +362,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_ACCEL_3_WAI_EXP,
|
.wai = ST_ACCEL_3_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LSM330_ACCEL_DEV_NAME,
|
[0] = LSM330_ACCEL_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
@ -437,6 +441,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_ACCEL_4_WAI_EXP,
|
.wai = ST_ACCEL_4_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LIS3LV02DL_ACCEL_DEV_NAME,
|
[0] = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
@ -494,6 +499,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_ACCEL_5_WAI_EXP,
|
.wai = ST_ACCEL_5_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LIS331DL_ACCEL_DEV_NAME,
|
[0] = LIS331DL_ACCEL_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
|
|
@ -68,6 +68,10 @@ static const struct of_device_id st_accel_of_match[] = {
|
||||||
.compatible = "st,lsm330-accel",
|
.compatible = "st,lsm330-accel",
|
||||||
.data = LSM330_ACCEL_DEV_NAME,
|
.data = LSM330_ACCEL_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "st,lsm303agr-accel",
|
||||||
|
.data = LSM303AGR_ACCEL_DEV_NAME,
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||||
|
@ -116,13 +120,13 @@ static const struct i2c_device_id st_accel_id_table[] = {
|
||||||
{ LSM303DL_ACCEL_DEV_NAME },
|
{ LSM303DL_ACCEL_DEV_NAME },
|
||||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||||
{ LSM330_ACCEL_DEV_NAME },
|
{ LSM330_ACCEL_DEV_NAME },
|
||||||
|
{ LSM303AGR_ACCEL_DEV_NAME },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||||
|
|
||||||
static struct i2c_driver st_accel_driver = {
|
static struct i2c_driver st_accel_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "st-accel-i2c",
|
.name = "st-accel-i2c",
|
||||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -57,6 +57,7 @@ static const struct spi_device_id st_accel_id_table[] = {
|
||||||
{ LSM303DL_ACCEL_DEV_NAME },
|
{ LSM303DL_ACCEL_DEV_NAME },
|
||||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||||
{ LSM330_ACCEL_DEV_NAME },
|
{ LSM330_ACCEL_DEV_NAME },
|
||||||
|
{ LSM303AGR_ACCEL_DEV_NAME },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||||
|
|
|
@ -11,17 +11,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/iio/buffer.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
|
#include <linux/iio/trigger.h>
|
||||||
|
#include <linux/iio/triggered_buffer.h>
|
||||||
|
#include <linux/iio/trigger_consumer.h>
|
||||||
|
|
||||||
#define STK8312_REG_XOUT 0x00
|
#define STK8312_REG_XOUT 0x00
|
||||||
#define STK8312_REG_YOUT 0x01
|
#define STK8312_REG_YOUT 0x01
|
||||||
#define STK8312_REG_ZOUT 0x02
|
#define STK8312_REG_ZOUT 0x02
|
||||||
|
#define STK8312_REG_INTSU 0x06
|
||||||
#define STK8312_REG_MODE 0x07
|
#define STK8312_REG_MODE 0x07
|
||||||
|
#define STK8312_REG_SR 0x08
|
||||||
#define STK8312_REG_STH 0x13
|
#define STK8312_REG_STH 0x13
|
||||||
#define STK8312_REG_RESET 0x20
|
#define STK8312_REG_RESET 0x20
|
||||||
#define STK8312_REG_AFECTRL 0x24
|
#define STK8312_REG_AFECTRL 0x24
|
||||||
|
@ -29,14 +37,21 @@
|
||||||
#define STK8312_REG_OTPDATA 0x3E
|
#define STK8312_REG_OTPDATA 0x3E
|
||||||
#define STK8312_REG_OTPCTRL 0x3F
|
#define STK8312_REG_OTPCTRL 0x3F
|
||||||
|
|
||||||
#define STK8312_MODE_ACTIVE 1
|
#define STK8312_MODE_ACTIVE BIT(0)
|
||||||
#define STK8312_MODE_STANDBY 0
|
#define STK8312_MODE_STANDBY 0x00
|
||||||
#define STK8312_MODE_MASK 0x01
|
#define STK8312_MODE_INT_AH_PP 0xC0 /* active-high, push-pull */
|
||||||
#define STK8312_RNG_MASK 0xC0
|
#define STK8312_DREADY_BIT BIT(4)
|
||||||
|
#define STK8312_RNG_6G 1
|
||||||
#define STK8312_RNG_SHIFT 6
|
#define STK8312_RNG_SHIFT 6
|
||||||
#define STK8312_READ_RETRIES 16
|
#define STK8312_RNG_MASK GENMASK(7, 6)
|
||||||
|
#define STK8312_SR_MASK GENMASK(2, 0)
|
||||||
|
#define STK8312_SR_400HZ_IDX 0
|
||||||
|
#define STK8312_ALL_CHANNEL_MASK GENMASK(2, 0)
|
||||||
|
#define STK8312_ALL_CHANNEL_SIZE 3
|
||||||
|
|
||||||
#define STK8312_DRIVER_NAME "stk8312"
|
#define STK8312_DRIVER_NAME "stk8312"
|
||||||
|
#define STK8312_GPIO "stk8312_gpio"
|
||||||
|
#define STK8312_IRQ_NAME "stk8312_event"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The accelerometer has two measurement ranges:
|
* The accelerometer has two measurement ranges:
|
||||||
|
@ -53,32 +68,56 @@ static const int stk8312_scale_table[][2] = {
|
||||||
{0, 461600}, {1, 231100}
|
{0, 461600}, {1, 231100}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define STK8312_ACCEL_CHANNEL(reg, axis) { \
|
static const struct {
|
||||||
.type = IIO_ACCEL, \
|
int val;
|
||||||
.address = reg, \
|
int val2;
|
||||||
.modified = 1, \
|
} stk8312_samp_freq_table[] = {
|
||||||
.channel2 = IIO_MOD_##axis, \
|
{400, 0}, {200, 0}, {100, 0}, {50, 0}, {25, 0},
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
{12, 500000}, {6, 250000}, {3, 125000}
|
||||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
};
|
||||||
|
|
||||||
|
#define STK8312_ACCEL_CHANNEL(index, reg, axis) { \
|
||||||
|
.type = IIO_ACCEL, \
|
||||||
|
.address = reg, \
|
||||||
|
.modified = 1, \
|
||||||
|
.channel2 = IIO_MOD_##axis, \
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||||
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||||
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||||
|
.scan_index = index, \
|
||||||
|
.scan_type = { \
|
||||||
|
.sign = 's', \
|
||||||
|
.realbits = 8, \
|
||||||
|
.storagebits = 8, \
|
||||||
|
.endianness = IIO_CPU, \
|
||||||
|
}, \
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iio_chan_spec stk8312_channels[] = {
|
static const struct iio_chan_spec stk8312_channels[] = {
|
||||||
STK8312_ACCEL_CHANNEL(STK8312_REG_XOUT, X),
|
STK8312_ACCEL_CHANNEL(0, STK8312_REG_XOUT, X),
|
||||||
STK8312_ACCEL_CHANNEL(STK8312_REG_YOUT, Y),
|
STK8312_ACCEL_CHANNEL(1, STK8312_REG_YOUT, Y),
|
||||||
STK8312_ACCEL_CHANNEL(STK8312_REG_ZOUT, Z),
|
STK8312_ACCEL_CHANNEL(2, STK8312_REG_ZOUT, Z),
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct stk8312_data {
|
struct stk8312_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
int range;
|
u8 range;
|
||||||
|
u8 sample_rate_idx;
|
||||||
u8 mode;
|
u8 mode;
|
||||||
|
struct iio_trigger *dready_trig;
|
||||||
|
bool dready_trigger_on;
|
||||||
|
s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 64-bit timestamp */
|
||||||
};
|
};
|
||||||
|
|
||||||
static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
|
static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
|
||||||
|
|
||||||
|
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("3.125 6.25 12.5 25 50 100 200 400");
|
||||||
|
|
||||||
static struct attribute *stk8312_attributes[] = {
|
static struct attribute *stk8312_attributes[] = {
|
||||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||||
|
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,22 +144,25 @@ static int stk8312_otp_init(struct stk8312_data *data)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto exit_err;
|
goto exit_err;
|
||||||
count--;
|
count--;
|
||||||
} while (!(ret & 0x80) && count > 0);
|
} while (!(ret & BIT(7)) && count > 0);
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0) {
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
goto exit_err;
|
goto exit_err;
|
||||||
|
}
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPDATA);
|
ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPDATA);
|
||||||
|
if (ret == 0)
|
||||||
|
ret = -EINVAL;
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto exit_err;
|
goto exit_err;
|
||||||
|
|
||||||
ret = i2c_smbus_write_byte_data(data->client,
|
ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_AFECTRL, ret);
|
||||||
STK8312_REG_AFECTRL, ret);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto exit_err;
|
goto exit_err;
|
||||||
msleep(150);
|
msleep(150);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
|
|
||||||
exit_err:
|
exit_err:
|
||||||
dev_err(&client->dev, "failed to initialize sensor\n");
|
dev_err(&client->dev, "failed to initialize sensor\n");
|
||||||
|
@ -130,31 +172,19 @@ exit_err:
|
||||||
static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
|
static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
u8 masked_reg;
|
|
||||||
struct i2c_client *client = data->client;
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
if (mode > 1)
|
if (mode == data->mode)
|
||||||
return -EINVAL;
|
|
||||||
else if (mode == data->mode)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
|
ret = i2c_smbus_write_byte_data(client, STK8312_REG_MODE, mode);
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&client->dev, "failed to change sensor mode\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
masked_reg = ret & (~STK8312_MODE_MASK);
|
|
||||||
masked_reg |= mode;
|
|
||||||
|
|
||||||
ret = i2c_smbus_write_byte_data(client,
|
|
||||||
STK8312_REG_MODE, masked_reg);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "failed to change sensor mode\n");
|
dev_err(&client->dev, "failed to change sensor mode\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->mode = mode;
|
data->mode = mode;
|
||||||
if (mode == STK8312_MODE_ACTIVE) {
|
if (mode & STK8312_MODE_ACTIVE) {
|
||||||
/* Need to run OTP sequence before entering active mode */
|
/* Need to run OTP sequence before entering active mode */
|
||||||
usleep_range(1000, 5000);
|
usleep_range(1000, 5000);
|
||||||
ret = stk8312_otp_init(data);
|
ret = stk8312_otp_init(data);
|
||||||
|
@ -163,6 +193,92 @@ static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stk8312_set_interrupts(struct stk8312_data *data, u8 int_mask)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 mode;
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
|
mode = data->mode;
|
||||||
|
/* We need to go in standby mode to modify registers */
|
||||||
|
ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, STK8312_REG_INTSU, int_mask);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "failed to set interrupts\n");
|
||||||
|
stk8312_set_mode(data, mode);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stk8312_set_mode(data, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stk8312_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||||
|
bool state)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||||
|
struct stk8312_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
ret = stk8312_set_interrupts(data, STK8312_DREADY_BIT);
|
||||||
|
else
|
||||||
|
ret = stk8312_set_interrupts(data, 0x00);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&data->client->dev, "failed to set trigger state\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dready_trigger_on = state;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_trigger_ops stk8312_trigger_ops = {
|
||||||
|
.set_trigger_state = stk8312_data_rdy_trigger_set_state,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stk8312_set_sample_rate(struct stk8312_data *data, u8 rate)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u8 masked_reg;
|
||||||
|
u8 mode;
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
|
if (rate == data->sample_rate_idx)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mode = data->mode;
|
||||||
|
/* We need to go in standby mode to modify registers */
|
||||||
|
ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte_data(client, STK8312_REG_SR);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_activate;
|
||||||
|
|
||||||
|
masked_reg = (ret & (~STK8312_SR_MASK)) | rate;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, STK8312_REG_SR, masked_reg);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_activate;
|
||||||
|
|
||||||
|
data->sample_rate_idx = rate;
|
||||||
|
|
||||||
|
return stk8312_set_mode(data, mode);
|
||||||
|
|
||||||
|
err_activate:
|
||||||
|
dev_err(&client->dev, "failed to set sampling rate\n");
|
||||||
|
stk8312_set_mode(data, mode);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int stk8312_set_range(struct stk8312_data *data, u8 range)
|
static int stk8312_set_range(struct stk8312_data *data, u8 range)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -182,21 +298,25 @@ static int stk8312_set_range(struct stk8312_data *data, u8 range)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
|
ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
dev_err(&client->dev, "failed to change sensor range\n");
|
goto err_activate;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
masked_reg = ret & (~STK8312_RNG_MASK);
|
masked_reg = ret & (~STK8312_RNG_MASK);
|
||||||
masked_reg |= range << STK8312_RNG_SHIFT;
|
masked_reg |= range << STK8312_RNG_SHIFT;
|
||||||
|
|
||||||
ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
|
ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(&client->dev, "failed to change sensor range\n");
|
goto err_activate;
|
||||||
else
|
|
||||||
data->range = range;
|
data->range = range;
|
||||||
|
|
||||||
return stk8312_set_mode(data, mode);
|
return stk8312_set_mode(data, mode);
|
||||||
|
|
||||||
|
err_activate:
|
||||||
|
dev_err(&client->dev, "failed to change sensor range\n");
|
||||||
|
stk8312_set_mode(data, mode);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stk8312_read_accel(struct stk8312_data *data, u8 address)
|
static int stk8312_read_accel(struct stk8312_data *data, u8 address)
|
||||||
|
@ -208,12 +328,10 @@ static int stk8312_read_accel(struct stk8312_data *data, u8 address)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(client, address);
|
ret = i2c_smbus_read_byte_data(client, address);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
dev_err(&client->dev, "register read failed\n");
|
dev_err(&client->dev, "register read failed\n");
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sign_extend32(ret, 7);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stk8312_read_raw(struct iio_dev *indio_dev,
|
static int stk8312_read_raw(struct iio_dev *indio_dev,
|
||||||
|
@ -221,20 +339,40 @@ static int stk8312_read_raw(struct iio_dev *indio_dev,
|
||||||
int *val, int *val2, long mask)
|
int *val, int *val2, long mask)
|
||||||
{
|
{
|
||||||
struct stk8312_data *data = iio_priv(indio_dev);
|
struct stk8312_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
if (chan->type != IIO_ACCEL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
switch (mask) {
|
switch (mask) {
|
||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
if (iio_buffer_enabled(indio_dev))
|
||||||
|
return -EBUSY;
|
||||||
mutex_lock(&data->lock);
|
mutex_lock(&data->lock);
|
||||||
*val = stk8312_read_accel(data, chan->address);
|
ret = stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE);
|
||||||
|
if (ret < 0) {
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = stk8312_read_accel(data, chan->address);
|
||||||
|
if (ret < 0) {
|
||||||
|
stk8312_set_mode(data,
|
||||||
|
data->mode & (~STK8312_MODE_ACTIVE));
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
*val = sign_extend32(ret, 7);
|
||||||
|
ret = stk8312_set_mode(data,
|
||||||
|
data->mode & (~STK8312_MODE_ACTIVE));
|
||||||
mutex_unlock(&data->lock);
|
mutex_unlock(&data->lock);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
*val = stk8312_scale_table[data->range - 1][0];
|
*val = stk8312_scale_table[data->range - 1][0];
|
||||||
*val2 = stk8312_scale_table[data->range - 1][1];
|
*val2 = stk8312_scale_table[data->range - 1][1];
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
*val = stk8312_samp_freq_table[data->sample_rate_idx].val;
|
||||||
|
*val2 = stk8312_samp_freq_table[data->sample_rate_idx].val2;
|
||||||
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -264,6 +402,20 @@ static int stk8312_write_raw(struct iio_dev *indio_dev,
|
||||||
ret = stk8312_set_range(data, index);
|
ret = stk8312_set_range(data, index);
|
||||||
mutex_unlock(&data->lock);
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
for (i = 0; i < ARRAY_SIZE(stk8312_samp_freq_table); i++)
|
||||||
|
if (val == stk8312_samp_freq_table[i].val &&
|
||||||
|
val2 == stk8312_samp_freq_table[i].val2) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = stk8312_set_sample_rate(data, index);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +429,105 @@ static const struct iio_info stk8312_info = {
|
||||||
.attrs = &stk8312_attribute_group,
|
.attrs = &stk8312_attribute_group,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static irqreturn_t stk8312_trigger_handler(int irq, void *p)
|
||||||
|
{
|
||||||
|
struct iio_poll_func *pf = p;
|
||||||
|
struct iio_dev *indio_dev = pf->indio_dev;
|
||||||
|
struct stk8312_data *data = iio_priv(indio_dev);
|
||||||
|
int bit, ret, i = 0;
|
||||||
|
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
/*
|
||||||
|
* Do a bulk read if all channels are requested,
|
||||||
|
* from 0x00 (XOUT) to 0x02 (ZOUT)
|
||||||
|
*/
|
||||||
|
if (*(indio_dev->active_scan_mask) == STK8312_ALL_CHANNEL_MASK) {
|
||||||
|
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||||
|
STK8312_REG_XOUT,
|
||||||
|
STK8312_ALL_CHANNEL_SIZE,
|
||||||
|
data->buffer);
|
||||||
|
if (ret < STK8312_ALL_CHANNEL_SIZE) {
|
||||||
|
dev_err(&data->client->dev, "register read failed\n");
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||||
|
indio_dev->masklength) {
|
||||||
|
ret = stk8312_read_accel(data, bit);
|
||||||
|
if (ret < 0) {
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
data->buffer[i++] = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||||
|
pf->timestamp);
|
||||||
|
err:
|
||||||
|
iio_trigger_notify_done(indio_dev->trig);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t stk8312_data_rdy_trig_poll(int irq, void *private)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = private;
|
||||||
|
struct stk8312_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
if (data->dready_trigger_on)
|
||||||
|
iio_trigger_poll(data->dready_trig);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stk8312_buffer_preenable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct stk8312_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stk8312_buffer_postdisable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct stk8312_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return stk8312_set_mode(data, data->mode & (~STK8312_MODE_ACTIVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_buffer_setup_ops stk8312_buffer_setup_ops = {
|
||||||
|
.preenable = stk8312_buffer_preenable,
|
||||||
|
.postenable = iio_triggered_buffer_postenable,
|
||||||
|
.predisable = iio_triggered_buffer_predisable,
|
||||||
|
.postdisable = stk8312_buffer_postdisable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stk8312_gpio_probe(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct device *dev;
|
||||||
|
struct gpio_desc *gpio;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dev = &client->dev;
|
||||||
|
|
||||||
|
/* data ready gpio interrupt pin */
|
||||||
|
gpio = devm_gpiod_get_index(dev, STK8312_GPIO, 0, GPIOD_IN);
|
||||||
|
if (IS_ERR(gpio)) {
|
||||||
|
dev_err(dev, "acpi gpio get index failed\n");
|
||||||
|
return PTR_ERR(gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpiod_to_irq(gpio);
|
||||||
|
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int stk8312_probe(struct i2c_client *client,
|
static int stk8312_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -308,30 +559,91 @@ static int stk8312_probe(struct i2c_client *client,
|
||||||
dev_err(&client->dev, "failed to reset sensor\n");
|
dev_err(&client->dev, "failed to reset sensor\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = stk8312_set_range(data, 1);
|
data->sample_rate_idx = STK8312_SR_400HZ_IDX;
|
||||||
|
ret = stk8312_set_range(data, STK8312_RNG_6G);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
|
ret = stk8312_set_mode(data,
|
||||||
|
STK8312_MODE_INT_AH_PP | STK8312_MODE_ACTIVE);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (client->irq < 0)
|
||||||
|
client->irq = stk8312_gpio_probe(client);
|
||||||
|
|
||||||
|
if (client->irq >= 0) {
|
||||||
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
|
stk8312_data_rdy_trig_poll,
|
||||||
|
NULL,
|
||||||
|
IRQF_TRIGGER_RISING |
|
||||||
|
IRQF_ONESHOT,
|
||||||
|
STK8312_IRQ_NAME,
|
||||||
|
indio_dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "request irq %d failed\n",
|
||||||
|
client->irq);
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||||
|
"%s-dev%d",
|
||||||
|
indio_dev->name,
|
||||||
|
indio_dev->id);
|
||||||
|
if (!data->dready_trig) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dready_trig->dev.parent = &client->dev;
|
||||||
|
data->dready_trig->ops = &stk8312_trigger_ops;
|
||||||
|
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
|
||||||
|
ret = iio_trigger_register(data->dready_trig);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&client->dev, "iio trigger register failed\n");
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = iio_triggered_buffer_setup(indio_dev,
|
||||||
|
iio_pollfunc_store_time,
|
||||||
|
stk8312_trigger_handler,
|
||||||
|
&stk8312_buffer_setup_ops);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||||
|
goto err_trigger_unregister;
|
||||||
|
}
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "device_register failed\n");
|
dev_err(&client->dev, "device_register failed\n");
|
||||||
stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
goto err_buffer_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_buffer_cleanup:
|
||||||
|
iio_triggered_buffer_cleanup(indio_dev);
|
||||||
|
err_trigger_unregister:
|
||||||
|
if (data->dready_trig)
|
||||||
|
iio_trigger_unregister(data->dready_trig);
|
||||||
|
err_power_off:
|
||||||
|
stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stk8312_remove(struct i2c_client *client)
|
static int stk8312_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
|
struct stk8312_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
|
iio_triggered_buffer_cleanup(indio_dev);
|
||||||
|
|
||||||
return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
|
if (data->dready_trig)
|
||||||
|
iio_trigger_unregister(data->dready_trig);
|
||||||
|
|
||||||
|
return stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
@ -341,7 +653,7 @@ static int stk8312_suspend(struct device *dev)
|
||||||
|
|
||||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||||
|
|
||||||
return stk8312_set_mode(data, STK8312_MODE_STANDBY);
|
return stk8312_set_mode(data, data->mode & (~STK8312_MODE_ACTIVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stk8312_resume(struct device *dev)
|
static int stk8312_resume(struct device *dev)
|
||||||
|
@ -350,7 +662,7 @@ static int stk8312_resume(struct device *dev)
|
||||||
|
|
||||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||||
|
|
||||||
return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
|
return stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
|
static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
|
||||||
|
@ -364,6 +676,7 @@ static const struct i2c_device_id stk8312_i2c_id[] = {
|
||||||
{"STK8312", 0},
|
{"STK8312", 0},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id);
|
||||||
|
|
||||||
static const struct acpi_device_id stk8312_acpi_id[] = {
|
static const struct acpi_device_id stk8312_acpi_id[] = {
|
||||||
{"STK8312", 0},
|
{"STK8312", 0},
|
||||||
|
@ -374,7 +687,7 @@ MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
|
||||||
|
|
||||||
static struct i2c_driver stk8312_driver = {
|
static struct i2c_driver stk8312_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "stk8312",
|
.name = STK8312_DRIVER_NAME,
|
||||||
.pm = STK8312_PM_OPS,
|
.pm = STK8312_PM_OPS,
|
||||||
.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
|
.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,26 +11,42 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/iio/buffer.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
|
#include <linux/iio/trigger.h>
|
||||||
|
#include <linux/iio/triggered_buffer.h>
|
||||||
|
#include <linux/iio/trigger_consumer.h>
|
||||||
|
|
||||||
#define STK8BA50_REG_XOUT 0x02
|
#define STK8BA50_REG_XOUT 0x02
|
||||||
#define STK8BA50_REG_YOUT 0x04
|
#define STK8BA50_REG_YOUT 0x04
|
||||||
#define STK8BA50_REG_ZOUT 0x06
|
#define STK8BA50_REG_ZOUT 0x06
|
||||||
#define STK8BA50_REG_RANGE 0x0F
|
#define STK8BA50_REG_RANGE 0x0F
|
||||||
|
#define STK8BA50_REG_BWSEL 0x10
|
||||||
#define STK8BA50_REG_POWMODE 0x11
|
#define STK8BA50_REG_POWMODE 0x11
|
||||||
#define STK8BA50_REG_SWRST 0x14
|
#define STK8BA50_REG_SWRST 0x14
|
||||||
|
#define STK8BA50_REG_INTEN2 0x17
|
||||||
|
#define STK8BA50_REG_INTMAP2 0x1A
|
||||||
|
|
||||||
#define STK8BA50_MODE_NORMAL 0
|
#define STK8BA50_MODE_NORMAL 0
|
||||||
#define STK8BA50_MODE_SUSPEND 1
|
#define STK8BA50_MODE_SUSPEND 1
|
||||||
#define STK8BA50_MODE_POWERBIT BIT(7)
|
#define STK8BA50_MODE_POWERBIT BIT(7)
|
||||||
#define STK8BA50_DATA_SHIFT 6
|
#define STK8BA50_DATA_SHIFT 6
|
||||||
#define STK8BA50_RESET_CMD 0xB6
|
#define STK8BA50_RESET_CMD 0xB6
|
||||||
|
#define STK8BA50_SR_1792HZ_IDX 7
|
||||||
|
#define STK8BA50_DREADY_INT_MASK 0x10
|
||||||
|
#define STK8BA50_DREADY_INT_MAP 0x81
|
||||||
|
#define STK8BA50_ALL_CHANNEL_MASK 7
|
||||||
|
#define STK8BA50_ALL_CHANNEL_SIZE 6
|
||||||
|
|
||||||
#define STK8BA50_DRIVER_NAME "stk8ba50"
|
#define STK8BA50_DRIVER_NAME "stk8ba50"
|
||||||
|
#define STK8BA50_GPIO "stk8ba50_gpio"
|
||||||
|
#define STK8BA50_IRQ_NAME "stk8ba50_event"
|
||||||
|
|
||||||
#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
|
#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
|
||||||
|
|
||||||
|
@ -50,35 +66,76 @@
|
||||||
*
|
*
|
||||||
* Locally, the range is stored as a table index.
|
* Locally, the range is stored as a table index.
|
||||||
*/
|
*/
|
||||||
static const int stk8ba50_scale_table[][2] = {
|
static const struct {
|
||||||
|
u8 reg_val;
|
||||||
|
u32 scale_val;
|
||||||
|
} stk8ba50_scale_table[] = {
|
||||||
{3, 38400}, {5, 76700}, {8, 153400}, {12, 306900}
|
{3, 38400}, {5, 76700}, {8, 153400}, {12, 306900}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Sample rates are stored as { <register value>, <Hz value> } */
|
||||||
|
static const struct {
|
||||||
|
u8 reg_val;
|
||||||
|
u16 samp_freq;
|
||||||
|
} stk8ba50_samp_freq_table[] = {
|
||||||
|
{0x08, 14}, {0x09, 25}, {0x0A, 56}, {0x0B, 112},
|
||||||
|
{0x0C, 224}, {0x0D, 448}, {0x0E, 896}, {0x0F, 1792}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used to map scan mask bits to their corresponding channel register. */
|
||||||
|
static const int stk8ba50_channel_table[] = {
|
||||||
|
STK8BA50_REG_XOUT,
|
||||||
|
STK8BA50_REG_YOUT,
|
||||||
|
STK8BA50_REG_ZOUT
|
||||||
|
};
|
||||||
|
|
||||||
struct stk8ba50_data {
|
struct stk8ba50_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
int range;
|
int range;
|
||||||
|
u8 sample_rate_idx;
|
||||||
|
struct iio_trigger *dready_trig;
|
||||||
|
bool dready_trigger_on;
|
||||||
|
/*
|
||||||
|
* 3 x 16-bit channels (10-bit data, 6-bit padding) +
|
||||||
|
* 1 x 16 padding +
|
||||||
|
* 4 x 16 64-bit timestamp
|
||||||
|
*/
|
||||||
|
s16 buffer[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define STK8BA50_ACCEL_CHANNEL(reg, axis) { \
|
#define STK8BA50_ACCEL_CHANNEL(index, reg, axis) { \
|
||||||
.type = IIO_ACCEL, \
|
.type = IIO_ACCEL, \
|
||||||
.address = reg, \
|
.address = reg, \
|
||||||
.modified = 1, \
|
.modified = 1, \
|
||||||
.channel2 = IIO_MOD_##axis, \
|
.channel2 = IIO_MOD_##axis, \
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||||
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||||
|
.scan_index = index, \
|
||||||
|
.scan_type = { \
|
||||||
|
.sign = 's', \
|
||||||
|
.realbits = 10, \
|
||||||
|
.storagebits = 16, \
|
||||||
|
.shift = STK8BA50_DATA_SHIFT, \
|
||||||
|
.endianness = IIO_CPU, \
|
||||||
|
}, \
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iio_chan_spec stk8ba50_channels[] = {
|
static const struct iio_chan_spec stk8ba50_channels[] = {
|
||||||
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_XOUT, X),
|
STK8BA50_ACCEL_CHANNEL(0, STK8BA50_REG_XOUT, X),
|
||||||
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_YOUT, Y),
|
STK8BA50_ACCEL_CHANNEL(1, STK8BA50_REG_YOUT, Y),
|
||||||
STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_ZOUT, Z),
|
STK8BA50_ACCEL_CHANNEL(2, STK8BA50_REG_ZOUT, Z),
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||||
};
|
};
|
||||||
|
|
||||||
static IIO_CONST_ATTR(in_accel_scale_available, STK8BA50_SCALE_AVAIL);
|
static IIO_CONST_ATTR(in_accel_scale_available, STK8BA50_SCALE_AVAIL);
|
||||||
|
|
||||||
|
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("14 25 56 112 224 448 896 1792");
|
||||||
|
|
||||||
static struct attribute *stk8ba50_attributes[] = {
|
static struct attribute *stk8ba50_attributes[] = {
|
||||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||||
|
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,72 +154,34 @@ static int stk8ba50_read_accel(struct stk8ba50_data *data, u8 reg)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stk8ba50_read_raw(struct iio_dev *indio_dev,
|
static int stk8ba50_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||||
struct iio_chan_spec const *chan,
|
bool state)
|
||||||
int *val, int *val2, long mask)
|
|
||||||
{
|
{
|
||||||
|
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||||
struct stk8ba50_data *data = iio_priv(indio_dev);
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
switch (mask) {
|
|
||||||
case IIO_CHAN_INFO_RAW:
|
|
||||||
mutex_lock(&data->lock);
|
|
||||||
*val = stk8ba50_read_accel(data, chan->address);
|
|
||||||
mutex_unlock(&data->lock);
|
|
||||||
return IIO_VAL_INT;
|
|
||||||
case IIO_CHAN_INFO_SCALE:
|
|
||||||
*val = 0;
|
|
||||||
*val2 = stk8ba50_scale_table[data->range][1];
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int stk8ba50_write_raw(struct iio_dev *indio_dev,
|
|
||||||
struct iio_chan_spec const *chan,
|
|
||||||
int val, int val2, long mask)
|
|
||||||
{
|
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
|
||||||
int index = -1;
|
|
||||||
struct stk8ba50_data *data = iio_priv(indio_dev);
|
|
||||||
|
|
||||||
switch (mask) {
|
|
||||||
case IIO_CHAN_INFO_SCALE:
|
|
||||||
if (val != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(stk8ba50_scale_table); i++)
|
|
||||||
if (val2 == stk8ba50_scale_table[i][1]) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (index < 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
|
if (state)
|
||||||
ret = i2c_smbus_write_byte_data(data->client,
|
ret = i2c_smbus_write_byte_data(data->client,
|
||||||
STK8BA50_REG_RANGE,
|
STK8BA50_REG_INTEN2, STK8BA50_DREADY_INT_MASK);
|
||||||
stk8ba50_scale_table[index][0]);
|
else
|
||||||
if (ret < 0)
|
ret = i2c_smbus_write_byte_data(data->client,
|
||||||
dev_err(&data->client->dev,
|
STK8BA50_REG_INTEN2, 0x00);
|
||||||
"failed to set measurement range\n");
|
|
||||||
else
|
|
||||||
data->range = index;
|
|
||||||
|
|
||||||
return ret;
|
if (ret < 0)
|
||||||
}
|
dev_err(&data->client->dev, "failed to set trigger state\n");
|
||||||
|
else
|
||||||
|
data->dready_trigger_on = state;
|
||||||
|
|
||||||
return -EINVAL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iio_info stk8ba50_info = {
|
static const struct iio_trigger_ops stk8ba50_trigger_ops = {
|
||||||
.driver_module = THIS_MODULE,
|
.set_trigger_state = stk8ba50_data_rdy_trigger_set_state,
|
||||||
.read_raw = stk8ba50_read_raw,
|
.owner = THIS_MODULE,
|
||||||
.write_raw = stk8ba50_write_raw,
|
|
||||||
.attrs = &stk8ba50_attribute_group,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int stk8ba50_set_power(struct stk8ba50_data *data, bool mode)
|
static int stk8ba50_set_power(struct stk8ba50_data *data, bool mode)
|
||||||
|
@ -192,6 +211,207 @@ exit_err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stk8ba50_read_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan,
|
||||||
|
int *val, int *val2, long mask)
|
||||||
|
{
|
||||||
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
if (iio_buffer_enabled(indio_dev))
|
||||||
|
return -EBUSY;
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
|
||||||
|
if (ret < 0) {
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
ret = stk8ba50_read_accel(data, chan->address);
|
||||||
|
if (ret < 0) {
|
||||||
|
stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
*val = sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9);
|
||||||
|
stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
*val = 0;
|
||||||
|
*val2 = stk8ba50_scale_table[data->range].scale_val;
|
||||||
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
*val = stk8ba50_samp_freq_table
|
||||||
|
[data->sample_rate_idx].samp_freq;
|
||||||
|
*val2 = 0;
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stk8ba50_write_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan,
|
||||||
|
int val, int val2, long mask)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
int index = -1;
|
||||||
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
if (val != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(stk8ba50_scale_table); i++)
|
||||||
|
if (val2 == stk8ba50_scale_table[i].scale_val) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(data->client,
|
||||||
|
STK8BA50_REG_RANGE,
|
||||||
|
stk8ba50_scale_table[index].reg_val);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(&data->client->dev,
|
||||||
|
"failed to set measurement range\n");
|
||||||
|
else
|
||||||
|
data->range = index;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
for (i = 0; i < ARRAY_SIZE(stk8ba50_samp_freq_table); i++)
|
||||||
|
if (val == stk8ba50_samp_freq_table[i].samp_freq) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(data->client,
|
||||||
|
STK8BA50_REG_BWSEL,
|
||||||
|
stk8ba50_samp_freq_table[index].reg_val);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(&data->client->dev,
|
||||||
|
"failed to set sampling rate\n");
|
||||||
|
else
|
||||||
|
data->sample_rate_idx = index;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_info stk8ba50_info = {
|
||||||
|
.driver_module = THIS_MODULE,
|
||||||
|
.read_raw = stk8ba50_read_raw,
|
||||||
|
.write_raw = stk8ba50_write_raw,
|
||||||
|
.attrs = &stk8ba50_attribute_group,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t stk8ba50_trigger_handler(int irq, void *p)
|
||||||
|
{
|
||||||
|
struct iio_poll_func *pf = p;
|
||||||
|
struct iio_dev *indio_dev = pf->indio_dev;
|
||||||
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
int bit, ret, i = 0;
|
||||||
|
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
/*
|
||||||
|
* Do a bulk read if all channels are requested,
|
||||||
|
* from 0x02 (XOUT1) to 0x07 (ZOUT2)
|
||||||
|
*/
|
||||||
|
if (*(indio_dev->active_scan_mask) == STK8BA50_ALL_CHANNEL_MASK) {
|
||||||
|
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||||
|
STK8BA50_REG_XOUT,
|
||||||
|
STK8BA50_ALL_CHANNEL_SIZE,
|
||||||
|
(u8 *)data->buffer);
|
||||||
|
if (ret < STK8BA50_ALL_CHANNEL_SIZE) {
|
||||||
|
dev_err(&data->client->dev, "register read failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||||
|
indio_dev->masklength) {
|
||||||
|
ret = stk8ba50_read_accel(data,
|
||||||
|
stk8ba50_channel_table[bit]);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
data->buffer[i++] = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||||
|
pf->timestamp);
|
||||||
|
err:
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
iio_trigger_notify_done(indio_dev->trig);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t stk8ba50_data_rdy_trig_poll(int irq, void *private)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = private;
|
||||||
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
if (data->dready_trigger_on)
|
||||||
|
iio_trigger_poll(data->dready_trig);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stk8ba50_buffer_preenable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stk8ba50_buffer_postdisable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_buffer_setup_ops stk8ba50_buffer_setup_ops = {
|
||||||
|
.preenable = stk8ba50_buffer_preenable,
|
||||||
|
.postenable = iio_triggered_buffer_postenable,
|
||||||
|
.predisable = iio_triggered_buffer_predisable,
|
||||||
|
.postdisable = stk8ba50_buffer_postdisable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stk8ba50_gpio_probe(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct device *dev;
|
||||||
|
struct gpio_desc *gpio;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dev = &client->dev;
|
||||||
|
|
||||||
|
/* data ready gpio interrupt pin */
|
||||||
|
gpio = devm_gpiod_get_index(dev, STK8BA50_GPIO, 0, GPIOD_IN);
|
||||||
|
if (IS_ERR(gpio)) {
|
||||||
|
dev_err(dev, "acpi gpio get index failed\n");
|
||||||
|
return PTR_ERR(gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpiod_to_irq(gpio);
|
||||||
|
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int stk8ba50_probe(struct i2c_client *client,
|
static int stk8ba50_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -222,28 +442,104 @@ static int stk8ba50_probe(struct i2c_client *client,
|
||||||
STK8BA50_REG_SWRST, STK8BA50_RESET_CMD);
|
STK8BA50_REG_SWRST, STK8BA50_RESET_CMD);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "failed to reset sensor\n");
|
dev_err(&client->dev, "failed to reset sensor\n");
|
||||||
return ret;
|
goto err_power_off;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The default range is +/-2g */
|
/* The default range is +/-2g */
|
||||||
data->range = 0;
|
data->range = 0;
|
||||||
|
|
||||||
|
/* The default sampling rate is 1792 Hz (maximum) */
|
||||||
|
data->sample_rate_idx = STK8BA50_SR_1792HZ_IDX;
|
||||||
|
|
||||||
|
/* Set up interrupts */
|
||||||
|
ret = i2c_smbus_write_byte_data(client,
|
||||||
|
STK8BA50_REG_INTEN2, STK8BA50_DREADY_INT_MASK);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "failed to set up interrupts\n");
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
ret = i2c_smbus_write_byte_data(client,
|
||||||
|
STK8BA50_REG_INTMAP2, STK8BA50_DREADY_INT_MAP);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "failed to set up interrupts\n");
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->irq < 0)
|
||||||
|
client->irq = stk8ba50_gpio_probe(client);
|
||||||
|
|
||||||
|
if (client->irq >= 0) {
|
||||||
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
|
stk8ba50_data_rdy_trig_poll,
|
||||||
|
NULL,
|
||||||
|
IRQF_TRIGGER_RISING |
|
||||||
|
IRQF_ONESHOT,
|
||||||
|
STK8BA50_IRQ_NAME,
|
||||||
|
indio_dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "request irq %d failed\n",
|
||||||
|
client->irq);
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||||
|
"%s-dev%d",
|
||||||
|
indio_dev->name,
|
||||||
|
indio_dev->id);
|
||||||
|
if (!data->dready_trig) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dready_trig->dev.parent = &client->dev;
|
||||||
|
data->dready_trig->ops = &stk8ba50_trigger_ops;
|
||||||
|
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
|
||||||
|
ret = iio_trigger_register(data->dready_trig);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&client->dev, "iio trigger register failed\n");
|
||||||
|
goto err_power_off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = iio_triggered_buffer_setup(indio_dev,
|
||||||
|
iio_pollfunc_store_time,
|
||||||
|
stk8ba50_trigger_handler,
|
||||||
|
&stk8ba50_buffer_setup_ops);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||||
|
goto err_trigger_unregister;
|
||||||
|
}
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "device_register failed\n");
|
dev_err(&client->dev, "device_register failed\n");
|
||||||
stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
goto err_buffer_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
err_buffer_cleanup:
|
||||||
|
iio_triggered_buffer_cleanup(indio_dev);
|
||||||
|
err_trigger_unregister:
|
||||||
|
if (data->dready_trig)
|
||||||
|
iio_trigger_unregister(data->dready_trig);
|
||||||
|
err_power_off:
|
||||||
|
stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stk8ba50_remove(struct i2c_client *client)
|
static int stk8ba50_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
|
struct stk8ba50_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
|
iio_triggered_buffer_cleanup(indio_dev);
|
||||||
|
|
||||||
return stk8ba50_set_power(iio_priv(indio_dev), STK8BA50_MODE_SUSPEND);
|
if (data->dready_trig)
|
||||||
|
iio_trigger_unregister(data->dready_trig);
|
||||||
|
|
||||||
|
return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
@ -276,6 +572,7 @@ static const struct i2c_device_id stk8ba50_i2c_id[] = {
|
||||||
{"stk8ba50", 0},
|
{"stk8ba50", 0},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, stk8ba50_i2c_id);
|
||||||
|
|
||||||
static const struct acpi_device_id stk8ba50_acpi_id[] = {
|
static const struct acpi_device_id stk8ba50_acpi_id[] = {
|
||||||
{"STK8BA50", 0},
|
{"STK8BA50", 0},
|
||||||
|
|
|
@ -20,6 +20,9 @@ config AD7266
|
||||||
Say yes here to build support for Analog Devices AD7265 and AD7266
|
Say yes here to build support for Analog Devices AD7265 and AD7266
|
||||||
ADCs.
|
ADCs.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called ad7266.
|
||||||
|
|
||||||
config AD7291
|
config AD7291
|
||||||
tristate "Analog Devices AD7291 ADC driver"
|
tristate "Analog Devices AD7291 ADC driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -52,8 +55,6 @@ config AD7476
|
||||||
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
|
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
|
||||||
AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
|
AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
|
||||||
|
|
||||||
If unsure, say N (but it's safe to say "Y").
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ad7476.
|
module will be called ad7476.
|
||||||
|
|
||||||
|
@ -63,8 +64,7 @@ config AD7791
|
||||||
select AD_SIGMA_DELTA
|
select AD_SIGMA_DELTA
|
||||||
help
|
help
|
||||||
Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
|
Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
|
||||||
AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say
|
AD7790 and AD7791 SPI analog to digital converters (ADC).
|
||||||
N (but it is safe to say "Y").
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will be
|
To compile this driver as a module, choose M here: the module will be
|
||||||
called ad7791.
|
called ad7791.
|
||||||
|
@ -76,7 +76,6 @@ config AD7793
|
||||||
help
|
help
|
||||||
Say yes here to build support for Analog Devices AD7785, AD7792, AD7793,
|
Say yes here to build support for Analog Devices AD7785, AD7792, AD7793,
|
||||||
AD7794 and AD7795 SPI analog to digital converters (ADC).
|
AD7794 and AD7795 SPI analog to digital converters (ADC).
|
||||||
If unsure, say N (but it's safe to say "Y").
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called AD7793.
|
module will be called AD7793.
|
||||||
|
@ -89,7 +88,6 @@ config AD7887
|
||||||
help
|
help
|
||||||
Say yes here to build support for Analog Devices
|
Say yes here to build support for Analog Devices
|
||||||
AD7887 SPI analog to digital converter (ADC).
|
AD7887 SPI analog to digital converter (ADC).
|
||||||
If unsure, say N (but it's safe to say "Y").
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ad7887.
|
module will be called ad7887.
|
||||||
|
@ -117,6 +115,9 @@ config AD799X
|
||||||
i2c analog to digital converters (ADC). Provides direct access
|
i2c analog to digital converters (ADC). Provides direct access
|
||||||
via sysfs.
|
via sysfs.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called ad799x.
|
||||||
|
|
||||||
config AT91_ADC
|
config AT91_ADC
|
||||||
tristate "Atmel AT91 ADC"
|
tristate "Atmel AT91 ADC"
|
||||||
depends on ARCH_AT91
|
depends on ARCH_AT91
|
||||||
|
@ -127,6 +128,9 @@ config AT91_ADC
|
||||||
help
|
help
|
||||||
Say yes here to build support for Atmel AT91 ADC.
|
Say yes here to build support for Atmel AT91 ADC.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called at91_adc.
|
||||||
|
|
||||||
config AXP288_ADC
|
config AXP288_ADC
|
||||||
tristate "X-Powers AXP288 ADC driver"
|
tristate "X-Powers AXP288 ADC driver"
|
||||||
depends on MFD_AXP20X
|
depends on MFD_AXP20X
|
||||||
|
@ -135,6 +139,9 @@ config AXP288_ADC
|
||||||
device. Depending on platform configuration, this general purpose ADC can
|
device. Depending on platform configuration, this general purpose ADC can
|
||||||
be used for sampling sensors such as thermal resistors.
|
be used for sampling sensors such as thermal resistors.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called axp288_adc.
|
||||||
|
|
||||||
config BERLIN2_ADC
|
config BERLIN2_ADC
|
||||||
tristate "Marvell Berlin2 ADC driver"
|
tristate "Marvell Berlin2 ADC driver"
|
||||||
depends on ARCH_BERLIN
|
depends on ARCH_BERLIN
|
||||||
|
@ -151,6 +158,9 @@ config DA9150_GPADC
|
||||||
This driver can also be built as a module. If chosen, the module name
|
This driver can also be built as a module. If chosen, the module name
|
||||||
will be da9150-gpadc.
|
will be da9150-gpadc.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called berlin2-adc.
|
||||||
|
|
||||||
config CC10001_ADC
|
config CC10001_ADC
|
||||||
tristate "Cosmic Circuits 10001 ADC driver"
|
tristate "Cosmic Circuits 10001 ADC driver"
|
||||||
depends on HAS_IOMEM && HAVE_CLK && REGULATOR
|
depends on HAS_IOMEM && HAVE_CLK && REGULATOR
|
||||||
|
@ -170,12 +180,18 @@ config EXYNOS_ADC
|
||||||
of SoCs for drivers such as the touchscreen and hwmon to use to share
|
of SoCs for drivers such as the touchscreen and hwmon to use to share
|
||||||
this resource.
|
this resource.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called exynos_adc.
|
||||||
|
|
||||||
config LP8788_ADC
|
config LP8788_ADC
|
||||||
tristate "LP8788 ADC driver"
|
tristate "LP8788 ADC driver"
|
||||||
depends on MFD_LP8788
|
depends on MFD_LP8788
|
||||||
help
|
help
|
||||||
Say yes here to build support for TI LP8788 ADC.
|
Say yes here to build support for TI LP8788 ADC.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called lp8788_adc.
|
||||||
|
|
||||||
config MAX1027
|
config MAX1027
|
||||||
tristate "Maxim max1027 ADC driver"
|
tristate "Maxim max1027 ADC driver"
|
||||||
depends on SPI
|
depends on SPI
|
||||||
|
@ -185,6 +201,9 @@ config MAX1027
|
||||||
Say yes here to build support for Maxim SPI ADC models
|
Say yes here to build support for Maxim SPI ADC models
|
||||||
max1027, max1029 and max1031.
|
max1027, max1029 and max1031.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called max1027.
|
||||||
|
|
||||||
config MAX1363
|
config MAX1363
|
||||||
tristate "Maxim max1363 ADC driver"
|
tristate "Maxim max1363 ADC driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -201,13 +220,16 @@ config MAX1363
|
||||||
max11646, max11647) Provides direct access via sysfs and buffered
|
max11646, max11647) Provides direct access via sysfs and buffered
|
||||||
data via the iio dev interface.
|
data via the iio dev interface.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called max1363.
|
||||||
|
|
||||||
config MCP320X
|
config MCP320X
|
||||||
tristate "Microchip Technology MCP3x01/02/04/08"
|
tristate "Microchip Technology MCP3x01/02/04/08"
|
||||||
depends on SPI
|
depends on SPI
|
||||||
help
|
help
|
||||||
Say yes here to build support for Microchip Technology's
|
Say yes here to build support for Microchip Technology's
|
||||||
MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204 or
|
MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204,
|
||||||
MCP3208 analog to digital converter.
|
MCP3208 or MCP3301 analog to digital converter.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
called mcp320x.
|
called mcp320x.
|
||||||
|
@ -309,15 +331,18 @@ config TI_AM335X_ADC
|
||||||
Say yes here to build support for Texas Instruments ADC
|
Say yes here to build support for Texas Instruments ADC
|
||||||
driver which is also a MFD client.
|
driver which is also a MFD client.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called ti_am335x_adc.
|
||||||
|
|
||||||
config TWL4030_MADC
|
config TWL4030_MADC
|
||||||
tristate "TWL4030 MADC (Monitoring A/D Converter)"
|
tristate "TWL4030 MADC (Monitoring A/D Converter)"
|
||||||
depends on TWL4030_CORE
|
depends on TWL4030_CORE
|
||||||
help
|
help
|
||||||
This driver provides support for Triton TWL4030-MADC. The
|
This driver provides support for Triton TWL4030-MADC. The
|
||||||
driver supports both RT and SW conversion methods.
|
driver supports both RT and SW conversion methods.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
called twl4030-madc.
|
called twl4030-madc.
|
||||||
|
|
||||||
config TWL6030_GPADC
|
config TWL6030_GPADC
|
||||||
tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
|
tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
|
||||||
|
@ -350,6 +375,9 @@ config VIPERBOARD_ADC
|
||||||
Say yes here to access the ADC part of the Nano River
|
Say yes here to access the ADC part of the Nano River
|
||||||
Technologies Viperboard.
|
Technologies Viperboard.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module will be
|
||||||
|
called viperboard_adc.
|
||||||
|
|
||||||
config XILINX_XADC
|
config XILINX_XADC
|
||||||
tristate "Xilinx XADC driver"
|
tristate "Xilinx XADC driver"
|
||||||
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
|
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#define BERLIN2_SM_CTRL 0x14
|
#define BERLIN2_SM_CTRL 0x14
|
||||||
#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
|
#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
|
||||||
#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
|
#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
|
||||||
#define BERLIN2_SM_CTRL_ADC_SEL(x) (BIT(x) << 5) /* 0-15 */
|
#define BERLIN2_SM_CTRL_ADC_SEL(x) ((x) << 5) /* 0-15 */
|
||||||
#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5)
|
#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5)
|
||||||
#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
|
#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
|
||||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
|
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
|
||||||
|
@ -53,14 +53,14 @@
|
||||||
#define BERLIN2_SM_ADC_MASK 0x3ff
|
#define BERLIN2_SM_ADC_MASK 0x3ff
|
||||||
#define BERLIN2_SM_ADC_STATUS 0x1c
|
#define BERLIN2_SM_ADC_STATUS 0x1c
|
||||||
#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
|
#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
|
||||||
#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK 0xf
|
#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK GENMASK(15, 0)
|
||||||
#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */
|
#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */
|
||||||
#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK (0xf << 16)
|
#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK GENMASK(31, 16)
|
||||||
#define BERLIN2_SM_TSEN_STATUS 0x24
|
#define BERLIN2_SM_TSEN_STATUS 0x24
|
||||||
#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0)
|
#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0)
|
||||||
#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1)
|
#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1)
|
||||||
#define BERLIN2_SM_TSEN_DATA 0x28
|
#define BERLIN2_SM_TSEN_DATA 0x28
|
||||||
#define BERLIN2_SM_TSEN_MASK 0xfff
|
#define BERLIN2_SM_TSEN_MASK GENMASK(9, 0)
|
||||||
#define BERLIN2_SM_TSEN_CTRL 0x74
|
#define BERLIN2_SM_TSEN_CTRL 0x74
|
||||||
#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
|
#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
|
||||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
|
#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
|
||||||
|
@ -86,7 +86,7 @@ struct berlin2_adc_priv {
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct iio_chan_spec berlin2_adc_channels[] = {
|
static const struct iio_chan_spec berlin2_adc_channels[] = {
|
||||||
BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
|
BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
|
||||||
BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */
|
BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */
|
||||||
BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */
|
BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */
|
||||||
|
@ -103,7 +103,6 @@ static struct iio_chan_spec berlin2_adc_channels[] = {
|
||||||
BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */
|
BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */
|
||||||
IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */
|
IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */
|
||||||
};
|
};
|
||||||
#define BERLIN2_N_CHANNELS ARRAY_SIZE(berlin2_adc_channels)
|
|
||||||
|
|
||||||
static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
|
static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
|
||||||
{
|
{
|
||||||
|
@ -221,7 +220,7 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
if (temp > 2047)
|
if (temp > 2047)
|
||||||
temp = -(4096 - temp);
|
temp -= 4096;
|
||||||
|
|
||||||
/* Convert to milli Celsius */
|
/* Convert to milli Celsius */
|
||||||
*val = ((temp * 100000) / 264 - 270000);
|
*val = ((temp * 100000) / 264 - 270000);
|
||||||
|
@ -286,8 +285,7 @@ static int berlin2_adc_probe(struct platform_device *pdev)
|
||||||
int irq, tsen_irq;
|
int irq, tsen_irq;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||||
sizeof(struct berlin2_adc_priv));
|
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -301,11 +299,11 @@ static int berlin2_adc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
irq = platform_get_irq_byname(pdev, "adc");
|
irq = platform_get_irq_byname(pdev, "adc");
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
return -ENODEV;
|
return irq;
|
||||||
|
|
||||||
tsen_irq = platform_get_irq_byname(pdev, "tsen");
|
tsen_irq = platform_get_irq_byname(pdev, "tsen");
|
||||||
if (tsen_irq < 0)
|
if (tsen_irq < 0)
|
||||||
return -ENODEV;
|
return tsen_irq;
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
|
ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
|
||||||
pdev->dev.driver->name, indio_dev);
|
pdev->dev.driver->name, indio_dev);
|
||||||
|
@ -325,8 +323,8 @@ static int berlin2_adc_probe(struct platform_device *pdev)
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
indio_dev->info = &berlin2_adc_info;
|
indio_dev->info = &berlin2_adc_info;
|
||||||
|
|
||||||
indio_dev->num_channels = BERLIN2_N_CHANNELS;
|
|
||||||
indio_dev->channels = berlin2_adc_channels;
|
indio_dev->channels = berlin2_adc_channels;
|
||||||
|
indio_dev->num_channels = ARRAY_SIZE(berlin2_adc_channels);
|
||||||
|
|
||||||
/* Power up the ADC */
|
/* Power up the ADC */
|
||||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||||
|
|
|
@ -62,6 +62,7 @@ struct cc10001_adc_device {
|
||||||
struct regulator *reg;
|
struct regulator *reg;
|
||||||
u16 *buf;
|
u16 *buf;
|
||||||
|
|
||||||
|
bool shared;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
unsigned int start_delay_ns;
|
unsigned int start_delay_ns;
|
||||||
unsigned int eoc_delay_ns;
|
unsigned int eoc_delay_ns;
|
||||||
|
@ -153,7 +154,8 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
|
||||||
|
|
||||||
mutex_lock(&adc_dev->lock);
|
mutex_lock(&adc_dev->lock);
|
||||||
|
|
||||||
cc10001_adc_power_up(adc_dev);
|
if (!adc_dev->shared)
|
||||||
|
cc10001_adc_power_up(adc_dev);
|
||||||
|
|
||||||
/* Calculate delay step for eoc and sampled data */
|
/* Calculate delay step for eoc and sampled data */
|
||||||
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
|
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
|
||||||
|
@ -177,7 +179,8 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
cc10001_adc_power_down(adc_dev);
|
if (!adc_dev->shared)
|
||||||
|
cc10001_adc_power_down(adc_dev);
|
||||||
|
|
||||||
mutex_unlock(&adc_dev->lock);
|
mutex_unlock(&adc_dev->lock);
|
||||||
|
|
||||||
|
@ -196,7 +199,8 @@ static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
|
||||||
unsigned int delay_ns;
|
unsigned int delay_ns;
|
||||||
u16 val;
|
u16 val;
|
||||||
|
|
||||||
cc10001_adc_power_up(adc_dev);
|
if (!adc_dev->shared)
|
||||||
|
cc10001_adc_power_up(adc_dev);
|
||||||
|
|
||||||
/* Calculate delay step for eoc and sampled data */
|
/* Calculate delay step for eoc and sampled data */
|
||||||
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
|
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
|
||||||
|
@ -205,7 +209,8 @@ static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
|
||||||
|
|
||||||
val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
|
val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
|
||||||
|
|
||||||
cc10001_adc_power_down(adc_dev);
|
if (!adc_dev->shared)
|
||||||
|
cc10001_adc_power_down(adc_dev);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -322,8 +327,10 @@ static int cc10001_adc_probe(struct platform_device *pdev)
|
||||||
adc_dev = iio_priv(indio_dev);
|
adc_dev = iio_priv(indio_dev);
|
||||||
|
|
||||||
channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
|
channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
|
||||||
if (!of_property_read_u32(node, "adc-reserved-channels", &ret))
|
if (!of_property_read_u32(node, "adc-reserved-channels", &ret)) {
|
||||||
|
adc_dev->shared = true;
|
||||||
channel_map &= ~ret;
|
channel_map &= ~ret;
|
||||||
|
}
|
||||||
|
|
||||||
adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
|
adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
|
||||||
if (IS_ERR(adc_dev->reg))
|
if (IS_ERR(adc_dev->reg))
|
||||||
|
@ -368,6 +375,14 @@ static int cc10001_adc_probe(struct platform_device *pdev)
|
||||||
adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
|
adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
|
||||||
adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
|
adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is only one register to power-up/power-down the AUX ADC.
|
||||||
|
* If the ADC is shared among multiple CPUs, always power it up here.
|
||||||
|
* If the ADC is used only by the MIPS, power-up/power-down at runtime.
|
||||||
|
*/
|
||||||
|
if (adc_dev->shared)
|
||||||
|
cc10001_adc_power_up(adc_dev);
|
||||||
|
|
||||||
/* Setup the ADC channels available on the device */
|
/* Setup the ADC channels available on the device */
|
||||||
ret = cc10001_adc_channel_init(indio_dev, channel_map);
|
ret = cc10001_adc_channel_init(indio_dev, channel_map);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -402,6 +417,7 @@ static int cc10001_adc_remove(struct platform_device *pdev)
|
||||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||||
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
cc10001_adc_power_down(adc_dev);
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
iio_triggered_buffer_cleanup(indio_dev);
|
iio_triggered_buffer_cleanup(indio_dev);
|
||||||
clk_disable_unprepare(adc_dev->adc_clk);
|
clk_disable_unprepare(adc_dev->adc_clk);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
|
* http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
|
||||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
|
* http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
|
||||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
|
* http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
|
||||||
|
* http://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf mcp3301
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
@ -47,6 +48,7 @@ enum {
|
||||||
mcp3202,
|
mcp3202,
|
||||||
mcp3204,
|
mcp3204,
|
||||||
mcp3208,
|
mcp3208,
|
||||||
|
mcp3301,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mcp320x_chip_info {
|
struct mcp320x_chip_info {
|
||||||
|
@ -76,6 +78,7 @@ static int mcp320x_channel_to_tx_data(int device_index,
|
||||||
switch (device_index) {
|
switch (device_index) {
|
||||||
case mcp3001:
|
case mcp3001:
|
||||||
case mcp3201:
|
case mcp3201:
|
||||||
|
case mcp3301:
|
||||||
return 0;
|
return 0;
|
||||||
case mcp3002:
|
case mcp3002:
|
||||||
case mcp3202:
|
case mcp3202:
|
||||||
|
@ -102,7 +105,7 @@ static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
|
||||||
adc->tx_buf = mcp320x_channel_to_tx_data(device_index,
|
adc->tx_buf = mcp320x_channel_to_tx_data(device_index,
|
||||||
channel, differential);
|
channel, differential);
|
||||||
|
|
||||||
if (device_index != mcp3001 && device_index != mcp3201) {
|
if (device_index != mcp3001 && device_index != mcp3201 && device_index != mcp3301) {
|
||||||
ret = spi_sync(adc->spi, &adc->msg);
|
ret = spi_sync(adc->spi, &adc->msg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -125,6 +128,8 @@ static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
|
||||||
case mcp3204:
|
case mcp3204:
|
||||||
case mcp3208:
|
case mcp3208:
|
||||||
return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
|
return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
|
||||||
|
case mcp3301:
|
||||||
|
return sign_extend32((adc->rx_buf[0] & 0x1f) << 8 | adc->rx_buf[1], 12);
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -274,6 +279,11 @@ static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
|
||||||
.num_channels = ARRAY_SIZE(mcp3208_channels),
|
.num_channels = ARRAY_SIZE(mcp3208_channels),
|
||||||
.resolution = 12
|
.resolution = 12
|
||||||
},
|
},
|
||||||
|
[mcp3301] = {
|
||||||
|
.channels = mcp3201_channels,
|
||||||
|
.num_channels = ARRAY_SIZE(mcp3201_channels),
|
||||||
|
.resolution = 13
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mcp320x_probe(struct spi_device *spi)
|
static int mcp320x_probe(struct spi_device *spi)
|
||||||
|
@ -368,6 +378,9 @@ static const struct of_device_id mcp320x_dt_ids[] = {
|
||||||
}, {
|
}, {
|
||||||
.compatible = "mcp3208",
|
.compatible = "mcp3208",
|
||||||
.data = &mcp320x_chip_infos[mcp3208],
|
.data = &mcp320x_chip_infos[mcp3208],
|
||||||
|
}, {
|
||||||
|
.compatible = "mcp3301",
|
||||||
|
.data = &mcp320x_chip_infos[mcp3301],
|
||||||
}, {
|
}, {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -383,6 +396,7 @@ static const struct spi_device_id mcp320x_id[] = {
|
||||||
{ "mcp3202", mcp3202 },
|
{ "mcp3202", mcp3202 },
|
||||||
{ "mcp3204", mcp3204 },
|
{ "mcp3204", mcp3204 },
|
||||||
{ "mcp3208", mcp3208 },
|
{ "mcp3208", mcp3208 },
|
||||||
|
{ "mcp3301", mcp3301 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, mcp320x_id);
|
MODULE_DEVICE_TABLE(spi, mcp320x_id);
|
||||||
|
|
|
@ -404,7 +404,6 @@ MODULE_DEVICE_TABLE(of, mcp3422_of_match);
|
||||||
static struct i2c_driver mcp3422_driver = {
|
static struct i2c_driver mcp3422_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mcp3422",
|
.name = "mcp3422",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.of_match_table = of_match_ptr(mcp3422_of_match),
|
.of_match_table = of_match_ptr(mcp3422_of_match),
|
||||||
},
|
},
|
||||||
.probe = mcp3422_probe,
|
.probe = mcp3422_probe,
|
||||||
|
|
|
@ -140,7 +140,6 @@ MODULE_DEVICE_TABLE(of, adc081c_of_match);
|
||||||
static struct i2c_driver adc081c_driver = {
|
static struct i2c_driver adc081c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "adc081c",
|
.name = "adc081c",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.of_match_table = of_match_ptr(adc081c_of_match),
|
.of_match_table = of_match_ptr(adc081c_of_match),
|
||||||
},
|
},
|
||||||
.probe = adc081c_probe,
|
.probe = adc081c_probe,
|
||||||
|
|
|
@ -68,6 +68,9 @@
|
||||||
#define VF610_ADC_CLK_DIV8 0x60
|
#define VF610_ADC_CLK_DIV8 0x60
|
||||||
#define VF610_ADC_CLK_MASK 0x60
|
#define VF610_ADC_CLK_MASK 0x60
|
||||||
#define VF610_ADC_ADLSMP_LONG 0x10
|
#define VF610_ADC_ADLSMP_LONG 0x10
|
||||||
|
#define VF610_ADC_ADSTS_SHORT 0x100
|
||||||
|
#define VF610_ADC_ADSTS_NORMAL 0x200
|
||||||
|
#define VF610_ADC_ADSTS_LONG 0x300
|
||||||
#define VF610_ADC_ADSTS_MASK 0x300
|
#define VF610_ADC_ADSTS_MASK 0x300
|
||||||
#define VF610_ADC_ADLPC_EN 0x80
|
#define VF610_ADC_ADLPC_EN 0x80
|
||||||
#define VF610_ADC_ADHSC_EN 0x400
|
#define VF610_ADC_ADHSC_EN 0x400
|
||||||
|
@ -98,6 +101,8 @@
|
||||||
#define VF610_ADC_CALF 0x2
|
#define VF610_ADC_CALF 0x2
|
||||||
#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
|
#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
|
||||||
|
|
||||||
|
#define DEFAULT_SAMPLE_TIME 1000
|
||||||
|
|
||||||
enum clk_sel {
|
enum clk_sel {
|
||||||
VF610_ADCIOC_BUSCLK_SET,
|
VF610_ADCIOC_BUSCLK_SET,
|
||||||
VF610_ADCIOC_ALTCLK_SET,
|
VF610_ADCIOC_ALTCLK_SET,
|
||||||
|
@ -124,6 +129,17 @@ enum conversion_mode_sel {
|
||||||
VF610_ADC_CONV_LOW_POWER,
|
VF610_ADC_CONV_LOW_POWER,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum lst_adder_sel {
|
||||||
|
VF610_ADCK_CYCLES_3,
|
||||||
|
VF610_ADCK_CYCLES_5,
|
||||||
|
VF610_ADCK_CYCLES_7,
|
||||||
|
VF610_ADCK_CYCLES_9,
|
||||||
|
VF610_ADCK_CYCLES_13,
|
||||||
|
VF610_ADCK_CYCLES_17,
|
||||||
|
VF610_ADCK_CYCLES_21,
|
||||||
|
VF610_ADCK_CYCLES_25,
|
||||||
|
};
|
||||||
|
|
||||||
struct vf610_adc_feature {
|
struct vf610_adc_feature {
|
||||||
enum clk_sel clk_sel;
|
enum clk_sel clk_sel;
|
||||||
enum vol_ref vol_ref;
|
enum vol_ref vol_ref;
|
||||||
|
@ -132,6 +148,8 @@ struct vf610_adc_feature {
|
||||||
int clk_div;
|
int clk_div;
|
||||||
int sample_rate;
|
int sample_rate;
|
||||||
int res_mode;
|
int res_mode;
|
||||||
|
u32 lst_adder_index;
|
||||||
|
u32 default_sample_time;
|
||||||
|
|
||||||
bool calibration;
|
bool calibration;
|
||||||
bool ovwren;
|
bool ovwren;
|
||||||
|
@ -155,11 +173,13 @@ struct vf610_adc {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
|
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
|
||||||
|
static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 };
|
||||||
|
|
||||||
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
|
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
|
||||||
{
|
{
|
||||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||||
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
|
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
|
||||||
|
u32 adck_period, lst_addr_min;
|
||||||
int divisor, i;
|
int divisor, i;
|
||||||
|
|
||||||
adck_rate = info->max_adck_rate[adc_feature->conv_mode];
|
adck_rate = info->max_adck_rate[adc_feature->conv_mode];
|
||||||
|
@ -173,6 +193,19 @@ static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
|
||||||
adc_feature->clk_div = 8;
|
adc_feature->clk_div = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the long sample time adder value to be used based
|
||||||
|
* on the default minimum sample time provided.
|
||||||
|
*/
|
||||||
|
adck_period = NSEC_PER_SEC / adck_rate;
|
||||||
|
lst_addr_min = adc_feature->default_sample_time / adck_period;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(vf610_lst_adder); i++) {
|
||||||
|
if (vf610_lst_adder[i] > lst_addr_min) {
|
||||||
|
adc_feature->lst_adder_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate ADC sample frequencies
|
* Calculate ADC sample frequencies
|
||||||
* Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
|
* Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
|
||||||
|
@ -182,12 +215,13 @@ static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
|
||||||
* SFCAdder: fixed to 6 ADCK cycles
|
* SFCAdder: fixed to 6 ADCK cycles
|
||||||
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
|
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
|
||||||
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
|
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
|
||||||
* LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
|
* LSTAdder(Long Sample Time): 3, 5, 7, 9, 13, 17, 21, 25 ADCK cycles
|
||||||
*/
|
*/
|
||||||
adck_rate = ipg_rate / info->adc_feature.clk_div;
|
adck_rate = ipg_rate / info->adc_feature.clk_div;
|
||||||
for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
|
for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
|
||||||
info->sample_freq_avail[i] =
|
info->sample_freq_avail[i] =
|
||||||
adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
|
adck_rate / (6 + vf610_hw_avgs[i] *
|
||||||
|
(25 + vf610_lst_adder[adc_feature->lst_adder_index]));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
|
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
|
||||||
|
@ -347,8 +381,40 @@ static void vf610_adc_sample_set(struct vf610_adc *info)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use the short sample mode */
|
/*
|
||||||
cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK);
|
* Set ADLSMP and ADSTS based on the Long Sample Time Adder value
|
||||||
|
* determined.
|
||||||
|
*/
|
||||||
|
switch (adc_feature->lst_adder_index) {
|
||||||
|
case VF610_ADCK_CYCLES_3:
|
||||||
|
break;
|
||||||
|
case VF610_ADCK_CYCLES_5:
|
||||||
|
cfg_data |= VF610_ADC_ADSTS_SHORT;
|
||||||
|
break;
|
||||||
|
case VF610_ADCK_CYCLES_7:
|
||||||
|
cfg_data |= VF610_ADC_ADSTS_NORMAL;
|
||||||
|
break;
|
||||||
|
case VF610_ADCK_CYCLES_9:
|
||||||
|
cfg_data |= VF610_ADC_ADSTS_LONG;
|
||||||
|
break;
|
||||||
|
case VF610_ADCK_CYCLES_13:
|
||||||
|
cfg_data |= VF610_ADC_ADLSMP_LONG;
|
||||||
|
break;
|
||||||
|
case VF610_ADCK_CYCLES_17:
|
||||||
|
cfg_data |= VF610_ADC_ADLSMP_LONG;
|
||||||
|
cfg_data |= VF610_ADC_ADSTS_SHORT;
|
||||||
|
break;
|
||||||
|
case VF610_ADCK_CYCLES_21:
|
||||||
|
cfg_data |= VF610_ADC_ADLSMP_LONG;
|
||||||
|
cfg_data |= VF610_ADC_ADSTS_NORMAL;
|
||||||
|
break;
|
||||||
|
case VF610_ADCK_CYCLES_25:
|
||||||
|
cfg_data |= VF610_ADC_ADLSMP_LONG;
|
||||||
|
cfg_data |= VF610_ADC_ADSTS_NORMAL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(info->dev, "error in sample time select\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* update hardware average selection */
|
/* update hardware average selection */
|
||||||
cfg_data &= ~VF610_ADC_AVGS_MASK;
|
cfg_data &= ~VF610_ADC_AVGS_MASK;
|
||||||
|
@ -713,6 +779,11 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||||
of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency",
|
of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency",
|
||||||
info->max_adck_rate, 3);
|
info->max_adck_rate, 3);
|
||||||
|
|
||||||
|
ret = of_property_read_u32(pdev->dev.of_node, "min-sample-time",
|
||||||
|
&info->adc_feature.default_sample_time);
|
||||||
|
if (ret)
|
||||||
|
info->adc_feature.default_sample_time = DEFAULT_SAMPLE_TIME;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, indio_dev);
|
platform_set_drvdata(pdev, indio_dev);
|
||||||
|
|
||||||
init_completion(&info->completion);
|
init_completion(&info->completion);
|
||||||
|
|
|
@ -700,7 +700,6 @@ static struct spi_driver ssp_driver = {
|
||||||
.remove = ssp_remove,
|
.remove = ssp_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.pm = &ssp_pm_ops,
|
.pm = &ssp_pm_ops,
|
||||||
.bus = &spi_bus_type,
|
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = of_match_ptr(ssp_of_match),
|
.of_match_table = of_match_ptr(ssp_of_match),
|
||||||
.name = "sensorhub"
|
.name = "sensorhub"
|
||||||
|
|
|
@ -126,6 +126,9 @@ static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs)
|
||||||
int err, i = 0;
|
int err, i = 0;
|
||||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
if (sdata->sensor_settings->fs.addr == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
err = st_sensors_match_fs(sdata->sensor_settings, fs, &i);
|
err = st_sensors_match_fs(sdata->sensor_settings, fs, &i);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto st_accel_set_fullscale_error;
|
goto st_accel_set_fullscale_error;
|
||||||
|
@ -479,46 +482,43 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||||
int num_sensors_list,
|
int num_sensors_list,
|
||||||
const struct st_sensor_settings *sensor_settings)
|
const struct st_sensor_settings *sensor_settings)
|
||||||
{
|
{
|
||||||
u8 wai;
|
|
||||||
int i, n, err;
|
int i, n, err;
|
||||||
|
u8 wai;
|
||||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
for (i = 0; i < num_sensors_list; i++) {
|
||||||
|
for (n = 0; n < ST_SENSORS_MAX_4WAI; n++) {
|
||||||
|
if (strcmp(indio_dev->name,
|
||||||
|
sensor_settings[i].sensors_supported[n]) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n < ST_SENSORS_MAX_4WAI)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == num_sensors_list) {
|
||||||
|
dev_err(&indio_dev->dev, "device name %s not recognized.\n",
|
||||||
|
indio_dev->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||||
ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai);
|
sensor_settings[i].wai_addr, &wai);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
|
dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
|
||||||
goto read_wai_error;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num_sensors_list; i++) {
|
if (sensor_settings[i].wai != wai) {
|
||||||
if (sensor_settings[i].wai == wai)
|
dev_err(&indio_dev->dev, "%s: WhoAmI mismatch (0x%x).\n",
|
||||||
break;
|
indio_dev->name, wai);
|
||||||
}
|
return -EINVAL;
|
||||||
if (i == num_sensors_list)
|
|
||||||
goto device_not_supported;
|
|
||||||
|
|
||||||
for (n = 0; n < ARRAY_SIZE(sensor_settings[i].sensors_supported); n++) {
|
|
||||||
if (strcmp(indio_dev->name,
|
|
||||||
&sensor_settings[i].sensors_supported[n][0]) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
|
|
||||||
dev_err(&indio_dev->dev, "device name \"%s\" and WhoAmI (0x%02x) mismatch",
|
|
||||||
indio_dev->name, wai);
|
|
||||||
goto sensor_name_mismatch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sdata->sensor_settings =
|
sdata->sensor_settings =
|
||||||
(struct st_sensor_settings *)&sensor_settings[i];
|
(struct st_sensor_settings *)&sensor_settings[i];
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
device_not_supported:
|
|
||||||
dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai);
|
|
||||||
sensor_name_mismatch:
|
|
||||||
err = -ENODEV;
|
|
||||||
read_wai_error:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(st_sensors_check_device_support);
|
EXPORT_SYMBOL(st_sensors_check_device_support);
|
||||||
|
|
||||||
|
|
|
@ -630,7 +630,6 @@ MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);
|
||||||
static struct i2c_driver ad5064_i2c_driver = {
|
static struct i2c_driver ad5064_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ad5064",
|
.name = "ad5064",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = ad5064_i2c_probe,
|
.probe = ad5064_i2c_probe,
|
||||||
.remove = ad5064_i2c_remove,
|
.remove = ad5064_i2c_remove,
|
||||||
|
|
|
@ -593,7 +593,6 @@ MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids);
|
||||||
static struct i2c_driver ad5380_i2c_driver = {
|
static struct i2c_driver ad5380_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ad5380",
|
.name = "ad5380",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = ad5380_i2c_probe,
|
.probe = ad5380_i2c_probe,
|
||||||
.remove = ad5380_i2c_remove,
|
.remove = ad5380_i2c_remove,
|
||||||
|
|
|
@ -569,7 +569,6 @@ MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids);
|
||||||
static struct i2c_driver ad5446_i2c_driver = {
|
static struct i2c_driver ad5446_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ad5446",
|
.name = "ad5446",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = ad5446_i2c_probe,
|
.probe = ad5446_i2c_probe,
|
||||||
.remove = ad5446_i2c_remove,
|
.remove = ad5446_i2c_remove,
|
||||||
|
|
|
@ -392,7 +392,6 @@ static struct i2c_driver max5821_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "max5821",
|
.name = "max5821",
|
||||||
.pm = MAX5821_PM_OPS,
|
.pm = MAX5821_PM_OPS,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = max5821_probe,
|
.probe = max5821_probe,
|
||||||
.remove = max5821_remove,
|
.remove = max5821_remove,
|
||||||
|
|
|
@ -72,7 +72,6 @@ static int adf4350_sync_config(struct adf4350_state *st)
|
||||||
for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) {
|
for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) {
|
||||||
if ((st->regs_hw[i] != st->regs[i]) ||
|
if ((st->regs_hw[i] != st->regs[i]) ||
|
||||||
((i == ADF4350_REG0) && doublebuf)) {
|
((i == ADF4350_REG0) && doublebuf)) {
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case ADF4350_REG1:
|
case ADF4350_REG1:
|
||||||
case ADF4350_REG4:
|
case ADF4350_REG4:
|
||||||
|
|
|
@ -53,7 +53,8 @@ config ADXRS450
|
||||||
config BMG160
|
config BMG160
|
||||||
tristate "BOSCH BMG160 Gyro Sensor"
|
tristate "BOSCH BMG160 Gyro Sensor"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
select IIO_TRIGGERED_BUFFER if IIO_BUFFER
|
select IIO_BUFFER
|
||||||
|
select IIO_TRIGGERED_BUFFER
|
||||||
help
|
help
|
||||||
Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor
|
Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor
|
||||||
driver. This driver also supports BMI055 gyroscope.
|
driver. This driver also supports BMI055 gyroscope.
|
||||||
|
|
|
@ -473,6 +473,7 @@ enum adis16136_id {
|
||||||
ID_ADIS16133,
|
ID_ADIS16133,
|
||||||
ID_ADIS16135,
|
ID_ADIS16135,
|
||||||
ID_ADIS16136,
|
ID_ADIS16136,
|
||||||
|
ID_ADIS16137,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct adis16136_chip_info adis16136_chip_info[] = {
|
static const struct adis16136_chip_info adis16136_chip_info[] = {
|
||||||
|
@ -488,6 +489,10 @@ static const struct adis16136_chip_info adis16136_chip_info[] = {
|
||||||
.precision = IIO_DEGREE_TO_RAD(450),
|
.precision = IIO_DEGREE_TO_RAD(450),
|
||||||
.fullscale = 24623,
|
.fullscale = 24623,
|
||||||
},
|
},
|
||||||
|
[ID_ADIS16137] = {
|
||||||
|
.precision = IIO_DEGREE_TO_RAD(1000),
|
||||||
|
.fullscale = 24609,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int adis16136_probe(struct spi_device *spi)
|
static int adis16136_probe(struct spi_device *spi)
|
||||||
|
@ -557,6 +562,7 @@ static const struct spi_device_id adis16136_ids[] = {
|
||||||
{ "adis16133", ID_ADIS16133 },
|
{ "adis16133", ID_ADIS16133 },
|
||||||
{ "adis16135", ID_ADIS16135 },
|
{ "adis16135", ID_ADIS16135 },
|
||||||
{ "adis16136", ID_ADIS16136 },
|
{ "adis16136", ID_ADIS16136 },
|
||||||
|
{ "adis16137", ID_ADIS16137 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, adis16136_ids);
|
MODULE_DEVICE_TABLE(spi, adis16136_ids);
|
||||||
|
|
|
@ -101,19 +101,24 @@
|
||||||
#define ADIS16260_SCAN_TEMP 3
|
#define ADIS16260_SCAN_TEMP 3
|
||||||
#define ADIS16260_SCAN_ANGL 4
|
#define ADIS16260_SCAN_ANGL 4
|
||||||
|
|
||||||
/* Power down the device */
|
struct adis16260_chip_info {
|
||||||
static int adis16260_stop_device(struct iio_dev *indio_dev)
|
unsigned int gyro_max_val;
|
||||||
{
|
unsigned int gyro_max_scale;
|
||||||
struct adis *adis = iio_priv(indio_dev);
|
const struct iio_chan_spec *channels;
|
||||||
int ret;
|
unsigned int num_channels;
|
||||||
u16 val = ADIS16260_SLP_CNT_POWER_OFF;
|
};
|
||||||
|
|
||||||
ret = adis_write_reg_16(adis, ADIS16260_SLP_CNT, val);
|
struct adis16260 {
|
||||||
if (ret)
|
const struct adis16260_chip_info *info;
|
||||||
dev_err(&indio_dev->dev, "problem with turning device off: SLP_CNT");
|
|
||||||
|
|
||||||
return ret;
|
struct adis adis;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
enum adis16260_type {
|
||||||
|
ADIS16251,
|
||||||
|
ADIS16260,
|
||||||
|
ADIS16266,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct iio_chan_spec adis16260_channels[] = {
|
static const struct iio_chan_spec adis16260_channels[] = {
|
||||||
ADIS_GYRO_CHAN(X, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO,
|
ADIS_GYRO_CHAN(X, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO,
|
||||||
|
@ -131,6 +136,55 @@ static const struct iio_chan_spec adis16260_channels[] = {
|
||||||
IIO_CHAN_SOFT_TIMESTAMP(5),
|
IIO_CHAN_SOFT_TIMESTAMP(5),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct iio_chan_spec adis16266_channels[] = {
|
||||||
|
ADIS_GYRO_CHAN(X, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO,
|
||||||
|
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||||
|
BIT(IIO_CHAN_INFO_CALIBSCALE),
|
||||||
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), 14),
|
||||||
|
ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP,
|
||||||
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||||
|
ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY,
|
||||||
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||||
|
ADIS_AUX_ADC_CHAN(ADIS16260_AUX_ADC, ADIS16260_SCAN_AUX_ADC,
|
||||||
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct adis16260_chip_info adis16260_chip_info_table[] = {
|
||||||
|
[ADIS16251] = {
|
||||||
|
.gyro_max_scale = 80,
|
||||||
|
.gyro_max_val = IIO_RAD_TO_DEGREE(4368),
|
||||||
|
.channels = adis16260_channels,
|
||||||
|
.num_channels = ARRAY_SIZE(adis16260_channels),
|
||||||
|
},
|
||||||
|
[ADIS16260] = {
|
||||||
|
.gyro_max_scale = 320,
|
||||||
|
.gyro_max_val = IIO_RAD_TO_DEGREE(4368),
|
||||||
|
.channels = adis16260_channels,
|
||||||
|
.num_channels = ARRAY_SIZE(adis16260_channels),
|
||||||
|
},
|
||||||
|
[ADIS16266] = {
|
||||||
|
.gyro_max_scale = 14000,
|
||||||
|
.gyro_max_val = IIO_RAD_TO_DEGREE(3357),
|
||||||
|
.channels = adis16266_channels,
|
||||||
|
.num_channels = ARRAY_SIZE(adis16266_channels),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Power down the device */
|
||||||
|
static int adis16260_stop_device(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct adis16260 *adis16260 = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
u16 val = ADIS16260_SLP_CNT_POWER_OFF;
|
||||||
|
|
||||||
|
ret = adis_write_reg_16(&adis16260->adis, ADIS16260_SLP_CNT, val);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&indio_dev->dev, "problem with turning device off: SLP_CNT");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static const u8 adis16260_addresses[][2] = {
|
static const u8 adis16260_addresses[][2] = {
|
||||||
[ADIS16260_SCAN_GYRO] = { ADIS16260_GYRO_OFF, ADIS16260_GYRO_SCALE },
|
[ADIS16260_SCAN_GYRO] = { ADIS16260_GYRO_OFF, ADIS16260_GYRO_SCALE },
|
||||||
};
|
};
|
||||||
|
@ -140,7 +194,9 @@ static int adis16260_read_raw(struct iio_dev *indio_dev,
|
||||||
int *val, int *val2,
|
int *val, int *val2,
|
||||||
long mask)
|
long mask)
|
||||||
{
|
{
|
||||||
struct adis *adis = iio_priv(indio_dev);
|
struct adis16260 *adis16260 = iio_priv(indio_dev);
|
||||||
|
const struct adis16260_chip_info *info = adis16260->info;
|
||||||
|
struct adis *adis = &adis16260->adis;
|
||||||
int ret;
|
int ret;
|
||||||
u8 addr;
|
u8 addr;
|
||||||
s16 val16;
|
s16 val16;
|
||||||
|
@ -152,15 +208,9 @@ static int adis16260_read_raw(struct iio_dev *indio_dev,
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
case IIO_ANGL_VEL:
|
case IIO_ANGL_VEL:
|
||||||
*val = 0;
|
*val = info->gyro_max_scale;
|
||||||
if (spi_get_device_id(adis->spi)->driver_data) {
|
*val2 = info->gyro_max_val;
|
||||||
/* 0.01832 degree / sec */
|
return IIO_VAL_FRACTIONAL;
|
||||||
*val2 = IIO_DEGREE_TO_RAD(18320);
|
|
||||||
} else {
|
|
||||||
/* 0.07326 degree / sec */
|
|
||||||
*val2 = IIO_DEGREE_TO_RAD(73260);
|
|
||||||
}
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
|
||||||
case IIO_INCLI:
|
case IIO_INCLI:
|
||||||
*val = 0;
|
*val = 0;
|
||||||
*val2 = IIO_DEGREE_TO_RAD(36630);
|
*val2 = IIO_DEGREE_TO_RAD(36630);
|
||||||
|
@ -224,7 +274,8 @@ static int adis16260_write_raw(struct iio_dev *indio_dev,
|
||||||
int val2,
|
int val2,
|
||||||
long mask)
|
long mask)
|
||||||
{
|
{
|
||||||
struct adis *adis = iio_priv(indio_dev);
|
struct adis16260 *adis16260 = iio_priv(indio_dev);
|
||||||
|
struct adis *adis = &adis16260->adis;
|
||||||
int ret;
|
int ret;
|
||||||
u8 addr;
|
u8 addr;
|
||||||
u8 t;
|
u8 t;
|
||||||
|
@ -305,35 +356,42 @@ static const struct adis_data adis16260_data = {
|
||||||
|
|
||||||
static int adis16260_probe(struct spi_device *spi)
|
static int adis16260_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
|
const struct spi_device_id *id;
|
||||||
|
struct adis16260 *adis16260;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
struct adis *adis;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
id = spi_get_device_id(spi);
|
||||||
|
if (!id)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
/* setup the industrialio driver allocated elements */
|
/* setup the industrialio driver allocated elements */
|
||||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis));
|
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis16260));
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
adis = iio_priv(indio_dev);
|
adis16260 = iio_priv(indio_dev);
|
||||||
/* this is only used for removal purposes */
|
/* this is only used for removal purposes */
|
||||||
spi_set_drvdata(spi, indio_dev);
|
spi_set_drvdata(spi, indio_dev);
|
||||||
|
|
||||||
indio_dev->name = spi_get_device_id(spi)->name;
|
adis16260->info = &adis16260_chip_info_table[id->driver_data];
|
||||||
|
|
||||||
|
indio_dev->name = id->name;
|
||||||
indio_dev->dev.parent = &spi->dev;
|
indio_dev->dev.parent = &spi->dev;
|
||||||
indio_dev->info = &adis16260_info;
|
indio_dev->info = &adis16260_info;
|
||||||
indio_dev->channels = adis16260_channels;
|
indio_dev->channels = adis16260->info->channels;
|
||||||
indio_dev->num_channels = ARRAY_SIZE(adis16260_channels);
|
indio_dev->num_channels = adis16260->info->num_channels;
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
|
|
||||||
ret = adis_init(adis, indio_dev, spi, &adis16260_data);
|
ret = adis_init(&adis16260->adis, indio_dev, spi, &adis16260_data);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = adis_setup_buffer_and_trigger(adis, indio_dev, NULL);
|
ret = adis_setup_buffer_and_trigger(&adis16260->adis, indio_dev, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Get the device into a sane initial state */
|
/* Get the device into a sane initial state */
|
||||||
ret = adis_initial_startup(adis);
|
ret = adis_initial_startup(&adis16260->adis);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_cleanup_buffer_trigger;
|
goto error_cleanup_buffer_trigger;
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
|
@ -343,18 +401,18 @@ static int adis16260_probe(struct spi_device *spi)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_cleanup_buffer_trigger:
|
error_cleanup_buffer_trigger:
|
||||||
adis_cleanup_buffer_and_trigger(adis, indio_dev);
|
adis_cleanup_buffer_and_trigger(&adis16260->adis, indio_dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adis16260_remove(struct spi_device *spi)
|
static int adis16260_remove(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||||
struct adis *adis = iio_priv(indio_dev);
|
struct adis16260 *adis16260 = iio_priv(indio_dev);
|
||||||
|
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
adis16260_stop_device(indio_dev);
|
adis16260_stop_device(indio_dev);
|
||||||
adis_cleanup_buffer_and_trigger(adis, indio_dev);
|
adis_cleanup_buffer_and_trigger(&adis16260->adis, indio_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -364,11 +422,12 @@ static int adis16260_remove(struct spi_device *spi)
|
||||||
* support for the on chip filtering.
|
* support for the on chip filtering.
|
||||||
*/
|
*/
|
||||||
static const struct spi_device_id adis16260_id[] = {
|
static const struct spi_device_id adis16260_id[] = {
|
||||||
{"adis16260", 0},
|
{"adis16260", ADIS16260},
|
||||||
{"adis16265", 0},
|
{"adis16265", ADIS16260},
|
||||||
{"adis16250", 0},
|
{"adis16266", ADIS16266},
|
||||||
{"adis16255", 0},
|
{"adis16250", ADIS16260},
|
||||||
{"adis16251", 1},
|
{"adis16255", ADIS16260},
|
||||||
|
{"adis16251", ADIS16251},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, adis16260_id);
|
MODULE_DEVICE_TABLE(spi, adis16260_id);
|
||||||
|
|
|
@ -379,7 +379,6 @@ MODULE_DEVICE_TABLE(i2c, itg3200_id);
|
||||||
|
|
||||||
static struct i2c_driver itg3200_driver = {
|
static struct i2c_driver itg3200_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "itg3200",
|
.name = "itg3200",
|
||||||
.pm = &itg3200_pm_ops,
|
.pm = &itg3200_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -131,6 +131,7 @@ static const struct iio_chan_spec st_gyro_16bit_channels[] = {
|
||||||
static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||||
{
|
{
|
||||||
.wai = ST_GYRO_1_WAI_EXP,
|
.wai = ST_GYRO_1_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = L3G4200D_GYRO_DEV_NAME,
|
[0] = L3G4200D_GYRO_DEV_NAME,
|
||||||
[1] = LSM330DL_GYRO_DEV_NAME,
|
[1] = LSM330DL_GYRO_DEV_NAME,
|
||||||
|
@ -190,6 +191,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_GYRO_2_WAI_EXP,
|
.wai = ST_GYRO_2_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = L3GD20_GYRO_DEV_NAME,
|
[0] = L3GD20_GYRO_DEV_NAME,
|
||||||
[1] = LSM330D_GYRO_DEV_NAME,
|
[1] = LSM330D_GYRO_DEV_NAME,
|
||||||
|
@ -252,6 +254,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_GYRO_3_WAI_EXP,
|
.wai = ST_GYRO_3_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = L3GD20_GYRO_DEV_NAME,
|
[0] = L3GD20_GYRO_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
|
|
@ -99,7 +99,6 @@ MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
|
||||||
|
|
||||||
static struct i2c_driver st_gyro_driver = {
|
static struct i2c_driver st_gyro_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "st-gyro-i2c",
|
.name = "st-gyro-i2c",
|
||||||
.of_match_table = of_match_ptr(st_gyro_of_match),
|
.of_match_table = of_match_ptr(st_gyro_of_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/timekeeping.h>
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
|
|
||||||
|
@ -46,7 +47,8 @@
|
||||||
* Note that when reading the sensor actually 84 edges are detected, but
|
* Note that when reading the sensor actually 84 edges are detected, but
|
||||||
* since the last edge is not significant, we only store 83:
|
* since the last edge is not significant, we only store 83:
|
||||||
*/
|
*/
|
||||||
#define DHT11_EDGES_PER_READ (2*DHT11_BITS_PER_READ + DHT11_EDGES_PREAMBLE + 1)
|
#define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \
|
||||||
|
DHT11_EDGES_PREAMBLE + 1)
|
||||||
|
|
||||||
/* Data transmission timing (nano seconds) */
|
/* Data transmission timing (nano seconds) */
|
||||||
#define DHT11_START_TRANSMISSION 18 /* ms */
|
#define DHT11_START_TRANSMISSION 18 /* ms */
|
||||||
|
@ -62,6 +64,7 @@ struct dht11 {
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
struct completion completion;
|
struct completion completion;
|
||||||
|
/* The iio sysfs interface doesn't prevent concurrent reads: */
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
|
||||||
s64 timestamp;
|
s64 timestamp;
|
||||||
|
@ -87,32 +90,20 @@ static unsigned char dht11_decode_byte(int *timing, int threshold)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dht11_decode(struct dht11 *dht11, int offset)
|
static int dht11_decode(struct dht11 *dht11, int offset, int timeres)
|
||||||
{
|
{
|
||||||
int i, t, timing[DHT11_BITS_PER_READ], threshold,
|
int i, t, timing[DHT11_BITS_PER_READ], threshold;
|
||||||
timeres = DHT11_SENSOR_RESPONSE;
|
|
||||||
unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum;
|
unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum;
|
||||||
|
|
||||||
/* Calculate timestamp resolution */
|
|
||||||
for (i = 1; i < dht11->num_edges; ++i) {
|
|
||||||
t = dht11->edges[i].ts - dht11->edges[i-1].ts;
|
|
||||||
if (t > 0 && t < timeres)
|
|
||||||
timeres = t;
|
|
||||||
}
|
|
||||||
if (2*timeres > DHT11_DATA_BIT_HIGH) {
|
|
||||||
pr_err("dht11: timeresolution %d too bad for decoding\n",
|
|
||||||
timeres);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
threshold = DHT11_DATA_BIT_HIGH / timeres;
|
threshold = DHT11_DATA_BIT_HIGH / timeres;
|
||||||
if (DHT11_DATA_BIT_LOW/timeres + 1 >= threshold)
|
if (DHT11_DATA_BIT_LOW / timeres + 1 >= threshold)
|
||||||
pr_err("dht11: WARNING: decoding ambiguous\n");
|
pr_err("dht11: WARNING: decoding ambiguous\n");
|
||||||
|
|
||||||
/* scale down with timeres and check validity */
|
/* scale down with timeres and check validity */
|
||||||
for (i = 0; i < DHT11_BITS_PER_READ; ++i) {
|
for (i = 0; i < DHT11_BITS_PER_READ; ++i) {
|
||||||
t = dht11->edges[offset + 2*i + 2].ts -
|
t = dht11->edges[offset + 2 * i + 2].ts -
|
||||||
dht11->edges[offset + 2*i + 1].ts;
|
dht11->edges[offset + 2 * i + 1].ts;
|
||||||
if (!dht11->edges[offset + 2*i + 1].value)
|
if (!dht11->edges[offset + 2 * i + 1].value)
|
||||||
return -EIO; /* lost synchronisation */
|
return -EIO; /* lost synchronisation */
|
||||||
timing[i] = t / timeres;
|
timing[i] = t / timeres;
|
||||||
}
|
}
|
||||||
|
@ -126,7 +117,7 @@ static int dht11_decode(struct dht11 *dht11, int offset)
|
||||||
if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum)
|
if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
dht11->timestamp = iio_get_time_ns();
|
dht11->timestamp = ktime_get_real_ns();
|
||||||
if (hum_int < 20) { /* DHT22 */
|
if (hum_int < 20) { /* DHT22 */
|
||||||
dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
|
dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
|
||||||
((temp_int & 0x80) ? -100 : 100);
|
((temp_int & 0x80) ? -100 : 100);
|
||||||
|
@ -154,7 +145,7 @@ static irqreturn_t dht11_handle_irq(int irq, void *data)
|
||||||
|
|
||||||
/* TODO: Consider making the handler safe for IRQ sharing */
|
/* TODO: Consider making the handler safe for IRQ sharing */
|
||||||
if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
|
if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
|
||||||
dht11->edges[dht11->num_edges].ts = iio_get_time_ns();
|
dht11->edges[dht11->num_edges].ts = ktime_get_real_ns();
|
||||||
dht11->edges[dht11->num_edges++].value =
|
dht11->edges[dht11->num_edges++].value =
|
||||||
gpio_get_value(dht11->gpio);
|
gpio_get_value(dht11->gpio);
|
||||||
|
|
||||||
|
@ -166,14 +157,26 @@ static irqreturn_t dht11_handle_irq(int irq, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dht11_read_raw(struct iio_dev *iio_dev,
|
static int dht11_read_raw(struct iio_dev *iio_dev,
|
||||||
const struct iio_chan_spec *chan,
|
const struct iio_chan_spec *chan,
|
||||||
int *val, int *val2, long m)
|
int *val, int *val2, long m)
|
||||||
{
|
{
|
||||||
struct dht11 *dht11 = iio_priv(iio_dev);
|
struct dht11 *dht11 = iio_priv(iio_dev);
|
||||||
int ret;
|
int ret, timeres;
|
||||||
|
|
||||||
mutex_lock(&dht11->lock);
|
mutex_lock(&dht11->lock);
|
||||||
if (dht11->timestamp + DHT11_DATA_VALID_TIME < iio_get_time_ns()) {
|
if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_real_ns()) {
|
||||||
|
timeres = ktime_get_resolution_ns();
|
||||||
|
if (DHT11_DATA_BIT_HIGH < 2 * timeres) {
|
||||||
|
dev_err(dht11->dev, "timeresolution %dns too low\n",
|
||||||
|
timeres);
|
||||||
|
/* In theory a better clock could become available
|
||||||
|
* at some point ... and there is no error code
|
||||||
|
* that really fits better.
|
||||||
|
*/
|
||||||
|
ret = -EAGAIN;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
reinit_completion(&dht11->completion);
|
reinit_completion(&dht11->completion);
|
||||||
|
|
||||||
dht11->num_edges = 0;
|
dht11->num_edges = 0;
|
||||||
|
@ -192,13 +195,13 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = wait_for_completion_killable_timeout(&dht11->completion,
|
ret = wait_for_completion_killable_timeout(&dht11->completion,
|
||||||
HZ);
|
HZ);
|
||||||
|
|
||||||
free_irq(dht11->irq, iio_dev);
|
free_irq(dht11->irq, iio_dev);
|
||||||
|
|
||||||
if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) {
|
if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) {
|
||||||
dev_err(&iio_dev->dev,
|
dev_err(&iio_dev->dev,
|
||||||
"Only %d signal edges detected\n",
|
"Only %d signal edges detected\n",
|
||||||
dht11->num_edges);
|
dht11->num_edges);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
@ -206,9 +209,10 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = dht11_decode(dht11,
|
ret = dht11_decode(dht11,
|
||||||
dht11->num_edges == DHT11_EDGES_PER_READ ?
|
dht11->num_edges == DHT11_EDGES_PER_READ ?
|
||||||
DHT11_EDGES_PREAMBLE :
|
DHT11_EDGES_PREAMBLE :
|
||||||
DHT11_EDGES_PREAMBLE - 2);
|
DHT11_EDGES_PREAMBLE - 2,
|
||||||
|
timeres);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@ -261,9 +265,10 @@ static int dht11_probe(struct platform_device *pdev)
|
||||||
dht11 = iio_priv(iio);
|
dht11 = iio_priv(iio);
|
||||||
dht11->dev = dev;
|
dht11->dev = dev;
|
||||||
|
|
||||||
dht11->gpio = ret = of_get_gpio(node, 0);
|
ret = of_get_gpio(node, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
dht11->gpio = ret;
|
||||||
ret = devm_gpio_request_one(dev, dht11->gpio, GPIOF_IN, pdev->name);
|
ret = devm_gpio_request_one(dev, dht11->gpio, GPIOF_IN, pdev->name);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -274,7 +279,7 @@ static int dht11_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dht11->timestamp = iio_get_time_ns() - DHT11_DATA_VALID_TIME - 1;
|
dht11->timestamp = ktime_get_real_ns() - DHT11_DATA_VALID_TIME - 1;
|
||||||
dht11->num_edges = -1;
|
dht11->num_edges = -1;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, iio);
|
platform_set_drvdata(pdev, iio);
|
||||||
|
|
|
@ -177,7 +177,6 @@ MODULE_DEVICE_TABLE(i2c, si7005_id);
|
||||||
static struct i2c_driver si7005_driver = {
|
static struct i2c_driver si7005_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "si7005",
|
.name = "si7005",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = si7005_probe,
|
.probe = si7005_probe,
|
||||||
.id_table = si7005_id,
|
.id_table = si7005_id,
|
||||||
|
|
|
@ -139,7 +139,9 @@ enum adis16400_chip_variant {
|
||||||
ADIS16360,
|
ADIS16360,
|
||||||
ADIS16362,
|
ADIS16362,
|
||||||
ADIS16364,
|
ADIS16364,
|
||||||
|
ADIS16367,
|
||||||
ADIS16400,
|
ADIS16400,
|
||||||
|
ADIS16445,
|
||||||
ADIS16448,
|
ADIS16448,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -622,6 +624,17 @@ static const struct iio_chan_spec adis16400_channels[] = {
|
||||||
IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
|
IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct iio_chan_spec adis16445_channels[] = {
|
||||||
|
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 16),
|
||||||
|
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 16),
|
||||||
|
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 16),
|
||||||
|
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 16),
|
||||||
|
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 16),
|
||||||
|
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 16),
|
||||||
|
ADIS16400_TEMP_CHAN(ADIS16448_TEMP_OUT, 12),
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
|
||||||
|
};
|
||||||
|
|
||||||
static const struct iio_chan_spec adis16448_channels[] = {
|
static const struct iio_chan_spec adis16448_channels[] = {
|
||||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 16),
|
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 16),
|
||||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 16),
|
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 16),
|
||||||
|
@ -696,7 +709,8 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||||
[ADIS16300] = {
|
[ADIS16300] = {
|
||||||
.channels = adis16300_channels,
|
.channels = adis16300_channels,
|
||||||
.num_channels = ARRAY_SIZE(adis16300_channels),
|
.num_channels = ARRAY_SIZE(adis16300_channels),
|
||||||
.flags = ADIS16400_HAS_SLOW_MODE,
|
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
|
||||||
|
ADIS16400_HAS_SERIAL_NUMBER,
|
||||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||||
.accel_scale_micro = 5884,
|
.accel_scale_micro = 5884,
|
||||||
.temp_scale_nano = 140000000, /* 0.14 C */
|
.temp_scale_nano = 140000000, /* 0.14 C */
|
||||||
|
@ -763,6 +777,18 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
},
|
},
|
||||||
|
[ADIS16367] = {
|
||||||
|
.channels = adis16350_channels,
|
||||||
|
.num_channels = ARRAY_SIZE(adis16350_channels),
|
||||||
|
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
|
||||||
|
ADIS16400_HAS_SERIAL_NUMBER,
|
||||||
|
.gyro_scale_micro = IIO_DEGREE_TO_RAD(2000), /* 0.2 deg/s */
|
||||||
|
.accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
|
||||||
|
.temp_scale_nano = 136000000, /* 0.136 C */
|
||||||
|
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||||
|
.set_freq = adis16400_set_freq,
|
||||||
|
.get_freq = adis16400_get_freq,
|
||||||
|
},
|
||||||
[ADIS16400] = {
|
[ADIS16400] = {
|
||||||
.channels = adis16400_channels,
|
.channels = adis16400_channels,
|
||||||
.num_channels = ARRAY_SIZE(adis16400_channels),
|
.num_channels = ARRAY_SIZE(adis16400_channels),
|
||||||
|
@ -774,13 +800,26 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
},
|
},
|
||||||
|
[ADIS16445] = {
|
||||||
|
.channels = adis16445_channels,
|
||||||
|
.num_channels = ARRAY_SIZE(adis16445_channels),
|
||||||
|
.flags = ADIS16400_HAS_PROD_ID |
|
||||||
|
ADIS16400_HAS_SERIAL_NUMBER |
|
||||||
|
ADIS16400_BURST_DIAG_STAT,
|
||||||
|
.gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
|
||||||
|
.accel_scale_micro = IIO_G_TO_M_S_2(250), /* 1/4000 g */
|
||||||
|
.temp_scale_nano = 73860000, /* 0.07386 C */
|
||||||
|
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||||
|
.set_freq = adis16334_set_freq,
|
||||||
|
.get_freq = adis16334_get_freq,
|
||||||
|
},
|
||||||
[ADIS16448] = {
|
[ADIS16448] = {
|
||||||
.channels = adis16448_channels,
|
.channels = adis16448_channels,
|
||||||
.num_channels = ARRAY_SIZE(adis16448_channels),
|
.num_channels = ARRAY_SIZE(adis16448_channels),
|
||||||
.flags = ADIS16400_HAS_PROD_ID |
|
.flags = ADIS16400_HAS_PROD_ID |
|
||||||
ADIS16400_HAS_SERIAL_NUMBER |
|
ADIS16400_HAS_SERIAL_NUMBER |
|
||||||
ADIS16400_BURST_DIAG_STAT,
|
ADIS16400_BURST_DIAG_STAT,
|
||||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
|
.gyro_scale_micro = IIO_DEGREE_TO_RAD(40000), /* 0.04 deg/s */
|
||||||
.accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */
|
.accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */
|
||||||
.temp_scale_nano = 73860000, /* 0.07386 C */
|
.temp_scale_nano = 73860000, /* 0.07386 C */
|
||||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||||
|
@ -926,6 +965,7 @@ static int adis16400_remove(struct spi_device *spi)
|
||||||
|
|
||||||
static const struct spi_device_id adis16400_id[] = {
|
static const struct spi_device_id adis16400_id[] = {
|
||||||
{"adis16300", ADIS16300},
|
{"adis16300", ADIS16300},
|
||||||
|
{"adis16305", ADIS16300},
|
||||||
{"adis16334", ADIS16334},
|
{"adis16334", ADIS16334},
|
||||||
{"adis16350", ADIS16350},
|
{"adis16350", ADIS16350},
|
||||||
{"adis16354", ADIS16350},
|
{"adis16354", ADIS16350},
|
||||||
|
@ -934,8 +974,10 @@ static const struct spi_device_id adis16400_id[] = {
|
||||||
{"adis16362", ADIS16362},
|
{"adis16362", ADIS16362},
|
||||||
{"adis16364", ADIS16364},
|
{"adis16364", ADIS16364},
|
||||||
{"adis16365", ADIS16360},
|
{"adis16365", ADIS16360},
|
||||||
|
{"adis16367", ADIS16367},
|
||||||
{"adis16400", ADIS16400},
|
{"adis16400", ADIS16400},
|
||||||
{"adis16405", ADIS16400},
|
{"adis16405", ADIS16400},
|
||||||
|
{"adis16445", ADIS16445},
|
||||||
{"adis16448", ADIS16448},
|
{"adis16448", ADIS16448},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -110,6 +110,10 @@
|
||||||
struct adis16480_chip_info {
|
struct adis16480_chip_info {
|
||||||
unsigned int num_channels;
|
unsigned int num_channels;
|
||||||
const struct iio_chan_spec *channels;
|
const struct iio_chan_spec *channels;
|
||||||
|
unsigned int gyro_max_val;
|
||||||
|
unsigned int gyro_max_scale;
|
||||||
|
unsigned int accel_max_val;
|
||||||
|
unsigned int accel_max_scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct adis16480 {
|
struct adis16480 {
|
||||||
|
@ -497,19 +501,21 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
|
||||||
static int adis16480_read_raw(struct iio_dev *indio_dev,
|
static int adis16480_read_raw(struct iio_dev *indio_dev,
|
||||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||||
{
|
{
|
||||||
|
struct adis16480 *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
switch (info) {
|
switch (info) {
|
||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
return adis_single_conversion(indio_dev, chan, 0, val);
|
return adis_single_conversion(indio_dev, chan, 0, val);
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
case IIO_ANGL_VEL:
|
case IIO_ANGL_VEL:
|
||||||
*val = 0;
|
*val = st->chip_info->gyro_max_scale;
|
||||||
*val2 = IIO_DEGREE_TO_RAD(20000); /* 0.02 degree/sec */
|
*val2 = st->chip_info->gyro_max_val;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_FRACTIONAL;
|
||||||
case IIO_ACCEL:
|
case IIO_ACCEL:
|
||||||
*val = 0;
|
*val = st->chip_info->accel_max_scale;
|
||||||
*val2 = IIO_G_TO_M_S_2(800); /* 0.8 mg */
|
*val2 = st->chip_info->accel_max_val;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_FRACTIONAL;
|
||||||
case IIO_MAGN:
|
case IIO_MAGN:
|
||||||
*val = 0;
|
*val = 0;
|
||||||
*val2 = 100; /* 0.0001 gauss */
|
*val2 = 100; /* 0.0001 gauss */
|
||||||
|
@ -674,18 +680,39 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||||
[ADIS16375] = {
|
[ADIS16375] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||||
|
/*
|
||||||
|
* storing the value in rad/degree and the scale in degree
|
||||||
|
* gives us the result in rad and better precession than
|
||||||
|
* storing the scale directly in rad.
|
||||||
|
*/
|
||||||
|
.gyro_max_val = IIO_RAD_TO_DEGREE(22887),
|
||||||
|
.gyro_max_scale = 300,
|
||||||
|
.accel_max_val = IIO_M_S_2_TO_G(21973),
|
||||||
|
.accel_max_scale = 18,
|
||||||
},
|
},
|
||||||
[ADIS16480] = {
|
[ADIS16480] = {
|
||||||
.channels = adis16480_channels,
|
.channels = adis16480_channels,
|
||||||
.num_channels = ARRAY_SIZE(adis16480_channels),
|
.num_channels = ARRAY_SIZE(adis16480_channels),
|
||||||
|
.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
|
||||||
|
.gyro_max_scale = 450,
|
||||||
|
.accel_max_val = IIO_M_S_2_TO_G(12500),
|
||||||
|
.accel_max_scale = 5,
|
||||||
},
|
},
|
||||||
[ADIS16485] = {
|
[ADIS16485] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||||
|
.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
|
||||||
|
.gyro_max_scale = 450,
|
||||||
|
.accel_max_val = IIO_M_S_2_TO_G(20000),
|
||||||
|
.accel_max_scale = 5,
|
||||||
},
|
},
|
||||||
[ADIS16488] = {
|
[ADIS16488] = {
|
||||||
.channels = adis16480_channels,
|
.channels = adis16480_channels,
|
||||||
.num_channels = ARRAY_SIZE(adis16480_channels),
|
.num_channels = ARRAY_SIZE(adis16480_channels),
|
||||||
|
.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
|
||||||
|
.gyro_max_scale = 450,
|
||||||
|
.accel_max_val = IIO_M_S_2_TO_G(22500),
|
||||||
|
.accel_max_scale = 18,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -690,6 +690,10 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||||
|
|
||||||
/* constant IIO attribute */
|
/* constant IIO attribute */
|
||||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
|
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
|
||||||
|
static IIO_CONST_ATTR(in_anglvel_scale_available,
|
||||||
|
"0.000133090 0.000266181 0.000532362 0.001064724");
|
||||||
|
static IIO_CONST_ATTR(in_accel_scale_available,
|
||||||
|
"0.000598 0.001196 0.002392 0.004785");
|
||||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
|
static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
|
||||||
inv_mpu6050_fifo_rate_store);
|
inv_mpu6050_fifo_rate_store);
|
||||||
static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL,
|
static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL,
|
||||||
|
@ -702,6 +706,8 @@ static struct attribute *inv_attributes[] = {
|
||||||
&iio_dev_attr_in_accel_matrix.dev_attr.attr,
|
&iio_dev_attr_in_accel_matrix.dev_attr.attr,
|
||||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||||
|
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||||
|
&iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -921,7 +927,6 @@ static struct i2c_driver inv_mpu_driver = {
|
||||||
.remove = inv_mpu_remove,
|
.remove = inv_mpu_remove,
|
||||||
.id_table = inv_mpu_id,
|
.id_table = inv_mpu_id,
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "inv-mpu6050",
|
.name = "inv-mpu6050",
|
||||||
.pm = INV_MPU6050_PMOPS,
|
.pm = INV_MPU6050_PMOPS,
|
||||||
.acpi_match_table = ACPI_PTR(inv_acpi_match),
|
.acpi_match_table = ACPI_PTR(inv_acpi_match),
|
||||||
|
|
|
@ -1363,7 +1363,7 @@ static int kmx61_probe(struct i2c_client *client,
|
||||||
if (client->irq < 0)
|
if (client->irq < 0)
|
||||||
client->irq = kmx61_gpio_probe(client, data);
|
client->irq = kmx61_gpio_probe(client, data);
|
||||||
|
|
||||||
if (client->irq >= 0) {
|
if (client->irq > 0) {
|
||||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
kmx61_data_rdy_trig_poll,
|
kmx61_data_rdy_trig_poll,
|
||||||
kmx61_event_handler,
|
kmx61_event_handler,
|
||||||
|
@ -1445,10 +1445,10 @@ err_iio_unregister_mag:
|
||||||
err_iio_unregister_acc:
|
err_iio_unregister_acc:
|
||||||
iio_device_unregister(data->acc_indio_dev);
|
iio_device_unregister(data->acc_indio_dev);
|
||||||
err_buffer_cleanup_mag:
|
err_buffer_cleanup_mag:
|
||||||
if (client->irq >= 0)
|
if (client->irq > 0)
|
||||||
iio_triggered_buffer_cleanup(data->mag_indio_dev);
|
iio_triggered_buffer_cleanup(data->mag_indio_dev);
|
||||||
err_buffer_cleanup_acc:
|
err_buffer_cleanup_acc:
|
||||||
if (client->irq >= 0)
|
if (client->irq > 0)
|
||||||
iio_triggered_buffer_cleanup(data->acc_indio_dev);
|
iio_triggered_buffer_cleanup(data->acc_indio_dev);
|
||||||
err_trigger_unregister_motion:
|
err_trigger_unregister_motion:
|
||||||
iio_trigger_unregister(data->motion_trig);
|
iio_trigger_unregister(data->motion_trig);
|
||||||
|
@ -1472,7 +1472,7 @@ static int kmx61_remove(struct i2c_client *client)
|
||||||
iio_device_unregister(data->acc_indio_dev);
|
iio_device_unregister(data->acc_indio_dev);
|
||||||
iio_device_unregister(data->mag_indio_dev);
|
iio_device_unregister(data->mag_indio_dev);
|
||||||
|
|
||||||
if (client->irq >= 0) {
|
if (client->irq > 0) {
|
||||||
iio_triggered_buffer_cleanup(data->acc_indio_dev);
|
iio_triggered_buffer_cleanup(data->acc_indio_dev);
|
||||||
iio_triggered_buffer_cleanup(data->mag_indio_dev);
|
iio_triggered_buffer_cleanup(data->mag_indio_dev);
|
||||||
iio_trigger_unregister(data->acc_dready_trig);
|
iio_trigger_unregister(data->acc_dready_trig);
|
||||||
|
|
|
@ -71,8 +71,9 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
|
||||||
|
|
||||||
if (avail >= to_wait) {
|
if (avail >= to_wait) {
|
||||||
/* force a flush for non-blocking reads */
|
/* force a flush for non-blocking reads */
|
||||||
if (!to_wait && !avail && to_flush)
|
if (!to_wait && avail < to_flush)
|
||||||
iio_buffer_flush_hwfifo(indio_dev, buf, to_flush);
|
iio_buffer_flush_hwfifo(indio_dev, buf,
|
||||||
|
to_flush - avail);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,9 +91,16 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_buffer_read_first_n_outer() - chrdev read for buffer access
|
* iio_buffer_read_first_n_outer() - chrdev read for buffer access
|
||||||
|
* @filp: File structure pointer for the char device
|
||||||
|
* @buf: Destination buffer for iio buffer read
|
||||||
|
* @n: First n bytes to read
|
||||||
|
* @f_ps: Long offset provided by the user as a seek position
|
||||||
*
|
*
|
||||||
* This function relies on all buffer implementations having an
|
* This function relies on all buffer implementations having an
|
||||||
* iio_buffer as their first element.
|
* iio_buffer as their first element.
|
||||||
|
*
|
||||||
|
* Return: negative values corresponding to error codes or ret != 0
|
||||||
|
* for ending the reading activity
|
||||||
**/
|
**/
|
||||||
ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
||||||
size_t n, loff_t *f_ps)
|
size_t n, loff_t *f_ps)
|
||||||
|
@ -100,8 +108,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
||||||
struct iio_dev *indio_dev = filp->private_data;
|
struct iio_dev *indio_dev = filp->private_data;
|
||||||
struct iio_buffer *rb = indio_dev->buffer;
|
struct iio_buffer *rb = indio_dev->buffer;
|
||||||
size_t datum_size;
|
size_t datum_size;
|
||||||
size_t to_wait = 0;
|
size_t to_wait;
|
||||||
size_t to_read;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!indio_dev->info)
|
if (!indio_dev->info)
|
||||||
|
@ -119,14 +126,14 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
||||||
if (!datum_size)
|
if (!datum_size)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
to_read = min_t(size_t, n / datum_size, rb->watermark);
|
if (filp->f_flags & O_NONBLOCK)
|
||||||
|
to_wait = 0;
|
||||||
if (!(filp->f_flags & O_NONBLOCK))
|
else
|
||||||
to_wait = to_read;
|
to_wait = min_t(size_t, n / datum_size, rb->watermark);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
ret = wait_event_interruptible(rb->pollq,
|
ret = wait_event_interruptible(rb->pollq,
|
||||||
iio_buffer_ready(indio_dev, rb, to_wait, to_read));
|
iio_buffer_ready(indio_dev, rb, to_wait, n / datum_size));
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -143,6 +150,12 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_buffer_poll() - poll the buffer to find out if it has data
|
* iio_buffer_poll() - poll the buffer to find out if it has data
|
||||||
|
* @filp: File structure pointer for device access
|
||||||
|
* @wait: Poll table structure pointer for which the driver adds
|
||||||
|
* a wait queue
|
||||||
|
*
|
||||||
|
* Return: (POLLIN | POLLRDNORM) if data is available for reading
|
||||||
|
* or 0 for other cases
|
||||||
*/
|
*/
|
||||||
unsigned int iio_buffer_poll(struct file *filp,
|
unsigned int iio_buffer_poll(struct file *filp,
|
||||||
struct poll_table_struct *wait)
|
struct poll_table_struct *wait)
|
||||||
|
@ -151,7 +164,7 @@ unsigned int iio_buffer_poll(struct file *filp,
|
||||||
struct iio_buffer *rb = indio_dev->buffer;
|
struct iio_buffer *rb = indio_dev->buffer;
|
||||||
|
|
||||||
if (!indio_dev->info)
|
if (!indio_dev->info)
|
||||||
return -ENODEV;
|
return 0;
|
||||||
|
|
||||||
poll_wait(filp, &rb->pollq, wait);
|
poll_wait(filp, &rb->pollq, wait);
|
||||||
if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0))
|
if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0))
|
||||||
|
@ -1136,7 +1149,7 @@ int iio_scan_mask_query(struct iio_dev *indio_dev,
|
||||||
EXPORT_SYMBOL_GPL(iio_scan_mask_query);
|
EXPORT_SYMBOL_GPL(iio_scan_mask_query);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct iio_demux_table() - table describing demux memcpy ops
|
* struct iio_demux_table - table describing demux memcpy ops
|
||||||
* @from: index to copy from
|
* @from: index to copy from
|
||||||
* @to: index to copy to
|
* @to: index to copy to
|
||||||
* @length: how many bytes to copy
|
* @length: how many bytes to copy
|
||||||
|
|
|
@ -81,6 +81,14 @@ static const char * const iio_modifier_names[] = {
|
||||||
[IIO_MOD_X] = "x",
|
[IIO_MOD_X] = "x",
|
||||||
[IIO_MOD_Y] = "y",
|
[IIO_MOD_Y] = "y",
|
||||||
[IIO_MOD_Z] = "z",
|
[IIO_MOD_Z] = "z",
|
||||||
|
[IIO_MOD_X_AND_Y] = "x&y",
|
||||||
|
[IIO_MOD_X_AND_Z] = "x&z",
|
||||||
|
[IIO_MOD_Y_AND_Z] = "y&z",
|
||||||
|
[IIO_MOD_X_AND_Y_AND_Z] = "x&y&z",
|
||||||
|
[IIO_MOD_X_OR_Y] = "x|y",
|
||||||
|
[IIO_MOD_X_OR_Z] = "x|z",
|
||||||
|
[IIO_MOD_Y_OR_Z] = "y|z",
|
||||||
|
[IIO_MOD_X_OR_Y_OR_Z] = "x|y|z",
|
||||||
[IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
|
[IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
|
||||||
[IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
|
[IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
|
||||||
[IIO_MOD_LIGHT_BOTH] = "both",
|
[IIO_MOD_LIGHT_BOTH] = "both",
|
||||||
|
@ -398,10 +406,16 @@ EXPORT_SYMBOL_GPL(iio_enum_write);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_format_value() - Formats a IIO value into its string representation
|
* iio_format_value() - Formats a IIO value into its string representation
|
||||||
* @buf: The buffer to which the formated value gets written
|
* @buf: The buffer to which the formatted value gets written
|
||||||
* @type: One of the IIO_VAL_... constants. This decides how the val and val2
|
* @type: One of the IIO_VAL_... constants. This decides how the val
|
||||||
* parameters are formatted.
|
* and val2 parameters are formatted.
|
||||||
* @vals: pointer to the values, exact meaning depends on the type parameter.
|
* @size: Number of IIO value entries contained in vals
|
||||||
|
* @vals: Pointer to the values, exact meaning depends on the
|
||||||
|
* type parameter.
|
||||||
|
*
|
||||||
|
* Return: 0 by default, a negative number on failure or the
|
||||||
|
* total number of characters written for a type that belongs
|
||||||
|
* to the IIO_VAL_... constant.
|
||||||
*/
|
*/
|
||||||
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
|
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
|
||||||
{
|
{
|
||||||
|
@ -1088,6 +1102,11 @@ EXPORT_SYMBOL_GPL(devm_iio_device_free);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_chrdev_open() - chrdev file open for buffer access and ioctls
|
* iio_chrdev_open() - chrdev file open for buffer access and ioctls
|
||||||
|
* @inode: Inode structure for identifying the device in the file system
|
||||||
|
* @filp: File structure for iio device used to keep and later access
|
||||||
|
* private data
|
||||||
|
*
|
||||||
|
* Return: 0 on success or -EBUSY if the device is already opened
|
||||||
**/
|
**/
|
||||||
static int iio_chrdev_open(struct inode *inode, struct file *filp)
|
static int iio_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
|
@ -1106,7 +1125,11 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_chrdev_release() - chrdev file close buffer access and ioctls
|
* iio_chrdev_release() - chrdev file close buffer access and ioctls
|
||||||
**/
|
* @inode: Inode structure pointer for the char device
|
||||||
|
* @filp: File structure pointer for the char device
|
||||||
|
*
|
||||||
|
* Return: 0 for successful release
|
||||||
|
*/
|
||||||
static int iio_chrdev_release(struct inode *inode, struct file *filp)
|
static int iio_chrdev_release(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = container_of(inode->i_cdev,
|
struct iio_dev *indio_dev = container_of(inode->i_cdev,
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
* @dev_attr_list: list of event interface sysfs attribute
|
* @dev_attr_list: list of event interface sysfs attribute
|
||||||
* @flags: file operations related flags including busy flag.
|
* @flags: file operations related flags including busy flag.
|
||||||
* @group: event interface sysfs attribute group
|
* @group: event interface sysfs attribute group
|
||||||
|
* @read_lock: lock to protect kfifo read operations
|
||||||
*/
|
*/
|
||||||
struct iio_event_interface {
|
struct iio_event_interface {
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
|
@ -75,6 +76,11 @@ EXPORT_SYMBOL(iio_push_event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_event_poll() - poll the event queue to find out if it has data
|
* iio_event_poll() - poll the event queue to find out if it has data
|
||||||
|
* @filep: File structure pointer to identify the device
|
||||||
|
* @wait: Poll table pointer to add the wait queue on
|
||||||
|
*
|
||||||
|
* Return: (POLLIN | POLLRDNORM) if data is available for reading
|
||||||
|
* or a negative error code on failure
|
||||||
*/
|
*/
|
||||||
static unsigned int iio_event_poll(struct file *filep,
|
static unsigned int iio_event_poll(struct file *filep,
|
||||||
struct poll_table_struct *wait)
|
struct poll_table_struct *wait)
|
||||||
|
@ -84,7 +90,7 @@ static unsigned int iio_event_poll(struct file *filep,
|
||||||
unsigned int events = 0;
|
unsigned int events = 0;
|
||||||
|
|
||||||
if (!indio_dev->info)
|
if (!indio_dev->info)
|
||||||
return -ENODEV;
|
return events;
|
||||||
|
|
||||||
poll_wait(filep, &ev_int->wait, wait);
|
poll_wait(filep, &ev_int->wait, wait);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,14 @@ static DEFINE_MUTEX(iio_trigger_list_lock);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_trigger_read_name() - retrieve useful identifying name
|
* iio_trigger_read_name() - retrieve useful identifying name
|
||||||
**/
|
* @dev: device associated with the iio_trigger
|
||||||
|
* @attr: pointer to the device_attribute structure that is
|
||||||
|
* being processed
|
||||||
|
* @buf: buffer to print the name into
|
||||||
|
*
|
||||||
|
* Return: a negative number on failure or the number of written
|
||||||
|
* characters on success.
|
||||||
|
*/
|
||||||
static ssize_t iio_trigger_read_name(struct device *dev,
|
static ssize_t iio_trigger_read_name(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
|
@ -288,10 +295,17 @@ EXPORT_SYMBOL_GPL(iio_dealloc_pollfunc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_trigger_read_current() - trigger consumer sysfs query current trigger
|
* iio_trigger_read_current() - trigger consumer sysfs query current trigger
|
||||||
|
* @dev: device associated with an industrial I/O device
|
||||||
|
* @attr: pointer to the device_attribute structure that
|
||||||
|
* is being processed
|
||||||
|
* @buf: buffer where the current trigger name will be printed into
|
||||||
*
|
*
|
||||||
* For trigger consumers the current_trigger interface allows the trigger
|
* For trigger consumers the current_trigger interface allows the trigger
|
||||||
* used by the device to be queried.
|
* used by the device to be queried.
|
||||||
**/
|
*
|
||||||
|
* Return: a negative number on failure, the number of characters written
|
||||||
|
* on success or 0 if no trigger is available
|
||||||
|
*/
|
||||||
static ssize_t iio_trigger_read_current(struct device *dev,
|
static ssize_t iio_trigger_read_current(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
|
@ -305,11 +319,18 @@ static ssize_t iio_trigger_read_current(struct device *dev,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_trigger_write_current() - trigger consumer sysfs set current trigger
|
* iio_trigger_write_current() - trigger consumer sysfs set current trigger
|
||||||
|
* @dev: device associated with an industrial I/O device
|
||||||
|
* @attr: device attribute that is being processed
|
||||||
|
* @buf: string buffer that holds the name of the trigger
|
||||||
|
* @len: length of the trigger name held by buf
|
||||||
*
|
*
|
||||||
* For trigger consumers the current_trigger interface allows the trigger
|
* For trigger consumers the current_trigger interface allows the trigger
|
||||||
* used for this device to be specified at run time based on the trigger's
|
* used for this device to be specified at run time based on the trigger's
|
||||||
* name.
|
* name.
|
||||||
**/
|
*
|
||||||
|
* Return: negative error code on failure or length of the buffer
|
||||||
|
* on success
|
||||||
|
*/
|
||||||
static ssize_t iio_trigger_write_current(struct device *dev,
|
static ssize_t iio_trigger_write_current(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
const char *buf,
|
const char *buf,
|
||||||
|
|
|
@ -24,8 +24,8 @@ static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
|
||||||
/**
|
/**
|
||||||
* iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc
|
* iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc
|
||||||
* @indio_dev: IIO device structure
|
* @indio_dev: IIO device structure
|
||||||
* @pollfunc_bh: Function which will be used as pollfunc bottom half
|
* @h: Function which will be used as pollfunc top half
|
||||||
* @pollfunc_th: Function which will be used as pollfunc top half
|
* @thread: Function which will be used as pollfunc bottom half
|
||||||
* @setup_ops: Buffer setup functions to use for this device.
|
* @setup_ops: Buffer setup functions to use for this device.
|
||||||
* If NULL the default setup functions for triggered
|
* If NULL the default setup functions for triggered
|
||||||
* buffers will be used.
|
* buffers will be used.
|
||||||
|
@ -42,8 +42,8 @@ static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
|
||||||
* iio_triggered_buffer_cleanup().
|
* iio_triggered_buffer_cleanup().
|
||||||
*/
|
*/
|
||||||
int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
|
int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
|
||||||
irqreturn_t (*pollfunc_bh)(int irq, void *p),
|
irqreturn_t (*h)(int irq, void *p),
|
||||||
irqreturn_t (*pollfunc_th)(int irq, void *p),
|
irqreturn_t (*thread)(int irq, void *p),
|
||||||
const struct iio_buffer_setup_ops *setup_ops)
|
const struct iio_buffer_setup_ops *setup_ops)
|
||||||
{
|
{
|
||||||
struct iio_buffer *buffer;
|
struct iio_buffer *buffer;
|
||||||
|
@ -57,8 +57,8 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
|
||||||
|
|
||||||
iio_device_attach_buffer(indio_dev, buffer);
|
iio_device_attach_buffer(indio_dev, buffer);
|
||||||
|
|
||||||
indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
|
indio_dev->pollfunc = iio_alloc_pollfunc(h,
|
||||||
pollfunc_th,
|
thread,
|
||||||
IRQF_ONESHOT,
|
IRQF_ONESHOT,
|
||||||
indio_dev,
|
indio_dev,
|
||||||
"%s_consumer%d",
|
"%s_consumer%d",
|
||||||
|
|
|
@ -86,7 +86,7 @@ config CM3323
|
||||||
depends on I2C
|
depends on I2C
|
||||||
tristate "Capella CM3323 color light sensor"
|
tristate "Capella CM3323 color light sensor"
|
||||||
help
|
help
|
||||||
Say Y here if you want to build a driver for Capela CM3323
|
Say Y here if you want to build a driver for Capella CM3323
|
||||||
color sensor.
|
color sensor.
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
|
@ -168,6 +168,17 @@ config JSA1212
|
||||||
To compile this driver as a module, choose M here:
|
To compile this driver as a module, choose M here:
|
||||||
the module will be called jsa1212.
|
the module will be called jsa1212.
|
||||||
|
|
||||||
|
config RPR0521
|
||||||
|
tristate "ROHM RPR0521 ALS and proximity sensor driver"
|
||||||
|
depends on I2C
|
||||||
|
select REGMAP_I2C
|
||||||
|
help
|
||||||
|
Say Y here if you want to build support for ROHM's RPR0521
|
||||||
|
ambient light and proximity sensor device.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here:
|
||||||
|
the module will be called rpr0521.
|
||||||
|
|
||||||
config SENSORS_LM3533
|
config SENSORS_LM3533
|
||||||
tristate "LM3533 ambient light sensor"
|
tristate "LM3533 ambient light sensor"
|
||||||
depends on MFD_LM3533
|
depends on MFD_LM3533
|
||||||
|
@ -199,6 +210,27 @@ config LTR501
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called ltr501.
|
will be called ltr501.
|
||||||
|
|
||||||
|
config OPT3001
|
||||||
|
tristate "Texas Instruments OPT3001 Light Sensor"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
If you say Y or M here, you get support for Texas Instruments
|
||||||
|
OPT3001 Ambient Light Sensor.
|
||||||
|
|
||||||
|
If built as a dynamically linked module, it will be called
|
||||||
|
opt3001.
|
||||||
|
|
||||||
|
config PA12203001
|
||||||
|
tristate "TXC PA12203001 light and proximity sensor"
|
||||||
|
depends on I2C
|
||||||
|
select REGMAP_I2C
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the TXC PA12203001
|
||||||
|
ambient light and proximity sensor.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called pa12203001.
|
||||||
|
|
||||||
config STK3310
|
config STK3310
|
||||||
tristate "STK3310 ALS and proximity sensor"
|
tristate "STK3310 ALS and proximity sensor"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
|
|
@ -19,6 +19,9 @@ obj-$(CONFIG_ISL29125) += isl29125.o
|
||||||
obj-$(CONFIG_JSA1212) += jsa1212.o
|
obj-$(CONFIG_JSA1212) += jsa1212.o
|
||||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||||
obj-$(CONFIG_LTR501) += ltr501.o
|
obj-$(CONFIG_LTR501) += ltr501.o
|
||||||
|
obj-$(CONFIG_OPT3001) += opt3001.o
|
||||||
|
obj-$(CONFIG_PA12203001) += pa12203001.o
|
||||||
|
obj-$(CONFIG_RPR0521) += rpr0521.o
|
||||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||||
obj-$(CONFIG_STK3310) += stk3310.o
|
obj-$(CONFIG_STK3310) += stk3310.o
|
||||||
obj-$(CONFIG_TCS3414) += tcs3414.o
|
obj-$(CONFIG_TCS3414) += tcs3414.o
|
||||||
|
|
|
@ -65,20 +65,20 @@ static const struct iio_chan_spec acpi_als_channels[] = {
|
||||||
* to acpi_als_channels[], the evt_buffer below will grow
|
* to acpi_als_channels[], the evt_buffer below will grow
|
||||||
* automatically.
|
* automatically.
|
||||||
*/
|
*/
|
||||||
#define EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels)
|
#define ACPI_ALS_EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels)
|
||||||
#define EVT_BUFFER_SIZE \
|
#define ACPI_ALS_EVT_BUFFER_SIZE \
|
||||||
(sizeof(s64) + (EVT_NR_SOURCES * sizeof(s32)))
|
(sizeof(s64) + (ACPI_ALS_EVT_NR_SOURCES * sizeof(s32)))
|
||||||
|
|
||||||
struct acpi_als {
|
struct acpi_als {
|
||||||
struct acpi_device *device;
|
struct acpi_device *device;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
|
||||||
s32 evt_buffer[EVT_BUFFER_SIZE];
|
s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All types of properties the ACPI0008 block can report. The ALI, ALC, ALT
|
* All types of properties the ACPI0008 block can report. The ALI, ALC, ALT
|
||||||
* and ALP can all be handled by als_read_value() below, while the ALR is
|
* and ALP can all be handled by acpi_als_read_value() below, while the ALR is
|
||||||
* special.
|
* special.
|
||||||
*
|
*
|
||||||
* The _ALR property returns tables that can be used to fine-tune the values
|
* The _ALR property returns tables that can be used to fine-tune the values
|
||||||
|
@ -93,7 +93,7 @@ struct acpi_als {
|
||||||
#define ACPI_ALS_POLLING "_ALP"
|
#define ACPI_ALS_POLLING "_ALP"
|
||||||
#define ACPI_ALS_TABLES "_ALR"
|
#define ACPI_ALS_TABLES "_ALR"
|
||||||
|
|
||||||
static int als_read_value(struct acpi_als *als, char *prop, s32 *val)
|
static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val)
|
||||||
{
|
{
|
||||||
unsigned long long temp_val;
|
unsigned long long temp_val;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
|
@ -122,11 +122,11 @@ static void acpi_als_notify(struct acpi_device *device, u32 event)
|
||||||
|
|
||||||
mutex_lock(&als->lock);
|
mutex_lock(&als->lock);
|
||||||
|
|
||||||
memset(buffer, 0, EVT_BUFFER_SIZE);
|
memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ACPI_ALS_NOTIFY_ILLUMINANCE:
|
case ACPI_ALS_NOTIFY_ILLUMINANCE:
|
||||||
ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
|
ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
*buffer++ = val;
|
*buffer++ = val;
|
||||||
|
@ -159,7 +159,7 @@ static int acpi_als_read_raw(struct iio_dev *indio_dev,
|
||||||
if (chan->type != IIO_LIGHT)
|
if (chan->type != IIO_LIGHT)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val);
|
ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -515,7 +515,6 @@ MODULE_DEVICE_TABLE(i2c, apds9300_id);
|
||||||
static struct i2c_driver apds9300_driver = {
|
static struct i2c_driver apds9300_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = APDS9300_DRV_NAME,
|
.name = APDS9300_DRV_NAME,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = APDS9300_PM_OPS,
|
.pm = APDS9300_PM_OPS,
|
||||||
},
|
},
|
||||||
.probe = apds9300_probe,
|
.probe = apds9300_probe,
|
||||||
|
|
|
@ -319,7 +319,6 @@ MODULE_DEVICE_TABLE(i2c, bh1750_id);
|
||||||
static struct i2c_driver bh1750_driver = {
|
static struct i2c_driver bh1750_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "bh1750",
|
.name = "bh1750",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = BH1750_PM_OPS,
|
.pm = BH1750_PM_OPS,
|
||||||
},
|
},
|
||||||
.probe = bh1750_probe,
|
.probe = bh1750_probe,
|
||||||
|
|
|
@ -353,12 +353,12 @@ static const struct of_device_id cm32181_of_match[] = {
|
||||||
{ .compatible = "capella,cm32181" },
|
{ .compatible = "capella,cm32181" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, cm32181_of_match);
|
||||||
|
|
||||||
static struct i2c_driver cm32181_driver = {
|
static struct i2c_driver cm32181_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "cm32181",
|
.name = "cm32181",
|
||||||
.of_match_table = of_match_ptr(cm32181_of_match),
|
.of_match_table = of_match_ptr(cm32181_of_match),
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.id_table = cm32181_id,
|
.id_table = cm32181_id,
|
||||||
.probe = cm32181_probe,
|
.probe = cm32181_probe,
|
||||||
|
|
|
@ -417,11 +417,11 @@ static const struct of_device_id cm3232_of_match[] = {
|
||||||
{.compatible = "capella,cm3232"},
|
{.compatible = "capella,cm3232"},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, cm3232_of_match);
|
||||||
|
|
||||||
static struct i2c_driver cm3232_driver = {
|
static struct i2c_driver cm3232_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "cm3232",
|
.name = "cm3232",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.of_match_table = of_match_ptr(cm3232_of_match),
|
.of_match_table = of_match_ptr(cm3232_of_match),
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
.pm = &cm3232_pm_ops,
|
.pm = &cm3232_pm_ops,
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
#define CM3323_CONF_SD_BIT BIT(0) /* sensor disable */
|
#define CM3323_CONF_SD_BIT BIT(0) /* sensor disable */
|
||||||
#define CM3323_CONF_AF_BIT BIT(1) /* auto/manual force mode */
|
#define CM3323_CONF_AF_BIT BIT(1) /* auto/manual force mode */
|
||||||
#define CM3323_CONF_IT_MASK (BIT(4) | BIT(5) | BIT(6))
|
#define CM3323_CONF_IT_MASK GENMASK(6, 4)
|
||||||
#define CM3323_CONF_IT_SHIFT 4
|
#define CM3323_CONF_IT_SHIFT 4
|
||||||
|
|
||||||
#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
|
#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
|
||||||
|
@ -133,9 +133,11 @@ static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
data->reg_conf = reg_conf;
|
data->reg_conf = reg_conf;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +150,7 @@ static int cm3323_get_it_bits(struct cm3323_data *data)
|
||||||
|
|
||||||
if (bits >= ARRAY_SIZE(cm3323_int_time))
|
if (bits >= ARRAY_SIZE(cm3323_int_time))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +158,7 @@ static int cm3323_read_raw(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec const *chan, int *val,
|
struct iio_chan_spec const *chan, int *val,
|
||||||
int *val2, long mask)
|
int *val2, long mask)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int ret;
|
||||||
struct cm3323_data *data = iio_priv(indio_dev);
|
struct cm3323_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
switch (mask) {
|
switch (mask) {
|
||||||
|
@ -172,14 +175,14 @@ static int cm3323_read_raw(struct iio_dev *indio_dev,
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_INT_TIME:
|
case IIO_CHAN_INFO_INT_TIME:
|
||||||
mutex_lock(&data->mutex);
|
mutex_lock(&data->mutex);
|
||||||
i = cm3323_get_it_bits(data);
|
ret = cm3323_get_it_bits(data);
|
||||||
if (i < 0) {
|
if (ret < 0) {
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
return -EINVAL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
*val = cm3323_int_time[i].val;
|
*val = cm3323_int_time[ret].val;
|
||||||
*val2 = cm3323_int_time[i].val2;
|
*val2 = cm3323_int_time[ret].val2;
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
@ -243,11 +246,13 @@ static int cm3323_probe(struct i2c_client *client,
|
||||||
dev_err(&client->dev, "cm3323 chip init failed\n");
|
dev_err(&client->dev, "cm3323 chip init failed\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "failed to register iio dev\n");
|
dev_err(&client->dev, "failed to register iio dev\n");
|
||||||
goto err_init;
|
goto err_init;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err_init:
|
err_init:
|
||||||
cm3323_disable(indio_dev);
|
cm3323_disable(indio_dev);
|
||||||
|
|
|
@ -731,12 +731,12 @@ static const struct of_device_id cm36651_of_match[] = {
|
||||||
{ .compatible = "capella,cm36651" },
|
{ .compatible = "capella,cm36651" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, cm36651_of_match);
|
||||||
|
|
||||||
static struct i2c_driver cm36651_driver = {
|
static struct i2c_driver cm36651_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "cm36651",
|
.name = "cm36651",
|
||||||
.of_match_table = cm36651_of_match,
|
.of_match_table = cm36651_of_match,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = cm36651_probe,
|
.probe = cm36651_probe,
|
||||||
.remove = cm36651_remove,
|
.remove = cm36651_remove,
|
||||||
|
|
|
@ -1634,13 +1634,13 @@ static const struct of_device_id gp2ap020a00f_of_match[] = {
|
||||||
{ .compatible = "sharp,gp2ap020a00f" },
|
{ .compatible = "sharp,gp2ap020a00f" },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, gp2ap020a00f_of_match);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct i2c_driver gp2ap020a00f_driver = {
|
static struct i2c_driver gp2ap020a00f_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = GP2A_I2C_NAME,
|
.name = GP2A_I2C_NAME,
|
||||||
.of_match_table = of_match_ptr(gp2ap020a00f_of_match),
|
.of_match_table = of_match_ptr(gp2ap020a00f_of_match),
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = gp2ap020a00f_probe,
|
.probe = gp2ap020a00f_probe,
|
||||||
.remove = gp2ap020a00f_remove,
|
.remove = gp2ap020a00f_remove,
|
||||||
|
|
|
@ -284,8 +284,7 @@ static int hid_prox_probe(struct platform_device *pdev)
|
||||||
goto error_free_dev_mem;
|
goto error_free_dev_mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
indio_dev->num_channels =
|
indio_dev->num_channels = ARRAY_SIZE(prox_channels);
|
||||||
ARRAY_SIZE(prox_channels);
|
|
||||||
indio_dev->dev.parent = &pdev->dev;
|
indio_dev->dev.parent = &pdev->dev;
|
||||||
indio_dev->info = &prox_info;
|
indio_dev->info = &prox_info;
|
||||||
indio_dev->name = name;
|
indio_dev->name = name;
|
||||||
|
|
|
@ -197,9 +197,21 @@ done:
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IIO_CONST_ATTR(scale_available, "0.005722 0.152590");
|
||||||
|
|
||||||
|
static struct attribute *isl29125_attributes[] = {
|
||||||
|
&iio_const_attr_scale_available.dev_attr.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group isl29125_attribute_group = {
|
||||||
|
.attrs = isl29125_attributes,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct iio_info isl29125_info = {
|
static const struct iio_info isl29125_info = {
|
||||||
.read_raw = isl29125_read_raw,
|
.read_raw = isl29125_read_raw,
|
||||||
.write_raw = isl29125_write_raw,
|
.write_raw = isl29125_write_raw,
|
||||||
|
.attrs = &isl29125_attribute_group,
|
||||||
.driver_module = THIS_MODULE,
|
.driver_module = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -334,7 +346,6 @@ static struct i2c_driver isl29125_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = ISL29125_DRV_NAME,
|
.name = ISL29125_DRV_NAME,
|
||||||
.pm = &isl29125_pm_ops,
|
.pm = &isl29125_pm_ops,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = isl29125_probe,
|
.probe = isl29125_probe,
|
||||||
.remove = isl29125_remove,
|
.remove = isl29125_remove,
|
||||||
|
|
|
@ -457,7 +457,6 @@ static struct i2c_driver jsa1212_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = JSA1212_DRIVER_NAME,
|
.name = JSA1212_DRIVER_NAME,
|
||||||
.pm = JSA1212_PM_OPS,
|
.pm = JSA1212_PM_OPS,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
|
.acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
|
||||||
},
|
},
|
||||||
.probe = jsa1212_probe,
|
.probe = jsa1212_probe,
|
||||||
|
|
|
@ -1551,7 +1551,6 @@ static struct i2c_driver ltr501_driver = {
|
||||||
.name = LTR501_DRV_NAME,
|
.name = LTR501_DRV_NAME,
|
||||||
.pm = <r501_pm_ops,
|
.pm = <r501_pm_ops,
|
||||||
.acpi_match_table = ACPI_PTR(ltr_acpi_match),
|
.acpi_match_table = ACPI_PTR(ltr_acpi_match),
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = ltr501_probe,
|
.probe = ltr501_probe,
|
||||||
.remove = ltr501_remove,
|
.remove = ltr501_remove,
|
||||||
|
|
|
@ -0,0 +1,804 @@
|
||||||
|
/**
|
||||||
|
* opt3001.c - Texas Instruments OPT3001 Light Sensor
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
|
||||||
|
*
|
||||||
|
* Author: Andreas Dannenberg <dannenberg@ti.com>
|
||||||
|
* Based on previous work from: Felipe Balbi <balbi@ti.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 of the License
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <linux/iio/events.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/iio/sysfs.h>
|
||||||
|
|
||||||
|
#define OPT3001_RESULT 0x00
|
||||||
|
#define OPT3001_CONFIGURATION 0x01
|
||||||
|
#define OPT3001_LOW_LIMIT 0x02
|
||||||
|
#define OPT3001_HIGH_LIMIT 0x03
|
||||||
|
#define OPT3001_MANUFACTURER_ID 0x7e
|
||||||
|
#define OPT3001_DEVICE_ID 0x7f
|
||||||
|
|
||||||
|
#define OPT3001_CONFIGURATION_RN_MASK (0xf << 12)
|
||||||
|
#define OPT3001_CONFIGURATION_RN_AUTO (0xc << 12)
|
||||||
|
|
||||||
|
#define OPT3001_CONFIGURATION_CT BIT(11)
|
||||||
|
|
||||||
|
#define OPT3001_CONFIGURATION_M_MASK (3 << 9)
|
||||||
|
#define OPT3001_CONFIGURATION_M_SHUTDOWN (0 << 9)
|
||||||
|
#define OPT3001_CONFIGURATION_M_SINGLE (1 << 9)
|
||||||
|
#define OPT3001_CONFIGURATION_M_CONTINUOUS (2 << 9) /* also 3 << 9 */
|
||||||
|
|
||||||
|
#define OPT3001_CONFIGURATION_OVF BIT(8)
|
||||||
|
#define OPT3001_CONFIGURATION_CRF BIT(7)
|
||||||
|
#define OPT3001_CONFIGURATION_FH BIT(6)
|
||||||
|
#define OPT3001_CONFIGURATION_FL BIT(5)
|
||||||
|
#define OPT3001_CONFIGURATION_L BIT(4)
|
||||||
|
#define OPT3001_CONFIGURATION_POL BIT(3)
|
||||||
|
#define OPT3001_CONFIGURATION_ME BIT(2)
|
||||||
|
|
||||||
|
#define OPT3001_CONFIGURATION_FC_MASK (3 << 0)
|
||||||
|
|
||||||
|
/* The end-of-conversion enable is located in the low-limit register */
|
||||||
|
#define OPT3001_LOW_LIMIT_EOC_ENABLE 0xc000
|
||||||
|
|
||||||
|
#define OPT3001_REG_EXPONENT(n) ((n) >> 12)
|
||||||
|
#define OPT3001_REG_MANTISSA(n) ((n) & 0xfff)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Time to wait for conversion result to be ready. The device datasheet
|
||||||
|
* worst-case max value is 880ms. Add some slack to be on the safe side.
|
||||||
|
*/
|
||||||
|
#define OPT3001_RESULT_READY_TIMEOUT msecs_to_jiffies(1000)
|
||||||
|
|
||||||
|
struct opt3001 {
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
struct mutex lock;
|
||||||
|
u16 ok_to_ignore_lock:1;
|
||||||
|
u16 result_ready:1;
|
||||||
|
wait_queue_head_t result_ready_queue;
|
||||||
|
u16 result;
|
||||||
|
|
||||||
|
u32 int_time;
|
||||||
|
u32 mode;
|
||||||
|
|
||||||
|
u16 high_thresh_mantissa;
|
||||||
|
u16 low_thresh_mantissa;
|
||||||
|
|
||||||
|
u8 high_thresh_exp;
|
||||||
|
u8 low_thresh_exp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct opt3001_scale {
|
||||||
|
int val;
|
||||||
|
int val2;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct opt3001_scale opt3001_scales[] = {
|
||||||
|
{
|
||||||
|
.val = 40,
|
||||||
|
.val2 = 950000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 81,
|
||||||
|
.val2 = 900000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 163,
|
||||||
|
.val2 = 800000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 327,
|
||||||
|
.val2 = 600000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 655,
|
||||||
|
.val2 = 200000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 1310,
|
||||||
|
.val2 = 400000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 2620,
|
||||||
|
.val2 = 800000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 5241,
|
||||||
|
.val2 = 600000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 10483,
|
||||||
|
.val2 = 200000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 20966,
|
||||||
|
.val2 = 400000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.val = 83865,
|
||||||
|
.val2 = 600000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int opt3001_find_scale(const struct opt3001 *opt, int val,
|
||||||
|
int val2, u8 *exponent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(opt3001_scales); i++) {
|
||||||
|
const struct opt3001_scale *scale = &opt3001_scales[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Combine the integer and micro parts for comparison
|
||||||
|
* purposes. Use milli lux precision to avoid 32-bit integer
|
||||||
|
* overflows.
|
||||||
|
*/
|
||||||
|
if ((val * 1000 + val2 / 1000) <=
|
||||||
|
(scale->val * 1000 + scale->val2 / 1000)) {
|
||||||
|
*exponent = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent,
|
||||||
|
u16 mantissa, int *val, int *val2)
|
||||||
|
{
|
||||||
|
int lux;
|
||||||
|
|
||||||
|
lux = 10 * (mantissa << exponent);
|
||||||
|
*val = lux / 1000;
|
||||||
|
*val2 = (lux - (*val * 1000)) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void opt3001_set_mode(struct opt3001 *opt, u16 *reg, u16 mode)
|
||||||
|
{
|
||||||
|
*reg &= ~OPT3001_CONFIGURATION_M_MASK;
|
||||||
|
*reg |= mode;
|
||||||
|
opt->mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.8");
|
||||||
|
|
||||||
|
static struct attribute *opt3001_attributes[] = {
|
||||||
|
&iio_const_attr_integration_time_available.dev_attr.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group opt3001_attribute_group = {
|
||||||
|
.attrs = opt3001_attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct iio_event_spec opt3001_event_spec[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_EV_TYPE_THRESH,
|
||||||
|
.dir = IIO_EV_DIR_RISING,
|
||||||
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||||
|
BIT(IIO_EV_INFO_ENABLE),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IIO_EV_TYPE_THRESH,
|
||||||
|
.dir = IIO_EV_DIR_FALLING,
|
||||||
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||||
|
BIT(IIO_EV_INFO_ENABLE),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct iio_chan_spec opt3001_channels[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_LIGHT,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||||
|
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||||
|
.event_spec = opt3001_event_spec,
|
||||||
|
.num_event_specs = ARRAY_SIZE(opt3001_event_spec),
|
||||||
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u16 mantissa;
|
||||||
|
u16 reg;
|
||||||
|
u8 exponent;
|
||||||
|
u16 value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable the end-of-conversion interrupt mechanism. Note that doing
|
||||||
|
* so will overwrite the low-level limit value however we will restore
|
||||||
|
* this value later on.
|
||||||
|
*/
|
||||||
|
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
|
||||||
|
OPT3001_LOW_LIMIT_EOC_ENABLE);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to write register %02x\n",
|
||||||
|
OPT3001_LOW_LIMIT);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset data-ready indicator flag (will be set in the IRQ routine) */
|
||||||
|
opt->result_ready = false;
|
||||||
|
|
||||||
|
/* Allow IRQ to access the device despite lock being set */
|
||||||
|
opt->ok_to_ignore_lock = true;
|
||||||
|
|
||||||
|
/* Configure for single-conversion mode and start a new conversion */
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = ret;
|
||||||
|
opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE);
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
|
||||||
|
reg);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to write register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for the IRQ to indicate the conversion is complete */
|
||||||
|
ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready,
|
||||||
|
OPT3001_RESULT_READY_TIMEOUT);
|
||||||
|
|
||||||
|
err:
|
||||||
|
/* Disallow IRQ to access the device while lock is active */
|
||||||
|
opt->ok_to_ignore_lock = false;
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
else if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable the end-of-conversion interrupt mechanism by restoring the
|
||||||
|
* low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note
|
||||||
|
* that selectively clearing those enable bits would affect the actual
|
||||||
|
* limit value due to bit-overlap and therefore can't be done.
|
||||||
|
*/
|
||||||
|
value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
|
||||||
|
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
|
||||||
|
value);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to write register %02x\n",
|
||||||
|
OPT3001_LOW_LIMIT);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
exponent = OPT3001_REG_EXPONENT(opt->result);
|
||||||
|
mantissa = OPT3001_REG_MANTISSA(opt->result);
|
||||||
|
|
||||||
|
opt3001_to_iio_ret(opt, exponent, mantissa, val, val2);
|
||||||
|
|
||||||
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2)
|
||||||
|
{
|
||||||
|
*val = 0;
|
||||||
|
*val2 = opt->int_time;
|
||||||
|
|
||||||
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_set_int_time(struct opt3001 *opt, int time)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = ret;
|
||||||
|
|
||||||
|
switch (time) {
|
||||||
|
case 100000:
|
||||||
|
reg &= ~OPT3001_CONFIGURATION_CT;
|
||||||
|
opt->int_time = 100000;
|
||||||
|
break;
|
||||||
|
case 800000:
|
||||||
|
reg |= OPT3001_CONFIGURATION_CT;
|
||||||
|
opt->int_time = 800000;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
|
||||||
|
reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_read_raw(struct iio_dev *iio,
|
||||||
|
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||||
|
long mask)
|
||||||
|
{
|
||||||
|
struct opt3001 *opt = iio_priv(iio);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (chan->type != IIO_LIGHT)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&opt->lock);
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_PROCESSED:
|
||||||
|
ret = opt3001_get_lux(opt, val, val2);
|
||||||
|
break;
|
||||||
|
case IIO_CHAN_INFO_INT_TIME:
|
||||||
|
ret = opt3001_get_int_time(opt, val, val2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&opt->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_write_raw(struct iio_dev *iio,
|
||||||
|
struct iio_chan_spec const *chan, int val, int val2,
|
||||||
|
long mask)
|
||||||
|
{
|
||||||
|
struct opt3001 *opt = iio_priv(iio);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (chan->type != IIO_LIGHT)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (mask != IIO_CHAN_INFO_INT_TIME)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (val != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&opt->lock);
|
||||||
|
ret = opt3001_set_int_time(opt, val2);
|
||||||
|
mutex_unlock(&opt->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_read_event_value(struct iio_dev *iio,
|
||||||
|
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 opt3001 *opt = iio_priv(iio);
|
||||||
|
int ret = IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
|
||||||
|
mutex_lock(&opt->lock);
|
||||||
|
|
||||||
|
switch (dir) {
|
||||||
|
case IIO_EV_DIR_RISING:
|
||||||
|
opt3001_to_iio_ret(opt, opt->high_thresh_exp,
|
||||||
|
opt->high_thresh_mantissa, val, val2);
|
||||||
|
break;
|
||||||
|
case IIO_EV_DIR_FALLING:
|
||||||
|
opt3001_to_iio_ret(opt, opt->low_thresh_exp,
|
||||||
|
opt->low_thresh_mantissa, val, val2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&opt->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_write_event_value(struct iio_dev *iio,
|
||||||
|
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 opt3001 *opt = iio_priv(iio);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
u16 mantissa;
|
||||||
|
u16 value;
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
u8 exponent;
|
||||||
|
|
||||||
|
if (val < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&opt->lock);
|
||||||
|
|
||||||
|
ret = opt3001_find_scale(opt, val, val2, &exponent);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent;
|
||||||
|
value = (exponent << 12) | mantissa;
|
||||||
|
|
||||||
|
switch (dir) {
|
||||||
|
case IIO_EV_DIR_RISING:
|
||||||
|
reg = OPT3001_HIGH_LIMIT;
|
||||||
|
opt->high_thresh_mantissa = mantissa;
|
||||||
|
opt->high_thresh_exp = exponent;
|
||||||
|
break;
|
||||||
|
case IIO_EV_DIR_FALLING:
|
||||||
|
reg = OPT3001_LOW_LIMIT;
|
||||||
|
opt->low_thresh_mantissa = mantissa;
|
||||||
|
opt->low_thresh_exp = exponent;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_word_swapped(opt->client, reg, value);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to write register %02x\n", reg);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
mutex_unlock(&opt->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_read_event_config(struct iio_dev *iio,
|
||||||
|
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||||
|
enum iio_event_direction dir)
|
||||||
|
{
|
||||||
|
struct opt3001 *opt = iio_priv(iio);
|
||||||
|
|
||||||
|
return opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_write_event_config(struct iio_dev *iio,
|
||||||
|
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||||
|
enum iio_event_direction dir, int state)
|
||||||
|
{
|
||||||
|
struct opt3001 *opt = iio_priv(iio);
|
||||||
|
int ret;
|
||||||
|
u16 mode;
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
if (state && opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mutex_lock(&opt->lock);
|
||||||
|
|
||||||
|
mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS
|
||||||
|
: OPT3001_CONFIGURATION_M_SHUTDOWN;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = ret;
|
||||||
|
opt3001_set_mode(opt, ®, mode);
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
|
||||||
|
reg);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to write register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
mutex_unlock(&opt->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_info opt3001_info = {
|
||||||
|
.driver_module = THIS_MODULE,
|
||||||
|
.attrs = &opt3001_attribute_group,
|
||||||
|
.read_raw = opt3001_read_raw,
|
||||||
|
.write_raw = opt3001_write_raw,
|
||||||
|
.read_event_value = opt3001_read_event_value,
|
||||||
|
.write_event_value = opt3001_write_event_value,
|
||||||
|
.read_event_config = opt3001_read_event_config,
|
||||||
|
.write_event_config = opt3001_write_event_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int opt3001_read_id(struct opt3001 *opt)
|
||||||
|
{
|
||||||
|
char manufacturer[2];
|
||||||
|
u16 device_id;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_MANUFACTURER_ID);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_MANUFACTURER_ID);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
manufacturer[0] = ret >> 8;
|
||||||
|
manufacturer[1] = ret & 0xff;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_DEVICE_ID);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_id = ret;
|
||||||
|
|
||||||
|
dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0],
|
||||||
|
manufacturer[1], device_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_configure(struct opt3001 *opt)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = ret;
|
||||||
|
|
||||||
|
/* Enable automatic full-scale setting mode */
|
||||||
|
reg &= ~OPT3001_CONFIGURATION_RN_MASK;
|
||||||
|
reg |= OPT3001_CONFIGURATION_RN_AUTO;
|
||||||
|
|
||||||
|
/* Reflect status of the device's integration time setting */
|
||||||
|
if (reg & OPT3001_CONFIGURATION_CT)
|
||||||
|
opt->int_time = 800000;
|
||||||
|
else
|
||||||
|
opt->int_time = 100000;
|
||||||
|
|
||||||
|
/* Ensure device is in shutdown initially */
|
||||||
|
opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN);
|
||||||
|
|
||||||
|
/* Configure for latched window-style comparison operation */
|
||||||
|
reg |= OPT3001_CONFIGURATION_L;
|
||||||
|
reg &= ~OPT3001_CONFIGURATION_POL;
|
||||||
|
reg &= ~OPT3001_CONFIGURATION_ME;
|
||||||
|
reg &= ~OPT3001_CONFIGURATION_FC_MASK;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
|
||||||
|
reg);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to write register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_LOW_LIMIT);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_LOW_LIMIT);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
|
||||||
|
opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret);
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_HIGH_LIMIT);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_HIGH_LIMIT);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
|
||||||
|
opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t opt3001_irq(int irq, void *_iio)
|
||||||
|
{
|
||||||
|
struct iio_dev *iio = _iio;
|
||||||
|
struct opt3001 *opt = iio_priv(iio);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!opt->ok_to_ignore_lock)
|
||||||
|
mutex_lock(&opt->lock);
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret & OPT3001_CONFIGURATION_M_MASK) ==
|
||||||
|
OPT3001_CONFIGURATION_M_CONTINUOUS) {
|
||||||
|
if (ret & OPT3001_CONFIGURATION_FH)
|
||||||
|
iio_push_event(iio,
|
||||||
|
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_RISING),
|
||||||
|
iio_get_time_ns());
|
||||||
|
if (ret & OPT3001_CONFIGURATION_FL)
|
||||||
|
iio_push_event(iio,
|
||||||
|
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||||
|
IIO_EV_TYPE_THRESH,
|
||||||
|
IIO_EV_DIR_FALLING),
|
||||||
|
iio_get_time_ns());
|
||||||
|
} else if (ret & OPT3001_CONFIGURATION_CRF) {
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_RESULT);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
opt->result = ret;
|
||||||
|
opt->result_ready = true;
|
||||||
|
wake_up(&opt->result_ready_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (!opt->ok_to_ignore_lock)
|
||||||
|
mutex_unlock(&opt->lock);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
|
||||||
|
struct iio_dev *iio;
|
||||||
|
struct opt3001 *opt;
|
||||||
|
int irq = client->irq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
iio = devm_iio_device_alloc(dev, sizeof(*opt));
|
||||||
|
if (!iio)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
opt = iio_priv(iio);
|
||||||
|
opt->client = client;
|
||||||
|
opt->dev = dev;
|
||||||
|
|
||||||
|
mutex_init(&opt->lock);
|
||||||
|
init_waitqueue_head(&opt->result_ready_queue);
|
||||||
|
i2c_set_clientdata(client, iio);
|
||||||
|
|
||||||
|
ret = opt3001_read_id(opt);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = opt3001_configure(opt);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
iio->name = client->name;
|
||||||
|
iio->channels = opt3001_channels;
|
||||||
|
iio->num_channels = ARRAY_SIZE(opt3001_channels);
|
||||||
|
iio->dev.parent = dev;
|
||||||
|
iio->modes = INDIO_DIRECT_MODE;
|
||||||
|
iio->info = &opt3001_info;
|
||||||
|
|
||||||
|
ret = devm_iio_device_register(dev, iio);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to register IIO device\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = request_threaded_irq(irq, NULL, opt3001_irq,
|
||||||
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
|
"opt3001", iio);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to request IRQ #%d\n", irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt3001_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct iio_dev *iio = i2c_get_clientdata(client);
|
||||||
|
struct opt3001 *opt = iio_priv(iio);
|
||||||
|
int ret;
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
free_irq(client->irq, iio);
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to read register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = ret;
|
||||||
|
opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN);
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
|
||||||
|
reg);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(opt->dev, "failed to write register %02x\n",
|
||||||
|
OPT3001_CONFIGURATION);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id opt3001_id[] = {
|
||||||
|
{ "opt3001", 0 },
|
||||||
|
{ } /* Terminating Entry */
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, opt3001_id);
|
||||||
|
|
||||||
|
static const struct of_device_id opt3001_of_match[] = {
|
||||||
|
{ .compatible = "ti,opt3001" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct i2c_driver opt3001_driver = {
|
||||||
|
.probe = opt3001_probe,
|
||||||
|
.remove = opt3001_remove,
|
||||||
|
.id_table = opt3001_id,
|
||||||
|
|
||||||
|
.driver = {
|
||||||
|
.name = "opt3001",
|
||||||
|
.of_match_table = of_match_ptr(opt3001_of_match),
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(opt3001_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
|
||||||
|
MODULE_DESCRIPTION("Texas Instruments OPT3001 Light Sensor Driver");
|
|
@ -0,0 +1,483 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Intel Corporation
|
||||||
|
*
|
||||||
|
* Driver for TXC PA12203001 Proximity and Ambient Light Sensor.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* To do: Interrupt support.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/iio/sysfs.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#define PA12203001_DRIVER_NAME "pa12203001"
|
||||||
|
|
||||||
|
#define PA12203001_REG_CFG0 0x00
|
||||||
|
#define PA12203001_REG_CFG1 0x01
|
||||||
|
#define PA12203001_REG_CFG2 0x02
|
||||||
|
#define PA12203001_REG_CFG3 0x03
|
||||||
|
|
||||||
|
#define PA12203001_REG_ADL 0x0b
|
||||||
|
#define PA12203001_REG_PDH 0x0e
|
||||||
|
|
||||||
|
#define PA12203001_REG_POFS 0x10
|
||||||
|
#define PA12203001_REG_PSET 0x11
|
||||||
|
|
||||||
|
#define PA12203001_ALS_EN_MASK BIT(0)
|
||||||
|
#define PA12203001_PX_EN_MASK BIT(1)
|
||||||
|
#define PA12203001_PX_NORMAL_MODE_MASK GENMASK(7, 6)
|
||||||
|
#define PA12203001_AFSR_MASK GENMASK(5, 4)
|
||||||
|
#define PA12203001_AFSR_SHIFT 4
|
||||||
|
|
||||||
|
#define PA12203001_PSCAN 0x03
|
||||||
|
|
||||||
|
/* als range 31000, ps, als disabled */
|
||||||
|
#define PA12203001_REG_CFG0_DEFAULT 0x30
|
||||||
|
|
||||||
|
/* led current: 100 mA */
|
||||||
|
#define PA12203001_REG_CFG1_DEFAULT 0x20
|
||||||
|
|
||||||
|
/* ps mode: normal, interrupts not active */
|
||||||
|
#define PA12203001_REG_CFG2_DEFAULT 0xcc
|
||||||
|
|
||||||
|
#define PA12203001_REG_CFG3_DEFAULT 0x00
|
||||||
|
|
||||||
|
#define PA12203001_SLEEP_DELAY_MS 3000
|
||||||
|
|
||||||
|
#define PA12203001_CHIP_ENABLE 0xff
|
||||||
|
#define PA12203001_CHIP_DISABLE 0x00
|
||||||
|
|
||||||
|
/* available scales: corresponding to [500, 4000, 7000, 31000] lux */
|
||||||
|
static const int pa12203001_scales[] = { 7629, 61036, 106813, 473029};
|
||||||
|
|
||||||
|
struct pa12203001_data {
|
||||||
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
/* protect device states */
|
||||||
|
struct mutex lock;
|
||||||
|
|
||||||
|
bool als_enabled;
|
||||||
|
bool px_enabled;
|
||||||
|
bool als_needs_enable;
|
||||||
|
bool px_needs_enable;
|
||||||
|
|
||||||
|
struct regmap *map;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
u8 reg;
|
||||||
|
u8 val;
|
||||||
|
} regvals[] = {
|
||||||
|
{PA12203001_REG_CFG0, PA12203001_REG_CFG0_DEFAULT},
|
||||||
|
{PA12203001_REG_CFG1, PA12203001_REG_CFG1_DEFAULT},
|
||||||
|
{PA12203001_REG_CFG2, PA12203001_REG_CFG2_DEFAULT},
|
||||||
|
{PA12203001_REG_CFG3, PA12203001_REG_CFG3_DEFAULT},
|
||||||
|
{PA12203001_REG_PSET, PA12203001_PSCAN},
|
||||||
|
};
|
||||||
|
|
||||||
|
static IIO_CONST_ATTR(in_illuminance_scale_available,
|
||||||
|
"0.007629 0.061036 0.106813 0.473029");
|
||||||
|
|
||||||
|
static struct attribute *pa12203001_attrs[] = {
|
||||||
|
&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group pa12203001_attr_group = {
|
||||||
|
.attrs = pa12203001_attrs,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct iio_chan_spec pa12203001_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 regmap_range pa12203001_volatile_regs_ranges[] = {
|
||||||
|
regmap_reg_range(PA12203001_REG_ADL, PA12203001_REG_ADL + 1),
|
||||||
|
regmap_reg_range(PA12203001_REG_PDH, PA12203001_REG_PDH),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_access_table pa12203001_volatile_regs = {
|
||||||
|
.yes_ranges = pa12203001_volatile_regs_ranges,
|
||||||
|
.n_yes_ranges = ARRAY_SIZE(pa12203001_volatile_regs_ranges),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_config pa12203001_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
.max_register = PA12203001_REG_PSET,
|
||||||
|
.cache_type = REGCACHE_RBTREE,
|
||||||
|
.volatile_table = &pa12203001_volatile_regs,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int pa12203001_als_enable(struct pa12203001_data *data, u8 enable)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(data->map, PA12203001_REG_CFG0,
|
||||||
|
PA12203001_ALS_EN_MASK, enable);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->als_enabled = !!enable;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int pa12203001_px_enable(struct pa12203001_data *data, u8 enable)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(data->map, PA12203001_REG_CFG0,
|
||||||
|
PA12203001_PX_EN_MASK, enable);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->px_enabled = !!enable;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa12203001_set_power_state(struct pa12203001_data *data, bool on,
|
||||||
|
u8 mask)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (on && (mask & PA12203001_ALS_EN_MASK)) {
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
if (data->px_enabled) {
|
||||||
|
ret = pa12203001_als_enable(data,
|
||||||
|
PA12203001_ALS_EN_MASK);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
} else {
|
||||||
|
data->als_needs_enable = true;
|
||||||
|
}
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on && (mask & PA12203001_PX_EN_MASK)) {
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
if (data->als_enabled) {
|
||||||
|
ret = pa12203001_px_enable(data, PA12203001_PX_EN_MASK);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err;
|
||||||
|
} else {
|
||||||
|
data->px_needs_enable = true;
|
||||||
|
}
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&data->client->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
pm_runtime_put_noidle(&data->client->dev);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pm_runtime_mark_last_busy(&data->client->dev);
|
||||||
|
ret = pm_runtime_put_autosuspend(&data->client->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err:
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa12203001_read_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan, int *val,
|
||||||
|
int *val2, long mask)
|
||||||
|
{
|
||||||
|
struct pa12203001_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
u8 dev_mask;
|
||||||
|
unsigned int reg_byte;
|
||||||
|
__le16 reg_word;
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
switch (chan->type) {
|
||||||
|
case IIO_LIGHT:
|
||||||
|
dev_mask = PA12203001_ALS_EN_MASK;
|
||||||
|
ret = pa12203001_set_power_state(data, true, dev_mask);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* ALS ADC value is stored in registers
|
||||||
|
* PA12203001_REG_ADL and in PA12203001_REG_ADL + 1.
|
||||||
|
*/
|
||||||
|
ret = regmap_bulk_read(data->map, PA12203001_REG_ADL,
|
||||||
|
®_word, 2);
|
||||||
|
if (ret < 0)
|
||||||
|
goto reg_err;
|
||||||
|
|
||||||
|
*val = le16_to_cpu(reg_word);
|
||||||
|
ret = pa12203001_set_power_state(data, false, dev_mask);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
case IIO_PROXIMITY:
|
||||||
|
dev_mask = PA12203001_PX_EN_MASK;
|
||||||
|
ret = pa12203001_set_power_state(data, true, dev_mask);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = regmap_read(data->map, PA12203001_REG_PDH,
|
||||||
|
®_byte);
|
||||||
|
if (ret < 0)
|
||||||
|
goto reg_err;
|
||||||
|
|
||||||
|
*val = reg_byte;
|
||||||
|
ret = pa12203001_set_power_state(data, false, dev_mask);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
ret = regmap_read(data->map, PA12203001_REG_CFG0, ®_byte);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
*val = 0;
|
||||||
|
reg_byte = (reg_byte & PA12203001_AFSR_MASK);
|
||||||
|
*val2 = pa12203001_scales[reg_byte >> 4];
|
||||||
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_err:
|
||||||
|
pa12203001_set_power_state(data, false, dev_mask);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa12203001_write_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan, int val,
|
||||||
|
int val2, long mask)
|
||||||
|
{
|
||||||
|
struct pa12203001_data *data = iio_priv(indio_dev);
|
||||||
|
int i, ret, new_val;
|
||||||
|
unsigned int reg_byte;
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
ret = regmap_read(data->map, PA12203001_REG_CFG0, ®_byte);
|
||||||
|
if (val != 0 || ret < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(pa12203001_scales); i++) {
|
||||||
|
if (val2 == pa12203001_scales[i]) {
|
||||||
|
new_val = i << PA12203001_AFSR_SHIFT;
|
||||||
|
return regmap_update_bits(data->map,
|
||||||
|
PA12203001_REG_CFG0,
|
||||||
|
PA12203001_AFSR_MASK,
|
||||||
|
new_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_info pa12203001_info = {
|
||||||
|
.driver_module = THIS_MODULE,
|
||||||
|
.read_raw = pa12203001_read_raw,
|
||||||
|
.write_raw = pa12203001_write_raw,
|
||||||
|
.attrs = &pa12203001_attr_group,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pa12203001_init(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct pa12203001_data *data = iio_priv(indio_dev);
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(regvals); i++) {
|
||||||
|
ret = regmap_write(data->map, regvals[i].reg, regvals[i].val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa12203001_power_chip(struct iio_dev *indio_dev, u8 state)
|
||||||
|
{
|
||||||
|
struct pa12203001_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = pa12203001_als_enable(data, state);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = pa12203001_px_enable(data, state);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa12203001_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct pa12203001_data *data;
|
||||||
|
struct iio_dev *indio_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
indio_dev = devm_iio_device_alloc(&client->dev,
|
||||||
|
sizeof(struct pa12203001_data));
|
||||||
|
if (!indio_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
data = iio_priv(indio_dev);
|
||||||
|
i2c_set_clientdata(client, indio_dev);
|
||||||
|
data->client = client;
|
||||||
|
|
||||||
|
data->map = devm_regmap_init_i2c(client, &pa12203001_regmap_config);
|
||||||
|
if (IS_ERR(data->map))
|
||||||
|
return PTR_ERR(data->map);
|
||||||
|
|
||||||
|
mutex_init(&data->lock);
|
||||||
|
|
||||||
|
indio_dev->dev.parent = &client->dev;
|
||||||
|
indio_dev->info = &pa12203001_info;
|
||||||
|
indio_dev->name = PA12203001_DRIVER_NAME;
|
||||||
|
indio_dev->channels = pa12203001_channels;
|
||||||
|
indio_dev->num_channels = ARRAY_SIZE(pa12203001_channels);
|
||||||
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
|
|
||||||
|
ret = pa12203001_init(indio_dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pa12203001_power_chip(indio_dev, PA12203001_CHIP_ENABLE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_set_active(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_enable(&client->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||||
|
PA12203001_SLEEP_DELAY_MS);
|
||||||
|
pm_runtime_use_autosuspend(&client->dev);
|
||||||
|
|
||||||
|
return iio_device_register(indio_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa12203001_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
iio_device_unregister(indio_dev);
|
||||||
|
|
||||||
|
pm_runtime_disable(&client->dev);
|
||||||
|
pm_runtime_set_suspended(&client->dev);
|
||||||
|
|
||||||
|
return pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM)
|
||||||
|
static int pa12203001_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
|
||||||
|
return pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int pa12203001_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
|
||||||
|
return pa12203001_power_chip(indio_dev, PA12203001_CHIP_ENABLE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int pa12203001_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pa12203001_data *data;
|
||||||
|
|
||||||
|
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||||
|
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
if (data->als_needs_enable) {
|
||||||
|
pa12203001_als_enable(data, PA12203001_ALS_EN_MASK);
|
||||||
|
data->als_needs_enable = false;
|
||||||
|
}
|
||||||
|
if (data->px_needs_enable) {
|
||||||
|
pa12203001_px_enable(data, PA12203001_PX_EN_MASK);
|
||||||
|
data->px_needs_enable = false;
|
||||||
|
}
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops pa12203001_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(pa12203001_suspend, pa12203001_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(pa12203001_suspend, pa12203001_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct acpi_device_id pa12203001_acpi_match[] = {
|
||||||
|
{ "TXCPA122", 0},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(acpi, pa12203001_acpi_match);
|
||||||
|
|
||||||
|
static const struct i2c_device_id pa12203001_id[] = {
|
||||||
|
{"txcpa122", 0},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(i2c, pa12203001_id);
|
||||||
|
|
||||||
|
static struct i2c_driver pa12203001_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = PA12203001_DRIVER_NAME,
|
||||||
|
.pm = &pa12203001_pm_ops,
|
||||||
|
.acpi_match_table = ACPI_PTR(pa12203001_acpi_match),
|
||||||
|
},
|
||||||
|
.probe = pa12203001_probe,
|
||||||
|
.remove = pa12203001_remove,
|
||||||
|
.id_table = pa12203001_id,
|
||||||
|
|
||||||
|
};
|
||||||
|
module_i2c_driver(pa12203001_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>");
|
||||||
|
MODULE_DESCRIPTION("Driver for TXC PA12203001 Proximity and Light Sensor");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,615 @@
|
||||||
|
/*
|
||||||
|
* RPR-0521 ROHM Ambient Light and Proximity Sensor
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015, Intel Corporation.
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of version 2 of
|
||||||
|
* the GNU General Public License. See the file COPYING in the main
|
||||||
|
* directory of this archive for more details.
|
||||||
|
*
|
||||||
|
* IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
|
||||||
|
*
|
||||||
|
* TODO: illuminance channel, PM support, buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/iio/sysfs.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
|
#define RPR0521_REG_SYSTEM_CTRL 0x40
|
||||||
|
#define RPR0521_REG_MODE_CTRL 0x41
|
||||||
|
#define RPR0521_REG_ALS_CTRL 0x42
|
||||||
|
#define RPR0521_REG_PXS_CTRL 0x43
|
||||||
|
#define RPR0521_REG_PXS_DATA 0x44 /* 16-bit, little endian */
|
||||||
|
#define RPR0521_REG_ALS_DATA0 0x46 /* 16-bit, little endian */
|
||||||
|
#define RPR0521_REG_ALS_DATA1 0x48 /* 16-bit, little endian */
|
||||||
|
#define RPR0521_REG_ID 0x92
|
||||||
|
|
||||||
|
#define RPR0521_MODE_ALS_MASK BIT(7)
|
||||||
|
#define RPR0521_MODE_PXS_MASK BIT(6)
|
||||||
|
#define RPR0521_MODE_MEAS_TIME_MASK GENMASK(3, 0)
|
||||||
|
#define RPR0521_ALS_DATA0_GAIN_MASK GENMASK(5, 4)
|
||||||
|
#define RPR0521_ALS_DATA0_GAIN_SHIFT 4
|
||||||
|
#define RPR0521_ALS_DATA1_GAIN_MASK GENMASK(3, 2)
|
||||||
|
#define RPR0521_ALS_DATA1_GAIN_SHIFT 2
|
||||||
|
#define RPR0521_PXS_GAIN_MASK GENMASK(5, 4)
|
||||||
|
#define RPR0521_PXS_GAIN_SHIFT 4
|
||||||
|
|
||||||
|
#define RPR0521_MODE_ALS_ENABLE BIT(7)
|
||||||
|
#define RPR0521_MODE_ALS_DISABLE 0x00
|
||||||
|
#define RPR0521_MODE_PXS_ENABLE BIT(6)
|
||||||
|
#define RPR0521_MODE_PXS_DISABLE 0x00
|
||||||
|
|
||||||
|
#define RPR0521_MANUFACT_ID 0xE0
|
||||||
|
#define RPR0521_DEFAULT_MEAS_TIME 0x06 /* ALS - 100ms, PXS - 100ms */
|
||||||
|
|
||||||
|
#define RPR0521_DRV_NAME "RPR0521"
|
||||||
|
#define RPR0521_REGMAP_NAME "rpr0521_regmap"
|
||||||
|
|
||||||
|
#define RPR0521_SLEEP_DELAY_MS 2000
|
||||||
|
|
||||||
|
#define RPR0521_ALS_SCALE_AVAIL "0.007812 0.015625 0.5 1"
|
||||||
|
#define RPR0521_PXS_SCALE_AVAIL "0.125 0.5 1"
|
||||||
|
|
||||||
|
struct rpr0521_gain {
|
||||||
|
int scale;
|
||||||
|
int uscale;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rpr0521_gain rpr0521_als_gain[4] = {
|
||||||
|
{1, 0}, /* x1 */
|
||||||
|
{0, 500000}, /* x2 */
|
||||||
|
{0, 15625}, /* x64 */
|
||||||
|
{0, 7812}, /* x128 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rpr0521_gain rpr0521_pxs_gain[3] = {
|
||||||
|
{1, 0}, /* x1 */
|
||||||
|
{0, 500000}, /* x2 */
|
||||||
|
{0, 125000}, /* x4 */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum rpr0521_channel {
|
||||||
|
RPR0521_CHAN_ALS_DATA0,
|
||||||
|
RPR0521_CHAN_ALS_DATA1,
|
||||||
|
RPR0521_CHAN_PXS,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rpr0521_reg_desc {
|
||||||
|
u8 address;
|
||||||
|
u8 device_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rpr0521_reg_desc rpr0521_data_reg[] = {
|
||||||
|
[RPR0521_CHAN_ALS_DATA0] = {
|
||||||
|
.address = RPR0521_REG_ALS_DATA0,
|
||||||
|
.device_mask = RPR0521_MODE_ALS_MASK,
|
||||||
|
},
|
||||||
|
[RPR0521_CHAN_ALS_DATA1] = {
|
||||||
|
.address = RPR0521_REG_ALS_DATA1,
|
||||||
|
.device_mask = RPR0521_MODE_ALS_MASK,
|
||||||
|
},
|
||||||
|
[RPR0521_CHAN_PXS] = {
|
||||||
|
.address = RPR0521_REG_PXS_DATA,
|
||||||
|
.device_mask = RPR0521_MODE_PXS_MASK,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rpr0521_gain_info {
|
||||||
|
u8 reg;
|
||||||
|
u8 mask;
|
||||||
|
u8 shift;
|
||||||
|
const struct rpr0521_gain *gain;
|
||||||
|
int size;
|
||||||
|
} rpr0521_gain[] = {
|
||||||
|
[RPR0521_CHAN_ALS_DATA0] = {
|
||||||
|
.reg = RPR0521_REG_ALS_CTRL,
|
||||||
|
.mask = RPR0521_ALS_DATA0_GAIN_MASK,
|
||||||
|
.shift = RPR0521_ALS_DATA0_GAIN_SHIFT,
|
||||||
|
.gain = rpr0521_als_gain,
|
||||||
|
.size = ARRAY_SIZE(rpr0521_als_gain),
|
||||||
|
},
|
||||||
|
[RPR0521_CHAN_ALS_DATA1] = {
|
||||||
|
.reg = RPR0521_REG_ALS_CTRL,
|
||||||
|
.mask = RPR0521_ALS_DATA1_GAIN_MASK,
|
||||||
|
.shift = RPR0521_ALS_DATA1_GAIN_SHIFT,
|
||||||
|
.gain = rpr0521_als_gain,
|
||||||
|
.size = ARRAY_SIZE(rpr0521_als_gain),
|
||||||
|
},
|
||||||
|
[RPR0521_CHAN_PXS] = {
|
||||||
|
.reg = RPR0521_REG_PXS_CTRL,
|
||||||
|
.mask = RPR0521_PXS_GAIN_MASK,
|
||||||
|
.shift = RPR0521_PXS_GAIN_SHIFT,
|
||||||
|
.gain = rpr0521_pxs_gain,
|
||||||
|
.size = ARRAY_SIZE(rpr0521_pxs_gain),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rpr0521_data {
|
||||||
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
/* protect device params updates (e.g state, gain) */
|
||||||
|
struct mutex lock;
|
||||||
|
|
||||||
|
/* device active status */
|
||||||
|
bool als_dev_en;
|
||||||
|
bool pxs_dev_en;
|
||||||
|
|
||||||
|
/* optimize runtime pm ops - enable device only if needed */
|
||||||
|
bool als_ps_need_en;
|
||||||
|
bool pxs_ps_need_en;
|
||||||
|
|
||||||
|
struct regmap *regmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static IIO_CONST_ATTR(in_intensity_scale_available, RPR0521_ALS_SCALE_AVAIL);
|
||||||
|
static IIO_CONST_ATTR(in_proximity_scale_available, RPR0521_PXS_SCALE_AVAIL);
|
||||||
|
|
||||||
|
static struct attribute *rpr0521_attributes[] = {
|
||||||
|
&iio_const_attr_in_intensity_scale_available.dev_attr.attr,
|
||||||
|
&iio_const_attr_in_proximity_scale_available.dev_attr.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group rpr0521_attribute_group = {
|
||||||
|
.attrs = rpr0521_attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct iio_chan_spec rpr0521_channels[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_INTENSITY,
|
||||||
|
.modified = 1,
|
||||||
|
.address = RPR0521_CHAN_ALS_DATA0,
|
||||||
|
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||||
|
BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IIO_INTENSITY,
|
||||||
|
.modified = 1,
|
||||||
|
.address = RPR0521_CHAN_ALS_DATA1,
|
||||||
|
.channel2 = IIO_MOD_LIGHT_IR,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||||
|
BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IIO_PROXIMITY,
|
||||||
|
.address = RPR0521_CHAN_PXS,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||||
|
BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rpr0521_als_enable(struct rpr0521_data *data, u8 status)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
|
||||||
|
RPR0521_MODE_ALS_MASK,
|
||||||
|
status);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->als_dev_en = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
|
||||||
|
RPR0521_MODE_PXS_MASK,
|
||||||
|
status);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->pxs_dev_en = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rpr0521_set_power_state - handles runtime PM state and sensors enabled status
|
||||||
|
*
|
||||||
|
* @data: rpr0521 device private data
|
||||||
|
* @on: state to be set for devices in @device_mask
|
||||||
|
* @device_mask: bitmask specifying for which device we need to update @on state
|
||||||
|
*
|
||||||
|
* We rely on rpr0521_runtime_resume to enable our @device_mask devices, but
|
||||||
|
* if (for example) PXS was enabled (pxs_dev_en = true) by a previous call to
|
||||||
|
* rpr0521_runtime_resume and we want to enable ALS we MUST set ALS enable
|
||||||
|
* bit of RPR0521_REG_MODE_CTRL here because rpr0521_runtime_resume will not
|
||||||
|
* be called twice.
|
||||||
|
*/
|
||||||
|
static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
|
||||||
|
u8 device_mask)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
int ret;
|
||||||
|
u8 update_mask = 0;
|
||||||
|
|
||||||
|
if (device_mask & RPR0521_MODE_ALS_MASK) {
|
||||||
|
if (on && !data->als_ps_need_en && data->pxs_dev_en)
|
||||||
|
update_mask |= RPR0521_MODE_ALS_MASK;
|
||||||
|
else
|
||||||
|
data->als_ps_need_en = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_mask & RPR0521_MODE_PXS_MASK) {
|
||||||
|
if (on && !data->pxs_ps_need_en && data->als_dev_en)
|
||||||
|
update_mask |= RPR0521_MODE_PXS_MASK;
|
||||||
|
else
|
||||||
|
data->pxs_ps_need_en = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update_mask) {
|
||||||
|
ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
|
||||||
|
update_mask, update_mask);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&data->client->dev);
|
||||||
|
} else {
|
||||||
|
pm_runtime_mark_last_busy(&data->client->dev);
|
||||||
|
ret = pm_runtime_put_autosuspend(&data->client->dev);
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&data->client->dev,
|
||||||
|
"Failed: rpr0521_set_power_state for %d, ret %d\n",
|
||||||
|
on, ret);
|
||||||
|
if (on)
|
||||||
|
pm_runtime_put_noidle(&data->client->dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_get_gain(struct rpr0521_data *data, int chan,
|
||||||
|
int *val, int *val2)
|
||||||
|
{
|
||||||
|
int ret, reg, idx;
|
||||||
|
|
||||||
|
ret = regmap_read(data->regmap, rpr0521_gain[chan].reg, ®);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
idx = (rpr0521_gain[chan].mask & reg) >> rpr0521_gain[chan].shift;
|
||||||
|
*val = rpr0521_gain[chan].gain[idx].scale;
|
||||||
|
*val2 = rpr0521_gain[chan].gain[idx].uscale;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_set_gain(struct rpr0521_data *data, int chan,
|
||||||
|
int val, int val2)
|
||||||
|
{
|
||||||
|
int i, idx = -EINVAL;
|
||||||
|
|
||||||
|
/* get gain index */
|
||||||
|
for (i = 0; i < rpr0521_gain[chan].size; i++)
|
||||||
|
if (val == rpr0521_gain[chan].gain[i].scale &&
|
||||||
|
val2 == rpr0521_gain[chan].gain[i].uscale) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx < 0)
|
||||||
|
return idx;
|
||||||
|
|
||||||
|
return regmap_update_bits(data->regmap, rpr0521_gain[chan].reg,
|
||||||
|
rpr0521_gain[chan].mask,
|
||||||
|
idx << rpr0521_gain[chan].shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_read_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan, int *val,
|
||||||
|
int *val2, long mask)
|
||||||
|
{
|
||||||
|
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
u8 device_mask;
|
||||||
|
__le16 raw_data;
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
device_mask = rpr0521_data_reg[chan->address].device_mask;
|
||||||
|
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = rpr0521_set_power_state(data, true, device_mask);
|
||||||
|
if (ret < 0) {
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regmap_bulk_read(data->regmap,
|
||||||
|
rpr0521_data_reg[chan->address].address,
|
||||||
|
&raw_data, 2);
|
||||||
|
if (ret < 0) {
|
||||||
|
rpr0521_set_power_state(data, false, device_mask);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rpr0521_set_power_state(data, false, device_mask);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
*val = le16_to_cpu(raw_data);
|
||||||
|
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = rpr0521_get_gain(data, chan->address, val, val2);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_write_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan, int val,
|
||||||
|
int val2, long mask)
|
||||||
|
{
|
||||||
|
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = rpr0521_set_gain(data, chan->address, val, val2);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_info rpr0521_info = {
|
||||||
|
.driver_module = THIS_MODULE,
|
||||||
|
.read_raw = rpr0521_read_raw,
|
||||||
|
.write_raw = rpr0521_write_raw,
|
||||||
|
.attrs = &rpr0521_attribute_group,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rpr0521_init(struct rpr0521_data *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
ret = regmap_read(data->regmap, RPR0521_REG_ID, &id);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&data->client->dev, "Failed to read REG_ID register\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id != RPR0521_MANUFACT_ID) {
|
||||||
|
dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n",
|
||||||
|
id, RPR0521_MANUFACT_ID);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set default measurement time - 100 ms for both ALS and PS */
|
||||||
|
ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
|
||||||
|
RPR0521_MODE_MEAS_TIME_MASK,
|
||||||
|
RPR0521_DEFAULT_MEAS_TIME);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("regmap_update_bits returned %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_poweroff(struct rpr0521_data *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
|
||||||
|
RPR0521_MODE_ALS_MASK |
|
||||||
|
RPR0521_MODE_PXS_MASK,
|
||||||
|
RPR0521_MODE_ALS_DISABLE |
|
||||||
|
RPR0521_MODE_PXS_DISABLE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->als_dev_en = false;
|
||||||
|
data->pxs_dev_en = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rpr0521_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case RPR0521_REG_MODE_CTRL:
|
||||||
|
case RPR0521_REG_ALS_CTRL:
|
||||||
|
case RPR0521_REG_PXS_CTRL:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config rpr0521_regmap_config = {
|
||||||
|
.name = RPR0521_REGMAP_NAME,
|
||||||
|
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
|
||||||
|
.max_register = RPR0521_REG_ID,
|
||||||
|
.cache_type = REGCACHE_RBTREE,
|
||||||
|
.volatile_reg = rpr0521_is_volatile_reg,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rpr0521_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct rpr0521_data *data;
|
||||||
|
struct iio_dev *indio_dev;
|
||||||
|
struct regmap *regmap;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||||
|
if (!indio_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_i2c(client, &rpr0521_regmap_config);
|
||||||
|
if (IS_ERR(regmap)) {
|
||||||
|
dev_err(&client->dev, "regmap_init 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);
|
||||||
|
|
||||||
|
indio_dev->dev.parent = &client->dev;
|
||||||
|
indio_dev->info = &rpr0521_info;
|
||||||
|
indio_dev->name = RPR0521_DRV_NAME;
|
||||||
|
indio_dev->channels = rpr0521_channels;
|
||||||
|
indio_dev->num_channels = ARRAY_SIZE(rpr0521_channels);
|
||||||
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
|
|
||||||
|
ret = rpr0521_init(data);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "rpr0521 chip init failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = iio_device_register(indio_dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_set_active(&client->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_iio_unregister;
|
||||||
|
|
||||||
|
pm_runtime_enable(&client->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
|
||||||
|
pm_runtime_use_autosuspend(&client->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_iio_unregister:
|
||||||
|
iio_device_unregister(indio_dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
pm_runtime_disable(&client->dev);
|
||||||
|
pm_runtime_set_suspended(&client->dev);
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
|
||||||
|
iio_device_unregister(indio_dev);
|
||||||
|
rpr0521_poweroff(iio_priv(indio_dev));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int rpr0521_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* disable channels and sets {als,pxs}_dev_en to false */
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = rpr0521_poweroff(data);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpr0521_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (data->als_ps_need_en) {
|
||||||
|
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
data->als_ps_need_en = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->pxs_ps_need_en) {
|
||||||
|
ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
data->pxs_ps_need_en = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops rpr0521_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(rpr0521_runtime_suspend,
|
||||||
|
rpr0521_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct acpi_device_id rpr0521_acpi_match[] = {
|
||||||
|
{"RPR0521", 0},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, rpr0521_acpi_match);
|
||||||
|
|
||||||
|
static const struct i2c_device_id rpr0521_id[] = {
|
||||||
|
{"rpr0521", 0},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(i2c, rpr0521_id);
|
||||||
|
|
||||||
|
static struct i2c_driver rpr0521_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = RPR0521_DRV_NAME,
|
||||||
|
.pm = &rpr0521_pm_ops,
|
||||||
|
.acpi_match_table = ACPI_PTR(rpr0521_acpi_match),
|
||||||
|
},
|
||||||
|
.probe = rpr0521_probe,
|
||||||
|
.remove = rpr0521_remove,
|
||||||
|
.id_table = rpr0521_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(rpr0521_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
|
||||||
|
MODULE_DESCRIPTION("RPR0521 ROHM Ambient Light and Proximity Sensor driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -676,6 +676,7 @@ static const struct i2c_device_id stk3310_i2c_id[] = {
|
||||||
{"STK3311", 0},
|
{"STK3311", 0},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id);
|
||||||
|
|
||||||
static const struct acpi_device_id stk3310_acpi_id[] = {
|
static const struct acpi_device_id stk3310_acpi_id[] = {
|
||||||
{"STK3310", 0},
|
{"STK3310", 0},
|
||||||
|
|
|
@ -392,7 +392,6 @@ static struct i2c_driver tcs3414_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = TCS3414_DRV_NAME,
|
.name = TCS3414_DRV_NAME,
|
||||||
.pm = &tcs3414_pm_ops,
|
.pm = &tcs3414_pm_ops,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = tcs3414_probe,
|
.probe = tcs3414_probe,
|
||||||
.remove = tcs3414_remove,
|
.remove = tcs3414_remove,
|
||||||
|
|
|
@ -366,7 +366,6 @@ static struct i2c_driver tcs3472_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = TCS3472_DRV_NAME,
|
.name = TCS3472_DRV_NAME,
|
||||||
.pm = &tcs3472_pm_ops,
|
.pm = &tcs3472_pm_ops,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = tcs3472_probe,
|
.probe = tcs3472_probe,
|
||||||
.remove = tcs3472_remove,
|
.remove = tcs3472_remove,
|
||||||
|
|
|
@ -247,7 +247,6 @@ static struct i2c_driver tsl4531_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = TSL4531_DRV_NAME,
|
.name = TSL4531_DRV_NAME,
|
||||||
.pm = TSL4531_PM_OPS,
|
.pm = TSL4531_PM_OPS,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = tsl4531_probe,
|
.probe = tsl4531_probe,
|
||||||
.remove = tsl4531_remove,
|
.remove = tsl4531_remove,
|
||||||
|
|
|
@ -185,7 +185,6 @@ static int vcnl4000_probe(struct i2c_client *client,
|
||||||
static struct i2c_driver vcnl4000_driver = {
|
static struct i2c_driver vcnl4000_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = VCNL4000_DRV_NAME,
|
.name = VCNL4000_DRV_NAME,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = vcnl4000_probe,
|
.probe = vcnl4000_probe,
|
||||||
.id_table = vcnl4000_id,
|
.id_table = vcnl4000_id,
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
#define BMC150_MAGN_REG_HIGH_THRESH 0x50
|
#define BMC150_MAGN_REG_HIGH_THRESH 0x50
|
||||||
#define BMC150_MAGN_REG_REP_XY 0x51
|
#define BMC150_MAGN_REG_REP_XY 0x51
|
||||||
#define BMC150_MAGN_REG_REP_Z 0x52
|
#define BMC150_MAGN_REG_REP_Z 0x52
|
||||||
|
#define BMC150_MAGN_REG_REP_DATAMASK GENMASK(7, 0)
|
||||||
|
|
||||||
#define BMC150_MAGN_REG_TRIM_START 0x5D
|
#define BMC150_MAGN_REG_TRIM_START 0x5D
|
||||||
#define BMC150_MAGN_REG_TRIM_END 0x71
|
#define BMC150_MAGN_REG_TRIM_END 0x71
|
||||||
|
@ -559,7 +560,7 @@ static int bmc150_magn_write_raw(struct iio_dev *indio_dev,
|
||||||
}
|
}
|
||||||
ret = regmap_update_bits(data->regmap,
|
ret = regmap_update_bits(data->regmap,
|
||||||
BMC150_MAGN_REG_REP_XY,
|
BMC150_MAGN_REG_REP_XY,
|
||||||
0xFF,
|
BMC150_MAGN_REG_REP_DATAMASK,
|
||||||
BMC150_MAGN_REPXY_TO_REGVAL
|
BMC150_MAGN_REPXY_TO_REGVAL
|
||||||
(val));
|
(val));
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
@ -575,7 +576,7 @@ static int bmc150_magn_write_raw(struct iio_dev *indio_dev,
|
||||||
}
|
}
|
||||||
ret = regmap_update_bits(data->regmap,
|
ret = regmap_update_bits(data->regmap,
|
||||||
BMC150_MAGN_REG_REP_Z,
|
BMC150_MAGN_REG_REP_Z,
|
||||||
0xFF,
|
BMC150_MAGN_REG_REP_DATAMASK,
|
||||||
BMC150_MAGN_REPZ_TO_REGVAL
|
BMC150_MAGN_REPZ_TO_REGVAL
|
||||||
(val));
|
(val));
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
@ -588,17 +589,6 @@ static int bmc150_magn_write_raw(struct iio_dev *indio_dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bmc150_magn_validate_trigger(struct iio_dev *indio_dev,
|
|
||||||
struct iio_trigger *trig)
|
|
||||||
{
|
|
||||||
struct bmc150_magn_data *data = iio_priv(indio_dev);
|
|
||||||
|
|
||||||
if (data->dready_trig != trig)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t bmc150_magn_show_samp_freq_avail(struct device *dev,
|
static ssize_t bmc150_magn_show_samp_freq_avail(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
|
@ -659,11 +649,12 @@ static const struct iio_info bmc150_magn_info = {
|
||||||
.attrs = &bmc150_magn_attrs_group,
|
.attrs = &bmc150_magn_attrs_group,
|
||||||
.read_raw = bmc150_magn_read_raw,
|
.read_raw = bmc150_magn_read_raw,
|
||||||
.write_raw = bmc150_magn_write_raw,
|
.write_raw = bmc150_magn_write_raw,
|
||||||
.validate_trigger = bmc150_magn_validate_trigger,
|
|
||||||
.driver_module = THIS_MODULE,
|
.driver_module = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned long bmc150_magn_scan_masks[] = {0x07, 0};
|
static const unsigned long bmc150_magn_scan_masks[] = {
|
||||||
|
BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
|
||||||
|
0};
|
||||||
|
|
||||||
static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p)
|
static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p)
|
||||||
{
|
{
|
||||||
|
@ -674,7 +665,6 @@ static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p)
|
||||||
|
|
||||||
mutex_lock(&data->mutex);
|
mutex_lock(&data->mutex);
|
||||||
ret = bmc150_magn_read_xyz(data, data->buffer);
|
ret = bmc150_magn_read_xyz(data, data->buffer);
|
||||||
mutex_unlock(&data->mutex);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
@ -682,7 +672,8 @@ static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p)
|
||||||
pf->timestamp);
|
pf->timestamp);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
iio_trigger_notify_done(data->dready_trig);
|
mutex_unlock(&data->mutex);
|
||||||
|
iio_trigger_notify_done(indio_dev->trig);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -793,29 +784,23 @@ static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||||
if (state == data->dready_trigger_on)
|
if (state == data->dready_trigger_on)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
||||||
ret = bmc150_magn_set_power_state(data, state);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err_unlock;
|
|
||||||
|
|
||||||
ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY,
|
ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY,
|
||||||
BMC150_MAGN_MASK_DRDY_EN,
|
BMC150_MAGN_MASK_DRDY_EN,
|
||||||
state << BMC150_MAGN_SHIFT_DRDY_EN);
|
state << BMC150_MAGN_SHIFT_DRDY_EN);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_poweroff;
|
goto err_unlock;
|
||||||
|
|
||||||
data->dready_trigger_on = state;
|
data->dready_trigger_on = state;
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
ret = bmc150_magn_reset_intr(data);
|
ret = bmc150_magn_reset_intr(data);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_poweroff;
|
goto err_unlock;
|
||||||
}
|
}
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_poweroff:
|
|
||||||
bmc150_magn_set_power_state(data, false);
|
|
||||||
err_unlock:
|
err_unlock:
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -827,6 +812,27 @@ static const struct iio_trigger_ops bmc150_magn_trigger_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int bmc150_magn_buffer_preenable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct bmc150_magn_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return bmc150_magn_set_power_state(data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bmc150_magn_buffer_postdisable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct bmc150_magn_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return bmc150_magn_set_power_state(data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_buffer_setup_ops bmc150_magn_buffer_setup_ops = {
|
||||||
|
.preenable = bmc150_magn_buffer_preenable,
|
||||||
|
.postenable = iio_triggered_buffer_postenable,
|
||||||
|
.predisable = iio_triggered_buffer_predisable,
|
||||||
|
.postdisable = bmc150_magn_buffer_postdisable,
|
||||||
|
};
|
||||||
|
|
||||||
static int bmc150_magn_gpio_probe(struct i2c_client *client)
|
static int bmc150_magn_gpio_probe(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
@ -932,16 +938,6 @@ static int bmc150_magn_probe(struct i2c_client *client,
|
||||||
goto err_poweroff;
|
goto err_poweroff;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = iio_triggered_buffer_setup(indio_dev,
|
|
||||||
&iio_pollfunc_store_time,
|
|
||||||
bmc150_magn_trigger_handler,
|
|
||||||
NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&client->dev,
|
|
||||||
"iio triggered buffer setup failed\n");
|
|
||||||
goto err_trigger_unregister;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = request_threaded_irq(client->irq,
|
ret = request_threaded_irq(client->irq,
|
||||||
iio_trigger_generic_data_rdy_poll,
|
iio_trigger_generic_data_rdy_poll,
|
||||||
NULL,
|
NULL,
|
||||||
|
@ -951,14 +947,24 @@ static int bmc150_magn_probe(struct i2c_client *client,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "request irq %d failed\n",
|
dev_err(&client->dev, "request irq %d failed\n",
|
||||||
client->irq);
|
client->irq);
|
||||||
goto err_buffer_cleanup;
|
goto err_trigger_unregister;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = iio_triggered_buffer_setup(indio_dev,
|
||||||
|
iio_pollfunc_store_time,
|
||||||
|
bmc150_magn_trigger_handler,
|
||||||
|
&bmc150_magn_buffer_setup_ops);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"iio triggered buffer setup failed\n");
|
||||||
|
goto err_free_irq;
|
||||||
|
}
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev, "unable to register iio device\n");
|
dev_err(&client->dev, "unable to register iio device\n");
|
||||||
goto err_free_irq;
|
goto err_buffer_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pm_runtime_set_active(&client->dev);
|
ret = pm_runtime_set_active(&client->dev);
|
||||||
|
@ -976,12 +982,11 @@ static int bmc150_magn_probe(struct i2c_client *client,
|
||||||
|
|
||||||
err_iio_unregister:
|
err_iio_unregister:
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
|
err_buffer_cleanup:
|
||||||
|
iio_triggered_buffer_cleanup(indio_dev);
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
if (client->irq > 0)
|
if (client->irq > 0)
|
||||||
free_irq(client->irq, data->dready_trig);
|
free_irq(client->irq, data->dready_trig);
|
||||||
err_buffer_cleanup:
|
|
||||||
if (data->dready_trig)
|
|
||||||
iio_triggered_buffer_cleanup(indio_dev);
|
|
||||||
err_trigger_unregister:
|
err_trigger_unregister:
|
||||||
if (data->dready_trig)
|
if (data->dready_trig)
|
||||||
iio_trigger_unregister(data->dready_trig);
|
iio_trigger_unregister(data->dready_trig);
|
||||||
|
@ -1000,14 +1005,13 @@ static int bmc150_magn_remove(struct i2c_client *client)
|
||||||
pm_runtime_put_noidle(&client->dev);
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
|
iio_triggered_buffer_cleanup(indio_dev);
|
||||||
|
|
||||||
if (client->irq > 0)
|
if (client->irq > 0)
|
||||||
free_irq(data->client->irq, data->dready_trig);
|
free_irq(data->client->irq, data->dready_trig);
|
||||||
|
|
||||||
if (data->dready_trig) {
|
if (data->dready_trig)
|
||||||
iio_triggered_buffer_cleanup(indio_dev);
|
|
||||||
iio_trigger_unregister(data->dready_trig);
|
iio_trigger_unregister(data->dready_trig);
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&data->mutex);
|
mutex_lock(&data->mutex);
|
||||||
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
||||||
|
@ -1034,6 +1038,9 @@ static int bmc150_magn_runtime_suspend(struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Should be called with data->mutex held.
|
||||||
|
*/
|
||||||
static int bmc150_magn_runtime_resume(struct device *dev)
|
static int bmc150_magn_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
@ -1082,12 +1089,14 @@ static const struct dev_pm_ops bmc150_magn_pm_ops = {
|
||||||
|
|
||||||
static const struct acpi_device_id bmc150_magn_acpi_match[] = {
|
static const struct acpi_device_id bmc150_magn_acpi_match[] = {
|
||||||
{"BMC150B", 0},
|
{"BMC150B", 0},
|
||||||
|
{"BMC156B", 0},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
|
MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
|
||||||
|
|
||||||
static const struct i2c_device_id bmc150_magn_id[] = {
|
static const struct i2c_device_id bmc150_magn_id[] = {
|
||||||
{"bmc150_magn", 0},
|
{"bmc150_magn", 0},
|
||||||
|
{"bmc156_magn", 0},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, bmc150_magn_id);
|
MODULE_DEVICE_TABLE(i2c, bmc150_magn_id);
|
||||||
|
|
|
@ -316,31 +316,31 @@ static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3])
|
||||||
static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
|
static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
|
||||||
__le16 buf[], int *val)
|
__le16 buf[], int *val)
|
||||||
{
|
{
|
||||||
int raw_x, raw_y, raw_z;
|
int raw[3];
|
||||||
int sens_x, sens_y, sens_z;
|
int sens[3];
|
||||||
int nfo;
|
int nfo;
|
||||||
|
|
||||||
raw_x = le16_to_cpu(buf[AXIS_X]);
|
raw[AXIS_X] = le16_to_cpu(buf[AXIS_X]);
|
||||||
raw_y = le16_to_cpu(buf[AXIS_Y]);
|
raw[AXIS_Y] = le16_to_cpu(buf[AXIS_Y]);
|
||||||
raw_z = le16_to_cpu(buf[AXIS_Z]);
|
raw[AXIS_Z] = le16_to_cpu(buf[AXIS_Z]);
|
||||||
|
|
||||||
sens_x = mmc35240_props_table[data->res].sens[AXIS_X];
|
sens[AXIS_X] = mmc35240_props_table[data->res].sens[AXIS_X];
|
||||||
sens_y = mmc35240_props_table[data->res].sens[AXIS_Y];
|
sens[AXIS_Y] = mmc35240_props_table[data->res].sens[AXIS_Y];
|
||||||
sens_z = mmc35240_props_table[data->res].sens[AXIS_Z];
|
sens[AXIS_Z] = mmc35240_props_table[data->res].sens[AXIS_Z];
|
||||||
|
|
||||||
nfo = mmc35240_props_table[data->res].nfo;
|
nfo = mmc35240_props_table[data->res].nfo;
|
||||||
|
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case AXIS_X:
|
case AXIS_X:
|
||||||
*val = (raw_x - nfo) * 1000 / sens_x;
|
*val = (raw[AXIS_X] - nfo) * 1000 / sens[AXIS_X];
|
||||||
break;
|
break;
|
||||||
case AXIS_Y:
|
case AXIS_Y:
|
||||||
*val = (raw_y - nfo) * 1000 / sens_y -
|
*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] -
|
||||||
(raw_z - nfo) * 1000 / sens_z;
|
(raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z];
|
||||||
break;
|
break;
|
||||||
case AXIS_Z:
|
case AXIS_Z:
|
||||||
*val = (raw_y - nfo) * 1000 / sens_y +
|
*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] +
|
||||||
(raw_z - nfo) * 1000 / sens_z;
|
(raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -559,6 +559,12 @@ static const struct dev_pm_ops mmc35240_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id mmc35240_of_match[] = {
|
||||||
|
{ .compatible = "memsic,mmc35240", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, mmc35240_of_match);
|
||||||
|
|
||||||
static const struct acpi_device_id mmc35240_acpi_match[] = {
|
static const struct acpi_device_id mmc35240_acpi_match[] = {
|
||||||
{"MMC35240", 0},
|
{"MMC35240", 0},
|
||||||
{ },
|
{ },
|
||||||
|
@ -574,6 +580,7 @@ MODULE_DEVICE_TABLE(i2c, mmc35240_id);
|
||||||
static struct i2c_driver mmc35240_driver = {
|
static struct i2c_driver mmc35240_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = MMC35240_DRV_NAME,
|
.name = MMC35240_DRV_NAME,
|
||||||
|
.of_match_table = mmc35240_of_match,
|
||||||
.pm = &mmc35240_pm_ops,
|
.pm = &mmc35240_pm_ops,
|
||||||
.acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
|
.acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
|
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
|
||||||
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
|
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
|
||||||
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
||||||
|
#define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn"
|
||||||
|
|
||||||
int st_magn_common_probe(struct iio_dev *indio_dev);
|
int st_magn_common_probe(struct iio_dev *indio_dev);
|
||||||
void st_magn_common_remove(struct iio_dev *indio_dev);
|
void st_magn_common_remove(struct iio_dev *indio_dev);
|
||||||
|
@ -25,6 +26,8 @@ void st_magn_common_remove(struct iio_dev *indio_dev);
|
||||||
#ifdef CONFIG_IIO_BUFFER
|
#ifdef CONFIG_IIO_BUFFER
|
||||||
int st_magn_allocate_ring(struct iio_dev *indio_dev);
|
int st_magn_allocate_ring(struct iio_dev *indio_dev);
|
||||||
void st_magn_deallocate_ring(struct iio_dev *indio_dev);
|
void st_magn_deallocate_ring(struct iio_dev *indio_dev);
|
||||||
|
int st_magn_trig_set_state(struct iio_trigger *trig, bool state);
|
||||||
|
#define ST_MAGN_TRIGGER_SET_STATE (&st_magn_trig_set_state)
|
||||||
#else /* CONFIG_IIO_BUFFER */
|
#else /* CONFIG_IIO_BUFFER */
|
||||||
static inline int st_magn_probe_trigger(struct iio_dev *indio_dev, int irq)
|
static inline int st_magn_probe_trigger(struct iio_dev *indio_dev, int irq)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,6 +23,13 @@
|
||||||
#include <linux/iio/common/st_sensors.h>
|
#include <linux/iio/common/st_sensors.h>
|
||||||
#include "st_magn.h"
|
#include "st_magn.h"
|
||||||
|
|
||||||
|
int st_magn_trig_set_state(struct iio_trigger *trig, bool state)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||||
|
|
||||||
|
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||||
|
}
|
||||||
|
|
||||||
static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
|
static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
|
||||||
{
|
{
|
||||||
return st_sensors_set_enable(indio_dev, true);
|
return st_sensors_set_enable(indio_dev, true);
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#define ST_MAGN_FS_AVL_8000MG 8000
|
#define ST_MAGN_FS_AVL_8000MG 8000
|
||||||
#define ST_MAGN_FS_AVL_8100MG 8100
|
#define ST_MAGN_FS_AVL_8100MG 8100
|
||||||
#define ST_MAGN_FS_AVL_12000MG 12000
|
#define ST_MAGN_FS_AVL_12000MG 12000
|
||||||
|
#define ST_MAGN_FS_AVL_15000MG 15000
|
||||||
#define ST_MAGN_FS_AVL_16000MG 16000
|
#define ST_MAGN_FS_AVL_16000MG 16000
|
||||||
|
|
||||||
/* CUSTOM VALUES FOR SENSOR 0 */
|
/* CUSTOM VALUES FOR SENSOR 0 */
|
||||||
|
@ -157,6 +158,29 @@
|
||||||
#define ST_MAGN_2_OUT_Y_L_ADDR 0x2a
|
#define ST_MAGN_2_OUT_Y_L_ADDR 0x2a
|
||||||
#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c
|
#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c
|
||||||
|
|
||||||
|
/* CUSTOM VALUES FOR SENSOR 3 */
|
||||||
|
#define ST_MAGN_3_WAI_ADDR 0x4f
|
||||||
|
#define ST_MAGN_3_WAI_EXP 0x40
|
||||||
|
#define ST_MAGN_3_ODR_ADDR 0x60
|
||||||
|
#define ST_MAGN_3_ODR_MASK 0x0c
|
||||||
|
#define ST_MAGN_3_ODR_AVL_10HZ_VAL 0x00
|
||||||
|
#define ST_MAGN_3_ODR_AVL_20HZ_VAL 0x01
|
||||||
|
#define ST_MAGN_3_ODR_AVL_50HZ_VAL 0x02
|
||||||
|
#define ST_MAGN_3_ODR_AVL_100HZ_VAL 0x03
|
||||||
|
#define ST_MAGN_3_PW_ADDR 0x60
|
||||||
|
#define ST_MAGN_3_PW_MASK 0x03
|
||||||
|
#define ST_MAGN_3_PW_ON 0x00
|
||||||
|
#define ST_MAGN_3_PW_OFF 0x03
|
||||||
|
#define ST_MAGN_3_BDU_ADDR 0x62
|
||||||
|
#define ST_MAGN_3_BDU_MASK 0x10
|
||||||
|
#define ST_MAGN_3_DRDY_IRQ_ADDR 0x62
|
||||||
|
#define ST_MAGN_3_DRDY_INT_MASK 0x01
|
||||||
|
#define ST_MAGN_3_FS_AVL_15000_GAIN 1500
|
||||||
|
#define ST_MAGN_3_MULTIREAD_BIT false
|
||||||
|
#define ST_MAGN_3_OUT_X_L_ADDR 0x68
|
||||||
|
#define ST_MAGN_3_OUT_Y_L_ADDR 0x6a
|
||||||
|
#define ST_MAGN_3_OUT_Z_L_ADDR 0x6c
|
||||||
|
|
||||||
static const struct iio_chan_spec st_magn_16bit_channels[] = {
|
static const struct iio_chan_spec st_magn_16bit_channels[] = {
|
||||||
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
|
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
|
||||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
@ -189,9 +213,26 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
|
||||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct iio_chan_spec st_magn_3_16bit_channels[] = {
|
||||||
|
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
|
||||||
|
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
|
||||||
|
ST_MAGN_3_OUT_X_L_ADDR),
|
||||||
|
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
|
||||||
|
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
|
||||||
|
ST_MAGN_3_OUT_Y_L_ADDR),
|
||||||
|
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
|
||||||
|
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
|
||||||
|
ST_MAGN_3_OUT_Z_L_ADDR),
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||||
{
|
{
|
||||||
.wai = 0, /* This sensor has no valid WhoAmI report 0 */
|
.wai = 0, /* This sensor has no valid WhoAmI report 0 */
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LSM303DLH_MAGN_DEV_NAME,
|
[0] = LSM303DLH_MAGN_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
@ -268,6 +309,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_MAGN_1_WAI_EXP,
|
.wai = ST_MAGN_1_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LSM303DLHC_MAGN_DEV_NAME,
|
[0] = LSM303DLHC_MAGN_DEV_NAME,
|
||||||
[1] = LSM303DLM_MAGN_DEV_NAME,
|
[1] = LSM303DLM_MAGN_DEV_NAME,
|
||||||
|
@ -346,6 +388,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_MAGN_2_WAI_EXP,
|
.wai = ST_MAGN_2_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LIS3MDL_MAGN_DEV_NAME,
|
[0] = LIS3MDL_MAGN_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
@ -399,6 +442,48 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||||
.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
|
.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
|
||||||
.bootime = 2,
|
.bootime = 2,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.wai = ST_MAGN_3_WAI_EXP,
|
||||||
|
.wai_addr = ST_MAGN_3_WAI_ADDR,
|
||||||
|
.sensors_supported = {
|
||||||
|
[0] = LSM303AGR_MAGN_DEV_NAME,
|
||||||
|
},
|
||||||
|
.ch = (struct iio_chan_spec *)st_magn_3_16bit_channels,
|
||||||
|
.odr = {
|
||||||
|
.addr = ST_MAGN_3_ODR_ADDR,
|
||||||
|
.mask = ST_MAGN_3_ODR_MASK,
|
||||||
|
.odr_avl = {
|
||||||
|
{ 10, ST_MAGN_3_ODR_AVL_10HZ_VAL, },
|
||||||
|
{ 20, ST_MAGN_3_ODR_AVL_20HZ_VAL, },
|
||||||
|
{ 50, ST_MAGN_3_ODR_AVL_50HZ_VAL, },
|
||||||
|
{ 100, ST_MAGN_3_ODR_AVL_100HZ_VAL, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.pw = {
|
||||||
|
.addr = ST_MAGN_3_PW_ADDR,
|
||||||
|
.mask = ST_MAGN_3_PW_MASK,
|
||||||
|
.value_on = ST_MAGN_3_PW_ON,
|
||||||
|
.value_off = ST_MAGN_3_PW_OFF,
|
||||||
|
},
|
||||||
|
.fs = {
|
||||||
|
.fs_avl = {
|
||||||
|
[0] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_15000MG,
|
||||||
|
.gain = ST_MAGN_3_FS_AVL_15000_GAIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.bdu = {
|
||||||
|
.addr = ST_MAGN_3_BDU_ADDR,
|
||||||
|
.mask = ST_MAGN_3_BDU_MASK,
|
||||||
|
},
|
||||||
|
.drdy_irq = {
|
||||||
|
.addr = ST_MAGN_3_DRDY_IRQ_ADDR,
|
||||||
|
.mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
|
||||||
|
},
|
||||||
|
.multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
|
||||||
|
.bootime = 2,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int st_magn_read_raw(struct iio_dev *indio_dev,
|
static int st_magn_read_raw(struct iio_dev *indio_dev,
|
||||||
|
@ -477,6 +562,16 @@ static const struct iio_info magn_info = {
|
||||||
.write_raw = &st_magn_write_raw,
|
.write_raw = &st_magn_write_raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_IIO_TRIGGER
|
||||||
|
static const struct iio_trigger_ops st_magn_trigger_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.set_trigger_state = ST_MAGN_TRIGGER_SET_STATE,
|
||||||
|
};
|
||||||
|
#define ST_MAGN_TRIGGER_OPS (&st_magn_trigger_ops)
|
||||||
|
#else
|
||||||
|
#define ST_MAGN_TRIGGER_OPS NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
int st_magn_common_probe(struct iio_dev *indio_dev)
|
int st_magn_common_probe(struct iio_dev *indio_dev)
|
||||||
{
|
{
|
||||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||||
|
@ -513,7 +608,8 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (irq > 0) {
|
if (irq > 0) {
|
||||||
err = st_sensors_allocate_trigger(indio_dev, NULL);
|
err = st_sensors_allocate_trigger(indio_dev,
|
||||||
|
ST_MAGN_TRIGGER_OPS);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto st_magn_probe_trigger_error;
|
goto st_magn_probe_trigger_error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,10 @@ static const struct of_device_id st_magn_of_match[] = {
|
||||||
.compatible = "st,lis3mdl-magn",
|
.compatible = "st,lis3mdl-magn",
|
||||||
.data = LIS3MDL_MAGN_DEV_NAME,
|
.data = LIS3MDL_MAGN_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "st,lsm303agr-magn",
|
||||||
|
.data = LSM303AGR_MAGN_DEV_NAME,
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, st_magn_of_match);
|
MODULE_DEVICE_TABLE(of, st_magn_of_match);
|
||||||
|
@ -79,13 +83,13 @@ static const struct i2c_device_id st_magn_id_table[] = {
|
||||||
{ LSM303DLHC_MAGN_DEV_NAME },
|
{ LSM303DLHC_MAGN_DEV_NAME },
|
||||||
{ LSM303DLM_MAGN_DEV_NAME },
|
{ LSM303DLM_MAGN_DEV_NAME },
|
||||||
{ LIS3MDL_MAGN_DEV_NAME },
|
{ LIS3MDL_MAGN_DEV_NAME },
|
||||||
|
{ LSM303AGR_MAGN_DEV_NAME },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
|
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
|
||||||
|
|
||||||
static struct i2c_driver st_magn_driver = {
|
static struct i2c_driver st_magn_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "st-magn-i2c",
|
.name = "st-magn-i2c",
|
||||||
.of_match_table = of_match_ptr(st_magn_of_match),
|
.of_match_table = of_match_ptr(st_magn_of_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -51,6 +51,7 @@ static const struct spi_device_id st_magn_id_table[] = {
|
||||||
{ LSM303DLHC_MAGN_DEV_NAME },
|
{ LSM303DLHC_MAGN_DEV_NAME },
|
||||||
{ LSM303DLM_MAGN_DEV_NAME },
|
{ LSM303DLM_MAGN_DEV_NAME },
|
||||||
{ LIS3MDL_MAGN_DEV_NAME },
|
{ LIS3MDL_MAGN_DEV_NAME },
|
||||||
|
{ LSM303AGR_MAGN_DEV_NAME },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
|
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
|
||||||
|
|
|
@ -53,10 +53,10 @@ config MPL3115
|
||||||
will be called mpl3115.
|
will be called mpl3115.
|
||||||
|
|
||||||
config MS5611
|
config MS5611
|
||||||
tristate "Measurement Specialities MS5611 pressure sensor driver"
|
tristate "Measurement Specialties MS5611 pressure sensor driver"
|
||||||
help
|
help
|
||||||
Say Y here to build support for the Measurement Specialities
|
Say Y here to build support for the Measurement Specialties
|
||||||
MS5611 pressure and temperature sensor.
|
MS5611, MS5607 pressure and temperature sensors.
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called ms5611_core.
|
be called ms5611_core.
|
||||||
|
|
|
@ -27,6 +27,18 @@
|
||||||
|
|
||||||
#define MS5611_PROM_WORDS_NB 8
|
#define MS5611_PROM_WORDS_NB 8
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MS5611,
|
||||||
|
MS5607,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ms5611_chip_info {
|
||||||
|
u16 prom[MS5611_PROM_WORDS_NB];
|
||||||
|
|
||||||
|
int (*temp_and_pressure_compensate)(struct ms5611_chip_info *chip_info,
|
||||||
|
s32 *temp, s32 *pressure);
|
||||||
|
};
|
||||||
|
|
||||||
struct ms5611_state {
|
struct ms5611_state {
|
||||||
void *client;
|
void *client;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
@ -36,9 +48,9 @@ struct ms5611_state {
|
||||||
int (*read_adc_temp_and_pressure)(struct device *dev,
|
int (*read_adc_temp_and_pressure)(struct device *dev,
|
||||||
s32 *temp, s32 *pressure);
|
s32 *temp, s32 *pressure);
|
||||||
|
|
||||||
u16 prom[MS5611_PROM_WORDS_NB];
|
struct ms5611_chip_info *chip_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev);
|
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type);
|
||||||
|
|
||||||
#endif /* _MS5611_H */
|
#endif /* _MS5611_H */
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*
|
*
|
||||||
* Data sheet:
|
* Data sheet:
|
||||||
* http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
|
* http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
|
||||||
|
* http://www.meas-spec.com/downloads/MS5607-02BA03.pdf
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -50,7 +51,8 @@ static int ms5611_read_prom(struct iio_dev *indio_dev)
|
||||||
struct ms5611_state *st = iio_priv(indio_dev);
|
struct ms5611_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
|
for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
|
||||||
ret = st->read_prom_word(&indio_dev->dev, i, &st->prom[i]);
|
ret = st->read_prom_word(&indio_dev->dev,
|
||||||
|
i, &st->chip_info->prom[i]);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&indio_dev->dev,
|
dev_err(&indio_dev->dev,
|
||||||
"failed to read prom at %d\n", i);
|
"failed to read prom at %d\n", i);
|
||||||
|
@ -58,7 +60,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) {
|
if (!ms5611_prom_is_valid(st->chip_info->prom, MS5611_PROM_WORDS_NB)) {
|
||||||
dev_err(&indio_dev->dev, "PROM integrity check failed\n");
|
dev_err(&indio_dev->dev, "PROM integrity check failed\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@ -70,22 +72,30 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
|
||||||
s32 *temp, s32 *pressure)
|
s32 *temp, s32 *pressure)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
s32 t, p;
|
|
||||||
s64 off, sens, dt;
|
|
||||||
struct ms5611_state *st = iio_priv(indio_dev);
|
struct ms5611_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
ret = st->read_adc_temp_and_pressure(&indio_dev->dev, &t, &p);
|
ret = st->read_adc_temp_and_pressure(&indio_dev->dev, temp, pressure);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&indio_dev->dev,
|
dev_err(&indio_dev->dev,
|
||||||
"failed to read temperature and pressure\n");
|
"failed to read temperature and pressure\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
dt = t - (st->prom[5] << 8);
|
return st->chip_info->temp_and_pressure_compensate(st->chip_info,
|
||||||
off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7);
|
temp, pressure);
|
||||||
sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8);
|
}
|
||||||
|
|
||||||
t = 2000 + ((st->prom[6] * dt) >> 23);
|
static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
|
||||||
|
s32 *temp, s32 *pressure)
|
||||||
|
{
|
||||||
|
s32 t = *temp, p = *pressure;
|
||||||
|
s64 off, sens, dt;
|
||||||
|
|
||||||
|
dt = t - (chip_info->prom[5] << 8);
|
||||||
|
off = ((s64)chip_info->prom[2] << 16) + ((chip_info->prom[4] * dt) >> 7);
|
||||||
|
sens = ((s64)chip_info->prom[1] << 15) + ((chip_info->prom[3] * dt) >> 8);
|
||||||
|
|
||||||
|
t = 2000 + ((chip_info->prom[6] * dt) >> 23);
|
||||||
if (t < 2000) {
|
if (t < 2000) {
|
||||||
s64 off2, sens2, t2;
|
s64 off2, sens2, t2;
|
||||||
|
|
||||||
|
@ -111,6 +121,42 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
|
||||||
|
s32 *temp, s32 *pressure)
|
||||||
|
{
|
||||||
|
s32 t = *temp, p = *pressure;
|
||||||
|
s64 off, sens, dt;
|
||||||
|
|
||||||
|
dt = t - (chip_info->prom[5] << 8);
|
||||||
|
off = ((s64)chip_info->prom[2] << 17) + ((chip_info->prom[4] * dt) >> 6);
|
||||||
|
sens = ((s64)chip_info->prom[1] << 16) + ((chip_info->prom[3] * dt) >> 7);
|
||||||
|
|
||||||
|
t = 2000 + ((chip_info->prom[6] * dt) >> 23);
|
||||||
|
if (t < 2000) {
|
||||||
|
s64 off2, sens2, t2;
|
||||||
|
|
||||||
|
t2 = (dt * dt) >> 31;
|
||||||
|
off2 = (61 * (t - 2000) * (t - 2000)) >> 4;
|
||||||
|
sens2 = off2 << 1;
|
||||||
|
|
||||||
|
if (t < -1500) {
|
||||||
|
s64 tmp = (t + 1500) * (t + 1500);
|
||||||
|
|
||||||
|
off2 += 15 * tmp;
|
||||||
|
sens2 += (8 * tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
t -= t2;
|
||||||
|
off -= off2;
|
||||||
|
sens -= sens2;
|
||||||
|
}
|
||||||
|
|
||||||
|
*temp = t;
|
||||||
|
*pressure = (((p * sens) >> 21) - off) >> 15;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int ms5611_reset(struct iio_dev *indio_dev)
|
static int ms5611_reset(struct iio_dev *indio_dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -160,16 +206,23 @@ static int ms5611_read_raw(struct iio_dev *indio_dev,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ms5611_chip_info chip_info_tbl[] = {
|
||||||
|
[MS5611] = {
|
||||||
|
.temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate,
|
||||||
|
},
|
||||||
|
[MS5607] = {
|
||||||
|
.temp_and_pressure_compensate = ms5607_temp_and_pressure_compensate,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const struct iio_chan_spec ms5611_channels[] = {
|
static const struct iio_chan_spec ms5611_channels[] = {
|
||||||
{
|
{
|
||||||
.type = IIO_PRESSURE,
|
.type = IIO_PRESSURE,
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||||
BIT(IIO_CHAN_INFO_SCALE)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.type = IIO_TEMP,
|
.type = IIO_TEMP,
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||||
BIT(IIO_CHAN_INFO_SCALE)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,12 +242,13 @@ static int ms5611_init(struct iio_dev *indio_dev)
|
||||||
return ms5611_read_prom(indio_dev);
|
return ms5611_read_prom(indio_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev)
|
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct ms5611_state *st = iio_priv(indio_dev);
|
struct ms5611_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
mutex_init(&st->lock);
|
mutex_init(&st->lock);
|
||||||
|
st->chip_info = &chip_info_tbl[type];
|
||||||
indio_dev->dev.parent = dev;
|
indio_dev->dev.parent = dev;
|
||||||
indio_dev->name = dev->driver->name;
|
indio_dev->name = dev->driver->name;
|
||||||
indio_dev->info = &ms5611_info;
|
indio_dev->info = &ms5611_info;
|
||||||
|
|
|
@ -104,11 +104,12 @@ static int ms5611_i2c_probe(struct i2c_client *client,
|
||||||
st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure;
|
st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure;
|
||||||
st->client = client;
|
st->client = client;
|
||||||
|
|
||||||
return ms5611_probe(indio_dev, &client->dev);
|
return ms5611_probe(indio_dev, &client->dev, id->driver_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id ms5611_id[] = {
|
static const struct i2c_device_id ms5611_id[] = {
|
||||||
{ "ms5611", 0 },
|
{ "ms5611", MS5611 },
|
||||||
|
{ "ms5607", MS5607 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, ms5611_id);
|
MODULE_DEVICE_TABLE(i2c, ms5611_id);
|
||||||
|
@ -116,7 +117,6 @@ MODULE_DEVICE_TABLE(i2c, ms5611_id);
|
||||||
static struct i2c_driver ms5611_driver = {
|
static struct i2c_driver ms5611_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ms5611",
|
.name = "ms5611",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.id_table = ms5611_id,
|
.id_table = ms5611_id,
|
||||||
.probe = ms5611_i2c_probe,
|
.probe = ms5611_i2c_probe,
|
||||||
|
|
|
@ -103,11 +103,13 @@ static int ms5611_spi_probe(struct spi_device *spi)
|
||||||
st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure;
|
st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure;
|
||||||
st->client = spi;
|
st->client = spi;
|
||||||
|
|
||||||
return ms5611_probe(indio_dev, &spi->dev);
|
return ms5611_probe(indio_dev, &spi->dev,
|
||||||
|
spi_get_device_id(spi)->driver_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct spi_device_id ms5611_id[] = {
|
static const struct spi_device_id ms5611_id[] = {
|
||||||
{ "ms5611", 0 },
|
{ "ms5611", MS5611 },
|
||||||
|
{ "ms5607", MS5607 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, ms5611_id);
|
MODULE_DEVICE_TABLE(spi, ms5611_id);
|
||||||
|
|
|
@ -178,6 +178,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
||||||
static const struct st_sensor_settings st_press_sensors_settings[] = {
|
static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||||
{
|
{
|
||||||
.wai = ST_PRESS_LPS331AP_WAI_EXP,
|
.wai = ST_PRESS_LPS331AP_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LPS331AP_PRESS_DEV_NAME,
|
[0] = LPS331AP_PRESS_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
@ -225,6 +226,7 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_PRESS_LPS001WP_WAI_EXP,
|
.wai = ST_PRESS_LPS001WP_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LPS001WP_PRESS_DEV_NAME,
|
[0] = LPS001WP_PRESS_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
@ -260,6 +262,7 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_PRESS_LPS25H_WAI_EXP,
|
.wai = ST_PRESS_LPS25H_WAI_EXP,
|
||||||
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
[0] = LPS25H_PRESS_DEV_NAME,
|
[0] = LPS25H_PRESS_DEV_NAME,
|
||||||
},
|
},
|
||||||
|
|
|
@ -79,7 +79,6 @@ MODULE_DEVICE_TABLE(i2c, st_press_id_table);
|
||||||
|
|
||||||
static struct i2c_driver st_press_driver = {
|
static struct i2c_driver st_press_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "st-press-i2c",
|
.name = "st-press-i2c",
|
||||||
.of_match_table = of_match_ptr(st_press_of_match),
|
.of_match_table = of_match_ptr(st_press_of_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -65,6 +65,13 @@
|
||||||
|
|
||||||
#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
|
#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
|
||||||
|
|
||||||
|
/* Magic constants */
|
||||||
|
#define MLX90614_CONST_OFFSET_DEC -13657 /* decimal part of the Kelvin offset */
|
||||||
|
#define MLX90614_CONST_OFFSET_REM 500000 /* remainder of offset (273.15*50) */
|
||||||
|
#define MLX90614_CONST_SCALE 20 /* Scale in milliKelvin (0.02 * 1000) */
|
||||||
|
#define MLX90614_CONST_RAW_EMISSIVITY_MAX 65535 /* max value for emissivity */
|
||||||
|
#define MLX90614_CONST_EMISSIVITY_RESOLUTION 15259 /* 1/65535 ~ 0.000015259 */
|
||||||
|
|
||||||
struct mlx90614_data {
|
struct mlx90614_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct mutex lock; /* for EEPROM access only */
|
struct mutex lock; /* for EEPROM access only */
|
||||||
|
@ -204,11 +211,11 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
||||||
*val = ret;
|
*val = ret;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_OFFSET:
|
case IIO_CHAN_INFO_OFFSET:
|
||||||
*val = -13657;
|
*val = MLX90614_CONST_OFFSET_DEC;
|
||||||
*val2 = 500000;
|
*val2 = MLX90614_CONST_OFFSET_REM;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
*val = 20;
|
*val = MLX90614_CONST_SCALE;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
||||||
mlx90614_power_get(data, false);
|
mlx90614_power_get(data, false);
|
||||||
|
@ -221,12 +228,12 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (ret == 65535) {
|
if (ret == MLX90614_CONST_RAW_EMISSIVITY_MAX) {
|
||||||
*val = 1;
|
*val = 1;
|
||||||
*val2 = 0;
|
*val2 = 0;
|
||||||
} else {
|
} else {
|
||||||
*val = 0;
|
*val = 0;
|
||||||
*val2 = ret * 15259; /* 1/65535 ~ 0.000015259 */
|
*val2 = ret * MLX90614_CONST_EMISSIVITY_RESOLUTION;
|
||||||
}
|
}
|
||||||
return IIO_VAL_INT_PLUS_NANO;
|
return IIO_VAL_INT_PLUS_NANO;
|
||||||
default:
|
default:
|
||||||
|
@ -245,7 +252,8 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
|
||||||
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
||||||
if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
|
if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
|
val = val * MLX90614_CONST_RAW_EMISSIVITY_MAX +
|
||||||
|
val2 / MLX90614_CONST_EMISSIVITY_RESOLUTION;
|
||||||
|
|
||||||
mlx90614_power_get(data, false);
|
mlx90614_power_get(data, false);
|
||||||
mutex_lock(&data->lock);
|
mutex_lock(&data->lock);
|
||||||
|
@ -551,7 +559,6 @@ static const struct dev_pm_ops mlx90614_pm_ops = {
|
||||||
static struct i2c_driver mlx90614_driver = {
|
static struct i2c_driver mlx90614_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mlx90614",
|
.name = "mlx90614",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &mlx90614_pm_ops,
|
.pm = &mlx90614_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = mlx90614_probe,
|
.probe = mlx90614_probe,
|
||||||
|
|
|
@ -36,9 +36,9 @@
|
||||||
#define TMP006_CONFIG_DRDY_EN BIT(8)
|
#define TMP006_CONFIG_DRDY_EN BIT(8)
|
||||||
#define TMP006_CONFIG_DRDY BIT(7)
|
#define TMP006_CONFIG_DRDY BIT(7)
|
||||||
|
|
||||||
#define TMP006_CONFIG_MOD_MASK 0x7000
|
#define TMP006_CONFIG_MOD_MASK GENMASK(14, 12)
|
||||||
|
|
||||||
#define TMP006_CONFIG_CR_MASK 0x0e00
|
#define TMP006_CONFIG_CR_MASK GENMASK(11, 9)
|
||||||
#define TMP006_CONFIG_CR_SHIFT 9
|
#define TMP006_CONFIG_CR_SHIFT 9
|
||||||
|
|
||||||
#define TMP006_MANUFACTURER_MAGIC 0x5449
|
#define TMP006_MANUFACTURER_MAGIC 0x5449
|
||||||
|
@ -280,7 +280,6 @@ static struct i2c_driver tmp006_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "tmp006",
|
.name = "tmp006",
|
||||||
.pm = &tmp006_pm_ops,
|
.pm = &tmp006_pm_ops,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = tmp006_probe,
|
.probe = tmp006_probe,
|
||||||
.remove = tmp006_remove,
|
.remove = tmp006_remove,
|
||||||
|
|
|
@ -56,8 +56,6 @@ source "drivers/staging/vt6656/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/iio/Kconfig"
|
source "drivers/staging/iio/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/sm7xxfb/Kconfig"
|
|
||||||
|
|
||||||
source "drivers/staging/sm750fb/Kconfig"
|
source "drivers/staging/sm750fb/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/xgifb/Kconfig"
|
source "drivers/staging/xgifb/Kconfig"
|
||||||
|
@ -78,8 +76,6 @@ source "drivers/staging/android/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/board/Kconfig"
|
source "drivers/staging/board/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/ozwpan/Kconfig"
|
|
||||||
|
|
||||||
source "drivers/staging/gdm72xx/Kconfig"
|
source "drivers/staging/gdm72xx/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/gdm724x/Kconfig"
|
source "drivers/staging/gdm724x/Kconfig"
|
||||||
|
@ -112,4 +108,6 @@ source "drivers/staging/fsl-mc/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/wilc1000/Kconfig"
|
source "drivers/staging/wilc1000/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/staging/most/Kconfig"
|
||||||
|
|
||||||
endif # STAGING
|
endif # STAGING
|
||||||
|
|
|
@ -22,7 +22,6 @@ obj-$(CONFIG_VT6655) += vt6655/
|
||||||
obj-$(CONFIG_VT6656) += vt6656/
|
obj-$(CONFIG_VT6656) += vt6656/
|
||||||
obj-$(CONFIG_VME_BUS) += vme/
|
obj-$(CONFIG_VME_BUS) += vme/
|
||||||
obj-$(CONFIG_IIO) += iio/
|
obj-$(CONFIG_IIO) += iio/
|
||||||
obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
|
|
||||||
obj-$(CONFIG_FB_SM750) += sm750fb/
|
obj-$(CONFIG_FB_SM750) += sm750fb/
|
||||||
obj-$(CONFIG_FB_XGI) += xgifb/
|
obj-$(CONFIG_FB_XGI) += xgifb/
|
||||||
obj-$(CONFIG_USB_EMXX) += emxx_udc/
|
obj-$(CONFIG_USB_EMXX) += emxx_udc/
|
||||||
|
@ -32,7 +31,6 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
|
||||||
obj-$(CONFIG_MFD_NVEC) += nvec/
|
obj-$(CONFIG_MFD_NVEC) += nvec/
|
||||||
obj-$(CONFIG_ANDROID) += android/
|
obj-$(CONFIG_ANDROID) += android/
|
||||||
obj-$(CONFIG_STAGING_BOARD) += board/
|
obj-$(CONFIG_STAGING_BOARD) += board/
|
||||||
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
|
|
||||||
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
|
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
|
||||||
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
|
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
|
||||||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||||
|
@ -48,3 +46,4 @@ obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
|
||||||
obj-$(CONFIG_FB_TFT) += fbtft/
|
obj-$(CONFIG_FB_TFT) += fbtft/
|
||||||
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
|
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
|
||||||
obj-$(CONFIG_WILC1000) += wilc1000/
|
obj-$(CONFIG_WILC1000) += wilc1000/
|
||||||
|
obj-$(CONFIG_MOST) += most/
|
||||||
|
|
|
@ -20,7 +20,8 @@ config ANDROID_TIMED_OUTPUT
|
||||||
|
|
||||||
config ANDROID_TIMED_GPIO
|
config ANDROID_TIMED_GPIO
|
||||||
tristate "Android timed gpio driver"
|
tristate "Android timed gpio driver"
|
||||||
depends on GPIOLIB && ANDROID_TIMED_OUTPUT
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
|
depends on ANDROID_TIMED_OUTPUT
|
||||||
default n
|
default n
|
||||||
---help---
|
---help---
|
||||||
Unlike generic gpio is to allow programs to access and manipulate gpio
|
Unlike generic gpio is to allow programs to access and manipulate gpio
|
||||||
|
|
|
@ -2,16 +2,8 @@ TODO:
|
||||||
- checkpatch.pl cleanups
|
- checkpatch.pl cleanups
|
||||||
- sparse fixes
|
- sparse fixes
|
||||||
- rename files to be not so "generic"
|
- rename files to be not so "generic"
|
||||||
- make sure things build as modules properly
|
|
||||||
- add proper arch dependencies as needed
|
- add proper arch dependencies as needed
|
||||||
- audit userspace interfaces to make sure they are sane
|
- audit userspace interfaces to make sure they are sane
|
||||||
- kuid_t should never be exposed to user space as it is
|
|
||||||
kernel internal type. Data structure for this kuid_t is:
|
|
||||||
typedef struct {
|
|
||||||
uid_t val;
|
|
||||||
} kuid_t;
|
|
||||||
- This bug is introduced by Xiong Zhou in the patch bd471258f2e09
|
|
||||||
- ("staging: android: logger: use kuid_t instead of uid_t")
|
|
||||||
|
|
||||||
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
|
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
|
||||||
Brian Swetland <swetland@google.com>
|
Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com>
|
||||||
|
|
|
@ -388,7 +388,7 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
|
|
||||||
/* ... and allocate the backing shmem file */
|
/* ... and allocate the backing shmem file */
|
||||||
vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);
|
vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);
|
||||||
if (unlikely(IS_ERR(vmfile))) {
|
if (IS_ERR(vmfile)) {
|
||||||
ret = PTR_ERR(vmfile);
|
ret = PTR_ERR(vmfile);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -660,7 +660,7 @@ restart:
|
||||||
if (page_range_subsumed_by_range(range, pgstart, pgend))
|
if (page_range_subsumed_by_range(range, pgstart, pgend))
|
||||||
return 0;
|
return 0;
|
||||||
if (page_range_in_range(range, pgstart, pgend)) {
|
if (page_range_in_range(range, pgstart, pgend)) {
|
||||||
pgstart = min_t(size_t, range->pgstart, pgstart),
|
pgstart = min_t(size_t, range->pgstart, pgstart);
|
||||||
pgend = max_t(size_t, range->pgend, pgend);
|
pgend = max_t(size_t, range->pgend, pgend);
|
||||||
purged |= range->purged;
|
purged |= range->purged;
|
||||||
range_del(range);
|
range_del(range);
|
||||||
|
|
|
@ -1103,10 +1103,10 @@ static struct dma_buf_ops dma_buf_ops = {
|
||||||
struct dma_buf *ion_share_dma_buf(struct ion_client *client,
|
struct dma_buf *ion_share_dma_buf(struct ion_client *client,
|
||||||
struct ion_handle *handle)
|
struct ion_handle *handle)
|
||||||
{
|
{
|
||||||
|
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||||
struct ion_buffer *buffer;
|
struct ion_buffer *buffer;
|
||||||
struct dma_buf *dmabuf;
|
struct dma_buf *dmabuf;
|
||||||
bool valid_handle;
|
bool valid_handle;
|
||||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
|
||||||
|
|
||||||
mutex_lock(&client->lock);
|
mutex_lock(&client->lock);
|
||||||
valid_handle = ion_handle_validate(client, handle);
|
valid_handle = ion_handle_validate(client, handle);
|
||||||
|
@ -1466,7 +1466,6 @@ static const struct file_operations debug_heap_fops = {
|
||||||
.release = single_release,
|
.release = single_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef DEBUG_HEAP_SHRINKER
|
|
||||||
static int debug_shrink_set(void *data, u64 val)
|
static int debug_shrink_set(void *data, u64 val)
|
||||||
{
|
{
|
||||||
struct ion_heap *heap = data;
|
struct ion_heap *heap = data;
|
||||||
|
@ -1474,15 +1473,14 @@ static int debug_shrink_set(void *data, u64 val)
|
||||||
int objs;
|
int objs;
|
||||||
|
|
||||||
sc.gfp_mask = -1;
|
sc.gfp_mask = -1;
|
||||||
sc.nr_to_scan = 0;
|
sc.nr_to_scan = val;
|
||||||
|
|
||||||
if (!val)
|
if (!val) {
|
||||||
return 0;
|
objs = heap->shrinker.count_objects(&heap->shrinker, &sc);
|
||||||
|
sc.nr_to_scan = objs;
|
||||||
|
}
|
||||||
|
|
||||||
objs = heap->shrinker.shrink(&heap->shrinker, &sc);
|
heap->shrinker.scan_objects(&heap->shrinker, &sc);
|
||||||
sc.nr_to_scan = objs;
|
|
||||||
|
|
||||||
heap->shrinker.shrink(&heap->shrinker, &sc);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1495,14 +1493,13 @@ static int debug_shrink_get(void *data, u64 *val)
|
||||||
sc.gfp_mask = -1;
|
sc.gfp_mask = -1;
|
||||||
sc.nr_to_scan = 0;
|
sc.nr_to_scan = 0;
|
||||||
|
|
||||||
objs = heap->shrinker.shrink(&heap->shrinker, &sc);
|
objs = heap->shrinker.count_objects(&heap->shrinker, &sc);
|
||||||
*val = objs;
|
*val = objs;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_SIMPLE_ATTRIBUTE(debug_shrink_fops, debug_shrink_get,
|
DEFINE_SIMPLE_ATTRIBUTE(debug_shrink_fops, debug_shrink_get,
|
||||||
debug_shrink_set, "%llu\n");
|
debug_shrink_set, "%llu\n");
|
||||||
#endif
|
|
||||||
|
|
||||||
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
|
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
|
||||||
{
|
{
|
||||||
|
@ -1540,8 +1537,7 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
|
||||||
path, heap->name);
|
path, heap->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_HEAP_SHRINKER
|
if (heap->shrinker.count_objects && heap->shrinker.scan_objects) {
|
||||||
if (heap->shrinker.shrink) {
|
|
||||||
char debug_name[64];
|
char debug_name[64];
|
||||||
|
|
||||||
snprintf(debug_name, 64, "%s_shrink", heap->name);
|
snprintf(debug_name, 64, "%s_shrink", heap->name);
|
||||||
|
@ -1556,7 +1552,7 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
|
||||||
path, debug_name);
|
path, debug_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
up_write(&dev->lock);
|
up_write(&dev->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue