mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
Merge tag 'staging-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging tree update from Greg Kroah-Hartman: "Here is the big staging tree update for the 3.7-rc1 merge window. There are a few patches in here that are outside of the staging area, namely HID and IIO patches, but all of them have been acked by the relevant subsystem maintainers. The IIO stuff is still coming in through this tree as it hasn't entirely moved out of the staging tree, but is almost there. Other than that, there wa a ton of work on the comedi drivers to make them more readable and the correct style. Doing that removed a lot of code, but we added a new driver to the staging tree, so we didn't end up with a net reduction this time around: 662 files changed, 51649 insertions(+), 26582 deletions(-) All of these patches have been in the linux-next tree already. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>" * tag 'staging-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1094 commits) staging: comedi: jr3_pci: fix iomem dereference staging: comedi: drivers: use comedi_fc.h cmdtest helpers Staging: winbond: usb_free_urb(NULL) is safe Staging: winbond: checkpatch cleanup Staging: winbond: Removed undesired spaces, lines and tabs Staging: winbond: Typo corrections in comments Staging: winbond: Changed c99 comments to c89 comments staging: r8712u: Do not queue cloned skb staging: comedi: ni_mio_common: always lock in ni_ai_poll() staging: comedi: s626: add FIXME comment staging: comedi: s626: don't dereference insn->data staging: comedi: s526: fix if() check in s526_gpct_winsn() staging: comedi: s626: cleanup comments in s626_initialize() staging: comedi: s626: remove clear of kzalloc'ed data staging: comedi: s626: remove 'WDInterval' from private data staging: comedi: s626: remove 'ChargeEnabled' from private data staging: comedi: s626: remove 'IsBoardRevA' comment staging: comedi: s626: #if 0 out the "SAA7146 BUG WORKAROUND" staging: comedi: s626: remove 'allocatedBuf' from private data staging: comedi: s626: add final attach message ...
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
* Freescale i.MX28 LRADC device driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx28-lradc"
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain the LRADC interrupts
|
||||
|
||||
Examples:
|
||||
|
||||
lradc@80050000 {
|
||||
compatible = "fsl,imx28-lradc";
|
||||
reg = <0x80050000 0x2000>;
|
||||
interrupts = <10 14 15 16 17 18 19
|
||||
20 21 22 23 24 25>;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
Freescale i.MX IPUv3
|
||||
====================
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,<chip>-ipu"
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- interrupts: Should contain sync interrupt and error interrupt,
|
||||
in this order.
|
||||
- #crtc-cells: 1, See below
|
||||
|
||||
example:
|
||||
|
||||
ipu: ipu@18000000 {
|
||||
#crtc-cells = <1>;
|
||||
compatible = "fsl,imx53-ipu";
|
||||
reg = <0x18000000 0x080000000>;
|
||||
interrupts = <11 10>;
|
||||
};
|
||||
|
||||
Parallel display support
|
||||
========================
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx-parallel-display"
|
||||
- crtc: the crtc this display is connected to, see below
|
||||
Optional properties:
|
||||
- interface_pix_fmt: How this display is connected to the
|
||||
crtc. Currently supported types: "rgb24", "rgb565"
|
||||
- edid: verbatim EDID data block describing attached display.
|
||||
- ddc: phandle describing the i2c bus handling the display data
|
||||
channel
|
||||
|
||||
example:
|
||||
|
||||
display@di0 {
|
||||
compatible = "fsl,imx-parallel-display";
|
||||
edid = [edid-data];
|
||||
crtc = <&ipu 0>;
|
||||
interface-pix-fmt = "rgb24";
|
||||
};
|
||||
140
Documentation/hid/hid-sensor.txt
Executable file
140
Documentation/hid/hid-sensor.txt
Executable file
@@ -0,0 +1,140 @@
|
||||
|
||||
HID Sensors Framework
|
||||
======================
|
||||
HID sensor framework provides necessary interfaces to implement sensor drivers,
|
||||
which are connected to a sensor hub. The sensor hub is a HID device and it provides
|
||||
a report descriptor conforming to HID 1.12 sensor usage tables.
|
||||
|
||||
Description from the HID 1.12 "HID Sensor Usages" specification:
|
||||
"Standardization of HID usages for sensors would allow (but not require) sensor
|
||||
hardware vendors to provide a consistent Plug And Play interface at the USB boundary,
|
||||
thereby enabling some operating systems to incorporate common device drivers that
|
||||
could be reused between vendors, alleviating any need for the vendors to provide
|
||||
the drivers themselves."
|
||||
|
||||
This specification describes many usage IDs, which describe the type of sensor
|
||||
and also the individual data fields. Each sensor can have variable number of
|
||||
data fields. The length and order is specified in the report descriptor. For
|
||||
example a part of report descriptor can look like:
|
||||
|
||||
INPUT(1)[INPUT]
|
||||
..
|
||||
Field(2)
|
||||
Physical(0020.0073)
|
||||
Usage(1)
|
||||
0020.045f
|
||||
Logical Minimum(-32767)
|
||||
Logical Maximum(32767)
|
||||
Report Size(8)
|
||||
Report Count(1)
|
||||
Report Offset(16)
|
||||
Flags(Variable Absolute)
|
||||
..
|
||||
..
|
||||
|
||||
The report is indicating "sensor page (0x20)" contains an accelerometer-3D (0x73).
|
||||
This accelerometer-3D has some fields. Here for example field 2 is motion intensity
|
||||
(0x045f) with a logical minimum value of -32767 and logical maximum of 32767. The
|
||||
order of fields and length of each field is important as the input event raw
|
||||
data will use this format.
|
||||
|
||||
|
||||
Implementation
|
||||
=================
|
||||
|
||||
This specification defines many different types of sensors with different sets of
|
||||
data fields. It is difficult to have a common input event to user space applications,
|
||||
for different sensors. For example an accelerometer can send X,Y and Z data, whereas
|
||||
an ambient light sensor can send illumination data.
|
||||
So the implementation has two parts:
|
||||
- Core hid driver
|
||||
- Individual sensor processing part (sensor drivers)
|
||||
|
||||
Core driver
|
||||
-----------
|
||||
The core driver registers (hid-sensor-hub) registers as a HID driver. It parses
|
||||
report descriptors and identifies all the sensors present. It adds an MFD device
|
||||
with name HID-SENSOR-xxxx (where xxxx is usage id from the specification).
|
||||
For example
|
||||
HID-SENSOR-200073 is registered for an Accelerometer 3D driver.
|
||||
So if any driver with this name is inserted, then the probe routine for that
|
||||
function will be called. So an accelerometer processing driver can register
|
||||
with this name and will be probed if there is an accelerometer-3D detected.
|
||||
|
||||
The core driver provides a set of APIs which can be used by the processing
|
||||
drivers to register and get events for that usage id. Also it provides parsing
|
||||
functions, which get and set each input/feature/output report.
|
||||
|
||||
Individual sensor processing part (sensor drivers)
|
||||
-----------
|
||||
The processing driver will use an interface provided by the core driver to parse
|
||||
the report and get the indexes of the fields and also can get events. This driver
|
||||
can use IIO interface to use the standard ABI defined for a type of sensor.
|
||||
|
||||
|
||||
Core driver Interface
|
||||
=====================
|
||||
|
||||
Callback structure:
|
||||
Each processing driver can use this structure to set some callbacks.
|
||||
int (*suspend)(..): Callback when HID suspend is received
|
||||
int (*resume)(..): Callback when HID resume is received
|
||||
int (*capture_sample)(..): Capture a sample for one of its data fields
|
||||
int (*send_event)(..): One complete event is received which can have
|
||||
multiple data fields.
|
||||
|
||||
Registration functions:
|
||||
int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
struct hid_sensor_hub_callbacks *usage_callback):
|
||||
|
||||
Registers callbacks for an usage id. The callback functions are not allowed
|
||||
to sleep.
|
||||
|
||||
|
||||
int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id):
|
||||
|
||||
Removes callbacks for an usage id.
|
||||
|
||||
|
||||
Parsing function:
|
||||
int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
|
||||
u8 type,
|
||||
u32 usage_id, u32 attr_usage_id,
|
||||
struct hid_sensor_hub_attribute_info *info);
|
||||
|
||||
A processing driver can look for some field of interest and check if it exists
|
||||
in a report descriptor. If it exists it will store necessary information
|
||||
so that fields can be set or get individually.
|
||||
These indexes avoid searching every time and getting field index to get or set.
|
||||
|
||||
|
||||
Set Feature report
|
||||
int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
u32 field_index, s32 value);
|
||||
|
||||
This interface is used to set a value for a field in feature report. For example
|
||||
if there is a field report_interval, which is parsed by a call to
|
||||
sensor_hub_input_get_attribute_info before, then it can directly set that individual
|
||||
field.
|
||||
|
||||
|
||||
int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
|
||||
u32 field_index, s32 *value);
|
||||
|
||||
This interface is used to get a value for a field in input report. For example
|
||||
if there is a field report_interval, which is parsed by a call to
|
||||
sensor_hub_input_get_attribute_info before, then it can directly get that individual
|
||||
field value.
|
||||
|
||||
|
||||
int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
u32 attr_usage_id, u32 report_id);
|
||||
|
||||
This is used to get a particular field value through input reports. For example
|
||||
accelerometer wants to poll X axis value, then it can call this function with
|
||||
the usage id of X axis. HID sensors can provide events, so this is not necessary
|
||||
to poll for any field. If there is some new sample, the core driver will call
|
||||
registered callback function to process the sample.
|
||||
@@ -708,6 +708,20 @@ config HID_ZYDACRON
|
||||
---help---
|
||||
Support for Zydacron remote control.
|
||||
|
||||
config HID_SENSOR_HUB
|
||||
tristate "HID Sensors framework support"
|
||||
depends on USB_HID
|
||||
select MFD_CORE
|
||||
default n
|
||||
-- help---
|
||||
Support for HID Sensor framework. This creates a MFD instance
|
||||
for a sensor hub and identifies all the sensors connected to it.
|
||||
Each sensor is registered as a MFD cell, so that sensor specific
|
||||
processing can be done in a separate driver. Each sensor
|
||||
drivers can use the service provided by this driver to register
|
||||
for events and handle data streams. Each sensor driver can format
|
||||
data and present to user mode using input or IIO interface.
|
||||
|
||||
endmenu
|
||||
|
||||
endif # HID
|
||||
|
||||
@@ -112,6 +112,7 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
|
||||
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
|
||||
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
|
||||
obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o
|
||||
obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid/
|
||||
obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
|
||||
@@ -1568,6 +1568,10 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_1020) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
|
||||
@@ -1665,6 +1669,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_SENSOR_HUB_7014) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
|
||||
|
||||
@@ -429,6 +429,11 @@
|
||||
#define USB_VENDOR_ID_IMATION 0x0718
|
||||
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
|
||||
|
||||
#define USB_VENDOR_ID_INTEL_8086 0x8086
|
||||
#define USB_VENDOR_ID_INTEL_8087 0x8087
|
||||
#define USB_DEVICE_ID_SENSOR_HUB_1020 0x1020
|
||||
#define USB_DEVICE_ID_SENSOR_HUB_09FA 0x09FA
|
||||
|
||||
#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
|
||||
#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
|
||||
|
||||
@@ -706,6 +711,7 @@
|
||||
|
||||
#define USB_VENDOR_ID_STANTUM_STM 0x0483
|
||||
#define USB_DEVICE_ID_MTP_STM 0x3261
|
||||
#define USB_DEVICE_ID_SENSOR_HUB_7014 0x7014
|
||||
|
||||
#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403
|
||||
#define USB_DEVICE_ID_MTP_SITRONIX 0x5001
|
||||
|
||||
680
drivers/hid/hid-sensor-hub.c
Normal file
680
drivers/hid/hid-sensor-hub.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Industrial I/O subsytem configuration
|
||||
# Industrial I/O subsystem configuration
|
||||
#
|
||||
|
||||
menuconfig IIO
|
||||
@@ -54,10 +54,15 @@ config IIO_CONSUMERS_PER_TRIGGER
|
||||
This value controls the maximum number of consumers that a
|
||||
given trigger may handle. Default is 2.
|
||||
|
||||
source "drivers/iio/accel/Kconfig"
|
||||
source "drivers/iio/adc/Kconfig"
|
||||
source "drivers/iio/amplifiers/Kconfig"
|
||||
source "drivers/iio/light/Kconfig"
|
||||
source "drivers/iio/frequency/Kconfig"
|
||||
source "drivers/iio/dac/Kconfig"
|
||||
source "drivers/iio/common/Kconfig"
|
||||
source "drivers/iio/gyro/Kconfig"
|
||||
source "drivers/iio/light/Kconfig"
|
||||
source "drivers/iio/magnetometer/Kconfig"
|
||||
|
||||
endif # IIO
|
||||
|
||||
@@ -10,8 +10,13 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
|
||||
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
|
||||
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
|
||||
|
||||
obj-y += accel/
|
||||
obj-y += adc/
|
||||
obj-y += amplifiers/
|
||||
obj-y += light/
|
||||
obj-y += frequency/
|
||||
obj-y += dac/
|
||||
obj-y += common/
|
||||
obj-y += gyro/
|
||||
obj-y += light/
|
||||
obj-y += magnetometer/
|
||||
|
||||
16
drivers/iio/accel/Kconfig
Normal file
16
drivers/iio/accel/Kconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# Accelerometer drivers
|
||||
#
|
||||
menu "Accelerometers"
|
||||
|
||||
config HID_SENSOR_ACCEL_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
tristate "HID Acelerometers 3D"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
accelerometers 3D.
|
||||
|
||||
endmenu
|
||||
5
drivers/iio/accel/Makefile
Normal file
5
drivers/iio/accel/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for industrial I/O accelerometer drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
418
drivers/iio/accel/hid-sensor-accel-3d.c
Normal file
418
drivers/iio/accel/hid-sensor-accel-3d.c
Normal file
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-attributes.h"
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
/*Format: HID-SENSOR-usage_id_in_hex*/
|
||||
/*Usage ID from spec for Accelerometer-3D: 0x200073*/
|
||||
#define DRIVER_NAME "HID-SENSOR-200073"
|
||||
|
||||
enum accel_3d_channel {
|
||||
CHANNEL_SCAN_INDEX_X,
|
||||
CHANNEL_SCAN_INDEX_Y,
|
||||
CHANNEL_SCAN_INDEX_Z,
|
||||
ACCEL_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
struct accel_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_iio_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX];
|
||||
};
|
||||
|
||||
static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
|
||||
HID_USAGE_SENSOR_ACCEL_X_AXIS,
|
||||
HID_USAGE_SENSOR_ACCEL_Y_AXIS,
|
||||
HID_USAGE_SENSOR_ACCEL_Z_AXIS
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec accel_3d_channels[] = {
|
||||
{
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT |
|
||||
IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
|
||||
IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.sign = 's';
|
||||
/* Real storage bits will change based on the report desc. */
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
/* Maximum size of a sample to capture is u32 */
|
||||
channels[channel].scan_type.storagebits = sizeof(u32) * 8;
|
||||
}
|
||||
|
||||
/* Channel read_raw handler */
|
||||
static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret;
|
||||
int ret_type;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
report_id = accel_state->accel[chan->scan_index].report_id;
|
||||
address = accel_3d_addresses[chan->scan_index];
|
||||
if (report_id >= 0)
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
accel_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, address,
|
||||
report_id);
|
||||
else {
|
||||
*val = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = accel_state->accel[CHANNEL_SCAN_INDEX_X].units;
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = hid_sensor_convert_exponent(
|
||||
accel_state->accel[CHANNEL_SCAN_INDEX_X].unit_expo);
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_read_samp_freq_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
ret_type = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_read_raw_hyst_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
ret_type = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret_type = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_type;
|
||||
}
|
||||
|
||||
/* Channel write_raw handler */
|
||||
static int accel_3d_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_write_samp_freq_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_write_raw_hyst_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int accel_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static const struct iio_info accel_3d_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &accel_3d_read_raw,
|
||||
.write_raw = &accel_3d_write_raw,
|
||||
.write_raw_get_fmt = &accel_3d_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
||||
{
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
int datum_sz;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
if (!buffer) {
|
||||
dev_err(&indio_dev->dev, "Buffer == NULL\n");
|
||||
return;
|
||||
}
|
||||
datum_sz = buffer->access->get_bytes_per_datum(buffer);
|
||||
if (len > datum_sz) {
|
||||
dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
|
||||
datum_sz);
|
||||
return;
|
||||
}
|
||||
iio_push_to_buffer(buffer, (u8 *)data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n",
|
||||
accel_state->common_attributes.data_ready);
|
||||
if (accel_state->common_attributes.data_ready)
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
size_t raw_len, char *raw_data,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
int offset;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_ACCEL_X_AXIS:
|
||||
case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
|
||||
case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
|
||||
offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
|
||||
accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||
*(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id*/
|
||||
static int accel_3d_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned usage_id,
|
||||
struct accel_3d_state *st)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
|
||||
&st->accel[CHANNEL_SCAN_INDEX_X + i]);
|
||||
if (ret < 0)
|
||||
break;
|
||||
accel_3d_adjust_channel_bit_mask(channels,
|
||||
CHANNEL_SCAN_INDEX_X + i,
|
||||
st->accel[CHANNEL_SCAN_INDEX_X + i].size);
|
||||
}
|
||||
dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
|
||||
st->accel[0].index,
|
||||
st->accel[0].report_id,
|
||||
st->accel[1].index, st->accel[1].report_id,
|
||||
st->accel[2].index, st->accel[2].report_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to initialize the processing for usage id */
|
||||
static int __devinit hid_accel_3d_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "accel_3d";
|
||||
struct iio_dev *indio_dev;
|
||||
struct accel_3d_state *accel_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(struct accel_3d_state));
|
||||
if (indio_dev == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
accel_state = iio_priv(indio_dev);
|
||||
accel_state->common_attributes.hsdev = hsdev;
|
||||
accel_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D,
|
||||
&accel_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
goto error_free_dev;
|
||||
}
|
||||
|
||||
channels = kmemdup(accel_3d_channels,
|
||||
sizeof(accel_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
goto error_free_dev;
|
||||
}
|
||||
|
||||
ret = accel_3d_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, accel_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &accel_3d_info;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
NULL, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
accel_state->common_attributes.data_ready = false;
|
||||
ret = hid_sensor_setup_trigger(indio_dev, name,
|
||||
&accel_state->common_attributes);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||
goto error_unreg_buffer_funcs;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "device register failed\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
accel_state->callbacks.send_event = accel_3d_proc_event;
|
||||
accel_state->callbacks.capture_sample = accel_3d_capture_sample;
|
||||
accel_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
|
||||
&accel_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
goto error_iio_unreg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_iio_unreg:
|
||||
iio_device_unregister(indio_dev);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(indio_dev);
|
||||
error_unreg_buffer_funcs:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_dev_mem:
|
||||
kfree(indio_dev->channels);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int __devinit hid_accel_3d_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->channels);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver hid_accel_3d_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = hid_accel_3d_probe,
|
||||
.remove = hid_accel_3d_remove,
|
||||
};
|
||||
module_platform_driver(hid_accel_3d_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Accel 3D");
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -3,6 +3,11 @@
|
||||
#
|
||||
menu "Analog to digital converters"
|
||||
|
||||
config AD_SIGMA_DELTA
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config AD7266
|
||||
tristate "Analog Devices AD7265/AD7266 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
@@ -13,6 +18,33 @@ config AD7266
|
||||
Say yes here to build support for Analog Devices AD7265 and AD7266
|
||||
ADCs.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
|
||||
AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say
|
||||
N (but it is safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7791.
|
||||
|
||||
config AD7476
|
||||
tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
|
||||
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
|
||||
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
|
||||
module will be called ad7476.
|
||||
|
||||
config AT91_ADC
|
||||
tristate "Atmel AT91 ADC"
|
||||
depends on ARCH_AT91
|
||||
@@ -22,4 +54,10 @@ config AT91_ADC
|
||||
help
|
||||
Say yes here to build support for Atmel AT91 ADC.
|
||||
|
||||
config LP8788_ADC
|
||||
bool "LP8788 ADC driver"
|
||||
depends on MFD_LP8788
|
||||
help
|
||||
Say yes here to build support for TI LP8788 ADC.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -2,5 +2,9 @@
|
||||
# Makefile for IIO ADC drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
|
||||
@@ -99,7 +99,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p)
|
||||
if (ret == 0) {
|
||||
if (indio_dev->scan_timestamp)
|
||||
((s64 *)st->data)[1] = pf->timestamp;
|
||||
iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp);
|
||||
iio_push_to_buffer(buffer, (u8 *)st->data);
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
@@ -18,8 +18,76 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include "ad7476.h"
|
||||
#define RES_MASK(bits) ((1 << (bits)) - 1)
|
||||
|
||||
struct ad7476_state;
|
||||
|
||||
struct ad7476_chip_info {
|
||||
unsigned int int_vref_uv;
|
||||
struct iio_chan_spec channel[2];
|
||||
void (*reset)(struct ad7476_state *);
|
||||
};
|
||||
|
||||
struct ad7476_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad7476_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* Make the buffer large enough for one 16 bit sample and one 64 bit
|
||||
* aligned 64 bit timestamp.
|
||||
*/
|
||||
unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)]
|
||||
____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad7476_supported_device_ids {
|
||||
ID_AD7091R,
|
||||
ID_AD7276,
|
||||
ID_AD7277,
|
||||
ID_AD7278,
|
||||
ID_AD7466,
|
||||
ID_AD7467,
|
||||
ID_AD7468,
|
||||
ID_AD7495,
|
||||
ID_AD7940,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns;
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->msg);
|
||||
if (b_sent < 0)
|
||||
goto done;
|
||||
|
||||
time_ns = iio_get_time_ns();
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
((s64 *)st->data)[1] = time_ns;
|
||||
|
||||
iio_push_to_buffer(indio_dev->buffer, st->data);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7091_reset(struct ad7476_state *st)
|
||||
{
|
||||
/* Any transfers with 8 scl cycles will reset the device */
|
||||
spi_read(st->spi, st->data, 1);
|
||||
}
|
||||
|
||||
static int ad7476_scan_direct(struct ad7476_state *st)
|
||||
{
|
||||
@@ -29,7 +97,7 @@ static int ad7476_scan_direct(struct ad7476_state *st)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (st->data[0] << 8) | st->data[1];
|
||||
return be16_to_cpup((__be16 *)st->data);
|
||||
}
|
||||
|
||||
static int ad7476_read_raw(struct iio_dev *indio_dev,
|
||||
@@ -40,7 +108,7 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
int ret;
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
unsigned int scale_uv;
|
||||
int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
@@ -57,30 +125,60 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
||||
RES_MASK(st->chip_info->channel[0].scan_type.realbits);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = (st->int_vref_mv * 1000)
|
||||
>> st->chip_info->channel[0].scan_type.realbits;
|
||||
*val = scale_uv/1000;
|
||||
*val2 = (scale_uv%1000)*1000;
|
||||
if (!st->chip_info->int_vref_uv) {
|
||||
scale_uv = regulator_get_voltage(st->reg);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
} else {
|
||||
scale_uv = st->chip_info->int_vref_uv;
|
||||
}
|
||||
scale_uv >>= chan->scan_type.realbits;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define AD7476_CHAN(bits) \
|
||||
#define _AD7476_CHAN(bits, _shift, _info_mask) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
.info_mask = _info_mask | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = bits, \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - bits, \
|
||||
.shift = (_shift), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
|
||||
|
||||
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
[ID_AD7091R] = {
|
||||
.channel[0] = AD7091R_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.reset = ad7091_reset,
|
||||
},
|
||||
[ID_AD7276] = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7277] = {
|
||||
.channel[0] = AD7940_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7278] = {
|
||||
.channel[0] = AD7940_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7466] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
@@ -93,26 +191,14 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
.channel[0] = AD7476_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7475] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7476] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7477] = {
|
||||
.channel[0] = AD7476_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7478] = {
|
||||
.channel[0] = AD7476_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7495] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.int_vref_mv = 2500,
|
||||
.int_vref_uv = 2500000,
|
||||
},
|
||||
[ID_AD7940] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -123,10 +209,9 @@ static const struct iio_info ad7476_info = {
|
||||
|
||||
static int __devinit ad7476_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7476_platform_data *pdata = spi->dev.platform_data;
|
||||
struct ad7476_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, voltage_uv = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
@@ -134,25 +219,18 @@ static int __devinit ad7476_probe(struct spi_device *spi)
|
||||
goto error_ret;
|
||||
}
|
||||
st = iio_priv(indio_dev);
|
||||
st->reg = regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
}
|
||||
st->chip_info =
|
||||
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
if (st->chip_info->int_vref_mv)
|
||||
st->int_vref_mv = st->chip_info->int_vref_mv;
|
||||
else if (pdata && pdata->vref_mv)
|
||||
st->int_vref_mv = pdata->vref_mv;
|
||||
else if (voltage_uv)
|
||||
st->int_vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
dev_warn(&spi->dev, "reference voltage unspecified\n");
|
||||
st->reg = regulator_get(&spi->dev, "vcc");
|
||||
if (IS_ERR(st->reg)) {
|
||||
ret = PTR_ERR(st->reg);
|
||||
goto error_free_dev;
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
@@ -173,57 +251,67 @@ static int __devinit ad7476_probe(struct spi_device *spi)
|
||||
spi_message_init(&st->msg);
|
||||
spi_message_add_tail(&st->xfer, &st->msg);
|
||||
|
||||
ret = ad7476_register_ring_funcs_and_init(indio_dev);
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7476_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
if (st->chip_info->reset)
|
||||
st->chip_info->reset(st);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_ring_unregister;
|
||||
return 0;
|
||||
|
||||
error_ring_unregister:
|
||||
ad7476_ring_cleanup(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
regulator_disable(st->reg);
|
||||
error_put_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_put(st->reg);
|
||||
regulator_put(st->reg);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7476_remove(struct spi_device *spi)
|
||||
static int __devexit ad7476_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad7476_ring_cleanup(indio_dev);
|
||||
if (!IS_ERR(st->reg)) {
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
}
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7476_id[] = {
|
||||
{"ad7091r", ID_AD7091R},
|
||||
{"ad7273", ID_AD7277},
|
||||
{"ad7274", ID_AD7276},
|
||||
{"ad7276", ID_AD7276},
|
||||
{"ad7277", ID_AD7277},
|
||||
{"ad7278", ID_AD7278},
|
||||
{"ad7466", ID_AD7466},
|
||||
{"ad7467", ID_AD7467},
|
||||
{"ad7468", ID_AD7468},
|
||||
{"ad7475", ID_AD7475},
|
||||
{"ad7476", ID_AD7476},
|
||||
{"ad7476a", ID_AD7476},
|
||||
{"ad7477", ID_AD7477},
|
||||
{"ad7477a", ID_AD7477},
|
||||
{"ad7478", ID_AD7478},
|
||||
{"ad7478a", ID_AD7478},
|
||||
{"ad7475", ID_AD7466},
|
||||
{"ad7476", ID_AD7466},
|
||||
{"ad7476a", ID_AD7466},
|
||||
{"ad7477", ID_AD7467},
|
||||
{"ad7477a", ID_AD7467},
|
||||
{"ad7478", ID_AD7468},
|
||||
{"ad7478a", ID_AD7468},
|
||||
{"ad7495", ID_AD7495},
|
||||
{"ad7910", ID_AD7467},
|
||||
{"ad7920", ID_AD7466},
|
||||
{"ad7940", ID_AD7940},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
@@ -240,5 +328,5 @@ static struct spi_driver ad7476_driver = {
|
||||
module_spi_driver(ad7476_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7475/6/7/8(A) AD7466/7/8 ADC");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
460
drivers/iio/adc/ad7791.c
Normal file
460
drivers/iio/adc/ad7791.c
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/adc/ad_sigma_delta.h>
|
||||
|
||||
#include <linux/platform_data/ad7791.h>
|
||||
|
||||
#define AD7791_REG_COMM 0x0 /* For writes */
|
||||
#define AD7791_REG_STATUS 0x0 /* For reads */
|
||||
#define AD7791_REG_MODE 0x1
|
||||
#define AD7791_REG_FILTER 0x2
|
||||
#define AD7791_REG_DATA 0x3
|
||||
|
||||
#define AD7791_MODE_CONTINUOUS 0x00
|
||||
#define AD7791_MODE_SINGLE 0x02
|
||||
#define AD7791_MODE_POWERDOWN 0x03
|
||||
|
||||
#define AD7791_CH_AIN1P_AIN1N 0x00
|
||||
#define AD7791_CH_AIN2 0x01
|
||||
#define AD7791_CH_AIN1N_AIN1N 0x02
|
||||
#define AD7791_CH_AVDD_MONITOR 0x03
|
||||
|
||||
#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4)
|
||||
#define AD7791_FILTER_CLK_MASK (0x3 << 4)
|
||||
#define AD7791_FILTER_RATE_120 0x0
|
||||
#define AD7791_FILTER_RATE_100 0x1
|
||||
#define AD7791_FILTER_RATE_33_3 0x2
|
||||
#define AD7791_FILTER_RATE_20 0x3
|
||||
#define AD7791_FILTER_RATE_16_6 0x4
|
||||
#define AD7791_FILTER_RATE_16_7 0x5
|
||||
#define AD7791_FILTER_RATE_13_3 0x6
|
||||
#define AD7791_FILTER_RATE_9_5 0x7
|
||||
#define AD7791_FILTER_RATE_MASK 0x7
|
||||
|
||||
#define AD7791_MODE_BUFFER BIT(1)
|
||||
#define AD7791_MODE_UNIPOLAR BIT(2)
|
||||
#define AD7791_MODE_BURNOUT BIT(3)
|
||||
#define AD7791_MODE_SEL_MASK (0x3 << 6)
|
||||
#define AD7791_MODE_SEL(x) ((x) << 6)
|
||||
|
||||
#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \
|
||||
(bits), (storagebits), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \
|
||||
(bits), (storagebits), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32);
|
||||
static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16);
|
||||
static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32);
|
||||
|
||||
enum {
|
||||
AD7787,
|
||||
AD7788,
|
||||
AD7789,
|
||||
AD7790,
|
||||
AD7791,
|
||||
};
|
||||
|
||||
enum ad7791_chip_info_flags {
|
||||
AD7791_FLAG_HAS_FILTER = (1 << 0),
|
||||
AD7791_FLAG_HAS_BUFFER = (1 << 1),
|
||||
AD7791_FLAG_HAS_UNIPOLAR = (1 << 2),
|
||||
AD7791_FLAG_HAS_BURNOUT = (1 << 3),
|
||||
};
|
||||
|
||||
struct ad7791_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
enum ad7791_chip_info_flags flags;
|
||||
};
|
||||
|
||||
static const struct ad7791_chip_info ad7791_chip_infos[] = {
|
||||
[AD7787] = {
|
||||
.channels = ad7787_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7787_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
[AD7788] = {
|
||||
.channels = ad7790_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7790_channels),
|
||||
.flags = AD7791_FLAG_HAS_UNIPOLAR,
|
||||
},
|
||||
[AD7789] = {
|
||||
.channels = ad7791_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7791_channels),
|
||||
.flags = AD7791_FLAG_HAS_UNIPOLAR,
|
||||
},
|
||||
[AD7790] = {
|
||||
.channels = ad7790_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7790_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
[AD7791] = {
|
||||
.channels = ad7791_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7791_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
};
|
||||
|
||||
struct ad7791_state {
|
||||
struct ad_sigma_delta sd;
|
||||
uint8_t mode;
|
||||
uint8_t filter;
|
||||
|
||||
struct regulator *reg;
|
||||
const struct ad7791_chip_info *info;
|
||||
};
|
||||
|
||||
static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
|
||||
{
|
||||
return container_of(sd, struct ad7791_state, sd);
|
||||
}
|
||||
|
||||
static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
|
||||
{
|
||||
ad_sd_set_comm(sd, channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7791_set_mode(struct ad_sigma_delta *sd,
|
||||
enum ad_sigma_delta_mode mode)
|
||||
{
|
||||
struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd);
|
||||
|
||||
switch (mode) {
|
||||
case AD_SD_MODE_CONTINUOUS:
|
||||
mode = AD7791_MODE_CONTINUOUS;
|
||||
break;
|
||||
case AD_SD_MODE_SINGLE:
|
||||
mode = AD7791_MODE_SINGLE;
|
||||
break;
|
||||
case AD_SD_MODE_IDLE:
|
||||
case AD_SD_MODE_POWERDOWN:
|
||||
mode = AD7791_MODE_POWERDOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
st->mode &= ~AD7791_MODE_SEL_MASK;
|
||||
st->mode |= AD7791_MODE_SEL(mode);
|
||||
|
||||
return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode);
|
||||
}
|
||||
|
||||
static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
|
||||
.set_channel = ad7791_set_channel,
|
||||
.set_mode = ad7791_set_mode,
|
||||
.has_registers = true,
|
||||
.addr_shift = 4,
|
||||
.read_mask = BIT(3),
|
||||
};
|
||||
|
||||
static int ad7791_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
|
||||
unsigned long long scale_pv;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return ad_sigma_delta_single_conversion(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/**
|
||||
* Unipolar: 0 to VREF
|
||||
* Bipolar -VREF to VREF
|
||||
**/
|
||||
if (unipolar)
|
||||
*val = 0;
|
||||
else
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* The monitor channel uses an internal reference. */
|
||||
if (chan->address == AD7791_CH_AVDD_MONITOR) {
|
||||
scale_pv = 5850000000000ULL;
|
||||
} else {
|
||||
int voltage_uv;
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
if (voltage_uv < 0)
|
||||
return voltage_uv;
|
||||
scale_pv = (unsigned long long)voltage_uv * 1000000;
|
||||
}
|
||||
if (unipolar)
|
||||
scale_pv >>= chan->scan_type.realbits;
|
||||
else
|
||||
scale_pv >>= chan->scan_type.realbits - 1;
|
||||
*val2 = do_div(scale_pv, 1000000000);
|
||||
*val = scale_pv;
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const char * const ad7791_sample_freq_avail[] = {
|
||||
[AD7791_FILTER_RATE_120] = "120",
|
||||
[AD7791_FILTER_RATE_100] = "100",
|
||||
[AD7791_FILTER_RATE_33_3] = "33.3",
|
||||
[AD7791_FILTER_RATE_20] = "20",
|
||||
[AD7791_FILTER_RATE_16_6] = "16.6",
|
||||
[AD7791_FILTER_RATE_16_7] = "16.7",
|
||||
[AD7791_FILTER_RATE_13_3] = "13.3",
|
||||
[AD7791_FILTER_RATE_9_5] = "9.5",
|
||||
};
|
||||
|
||||
static ssize_t ad7791_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK;
|
||||
|
||||
return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]);
|
||||
}
|
||||
|
||||
static ssize_t ad7791_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
|
||||
if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) {
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->filter &= ~AD7791_FILTER_RATE_MASK;
|
||||
st->filter |= i;
|
||||
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
|
||||
sizeof(st->filter), st->filter);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad7791_read_frequency,
|
||||
ad7791_write_frequency);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
|
||||
|
||||
static struct attribute *ad7791_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7791_attribute_group = {
|
||||
.attrs = ad7791_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7791_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.attrs = &ad7791_attribute_group,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7791_no_filter_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __devinit ad7791_setup(struct ad7791_state *st,
|
||||
struct ad7791_platform_data *pdata)
|
||||
{
|
||||
/* Set to poweron-reset default values */
|
||||
st->mode = AD7791_MODE_BUFFER;
|
||||
st->filter = AD7791_FILTER_RATE_16_6;
|
||||
|
||||
if (!pdata)
|
||||
return 0;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered)
|
||||
st->mode &= ~AD7791_MODE_BUFFER;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) &&
|
||||
pdata->burnout_current)
|
||||
st->mode |= AD7791_MODE_BURNOUT;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar)
|
||||
st->mode |= AD7791_MODE_UNIPOLAR;
|
||||
|
||||
return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode),
|
||||
st->mode);
|
||||
}
|
||||
|
||||
static int __devinit ad7791_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7791_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7791_state *st;
|
||||
int ret;
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_err(&spi->dev, "Missing IRQ.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg)) {
|
||||
ret = PTR_ERR(st->reg);
|
||||
goto err_iio_free;
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto error_put_reg;
|
||||
|
||||
st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->info->channels;
|
||||
indio_dev->num_channels = st->info->num_channels;
|
||||
if (st->info->flags & AD7791_FLAG_HAS_FILTER)
|
||||
indio_dev->info = &ad7791_info;
|
||||
else
|
||||
indio_dev->info = &ad7791_no_filter_info;
|
||||
|
||||
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ad7791_setup(st, pdata);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
error_put_reg:
|
||||
regulator_put(st->reg);
|
||||
err_iio_free:
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ad7791_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7791_spi_ids[] = {
|
||||
{ "ad7787", AD7787 },
|
||||
{ "ad7788", AD7788 },
|
||||
{ "ad7789", AD7789 },
|
||||
{ "ad7790", AD7790 },
|
||||
{ "ad7791", AD7791 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
|
||||
|
||||
static struct spi_driver ad7791_driver = {
|
||||
.driver = {
|
||||
.name = "ad7791",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7791_probe,
|
||||
.remove = __devexit_p(ad7791_remove),
|
||||
.id_table = ad7791_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad7791_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
558
drivers/iio/adc/ad_sigma_delta.c
Normal file
558
drivers/iio/adc/ad_sigma_delta.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -82,7 +82,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||
*timestamp = pf->timestamp;
|
||||
}
|
||||
|
||||
buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
|
||||
buffer->access->store_to(buffer, (u8 *)st->buffer);
|
||||
|
||||
iio_trigger_notify_done(idev->trig);
|
||||
st->irq_enabled = true;
|
||||
@@ -545,13 +545,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
|
||||
goto error_free_device;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "No resource defined\n");
|
||||
ret = -ENXIO;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, idev);
|
||||
|
||||
idev->dev.parent = &pdev->dev;
|
||||
@@ -566,18 +559,12 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
|
||||
goto error_free_device;
|
||||
}
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res),
|
||||
"AT91 adc registers")) {
|
||||
dev_err(&pdev->dev, "Resources are unavailable.\n");
|
||||
ret = -EBUSY;
|
||||
goto error_free_device;
|
||||
}
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
st->reg_base = ioremap(res->start, resource_size(res));
|
||||
st->reg_base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!st->reg_base) {
|
||||
dev_err(&pdev->dev, "Failed to map registers.\n");
|
||||
ret = -ENOMEM;
|
||||
goto error_release_mem;
|
||||
goto error_free_device;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -592,45 +579,35 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
|
||||
idev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
|
||||
goto error_unmap_reg;
|
||||
goto error_free_device;
|
||||
}
|
||||
|
||||
st->clk = clk_get(&pdev->dev, "adc_clk");
|
||||
st->clk = devm_clk_get(&pdev->dev, "adc_clk");
|
||||
if (IS_ERR(st->clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get the clock.\n");
|
||||
ret = PTR_ERR(st->clk);
|
||||
goto error_free_irq;
|
||||
}
|
||||
|
||||
ret = clk_prepare(st->clk);
|
||||
ret = clk_prepare_enable(st->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not prepare the clock.\n");
|
||||
goto error_free_clk;
|
||||
dev_err(&pdev->dev,
|
||||
"Could not prepare or enable the clock.\n");
|
||||
goto error_free_irq;
|
||||
}
|
||||
|
||||
ret = clk_enable(st->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not enable the clock.\n");
|
||||
goto error_unprepare_clk;
|
||||
}
|
||||
|
||||
st->adc_clk = clk_get(&pdev->dev, "adc_op_clk");
|
||||
st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk");
|
||||
if (IS_ERR(st->adc_clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get the ADC clock.\n");
|
||||
ret = PTR_ERR(st->adc_clk);
|
||||
goto error_disable_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare(st->adc_clk);
|
||||
ret = clk_prepare_enable(st->adc_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not prepare the ADC clock.\n");
|
||||
goto error_free_adc_clk;
|
||||
}
|
||||
|
||||
ret = clk_enable(st->adc_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not enable the ADC clock.\n");
|
||||
goto error_unprepare_adc_clk;
|
||||
dev_err(&pdev->dev,
|
||||
"Could not prepare or enable the ADC clock.\n");
|
||||
goto error_disable_clk;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -694,23 +671,11 @@ error_remove_triggers:
|
||||
error_unregister_buffer:
|
||||
at91_adc_buffer_remove(idev);
|
||||
error_disable_adc_clk:
|
||||
clk_disable(st->adc_clk);
|
||||
error_unprepare_adc_clk:
|
||||
clk_unprepare(st->adc_clk);
|
||||
error_free_adc_clk:
|
||||
clk_put(st->adc_clk);
|
||||
clk_disable_unprepare(st->adc_clk);
|
||||
error_disable_clk:
|
||||
clk_disable(st->clk);
|
||||
error_unprepare_clk:
|
||||
clk_unprepare(st->clk);
|
||||
error_free_clk:
|
||||
clk_put(st->clk);
|
||||
clk_disable_unprepare(st->clk);
|
||||
error_free_irq:
|
||||
free_irq(st->irq, idev);
|
||||
error_unmap_reg:
|
||||
iounmap(st->reg_base);
|
||||
error_release_mem:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
error_free_device:
|
||||
iio_device_free(idev);
|
||||
error_ret:
|
||||
@@ -720,20 +685,14 @@ error_ret:
|
||||
static int __devexit at91_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *idev = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
|
||||
iio_device_unregister(idev);
|
||||
at91_adc_trigger_remove(idev);
|
||||
at91_adc_buffer_remove(idev);
|
||||
clk_disable_unprepare(st->adc_clk);
|
||||
clk_put(st->adc_clk);
|
||||
clk_disable(st->clk);
|
||||
clk_unprepare(st->clk);
|
||||
clk_put(st->clk);
|
||||
clk_disable_unprepare(st->clk);
|
||||
free_irq(st->irq, idev);
|
||||
iounmap(st->reg_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
iio_device_free(idev);
|
||||
|
||||
return 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user