mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
Staging tree update for 3.9-rc1
Here's the big staging tree merge for 3.9-rc1 Lots of cleanups and updates for drivers all through the staging tree. We are pretty much "code neutral" here, adding just about as many lines as we removed. All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEUEABECAAYFAlEmW7QACgkQMUfUDdst+ymlIACXT5mv8Y5A/KJa+QLTNNsofI8u aACgq9hNZxJzX6VQMLXUV8+2SILOqYo= =5wIj -----END PGP SIGNATURE----- Merge tag 'staging-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging tree update from Greg Kroah-Hartman: "Here's the big staging tree merge for 3.9-rc1 Lots of cleanups and updates for drivers all through the staging tree. We are pretty much "code neutral" here, adding just about as many lines as we removed. All of these have been in linux-next for a while." * tag 'staging-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (804 commits) staging: comedi: vmk80xx: wait for URBs to complete staging: comedi: drivers: addi-data: hwdrv_apci3200.c: Add a missing semicolon staging: et131x: Update TODO list staging: et131x: Remove assignment of skb->dev staging: wlan-ng: hfa384x.h: fix for error reported by smatch staging/zache checkpatch ERROR: spaces prohibited around that staging/ozwpan: Mark read only parameters and structs as const staging/ozwpan: Remove empty and unused function oz_cdev_heartbeat staging/ozwpan: Mark local functions as static (fix sparse warnings) staging/ozwpan: Add missing header includes staging/usbip: Mark local functions as static (fix sparse warnings) staging/xgifb: Remove duplicated code in loops. staging/xgifb: Consolidate return paths staging/xgifb: Remove code without effect staging/xgifb: Remove unnecessary casts staging/xgifb: Consolidate if/else if with identical code branches staging: vt6656: replaced custom TRUE definition with true staging: vt6656: replaced custom FALSE definition with false staging: vt6656: replace custom BOOL definition with bool staging/rtl8187se: Mark functions as static to silence sparse ...
This commit is contained in:
commit
b5c78e04dd
13
Documentation/ABI/testing/sysfs-bus-iio-mpu6050
Normal file
13
Documentation/ABI/testing/sysfs-bus-iio-mpu6050
Normal file
@ -0,0 +1,13 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_gyro_matrix
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_matrix
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_matrix
|
||||
KernelVersion: 3.4.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This is mounting matrix for motion sensors. Mounting matrix
|
||||
is a 3x3 unitary matrix. A typical mounting matrix would look like
|
||||
[0, 1, 0; 1, 0, 0; 0, 0, -1]. Using this information, it would be
|
||||
easy to tell the relative positions among sensors as well as their
|
||||
positions relative to the board that holds these sensors. Identity matrix
|
||||
[1, 0, 0; 0, 1, 0; 0, 0, 1] means sensor chip and device are perfectly
|
||||
aligned with each other. All axes are exactly the same.
|
@ -5,6 +5,12 @@ Required properties:
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain the LRADC interrupts
|
||||
|
||||
Optional properties:
|
||||
- fsl,lradc-touchscreen-wires: Number of wires used to connect the touchscreen
|
||||
to LRADC. Valid value is either 4 or 5. If this
|
||||
property is not present, then the touchscreen is
|
||||
disabled.
|
||||
|
||||
Examples:
|
||||
|
||||
lradc@80050000 {
|
||||
|
@ -7299,8 +7299,7 @@ S: Odd Fixes
|
||||
F: drivers/staging/olpc_dcon/
|
||||
|
||||
STAGING - OZMO DEVICES USB OVER WIFI DRIVER
|
||||
M: Rupesh Gujare <rgujare@ozmodevices.com>
|
||||
M: Chris Kelly <ckelly@ozmodevices.com>
|
||||
M: Rupesh Gujare <rupesh.gujare@atmel.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/ozwpan/
|
||||
|
||||
|
@ -391,7 +391,9 @@
|
||||
};
|
||||
|
||||
lradc@80050000 {
|
||||
compatible = "fsl,imx23-lradc";
|
||||
reg = <0x80050000 0x2000>;
|
||||
interrupts = <36 37 38 39 40 41 42 43 44>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
#include <media/si4713.h>
|
||||
#include <linux/leds-lp5523.h>
|
||||
|
||||
#include <../drivers/staging/iio/light/tsl2563.h>
|
||||
#include <linux/platform_data/tsl2563.h>
|
||||
#include <linux/lis3lv02d.h>
|
||||
|
||||
#if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE)
|
||||
|
@ -135,8 +135,7 @@ static int adc_jack_probe(struct platform_device *pdev)
|
||||
;
|
||||
data->num_conditions = i;
|
||||
|
||||
data->chan = iio_channel_get(dev_name(&pdev->dev),
|
||||
pdata->consumer_channel);
|
||||
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
|
||||
if (IS_ERR(data->chan)) {
|
||||
err = PTR_ERR(data->chan);
|
||||
goto out;
|
||||
|
@ -14,4 +14,42 @@ config HID_SENSOR_ACCEL_3D
|
||||
Say yes here to build support for the HID SENSOR
|
||||
accelerometers 3D.
|
||||
|
||||
config KXSD9
|
||||
tristate "Kionix KXSD9 Accelerometer Driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for the Kionix KXSD9 accelerometer.
|
||||
Currently this only supports the device via an SPI interface.
|
||||
|
||||
config IIO_ST_ACCEL_3AXIS
|
||||
tristate "STMicroelectronics accelerometers 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
select IIO_ST_SENSORS_CORE
|
||||
select IIO_ST_ACCEL_I2C_3AXIS if (I2C)
|
||||
select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER)
|
||||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
select IIO_ST_ACCEL_BUFFER if (IIO_TRIGGERED_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics accelerometers:
|
||||
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330.
|
||||
|
||||
This driver can also be built as a module. If so, will be created
|
||||
these modules:
|
||||
- st_accel (core functions for the driver [it is mandatory]);
|
||||
- st_accel_i2c (necessary for the I2C devices [optional*]);
|
||||
- st_accel_spi (necessary for the SPI devices [optional*]);
|
||||
|
||||
(*) one of these is necessary to do something.
|
||||
|
||||
config IIO_ST_ACCEL_I2C_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_ACCEL_3AXIS
|
||||
depends on IIO_ST_SENSORS_I2C
|
||||
|
||||
config IIO_ST_ACCEL_SPI_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_ACCEL_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
endmenu
|
||||
|
@ -3,3 +3,12 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
|
||||
st_accel-y := st_accel_core.o
|
||||
st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_I2C_3AXIS) += st_accel_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_SPI_3AXIS) += st_accel_spi.o
|
||||
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
|
@ -28,7 +28,6 @@
|
||||
#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*/
|
||||
@ -44,7 +43,7 @@ enum accel_3d_channel {
|
||||
|
||||
struct accel_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_iio_common common_attributes;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX];
|
||||
};
|
||||
|
@ -94,7 +94,6 @@ error_ret:
|
||||
|
||||
static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
|
||||
{
|
||||
struct spi_message msg;
|
||||
int ret;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[] = {
|
||||
@ -112,10 +111,7 @@ static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = KXSD9_READ(address);
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret)
|
||||
return ret;
|
||||
return (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
|
||||
@ -226,7 +222,7 @@ static int kxsd9_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct kxsd9_state *st;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
@ -245,14 +241,14 @@ static int kxsd9_probe(struct spi_device *spi)
|
||||
indio_dev->info = &kxsd9_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi_setup(spi);
|
||||
kxsd9_power_up(st);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_dev:
|
47
drivers/iio/accel/st_accel.h
Normal file
47
drivers/iio/accel/st_accel.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
* v. 1.0.0
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_ACCEL_H
|
||||
#define ST_ACCEL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
#define LIS3DH_ACCEL_DEV_NAME "lis3dh"
|
||||
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
|
||||
#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel"
|
||||
#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel"
|
||||
#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh"
|
||||
#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel"
|
||||
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"
|
||||
#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel"
|
||||
#define LSM330_ACCEL_DEV_NAME "lsm330_accel"
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev);
|
||||
void st_accel_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
int st_accel_allocate_ring(struct iio_dev *indio_dev);
|
||||
void st_accel_deallocate_ring(struct iio_dev *indio_dev);
|
||||
int st_accel_trig_set_state(struct iio_trigger *trig, bool state);
|
||||
#define ST_ACCEL_TRIGGER_SET_STATE (&st_accel_trig_set_state)
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
#define ST_ACCEL_TRIGGER_SET_STATE NULL
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#endif /* ST_ACCEL_H */
|
114
drivers/iio/accel/st_accel_buffer.c
Normal file
114
drivers/iio/accel/st_accel_buffer.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_accel_set_enable_error;
|
||||
|
||||
err = iio_sw_buffer_preenable(indio_dev);
|
||||
|
||||
st_accel_set_enable_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (adata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_accel_buffer_postenable_error:
|
||||
kfree(adata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
|
||||
st_accel_buffer_predisable_error:
|
||||
kfree(adata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
|
||||
.preenable = &st_accel_buffer_preenable,
|
||||
.postenable = &st_accel_buffer_postenable,
|
||||
.predisable = &st_accel_buffer_predisable,
|
||||
};
|
||||
|
||||
int st_accel_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&st_sensors_trigger_handler, &st_accel_buffer_setup_ops);
|
||||
}
|
||||
|
||||
void st_accel_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers buffer");
|
||||
MODULE_LICENSE("GPL v2");
|
500
drivers/iio/accel/st_accel_core.c
Normal file
500
drivers/iio/accel/st_accel_core.c
Normal file
@ -0,0 +1,500 @@
|
||||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
/* DEFAULT VALUE FOR SENSORS */
|
||||
#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28
|
||||
#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_ACCEL_FS_AVL_2G 2
|
||||
#define ST_ACCEL_FS_AVL_4G 4
|
||||
#define ST_ACCEL_FS_AVL_6G 6
|
||||
#define ST_ACCEL_FS_AVL_8G 8
|
||||
#define ST_ACCEL_FS_AVL_16G 16
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||
#define ST_ACCEL_1_WAI_EXP 0x33
|
||||
#define ST_ACCEL_1_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_1_ODR_MASK 0xf0
|
||||
#define ST_ACCEL_1_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_ACCEL_1_ODR_AVL_10HZ_VAL 0x02
|
||||
#define ST_ACCEL_1_ODR_AVL_25HZ_VAL 0x03
|
||||
#define ST_ACCEL_1_ODR_AVL_50HZ_VAL 0x04
|
||||
#define ST_ACCEL_1_ODR_AVL_100HZ_VAL 0x05
|
||||
#define ST_ACCEL_1_ODR_AVL_200HZ_VAL 0x06
|
||||
#define ST_ACCEL_1_ODR_AVL_400HZ_VAL 0x07
|
||||
#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL 0x08
|
||||
#define ST_ACCEL_1_FS_ADDR 0x23
|
||||
#define ST_ACCEL_1_FS_MASK 0x30
|
||||
#define ST_ACCEL_1_FS_AVL_2_VAL 0x00
|
||||
#define ST_ACCEL_1_FS_AVL_4_VAL 0x01
|
||||
#define ST_ACCEL_1_FS_AVL_8_VAL 0x02
|
||||
#define ST_ACCEL_1_FS_AVL_16_VAL 0x03
|
||||
#define ST_ACCEL_1_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000)
|
||||
#define ST_ACCEL_1_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000)
|
||||
#define ST_ACCEL_1_FS_AVL_8_GAIN IIO_G_TO_M_S_2(4000)
|
||||
#define ST_ACCEL_1_FS_AVL_16_GAIN IIO_G_TO_M_S_2(12000)
|
||||
#define ST_ACCEL_1_BDU_ADDR 0x23
|
||||
#define ST_ACCEL_1_BDU_MASK 0x80
|
||||
#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_1_DRDY_IRQ_MASK 0x10
|
||||
#define ST_ACCEL_1_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 2 */
|
||||
#define ST_ACCEL_2_WAI_EXP 0x32
|
||||
#define ST_ACCEL_2_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_2_ODR_MASK 0x18
|
||||
#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x00
|
||||
#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x01
|
||||
#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x02
|
||||
#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL 0x03
|
||||
#define ST_ACCEL_2_PW_ADDR 0x20
|
||||
#define ST_ACCEL_2_PW_MASK 0xe0
|
||||
#define ST_ACCEL_2_FS_ADDR 0x23
|
||||
#define ST_ACCEL_2_FS_MASK 0x30
|
||||
#define ST_ACCEL_2_FS_AVL_2_VAL 0X00
|
||||
#define ST_ACCEL_2_FS_AVL_4_VAL 0X01
|
||||
#define ST_ACCEL_2_FS_AVL_8_VAL 0x03
|
||||
#define ST_ACCEL_2_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000)
|
||||
#define ST_ACCEL_2_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000)
|
||||
#define ST_ACCEL_2_FS_AVL_8_GAIN IIO_G_TO_M_S_2(3900)
|
||||
#define ST_ACCEL_2_BDU_ADDR 0x23
|
||||
#define ST_ACCEL_2_BDU_MASK 0x80
|
||||
#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_2_DRDY_IRQ_MASK 0x02
|
||||
#define ST_ACCEL_2_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 3 */
|
||||
#define ST_ACCEL_3_WAI_EXP 0x40
|
||||
#define ST_ACCEL_3_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_3_ODR_MASK 0xf0
|
||||
#define ST_ACCEL_3_ODR_AVL_3HZ_VAL 0x01
|
||||
#define ST_ACCEL_3_ODR_AVL_6HZ_VAL 0x02
|
||||
#define ST_ACCEL_3_ODR_AVL_12HZ_VAL 0x03
|
||||
#define ST_ACCEL_3_ODR_AVL_25HZ_VAL 0x04
|
||||
#define ST_ACCEL_3_ODR_AVL_50HZ_VAL 0x05
|
||||
#define ST_ACCEL_3_ODR_AVL_100HZ_VAL 0x06
|
||||
#define ST_ACCEL_3_ODR_AVL_200HZ_VAL 0x07
|
||||
#define ST_ACCEL_3_ODR_AVL_400HZ_VAL 0x08
|
||||
#define ST_ACCEL_3_ODR_AVL_800HZ_VAL 0x09
|
||||
#define ST_ACCEL_3_ODR_AVL_1600HZ_VAL 0x0a
|
||||
#define ST_ACCEL_3_FS_ADDR 0x24
|
||||
#define ST_ACCEL_3_FS_MASK 0x38
|
||||
#define ST_ACCEL_3_FS_AVL_2_VAL 0X00
|
||||
#define ST_ACCEL_3_FS_AVL_4_VAL 0X01
|
||||
#define ST_ACCEL_3_FS_AVL_6_VAL 0x02
|
||||
#define ST_ACCEL_3_FS_AVL_8_VAL 0x03
|
||||
#define ST_ACCEL_3_FS_AVL_16_VAL 0x04
|
||||
#define ST_ACCEL_3_FS_AVL_2_GAIN IIO_G_TO_M_S_2(61)
|
||||
#define ST_ACCEL_3_FS_AVL_4_GAIN IIO_G_TO_M_S_2(122)
|
||||
#define ST_ACCEL_3_FS_AVL_6_GAIN IIO_G_TO_M_S_2(183)
|
||||
#define ST_ACCEL_3_FS_AVL_8_GAIN IIO_G_TO_M_S_2(244)
|
||||
#define ST_ACCEL_3_FS_AVL_16_GAIN IIO_G_TO_M_S_2(732)
|
||||
#define ST_ACCEL_3_BDU_ADDR 0x20
|
||||
#define ST_ACCEL_3_BDU_MASK 0x08
|
||||
#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23
|
||||
#define ST_ACCEL_3_DRDY_IRQ_MASK 0x80
|
||||
#define ST_ACCEL_3_IG1_EN_ADDR 0x23
|
||||
#define ST_ACCEL_3_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_3_MULTIREAD_BIT false
|
||||
|
||||
static const struct iio_chan_spec st_accel_12bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_accel_16bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_accel_sensors[] = {
|
||||
{
|
||||
.wai = ST_ACCEL_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS3DH_ACCEL_DEV_NAME,
|
||||
[1] = LSM303DLHC_ACCEL_DEV_NAME,
|
||||
[2] = LSM330D_ACCEL_DEV_NAME,
|
||||
[3] = LSM330DL_ACCEL_DEV_NAME,
|
||||
[4] = LSM330DLC_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_1_ODR_ADDR,
|
||||
.mask = ST_ACCEL_1_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
|
||||
{ 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
|
||||
{ 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
|
||||
{ 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
|
||||
{ 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
|
||||
{ 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
|
||||
{ 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
|
||||
{ 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_1_ODR_ADDR,
|
||||
.mask = ST_ACCEL_1_ODR_MASK,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_1_FS_ADDR,
|
||||
.mask = ST_ACCEL_1_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_1_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = ST_ACCEL_1_FS_AVL_4_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_4_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_1_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_8_GAIN,
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_ACCEL_FS_AVL_16G,
|
||||
.value = ST_ACCEL_1_FS_AVL_16_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_16_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_1_BDU_ADDR,
|
||||
.mask = ST_ACCEL_1_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
|
||||
.mask = ST_ACCEL_1_DRDY_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_2_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS331DLH_ACCEL_DEV_NAME,
|
||||
[1] = LSM303DL_ACCEL_DEV_NAME,
|
||||
[2] = LSM303DLH_ACCEL_DEV_NAME,
|
||||
[3] = LSM303DLM_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_2_ODR_ADDR,
|
||||
.mask = ST_ACCEL_2_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
|
||||
{ 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
|
||||
{ 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
|
||||
{ 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_2_PW_ADDR,
|
||||
.mask = ST_ACCEL_2_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_2_FS_ADDR,
|
||||
.mask = ST_ACCEL_2_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_2_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_2_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = ST_ACCEL_2_FS_AVL_4_VAL,
|
||||
.gain = ST_ACCEL_2_FS_AVL_4_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_2_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_2_FS_AVL_8_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_2_BDU_ADDR,
|
||||
.mask = ST_ACCEL_2_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
|
||||
.mask = ST_ACCEL_2_DRDY_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_3_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LSM330_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_3_ODR_ADDR,
|
||||
.mask = ST_ACCEL_3_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 3, ST_ACCEL_3_ODR_AVL_3HZ_VAL },
|
||||
{ 6, ST_ACCEL_3_ODR_AVL_6HZ_VAL, },
|
||||
{ 12, ST_ACCEL_3_ODR_AVL_12HZ_VAL, },
|
||||
{ 25, ST_ACCEL_3_ODR_AVL_25HZ_VAL, },
|
||||
{ 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
|
||||
{ 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
|
||||
{ 200, ST_ACCEL_3_ODR_AVL_200HZ_VAL, },
|
||||
{ 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
|
||||
{ 800, ST_ACCEL_3_ODR_AVL_800HZ_VAL, },
|
||||
{ 1600, ST_ACCEL_3_ODR_AVL_1600HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_3_ODR_ADDR,
|
||||
.mask = ST_ACCEL_3_ODR_MASK,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_3_FS_ADDR,
|
||||
.mask = ST_ACCEL_3_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_3_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = ST_ACCEL_3_FS_AVL_4_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_4_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_6G,
|
||||
.value = ST_ACCEL_3_FS_AVL_6_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_6_GAIN,
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_3_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_8_GAIN,
|
||||
},
|
||||
[4] = {
|
||||
.num = ST_ACCEL_FS_AVL_16G,
|
||||
.value = ST_ACCEL_3_FS_AVL_16_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_16_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_3_BDU_ADDR,
|
||||
.mask = ST_ACCEL_3_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
|
||||
.mask = ST_ACCEL_3_DRDY_IRQ_MASK,
|
||||
.ig1 = {
|
||||
.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
|
||||
.en_mask = ST_ACCEL_3_IG1_EN_MASK,
|
||||
},
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = st_sensors_read_info_raw(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = adata->current_fullscale->gain;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
read_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
err = st_sensors_set_fullscale_by_gain(indio_dev, val2);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ST_SENSOR_DEV_ATTR_SAMP_FREQ();
|
||||
static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
|
||||
static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available);
|
||||
|
||||
static struct attribute *st_accel_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_accel_attribute_group = {
|
||||
.attrs = st_accel_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info accel_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_accel_attribute_group,
|
||||
.read_raw = &st_accel_read_raw,
|
||||
.write_raw = &st_accel_write_raw,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
static const struct iio_trigger_ops st_accel_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = ST_ACCEL_TRIGGER_SET_STATE,
|
||||
};
|
||||
#define ST_ACCEL_TRIGGER_OPS (&st_accel_trigger_ops)
|
||||
#else
|
||||
#define ST_ACCEL_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &accel_info;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_accel_sensors), st_accel_sensors);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
|
||||
adata->multiread_bit = adata->sensor->multi_read_bit;
|
||||
indio_dev->channels = adata->sensor->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
adata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&adata->sensor->fs.fs_avl[0];
|
||||
adata->odr = adata->sensor->odr.odr_avl[0].hz;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0) {
|
||||
err = st_accel_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_ACCEL_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
goto st_accel_probe_trigger_error;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto st_accel_device_register_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_accel_device_register_error:
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_probe_trigger_error:
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
st_accel_common_probe_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_common_probe);
|
||||
|
||||
void st_accel_common_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0) {
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
}
|
||||
iio_device_free(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_common_remove);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
|
||||
MODULE_LICENSE("GPL v2");
|
86
drivers/iio/accel/st_accel_i2c.c
Normal file
86
drivers/iio/accel/st_accel_i2c.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *adata;
|
||||
int err;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*adata));
|
||||
if (indio_dev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto iio_device_alloc_error;
|
||||
}
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->dev = &client->dev;
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
|
||||
return 0;
|
||||
|
||||
st_accel_common_probe_error:
|
||||
iio_device_free(indio_dev);
|
||||
iio_device_alloc_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
st_accel_common_remove(i2c_get_clientdata(client));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME },
|
||||
{ LIS3DH_ACCEL_DEV_NAME },
|
||||
{ LSM330D_ACCEL_DEV_NAME },
|
||||
{ LSM330DL_ACCEL_DEV_NAME },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DL_ACCEL_DEV_NAME },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||
{ LSM330_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static struct i2c_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-accel-i2c",
|
||||
},
|
||||
.probe = st_accel_i2c_probe,
|
||||
.remove = st_accel_i2c_remove,
|
||||
.id_table = st_accel_id_table,
|
||||
};
|
||||
module_i2c_driver(st_accel_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
85
drivers/iio/accel/st_accel_spi.c
Normal file
85
drivers/iio/accel/st_accel_spi.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
static int st_accel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *adata;
|
||||
int err;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*adata));
|
||||
if (indio_dev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto iio_device_alloc_error;
|
||||
}
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
|
||||
return 0;
|
||||
|
||||
st_accel_common_probe_error:
|
||||
iio_device_free(indio_dev);
|
||||
iio_device_alloc_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
st_accel_common_remove(spi_get_drvdata(spi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME },
|
||||
{ LIS3DH_ACCEL_DEV_NAME },
|
||||
{ LSM330D_ACCEL_DEV_NAME },
|
||||
{ LSM330DL_ACCEL_DEV_NAME },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DL_ACCEL_DEV_NAME },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||
{ LSM330_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
|
||||
static struct spi_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-accel-spi",
|
||||
},
|
||||
.probe = st_accel_spi_probe,
|
||||
.remove = st_accel_spi_remove,
|
||||
.id_table = st_accel_id_table,
|
||||
};
|
||||
module_spi_driver(st_accel_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -100,10 +100,8 @@ config LP8788_ADC
|
||||
config MAX1363
|
||||
tristate "Maxim max1363 ADC driver"
|
||||
depends on I2C
|
||||
select IIO_TRIGGER
|
||||
select MAX1363_RING_BUFFER
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for many Maxim i2c analog to digital
|
||||
converters (ADC). (max1361, max1362, max1363, max1364, max1036,
|
||||
|
@ -179,7 +179,7 @@ static int lp8788_iio_map_register(struct iio_dev *indio_dev,
|
||||
|
||||
ret = iio_map_array_register(indio_dev, map);
|
||||
if (ret) {
|
||||
dev_err(adc->lp->dev, "iio map err: %d\n", ret);
|
||||
dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -187,12 +187,6 @@ static int lp8788_iio_map_register(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void lp8788_iio_map_unregister(struct iio_dev *indio_dev,
|
||||
struct lp8788_adc *adc)
|
||||
{
|
||||
iio_map_array_unregister(indio_dev, adc->map);
|
||||
}
|
||||
|
||||
static int lp8788_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
|
||||
@ -208,13 +202,14 @@ static int lp8788_adc_probe(struct platform_device *pdev)
|
||||
adc->lp = lp;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc);
|
||||
if (ret)
|
||||
goto err_iio_map;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->dev.parent = lp->dev;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &lp8788_adc_info;
|
||||
@ -223,14 +218,14 @@ static int lp8788_adc_probe(struct platform_device *pdev)
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(lp->dev, "iio dev register err: %d\n", ret);
|
||||
dev_err(&pdev->dev, "iio dev register err: %d\n", ret);
|
||||
goto err_iio_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_iio_device:
|
||||
lp8788_iio_map_unregister(indio_dev, adc);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
err_iio_map:
|
||||
iio_device_free(indio_dev);
|
||||
return ret;
|
||||
@ -239,10 +234,9 @@ err_iio_map:
|
||||
static int lp8788_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct lp8788_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
lp8788_iio_map_unregister(indio_dev, adc);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define MAX1363_SETUP_BYTE(a) ((a) | 0x80)
|
||||
|
||||
@ -55,7 +56,7 @@
|
||||
#define MAX1363_SETUP_POWER_UP_INT_REF 0x10
|
||||
#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00
|
||||
|
||||
/* think about includeing max11600 etc - more settings */
|
||||
/* think about including max11600 etc - more settings */
|
||||
#define MAX1363_SETUP_EXT_CLOCK 0x08
|
||||
#define MAX1363_SETUP_INT_CLOCK 0x00
|
||||
#define MAX1363_SETUP_UNIPOLAR 0x00
|
||||
@ -86,7 +87,7 @@
|
||||
/* max123{6-9} only */
|
||||
#define MAX1236_SCAN_MID_TO_CHANNEL 0x40
|
||||
|
||||
/* max1363 only - merely part of channel selects or don't care for others*/
|
||||
/* max1363 only - merely part of channel selects or don't care for others */
|
||||
#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18
|
||||
|
||||
#define MAX1363_CHANNEL_SEL(a) ((a) << 1)
|
||||
@ -133,7 +134,7 @@ enum max1363_modes {
|
||||
* @mode_list: array of available scan modes
|
||||
* @default_mode: the scan mode in which the chip starts up
|
||||
* @int_vref_mv: the internal reference voltage
|
||||
* @num_channels: number of channels
|
||||
* @num_modes: number of modes
|
||||
* @bits: accuracy of the adc in bits
|
||||
*/
|
||||
struct max1363_chip_info {
|
||||
@ -152,7 +153,7 @@ struct max1363_chip_info {
|
||||
* @client: i2c_client
|
||||
* @setupbyte: cache of current device setup byte
|
||||
* @configbyte: cache of current device config byte
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @chip_info: chip model specific constants, available modes, etc.
|
||||
* @current_mode: the scan mode of this chip
|
||||
* @requestedmask: a valid requested set of channels
|
||||
* @reg: supply regulator
|
||||
@ -162,6 +163,8 @@ struct max1363_chip_info {
|
||||
* @mask_low: bitmask for enabled low thresholds
|
||||
* @thresh_high: high threshold values
|
||||
* @thresh_low: low threshold values
|
||||
* @vref: Reference voltage regulator
|
||||
* @vref_uv: Actual (external or internal) reference voltage
|
||||
*/
|
||||
struct max1363_state {
|
||||
struct i2c_client *client;
|
||||
@ -181,6 +184,8 @@ struct max1363_state {
|
||||
/* 4x unipolar first then the fours bipolar ones */
|
||||
s16 thresh_high[8];
|
||||
s16 thresh_low[8];
|
||||
struct regulator *vref;
|
||||
u32 vref_uv;
|
||||
};
|
||||
|
||||
#define MAX1363_MODE_SINGLE(_num, _mask) { \
|
||||
@ -293,7 +298,7 @@ static const struct max1363_mode max1363_mode_table[] = {
|
||||
|
||||
static const struct max1363_mode
|
||||
*max1363_match_mode(const unsigned long *mask,
|
||||
const struct max1363_chip_info *ci)
|
||||
const struct max1363_chip_info *ci)
|
||||
{
|
||||
int i;
|
||||
if (mask)
|
||||
@ -334,7 +339,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
|
||||
{
|
||||
int ret = 0;
|
||||
s32 data;
|
||||
char rxbuf[2];
|
||||
u8 rxbuf[2];
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
struct i2c_client *client = st->client;
|
||||
|
||||
@ -366,7 +371,8 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
|
||||
ret = data;
|
||||
goto error_ret;
|
||||
}
|
||||
data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8;
|
||||
data = (rxbuf[1] | rxbuf[0] << 8) &
|
||||
((1 << st->chip_info->bits) - 1);
|
||||
} else {
|
||||
/* Get reading */
|
||||
data = i2c_master_recv(client, rxbuf, 1);
|
||||
@ -391,6 +397,8 @@ static int max1363_read_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned long scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = max1363_read_single_chan(indio_dev, chan, val, m);
|
||||
@ -398,16 +406,10 @@ static int max1363_read_raw(struct iio_dev *indio_dev,
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if ((1 << (st->chip_info->bits + 1)) >
|
||||
st->chip_info->int_vref_mv) {
|
||||
*val = 0;
|
||||
*val2 = 500000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
} else {
|
||||
*val = (st->chip_info->int_vref_mv)
|
||||
>> st->chip_info->bits;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
scale_uv = st->vref_uv >> st->chip_info->bits;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1388,13 +1390,17 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = {
|
||||
|
||||
static int max1363_initial_setup(struct max1363_state *st)
|
||||
{
|
||||
st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD
|
||||
| MAX1363_SETUP_POWER_UP_INT_REF
|
||||
| MAX1363_SETUP_INT_CLOCK
|
||||
st->setupbyte = MAX1363_SETUP_INT_CLOCK
|
||||
| MAX1363_SETUP_UNIPOLAR
|
||||
| MAX1363_SETUP_NORESET;
|
||||
|
||||
/* Set scan mode writes the config anyway so wait until then*/
|
||||
if (st->vref)
|
||||
st->setupbyte |= MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF;
|
||||
else
|
||||
st->setupbyte |= MAX1363_SETUP_POWER_UP_INT_REF
|
||||
| MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT;
|
||||
|
||||
/* Set scan mode writes the config anyway so wait until then */
|
||||
st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte);
|
||||
st->current_mode = &max1363_mode_table[st->chip_info->default_mode];
|
||||
st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte);
|
||||
@ -1408,8 +1414,9 @@ static int max1363_alloc_scan_masks(struct iio_dev *indio_dev)
|
||||
unsigned long *masks;
|
||||
int i;
|
||||
|
||||
masks = kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*sizeof(long)*
|
||||
(st->chip_info->num_modes + 1), GFP_KERNEL);
|
||||
masks = devm_kzalloc(&indio_dev->dev,
|
||||
BITS_TO_LONGS(MAX1363_MAX_CHANNELS) * sizeof(long) *
|
||||
(st->chip_info->num_modes + 1), GFP_KERNEL);
|
||||
if (!masks)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1423,7 +1430,6 @@ static int max1363_alloc_scan_masks(struct iio_dev *indio_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t max1363_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
@ -1483,54 +1489,13 @@ static const struct iio_buffer_setup_ops max1363_buffered_setup_ops = {
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
static int max1363_register_buffered_funcs_and_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
indio_dev->buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!indio_dev->buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
|
||||
&max1363_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"%s_consumer%d",
|
||||
st->client->name,
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_deallocate_sw_rb;
|
||||
}
|
||||
/* Buffer functions - here trigger setup related */
|
||||
indio_dev->setup_ops = &max1363_buffered_setup_ops;
|
||||
|
||||
/* Flag that polled buffering is possible */
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
|
||||
return 0;
|
||||
|
||||
error_deallocate_sw_rb:
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void max1363_buffer_cleanup(struct iio_dev *indio_dev)
|
||||
{
|
||||
/* ensure that the trigger has been detached */
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static int max1363_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct max1363_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regulator *vref;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(struct max1363_state));
|
||||
if (indio_dev == NULL) {
|
||||
@ -1538,13 +1503,14 @@ static int max1363_probe(struct i2c_client *client,
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
indio_dev->dev.of_node = client->dev.of_node;
|
||||
ret = iio_map_array_register(indio_dev, client->dev.platform_data);
|
||||
if (ret < 0)
|
||||
goto error_free_device;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = regulator_get(&client->dev, "vcc");
|
||||
st->reg = devm_regulator_get(&client->dev, "vcc");
|
||||
if (IS_ERR(st->reg)) {
|
||||
ret = PTR_ERR(st->reg);
|
||||
goto error_unregister_map;
|
||||
@ -1552,7 +1518,7 @@ static int max1363_probe(struct i2c_client *client,
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto error_put_reg;
|
||||
goto error_unregister_map;
|
||||
|
||||
/* this is only used for device removal purposes */
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
@ -1560,35 +1526,45 @@ static int max1363_probe(struct i2c_client *client,
|
||||
st->chip_info = &max1363_chip_info_tbl[id->driver_data];
|
||||
st->client = client;
|
||||
|
||||
st->vref_uv = st->chip_info->int_vref_mv * 1000;
|
||||
vref = devm_regulator_get(&client->dev, "vref");
|
||||
if (!IS_ERR(vref)) {
|
||||
int vref_uv;
|
||||
|
||||
ret = regulator_enable(vref);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
st->vref = vref;
|
||||
vref_uv = regulator_get_voltage(vref);
|
||||
if (vref_uv <= 0) {
|
||||
ret = -EINVAL;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
st->vref_uv = vref_uv;
|
||||
}
|
||||
|
||||
ret = max1363_alloc_scan_masks(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
/* Estabilish that the iio_dev is a child of the i2c device */
|
||||
/* Establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->info = st->chip_info->info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
ret = max1363_initial_setup(st);
|
||||
if (ret < 0)
|
||||
goto error_free_available_scan_masks;
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = max1363_register_buffered_funcs_and_init(indio_dev);
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&max1363_trigger_handler, &max1363_buffered_setup_ops);
|
||||
if (ret)
|
||||
goto error_free_available_scan_masks;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
st->chip_info->channels,
|
||||
st->chip_info->num_channels);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
goto error_disable_reg;
|
||||
|
||||
if (client->irq) {
|
||||
ret = request_threaded_irq(st->client->irq,
|
||||
ret = devm_request_threaded_irq(&client->dev, st->client->irq,
|
||||
NULL,
|
||||
&max1363_event_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
@ -1601,24 +1577,18 @@ static int max1363_probe(struct i2c_client *client,
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_free_irq;
|
||||
goto error_uninit_buffer;
|
||||
|
||||
return 0;
|
||||
error_free_irq:
|
||||
if (client->irq)
|
||||
free_irq(st->client->irq, indio_dev);
|
||||
|
||||
error_uninit_buffer:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_cleanup_buffer:
|
||||
max1363_buffer_cleanup(indio_dev);
|
||||
error_free_available_scan_masks:
|
||||
kfree(indio_dev->available_scan_masks);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
if (st->vref)
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
error_put_reg:
|
||||
regulator_put(st->reg);
|
||||
error_unregister_map:
|
||||
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
error_free_device:
|
||||
iio_device_free(indio_dev);
|
||||
error_out:
|
||||
@ -1631,14 +1601,11 @@ static int max1363_remove(struct i2c_client *client)
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (client->irq)
|
||||
free_irq(st->client->irq, indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
max1363_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->available_scan_masks);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (st->vref)
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
iio_map_array_unregister(indio_dev, client->dev.platform_data);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
@ -25,7 +25,7 @@ static struct iio_buffer_access_funcs iio_cb_access = {
|
||||
.store_to = &iio_buffer_cb_store_to,
|
||||
};
|
||||
|
||||
struct iio_cb_buffer *iio_channel_get_all_cb(const char *name,
|
||||
struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
||||
int (*cb)(u8 *data,
|
||||
void *private),
|
||||
void *private)
|
||||
@ -46,7 +46,7 @@ struct iio_cb_buffer *iio_channel_get_all_cb(const char *name,
|
||||
cb_buff->buffer.access = &iio_cb_access;
|
||||
INIT_LIST_HEAD(&cb_buff->buffer.demux_list);
|
||||
|
||||
cb_buff->channels = iio_channel_get_all(name);
|
||||
cb_buff->channels = iio_channel_get_all(dev);
|
||||
if (IS_ERR(cb_buff->channels)) {
|
||||
ret = PTR_ERR(cb_buff->channels);
|
||||
goto error_free_cb_buff;
|
||||
|
@ -3,3 +3,4 @@
|
||||
#
|
||||
|
||||
source "drivers/iio/common/hid-sensors/Kconfig"
|
||||
source "drivers/iio/common/st_sensors/Kconfig"
|
||||
|
@ -7,3 +7,4 @@
|
||||
#
|
||||
|
||||
obj-y += hid-sensors/
|
||||
obj-y += st_sensors/
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include "hid-sensor-attributes.h"
|
||||
|
||||
static int pow_10(unsigned power)
|
||||
{
|
||||
@ -114,7 +113,7 @@ static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
|
||||
return value;
|
||||
}
|
||||
|
||||
int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
|
||||
int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
|
||||
int *val1, int *val2)
|
||||
{
|
||||
s32 value;
|
||||
@ -141,7 +140,7 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_read_samp_freq_value);
|
||||
|
||||
int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
|
||||
int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
|
||||
int val1, int val2)
|
||||
{
|
||||
s32 value;
|
||||
@ -169,7 +168,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
|
||||
|
||||
int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
|
||||
int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,
|
||||
int *val1, int *val2)
|
||||
{
|
||||
s32 value;
|
||||
@ -191,7 +190,7 @@ int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value);
|
||||
|
||||
int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st,
|
||||
int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
|
||||
int val1, int val2)
|
||||
{
|
||||
s32 value;
|
||||
@ -212,7 +211,7 @@ EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
|
||||
|
||||
int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
struct hid_sensor_iio_common *st)
|
||||
struct hid_sensor_common *st)
|
||||
{
|
||||
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef _HID_SENSORS_ATTRIBUTES_H
|
||||
#define _HID_SENSORS_ATTRIBUTES_H
|
||||
|
||||
/* Common hid sensor iio structure */
|
||||
struct hid_sensor_iio_common {
|
||||
struct hid_sensor_hub_device *hsdev;
|
||||
struct platform_device *pdev;
|
||||
unsigned usage_id;
|
||||
bool data_ready;
|
||||
struct hid_sensor_hub_attribute_info poll;
|
||||
struct hid_sensor_hub_attribute_info report_state;
|
||||
struct hid_sensor_hub_attribute_info power_state;
|
||||
struct hid_sensor_hub_attribute_info sensitivity;
|
||||
};
|
||||
|
||||
/*Convert from hid unit expo to regular exponent*/
|
||||
static inline int hid_sensor_convert_exponent(int unit_expo)
|
||||
{
|
||||
if (unit_expo < 0x08)
|
||||
return unit_expo;
|
||||
else if (unit_expo <= 0x0f)
|
||||
return -(0x0f-unit_expo+1);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
struct hid_sensor_iio_common *st);
|
||||
int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st,
|
||||
int val1, int val2);
|
||||
int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
|
||||
int *val1, int *val2);
|
||||
int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
|
||||
int val1, int val2);
|
||||
int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
|
||||
int *val1, int *val2);
|
||||
|
||||
#endif
|
@ -26,13 +26,12 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include "hid-sensor-attributes.h"
|
||||
#include "hid-sensor-trigger.h"
|
||||
|
||||
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct hid_sensor_iio_common *st = trig->private_data;
|
||||
struct hid_sensor_common *st = trig->private_data;
|
||||
int state_val;
|
||||
|
||||
state_val = state ? 1 : 0;
|
||||
@ -64,7 +63,7 @@ static const struct iio_trigger_ops hid_sensor_trigger_ops = {
|
||||
};
|
||||
|
||||
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
||||
struct hid_sensor_iio_common *attrb)
|
||||
struct hid_sensor_common *attrb)
|
||||
{
|
||||
int ret;
|
||||
struct iio_trigger *trig;
|
||||
|
@ -20,7 +20,7 @@
|
||||
#define _HID_SENSOR_TRIGGER_H
|
||||
|
||||
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
||||
struct hid_sensor_iio_common *attrb);
|
||||
struct hid_sensor_common *attrb);
|
||||
void hid_sensor_remove_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
#endif
|
||||
|
14
drivers/iio/common/st_sensors/Kconfig
Normal file
14
drivers/iio/common/st_sensors/Kconfig
Normal file
@ -0,0 +1,14 @@
|
||||
#
|
||||
# STMicroelectronics sensors common library
|
||||
#
|
||||
|
||||
config IIO_ST_SENSORS_I2C
|
||||
tristate
|
||||
|
||||
config IIO_ST_SENSORS_SPI
|
||||
tristate
|
||||
|
||||
config IIO_ST_SENSORS_CORE
|
||||
tristate
|
||||
select IIO_ST_SENSORS_I2C if I2C
|
||||
select IIO_ST_SENSORS_SPI if SPI_MASTER
|
10
drivers/iio/common/st_sensors/Makefile
Normal file
10
drivers/iio/common/st_sensors/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Makefile for the STMicroelectronics sensor common modules.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
|
||||
obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o
|
||||
st_sensors-y := st_sensors_core.o
|
||||
st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o
|
||||
st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o
|
116
drivers/iio/common/st_sensors/st_sensors_buffer.c
Normal file
116
drivers/iio/common/st_sensors/st_sensors_buffer.c
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* STMicroelectronics sensors buffer library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
{
|
||||
int i, n = 0, len;
|
||||
u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
|
||||
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||
addr[n] = indio_dev->channels[i].address;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
switch (n) {
|
||||
case 1:
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
|
||||
sdata->multiread_bit);
|
||||
break;
|
||||
case 2:
|
||||
if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb,
|
||||
sdata->dev, addr[0],
|
||||
ST_SENSORS_BYTE_FOR_CHANNEL*n,
|
||||
buf, sdata->multiread_bit);
|
||||
} else {
|
||||
u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
|
||||
ST_SENSORS_NUMBER_DATA_CHANNELS];
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb,
|
||||
sdata->dev, addr[0],
|
||||
ST_SENSORS_BYTE_FOR_CHANNEL*
|
||||
ST_SENSORS_NUMBER_DATA_CHANNELS,
|
||||
rx_array, sdata->multiread_bit);
|
||||
if (len < 0)
|
||||
goto read_data_channels_error;
|
||||
|
||||
for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
|
||||
i++) {
|
||||
if (i < n)
|
||||
buf[i] = rx_array[i];
|
||||
else
|
||||
buf[i] = rx_array[n + i];
|
||||
}
|
||||
len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
|
||||
ST_SENSORS_NUMBER_DATA_CHANNELS,
|
||||
buf, sdata->multiread_bit);
|
||||
break;
|
||||
default:
|
||||
len = -EINVAL;
|
||||
goto read_data_channels_error;
|
||||
}
|
||||
if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
|
||||
len = -EIO;
|
||||
goto read_data_channels_error;
|
||||
}
|
||||
|
||||
read_data_channels_error:
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_get_buffer_element);
|
||||
|
||||
irqreturn_t st_sensors_trigger_handler(int irq, void *p)
|
||||
{
|
||||
int len;
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
|
||||
if (len < 0)
|
||||
goto st_sensors_get_buffer_element_error;
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
*(s64 *)((u8 *)sdata->buffer_data +
|
||||
ALIGN(len, sizeof(s64))) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, sdata->buffer_data);
|
||||
|
||||
st_sensors_get_buffer_element_error:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_trigger_handler);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
|
||||
MODULE_LICENSE("GPL v2");
|
446
drivers/iio/common/st_sensors/st_sensors_core.c
Normal file
446
drivers/iio/common/st_sensors/st_sensors_core.c
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* STMicroelectronics sensors core library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_WAI_ADDRESS 0x0f
|
||||
|
||||
static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
|
||||
u8 reg_addr, u8 mask, u8 data)
|
||||
{
|
||||
int err;
|
||||
u8 new_data;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
|
||||
if (err < 0)
|
||||
goto st_sensors_write_data_with_mask_error;
|
||||
|
||||
new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
|
||||
err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
|
||||
|
||||
st_sensors_write_data_with_mask_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_sensors_match_odr(struct st_sensors *sensor,
|
||||
unsigned int odr, struct st_sensor_odr_avl *odr_out)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
|
||||
if (sensor->odr.odr_avl[i].hz == 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if (sensor->odr.odr_avl[i].hz == odr) {
|
||||
odr_out->hz = sensor->odr.odr_avl[i].hz;
|
||||
odr_out->value = sensor->odr.odr_avl[i].value;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
st_sensors_match_odr_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_odr_avl odr_out;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_match_odr(sdata->sensor, odr, &odr_out);
|
||||
if (err < 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
|
||||
(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
|
||||
if (sdata->enabled == true) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->odr.addr,
|
||||
sdata->sensor->odr.mask,
|
||||
odr_out.value);
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
} else {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->odr.addr, sdata->sensor->odr.mask,
|
||||
odr_out.value);
|
||||
}
|
||||
if (err >= 0)
|
||||
sdata->odr = odr_out.hz;
|
||||
|
||||
st_sensors_match_odr_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_odr);
|
||||
|
||||
static int st_sensors_match_fs(struct st_sensors *sensor,
|
||||
unsigned int fs, int *index_fs_avl)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if (sensor->fs.fs_avl[i].num == 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if (sensor->fs.fs_avl[i].num == fs) {
|
||||
*index_fs_avl = i;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
st_sensors_match_odr_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs)
|
||||
{
|
||||
int err, i;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_match_fs(sdata->sensor, fs, &i);
|
||||
if (err < 0)
|
||||
goto st_accel_set_fullscale_error;
|
||||
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->fs.addr,
|
||||
sdata->sensor->fs.mask,
|
||||
sdata->sensor->fs.fs_avl[i].value);
|
||||
if (err < 0)
|
||||
goto st_accel_set_fullscale_error;
|
||||
|
||||
sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&sdata->sensor->fs.fs_avl[i];
|
||||
return err;
|
||||
|
||||
st_accel_set_fullscale_error:
|
||||
dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
bool found;
|
||||
u8 tmp_value;
|
||||
int err = -EINVAL;
|
||||
struct st_sensor_odr_avl odr_out;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
if (enable) {
|
||||
found = false;
|
||||
tmp_value = sdata->sensor->pw.value_on;
|
||||
if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
|
||||
(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
|
||||
err = st_sensors_match_odr(sdata->sensor,
|
||||
sdata->odr, &odr_out);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
tmp_value = odr_out.value;
|
||||
found = true;
|
||||
}
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->pw.addr,
|
||||
sdata->sensor->pw.mask, tmp_value);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
|
||||
sdata->enabled = true;
|
||||
|
||||
if (found)
|
||||
sdata->odr = odr_out.hz;
|
||||
} else {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->pw.addr,
|
||||
sdata->sensor->pw.mask,
|
||||
sdata->sensor->pw.value_off);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
|
||||
sdata->enabled = false;
|
||||
}
|
||||
|
||||
set_enable_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_enable);
|
||||
|
||||
int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->enable_axis.addr,
|
||||
sdata->sensor->enable_axis.mask, axis_enable);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_axis_enable);
|
||||
|
||||
int st_sensors_init_sensor(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_init(&sdata->tb.buf_lock);
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
|
||||
err = st_sensors_set_fullscale(indio_dev,
|
||||
sdata->current_fullscale->num);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
|
||||
err = st_sensors_set_odr(indio_dev, sdata->odr);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
|
||||
/* set BDU */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
init_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_init_sensor);
|
||||
|
||||
int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
/* Enable/Disable the interrupt generator 1. */
|
||||
if (sdata->sensor->drdy_irq.ig1.en_addr > 0) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->drdy_irq.ig1.en_addr,
|
||||
sdata->sensor->drdy_irq.ig1.en_mask, (int)enable);
|
||||
if (err < 0)
|
||||
goto st_accel_set_dataready_irq_error;
|
||||
}
|
||||
|
||||
/* Enable/Disable the interrupt generator for data ready. */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->drdy_irq.addr,
|
||||
sdata->sensor->drdy_irq.mask, (int)enable);
|
||||
|
||||
st_accel_set_dataready_irq_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_dataready_irq);
|
||||
|
||||
int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
|
||||
{
|
||||
int err = -EINVAL, i;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if ((sdata->sensor->fs.fs_avl[i].gain == scale) &&
|
||||
(sdata->sensor->fs.fs_avl[i].gain != 0)) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (err < 0)
|
||||
goto st_sensors_match_scale_error;
|
||||
|
||||
err = st_sensors_set_fullscale(indio_dev,
|
||||
sdata->sensor->fs.fs_avl[i].num);
|
||||
|
||||
st_sensors_match_scale_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
|
||||
|
||||
static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
||||
u8 ch_addr, int *data)
|
||||
{
|
||||
int err;
|
||||
u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
|
||||
outdata, sdata->multiread_bit);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
*data = (s16)get_unaligned_le16(outdata);
|
||||
|
||||
read_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_sensors_read_info_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
err = -EBUSY;
|
||||
goto read_error;
|
||||
} else {
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
msleep((sdata->sensor->bootime * 1000) / sdata->odr);
|
||||
err = st_sensors_read_axis_data(indio_dev, ch->address, val);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
*val = *val >> ch->scan_type.shift;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return err;
|
||||
|
||||
read_error:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_read_info_raw);
|
||||
|
||||
int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
int num_sensors_list, const struct st_sensors *sensors)
|
||||
{
|
||||
u8 wai;
|
||||
int i, n, err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||
ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
|
||||
goto read_wai_error;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_sensors_list; i++) {
|
||||
if (sensors[i].wai == wai)
|
||||
break;
|
||||
}
|
||||
if (i == num_sensors_list)
|
||||
goto device_not_supported;
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
|
||||
if (strcmp(indio_dev->name,
|
||||
&sensors[i].sensors_supported[n][0]) == 0)
|
||||
break;
|
||||
}
|
||||
if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
|
||||
dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
|
||||
goto sensor_name_mismatch;
|
||||
}
|
||||
|
||||
sdata->sensor = (struct st_sensors *)&sensors[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);
|
||||
|
||||
ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", adata->odr);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_sysfs_get_sampling_frequency);
|
||||
|
||||
ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
int err;
|
||||
unsigned int odr;
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
err = kstrtoint(buf, 10, &odr);
|
||||
if (err < 0)
|
||||
goto conversion_error;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
err = st_sensors_set_odr(indio_dev, odr);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
conversion_error:
|
||||
return err < 0 ? err : size;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_sysfs_set_sampling_frequency);
|
||||
|
||||
ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, len = 0;
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
|
||||
if (sdata->sensor->odr.odr_avl[i].hz == 0)
|
||||
break;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
sdata->sensor->odr.odr_avl[i].hz);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail);
|
||||
|
||||
ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, len = 0;
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if (sdata->sensor->fs.fs_avl[i].num == 0)
|
||||
break;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
sdata->sensor->fs.fs_avl[i].gain);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_sysfs_scale_avail);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
|
||||
MODULE_LICENSE("GPL v2");
|
81
drivers/iio/common/st_sensors/st_sensors_i2c.c
Normal file
81
drivers/iio/common/st_sensors/st_sensors_i2c.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* STMicroelectronics sensors i2c library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_I2C_MULTIREAD 0x80
|
||||
|
||||
static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_i2c_client(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
|
||||
if (err < 0)
|
||||
goto st_accel_i2c_read_byte_error;
|
||||
|
||||
*res_byte = err & 0xff;
|
||||
|
||||
st_accel_i2c_read_byte_error:
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
if (multiread_bit)
|
||||
reg_addr |= ST_SENSORS_I2C_MULTIREAD;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
|
||||
reg_addr, len, data);
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
|
||||
.read_byte = st_sensors_i2c_read_byte,
|
||||
.write_byte = st_sensors_i2c_write_byte,
|
||||
.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
|
||||
};
|
||||
|
||||
void st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
struct i2c_client *client, struct st_sensor_data *sdata)
|
||||
{
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
sdata->tf = &st_sensors_tf_i2c;
|
||||
sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_i2c_configure);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
128
drivers/iio/common/st_sensors/st_sensors_spi.c
Normal file
128
drivers/iio/common/st_sensors/st_sensors_spi.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* STMicroelectronics sensors spi library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_SPI_MULTIREAD 0xc0
|
||||
#define ST_SENSORS_SPI_READ 0x80
|
||||
|
||||
static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_spi_device(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
struct spi_message msg;
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = tb->rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
if ((multiread_bit) && (len > 1))
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
|
||||
else
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
err = spi_sync(to_spi_device(dev), &msg);
|
||||
if (err)
|
||||
goto acc_spi_read_error;
|
||||
|
||||
memcpy(data, tb->rx_buf, len*sizeof(u8));
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return len;
|
||||
|
||||
acc_spi_read_error:
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
struct spi_message msg;
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
tb->tx_buf[0] = reg_addr;
|
||||
tb->tx_buf[1] = data;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers, &msg);
|
||||
err = spi_sync(to_spi_device(dev), &msg);
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_spi = {
|
||||
.read_byte = st_sensors_spi_read_byte,
|
||||
.write_byte = st_sensors_spi_write_byte,
|
||||
.read_multiple_byte = st_sensors_spi_read_multiple_byte,
|
||||
};
|
||||
|
||||
void st_sensors_spi_configure(struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, struct st_sensor_data *sdata)
|
||||
{
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi->modalias;
|
||||
|
||||
sdata->tf = &st_sensors_tf_spi;
|
||||
sdata->get_irq_data_ready = st_sensors_spi_get_irq;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_spi_configure);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
77
drivers/iio/common/st_sensors/st_sensors_trigger.c
Normal file
77
drivers/iio/common/st_sensors/st_sensors_trigger.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* STMicroelectronics sensors trigger library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
const struct iio_trigger_ops *trigger_ops)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
|
||||
if (sdata->trig == NULL) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
|
||||
goto iio_trigger_alloc_error;
|
||||
}
|
||||
|
||||
err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_RISING,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
if (err)
|
||||
goto request_irq_error;
|
||||
|
||||
sdata->trig->private_data = indio_dev;
|
||||
sdata->trig->ops = trigger_ops;
|
||||
sdata->trig->dev.parent = sdata->dev;
|
||||
|
||||
err = iio_trigger_register(sdata->trig);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
|
||||
goto iio_trigger_register_error;
|
||||
}
|
||||
indio_dev->trig = sdata->trig;
|
||||
|
||||
return 0;
|
||||
|
||||
iio_trigger_register_error:
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
request_irq_error:
|
||||
iio_trigger_free(sdata->trig);
|
||||
iio_trigger_alloc_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_allocate_trigger);
|
||||
|
||||
void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(sdata->trig);
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
iio_trigger_free(sdata->trig);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_deallocate_trigger);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -213,7 +213,6 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type,
|
||||
unsigned int addr)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
@ -226,10 +225,6 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type,
|
||||
},
|
||||
};
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) |
|
||||
@ -237,7 +232,7 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type,
|
||||
AD5360_READBACK_TYPE(type) |
|
||||
AD5360_READBACK_ADDR(addr));
|
||||
|
||||
ret = spi_sync(st->spi, &m);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
|
@ -127,7 +127,6 @@ static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg,
|
||||
static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
@ -140,15 +139,11 @@ static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg)
|
||||
},
|
||||
};
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
|
||||
|
||||
ret = spi_sync(st->spi, &m);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
|
@ -85,11 +85,7 @@ static int ad5504_spi_read(struct spi_device *spi, u8 addr)
|
||||
.rx_buf = &val,
|
||||
.len = 2,
|
||||
};
|
||||
struct spi_message m;
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
ret = spi_sync(spi, &m);
|
||||
ret = spi_sync_transfer(spi, &t, 1);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -117,18 +117,13 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) |
|
||||
AD5686_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
|
||||
|
||||
ret = spi_sync(st->spi, &m);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -153,7 +153,6 @@ static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel,
|
||||
static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
@ -167,16 +166,12 @@ static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr)
|
||||
},
|
||||
};
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16));
|
||||
st->data[1].d32 = cpu_to_be32(AD5755_NOOP);
|
||||
|
||||
ret = spi_sync(st->spi, &m);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
|
@ -135,7 +135,6 @@ static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
@ -148,15 +147,11 @@ static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
|
||||
},
|
||||
};
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
|
||||
|
||||
ret = spi_sync(st->spi, &m);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
*val = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
|
@ -125,7 +125,6 @@ static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val)
|
||||
u8 d8[4];
|
||||
} data[3];
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &data[0].d8[1],
|
||||
@ -144,10 +143,7 @@ static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val)
|
||||
AD5791_ADDR(addr));
|
||||
data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP));
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
ret = spi_sync(spi, &msg);
|
||||
ret = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
|
||||
|
||||
*val = be32_to_cpu(data[2].d32);
|
||||
|
||||
|
@ -287,7 +287,6 @@ struct ad9523_state {
|
||||
static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
/* We encode the register size 1..3 bytes into the register address.
|
||||
@ -305,15 +304,11 @@ static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
|
||||
},
|
||||
};
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD9523_READ |
|
||||
AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
|
||||
AD9523_ADDR(addr));
|
||||
|
||||
ret = spi_sync(st->spi, &m);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "read failed (%d)", ret);
|
||||
else
|
||||
@ -326,7 +321,6 @@ static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
|
||||
static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
@ -338,16 +332,12 @@ static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
|
||||
},
|
||||
};
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD9523_WRITE |
|
||||
AD9523_CNT(AD9523_TRANSF_LEN(addr)) |
|
||||
AD9523_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(val);
|
||||
|
||||
ret = spi_sync(st->spi, &m);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "write failed (%d)", ret);
|
||||
|
@ -3,6 +3,13 @@
|
||||
#
|
||||
menu "Digital gyroscope sensors"
|
||||
|
||||
config ADIS16080
|
||||
tristate "Analog Devices ADIS16080/100 Yaw Rate Gyroscope with SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16080, ADIS16100 Yaw
|
||||
Rate Gyroscope with SPI.
|
||||
|
||||
config ADIS16136
|
||||
tristate "Analog devices ADIS16136 and similar gyroscopes driver"
|
||||
depends on SPI_MASTER
|
||||
@ -12,6 +19,16 @@ config ADIS16136
|
||||
Say yes here to build support for the Analog Devices ADIS16133, ADIS16135,
|
||||
ADIS16136 gyroscope devices.
|
||||
|
||||
config ADXRS450
|
||||
tristate "Analog Devices ADXRS450/3 Digital Output Gyroscope SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADXRS450 and ADXRS453
|
||||
programmable digital output gyroscope.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called adxrs450.
|
||||
|
||||
config HID_SENSOR_GYRO_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
@ -23,4 +40,42 @@ config HID_SENSOR_GYRO_3D
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Gyroscope 3D.
|
||||
|
||||
config IIO_ST_GYRO_3AXIS
|
||||
tristate "STMicroelectronics gyroscopes 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
select IIO_ST_SENSORS_CORE
|
||||
select IIO_ST_GYRO_I2C_3AXIS if (I2C)
|
||||
select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER)
|
||||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
select IIO_ST_GYRO_BUFFER if (IIO_TRIGGERED_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics gyroscopes:
|
||||
L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330.
|
||||
|
||||
This driver can also be built as a module. If so, will be created
|
||||
these modules:
|
||||
- st_gyro (core functions for the driver [it is mandatory]);
|
||||
- st_gyro_i2c (necessary for the I2C devices [optional*]);
|
||||
- st_gyro_spi (necessary for the SPI devices [optional*]);
|
||||
|
||||
(*) one of these is necessary to do something.
|
||||
|
||||
config IIO_ST_GYRO_I2C_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_GYRO_3AXIS
|
||||
depends on IIO_ST_SENSORS_I2C
|
||||
|
||||
config IIO_ST_GYRO_SPI_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_GYRO_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
config ITG3200
|
||||
tristate "InvenSense ITG3200 Digital 3-Axis Gyroscope I2C driver"
|
||||
depends on I2C
|
||||
select IIO_TRIGGERED_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to add support for the InvenSense ITG3200 digital
|
||||
3-axis gyroscope sensor.
|
||||
|
||||
endmenu
|
||||
|
@ -2,5 +2,19 @@
|
||||
# Makefile for industrial I/O gyroscope sensor drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ADIS16080) += adis16080.o
|
||||
obj-$(CONFIG_ADIS16136) += adis16136.o
|
||||
obj-$(CONFIG_ADXRS450) += adxrs450.o
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
|
||||
|
||||
itg3200-y := itg3200_core.o
|
||||
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
|
||||
obj-$(CONFIG_ITG3200) += itg3200.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o
|
||||
st_gyro-y := st_gyro_core.o
|
||||
st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_GYRO_I2C_3AXIS) += st_gyro_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_GYRO_SPI_3AXIS) += st_gyro_spi.o
|
||||
|
@ -29,48 +29,50 @@
|
||||
|
||||
#define ADIS16080_DIN_WRITE (1 << 15)
|
||||
|
||||
struct adis16080_chip_info {
|
||||
int scale_val;
|
||||
int scale_val2;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adis16080_state - device instance specific data
|
||||
* @us: actual spi_device to write data
|
||||
* @info: chip specific parameters
|
||||
* @buf: transmit or receive buffer
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
**/
|
||||
struct adis16080_state {
|
||||
struct spi_device *us;
|
||||
struct mutex buf_lock;
|
||||
const struct adis16080_chip_info *info;
|
||||
|
||||
u8 buf[2] ____cacheline_aligned;
|
||||
__be16 buf ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int adis16080_spi_write(struct iio_dev *indio_dev,
|
||||
u16 val)
|
||||
static int adis16080_read_sample(struct iio_dev *indio_dev,
|
||||
u16 addr, int *val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->buf[0] = val >> 8;
|
||||
st->buf[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->buf, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16080_spi_read(struct iio_dev *indio_dev,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->buf,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->buf,
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE);
|
||||
|
||||
ret = spi_read(st->us, st->buf, 2);
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
ret = spi_sync(st->us, &m);
|
||||
if (ret == 0)
|
||||
*val = sign_extend32(((st->buf[0] & 0xF) << 8) | st->buf[1], 11);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
*val = sign_extend32(be16_to_cpu(st->buf), 11);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -81,28 +83,52 @@ static int adis16080_read_raw(struct iio_dev *indio_dev,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u16 ut = 0;
|
||||
/* Take the iio_dev status lock */
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = adis16080_spi_write(indio_dev,
|
||||
chan->address |
|
||||
ADIS16080_DIN_WRITE);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = adis16080_spi_read(indio_dev, &ut);
|
||||
if (ret < 0)
|
||||
break;
|
||||
*val = ut;
|
||||
ret = IIO_VAL_INT;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis16080_read_sample(indio_dev, chan->address, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = st->info->scale_val;
|
||||
*val2 = st->info->scale_val2;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_VOLTAGE:
|
||||
/* VREF = 5V, 12 bits */
|
||||
*val = 5000;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
/* 85 C = 585, 25 C = 0 */
|
||||
*val = 85000 - 25000;
|
||||
*val2 = 585;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
/* 2.5 V = 0 */
|
||||
*val = 2048;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
/* 85 C = 585, 25 C = 0 */
|
||||
*val = DIV_ROUND_CLOSEST(25 * 585, 85 - 25);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16080_channels[] = {
|
||||
@ -110,25 +136,32 @@ static const struct iio_chan_spec adis16080_channels[] = {
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_GYRO,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_AIN1,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_AIN2,
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_TEMP,
|
||||
}
|
||||
};
|
||||
@ -138,8 +171,27 @@ static const struct iio_info adis16080_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_ADIS16080,
|
||||
ID_ADIS16100,
|
||||
};
|
||||
|
||||
static const struct adis16080_chip_info adis16080_chip_info[] = {
|
||||
[ID_ADIS16080] = {
|
||||
/* 80 degree = 819, 819 rad = 46925 degree */
|
||||
.scale_val = 80,
|
||||
.scale_val2 = 46925,
|
||||
},
|
||||
[ID_ADIS16100] = {
|
||||
/* 300 degree = 1230, 1230 rad = 70474 degree */
|
||||
.scale_val = 300,
|
||||
.scale_val2 = 70474,
|
||||
},
|
||||
};
|
||||
|
||||
static int adis16080_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
int ret;
|
||||
struct adis16080_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
@ -156,7 +208,7 @@ static int adis16080_probe(struct spi_device *spi)
|
||||
|
||||
/* Allocate the comms buffers */
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
st->info = &adis16080_chip_info[id->driver_data];
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->channels = adis16080_channels;
|
||||
@ -176,7 +228,6 @@ error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fixme, confirm ordering in this function */
|
||||
static int adis16080_remove(struct spi_device *spi)
|
||||
{
|
||||
iio_device_unregister(spi_get_drvdata(spi));
|
||||
@ -185,6 +236,13 @@ static int adis16080_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16080_ids[] = {
|
||||
{ "adis16080", ID_ADIS16080 },
|
||||
{ "adis16100", ID_ADIS16100 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16080_ids);
|
||||
|
||||
static struct spi_driver adis16080_driver = {
|
||||
.driver = {
|
||||
.name = "adis16080",
|
||||
@ -192,10 +250,10 @@ static struct spi_driver adis16080_driver = {
|
||||
},
|
||||
.probe = adis16080_probe,
|
||||
.remove = adis16080_remove,
|
||||
.id_table = adis16080_ids,
|
||||
};
|
||||
module_spi_driver(adis16080_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:adis16080");
|
@ -21,45 +21,110 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "adxrs450.h"
|
||||
#define ADXRS450_STARTUP_DELAY 50 /* ms */
|
||||
|
||||
/* The MSB for the spi commands */
|
||||
#define ADXRS450_SENSOR_DATA (0x20 << 24)
|
||||
#define ADXRS450_WRITE_DATA (0x40 << 24)
|
||||
#define ADXRS450_READ_DATA (0x80 << 24)
|
||||
|
||||
#define ADXRS450_RATE1 0x00 /* Rate Registers */
|
||||
#define ADXRS450_TEMP1 0x02 /* Temperature Registers */
|
||||
#define ADXRS450_LOCST1 0x04 /* Low CST Memory Registers */
|
||||
#define ADXRS450_HICST1 0x06 /* High CST Memory Registers */
|
||||
#define ADXRS450_QUAD1 0x08 /* Quad Memory Registers */
|
||||
#define ADXRS450_FAULT1 0x0A /* Fault Registers */
|
||||
#define ADXRS450_PID1 0x0C /* Part ID Register 1 */
|
||||
#define ADXRS450_SNH 0x0E /* Serial Number Registers, 4 bytes */
|
||||
#define ADXRS450_SNL 0x10
|
||||
#define ADXRS450_DNC1 0x12 /* Dynamic Null Correction Registers */
|
||||
/* Check bits */
|
||||
#define ADXRS450_P 0x01
|
||||
#define ADXRS450_CHK 0x02
|
||||
#define ADXRS450_CST 0x04
|
||||
#define ADXRS450_PWR 0x08
|
||||
#define ADXRS450_POR 0x10
|
||||
#define ADXRS450_NVM 0x20
|
||||
#define ADXRS450_Q 0x40
|
||||
#define ADXRS450_PLL 0x80
|
||||
#define ADXRS450_UV 0x100
|
||||
#define ADXRS450_OV 0x200
|
||||
#define ADXRS450_AMP 0x400
|
||||
#define ADXRS450_FAIL 0x800
|
||||
|
||||
#define ADXRS450_WRERR_MASK (0x7 << 29)
|
||||
|
||||
#define ADXRS450_MAX_RX 4
|
||||
#define ADXRS450_MAX_TX 4
|
||||
|
||||
#define ADXRS450_GET_ST(a) ((a >> 26) & 0x3)
|
||||
|
||||
enum {
|
||||
ID_ADXRS450,
|
||||
ID_ADXRS453,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adxrs450_state - device instance specific data
|
||||
* @us: actual spi_device
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
* @tx: transmit buffer
|
||||
* @rx: receive buffer
|
||||
**/
|
||||
struct adxrs450_state {
|
||||
struct spi_device *us;
|
||||
struct mutex buf_lock;
|
||||
__be32 tx ____cacheline_aligned;
|
||||
__be32 rx;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* adxrs450_spi_read_reg_16() - read 2 bytes from a register pair
|
||||
* @dev: device associated with child of actual iio_dev
|
||||
* @reg_address: the address of the lower of the two registers,which should be an even address,
|
||||
* Second register's address is reg_address + 1.
|
||||
* @indio_dev: device associated with child of actual iio_dev
|
||||
* @reg_address: the address of the lower of the two registers, which should be
|
||||
* an even address, the second register's address is reg_address + 1.
|
||||
* @val: somewhere to pass back the value read
|
||||
**/
|
||||
static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
u32 tx;
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->tx),
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->rx),
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADXRS450_READ_DATA | (reg_address >> 7);
|
||||
st->tx[1] = reg_address << 1;
|
||||
st->tx[2] = 0;
|
||||
st->tx[3] = 0;
|
||||
tx = ADXRS450_READ_DATA | (reg_address << 17);
|
||||
|
||||
if (!(hweight32(be32_to_cpu(*(u32 *)st->tx)) & 1))
|
||||
st->tx[3] |= ADXRS450_P;
|
||||
if (!(hweight32(tx) & 1))
|
||||
tx |= ADXRS450_P;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 4);
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n",
|
||||
reg_address);
|
||||
goto error_ret;
|
||||
}
|
||||
ret = spi_read(st->us, st->rx, 4);
|
||||
st->tx = cpu_to_be32(tx);
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n",
|
||||
reg_address);
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
*val = (be32_to_cpu(*(u32 *)st->rx) >> 5) & 0xFFFF;
|
||||
*val = (be32_to_cpu(st->rx) >> 5) & 0xFFFF;
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
@ -68,9 +133,9 @@ error_ret:
|
||||
|
||||
/**
|
||||
* adxrs450_spi_write_reg_16() - write 2 bytes data to a register pair
|
||||
* @dev: device associated with child of actual actual iio_dev
|
||||
* @reg_address: the address of the lower of the two registers,which should be an even address,
|
||||
* Second register's address is reg_address + 1.
|
||||
* @indio_dev: device associated with child of actual actual iio_dev
|
||||
* @reg_address: the address of the lower of the two registers,which should be
|
||||
* an even address, the second register's address is reg_address + 1.
|
||||
* @val: value to be written.
|
||||
**/
|
||||
static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
@ -78,55 +143,61 @@ static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u16 val)
|
||||
{
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
u32 tx;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADXRS450_WRITE_DATA | reg_address >> 7;
|
||||
st->tx[1] = reg_address << 1 | val >> 15;
|
||||
st->tx[2] = val >> 7;
|
||||
st->tx[3] = val << 1;
|
||||
tx = ADXRS450_WRITE_DATA | (reg_address << 17) | (val << 1);
|
||||
|
||||
if (!(hweight32(be32_to_cpu(*(u32 *)st->tx)) & 1))
|
||||
st->tx[3] |= ADXRS450_P;
|
||||
if (!(hweight32(tx) & 1))
|
||||
tx |= ADXRS450_P;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 4);
|
||||
st->tx = cpu_to_be32(tx);
|
||||
ret = spi_write(st->us, &st->tx, sizeof(st->tx));
|
||||
if (ret)
|
||||
dev_err(&st->us->dev, "problem while writing 16 bit register 0x%02x\n",
|
||||
reg_address);
|
||||
msleep(1); /* enforce sequential transfer delay 0.1ms */
|
||||
usleep_range(100, 1000); /* enforce sequential transfer delay 0.1ms */
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adxrs450_spi_sensor_data() - read 2 bytes sensor data
|
||||
* @dev: device associated with child of actual iio_dev
|
||||
* @indio_dev: device associated with child of actual iio_dev
|
||||
* @val: somewhere to pass back the value read
|
||||
**/
|
||||
static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->tx),
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->rx),
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADXRS450_SENSOR_DATA;
|
||||
st->tx[1] = 0;
|
||||
st->tx[2] = 0;
|
||||
st->tx[3] = 0;
|
||||
st->tx = cpu_to_be32(ADXRS450_SENSOR_DATA);
|
||||
|
||||
ret = spi_write(st->us, st->tx, 4);
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "Problem while reading sensor data\n");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = spi_read(st->us, st->rx, 4);
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "Problem while reading sensor data\n");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
*val = (be32_to_cpu(*(u32 *)st->rx) >> 10) & 0xFFFF;
|
||||
*val = (be32_to_cpu(st->rx) >> 10) & 0xFFFF;
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
@ -137,35 +208,32 @@ error_ret:
|
||||
* adxrs450_spi_initial() - use for initializing procedure.
|
||||
* @st: device instance specific data
|
||||
* @val: somewhere to pass back the value read
|
||||
* @chk: Whether to perform fault check
|
||||
**/
|
||||
static int adxrs450_spi_initial(struct adxrs450_state *st,
|
||||
u32 *val, char chk)
|
||||
{
|
||||
struct spi_message msg;
|
||||
int ret;
|
||||
u32 tx;
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = st->tx,
|
||||
.rx_buf = st->rx,
|
||||
.tx_buf = &st->tx,
|
||||
.rx_buf = &st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 4,
|
||||
.len = sizeof(st->tx),
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADXRS450_SENSOR_DATA;
|
||||
st->tx[1] = 0;
|
||||
st->tx[2] = 0;
|
||||
st->tx[3] = 0;
|
||||
tx = ADXRS450_SENSOR_DATA;
|
||||
if (chk)
|
||||
st->tx[3] |= (ADXRS450_CHK | ADXRS450_P);
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers, &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
tx |= (ADXRS450_CHK | ADXRS450_P);
|
||||
st->tx = cpu_to_be32(tx);
|
||||
ret = spi_sync_transfer(st->us, &xfers, 1);
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "Problem while reading initializing data\n");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
*val = be32_to_cpu(*(u32 *)st->rx);
|
||||
*val = be32_to_cpu(st->rx);
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
@ -185,8 +253,7 @@ static int adxrs450_initial_setup(struct iio_dev *indio_dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
if (t != 0x01)
|
||||
dev_warn(&st->us->dev, "The initial power on response "
|
||||
"is not correct! Restart without reset?\n");
|
||||
dev_warn(&st->us->dev, "The initial power on response is not correct! Restart without reset?\n");
|
||||
|
||||
msleep(ADXRS450_STARTUP_DELAY);
|
||||
ret = adxrs450_spi_initial(st, &t, 0);
|
||||
@ -217,20 +284,6 @@ static int adxrs450_initial_setup(struct iio_dev *indio_dev)
|
||||
dev_err(&st->us->dev, "The device is not in normal status!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_PID1, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_info(&st->us->dev, "The Part ID is 0x%x\n", data);
|
||||
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_SNL, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
t = data;
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_SNH, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
t |= data << 16;
|
||||
dev_info(&st->us->dev, "The Serial Number is 0x%x\n", t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -244,9 +297,10 @@ static int adxrs450_write_raw(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -0x400 || val >= 0x400)
|
||||
return -EINVAL;
|
||||
ret = adxrs450_spi_write_reg_16(indio_dev,
|
||||
ADXRS450_DNC1,
|
||||
val & 0x3FF);
|
||||
ADXRS450_DNC1, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@ -312,7 +366,7 @@ static int adxrs450_read_raw(struct iio_dev *indio_dev,
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_DNC1, &t);
|
||||
if (ret)
|
||||
break;
|
||||
*val = t;
|
||||
*val = sign_extend32(t, 9);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
@ -28,7 +28,6 @@
|
||||
#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*/
|
||||
@ -44,7 +43,7 @@ enum gyro_3d_channel {
|
||||
|
||||
struct gyro_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_iio_common common_attributes;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
|
||||
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
|
||||
};
|
||||
|
156
drivers/iio/gyro/itg3200_buffer.c
Normal file
156
drivers/iio/gyro/itg3200_buffer.c
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* itg3200_buffer.c -- support InvenSense ITG3200
|
||||
* Digital 3-Axis Gyroscope driver
|
||||
*
|
||||
* Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de>
|
||||
* Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
|
||||
* Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/iio.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/gyro/itg3200.h>
|
||||
|
||||
|
||||
static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf)
|
||||
{
|
||||
u8 tx = 0x80 | ITG3200_REG_TEMP_OUT_H;
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = i2c->addr,
|
||||
.flags = i2c->flags,
|
||||
.len = 1,
|
||||
.buf = &tx,
|
||||
},
|
||||
{
|
||||
.addr = i2c->addr,
|
||||
.flags = i2c->flags | I2C_M_RD,
|
||||
.len = ITG3200_SCAN_ELEMENTS * sizeof(s16),
|
||||
.buf = (char *)&buf,
|
||||
},
|
||||
};
|
||||
|
||||
return i2c_transfer(i2c->adapter, msg, 2);
|
||||
}
|
||||
|
||||
static irqreturn_t itg3200_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
__be16 buf[ITG3200_SCAN_ELEMENTS + sizeof(s64)/sizeof(u16)];
|
||||
|
||||
int ret = itg3200_read_all_channels(st->i2c, buf);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
memcpy(buf + indio_dev->scan_bytes - sizeof(s64),
|
||||
&pf->timestamp, sizeof(pf->timestamp));
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)buf);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
error_ret:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int itg3200_buffer_configure(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
itg3200_trigger_handler, NULL);
|
||||
}
|
||||
|
||||
void itg3200_buffer_unconfigure(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
|
||||
|
||||
static int itg3200_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
int ret;
|
||||
u8 msc;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
if (state)
|
||||
msc |= ITG3200_IRQ_DATA_RDY_ENABLE;
|
||||
else
|
||||
msc &= ~ITG3200_IRQ_DATA_RDY_ENABLE;
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops itg3200_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &itg3200_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int itg3200_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!st->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = request_irq(st->i2c->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"itg3200_data_rdy",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
|
||||
st->trig->dev.parent = &st->i2c->dev;
|
||||
st->trig->ops = &itg3200_trigger_ops;
|
||||
st->trig->private_data = indio_dev;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->i2c->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void itg3200_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->i2c->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
401
drivers/iio/gyro/itg3200_core.c
Normal file
401
drivers/iio/gyro/itg3200_core.c
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* itg3200_core.c -- support InvenSense ITG3200
|
||||
* Digital 3-Axis Gyroscope driver
|
||||
*
|
||||
* Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de>
|
||||
* Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
|
||||
* Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* TODO:
|
||||
* - Support digital low pass filter
|
||||
* - Support power management
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/iio/gyro/itg3200.h>
|
||||
|
||||
|
||||
int itg3200_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address, u8 val)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(st->i2c, 0x80 | reg_address, val);
|
||||
}
|
||||
|
||||
int itg3200_read_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address, u8 *val)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->i2c, reg_address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int itg3200_read_reg_s16(struct iio_dev *indio_dev, u8 lower_reg_address,
|
||||
int *val)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
struct i2c_client *client = st->i2c;
|
||||
int ret;
|
||||
s16 out;
|
||||
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = client->flags,
|
||||
.len = 1,
|
||||
.buf = (char *)&lower_reg_address,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = client->flags | I2C_M_RD,
|
||||
.len = 2,
|
||||
.buf = (char *)&out,
|
||||
},
|
||||
};
|
||||
|
||||
lower_reg_address |= 0x80;
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
be16_to_cpus(&out);
|
||||
*val = out;
|
||||
|
||||
return (ret == 2) ? 0 : ret;
|
||||
}
|
||||
|
||||
static int itg3200_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 reg;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
reg = (u8)chan->address;
|
||||
ret = itg3200_read_reg_s16(indio_dev, reg, val);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
if (chan->type == IIO_TEMP)
|
||||
*val2 = 1000000000/280;
|
||||
else
|
||||
*val2 = 1214142; /* (1 / 14,375) * (PI / 180) */
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* Only the temperature channel has an offset */
|
||||
*val = 23000;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t itg3200_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
int ret, sps;
|
||||
u8 val;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sps = (val & ITG3200_DLPF_CFG_MASK) ? 1000 : 8000;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_SAMPLE_RATE_DIV, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sps /= val + 1;
|
||||
|
||||
return sprintf(buf, "%d\n", sps);
|
||||
}
|
||||
|
||||
static ssize_t itg3200_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);
|
||||
unsigned val;
|
||||
int ret;
|
||||
u8 t;
|
||||
|
||||
ret = kstrtouint(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &t);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
if (val == 0) {
|
||||
ret = -EINVAL;
|
||||
goto err_ret;
|
||||
}
|
||||
t = ((t & ITG3200_DLPF_CFG_MASK) ? 1000u : 8000u) / val - 1;
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_SAMPLE_RATE_DIV, t);
|
||||
|
||||
err_ret:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset device and internal registers to the power-up-default settings
|
||||
* Use the gyro clock as reference, as suggested by the datasheet
|
||||
*/
|
||||
static int itg3200_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(&st->i2c->dev, "reset device");
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev,
|
||||
ITG3200_REG_POWER_MANAGEMENT,
|
||||
ITG3200_RESET);
|
||||
if (ret) {
|
||||
dev_err(&st->i2c->dev, "error resetting device");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
/* Wait for PLL (1ms according to datasheet) */
|
||||
udelay(1500);
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev,
|
||||
ITG3200_REG_IRQ_CONFIG,
|
||||
ITG3200_IRQ_ACTIVE_HIGH |
|
||||
ITG3200_IRQ_PUSH_PULL |
|
||||
ITG3200_IRQ_LATCH_50US_PULSE |
|
||||
ITG3200_IRQ_LATCH_CLEAR_ANY);
|
||||
|
||||
if (ret)
|
||||
dev_err(&st->i2c->dev, "error init device");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* itg3200_enable_full_scale() - Disables the digital low pass filter */
|
||||
static int itg3200_enable_full_scale(struct iio_dev *indio_dev)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &val);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
val |= ITG3200_DLPF_FS_SEL_2000;
|
||||
return itg3200_write_reg_8(indio_dev, ITG3200_REG_DLPF, val);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int itg3200_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
if (((val >> 1) & 0x3f) != 0x34) {
|
||||
dev_err(&st->i2c->dev, "invalid reg value 0x%02x", val);
|
||||
ret = -ENXIO;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
ret = itg3200_reset(indio_dev);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
ret = itg3200_enable_full_scale(indio_dev);
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define ITG3200_TEMP_INFO_MASK (IIO_CHAN_INFO_OFFSET_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
#define ITG3200_GYRO_INFO_MASK (IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_RAW_SEPARATE_BIT)
|
||||
|
||||
#define ITG3200_ST \
|
||||
{ .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_BE }
|
||||
|
||||
#define ITG3200_GYRO_CHAN(_mod) { \
|
||||
.type = IIO_ANGL_VEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## _mod, \
|
||||
.info_mask = ITG3200_GYRO_INFO_MASK, \
|
||||
.address = ITG3200_REG_GYRO_ ## _mod ## OUT_H, \
|
||||
.scan_index = ITG3200_SCAN_GYRO_ ## _mod, \
|
||||
.scan_type = ITG3200_ST, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec itg3200_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.info_mask = ITG3200_TEMP_INFO_MASK,
|
||||
.address = ITG3200_REG_TEMP_OUT_H,
|
||||
.scan_index = ITG3200_SCAN_TEMP,
|
||||
.scan_type = ITG3200_ST,
|
||||
},
|
||||
ITG3200_GYRO_CHAN(X),
|
||||
ITG3200_GYRO_CHAN(Y),
|
||||
ITG3200_GYRO_CHAN(Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(ITG3200_SCAN_ELEMENTS),
|
||||
};
|
||||
|
||||
/* IIO device attributes */
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, itg3200_read_frequency,
|
||||
itg3200_write_frequency);
|
||||
|
||||
static struct attribute *itg3200_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group itg3200_attribute_group = {
|
||||
.attrs = itg3200_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info itg3200_info = {
|
||||
.attrs = &itg3200_attribute_group,
|
||||
.read_raw = &itg3200_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long itg3200_available_scan_masks[] = { 0xffffffff, 0x0 };
|
||||
|
||||
static int itg3200_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct itg3200 *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
dev_dbg(&client->dev, "probe I2C dev with IRQ %i", client->irq);
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
st->i2c = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->dev.driver->name;
|
||||
indio_dev->channels = itg3200_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(itg3200_channels);
|
||||
indio_dev->available_scan_masks = itg3200_available_scan_masks;
|
||||
indio_dev->info = &itg3200_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = itg3200_buffer_configure(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
if (client->irq) {
|
||||
ret = itg3200_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_unconfigure_buffer;
|
||||
}
|
||||
|
||||
ret = itg3200_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
if (client->irq)
|
||||
itg3200_remove_trigger(indio_dev);
|
||||
error_unconfigure_buffer:
|
||||
itg3200_buffer_unconfigure(indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int itg3200_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (client->irq)
|
||||
itg3200_remove_trigger(indio_dev);
|
||||
|
||||
itg3200_buffer_unconfigure(indio_dev);
|
||||
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id itg3200_id[] = {
|
||||
{ "itg3200", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, itg3200_id);
|
||||
|
||||
static struct i2c_driver itg3200_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "itg3200",
|
||||
},
|
||||
.id_table = itg3200_id,
|
||||
.probe = itg3200_probe,
|
||||
.remove = itg3200_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(itg3200_driver);
|
||||
|
||||
MODULE_AUTHOR("Christian Strobel <christian.strobel@iis.fraunhofer.de>");
|
||||
MODULE_DESCRIPTION("ITG3200 Gyroscope I2C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
45
drivers/iio/gyro/st_gyro.h
Normal file
45
drivers/iio/gyro/st_gyro.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* STMicroelectronics gyroscopes driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
* v. 1.0.0
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_GYRO_H
|
||||
#define ST_GYRO_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define L3G4200D_GYRO_DEV_NAME "l3g4200d"
|
||||
#define LSM330D_GYRO_DEV_NAME "lsm330d_gyro"
|
||||
#define LSM330DL_GYRO_DEV_NAME "lsm330dl_gyro"
|
||||
#define LSM330DLC_GYRO_DEV_NAME "lsm330dlc_gyro"
|
||||
#define L3GD20_GYRO_DEV_NAME "l3gd20"
|
||||
#define L3GD20H_GYRO_DEV_NAME "l3gd20h"
|
||||
#define L3G4IS_GYRO_DEV_NAME "l3g4is_ui"
|
||||
#define LSM330_GYRO_DEV_NAME "lsm330_gyro"
|
||||
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev);
|
||||
void st_gyro_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
int st_gyro_allocate_ring(struct iio_dev *indio_dev);
|
||||
void st_gyro_deallocate_ring(struct iio_dev *indio_dev);
|
||||
int st_gyro_trig_set_state(struct iio_trigger *trig, bool state);
|
||||
#define ST_GYRO_TRIGGER_SET_STATE (&st_gyro_trig_set_state)
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
static inline int st_gyro_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void st_gyro_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
#define ST_GYRO_TRIGGER_SET_STATE NULL
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#endif /* ST_GYRO_H */
|
114
drivers/iio/gyro/st_gyro_buffer.c
Normal file
114
drivers/iio/gyro/st_gyro_buffer.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* STMicroelectronics gyroscopes driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_gyro.h"
|
||||
|
||||
int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_gyro_set_enable_error;
|
||||
|
||||
err = iio_sw_buffer_preenable(indio_dev);
|
||||
|
||||
st_gyro_set_enable_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (gdata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_postenable_error;
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_postenable_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_gyro_buffer_postenable_error:
|
||||
kfree(gdata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
|
||||
st_gyro_buffer_predisable_error:
|
||||
kfree(gdata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = {
|
||||
.preenable = &st_gyro_buffer_preenable,
|
||||
.postenable = &st_gyro_buffer_postenable,
|
||||
.predisable = &st_gyro_buffer_predisable,
|
||||
};
|
||||
|
||||
int st_gyro_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&st_sensors_trigger_handler, &st_gyro_buffer_setup_ops);
|
||||
}
|
||||
|
||||
void st_gyro_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics gyroscopes buffer");
|
||||
MODULE_LICENSE("GPL v2");
|
368
drivers/iio/gyro/st_gyro_core.c
Normal file
368
drivers/iio/gyro/st_gyro_core.c
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* STMicroelectronics gyroscopes driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_gyro.h"
|
||||
|
||||
/* DEFAULT VALUE FOR SENSORS */
|
||||
#define ST_GYRO_DEFAULT_OUT_X_L_ADDR 0x28
|
||||
#define ST_GYRO_DEFAULT_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_GYRO_DEFAULT_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_GYRO_FS_AVL_250DPS 250
|
||||
#define ST_GYRO_FS_AVL_500DPS 500
|
||||
#define ST_GYRO_FS_AVL_2000DPS 2000
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||
#define ST_GYRO_1_WAI_EXP 0xd3
|
||||
#define ST_GYRO_1_ODR_ADDR 0x20
|
||||
#define ST_GYRO_1_ODR_MASK 0xc0
|
||||
#define ST_GYRO_1_ODR_AVL_100HZ_VAL 0x00
|
||||
#define ST_GYRO_1_ODR_AVL_200HZ_VAL 0x01
|
||||
#define ST_GYRO_1_ODR_AVL_400HZ_VAL 0x02
|
||||
#define ST_GYRO_1_ODR_AVL_800HZ_VAL 0x03
|
||||
#define ST_GYRO_1_PW_ADDR 0x20
|
||||
#define ST_GYRO_1_PW_MASK 0x08
|
||||
#define ST_GYRO_1_FS_ADDR 0x23
|
||||
#define ST_GYRO_1_FS_MASK 0x30
|
||||
#define ST_GYRO_1_FS_AVL_250_VAL 0x00
|
||||
#define ST_GYRO_1_FS_AVL_500_VAL 0x01
|
||||
#define ST_GYRO_1_FS_AVL_2000_VAL 0x02
|
||||
#define ST_GYRO_1_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
|
||||
#define ST_GYRO_1_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
|
||||
#define ST_GYRO_1_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
|
||||
#define ST_GYRO_1_BDU_ADDR 0x23
|
||||
#define ST_GYRO_1_BDU_MASK 0x80
|
||||
#define ST_GYRO_1_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_GYRO_1_DRDY_IRQ_MASK 0x08
|
||||
#define ST_GYRO_1_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 2 */
|
||||
#define ST_GYRO_2_WAI_EXP 0xd4
|
||||
#define ST_GYRO_2_ODR_ADDR 0x20
|
||||
#define ST_GYRO_2_ODR_MASK 0xc0
|
||||
#define ST_GYRO_2_ODR_AVL_95HZ_VAL 0x00
|
||||
#define ST_GYRO_2_ODR_AVL_190HZ_VAL 0x01
|
||||
#define ST_GYRO_2_ODR_AVL_380HZ_VAL 0x02
|
||||
#define ST_GYRO_2_ODR_AVL_760HZ_VAL 0x03
|
||||
#define ST_GYRO_2_PW_ADDR 0x20
|
||||
#define ST_GYRO_2_PW_MASK 0x08
|
||||
#define ST_GYRO_2_FS_ADDR 0x23
|
||||
#define ST_GYRO_2_FS_MASK 0x30
|
||||
#define ST_GYRO_2_FS_AVL_250_VAL 0x00
|
||||
#define ST_GYRO_2_FS_AVL_500_VAL 0x01
|
||||
#define ST_GYRO_2_FS_AVL_2000_VAL 0x02
|
||||
#define ST_GYRO_2_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
|
||||
#define ST_GYRO_2_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
|
||||
#define ST_GYRO_2_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
|
||||
#define ST_GYRO_2_BDU_ADDR 0x23
|
||||
#define ST_GYRO_2_BDU_MASK 0x80
|
||||
#define ST_GYRO_2_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_GYRO_2_DRDY_IRQ_MASK 0x08
|
||||
#define ST_GYRO_2_MULTIREAD_BIT true
|
||||
|
||||
static const struct iio_chan_spec st_gyro_16bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_X,
|
||||
IIO_MOD_X, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
|
||||
ST_GYRO_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Y,
|
||||
IIO_MOD_Y, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
|
||||
ST_GYRO_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Z,
|
||||
IIO_MOD_Z, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS,
|
||||
ST_GYRO_DEFAULT_OUT_Z_L_ADDR),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_gyro_sensors[] = {
|
||||
{
|
||||
.wai = ST_GYRO_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = L3G4200D_GYRO_DEV_NAME,
|
||||
[1] = LSM330DL_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_GYRO_1_ODR_ADDR,
|
||||
.mask = ST_GYRO_1_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, },
|
||||
{ 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, },
|
||||
{ 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, },
|
||||
{ 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_GYRO_1_PW_ADDR,
|
||||
.mask = ST_GYRO_1_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_GYRO_1_FS_ADDR,
|
||||
.mask = ST_GYRO_1_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_GYRO_FS_AVL_250DPS,
|
||||
.value = ST_GYRO_1_FS_AVL_250_VAL,
|
||||
.gain = ST_GYRO_1_FS_AVL_250_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_GYRO_FS_AVL_500DPS,
|
||||
.value = ST_GYRO_1_FS_AVL_500_VAL,
|
||||
.gain = ST_GYRO_1_FS_AVL_500_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_GYRO_FS_AVL_2000DPS,
|
||||
.value = ST_GYRO_1_FS_AVL_2000_VAL,
|
||||
.gain = ST_GYRO_1_FS_AVL_2000_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_GYRO_1_BDU_ADDR,
|
||||
.mask = ST_GYRO_1_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_GYRO_1_DRDY_IRQ_ADDR,
|
||||
.mask = ST_GYRO_1_DRDY_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_GYRO_2_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = L3GD20_GYRO_DEV_NAME,
|
||||
[1] = L3GD20H_GYRO_DEV_NAME,
|
||||
[2] = LSM330D_GYRO_DEV_NAME,
|
||||
[3] = LSM330DLC_GYRO_DEV_NAME,
|
||||
[4] = L3G4IS_GYRO_DEV_NAME,
|
||||
[5] = LSM330_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_GYRO_2_ODR_ADDR,
|
||||
.mask = ST_GYRO_2_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, },
|
||||
{ 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, },
|
||||
{ 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, },
|
||||
{ 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_GYRO_2_PW_ADDR,
|
||||
.mask = ST_GYRO_2_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_GYRO_2_FS_ADDR,
|
||||
.mask = ST_GYRO_2_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_GYRO_FS_AVL_250DPS,
|
||||
.value = ST_GYRO_2_FS_AVL_250_VAL,
|
||||
.gain = ST_GYRO_2_FS_AVL_250_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_GYRO_FS_AVL_500DPS,
|
||||
.value = ST_GYRO_2_FS_AVL_500_VAL,
|
||||
.gain = ST_GYRO_2_FS_AVL_500_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_GYRO_FS_AVL_2000DPS,
|
||||
.value = ST_GYRO_2_FS_AVL_2000_VAL,
|
||||
.gain = ST_GYRO_2_FS_AVL_2000_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_GYRO_2_BDU_ADDR,
|
||||
.mask = ST_GYRO_2_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_GYRO_2_DRDY_IRQ_ADDR,
|
||||
.mask = ST_GYRO_2_DRDY_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_gyro_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = st_sensors_read_info_raw(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = gdata->current_fullscale->gain;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
read_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_gyro_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
err = st_sensors_set_fullscale_by_gain(indio_dev, val2);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ST_SENSOR_DEV_ATTR_SAMP_FREQ();
|
||||
static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
|
||||
static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_anglvel_scale_available);
|
||||
|
||||
static struct attribute *st_gyro_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_gyro_attribute_group = {
|
||||
.attrs = st_gyro_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info gyro_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_gyro_attribute_group,
|
||||
.read_raw = &st_gyro_read_raw,
|
||||
.write_raw = &st_gyro_write_raw,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
static const struct iio_trigger_ops st_gyro_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = ST_GYRO_TRIGGER_SET_STATE,
|
||||
};
|
||||
#define ST_GYRO_TRIGGER_OPS (&st_gyro_trigger_ops)
|
||||
#else
|
||||
#define ST_GYRO_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &gyro_info;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
|
||||
gdata->multiread_bit = gdata->sensor->multi_read_bit;
|
||||
indio_dev->channels = gdata->sensor->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
gdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&gdata->sensor->fs.fs_avl[0];
|
||||
gdata->odr = gdata->sensor->odr.odr_avl[0].hz;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
err = st_gyro_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_GYRO_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
goto st_gyro_probe_trigger_error;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto st_gyro_device_register_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_gyro_device_register_error:
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_gyro_probe_trigger_error:
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
st_gyro_common_probe_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_gyro_common_probe);
|
||||
|
||||
void st_gyro_common_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
}
|
||||
iio_device_free(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_gyro_common_remove);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver");
|
||||
MODULE_LICENSE("GPL v2");
|
84
drivers/iio/gyro/st_gyro_i2c.c
Normal file
84
drivers/iio/gyro/st_gyro_i2c.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* STMicroelectronics gyroscopes driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_gyro.h"
|
||||
|
||||
static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *gdata;
|
||||
int err;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*gdata));
|
||||
if (indio_dev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto iio_device_alloc_error;
|
||||
}
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
gdata->dev = &client->dev;
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, gdata);
|
||||
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
|
||||
return 0;
|
||||
|
||||
st_gyro_common_probe_error:
|
||||
iio_device_free(indio_dev);
|
||||
iio_device_alloc_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_gyro_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
st_gyro_common_remove(i2c_get_clientdata(client));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_gyro_id_table[] = {
|
||||
{ L3G4200D_GYRO_DEV_NAME },
|
||||
{ LSM330D_GYRO_DEV_NAME },
|
||||
{ LSM330DL_GYRO_DEV_NAME },
|
||||
{ LSM330DLC_GYRO_DEV_NAME },
|
||||
{ L3GD20_GYRO_DEV_NAME },
|
||||
{ L3GD20H_GYRO_DEV_NAME },
|
||||
{ L3G4IS_GYRO_DEV_NAME },
|
||||
{ LSM330_GYRO_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
|
||||
|
||||
static struct i2c_driver st_gyro_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-gyro-i2c",
|
||||
},
|
||||
.probe = st_gyro_i2c_probe,
|
||||
.remove = st_gyro_i2c_remove,
|
||||
.id_table = st_gyro_id_table,
|
||||
};
|
||||
module_i2c_driver(st_gyro_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics gyroscopes i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
83
drivers/iio/gyro/st_gyro_spi.c
Normal file
83
drivers/iio/gyro/st_gyro_spi.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* STMicroelectronics gyroscopes driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_gyro.h"
|
||||
|
||||
static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *gdata;
|
||||
int err;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*gdata));
|
||||
if (indio_dev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto iio_device_alloc_error;
|
||||
}
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
gdata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, gdata);
|
||||
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
|
||||
return 0;
|
||||
|
||||
st_gyro_common_probe_error:
|
||||
iio_device_free(indio_dev);
|
||||
iio_device_alloc_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_gyro_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
st_gyro_common_remove(spi_get_drvdata(spi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id st_gyro_id_table[] = {
|
||||
{ L3G4200D_GYRO_DEV_NAME },
|
||||
{ LSM330D_GYRO_DEV_NAME },
|
||||
{ LSM330DL_GYRO_DEV_NAME },
|
||||
{ LSM330DLC_GYRO_DEV_NAME },
|
||||
{ L3GD20_GYRO_DEV_NAME },
|
||||
{ L3GD20H_GYRO_DEV_NAME },
|
||||
{ L3G4IS_GYRO_DEV_NAME },
|
||||
{ LSM330_GYRO_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
|
||||
|
||||
static struct spi_driver st_gyro_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-gyro-spi",
|
||||
},
|
||||
.probe = st_gyro_spi_probe,
|
||||
.remove = st_gyro_spi_remove,
|
||||
.id_table = st_gyro_id_table,
|
||||
};
|
||||
module_spi_driver(st_gyro_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics gyroscopes spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -3,6 +3,17 @@
|
||||
#
|
||||
menu "Inertial measurement units"
|
||||
|
||||
config ADIS16400
|
||||
tristate "Analog Devices ADIS16400 and similar IMU SPI driver"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16300, adis16344,
|
||||
adis16350, adis16354, adis16355, adis16360, adis16362, adis16364,
|
||||
adis16365, adis16400 and adis16405 triaxial inertial sensors
|
||||
(adis16400 series also have magnetometers).
|
||||
|
||||
config ADIS16480
|
||||
tristate "Analog Devices ADIS16480 and similar IMU driver"
|
||||
depends on SPI
|
||||
@ -25,3 +36,5 @@ config IIO_ADIS_LIB_BUFFER
|
||||
help
|
||||
A set of buffer helper functions for the Analog Devices ADIS* device
|
||||
family.
|
||||
|
||||
source "drivers/iio/imu/inv_mpu6050/Kconfig"
|
||||
|
@ -2,9 +2,14 @@
|
||||
# Makefile for Inertial Measurement Units
|
||||
#
|
||||
|
||||
adis16400-y := adis16400_core.o
|
||||
adis16400-$(CONFIG_IIO_BUFFER) += adis16400_buffer.o
|
||||
obj-$(CONFIG_ADIS16400) += adis16400.o
|
||||
obj-$(CONFIG_ADIS16480) += adis16480.o
|
||||
|
||||
adis_lib-y += adis.o
|
||||
adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
|
||||
adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
|
||||
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
|
||||
|
||||
obj-y += inv_mpu6050/
|
||||
|
@ -17,12 +17,11 @@
|
||||
#ifndef SPI_ADIS16400_H_
|
||||
#define SPI_ADIS16400_H_
|
||||
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#define ADIS16400_STARTUP_DELAY 290 /* ms */
|
||||
#define ADIS16400_MTEST_DELAY 90 /* ms */
|
||||
|
||||
#define ADIS16400_READ_REG(a) a
|
||||
#define ADIS16400_WRITE_REG(a) ((a) | 0x80)
|
||||
|
||||
#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */
|
||||
#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */
|
||||
#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */
|
||||
@ -45,6 +44,9 @@
|
||||
#define ADIS16300_ROLL_OUT 0x14 /* Y axis inclinometer output measurement */
|
||||
#define ADIS16300_AUX_ADC 0x16 /* Auxiliary ADC measurement */
|
||||
|
||||
#define ADIS16448_BARO_OUT 0x16 /* Barometric pressure output */
|
||||
#define ADIS16448_TEMP_OUT 0x18 /* Temperature output */
|
||||
|
||||
/* Calibration parameters */
|
||||
#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */
|
||||
#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */
|
||||
@ -75,7 +77,10 @@
|
||||
#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */
|
||||
#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */
|
||||
|
||||
#define ADIS16334_LOT_ID1 0x52 /* Lot identification code 1 */
|
||||
#define ADIS16334_LOT_ID2 0x54 /* Lot identification code 2 */
|
||||
#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */
|
||||
#define ADIS16334_SERIAL_NUMBER 0x58 /* Serial number, lot specific */
|
||||
|
||||
#define ADIS16400_ERROR_ACTIVE (1<<14)
|
||||
#define ADIS16400_NEW_DATA (1<<14)
|
||||
@ -96,21 +101,21 @@
|
||||
#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F
|
||||
|
||||
/* DIAG_STAT */
|
||||
#define ADIS16400_DIAG_STAT_ZACCL_FAIL (1<<15)
|
||||
#define ADIS16400_DIAG_STAT_YACCL_FAIL (1<<14)
|
||||
#define ADIS16400_DIAG_STAT_XACCL_FAIL (1<<13)
|
||||
#define ADIS16400_DIAG_STAT_XGYRO_FAIL (1<<12)
|
||||
#define ADIS16400_DIAG_STAT_YGYRO_FAIL (1<<11)
|
||||
#define ADIS16400_DIAG_STAT_ZGYRO_FAIL (1<<10)
|
||||
#define ADIS16400_DIAG_STAT_ALARM2 (1<<9)
|
||||
#define ADIS16400_DIAG_STAT_ALARM1 (1<<8)
|
||||
#define ADIS16400_DIAG_STAT_FLASH_CHK (1<<6)
|
||||
#define ADIS16400_DIAG_STAT_SELF_TEST (1<<5)
|
||||
#define ADIS16400_DIAG_STAT_OVERFLOW (1<<4)
|
||||
#define ADIS16400_DIAG_STAT_SPI_FAIL (1<<3)
|
||||
#define ADIS16400_DIAG_STAT_FLASH_UPT (1<<2)
|
||||
#define ADIS16400_DIAG_STAT_POWER_HIGH (1<<1)
|
||||
#define ADIS16400_DIAG_STAT_POWER_LOW (1<<0)
|
||||
#define ADIS16400_DIAG_STAT_ZACCL_FAIL 15
|
||||
#define ADIS16400_DIAG_STAT_YACCL_FAIL 14
|
||||
#define ADIS16400_DIAG_STAT_XACCL_FAIL 13
|
||||
#define ADIS16400_DIAG_STAT_XGYRO_FAIL 12
|
||||
#define ADIS16400_DIAG_STAT_YGYRO_FAIL 11
|
||||
#define ADIS16400_DIAG_STAT_ZGYRO_FAIL 10
|
||||
#define ADIS16400_DIAG_STAT_ALARM2 9
|
||||
#define ADIS16400_DIAG_STAT_ALARM1 8
|
||||
#define ADIS16400_DIAG_STAT_FLASH_CHK 6
|
||||
#define ADIS16400_DIAG_STAT_SELF_TEST 5
|
||||
#define ADIS16400_DIAG_STAT_OVERFLOW 4
|
||||
#define ADIS16400_DIAG_STAT_SPI_FAIL 3
|
||||
#define ADIS16400_DIAG_STAT_FLASH_UPT 2
|
||||
#define ADIS16400_DIAG_STAT_POWER_HIGH 1
|
||||
#define ADIS16400_DIAG_STAT_POWER_LOW 0
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16400_GLOB_CMD_SW_RESET (1<<7)
|
||||
@ -126,9 +131,6 @@
|
||||
#define ADIS16334_RATE_DIV_SHIFT 8
|
||||
#define ADIS16334_RATE_INT_CLK BIT(0)
|
||||
|
||||
#define ADIS16400_MAX_TX 24
|
||||
#define ADIS16400_MAX_RX 24
|
||||
|
||||
#define ADIS16400_SPI_SLOW (u32)(300 * 1000)
|
||||
#define ADIS16400_SPI_BURST (u32)(1000 * 1000)
|
||||
#define ADIS16400_SPI_FAST (u32)(2000 * 1000)
|
||||
@ -136,6 +138,9 @@
|
||||
#define ADIS16400_HAS_PROD_ID BIT(0)
|
||||
#define ADIS16400_NO_BURST BIT(1)
|
||||
#define ADIS16400_HAS_SLOW_MODE BIT(2)
|
||||
#define ADIS16400_HAS_SERIAL_NUMBER BIT(3)
|
||||
|
||||
struct adis16400_state;
|
||||
|
||||
struct adis16400_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
@ -145,95 +150,63 @@ struct adis16400_chip_info {
|
||||
unsigned int accel_scale_micro;
|
||||
int temp_scale_nano;
|
||||
int temp_offset;
|
||||
unsigned long default_scan_mask;
|
||||
int (*set_freq)(struct iio_dev *indio_dev, unsigned int freq);
|
||||
int (*get_freq)(struct iio_dev *indio_dev);
|
||||
int (*set_freq)(struct adis16400_state *st, unsigned int freq);
|
||||
int (*get_freq)(struct adis16400_state *st);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adis16400_state - device instance specific data
|
||||
* @us: actual spi_device
|
||||
* @trig: data ready trigger registered with iio
|
||||
* @tx: transmit buffer
|
||||
* @rx: receive buffer
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
* @filt_int: integer part of requested filter frequency
|
||||
* @variant: chip variant info
|
||||
* @filt_int: integer part of requested filter frequency
|
||||
* @adis: adis device
|
||||
**/
|
||||
struct adis16400_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
struct adis16400_chip_info *variant;
|
||||
int filt_int;
|
||||
|
||||
u8 tx[ADIS16400_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16400_MAX_RX] ____cacheline_aligned;
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
int adis16400_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
/* At the moment triggers are only used for ring buffer
|
||||
* filling. This may change!
|
||||
*/
|
||||
|
||||
#define ADIS16400_SCAN_SUPPLY 0
|
||||
#define ADIS16400_SCAN_GYRO_X 1
|
||||
#define ADIS16400_SCAN_GYRO_Y 2
|
||||
#define ADIS16400_SCAN_GYRO_Z 3
|
||||
#define ADIS16400_SCAN_ACC_X 4
|
||||
#define ADIS16400_SCAN_ACC_Y 5
|
||||
#define ADIS16400_SCAN_ACC_Z 6
|
||||
#define ADIS16400_SCAN_MAGN_X 7
|
||||
#define ADIS16350_SCAN_TEMP_X 7
|
||||
#define ADIS16400_SCAN_MAGN_Y 8
|
||||
#define ADIS16350_SCAN_TEMP_Y 8
|
||||
#define ADIS16400_SCAN_MAGN_Z 9
|
||||
#define ADIS16350_SCAN_TEMP_Z 9
|
||||
#define ADIS16400_SCAN_TEMP 10
|
||||
#define ADIS16350_SCAN_ADC_0 10
|
||||
#define ADIS16400_SCAN_ADC_0 11
|
||||
#define ADIS16300_SCAN_INCLI_X 12
|
||||
#define ADIS16300_SCAN_INCLI_Y 13
|
||||
enum {
|
||||
ADIS16400_SCAN_SUPPLY,
|
||||
ADIS16400_SCAN_GYRO_X,
|
||||
ADIS16400_SCAN_GYRO_Y,
|
||||
ADIS16400_SCAN_GYRO_Z,
|
||||
ADIS16400_SCAN_ACC_X,
|
||||
ADIS16400_SCAN_ACC_Y,
|
||||
ADIS16400_SCAN_ACC_Z,
|
||||
ADIS16400_SCAN_MAGN_X,
|
||||
ADIS16400_SCAN_MAGN_Y,
|
||||
ADIS16400_SCAN_MAGN_Z,
|
||||
ADIS16400_SCAN_BARO,
|
||||
ADIS16350_SCAN_TEMP_X,
|
||||
ADIS16350_SCAN_TEMP_Y,
|
||||
ADIS16350_SCAN_TEMP_Z,
|
||||
ADIS16300_SCAN_INCLI_X,
|
||||
ADIS16300_SCAN_INCLI_Y,
|
||||
ADIS16400_SCAN_ADC,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
void adis16400_remove_trigger(struct iio_dev *indio_dev);
|
||||
int adis16400_probe_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
ssize_t adis16400_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
|
||||
int adis16400_configure_ring(struct iio_dev *indio_dev);
|
||||
void adis16400_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
int adis16400_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask);
|
||||
irqreturn_t adis16400_trigger_handler(int irq, void *p);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void adis16400_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16400_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
adis16400_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16400_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16400_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
#define adis16400_update_scan_mode NULL
|
||||
#define adis16400_trigger_handler NULL
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#endif /* SPI_ADIS16400_H_ */
|
96
drivers/iio/imu/adis16400_buffer.c
Normal file
96
drivers/iio/imu/adis16400_buffer.c
Normal file
@ -0,0 +1,96 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#include "adis16400.h"
|
||||
|
||||
int adis16400_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
struct adis *adis = &st->adis;
|
||||
uint16_t *tx, *rx;
|
||||
|
||||
if (st->variant->flags & ADIS16400_NO_BURST)
|
||||
return adis_update_scan_mode(indio_dev, scan_mask);
|
||||
|
||||
kfree(adis->xfer);
|
||||
kfree(adis->buffer);
|
||||
|
||||
adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
|
||||
if (!adis->xfer)
|
||||
return -ENOMEM;
|
||||
|
||||
adis->buffer = kzalloc(indio_dev->scan_bytes + sizeof(u16),
|
||||
GFP_KERNEL);
|
||||
if (!adis->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
rx = adis->buffer;
|
||||
tx = adis->buffer + indio_dev->scan_bytes;
|
||||
|
||||
tx[0] = ADIS_READ_REG(ADIS16400_GLOB_CMD);
|
||||
tx[1] = 0;
|
||||
|
||||
adis->xfer[0].tx_buf = tx;
|
||||
adis->xfer[0].bits_per_word = 8;
|
||||
adis->xfer[0].len = 2;
|
||||
adis->xfer[1].tx_buf = tx;
|
||||
adis->xfer[1].bits_per_word = 8;
|
||||
adis->xfer[1].len = indio_dev->scan_bytes;
|
||||
|
||||
spi_message_init(&adis->msg);
|
||||
spi_message_add_tail(&adis->xfer[0], &adis->msg);
|
||||
spi_message_add_tail(&adis->xfer[1], &adis->msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
irqreturn_t adis16400_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
struct adis *adis = &st->adis;
|
||||
u32 old_speed_hz = st->adis.spi->max_speed_hz;
|
||||
int ret;
|
||||
|
||||
if (!adis->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(st->variant->flags & ADIS16400_NO_BURST) &&
|
||||
st->adis.spi->max_speed_hz > ADIS16400_SPI_BURST) {
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_BURST;
|
||||
spi_setup(st->adis.spi);
|
||||
}
|
||||
|
||||
ret = spi_sync(adis->spi, &adis->msg);
|
||||
if (ret)
|
||||
dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret);
|
||||
|
||||
if (!(st->variant->flags & ADIS16400_NO_BURST)) {
|
||||
st->adis.spi->max_speed_hz = old_speed_hz;
|
||||
spi_setup(st->adis.spi);
|
||||
}
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp) {
|
||||
void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
|
||||
*(s64 *)b = pf->timestamp;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, adis->buffer);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
965
drivers/iio/imu/adis16400_core.c
Normal file
965
drivers/iio/imu/adis16400_core.c
Normal file
@ -0,0 +1,965 @@
|
||||
/*
|
||||
* adis16400.c support Analog Devices ADIS16400/5
|
||||
* 3d 2g Linear Accelerometers,
|
||||
* 3d Gyroscopes,
|
||||
* 3d Magnetometers via SPI
|
||||
*
|
||||
* Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
|
||||
* Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
|
||||
* Copyright (c) 2011 Analog Devices Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "adis16400.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static ssize_t adis16400_show_serial_number(struct file *file,
|
||||
char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct adis16400_state *st = file->private_data;
|
||||
u16 lot1, lot2, serial_number;
|
||||
char buf[16];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID1, &lot1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID2, &lot2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_SERIAL_NUMBER,
|
||||
&serial_number);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.4x-%.4x-%.4x\n", lot1, lot2,
|
||||
serial_number);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static const struct file_operations adis16400_serial_number_fops = {
|
||||
.open = simple_open,
|
||||
.read = adis16400_show_serial_number,
|
||||
.llseek = default_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adis16400_show_product_id(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16400_state *st = arg;
|
||||
uint16_t prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_PRODUCT_ID, &prod_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16400_product_id_fops,
|
||||
adis16400_show_product_id, NULL, "%lld\n");
|
||||
|
||||
static int adis16400_show_flash_count(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16400_state *st = arg;
|
||||
uint16_t flash_count;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_FLASH_CNT, &flash_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16400_flash_count_fops,
|
||||
adis16400_show_flash_count, NULL, "%lld\n");
|
||||
|
||||
static int adis16400_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER)
|
||||
debugfs_create_file("serial_number", 0400,
|
||||
indio_dev->debugfs_dentry, st,
|
||||
&adis16400_serial_number_fops);
|
||||
if (st->variant->flags & ADIS16400_HAS_PROD_ID)
|
||||
debugfs_create_file("product_id", 0400,
|
||||
indio_dev->debugfs_dentry, st,
|
||||
&adis16400_product_id_fops);
|
||||
debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
|
||||
st, &adis16400_flash_count_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int adis16400_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
enum adis16400_chip_variant {
|
||||
ADIS16300,
|
||||
ADIS16334,
|
||||
ADIS16350,
|
||||
ADIS16360,
|
||||
ADIS16362,
|
||||
ADIS16364,
|
||||
ADIS16400,
|
||||
ADIS16448,
|
||||
};
|
||||
|
||||
static int adis16334_get_freq(struct adis16400_state *st)
|
||||
{
|
||||
int ret;
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
t >>= ADIS16334_RATE_DIV_SHIFT;
|
||||
|
||||
return 819200 >> t;
|
||||
}
|
||||
|
||||
static int adis16334_set_freq(struct adis16400_state *st, unsigned int freq)
|
||||
{
|
||||
unsigned int t;
|
||||
|
||||
if (freq < 819200)
|
||||
t = ilog2(819200 / freq);
|
||||
else
|
||||
t = 0;
|
||||
|
||||
if (t > 0x31)
|
||||
t = 0x31;
|
||||
|
||||
t <<= ADIS16334_RATE_DIV_SHIFT;
|
||||
t |= ADIS16334_RATE_INT_CLK;
|
||||
|
||||
return adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t);
|
||||
}
|
||||
|
||||
static int adis16400_get_freq(struct adis16400_state *st)
|
||||
{
|
||||
int sps, ret;
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 52851 : 1638404;
|
||||
sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1;
|
||||
|
||||
return sps;
|
||||
}
|
||||
|
||||
static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq)
|
||||
{
|
||||
unsigned int t;
|
||||
uint8_t val = 0;
|
||||
|
||||
t = 1638404 / freq;
|
||||
if (t >= 128) {
|
||||
val |= ADIS16400_SMPL_PRD_TIME_BASE;
|
||||
t = 52851 / freq;
|
||||
if (t >= 128)
|
||||
t = 127;
|
||||
} else if (t != 0) {
|
||||
t--;
|
||||
}
|
||||
|
||||
val |= t;
|
||||
|
||||
if (t >= 0x0A || (val & ADIS16400_SMPL_PRD_TIME_BASE))
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
|
||||
else
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
||||
|
||||
return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val);
|
||||
}
|
||||
|
||||
static ssize_t adis16400_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = st->variant->get_freq(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d.%.3d\n", ret / 1000, ret % 1000);
|
||||
}
|
||||
|
||||
static const unsigned adis16400_3db_divisors[] = {
|
||||
[0] = 2, /* Special case */
|
||||
[1] = 6,
|
||||
[2] = 12,
|
||||
[3] = 25,
|
||||
[4] = 50,
|
||||
[5] = 100,
|
||||
[6] = 200,
|
||||
[7] = 200, /* Not a valid setting */
|
||||
};
|
||||
|
||||
static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
uint16_t val16;
|
||||
int i, ret;
|
||||
|
||||
for (i = ARRAY_SIZE(adis16400_3db_divisors) - 1; i >= 1; i--) {
|
||||
if (sps / adis16400_3db_divisors[i] >= val)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG,
|
||||
(val16 & ~0x07) | i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t adis16400_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 adis16400_state *st = iio_priv(indio_dev);
|
||||
int i, f, val;
|
||||
int ret;
|
||||
|
||||
ret = iio_str_to_fixpoint(buf, 100, &i, &f);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = i * 1000 + f;
|
||||
|
||||
if (val <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->variant->set_freq(st, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
/* Power down the device */
|
||||
static int adis16400_stop_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16400_SLP_CNT,
|
||||
ADIS16400_SLP_CNT_POWER_OFF);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev,
|
||||
"problem with turning device off: SLP_CNT");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16400_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
uint16_t prod_id, smp_prd;
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
/* use low spi speed for init if the device has a slow mode */
|
||||
if (st->variant->flags & ADIS16400_HAS_SLOW_MODE)
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
|
||||
else
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
||||
st->adis.spi->mode = SPI_MODE_3;
|
||||
spi_setup(st->adis.spi);
|
||||
|
||||
ret = adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (st->variant->flags & ADIS16400_HAS_PROD_ID) {
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
ADIS16400_PRODUCT_ID, &prod_id);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
dev_info(&indio_dev->dev, "%s: prod_id 0x%04x at CS%d (irq %d)\n",
|
||||
indio_dev->name, prod_id,
|
||||
st->adis.spi->chip_select, st->adis.spi->irq);
|
||||
}
|
||||
/* use high spi speed if possible */
|
||||
if (st->variant->flags & ADIS16400_HAS_SLOW_MODE) {
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &smp_prd);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
if ((smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) {
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
||||
spi_setup(st->adis.spi);
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
adis16400_read_frequency,
|
||||
adis16400_write_frequency);
|
||||
|
||||
static const uint8_t adis16400_addresses[] = {
|
||||
[ADIS16400_SCAN_GYRO_X] = ADIS16400_XGYRO_OFF,
|
||||
[ADIS16400_SCAN_GYRO_Y] = ADIS16400_YGYRO_OFF,
|
||||
[ADIS16400_SCAN_GYRO_Z] = ADIS16400_ZGYRO_OFF,
|
||||
[ADIS16400_SCAN_ACC_X] = ADIS16400_XACCL_OFF,
|
||||
[ADIS16400_SCAN_ACC_Y] = ADIS16400_YACCL_OFF,
|
||||
[ADIS16400_SCAN_ACC_Z] = ADIS16400_ZACCL_OFF,
|
||||
};
|
||||
|
||||
static int adis16400_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int ret, sps;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis_write_reg_16(&st->adis,
|
||||
adis16400_addresses[chan->scan_index], val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
/*
|
||||
* Need to cache values so we can update if the frequency
|
||||
* changes.
|
||||
*/
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->filt_int = val;
|
||||
/* Work out update to current value */
|
||||
sps = st->variant->get_freq(st);
|
||||
if (sps < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return sps;
|
||||
}
|
||||
|
||||
ret = adis16400_set_filter(indio_dev, sps,
|
||||
val * 1000 + val2 / 1000);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int adis16400_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int16_t val16;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan, 0, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = 0;
|
||||
*val2 = st->variant->gyro_scale_micro;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_VOLTAGE:
|
||||
*val = 0;
|
||||
if (chan->channel == 0) {
|
||||
*val = 2;
|
||||
*val2 = 418000; /* 2.418 mV */
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = 805800; /* 805.8 uV */
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
*val2 = st->variant->accel_scale_micro;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_MAGN:
|
||||
*val = 0;
|
||||
*val2 = 500; /* 0.5 mgauss */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = st->variant->temp_scale_nano / 1000000;
|
||||
*val2 = (st->variant->temp_scale_nano % 1000000);
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
adis16400_addresses[chan->scan_index], &val16);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret)
|
||||
return ret;
|
||||
val16 = ((val16 & 0xFFF) << 4) >> 4;
|
||||
*val = val16;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* currently only temperature */
|
||||
*val = st->variant->temp_offset;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
/* Need both the number of taps and the sampling frequency */
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
ADIS16400_SENS_AVG,
|
||||
&val16);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
ret = st->variant->get_freq(st);
|
||||
if (ret >= 0) {
|
||||
ret /= adis16400_3db_divisors[val16 & 0x07];
|
||||
*val = ret / 1000;
|
||||
*val2 = (ret % 1000) * 1000;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.extend_name = name, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = (si), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_SUPPLY_CHAN(addr, bits) \
|
||||
ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY)
|
||||
|
||||
#define ADIS16400_AUX_ADC_CHAN(addr, bits) \
|
||||
ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC)
|
||||
|
||||
#define ADIS16400_GYRO_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_ANGL_VEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = addr, \
|
||||
.scan_index = ADIS16400_SCAN_GYRO_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_ACCEL_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16400_SCAN_ACC_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_MAGN_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16400_SCAN_MAGN_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_MOD_TEMP_NAME_X "x"
|
||||
#define ADIS16400_MOD_TEMP_NAME_Y "y"
|
||||
#define ADIS16400_MOD_TEMP_NAME_Z "z"
|
||||
|
||||
#define ADIS16400_MOD_TEMP_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.extend_name = ADIS16400_MOD_TEMP_NAME_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16350_SCAN_TEMP_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_TEMP_CHAN(addr, bits) { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16350_SCAN_TEMP_X, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_INCLI_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_INCLI, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16300_SCAN_INCLI_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16400_channels[] = {
|
||||
ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
|
||||
ADIS16400_TEMP_CHAN(ADIS16400_TEMP_OUT, 12),
|
||||
ADIS16400_AUX_ADC_CHAN(ADIS16400_AUX_ADC, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(12)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16448_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_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 16),
|
||||
ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 16),
|
||||
ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 16),
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.address = ADIS16448_BARO_OUT,
|
||||
.scan_index = ADIS16400_SCAN_BARO,
|
||||
.scan_type = IIO_ST('s', 16, 16, 0),
|
||||
},
|
||||
ADIS16400_TEMP_CHAN(ADIS16448_TEMP_OUT, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(11)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16350_channels[] = {
|
||||
ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
|
||||
ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
|
||||
ADIS16400_MOD_TEMP_CHAN(X, ADIS16350_XTEMP_OUT, 12),
|
||||
ADIS16400_MOD_TEMP_CHAN(Y, ADIS16350_YTEMP_OUT, 12),
|
||||
ADIS16400_MOD_TEMP_CHAN(Z, ADIS16350_ZTEMP_OUT, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(11)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16300_channels[] = {
|
||||
ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
|
||||
ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
|
||||
ADIS16400_INCLI_CHAN(X, ADIS16300_PITCH_OUT, 13),
|
||||
ADIS16400_INCLI_CHAN(Y, ADIS16300_ROLL_OUT, 13),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(14)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16334_channels[] = {
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8)
|
||||
};
|
||||
|
||||
static struct attribute *adis16400_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group adis16400_attribute_group = {
|
||||
.attrs = adis16400_attributes,
|
||||
};
|
||||
|
||||
static struct adis16400_chip_info adis16400_chips[] = {
|
||||
[ADIS16300] = {
|
||||
.channels = adis16300_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16300_channels),
|
||||
.flags = ADIS16400_HAS_SLOW_MODE,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = 5884,
|
||||
.temp_scale_nano = 140000000, /* 0.14 C */
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16334] = {
|
||||
.channels = adis16334_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16334_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_NO_BURST |
|
||||
ADIS16400_HAS_SERIAL_NUMBER,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */
|
||||
.temp_scale_nano = 67850000, /* 0.06785 C */
|
||||
.temp_offset = 25000000 / 67850, /* 25 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
},
|
||||
[ADIS16350] = {
|
||||
.channels = adis16350_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16350_channels),
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(73260), /* 0.07326 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(2522), /* 0.002522 g */
|
||||
.temp_scale_nano = 145300000, /* 0.1453 C */
|
||||
.temp_offset = 25000000 / 145300, /* 25 C = 0x00 */
|
||||
.flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16360] = {
|
||||
.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(50000), /* 0.05 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,
|
||||
},
|
||||
[ADIS16362] = {
|
||||
.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(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(333), /* 0.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,
|
||||
},
|
||||
[ADIS16364] = {
|
||||
.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(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 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] = {
|
||||
.channels = adis16400_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16400_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
|
||||
.temp_scale_nano = 140000000, /* 0.14 C */
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16448] = {
|
||||
.channels = adis16448_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16448_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID |
|
||||
ADIS16400_HAS_SERIAL_NUMBER,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 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,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info adis16400_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &adis16400_read_raw,
|
||||
.write_raw = &adis16400_write_raw,
|
||||
.attrs = &adis16400_attribute_group,
|
||||
.update_scan_mode = adis16400_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static const unsigned long adis16400_burst_scan_mask[] = {
|
||||
~0UL,
|
||||
0,
|
||||
};
|
||||
|
||||
static const char * const adis16400_status_error_msgs[] = {
|
||||
[ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active",
|
||||
[ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active",
|
||||
[ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error",
|
||||
[ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error",
|
||||
[ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange",
|
||||
[ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure",
|
||||
[ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed",
|
||||
[ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V",
|
||||
[ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16400_data = {
|
||||
.msc_ctrl_reg = ADIS16400_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16400_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16400_DIAG_STAT,
|
||||
|
||||
.read_delay = 50,
|
||||
.write_delay = 50,
|
||||
|
||||
.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
|
||||
.startup_delay = ADIS16400_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16400_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM2) |
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM1) |
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_CHK) |
|
||||
BIT(ADIS16400_DIAG_STAT_SELF_TEST) |
|
||||
BIT(ADIS16400_DIAG_STAT_OVERFLOW) |
|
||||
BIT(ADIS16400_DIAG_STAT_SPI_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_UPT) |
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_HIGH) |
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_LOW),
|
||||
};
|
||||
|
||||
static int adis16400_probe(struct spi_device *spi)
|
||||
{
|
||||
struct adis16400_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = st->variant->channels;
|
||||
indio_dev->num_channels = st->variant->num_channels;
|
||||
indio_dev->info = &adis16400_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (!(st->variant->flags & ADIS16400_NO_BURST))
|
||||
indio_dev->available_scan_masks = adis16400_burst_scan_mask;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev,
|
||||
adis16400_trigger_handler);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16400_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
adis16400_debugfs_init(indio_dev);
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16400_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16400_stop_device(indio_dev);
|
||||
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16400_id[] = {
|
||||
{"adis16300", ADIS16300},
|
||||
{"adis16334", ADIS16334},
|
||||
{"adis16350", ADIS16350},
|
||||
{"adis16354", ADIS16350},
|
||||
{"adis16355", ADIS16350},
|
||||
{"adis16360", ADIS16360},
|
||||
{"adis16362", ADIS16362},
|
||||
{"adis16364", ADIS16364},
|
||||
{"adis16365", ADIS16360},
|
||||
{"adis16400", ADIS16400},
|
||||
{"adis16405", ADIS16400},
|
||||
{"adis16448", ADIS16448},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16400_id);
|
||||
|
||||
static struct spi_driver adis16400_driver = {
|
||||
.driver = {
|
||||
.name = "adis16400",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = adis16400_id,
|
||||
.probe = adis16400_probe,
|
||||
.remove = adis16400_remove,
|
||||
};
|
||||
module_spi_driver(adis16400_driver);
|
||||
|
||||
MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
13
drivers/iio/imu/inv_mpu6050/Kconfig
Normal file
13
drivers/iio/imu/inv_mpu6050/Kconfig
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# inv-mpu6050 drivers for Invensense MPU devices and combos
|
||||
#
|
||||
|
||||
config INV_MPU6050_IIO
|
||||
tristate "Invensense MPU6050 devices"
|
||||
depends on I2C && SYSFS
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
This driver supports the Invensense MPU6050 devices.
|
||||
It is a gyroscope/accelerometer combo device.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050.
|
6
drivers/iio/imu/inv_mpu6050/Makefile
Normal file
6
drivers/iio/imu/inv_mpu6050/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for Invensense MPU6050 device.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
|
||||
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
|
795
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
Normal file
795
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
Normal file
@ -0,0 +1,795 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Invensense, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
/*
|
||||
* this is the gyro scale translated from dynamic range plus/minus
|
||||
* {250, 500, 1000, 2000} to rad/s
|
||||
*/
|
||||
static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
|
||||
|
||||
/*
|
||||
* this is the accel scale translated from dynamic range plus/minus
|
||||
* {2, 4, 8, 16} to m/s^2
|
||||
*/
|
||||
static const int accel_scale[] = {598, 1196, 2392, 4785};
|
||||
|
||||
static const struct inv_mpu6050_reg_map reg_set_6050 = {
|
||||
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
|
||||
.lpf = INV_MPU6050_REG_CONFIG,
|
||||
.user_ctrl = INV_MPU6050_REG_USER_CTRL,
|
||||
.fifo_en = INV_MPU6050_REG_FIFO_EN,
|
||||
.gyro_config = INV_MPU6050_REG_GYRO_CONFIG,
|
||||
.accl_config = INV_MPU6050_REG_ACCEL_CONFIG,
|
||||
.fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H,
|
||||
.fifo_r_w = INV_MPU6050_REG_FIFO_R_W,
|
||||
.raw_gyro = INV_MPU6050_REG_RAW_GYRO,
|
||||
.raw_accl = INV_MPU6050_REG_RAW_ACCEL,
|
||||
.temperature = INV_MPU6050_REG_TEMPERATURE,
|
||||
.int_enable = INV_MPU6050_REG_INT_ENABLE,
|
||||
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
|
||||
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
|
||||
};
|
||||
|
||||
static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
||||
.fsr = INV_MPU6050_FSR_2000DPS,
|
||||
.lpf = INV_MPU6050_FILTER_20HZ,
|
||||
.fifo_rate = INV_MPU6050_INIT_FIFO_RATE,
|
||||
.gyro_fifo_enable = false,
|
||||
.accl_fifo_enable = false,
|
||||
.accl_fs = INV_MPU6050_FS_02G,
|
||||
};
|
||||
|
||||
static const struct inv_mpu6050_hw hw_info[INV_NUM_PARTS] = {
|
||||
{
|
||||
.num_reg = 117,
|
||||
.name = "MPU6050",
|
||||
.reg = ®_set_6050,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
};
|
||||
|
||||
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d)
|
||||
{
|
||||
return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d);
|
||||
}
|
||||
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
|
||||
{
|
||||
u8 d, mgmt_1;
|
||||
int result;
|
||||
|
||||
/* switch clock needs to be careful. Only when gyro is on, can
|
||||
clock source be switched to gyro. Otherwise, it must be set to
|
||||
internal clock */
|
||||
if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {
|
||||
result = i2c_smbus_read_i2c_block_data(st->client,
|
||||
st->reg->pwr_mgmt_1, 1, &mgmt_1);
|
||||
if (result != 1)
|
||||
return result;
|
||||
|
||||
mgmt_1 &= ~INV_MPU6050_BIT_CLK_MASK;
|
||||
}
|
||||
|
||||
if ((INV_MPU6050_BIT_PWR_GYRO_STBY == mask) && (!en)) {
|
||||
/* turning off gyro requires switch to internal clock first.
|
||||
Then turn off gyro engine */
|
||||
mgmt_1 |= INV_CLK_INTERNAL;
|
||||
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, mgmt_1);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = i2c_smbus_read_i2c_block_data(st->client,
|
||||
st->reg->pwr_mgmt_2, 1, &d);
|
||||
if (result != 1)
|
||||
return result;
|
||||
if (en)
|
||||
d &= ~mask;
|
||||
else
|
||||
d |= mask;
|
||||
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_2, d);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (en) {
|
||||
/* Wait for output stablize */
|
||||
msleep(INV_MPU6050_TEMP_UP_TIME);
|
||||
if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {
|
||||
/* switch internal clock to PLL */
|
||||
mgmt_1 |= INV_CLK_PLL;
|
||||
result = inv_mpu6050_write_reg(st,
|
||||
st->reg->pwr_mgmt_1, mgmt_1);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (power_on)
|
||||
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, 0);
|
||||
else
|
||||
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
|
||||
INV_MPU6050_BIT_SLEEP);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (power_on)
|
||||
msleep(INV_MPU6050_REG_UP_TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
|
||||
*
|
||||
* Initial configuration:
|
||||
* FSR: ± 2000DPS
|
||||
* DLPF: 20Hz
|
||||
* FIFO rate: 50Hz
|
||||
* Clock source: Gyro PLL
|
||||
*/
|
||||
static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
int result;
|
||||
u8 d;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
|
||||
result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
d = INV_MPU6050_FILTER_20HZ;
|
||||
result = inv_mpu6050_write_reg(st, st->reg->lpf, d);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1;
|
||||
result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
|
||||
result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
memcpy(&st->chip_config, hw_info[st->chip_type].config,
|
||||
sizeof(struct inv_mpu6050_chip_config));
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg,
|
||||
int axis, int *val)
|
||||
{
|
||||
int ind, result;
|
||||
__be16 d;
|
||||
|
||||
ind = (axis - IIO_MOD_X) * 2;
|
||||
result = i2c_smbus_read_i2c_block_data(st->client, reg + ind, 2,
|
||||
(u8 *)&d);
|
||||
if (result != 2)
|
||||
return -EINVAL;
|
||||
*val = (short)be16_to_cpup(&d);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask) {
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
{
|
||||
int ret, result;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
result = 0;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (!st->chip_config.enable) {
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto error_read_raw;
|
||||
}
|
||||
/* when enable is on, power is already on */
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
if (!st->chip_config.gyro_fifo_enable ||
|
||||
!st->chip_config.enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_read_raw;
|
||||
}
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
|
||||
chan->channel2, val);
|
||||
if (!st->chip_config.gyro_fifo_enable ||
|
||||
!st->chip_config.enable) {
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_read_raw;
|
||||
}
|
||||
break;
|
||||
case IIO_ACCEL:
|
||||
if (!st->chip_config.accl_fifo_enable ||
|
||||
!st->chip_config.enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_read_raw;
|
||||
}
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
|
||||
chan->channel2, val);
|
||||
if (!st->chip_config.accl_fifo_enable ||
|
||||
!st->chip_config.enable) {
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_read_raw;
|
||||
}
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
/* wait for stablization */
|
||||
msleep(INV_MPU6050_SENSOR_UP_TIME);
|
||||
inv_mpu6050_sensor_show(st, st->reg->temperature,
|
||||
IIO_MOD_X, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
error_read_raw:
|
||||
if (!st->chip_config.enable)
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return ret;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = 0;
|
||||
*val2 = gyro_scale_6050[st->chip_config.fsr];
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
*val2 = accel_scale[st->chip_config.accl_fs];
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = 0;
|
||||
*val2 = INV_MPU6050_TEMP_SCALE;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = INV_MPU6050_TEMP_OFFSET;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int inv_mpu6050_write_fsr(struct inv_mpu6050_state *st, int fsr)
|
||||
{
|
||||
int result;
|
||||
u8 d;
|
||||
|
||||
if (fsr < 0 || fsr > INV_MPU6050_MAX_GYRO_FS_PARAM)
|
||||
return -EINVAL;
|
||||
if (fsr == st->chip_config.fsr)
|
||||
return 0;
|
||||
|
||||
d = (fsr << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
|
||||
result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
|
||||
if (result)
|
||||
return result;
|
||||
st->chip_config.fsr = fsr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_write_accel_fs(struct inv_mpu6050_state *st, int fs)
|
||||
{
|
||||
int result;
|
||||
u8 d;
|
||||
|
||||
if (fs < 0 || fs > INV_MPU6050_MAX_ACCL_FS_PARAM)
|
||||
return -EINVAL;
|
||||
if (fs == st->chip_config.accl_fs)
|
||||
return 0;
|
||||
|
||||
d = (fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
|
||||
result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
|
||||
if (result)
|
||||
return result;
|
||||
st->chip_config.accl_fs = fs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask) {
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int result;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
/* we should only update scale when the chip is disabled, i.e.,
|
||||
not running */
|
||||
if (st->chip_config.enable) {
|
||||
result = -EBUSY;
|
||||
goto error_write_raw;
|
||||
}
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto error_write_raw;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
result = inv_mpu6050_write_fsr(st, val);
|
||||
break;
|
||||
case IIO_ACCEL:
|
||||
result = inv_mpu6050_write_accel_fs(st, val);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
error_write_raw:
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
|
||||
*
|
||||
* Based on the Nyquist principle, the sampling rate must
|
||||
* exceed twice of the bandwidth of the signal, or there
|
||||
* would be alising. This function basically search for the
|
||||
* correct low pass parameters based on the fifo rate, e.g,
|
||||
* sampling frequency.
|
||||
*/
|
||||
static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate)
|
||||
{
|
||||
const int hz[] = {188, 98, 42, 20, 10, 5};
|
||||
const int d[] = {INV_MPU6050_FILTER_188HZ, INV_MPU6050_FILTER_98HZ,
|
||||
INV_MPU6050_FILTER_42HZ, INV_MPU6050_FILTER_20HZ,
|
||||
INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ};
|
||||
int i, h, result;
|
||||
u8 data;
|
||||
|
||||
h = (rate >> 1);
|
||||
i = 0;
|
||||
while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
|
||||
i++;
|
||||
data = d[i];
|
||||
result = inv_mpu6050_write_reg(st, st->reg->lpf, data);
|
||||
if (result)
|
||||
return result;
|
||||
st->chip_config.lpf = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_fifo_rate_store() - Set fifo rate.
|
||||
*/
|
||||
static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
s32 fifo_rate;
|
||||
u8 d;
|
||||
int result;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (kstrtoint(buf, 10, &fifo_rate))
|
||||
return -EINVAL;
|
||||
if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE ||
|
||||
fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
|
||||
return -EINVAL;
|
||||
if (fifo_rate == st->chip_config.fifo_rate)
|
||||
return count;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (st->chip_config.enable) {
|
||||
result = -EBUSY;
|
||||
goto fifo_rate_fail;
|
||||
}
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto fifo_rate_fail;
|
||||
|
||||
d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1;
|
||||
result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d);
|
||||
if (result)
|
||||
goto fifo_rate_fail;
|
||||
st->chip_config.fifo_rate = fifo_rate;
|
||||
|
||||
result = inv_mpu6050_set_lpf(st, fifo_rate);
|
||||
if (result)
|
||||
goto fifo_rate_fail;
|
||||
|
||||
fifo_rate_fail:
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_fifo_rate_show() - Get the current sampling rate.
|
||||
*/
|
||||
static ssize_t inv_fifo_rate_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", st->chip_config.fifo_rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_attr_show() - calling this function will show current
|
||||
* parameters.
|
||||
*/
|
||||
static ssize_t inv_attr_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
s8 *m;
|
||||
|
||||
switch (this_attr->address) {
|
||||
/* In MPU6050, the two matrix are the same because gyro and accel
|
||||
are integrated in one chip */
|
||||
case ATTR_GYRO_MATRIX:
|
||||
case ATTR_ACCL_MATRIX:
|
||||
m = st->plat_data.orientation;
|
||||
|
||||
return sprintf(buf, "%d, %d, %d; %d, %d, %d; %d, %d, %d\n",
|
||||
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_validate_trigger() - validate_trigger callback for invensense
|
||||
* MPU6050 device.
|
||||
* @indio_dev: The IIO device
|
||||
* @trig: The new trigger
|
||||
*
|
||||
* Returns: 0 if the 'trig' matches the trigger registered by the MPU6050
|
||||
* device, -EINVAL otherwise.
|
||||
*/
|
||||
static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define INV_MPU6050_CHAN(_type, _channel2, _index) \
|
||||
{ \
|
||||
.type = _type, \
|
||||
.modified = 1, \
|
||||
.channel2 = _channel2, \
|
||||
.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT \
|
||||
| IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.shift = 0 , \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
|
||||
/*
|
||||
* Note that temperature should only be via polled reading only,
|
||||
* not the final scan elements output.
|
||||
*/
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT
|
||||
| IIO_CHAN_INFO_OFFSET_SEPARATE_BIT
|
||||
| IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.scan_index = -1,
|
||||
},
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
|
||||
};
|
||||
|
||||
/* constant IIO attribute */
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
|
||||
inv_mpu6050_fifo_rate_store);
|
||||
static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL,
|
||||
ATTR_GYRO_MATRIX);
|
||||
static IIO_DEVICE_ATTR(in_accel_matrix, S_IRUGO, inv_attr_show, NULL,
|
||||
ATTR_ACCL_MATRIX);
|
||||
|
||||
static struct attribute *inv_attributes[] = {
|
||||
&iio_dev_attr_in_gyro_matrix.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_matrix.dev_attr.attr,
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group inv_attribute_group = {
|
||||
.attrs = inv_attributes
|
||||
};
|
||||
|
||||
static const struct iio_info mpu_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &inv_mpu6050_read_raw,
|
||||
.write_raw = &inv_mpu6050_write_raw,
|
||||
.attrs = &inv_attribute_group,
|
||||
.validate_trigger = inv_mpu6050_validate_trigger,
|
||||
};
|
||||
|
||||
/**
|
||||
* inv_check_and_setup_chip() - check and setup chip.
|
||||
*/
|
||||
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int result;
|
||||
|
||||
st->chip_type = INV_MPU6050;
|
||||
st->hw = &hw_info[st->chip_type];
|
||||
st->reg = hw_info[st->chip_type].reg;
|
||||
|
||||
/* reset to make sure previous state are not there */
|
||||
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
|
||||
INV_MPU6050_BIT_H_RESET);
|
||||
if (result)
|
||||
return result;
|
||||
msleep(INV_MPU6050_POWER_UP_TIME);
|
||||
/* toggle power state. After reset, the sleep bit could be on
|
||||
or off depending on the OTP settings. Toggling power would
|
||||
make it in a definite state as well as making the hardware
|
||||
state align with the software state */
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
return result;
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu_probe() - probe function.
|
||||
* @client: i2c client.
|
||||
* @id: i2c device id.
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
static int inv_mpu_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct inv_mpu6050_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int result;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK |
|
||||
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
|
||||
result = -ENOSYS;
|
||||
goto out_no_free;
|
||||
}
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto out_no_free;
|
||||
}
|
||||
st = iio_priv(indio_dev);
|
||||
st->client = client;
|
||||
st->plat_data = *(struct inv_mpu6050_platform_data
|
||||
*)dev_get_platdata(&client->dev);
|
||||
/* power is turned on inside check chip type*/
|
||||
result = inv_check_and_setup_chip(st, id);
|
||||
if (result)
|
||||
goto out_free;
|
||||
|
||||
result = inv_mpu6050_init_config(indio_dev);
|
||||
if (result) {
|
||||
dev_err(&client->dev,
|
||||
"Could not initialize device.\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
|
||||
indio_dev->info = &mpu_info;
|
||||
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
|
||||
|
||||
result = iio_triggered_buffer_setup(indio_dev,
|
||||
inv_mpu6050_irq_handler,
|
||||
inv_mpu6050_read_fifo,
|
||||
NULL);
|
||||
if (result) {
|
||||
dev_err(&st->client->dev, "configure buffer fail %d\n",
|
||||
result);
|
||||
goto out_free;
|
||||
}
|
||||
result = inv_mpu6050_probe_trigger(indio_dev);
|
||||
if (result) {
|
||||
dev_err(&st->client->dev, "trigger probe fail %d\n", result);
|
||||
goto out_unreg_ring;
|
||||
}
|
||||
|
||||
INIT_KFIFO(st->timestamps);
|
||||
spin_lock_init(&st->time_stamp_lock);
|
||||
result = iio_device_register(indio_dev);
|
||||
if (result) {
|
||||
dev_err(&st->client->dev, "IIO register fail %d\n", result);
|
||||
goto out_remove_trigger;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove_trigger:
|
||||
inv_mpu6050_remove_trigger(st);
|
||||
out_unreg_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
out_free:
|
||||
iio_device_free(indio_dev);
|
||||
out_no_free:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
inv_mpu6050_remove_trigger(st);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int inv_mpu_resume(struct device *dev)
|
||||
{
|
||||
return inv_mpu6050_set_power_itg(
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev))), true);
|
||||
}
|
||||
|
||||
static int inv_mpu_suspend(struct device *dev)
|
||||
{
|
||||
return inv_mpu6050_set_power_itg(
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev))), false);
|
||||
}
|
||||
static SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume);
|
||||
|
||||
#define INV_MPU6050_PMOPS (&inv_mpu_pmops)
|
||||
#else
|
||||
#define INV_MPU6050_PMOPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
/*
|
||||
* device id table is used to identify what device can be
|
||||
* supported by this driver
|
||||
*/
|
||||
static const struct i2c_device_id inv_mpu_id[] = {
|
||||
{"mpu6050", INV_MPU6050},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
|
||||
|
||||
static struct i2c_driver inv_mpu_driver = {
|
||||
.probe = inv_mpu_probe,
|
||||
.remove = inv_mpu_remove,
|
||||
.id_table = inv_mpu_id,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "inv-mpu6050",
|
||||
.pm = INV_MPU6050_PMOPS,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(inv_mpu_driver);
|
||||
|
||||
MODULE_AUTHOR("Invensense Corporation");
|
||||
MODULE_DESCRIPTION("Invensense device MPU6050 driver");
|
||||
MODULE_LICENSE("GPL");
|
246
drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
Normal file
246
drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Invensense, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/platform_data/invensense_mpu6050.h>
|
||||
|
||||
/**
|
||||
* struct inv_mpu6050_reg_map - Notable registers.
|
||||
* @sample_rate_div: Divider applied to gyro output rate.
|
||||
* @lpf: Configures internal low pass filter.
|
||||
* @user_ctrl: Enables/resets the FIFO.
|
||||
* @fifo_en: Determines which data will appear in FIFO.
|
||||
* @gyro_config: gyro config register.
|
||||
* @accl_config: accel config register
|
||||
* @fifo_count_h: Upper byte of FIFO count.
|
||||
* @fifo_r_w: FIFO register.
|
||||
* @raw_gyro: Address of first gyro register.
|
||||
* @raw_accl: Address of first accel register.
|
||||
* @temperature: temperature register
|
||||
* @int_enable: Interrupt enable register.
|
||||
* @pwr_mgmt_1: Controls chip's power state and clock source.
|
||||
* @pwr_mgmt_2: Controls power state of individual sensors.
|
||||
*/
|
||||
struct inv_mpu6050_reg_map {
|
||||
u8 sample_rate_div;
|
||||
u8 lpf;
|
||||
u8 user_ctrl;
|
||||
u8 fifo_en;
|
||||
u8 gyro_config;
|
||||
u8 accl_config;
|
||||
u8 fifo_count_h;
|
||||
u8 fifo_r_w;
|
||||
u8 raw_gyro;
|
||||
u8 raw_accl;
|
||||
u8 temperature;
|
||||
u8 int_enable;
|
||||
u8 pwr_mgmt_1;
|
||||
u8 pwr_mgmt_2;
|
||||
};
|
||||
|
||||
/*device enum */
|
||||
enum inv_devices {
|
||||
INV_MPU6050,
|
||||
INV_NUM_PARTS
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inv_mpu6050_chip_config - Cached chip configuration data.
|
||||
* @fsr: Full scale range.
|
||||
* @lpf: Digital low pass filter frequency.
|
||||
* @accl_fs: accel full scale range.
|
||||
* @enable: master enable state.
|
||||
* @accl_fifo_enable: enable accel data output
|
||||
* @gyro_fifo_enable: enable gyro data output
|
||||
* @fifo_rate: FIFO update rate.
|
||||
*/
|
||||
struct inv_mpu6050_chip_config {
|
||||
unsigned int fsr:2;
|
||||
unsigned int lpf:3;
|
||||
unsigned int accl_fs:2;
|
||||
unsigned int enable:1;
|
||||
unsigned int accl_fifo_enable:1;
|
||||
unsigned int gyro_fifo_enable:1;
|
||||
u16 fifo_rate;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inv_mpu6050_hw - Other important hardware information.
|
||||
* @num_reg: Number of registers on device.
|
||||
* @name: name of the chip.
|
||||
* @reg: register map of the chip.
|
||||
* @config: configuration of the chip.
|
||||
*/
|
||||
struct inv_mpu6050_hw {
|
||||
u8 num_reg;
|
||||
u8 *name;
|
||||
const struct inv_mpu6050_reg_map *reg;
|
||||
const struct inv_mpu6050_chip_config *config;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct inv_mpu6050_state - Driver state variables.
|
||||
* @TIMESTAMP_FIFO_SIZE: fifo size for timestamp.
|
||||
* @trig: IIO trigger.
|
||||
* @chip_config: Cached attribute information.
|
||||
* @reg: Map of important registers.
|
||||
* @hw: Other hardware-specific information.
|
||||
* @chip_type: chip type.
|
||||
* @time_stamp_lock: spin lock to time stamp.
|
||||
* @client: i2c client handle.
|
||||
* @plat_data: platform data.
|
||||
* @timestamps: kfifo queue to store time stamp.
|
||||
*/
|
||||
struct inv_mpu6050_state {
|
||||
#define TIMESTAMP_FIFO_SIZE 16
|
||||
struct iio_trigger *trig;
|
||||
struct inv_mpu6050_chip_config chip_config;
|
||||
const struct inv_mpu6050_reg_map *reg;
|
||||
const struct inv_mpu6050_hw *hw;
|
||||
enum inv_devices chip_type;
|
||||
spinlock_t time_stamp_lock;
|
||||
struct i2c_client *client;
|
||||
struct inv_mpu6050_platform_data plat_data;
|
||||
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
|
||||
};
|
||||
|
||||
/*register and associated bit definition*/
|
||||
#define INV_MPU6050_REG_SAMPLE_RATE_DIV 0x19
|
||||
#define INV_MPU6050_REG_CONFIG 0x1A
|
||||
#define INV_MPU6050_REG_GYRO_CONFIG 0x1B
|
||||
#define INV_MPU6050_REG_ACCEL_CONFIG 0x1C
|
||||
|
||||
#define INV_MPU6050_REG_FIFO_EN 0x23
|
||||
#define INV_MPU6050_BIT_ACCEL_OUT 0x08
|
||||
#define INV_MPU6050_BITS_GYRO_OUT 0x70
|
||||
|
||||
#define INV_MPU6050_REG_INT_ENABLE 0x38
|
||||
#define INV_MPU6050_BIT_DATA_RDY_EN 0x01
|
||||
#define INV_MPU6050_BIT_DMP_INT_EN 0x02
|
||||
|
||||
#define INV_MPU6050_REG_RAW_ACCEL 0x3B
|
||||
#define INV_MPU6050_REG_TEMPERATURE 0x41
|
||||
#define INV_MPU6050_REG_RAW_GYRO 0x43
|
||||
|
||||
#define INV_MPU6050_REG_USER_CTRL 0x6A
|
||||
#define INV_MPU6050_BIT_FIFO_RST 0x04
|
||||
#define INV_MPU6050_BIT_DMP_RST 0x08
|
||||
#define INV_MPU6050_BIT_I2C_MST_EN 0x20
|
||||
#define INV_MPU6050_BIT_FIFO_EN 0x40
|
||||
#define INV_MPU6050_BIT_DMP_EN 0x80
|
||||
|
||||
#define INV_MPU6050_REG_PWR_MGMT_1 0x6B
|
||||
#define INV_MPU6050_BIT_H_RESET 0x80
|
||||
#define INV_MPU6050_BIT_SLEEP 0x40
|
||||
#define INV_MPU6050_BIT_CLK_MASK 0x7
|
||||
|
||||
#define INV_MPU6050_REG_PWR_MGMT_2 0x6C
|
||||
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
|
||||
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
|
||||
|
||||
#define INV_MPU6050_REG_FIFO_COUNT_H 0x72
|
||||
#define INV_MPU6050_REG_FIFO_R_W 0x74
|
||||
|
||||
#define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6
|
||||
#define INV_MPU6050_FIFO_COUNT_BYTE 2
|
||||
#define INV_MPU6050_FIFO_THRESHOLD 500
|
||||
#define INV_MPU6050_POWER_UP_TIME 100
|
||||
#define INV_MPU6050_TEMP_UP_TIME 100
|
||||
#define INV_MPU6050_SENSOR_UP_TIME 30
|
||||
#define INV_MPU6050_REG_UP_TIME 5
|
||||
|
||||
#define INV_MPU6050_TEMP_OFFSET 12421
|
||||
#define INV_MPU6050_TEMP_SCALE 2941
|
||||
#define INV_MPU6050_MAX_GYRO_FS_PARAM 3
|
||||
#define INV_MPU6050_MAX_ACCL_FS_PARAM 3
|
||||
#define INV_MPU6050_THREE_AXIS 3
|
||||
#define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3
|
||||
#define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3
|
||||
|
||||
/* 6 + 6 round up and plus 8 */
|
||||
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
|
||||
|
||||
/* init parameters */
|
||||
#define INV_MPU6050_INIT_FIFO_RATE 50
|
||||
#define INV_MPU6050_TIME_STAMP_TOR 5
|
||||
#define INV_MPU6050_MAX_FIFO_RATE 1000
|
||||
#define INV_MPU6050_MIN_FIFO_RATE 4
|
||||
#define INV_MPU6050_ONE_K_HZ 1000
|
||||
|
||||
/* scan element definition */
|
||||
enum inv_mpu6050_scan {
|
||||
INV_MPU6050_SCAN_ACCL_X,
|
||||
INV_MPU6050_SCAN_ACCL_Y,
|
||||
INV_MPU6050_SCAN_ACCL_Z,
|
||||
INV_MPU6050_SCAN_GYRO_X,
|
||||
INV_MPU6050_SCAN_GYRO_Y,
|
||||
INV_MPU6050_SCAN_GYRO_Z,
|
||||
INV_MPU6050_SCAN_TIMESTAMP,
|
||||
};
|
||||
|
||||
enum inv_mpu6050_filter_e {
|
||||
INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
|
||||
INV_MPU6050_FILTER_188HZ,
|
||||
INV_MPU6050_FILTER_98HZ,
|
||||
INV_MPU6050_FILTER_42HZ,
|
||||
INV_MPU6050_FILTER_20HZ,
|
||||
INV_MPU6050_FILTER_10HZ,
|
||||
INV_MPU6050_FILTER_5HZ,
|
||||
INV_MPU6050_FILTER_2100HZ_NOLPF,
|
||||
NUM_MPU6050_FILTER
|
||||
};
|
||||
|
||||
/* IIO attribute address */
|
||||
enum INV_MPU6050_IIO_ATTR_ADDR {
|
||||
ATTR_GYRO_MATRIX,
|
||||
ATTR_ACCL_MATRIX,
|
||||
};
|
||||
|
||||
enum inv_mpu6050_accl_fs_e {
|
||||
INV_MPU6050_FS_02G = 0,
|
||||
INV_MPU6050_FS_04G,
|
||||
INV_MPU6050_FS_08G,
|
||||
INV_MPU6050_FS_16G,
|
||||
NUM_ACCL_FSR
|
||||
};
|
||||
|
||||
enum inv_mpu6050_fsr_e {
|
||||
INV_MPU6050_FSR_250DPS = 0,
|
||||
INV_MPU6050_FSR_500DPS,
|
||||
INV_MPU6050_FSR_1000DPS,
|
||||
INV_MPU6050_FSR_2000DPS,
|
||||
NUM_MPU6050_FSR
|
||||
};
|
||||
|
||||
enum inv_mpu6050_clock_sel_e {
|
||||
INV_CLK_INTERNAL = 0,
|
||||
INV_CLK_PLL,
|
||||
NUM_CLK
|
||||
};
|
||||
|
||||
irqreturn_t inv_mpu6050_irq_handler(int irq, void *p);
|
||||
irqreturn_t inv_mpu6050_read_fifo(int irq, void *p);
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev);
|
||||
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st);
|
||||
int inv_reset_fifo(struct iio_dev *indio_dev);
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
|
||||
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
|
196
drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
Normal file
196
drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Invensense, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/poll.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
{
|
||||
int result;
|
||||
u8 d;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* disable interrupt */
|
||||
result = inv_mpu6050_write_reg(st, st->reg->int_enable, 0);
|
||||
if (result) {
|
||||
dev_err(&st->client->dev, "int_enable failed %d\n", result);
|
||||
return result;
|
||||
}
|
||||
/* disable the sensor output to FIFO */
|
||||
result = inv_mpu6050_write_reg(st, st->reg->fifo_en, 0);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
/* disable fifo reading */
|
||||
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, 0);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
/* reset FIFO*/
|
||||
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl,
|
||||
INV_MPU6050_BIT_FIFO_RST);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
/* enable interrupt */
|
||||
if (st->chip_config.accl_fifo_enable ||
|
||||
st->chip_config.gyro_fifo_enable) {
|
||||
result = inv_mpu6050_write_reg(st, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
/* enable FIFO reading and I2C master interface*/
|
||||
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl,
|
||||
INV_MPU6050_BIT_FIFO_EN);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
/* enable sensor output to FIFO */
|
||||
d = 0;
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
d |= INV_MPU6050_BITS_GYRO_OUT;
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_ACCEL_OUT;
|
||||
result = inv_mpu6050_write_reg(st, st->reg->fifo_en, d);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
return 0;
|
||||
|
||||
reset_fifo_fail:
|
||||
dev_err(&st->client->dev, "reset fifo failed %d\n", result);
|
||||
result = inv_mpu6050_write_reg(st, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void inv_clear_kfifo(struct inv_mpu6050_state *st)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* take the spin lock sem to avoid interrupt kick in */
|
||||
spin_lock_irqsave(&st->time_stamp_lock, flags);
|
||||
kfifo_reset(&st->timestamps);
|
||||
spin_unlock_irqrestore(&st->time_stamp_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_irq_handler() - Cache a timestamp at each data ready interrupt.
|
||||
*/
|
||||
irqreturn_t inv_mpu6050_irq_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
s64 timestamp;
|
||||
|
||||
timestamp = iio_get_time_ns();
|
||||
spin_lock(&st->time_stamp_lock);
|
||||
kfifo_in(&st->timestamps, ×tamp, 1);
|
||||
spin_unlock(&st->time_stamp_lock);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_read_fifo() - Transfer data from hardware FIFO to KFIFO.
|
||||
*/
|
||||
irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
size_t bytes_per_datum;
|
||||
int result;
|
||||
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
|
||||
u16 fifo_count;
|
||||
s64 timestamp;
|
||||
u64 *tmp;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (!(st->chip_config.accl_fifo_enable |
|
||||
st->chip_config.gyro_fifo_enable))
|
||||
goto end_session;
|
||||
bytes_per_datum = 0;
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
|
||||
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
|
||||
|
||||
/*
|
||||
* read fifo_count register to know how many bytes inside FIFO
|
||||
* right now
|
||||
*/
|
||||
result = i2c_smbus_read_i2c_block_data(st->client,
|
||||
st->reg->fifo_count_h,
|
||||
INV_MPU6050_FIFO_COUNT_BYTE, data);
|
||||
if (result != INV_MPU6050_FIFO_COUNT_BYTE)
|
||||
goto end_session;
|
||||
fifo_count = be16_to_cpup((__be16 *)(&data[0]));
|
||||
if (fifo_count < bytes_per_datum)
|
||||
goto end_session;
|
||||
/* fifo count can't be odd number, if it is odd, reset fifo*/
|
||||
if (fifo_count & 1)
|
||||
goto flush_fifo;
|
||||
if (fifo_count > INV_MPU6050_FIFO_THRESHOLD)
|
||||
goto flush_fifo;
|
||||
/* Timestamp mismatch. */
|
||||
if (kfifo_len(&st->timestamps) >
|
||||
fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR)
|
||||
goto flush_fifo;
|
||||
while (fifo_count >= bytes_per_datum) {
|
||||
result = i2c_smbus_read_i2c_block_data(st->client,
|
||||
st->reg->fifo_r_w,
|
||||
bytes_per_datum, data);
|
||||
if (result != bytes_per_datum)
|
||||
goto flush_fifo;
|
||||
|
||||
result = kfifo_out(&st->timestamps, ×tamp, 1);
|
||||
/* when there is no timestamp, put timestamp as 0 */
|
||||
if (0 == result)
|
||||
timestamp = 0;
|
||||
|
||||
tmp = (u64 *)data;
|
||||
tmp[DIV_ROUND_UP(bytes_per_datum, 8)] = timestamp;
|
||||
result = iio_push_to_buffers(indio_dev, data);
|
||||
if (result)
|
||||
goto flush_fifo;
|
||||
fifo_count -= bytes_per_datum;
|
||||
}
|
||||
|
||||
end_session:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
||||
flush_fifo:
|
||||
/* Flush HW and SW FIFOs. */
|
||||
inv_reset_fifo(indio_dev);
|
||||
inv_clear_kfifo(st);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
155
drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
Normal file
155
drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Invensense, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
static void inv_scan_query(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->chip_config.gyro_fifo_enable =
|
||||
test_bit(INV_MPU6050_SCAN_GYRO_X,
|
||||
indio_dev->active_scan_mask) ||
|
||||
test_bit(INV_MPU6050_SCAN_GYRO_Y,
|
||||
indio_dev->active_scan_mask) ||
|
||||
test_bit(INV_MPU6050_SCAN_GYRO_Z,
|
||||
indio_dev->active_scan_mask);
|
||||
|
||||
st->chip_config.accl_fifo_enable =
|
||||
test_bit(INV_MPU6050_SCAN_ACCL_X,
|
||||
indio_dev->active_scan_mask) ||
|
||||
test_bit(INV_MPU6050_SCAN_ACCL_Y,
|
||||
indio_dev->active_scan_mask) ||
|
||||
test_bit(INV_MPU6050_SCAN_ACCL_Z,
|
||||
indio_dev->active_scan_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_set_enable() - enable chip functions.
|
||||
* @indio_dev: Device driver instance.
|
||||
* @enable: enable/disable
|
||||
*/
|
||||
static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int result;
|
||||
|
||||
if (enable) {
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
inv_scan_query(indio_dev);
|
||||
if (st->chip_config.gyro_fifo_enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
if (st->chip_config.accl_fifo_enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
result = inv_reset_fifo(indio_dev);
|
||||
if (result)
|
||||
return result;
|
||||
} else {
|
||||
result = inv_mpu6050_write_reg(st, st->reg->fifo_en, 0);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_write_reg(st, st->reg->int_enable, 0);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, 0);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
st->chip_config.enable = enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu_data_rdy_trigger_set_state() - set data ready interrupt state
|
||||
* @trig: Trigger instance
|
||||
* @state: Desired trigger state
|
||||
*/
|
||||
static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
return inv_mpu6050_set_enable(trig->private_data, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops inv_mpu_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"inv_mpu",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
st->trig->dev.parent = &st->client->dev;
|
||||
st->trig->private_data = indio_dev;
|
||||
st->trig->ops = &inv_mpu_trigger_ops;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
indio_dev->trig = st->trig;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->client->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st)
|
||||
{
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->client->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
@ -160,7 +160,7 @@ void iio_trigger_notify_done(struct iio_trigger *trig)
|
||||
trig->use_count--;
|
||||
if (trig->use_count == 0 && trig->ops && trig->ops->try_reenable)
|
||||
if (trig->ops->try_reenable(trig))
|
||||
/* Missed and interrupt so launch new poll now */
|
||||
/* Missed an interrupt so launch new poll now */
|
||||
iio_trigger_poll(trig, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_trigger_notify_done);
|
||||
@ -193,7 +193,7 @@ static void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
|
||||
* This is not currently handled. Alternative of not enabling trigger unless
|
||||
* the relevant function is in there may be the best option.
|
||||
*/
|
||||
/* Worth protecting against double additions?*/
|
||||
/* Worth protecting against double additions? */
|
||||
static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
|
||||
struct iio_poll_func *pf)
|
||||
{
|
||||
@ -201,7 +201,7 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
|
||||
bool notinuse
|
||||
= bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
|
||||
|
||||
/* Prevent the module being removed whilst attached to a trigger */
|
||||
/* Prevent the module from being removed whilst attached to a trigger */
|
||||
__module_get(pf->indio_dev->info->driver_module);
|
||||
pf->irq = iio_trigger_get_irq(trig);
|
||||
ret = request_threaded_irq(pf->irq, pf->h, pf->thread,
|
||||
@ -288,7 +288,7 @@ void iio_dealloc_pollfunc(struct iio_poll_func *pf)
|
||||
EXPORT_SYMBOL_GPL(iio_dealloc_pollfunc);
|
||||
|
||||
/**
|
||||
* iio_trigger_read_current() - trigger consumer sysfs query which trigger
|
||||
* iio_trigger_read_current() - trigger consumer sysfs query current trigger
|
||||
*
|
||||
* For trigger consumers the current_trigger interface allows the trigger
|
||||
* used by the device to be queried.
|
||||
@ -305,7 +305,7 @@ 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
|
||||
*
|
||||
* For trigger consumers the current_trigger interface allows the trigger
|
||||
* used for this device to be specified at run time based on the triggers
|
||||
@ -476,7 +476,7 @@ void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
|
||||
|
||||
void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev)
|
||||
{
|
||||
/* Clean up and associated but not attached triggers references */
|
||||
/* Clean up an associated but not attached trigger reference */
|
||||
if (indio_dev->trig)
|
||||
iio_trigger_put(indio_dev->trig);
|
||||
}
|
||||
|
@ -54,39 +54,25 @@ error_ret:
|
||||
EXPORT_SYMBOL_GPL(iio_map_array_register);
|
||||
|
||||
|
||||
/* Assumes the exact same array (e.g. memory locations)
|
||||
* used at unregistration as used at registration rather than
|
||||
* more complex checking of contents.
|
||||
/*
|
||||
* Remove all map entries associated with the given iio device
|
||||
*/
|
||||
int iio_map_array_unregister(struct iio_dev *indio_dev,
|
||||
struct iio_map *maps)
|
||||
int iio_map_array_unregister(struct iio_dev *indio_dev)
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
bool found_it;
|
||||
int ret = -ENODEV;
|
||||
struct iio_map_internal *mapi;
|
||||
|
||||
if (maps == NULL)
|
||||
return 0;
|
||||
struct list_head *pos, *tmp;
|
||||
|
||||
mutex_lock(&iio_map_list_lock);
|
||||
while (maps[i].consumer_dev_name != NULL) {
|
||||
found_it = false;
|
||||
list_for_each_entry(mapi, &iio_map_list, l)
|
||||
if (&maps[i] == mapi->map) {
|
||||
list_del(&mapi->l);
|
||||
kfree(mapi);
|
||||
found_it = true;
|
||||
break;
|
||||
}
|
||||
if (!found_it) {
|
||||
ret = -ENODEV;
|
||||
goto error_ret;
|
||||
list_for_each_safe(pos, tmp, &iio_map_list) {
|
||||
mapi = list_entry(pos, struct iio_map_internal, l);
|
||||
if (indio_dev == mapi->indio_dev) {
|
||||
list_del(&mapi->l);
|
||||
kfree(mapi);
|
||||
ret = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
error_ret:
|
||||
mutex_unlock(&iio_map_list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_map_array_unregister);
|
||||
@ -107,7 +93,8 @@ static const struct iio_chan_spec
|
||||
}
|
||||
|
||||
|
||||
struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
|
||||
static struct iio_channel *iio_channel_get_sys(const char *name,
|
||||
const char *channel_name)
|
||||
{
|
||||
struct iio_map_internal *c_i = NULL, *c = NULL;
|
||||
struct iio_channel *channel;
|
||||
@ -158,6 +145,14 @@ error_no_mem:
|
||||
iio_device_put(c->indio_dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
struct iio_channel *iio_channel_get(struct device *dev,
|
||||
const char *channel_name)
|
||||
{
|
||||
const char *name = dev ? dev_name(dev) : NULL;
|
||||
|
||||
return iio_channel_get_sys(name, channel_name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_get);
|
||||
|
||||
void iio_channel_release(struct iio_channel *channel)
|
||||
@ -167,16 +162,18 @@ void iio_channel_release(struct iio_channel *channel)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_release);
|
||||
|
||||
struct iio_channel *iio_channel_get_all(const char *name)
|
||||
struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
const char *name;
|
||||
struct iio_channel *chans;
|
||||
struct iio_map_internal *c = NULL;
|
||||
int nummaps = 0;
|
||||
int mapind = 0;
|
||||
int i, ret;
|
||||
|
||||
if (name == NULL)
|
||||
if (dev == NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
name = dev_name(dev);
|
||||
|
||||
mutex_lock(&iio_map_list_lock);
|
||||
/* first count the matching maps */
|
||||
|
@ -22,7 +22,6 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
|
||||
if ((length == 0) || (bytes_per_datum == 0))
|
||||
return -EINVAL;
|
||||
|
||||
__iio_update_buffer(&buf->buffer, bytes_per_datum, length);
|
||||
return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
|
||||
bytes_per_datum, GFP_KERNEL);
|
||||
}
|
||||
|
@ -32,6 +32,16 @@ config SENSORS_LM3533
|
||||
changes. The ALS-control output values can be set per zone for the
|
||||
three current output channels.
|
||||
|
||||
config SENSORS_TSL2563
|
||||
tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Taos TSL2560,
|
||||
TSL2561, TSL2562 and TSL2563 ambient light sensors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tsl2563.
|
||||
|
||||
config VCNL4000
|
||||
tristate "VCNL4000 combined ALS and proximity sensor"
|
||||
depends on I2C
|
||||
|
@ -4,5 +4,6 @@
|
||||
|
||||
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
@ -28,7 +28,6 @@
|
||||
#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*/
|
||||
@ -39,7 +38,7 @@
|
||||
|
||||
struct als_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_iio_common common_attributes;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info als_illum;
|
||||
u32 illum;
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* drivers/i2c/chips/tsl2563.c
|
||||
* drivers/iio/light/tsl2563.c
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
@ -38,52 +38,52 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include "tsl2563.h"
|
||||
#include <linux/platform_data/tsl2563.h>
|
||||
|
||||
/* Use this many bits for fraction part. */
|
||||
#define ADC_FRAC_BITS (14)
|
||||
#define ADC_FRAC_BITS 14
|
||||
|
||||
/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
|
||||
#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000))
|
||||
|
||||
/* Bits used for fraction in calibration coefficients.*/
|
||||
#define CALIB_FRAC_BITS (10)
|
||||
#define CALIB_FRAC_BITS 10
|
||||
/* 0.5 in CALIB_FRAC_BITS precision */
|
||||
#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1))
|
||||
/* Make a fraction from a number n that was multiplied with b. */
|
||||
#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b))
|
||||
/* Decimal 10^(digits in sysfs presentation) */
|
||||
#define CALIB_BASE_SYSFS (1000)
|
||||
#define CALIB_BASE_SYSFS 1000
|
||||
|
||||
#define TSL2563_CMD (0x80)
|
||||
#define TSL2563_CLEARINT (0x40)
|
||||
#define TSL2563_CMD 0x80
|
||||
#define TSL2563_CLEARINT 0x40
|
||||
|
||||
#define TSL2563_REG_CTRL (0x00)
|
||||
#define TSL2563_REG_TIMING (0x01)
|
||||
#define TSL2563_REG_LOWLOW (0x02) /* data0 low threshold, 2 bytes */
|
||||
#define TSL2563_REG_LOWHIGH (0x03)
|
||||
#define TSL2563_REG_HIGHLOW (0x04) /* data0 high threshold, 2 bytes */
|
||||
#define TSL2563_REG_HIGHHIGH (0x05)
|
||||
#define TSL2563_REG_INT (0x06)
|
||||
#define TSL2563_REG_ID (0x0a)
|
||||
#define TSL2563_REG_DATA0LOW (0x0c) /* broadband sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA0HIGH (0x0d)
|
||||
#define TSL2563_REG_DATA1LOW (0x0e) /* infrared sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA1HIGH (0x0f)
|
||||
#define TSL2563_REG_CTRL 0x00
|
||||
#define TSL2563_REG_TIMING 0x01
|
||||
#define TSL2563_REG_LOWLOW 0x02 /* data0 low threshold, 2 bytes */
|
||||
#define TSL2563_REG_LOWHIGH 0x03
|
||||
#define TSL2563_REG_HIGHLOW 0x04 /* data0 high threshold, 2 bytes */
|
||||
#define TSL2563_REG_HIGHHIGH 0x05
|
||||
#define TSL2563_REG_INT 0x06
|
||||
#define TSL2563_REG_ID 0x0a
|
||||
#define TSL2563_REG_DATA0LOW 0x0c /* broadband sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA0HIGH 0x0d
|
||||
#define TSL2563_REG_DATA1LOW 0x0e /* infrared sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA1HIGH 0x0f
|
||||
|
||||
#define TSL2563_CMD_POWER_ON (0x03)
|
||||
#define TSL2563_CMD_POWER_OFF (0x00)
|
||||
#define TSL2563_CTRL_POWER_MASK (0x03)
|
||||
#define TSL2563_CMD_POWER_ON 0x03
|
||||
#define TSL2563_CMD_POWER_OFF 0x00
|
||||
#define TSL2563_CTRL_POWER_MASK 0x03
|
||||
|
||||
#define TSL2563_TIMING_13MS (0x00)
|
||||
#define TSL2563_TIMING_100MS (0x01)
|
||||
#define TSL2563_TIMING_400MS (0x02)
|
||||
#define TSL2563_TIMING_MASK (0x03)
|
||||
#define TSL2563_TIMING_GAIN16 (0x10)
|
||||
#define TSL2563_TIMING_GAIN1 (0x00)
|
||||
#define TSL2563_TIMING_13MS 0x00
|
||||
#define TSL2563_TIMING_100MS 0x01
|
||||
#define TSL2563_TIMING_400MS 0x02
|
||||
#define TSL2563_TIMING_MASK 0x03
|
||||
#define TSL2563_TIMING_GAIN16 0x10
|
||||
#define TSL2563_TIMING_GAIN1 0x00
|
||||
|
||||
#define TSL2563_INT_DISBLED (0x00)
|
||||
#define TSL2563_INT_LEVEL (0x10)
|
||||
#define TSL2563_INT_DISBLED 0x00
|
||||
#define TSL2563_INT_LEVEL 0x10
|
||||
#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
|
||||
|
||||
struct tsl2563_gainlevel_coeff {
|
||||
@ -190,8 +190,10 @@ static int tsl2563_configure(struct tsl2563_chip *chip)
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
TSL2563_CMD | TSL2563_REG_LOWHIGH,
|
||||
(chip->low_thres >> 8) & 0xFF);
|
||||
/* Interrupt register is automatically written anyway if it is relevant
|
||||
so is not here */
|
||||
/*
|
||||
* Interrupt register is automatically written anyway if it is relevant
|
||||
* so is not here.
|
||||
*/
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
@ -423,9 +425,7 @@ static const struct tsl2563_lux_coeff lux_table[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert normalized, scaled ADC values to lux.
|
||||
*/
|
||||
/* Convert normalized, scaled ADC values to lux. */
|
||||
static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
||||
{
|
||||
const struct tsl2563_lux_coeff *lp = lux_table;
|
||||
@ -441,11 +441,6 @@ static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
||||
return (unsigned int) (lux >> ADC_FRAC_BITS);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Sysfs interface */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
|
||||
/* Apply calibration coefficient to ADC count. */
|
||||
static u32 calib_adc(u32 adc, u32 calib)
|
||||
{
|
||||
@ -677,18 +672,11 @@ static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev,
|
||||
TSL2563_CMD | TSL2563_REG_INT);
|
||||
mutex_unlock(&chip->lock);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
ret = !!(ret & 0x30);
|
||||
error_ret:
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return !!(ret & 0x30);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Probe, Attach, Remove */
|
||||
/*--------------------------------------------------------------*/
|
||||
static struct i2c_driver tsl2563_i2c_driver;
|
||||
|
||||
static const struct iio_info tsl2563_info_no_irq = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2563_read_raw,
|
@ -14,4 +14,34 @@ config HID_SENSOR_MAGNETOMETER_3D
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Magnetometer 3D.
|
||||
|
||||
config IIO_ST_MAGN_3AXIS
|
||||
tristate "STMicroelectronics magnetometers 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
select IIO_ST_SENSORS_CORE
|
||||
select IIO_ST_MAGN_I2C_3AXIS if (I2C)
|
||||
select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER)
|
||||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
select IIO_ST_MAGN_BUFFER if (IIO_TRIGGERED_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics magnetometers:
|
||||
LSM303DLHC, LSM303DLM, LIS3MDL.
|
||||
|
||||
This driver can also be built as a module. If so, will be created
|
||||
these modules:
|
||||
- st_magn (core functions for the driver [it is mandatory]);
|
||||
- st_magn_i2c (necessary for the I2C devices [optional*]);
|
||||
- st_magn_spi (necessary for the SPI devices [optional*]);
|
||||
|
||||
(*) one of these is necessary to do something.
|
||||
|
||||
config IIO_ST_MAGN_I2C_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_MAGN_3AXIS
|
||||
depends on IIO_ST_SENSORS_I2C
|
||||
|
||||
config IIO_ST_MAGN_SPI_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_MAGN_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
endmenu
|
||||
|
@ -3,3 +3,10 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
|
||||
st_magn-y := st_magn_core.o
|
||||
st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
|
||||
|
@ -28,7 +28,6 @@
|
||||
#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*/
|
||||
@ -44,7 +43,7 @@ enum magn_3d_channel {
|
||||
|
||||
struct magn_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_iio_common common_attributes;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
|
||||
u32 magn_val[MAGN_3D_CHANNEL_MAX];
|
||||
};
|
||||
|
45
drivers/iio/magnetometer/st_magn.h
Normal file
45
drivers/iio/magnetometer/st_magn.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* STMicroelectronics magnetometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
* v. 1.0.0
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_MAGN_H
|
||||
#define ST_MAGN_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
|
||||
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
|
||||
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
||||
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev);
|
||||
void st_magn_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
int st_magn_allocate_ring(struct iio_dev *indio_dev);
|
||||
void st_magn_deallocate_ring(struct iio_dev *indio_dev);
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
static inline int st_magn_probe_trigger(struct iio_dev *indio_dev, int irq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void st_magn_remove_trigger(struct iio_dev *indio_dev, int irq)
|
||||
{
|
||||
return;
|
||||
}
|
||||
static inline int st_magn_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void st_magn_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#endif /* ST_MAGN_H */
|
98
drivers/iio/magnetometer/st_magn_buffer.c
Normal file
98
drivers/iio/magnetometer/st_magn_buffer.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* STMicroelectronics magnetometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_magn.h"
|
||||
|
||||
static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_magn_set_enable_error;
|
||||
|
||||
err = iio_sw_buffer_preenable(indio_dev);
|
||||
|
||||
st_magn_set_enable_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
|
||||
mdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (mdata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_buffer_postenable_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_magn_buffer_postenable_error:
|
||||
kfree(mdata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
|
||||
st_magn_buffer_predisable_error:
|
||||
kfree(mdata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = {
|
||||
.preenable = &st_magn_buffer_preenable,
|
||||
.postenable = &st_magn_buffer_postenable,
|
||||
.predisable = &st_magn_buffer_predisable,
|
||||
};
|
||||
|
||||
int st_magn_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&st_sensors_trigger_handler, &st_magn_buffer_setup_ops);
|
||||
}
|
||||
|
||||
void st_magn_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics magnetometers buffer");
|
||||
MODULE_LICENSE("GPL v2");
|
400
drivers/iio/magnetometer/st_magn_core.c
Normal file
400
drivers/iio/magnetometer/st_magn_core.c
Normal file
@ -0,0 +1,400 @@
|
||||
/*
|
||||
* STMicroelectronics magnetometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_magn.h"
|
||||
|
||||
/* DEFAULT VALUE FOR SENSORS */
|
||||
#define ST_MAGN_DEFAULT_OUT_X_L_ADDR 0X04
|
||||
#define ST_MAGN_DEFAULT_OUT_Y_L_ADDR 0X08
|
||||
#define ST_MAGN_DEFAULT_OUT_Z_L_ADDR 0X06
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_MAGN_FS_AVL_1300MG 1300
|
||||
#define ST_MAGN_FS_AVL_1900MG 1900
|
||||
#define ST_MAGN_FS_AVL_2500MG 2500
|
||||
#define ST_MAGN_FS_AVL_4000MG 4000
|
||||
#define ST_MAGN_FS_AVL_4700MG 4700
|
||||
#define ST_MAGN_FS_AVL_5600MG 5600
|
||||
#define ST_MAGN_FS_AVL_8000MG 8000
|
||||
#define ST_MAGN_FS_AVL_8100MG 8100
|
||||
#define ST_MAGN_FS_AVL_10000MG 10000
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||
#define ST_MAGN_1_WAI_EXP 0x3c
|
||||
#define ST_MAGN_1_ODR_ADDR 0x00
|
||||
#define ST_MAGN_1_ODR_MASK 0x1c
|
||||
#define ST_MAGN_1_ODR_AVL_1HZ_VAL 0x00
|
||||
#define ST_MAGN_1_ODR_AVL_2HZ_VAL 0x01
|
||||
#define ST_MAGN_1_ODR_AVL_3HZ_VAL 0x02
|
||||
#define ST_MAGN_1_ODR_AVL_8HZ_VAL 0x03
|
||||
#define ST_MAGN_1_ODR_AVL_15HZ_VAL 0x04
|
||||
#define ST_MAGN_1_ODR_AVL_30HZ_VAL 0x05
|
||||
#define ST_MAGN_1_ODR_AVL_75HZ_VAL 0x06
|
||||
#define ST_MAGN_1_ODR_AVL_220HZ_VAL 0x07
|
||||
#define ST_MAGN_1_PW_ADDR 0x02
|
||||
#define ST_MAGN_1_PW_MASK 0x03
|
||||
#define ST_MAGN_1_PW_ON 0x00
|
||||
#define ST_MAGN_1_PW_OFF 0x03
|
||||
#define ST_MAGN_1_FS_ADDR 0x01
|
||||
#define ST_MAGN_1_FS_MASK 0xe0
|
||||
#define ST_MAGN_1_FS_AVL_1300_VAL 0x01
|
||||
#define ST_MAGN_1_FS_AVL_1900_VAL 0x02
|
||||
#define ST_MAGN_1_FS_AVL_2500_VAL 0x03
|
||||
#define ST_MAGN_1_FS_AVL_4000_VAL 0x04
|
||||
#define ST_MAGN_1_FS_AVL_4700_VAL 0x05
|
||||
#define ST_MAGN_1_FS_AVL_5600_VAL 0x06
|
||||
#define ST_MAGN_1_FS_AVL_8100_VAL 0x07
|
||||
#define ST_MAGN_1_FS_AVL_1300_GAIN_XY 1100
|
||||
#define ST_MAGN_1_FS_AVL_1900_GAIN_XY 855
|
||||
#define ST_MAGN_1_FS_AVL_2500_GAIN_XY 670
|
||||
#define ST_MAGN_1_FS_AVL_4000_GAIN_XY 450
|
||||
#define ST_MAGN_1_FS_AVL_4700_GAIN_XY 400
|
||||
#define ST_MAGN_1_FS_AVL_5600_GAIN_XY 330
|
||||
#define ST_MAGN_1_FS_AVL_8100_GAIN_XY 230
|
||||
#define ST_MAGN_1_FS_AVL_1300_GAIN_Z 980
|
||||
#define ST_MAGN_1_FS_AVL_1900_GAIN_Z 760
|
||||
#define ST_MAGN_1_FS_AVL_2500_GAIN_Z 600
|
||||
#define ST_MAGN_1_FS_AVL_4000_GAIN_Z 400
|
||||
#define ST_MAGN_1_FS_AVL_4700_GAIN_Z 355
|
||||
#define ST_MAGN_1_FS_AVL_5600_GAIN_Z 295
|
||||
#define ST_MAGN_1_FS_AVL_8100_GAIN_Z 205
|
||||
#define ST_MAGN_1_MULTIREAD_BIT false
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 2 */
|
||||
#define ST_MAGN_2_WAI_EXP 0x3d
|
||||
#define ST_MAGN_2_ODR_ADDR 0x20
|
||||
#define ST_MAGN_2_ODR_MASK 0x1c
|
||||
#define ST_MAGN_2_ODR_AVL_1HZ_VAL 0x00
|
||||
#define ST_MAGN_2_ODR_AVL_2HZ_VAL 0x01
|
||||
#define ST_MAGN_2_ODR_AVL_3HZ_VAL 0x02
|
||||
#define ST_MAGN_2_ODR_AVL_5HZ_VAL 0x03
|
||||
#define ST_MAGN_2_ODR_AVL_10HZ_VAL 0x04
|
||||
#define ST_MAGN_2_ODR_AVL_20HZ_VAL 0x05
|
||||
#define ST_MAGN_2_ODR_AVL_40HZ_VAL 0x06
|
||||
#define ST_MAGN_2_ODR_AVL_80HZ_VAL 0x07
|
||||
#define ST_MAGN_2_PW_ADDR 0x22
|
||||
#define ST_MAGN_2_PW_MASK 0x03
|
||||
#define ST_MAGN_2_PW_ON 0x00
|
||||
#define ST_MAGN_2_PW_OFF 0x03
|
||||
#define ST_MAGN_2_FS_ADDR 0x21
|
||||
#define ST_MAGN_2_FS_MASK 0x60
|
||||
#define ST_MAGN_2_FS_AVL_4000_VAL 0x00
|
||||
#define ST_MAGN_2_FS_AVL_8000_VAL 0x01
|
||||
#define ST_MAGN_2_FS_AVL_10000_VAL 0x02
|
||||
#define ST_MAGN_2_FS_AVL_4000_GAIN 430
|
||||
#define ST_MAGN_2_FS_AVL_8000_GAIN 230
|
||||
#define ST_MAGN_2_FS_AVL_10000_GAIN 230
|
||||
#define ST_MAGN_2_MULTIREAD_BIT false
|
||||
#define ST_MAGN_2_OUT_X_L_ADDR 0x28
|
||||
#define ST_MAGN_2_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
static const struct iio_chan_spec st_magn_16bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Z_L_ADDR),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE,
|
||||
ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Z_L_ADDR),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_magn_sensors[] = {
|
||||
{
|
||||
.wai = ST_MAGN_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LSM303DLHC_MAGN_DEV_NAME,
|
||||
[1] = LSM303DLM_MAGN_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_MAGN_1_ODR_ADDR,
|
||||
.mask = ST_MAGN_1_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_MAGN_1_ODR_AVL_1HZ_VAL, },
|
||||
{ 2, ST_MAGN_1_ODR_AVL_2HZ_VAL, },
|
||||
{ 3, ST_MAGN_1_ODR_AVL_3HZ_VAL, },
|
||||
{ 8, ST_MAGN_1_ODR_AVL_8HZ_VAL, },
|
||||
{ 15, ST_MAGN_1_ODR_AVL_15HZ_VAL, },
|
||||
{ 30, ST_MAGN_1_ODR_AVL_30HZ_VAL, },
|
||||
{ 75, ST_MAGN_1_ODR_AVL_75HZ_VAL, },
|
||||
{ 220, ST_MAGN_1_ODR_AVL_220HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_MAGN_1_PW_ADDR,
|
||||
.mask = ST_MAGN_1_PW_MASK,
|
||||
.value_on = ST_MAGN_1_PW_ON,
|
||||
.value_off = ST_MAGN_1_PW_OFF,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_MAGN_1_FS_ADDR,
|
||||
.mask = ST_MAGN_1_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_MAGN_FS_AVL_1300MG,
|
||||
.value = ST_MAGN_1_FS_AVL_1300_VAL,
|
||||
.gain = ST_MAGN_1_FS_AVL_1300_GAIN_XY,
|
||||
.gain2 = ST_MAGN_1_FS_AVL_1300_GAIN_Z,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_MAGN_FS_AVL_1900MG,
|
||||
.value = ST_MAGN_1_FS_AVL_1900_VAL,
|
||||
.gain = ST_MAGN_1_FS_AVL_1900_GAIN_XY,
|
||||
.gain2 = ST_MAGN_1_FS_AVL_1900_GAIN_Z,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_MAGN_FS_AVL_2500MG,
|
||||
.value = ST_MAGN_1_FS_AVL_2500_VAL,
|
||||
.gain = ST_MAGN_1_FS_AVL_2500_GAIN_XY,
|
||||
.gain2 = ST_MAGN_1_FS_AVL_2500_GAIN_Z,
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_MAGN_FS_AVL_4000MG,
|
||||
.value = ST_MAGN_1_FS_AVL_4000_VAL,
|
||||
.gain = ST_MAGN_1_FS_AVL_4000_GAIN_XY,
|
||||
.gain2 = ST_MAGN_1_FS_AVL_4000_GAIN_Z,
|
||||
},
|
||||
[4] = {
|
||||
.num = ST_MAGN_FS_AVL_4700MG,
|
||||
.value = ST_MAGN_1_FS_AVL_4700_VAL,
|
||||
.gain = ST_MAGN_1_FS_AVL_4700_GAIN_XY,
|
||||
.gain2 = ST_MAGN_1_FS_AVL_4700_GAIN_Z,
|
||||
},
|
||||
[5] = {
|
||||
.num = ST_MAGN_FS_AVL_5600MG,
|
||||
.value = ST_MAGN_1_FS_AVL_5600_VAL,
|
||||
.gain = ST_MAGN_1_FS_AVL_5600_GAIN_XY,
|
||||
.gain2 = ST_MAGN_1_FS_AVL_5600_GAIN_Z,
|
||||
},
|
||||
[6] = {
|
||||
.num = ST_MAGN_FS_AVL_8100MG,
|
||||
.value = ST_MAGN_1_FS_AVL_8100_VAL,
|
||||
.gain = ST_MAGN_1_FS_AVL_8100_GAIN_XY,
|
||||
.gain2 = ST_MAGN_1_FS_AVL_8100_GAIN_Z,
|
||||
},
|
||||
},
|
||||
},
|
||||
.multi_read_bit = ST_MAGN_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_MAGN_2_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS3MDL_MAGN_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_MAGN_2_ODR_ADDR,
|
||||
.mask = ST_MAGN_2_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_MAGN_2_ODR_AVL_1HZ_VAL, },
|
||||
{ 2, ST_MAGN_2_ODR_AVL_2HZ_VAL, },
|
||||
{ 3, ST_MAGN_2_ODR_AVL_3HZ_VAL, },
|
||||
{ 5, ST_MAGN_2_ODR_AVL_5HZ_VAL, },
|
||||
{ 10, ST_MAGN_2_ODR_AVL_10HZ_VAL, },
|
||||
{ 20, ST_MAGN_2_ODR_AVL_20HZ_VAL, },
|
||||
{ 40, ST_MAGN_2_ODR_AVL_40HZ_VAL, },
|
||||
{ 80, ST_MAGN_2_ODR_AVL_80HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_MAGN_2_PW_ADDR,
|
||||
.mask = ST_MAGN_2_PW_MASK,
|
||||
.value_on = ST_MAGN_2_PW_ON,
|
||||
.value_off = ST_MAGN_2_PW_OFF,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_MAGN_2_FS_ADDR,
|
||||
.mask = ST_MAGN_2_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_MAGN_FS_AVL_4000MG,
|
||||
.value = ST_MAGN_2_FS_AVL_4000_VAL,
|
||||
.gain = ST_MAGN_2_FS_AVL_4000_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_MAGN_FS_AVL_8000MG,
|
||||
.value = ST_MAGN_2_FS_AVL_8000_VAL,
|
||||
.gain = ST_MAGN_2_FS_AVL_8000_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_MAGN_FS_AVL_10000MG,
|
||||
.value = ST_MAGN_2_FS_AVL_10000_VAL,
|
||||
.gain = ST_MAGN_2_FS_AVL_10000_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_magn_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = st_sensors_read_info_raw(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
if ((ch->scan_index == ST_SENSORS_SCAN_Z) &&
|
||||
(mdata->current_fullscale->gain2 != 0))
|
||||
*val2 = mdata->current_fullscale->gain2;
|
||||
else
|
||||
*val2 = mdata->current_fullscale->gain;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
read_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_magn_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
err = st_sensors_set_fullscale_by_gain(indio_dev, val2);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ST_SENSOR_DEV_ATTR_SAMP_FREQ();
|
||||
static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
|
||||
static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_magn_scale_available);
|
||||
|
||||
static struct attribute *st_magn_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_magn_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_magn_attribute_group = {
|
||||
.attrs = st_magn_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info magn_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_magn_attribute_group,
|
||||
.read_raw = &st_magn_read_raw,
|
||||
.write_raw = &st_magn_write_raw,
|
||||
};
|
||||
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &magn_info;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_magn_sensors), st_magn_sensors);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
|
||||
mdata->multiread_bit = mdata->sensor->multi_read_bit;
|
||||
indio_dev->channels = mdata->sensor->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
mdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&mdata->sensor->fs.fs_avl[0];
|
||||
mdata->odr = mdata->sensor->odr.odr_avl[0].hz;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
err = st_magn_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
err = st_sensors_allocate_trigger(indio_dev, NULL);
|
||||
if (err < 0)
|
||||
goto st_magn_probe_trigger_error;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto st_magn_device_register_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_magn_device_register_error:
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_magn_probe_trigger_error:
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
st_magn_common_probe_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_magn_common_probe);
|
||||
|
||||
void st_magn_common_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
}
|
||||
iio_device_free(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_magn_common_remove);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics magnetometers driver");
|
||||
MODULE_LICENSE("GPL v2");
|
80
drivers/iio/magnetometer/st_magn_i2c.c
Normal file
80
drivers/iio/magnetometer/st_magn_i2c.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* STMicroelectronics magnetometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_magn.h"
|
||||
|
||||
static int st_magn_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *mdata;
|
||||
int err;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*mdata));
|
||||
if (indio_dev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto iio_device_alloc_error;
|
||||
}
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
mdata->dev = &client->dev;
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, mdata);
|
||||
|
||||
err = st_magn_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
|
||||
return 0;
|
||||
|
||||
st_magn_common_probe_error:
|
||||
iio_device_free(indio_dev);
|
||||
iio_device_alloc_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_magn_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
st_magn_common_remove(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_magn_id_table[] = {
|
||||
{ LSM303DLHC_MAGN_DEV_NAME },
|
||||
{ LSM303DLM_MAGN_DEV_NAME },
|
||||
{ LIS3MDL_MAGN_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
|
||||
|
||||
static struct i2c_driver st_magn_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-magn-i2c",
|
||||
},
|
||||
.probe = st_magn_i2c_probe,
|
||||
.remove = st_magn_i2c_remove,
|
||||
.id_table = st_magn_id_table,
|
||||
};
|
||||
module_i2c_driver(st_magn_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics magnetometers i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
79
drivers/iio/magnetometer/st_magn_spi.c
Normal file
79
drivers/iio/magnetometer/st_magn_spi.c
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* STMicroelectronics magnetometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_magn.h"
|
||||
|
||||
static int st_magn_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *mdata;
|
||||
int err;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*mdata));
|
||||
if (indio_dev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto iio_device_alloc_error;
|
||||
}
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
mdata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, mdata);
|
||||
|
||||
err = st_magn_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
|
||||
return 0;
|
||||
|
||||
st_magn_common_probe_error:
|
||||
iio_device_free(indio_dev);
|
||||
iio_device_alloc_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_magn_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
st_magn_common_remove(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id st_magn_id_table[] = {
|
||||
{ LSM303DLHC_MAGN_DEV_NAME },
|
||||
{ LSM303DLM_MAGN_DEV_NAME },
|
||||
{ LIS3MDL_MAGN_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
|
||||
|
||||
static struct spi_driver st_magn_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-magn-spi",
|
||||
},
|
||||
.probe = st_magn_spi_probe,
|
||||
.remove = st_magn_spi_remove,
|
||||
.id_table = st_magn_id_table,
|
||||
};
|
||||
module_spi_driver(st_magn_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics magnetometers spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,3 +1,7 @@
|
||||
if X86
|
||||
source "drivers/platform/x86/Kconfig"
|
||||
endif
|
||||
if GOLDFISH
|
||||
source "drivers/platform/goldfish/Kconfig"
|
||||
endif
|
||||
|
||||
|
@ -4,3 +4,4 @@
|
||||
|
||||
obj-$(CONFIG_X86) += x86/
|
||||
obj-$(CONFIG_OLPC) += olpc/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
|
5
drivers/platform/goldfish/Kconfig
Normal file
5
drivers/platform/goldfish/Kconfig
Normal file
@ -0,0 +1,5 @@
|
||||
config GOLDFISH_PIPE
|
||||
tristate "Goldfish virtual device for QEMU pipes"
|
||||
---help---
|
||||
This is a virtual device to drive the QEMU pipe interface used by
|
||||
the Goldfish Android Virtual Device.
|
5
drivers/platform/goldfish/Makefile
Normal file
5
drivers/platform/goldfish/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for Goldfish platform specific drivers
|
||||
#
|
||||
obj-$(CONFIG_GOLDFISH) += pdev_bus.o
|
||||
obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe.o
|
612
drivers/platform/goldfish/goldfish_pipe.c
Normal file
612
drivers/platform/goldfish/goldfish_pipe.c
Normal file
@ -0,0 +1,612 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
* Copyright (C) 2012 Intel, Inc.
|
||||
* Copyright (C) 2013 Intel, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* This source file contains the implementation of a special device driver
|
||||
* that intends to provide a *very* fast communication channel between the
|
||||
* guest system and the QEMU emulator.
|
||||
*
|
||||
* Usage from the guest is simply the following (error handling simplified):
|
||||
*
|
||||
* int fd = open("/dev/qemu_pipe",O_RDWR);
|
||||
* .... write() or read() through the pipe.
|
||||
*
|
||||
* This driver doesn't deal with the exact protocol used during the session.
|
||||
* It is intended to be as simple as something like:
|
||||
*
|
||||
* // do this _just_ after opening the fd to connect to a specific
|
||||
* // emulator service.
|
||||
* const char* msg = "<pipename>";
|
||||
* if (write(fd, msg, strlen(msg)+1) < 0) {
|
||||
* ... could not connect to <pipename> service
|
||||
* close(fd);
|
||||
* }
|
||||
*
|
||||
* // after this, simply read() and write() to communicate with the
|
||||
* // service. Exact protocol details left as an exercise to the reader.
|
||||
*
|
||||
* This driver is very fast because it doesn't copy any data through
|
||||
* intermediate buffers, since the emulator is capable of translating
|
||||
* guest user addresses into host ones.
|
||||
*
|
||||
* Note that we must however ensure that each user page involved in the
|
||||
* exchange is properly mapped during a transfer.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
/*
|
||||
* IMPORTANT: The following constants must match the ones used and defined
|
||||
* in external/qemu/hw/goldfish_pipe.c in the Android source tree.
|
||||
*/
|
||||
|
||||
/* pipe device registers */
|
||||
#define PIPE_REG_COMMAND 0x00 /* write: value = command */
|
||||
#define PIPE_REG_STATUS 0x04 /* read */
|
||||
#define PIPE_REG_CHANNEL 0x08 /* read/write: channel id */
|
||||
#define PIPE_REG_SIZE 0x0c /* read/write: buffer size */
|
||||
#define PIPE_REG_ADDRESS 0x10 /* write: physical address */
|
||||
#define PIPE_REG_WAKES 0x14 /* read: wake flags */
|
||||
#define PIPE_REG_PARAMS_ADDR_LOW 0x18 /* read/write: batch data address */
|
||||
#define PIPE_REG_PARAMS_ADDR_HIGH 0x1c /* read/write: batch data address */
|
||||
#define PIPE_REG_ACCESS_PARAMS 0x20 /* write: batch access */
|
||||
|
||||
/* list of commands for PIPE_REG_COMMAND */
|
||||
#define CMD_OPEN 1 /* open new channel */
|
||||
#define CMD_CLOSE 2 /* close channel (from guest) */
|
||||
#define CMD_POLL 3 /* poll read/write status */
|
||||
|
||||
/* List of bitflags returned in status of CMD_POLL command */
|
||||
#define PIPE_POLL_IN (1 << 0)
|
||||
#define PIPE_POLL_OUT (1 << 1)
|
||||
#define PIPE_POLL_HUP (1 << 2)
|
||||
|
||||
/* The following commands are related to write operations */
|
||||
#define CMD_WRITE_BUFFER 4 /* send a user buffer to the emulator */
|
||||
#define CMD_WAKE_ON_WRITE 5 /* tell the emulator to wake us when writing
|
||||
is possible */
|
||||
|
||||
/* The following commands are related to read operations, they must be
|
||||
* listed in the same order than the corresponding write ones, since we
|
||||
* will use (CMD_READ_BUFFER - CMD_WRITE_BUFFER) as a special offset
|
||||
* in goldfish_pipe_read_write() below.
|
||||
*/
|
||||
#define CMD_READ_BUFFER 6 /* receive a user buffer from the emulator */
|
||||
#define CMD_WAKE_ON_READ 7 /* tell the emulator to wake us when reading
|
||||
* is possible */
|
||||
|
||||
/* Possible status values used to signal errors - see goldfish_pipe_error_convert */
|
||||
#define PIPE_ERROR_INVAL -1
|
||||
#define PIPE_ERROR_AGAIN -2
|
||||
#define PIPE_ERROR_NOMEM -3
|
||||
#define PIPE_ERROR_IO -4
|
||||
|
||||
/* Bit-flags used to signal events from the emulator */
|
||||
#define PIPE_WAKE_CLOSED (1 << 0) /* emulator closed pipe */
|
||||
#define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */
|
||||
#define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */
|
||||
|
||||
struct access_params {
|
||||
u32 channel;
|
||||
u32 size;
|
||||
u32 address;
|
||||
u32 cmd;
|
||||
u32 result;
|
||||
/* reserved for future extension */
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/* The global driver data. Holds a reference to the i/o page used to
|
||||
* communicate with the emulator, and a wake queue for blocked tasks
|
||||
* waiting to be awoken.
|
||||
*/
|
||||
struct goldfish_pipe_dev {
|
||||
spinlock_t lock;
|
||||
unsigned char __iomem *base;
|
||||
struct access_params *aps;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static struct goldfish_pipe_dev pipe_dev[1];
|
||||
|
||||
/* This data type models a given pipe instance */
|
||||
struct goldfish_pipe {
|
||||
struct goldfish_pipe_dev *dev;
|
||||
struct mutex lock;
|
||||
unsigned long flags;
|
||||
wait_queue_head_t wake_queue;
|
||||
};
|
||||
|
||||
|
||||
/* Bit flags for the 'flags' field */
|
||||
enum {
|
||||
BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */
|
||||
BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */
|
||||
BIT_WAKE_ON_READ = 2, /* want to be woken on reads */
|
||||
};
|
||||
|
||||
|
||||
static u32 goldfish_cmd_status(struct goldfish_pipe *pipe, u32 cmd)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 status;
|
||||
struct goldfish_pipe_dev *dev = pipe->dev;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
writel((u32)pipe, dev->base + PIPE_REG_CHANNEL);
|
||||
writel(cmd, dev->base + PIPE_REG_COMMAND);
|
||||
status = readl(dev->base + PIPE_REG_STATUS);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void goldfish_cmd(struct goldfish_pipe *pipe, u32 cmd)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct goldfish_pipe_dev *dev = pipe->dev;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
writel((u32)pipe, dev->base + PIPE_REG_CHANNEL);
|
||||
writel(cmd, dev->base + PIPE_REG_COMMAND);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
/* This function converts an error code returned by the emulator through
|
||||
* the PIPE_REG_STATUS i/o register into a valid negative errno value.
|
||||
*/
|
||||
static int goldfish_pipe_error_convert(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case PIPE_ERROR_AGAIN:
|
||||
return -EAGAIN;
|
||||
case PIPE_ERROR_NOMEM:
|
||||
return -ENOMEM;
|
||||
case PIPE_ERROR_IO:
|
||||
return -EIO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notice: QEMU will return 0 for un-known register access, indicating
|
||||
* param_acess is supported or not
|
||||
*/
|
||||
static int valid_batchbuffer_addr(struct goldfish_pipe_dev *dev,
|
||||
struct access_params *aps)
|
||||
{
|
||||
u32 aph, apl;
|
||||
u64 paddr;
|
||||
aph = readl(dev->base + PIPE_REG_PARAMS_ADDR_HIGH);
|
||||
apl = readl(dev->base + PIPE_REG_PARAMS_ADDR_LOW);
|
||||
|
||||
paddr = ((u64)aph << 32) | apl;
|
||||
if (paddr != (__pa(aps)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 0 on success */
|
||||
static int setup_access_params_addr(struct platform_device *pdev,
|
||||
struct goldfish_pipe_dev *dev)
|
||||
{
|
||||
u64 paddr;
|
||||
struct access_params *aps;
|
||||
|
||||
aps = devm_kzalloc(&pdev->dev, sizeof(struct access_params), GFP_KERNEL);
|
||||
if (!aps)
|
||||
return -1;
|
||||
|
||||
/* FIXME */
|
||||
paddr = __pa(aps);
|
||||
writel((u32)(paddr >> 32), dev->base + PIPE_REG_PARAMS_ADDR_HIGH);
|
||||
writel((u32)paddr, dev->base + PIPE_REG_PARAMS_ADDR_LOW);
|
||||
|
||||
if (valid_batchbuffer_addr(dev, aps)) {
|
||||
dev->aps = aps;
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* A value that will not be set by qemu emulator */
|
||||
#define INITIAL_BATCH_RESULT (0xdeadbeaf)
|
||||
static int access_with_param(struct goldfish_pipe_dev *dev, const int cmd,
|
||||
unsigned long address, unsigned long avail,
|
||||
struct goldfish_pipe *pipe, int *status)
|
||||
{
|
||||
struct access_params *aps = dev->aps;
|
||||
|
||||
if (aps == NULL)
|
||||
return -1;
|
||||
|
||||
aps->result = INITIAL_BATCH_RESULT;
|
||||
aps->channel = (unsigned long)pipe;
|
||||
aps->size = avail;
|
||||
aps->address = address;
|
||||
aps->cmd = cmd;
|
||||
writel(cmd, dev->base + PIPE_REG_ACCESS_PARAMS);
|
||||
/*
|
||||
* If the aps->result has not changed, that means
|
||||
* that the batch command failed
|
||||
*/
|
||||
if (aps->result == INITIAL_BATCH_RESULT)
|
||||
return -1;
|
||||
*status = aps->result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function is used for both reading from and writing to a given
|
||||
* pipe.
|
||||
*/
|
||||
static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
|
||||
size_t bufflen, int is_write)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
struct goldfish_pipe *pipe = filp->private_data;
|
||||
struct goldfish_pipe_dev *dev = pipe->dev;
|
||||
const int cmd_offset = is_write ? 0
|
||||
: (CMD_READ_BUFFER - CMD_WRITE_BUFFER);
|
||||
unsigned long address, address_end;
|
||||
int ret = 0;
|
||||
|
||||
/* If the emulator already closed the pipe, no need to go further */
|
||||
if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
|
||||
return -EIO;
|
||||
|
||||
/* Null reads or writes succeeds */
|
||||
if (unlikely(bufflen) == 0)
|
||||
return 0;
|
||||
|
||||
/* Check the buffer range for access */
|
||||
if (!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ,
|
||||
buffer, bufflen))
|
||||
return -EFAULT;
|
||||
|
||||
/* Serialize access to the pipe */
|
||||
if (mutex_lock_interruptible(&pipe->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
address = (unsigned long)(void *)buffer;
|
||||
address_end = address + bufflen;
|
||||
|
||||
while (address < address_end) {
|
||||
unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
|
||||
unsigned long next = page_end < address_end ? page_end
|
||||
: address_end;
|
||||
unsigned long avail = next - address;
|
||||
int status, wakeBit;
|
||||
|
||||
/* Ensure that the corresponding page is properly mapped */
|
||||
/* FIXME: this isn't safe or sufficient - use get_user_pages */
|
||||
if (is_write) {
|
||||
char c;
|
||||
/* Ensure that the page is mapped and readable */
|
||||
if (__get_user(c, (char __user *)address)) {
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Ensure that the page is mapped and writable */
|
||||
if (__put_user(0, (char __user *)address)) {
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, try to transfer the bytes in the current page */
|
||||
spin_lock_irqsave(&dev->lock, irq_flags);
|
||||
if (access_with_param(dev, CMD_WRITE_BUFFER + cmd_offset,
|
||||
address, avail, pipe, &status)) {
|
||||
writel((u32)pipe, dev->base + PIPE_REG_CHANNEL);
|
||||
writel(avail, dev->base + PIPE_REG_SIZE);
|
||||
writel(address, dev->base + PIPE_REG_ADDRESS);
|
||||
writel(CMD_WRITE_BUFFER + cmd_offset,
|
||||
dev->base + PIPE_REG_COMMAND);
|
||||
status = readl(dev->base + PIPE_REG_STATUS);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, irq_flags);
|
||||
|
||||
if (status > 0) { /* Correct transfer */
|
||||
ret += status;
|
||||
address += status;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status == 0) /* EOF */
|
||||
break;
|
||||
|
||||
/* An error occured. If we already transfered stuff, just
|
||||
* return with its count. We expect the next call to return
|
||||
* an error code */
|
||||
if (ret > 0)
|
||||
break;
|
||||
|
||||
/* If the error is not PIPE_ERROR_AGAIN, or if we are not in
|
||||
* non-blocking mode, just return the error code.
|
||||
*/
|
||||
if (status != PIPE_ERROR_AGAIN ||
|
||||
(filp->f_flags & O_NONBLOCK) != 0) {
|
||||
ret = goldfish_pipe_error_convert(status);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We will have to wait until more data/space is available.
|
||||
* First, mark the pipe as waiting for a specific wake signal.
|
||||
*/
|
||||
wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;
|
||||
set_bit(wakeBit, &pipe->flags);
|
||||
|
||||
/* Tell the emulator we're going to wait for a wake event */
|
||||
goldfish_cmd(pipe, CMD_WAKE_ON_WRITE + cmd_offset);
|
||||
|
||||
/* Unlock the pipe, then wait for the wake signal */
|
||||
mutex_unlock(&pipe->lock);
|
||||
|
||||
while (test_bit(wakeBit, &pipe->flags)) {
|
||||
if (wait_event_interruptible(
|
||||
pipe->wake_queue,
|
||||
!test_bit(wakeBit, &pipe->flags)))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Try to re-acquire the lock */
|
||||
if (mutex_lock_interruptible(&pipe->lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* Try the transfer again */
|
||||
continue;
|
||||
}
|
||||
mutex_unlock(&pipe->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer,
|
||||
size_t bufflen, loff_t *ppos)
|
||||
{
|
||||
return goldfish_pipe_read_write(filp, buffer, bufflen, 0);
|
||||
}
|
||||
|
||||
static ssize_t goldfish_pipe_write(struct file *filp,
|
||||
const char __user *buffer, size_t bufflen,
|
||||
loff_t *ppos)
|
||||
{
|
||||
return goldfish_pipe_read_write(filp, (char __user *)buffer,
|
||||
bufflen, 1);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int goldfish_pipe_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
struct goldfish_pipe *pipe = filp->private_data;
|
||||
unsigned int mask = 0;
|
||||
int status;
|
||||
|
||||
mutex_lock(&pipe->lock);
|
||||
|
||||
poll_wait(filp, &pipe->wake_queue, wait);
|
||||
|
||||
status = goldfish_cmd_status(pipe, CMD_POLL);
|
||||
|
||||
mutex_unlock(&pipe->lock);
|
||||
|
||||
if (status & PIPE_POLL_IN)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
if (status & PIPE_POLL_OUT)
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
|
||||
if (status & PIPE_POLL_HUP)
|
||||
mask |= POLLHUP;
|
||||
|
||||
if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
|
||||
mask |= POLLERR;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct goldfish_pipe_dev *dev = dev_id;
|
||||
unsigned long irq_flags;
|
||||
int count = 0;
|
||||
|
||||
/* We're going to read from the emulator a list of (channel,flags)
|
||||
* pairs corresponding to the wake events that occured on each
|
||||
* blocked pipe (i.e. channel).
|
||||
*/
|
||||
spin_lock_irqsave(&dev->lock, irq_flags);
|
||||
for (;;) {
|
||||
/* First read the channel, 0 means the end of the list */
|
||||
struct goldfish_pipe *pipe;
|
||||
unsigned long wakes;
|
||||
unsigned long channel = readl(dev->base + PIPE_REG_CHANNEL);
|
||||
|
||||
if (channel == 0)
|
||||
break;
|
||||
|
||||
/* Convert channel to struct pipe pointer + read wake flags */
|
||||
wakes = readl(dev->base + PIPE_REG_WAKES);
|
||||
pipe = (struct goldfish_pipe *)(ptrdiff_t)channel;
|
||||
|
||||
/* Did the emulator just closed a pipe? */
|
||||
if (wakes & PIPE_WAKE_CLOSED) {
|
||||
set_bit(BIT_CLOSED_ON_HOST, &pipe->flags);
|
||||
wakes |= PIPE_WAKE_READ | PIPE_WAKE_WRITE;
|
||||
}
|
||||
if (wakes & PIPE_WAKE_READ)
|
||||
clear_bit(BIT_WAKE_ON_READ, &pipe->flags);
|
||||
if (wakes & PIPE_WAKE_WRITE)
|
||||
clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags);
|
||||
|
||||
wake_up_interruptible(&pipe->wake_queue);
|
||||
count++;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, irq_flags);
|
||||
|
||||
return (count == 0) ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* goldfish_pipe_open - open a channel to the AVD
|
||||
* @inode: inode of device
|
||||
* @file: file struct of opener
|
||||
*
|
||||
* Create a new pipe link between the emulator and the use application.
|
||||
* Each new request produces a new pipe.
|
||||
*
|
||||
* Note: we use the pipe ID as a mux. All goldfish emulations are 32bit
|
||||
* right now so this is fine. A move to 64bit will need this addressing
|
||||
*/
|
||||
static int goldfish_pipe_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct goldfish_pipe *pipe;
|
||||
struct goldfish_pipe_dev *dev = pipe_dev;
|
||||
int32_t status;
|
||||
|
||||
/* Allocate new pipe kernel object */
|
||||
pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
|
||||
if (pipe == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe->dev = dev;
|
||||
mutex_init(&pipe->lock);
|
||||
init_waitqueue_head(&pipe->wake_queue);
|
||||
|
||||
/*
|
||||
* Now, tell the emulator we're opening a new pipe. We use the
|
||||
* pipe object's address as the channel identifier for simplicity.
|
||||
*/
|
||||
|
||||
status = goldfish_cmd_status(pipe, CMD_OPEN);
|
||||
if (status < 0) {
|
||||
kfree(pipe);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* All is done, save the pipe into the file's private data field */
|
||||
file->private_data = pipe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goldfish_pipe_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct goldfish_pipe *pipe = filp->private_data;
|
||||
|
||||
/* The guest is closing the channel, so tell the emulator right now */
|
||||
goldfish_cmd(pipe, CMD_CLOSE);
|
||||
kfree(pipe);
|
||||
filp->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations goldfish_pipe_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = goldfish_pipe_read,
|
||||
.write = goldfish_pipe_write,
|
||||
.poll = goldfish_pipe_poll,
|
||||
.open = goldfish_pipe_open,
|
||||
.release = goldfish_pipe_release,
|
||||
};
|
||||
|
||||
static struct miscdevice goldfish_pipe_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "goldfish_pipe",
|
||||
.fops = &goldfish_pipe_fops,
|
||||
};
|
||||
|
||||
static int goldfish_pipe_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err;
|
||||
struct resource *r;
|
||||
struct goldfish_pipe_dev *dev = pipe_dev;
|
||||
|
||||
/* not thread safe, but this should not happen */
|
||||
WARN_ON(dev->base != NULL);
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (r == NULL || resource_size(r) < PAGE_SIZE) {
|
||||
dev_err(&pdev->dev, "can't allocate i/o page\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
|
||||
if (dev->base == NULL) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (r == NULL) {
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
dev->irq = r->start;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
|
||||
IRQF_SHARED, "goldfish_pipe", dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to allocate IRQ\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = misc_register(&goldfish_pipe_device);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to register device\n");
|
||||
goto error;
|
||||
}
|
||||
setup_access_params_addr(pdev, dev);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dev->base = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int goldfish_pipe_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct goldfish_pipe_dev *dev = pipe_dev;
|
||||
misc_deregister(&goldfish_pipe_device);
|
||||
dev->base = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver goldfish_pipe = {
|
||||
.probe = goldfish_pipe_probe,
|
||||
.remove = goldfish_pipe_remove,
|
||||
.driver = {
|
||||
.name = "goldfish_pipe"
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(goldfish_pipe);
|
||||
MODULE_AUTHOR("David Turner <digit@google.com>");
|
||||
MODULE_LICENSE("GPL");
|
240
drivers/platform/goldfish/pdev_bus.c
Normal file
240
drivers/platform/goldfish/pdev_bus.c
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
* Copyright (C) 2011 Intel, Inc.
|
||||
* Copyright (C) 2013 Intel, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define PDEV_BUS_OP_DONE (0x00)
|
||||
#define PDEV_BUS_OP_REMOVE_DEV (0x04)
|
||||
#define PDEV_BUS_OP_ADD_DEV (0x08)
|
||||
|
||||
#define PDEV_BUS_OP_INIT (0x00)
|
||||
|
||||
#define PDEV_BUS_OP (0x00)
|
||||
#define PDEV_BUS_GET_NAME (0x04)
|
||||
#define PDEV_BUS_NAME_LEN (0x08)
|
||||
#define PDEV_BUS_ID (0x0c)
|
||||
#define PDEV_BUS_IO_BASE (0x10)
|
||||
#define PDEV_BUS_IO_SIZE (0x14)
|
||||
#define PDEV_BUS_IRQ (0x18)
|
||||
#define PDEV_BUS_IRQ_COUNT (0x1c)
|
||||
|
||||
struct pdev_bus_dev {
|
||||
struct list_head list;
|
||||
struct platform_device pdev;
|
||||
struct resource resources[0];
|
||||
};
|
||||
|
||||
static void goldfish_pdev_worker(struct work_struct *work);
|
||||
|
||||
static void __iomem *pdev_bus_base;
|
||||
static unsigned long pdev_bus_addr;
|
||||
static unsigned long pdev_bus_len;
|
||||
static u32 pdev_bus_irq;
|
||||
static LIST_HEAD(pdev_bus_new_devices);
|
||||
static LIST_HEAD(pdev_bus_registered_devices);
|
||||
static LIST_HEAD(pdev_bus_removed_devices);
|
||||
static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker);
|
||||
|
||||
|
||||
static void goldfish_pdev_worker(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
struct pdev_bus_dev *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) {
|
||||
list_del(&pos->list);
|
||||
platform_device_unregister(&pos->pdev);
|
||||
kfree(pos);
|
||||
}
|
||||
list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
|
||||
list_del(&pos->list);
|
||||
ret = platform_device_register(&pos->pdev);
|
||||
if (ret)
|
||||
pr_err("goldfish_pdev_worker failed to register device, %s\n",
|
||||
pos->pdev.name);
|
||||
list_add_tail(&pos->list, &pdev_bus_registered_devices);
|
||||
}
|
||||
}
|
||||
|
||||
static void goldfish_pdev_remove(void)
|
||||
{
|
||||
struct pdev_bus_dev *pos, *n;
|
||||
u32 base;
|
||||
|
||||
base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
|
||||
|
||||
list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
|
||||
if (pos->resources[0].start == base) {
|
||||
list_del(&pos->list);
|
||||
kfree(pos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) {
|
||||
if (pos->resources[0].start == base) {
|
||||
list_del(&pos->list);
|
||||
list_add_tail(&pos->list, &pdev_bus_removed_devices);
|
||||
schedule_work(&pdev_bus_worker);
|
||||
return;
|
||||
}
|
||||
};
|
||||
pr_err("goldfish_pdev_remove could not find device at %x\n", base);
|
||||
}
|
||||
|
||||
static int goldfish_new_pdev(void)
|
||||
{
|
||||
struct pdev_bus_dev *dev;
|
||||
u32 name_len;
|
||||
u32 irq = -1, irq_count;
|
||||
int resource_count = 2;
|
||||
u32 base;
|
||||
char *name;
|
||||
|
||||
base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
|
||||
|
||||
irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT);
|
||||
name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN);
|
||||
if (irq_count)
|
||||
resource_count++;
|
||||
|
||||
dev = kzalloc(sizeof(*dev) +
|
||||
sizeof(struct resource) * resource_count +
|
||||
name_len + 1 + sizeof(*dev->pdev.dev.dma_mask), GFP_ATOMIC);
|
||||
if (dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->pdev.num_resources = resource_count;
|
||||
dev->pdev.resource = (struct resource *)(dev + 1);
|
||||
dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count);
|
||||
dev->pdev.dev.coherent_dma_mask = ~0;
|
||||
dev->pdev.dev.dma_mask = (void *)(dev->pdev.name + name_len + 1);
|
||||
*dev->pdev.dev.dma_mask = ~0;
|
||||
|
||||
writel((unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME);
|
||||
name[name_len] = '\0';
|
||||
dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID);
|
||||
dev->pdev.resource[0].start = base;
|
||||
dev->pdev.resource[0].end = base +
|
||||
readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1;
|
||||
dev->pdev.resource[0].flags = IORESOURCE_MEM;
|
||||
if (irq_count) {
|
||||
irq = readl(pdev_bus_base + PDEV_BUS_IRQ);
|
||||
dev->pdev.resource[1].start = irq;
|
||||
dev->pdev.resource[1].end = irq + irq_count - 1;
|
||||
dev->pdev.resource[1].flags = IORESOURCE_IRQ;
|
||||
}
|
||||
|
||||
pr_debug("goldfish_new_pdev %s at %x irq %d\n", name, base, irq);
|
||||
list_add_tail(&dev->list, &pdev_bus_new_devices);
|
||||
schedule_work(&pdev_bus_worker);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
while (1) {
|
||||
u32 op = readl(pdev_bus_base + PDEV_BUS_OP);
|
||||
switch (op) {
|
||||
case PDEV_BUS_OP_DONE:
|
||||
return IRQ_NONE;
|
||||
|
||||
case PDEV_BUS_OP_REMOVE_DEV:
|
||||
goldfish_pdev_remove();
|
||||
break;
|
||||
|
||||
case PDEV_BUS_OP_ADD_DEV:
|
||||
goldfish_new_pdev();
|
||||
break;
|
||||
}
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int goldfish_pdev_bus_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct resource *r;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (r == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
pdev_bus_addr = r->start;
|
||||
pdev_bus_len = resource_size(r);
|
||||
|
||||
if (request_mem_region(pdev_bus_addr, pdev_bus_len, "goldfish")) {
|
||||
dev_err(&pdev->dev, "unable to reserve Goldfish MMIO.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
pdev_bus_base = ioremap(pdev_bus_addr, pdev_bus_len);
|
||||
if (pdev_bus_base == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "unable to map Goldfish MMIO.\n");
|
||||
goto free_resources;
|
||||
}
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (r == NULL) {
|
||||
ret = -ENOENT;
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
pdev_bus_irq = r->start;
|
||||
|
||||
ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt,
|
||||
IRQF_SHARED, "goldfish_pdev_bus", pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to request Goldfish IRQ\n");
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP);
|
||||
return 0;
|
||||
|
||||
free_map:
|
||||
iounmap(pdev_bus_base);
|
||||
free_resources:
|
||||
release_mem_region(pdev_bus_addr, pdev_bus_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int goldfish_pdev_bus_remove(struct platform_device *pdev)
|
||||
{
|
||||
iounmap(pdev_bus_base);
|
||||
free_irq(pdev_bus_irq, pdev);
|
||||
release_mem_region(pdev_bus_addr, pdev_bus_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver goldfish_pdev_bus_driver = {
|
||||
.probe = goldfish_pdev_bus_probe,
|
||||
.remove = goldfish_pdev_bus_remove,
|
||||
.driver = {
|
||||
.name = "goldfish_pdev_bus"
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(goldfish_pdev_bus_driver);
|
@ -284,8 +284,8 @@ static int gab_probe(struct platform_device *pdev)
|
||||
* based on the channel supported by consumer device.
|
||||
*/
|
||||
for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
|
||||
adc_bat->channel[chan] = iio_channel_get(dev_name(&pdev->dev),
|
||||
gab_chan_name[chan]);
|
||||
adc_bat->channel[chan] = iio_channel_get(&pdev->dev,
|
||||
gab_chan_name[chan]);
|
||||
if (IS_ERR(adc_bat->channel[chan])) {
|
||||
ret = PTR_ERR(adc_bat->channel[chan]);
|
||||
adc_bat->channel[chan] = NULL;
|
||||
|
@ -580,7 +580,7 @@ static void lp8788_irq_unregister(struct platform_device *pdev,
|
||||
}
|
||||
}
|
||||
|
||||
static void lp8788_setup_adc_channel(const char *consumer_name,
|
||||
static void lp8788_setup_adc_channel(struct device *dev,
|
||||
struct lp8788_charger *pchg)
|
||||
{
|
||||
struct lp8788_charger_platform_data *pdata = pchg->pdata;
|
||||
@ -590,11 +590,11 @@ static void lp8788_setup_adc_channel(const char *consumer_name,
|
||||
return;
|
||||
|
||||
/* ADC channel for battery voltage */
|
||||
chan = iio_channel_get(consumer_name, pdata->adc_vbatt);
|
||||
chan = iio_channel_get(dev, pdata->adc_vbatt);
|
||||
pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
|
||||
|
||||
/* ADC channel for battery temperature */
|
||||
chan = iio_channel_get(consumer_name, pdata->adc_batt_temp);
|
||||
chan = iio_channel_get(dev, pdata->adc_batt_temp);
|
||||
pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
|
||||
}
|
||||
|
||||
@ -705,7 +705,7 @@ static int lp8788_charger_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
lp8788_setup_adc_channel(pdev->name, pchg);
|
||||
lp8788_setup_adc_channel(&pdev->dev, pchg);
|
||||
|
||||
ret = lp8788_psy_register(pdev, pchg);
|
||||
if (ret)
|
||||
|
@ -1183,4 +1183,20 @@ config RTC_DRV_SNVS
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-snvs".
|
||||
|
||||
comment "HID Sensor RTC drivers"
|
||||
|
||||
config RTC_DRV_HID_SENSOR_TIME
|
||||
tristate "HID Sensor Time"
|
||||
depends on USB_HID
|
||||
select IIO
|
||||
select HID_SENSOR_HUB
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
help
|
||||
Say yes here to build support for the HID Sensors of type Time.
|
||||
This drivers makes such sensors available as RTCs.
|
||||
|
||||
If this driver is compiled as a module, it will be named
|
||||
rtc-hid-sensor-time.
|
||||
|
||||
|
||||
endif # RTC_CLASS
|
||||
|
@ -53,6 +53,7 @@ obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
|
||||
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
|
||||
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
|
||||
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
|
||||
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
|
||||
obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
|
||||
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
|
||||
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
|
||||
|
292
drivers/rtc/rtc-hid-sensor-time.c
Normal file
292
drivers/rtc/rtc-hid-sensor-time.c
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* HID Sensor Time Driver
|
||||
* Copyright (c) 2012, Alexander Holler.
|
||||
*
|
||||
* 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/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
/* Format: HID-SENSOR-usage_id_in_hex */
|
||||
/* Usage ID from spec for Time: 0x2000A0 */
|
||||
#define DRIVER_NAME "HID-SENSOR-2000a0" /* must be lowercase */
|
||||
|
||||
enum hid_time_channel {
|
||||
CHANNEL_SCAN_INDEX_YEAR,
|
||||
CHANNEL_SCAN_INDEX_MONTH,
|
||||
CHANNEL_SCAN_INDEX_DAY,
|
||||
CHANNEL_SCAN_INDEX_HOUR,
|
||||
CHANNEL_SCAN_INDEX_MINUTE,
|
||||
CHANNEL_SCAN_INDEX_SECOND,
|
||||
TIME_RTC_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
struct hid_time_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info info[TIME_RTC_CHANNEL_MAX];
|
||||
struct rtc_time last_time;
|
||||
spinlock_t lock_last_time;
|
||||
struct completion comp_last_time;
|
||||
struct rtc_time time_buf;
|
||||
struct rtc_device *rtc;
|
||||
};
|
||||
|
||||
static const u32 hid_time_addresses[TIME_RTC_CHANNEL_MAX] = {
|
||||
HID_USAGE_SENSOR_TIME_YEAR,
|
||||
HID_USAGE_SENSOR_TIME_MONTH,
|
||||
HID_USAGE_SENSOR_TIME_DAY,
|
||||
HID_USAGE_SENSOR_TIME_HOUR,
|
||||
HID_USAGE_SENSOR_TIME_MINUTE,
|
||||
HID_USAGE_SENSOR_TIME_SECOND,
|
||||
};
|
||||
|
||||
/* Channel names for verbose error messages */
|
||||
static const char * const hid_time_channel_names[TIME_RTC_CHANNEL_MAX] = {
|
||||
"year", "month", "day", "hour", "minute", "second",
|
||||
};
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int hid_time_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id, void *priv)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct hid_time_state *time_state = platform_get_drvdata(priv);
|
||||
|
||||
spin_lock_irqsave(&time_state->lock_last_time, flags);
|
||||
time_state->last_time = time_state->time_buf;
|
||||
spin_unlock_irqrestore(&time_state->lock_last_time, flags);
|
||||
complete(&time_state->comp_last_time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_time_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id, size_t raw_len,
|
||||
char *raw_data, void *priv)
|
||||
{
|
||||
struct hid_time_state *time_state = platform_get_drvdata(priv);
|
||||
struct rtc_time *time_buf = &time_state->time_buf;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_TIME_YEAR:
|
||||
time_buf->tm_year = *(u8 *)raw_data;
|
||||
if (time_buf->tm_year < 70)
|
||||
/* assume we are in 1970...2069 */
|
||||
time_buf->tm_year += 100;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_MONTH:
|
||||
/* sensor sending the month as 1-12, we need 0-11 */
|
||||
time_buf->tm_mon = *(u8 *)raw_data-1;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_DAY:
|
||||
time_buf->tm_mday = *(u8 *)raw_data;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_HOUR:
|
||||
time_buf->tm_hour = *(u8 *)raw_data;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_MINUTE:
|
||||
time_buf->tm_min = *(u8 *)raw_data;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_SECOND:
|
||||
time_buf->tm_sec = *(u8 *)raw_data;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* small helper, haven't found any other way */
|
||||
static const char *hid_time_attrib_name(u32 attrib_id)
|
||||
{
|
||||
static const char unknown[] = "unknown";
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) {
|
||||
if (hid_time_addresses[i] == attrib_id)
|
||||
return hid_time_channel_names[i];
|
||||
}
|
||||
return unknown; /* should never happen */
|
||||
}
|
||||
|
||||
static int hid_time_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
struct hid_time_state *time_state)
|
||||
{
|
||||
int report_id, i;
|
||||
|
||||
for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i)
|
||||
if (sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT, usage_id,
|
||||
hid_time_addresses[i],
|
||||
&time_state->info[i]) < 0)
|
||||
return -EINVAL;
|
||||
/* Check the (needed) attributes for sanity */
|
||||
report_id = time_state->info[0].report_id;
|
||||
if (report_id < 0) {
|
||||
dev_err(&pdev->dev, "bad report ID!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) {
|
||||
if (time_state->info[i].report_id != report_id) {
|
||||
dev_err(&pdev->dev,
|
||||
"not all needed attributes inside the same report!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (time_state->info[i].size != 1) {
|
||||
dev_err(&pdev->dev,
|
||||
"attribute '%s' not 8 bits wide!\n",
|
||||
hid_time_attrib_name(
|
||||
time_state->info[i].attrib_id));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (time_state->info[i].units !=
|
||||
HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED &&
|
||||
/* allow attribute seconds with unit seconds */
|
||||
!(time_state->info[i].attrib_id ==
|
||||
HID_USAGE_SENSOR_TIME_SECOND &&
|
||||
time_state->info[i].units ==
|
||||
HID_USAGE_SENSOR_UNITS_SECOND)) {
|
||||
dev_err(&pdev->dev,
|
||||
"attribute '%s' hasn't a unit of type 'none'!\n",
|
||||
hid_time_attrib_name(
|
||||
time_state->info[i].attrib_id));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (time_state->info[i].unit_expo) {
|
||||
dev_err(&pdev->dev,
|
||||
"attribute '%s' hasn't a unit exponent of 1!\n",
|
||||
hid_time_attrib_name(
|
||||
time_state->info[i].attrib_id));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct hid_time_state *time_state =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
int ret;
|
||||
|
||||
INIT_COMPLETION(time_state->comp_last_time);
|
||||
/* get a report with all values through requesting one value */
|
||||
sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_TIME, hid_time_addresses[0],
|
||||
time_state->info[0].report_id);
|
||||
/* wait for all values (event) */
|
||||
ret = wait_for_completion_killable_timeout(
|
||||
&time_state->comp_last_time, HZ*6);
|
||||
if (ret > 0) {
|
||||
/* no error */
|
||||
spin_lock_irqsave(&time_state->lock_last_time, flags);
|
||||
*tm = time_state->last_time;
|
||||
spin_unlock_irqrestore(&time_state->lock_last_time, flags);
|
||||
return 0;
|
||||
}
|
||||
if (!ret)
|
||||
return -EIO; /* timeouted */
|
||||
return ret; /* killed (-ERESTARTSYS) */
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops hid_time_rtc_ops = {
|
||||
.read_time = hid_rtc_read_time,
|
||||
};
|
||||
|
||||
static int hid_time_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct hid_time_state *time_state = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct hid_time_state), GFP_KERNEL);
|
||||
|
||||
if (time_state == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, time_state);
|
||||
|
||||
spin_lock_init(&time_state->lock_last_time);
|
||||
init_completion(&time_state->comp_last_time);
|
||||
time_state->common_attributes.hsdev = hsdev;
|
||||
time_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_TIME,
|
||||
&time_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hid_time_parse_report(pdev, hsdev, HID_USAGE_SENSOR_TIME,
|
||||
time_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_state->callbacks.send_event = hid_time_proc_event;
|
||||
time_state->callbacks.capture_sample = hid_time_capture_sample;
|
||||
time_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TIME,
|
||||
&time_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "register callback failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_state->rtc = rtc_device_register("hid-sensor-time",
|
||||
&pdev->dev, &hid_time_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(time_state->rtc)) {
|
||||
dev_err(&pdev->dev, "rtc device register failed!\n");
|
||||
return PTR_ERR(time_state->rtc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hid_time_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct hid_time_state *time_state = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(time_state->rtc);
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver hid_time_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = hid_time_probe,
|
||||
.remove = hid_time_remove,
|
||||
};
|
||||
module_platform_driver(hid_time_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Time");
|
||||
MODULE_AUTHOR("Alexander Holler <holler@ahsoftware.de>");
|
||||
MODULE_LICENSE("GPL");
|
@ -74,8 +74,6 @@ source "drivers/staging/iio/Kconfig"
|
||||
|
||||
source "drivers/staging/zram/Kconfig"
|
||||
|
||||
source "drivers/staging/zcache/Kconfig"
|
||||
|
||||
source "drivers/staging/zsmalloc/Kconfig"
|
||||
|
||||
source "drivers/staging/wlags49_h2/Kconfig"
|
||||
@ -128,8 +126,6 @@ source "drivers/staging/csr/Kconfig"
|
||||
|
||||
source "drivers/staging/omap-thermal/Kconfig"
|
||||
|
||||
source "drivers/staging/ramster/Kconfig"
|
||||
|
||||
source "drivers/staging/silicom/Kconfig"
|
||||
|
||||
source "drivers/staging/ced1401/Kconfig"
|
||||
@ -142,4 +138,8 @@ source "drivers/staging/sb105x/Kconfig"
|
||||
|
||||
source "drivers/staging/fwserial/Kconfig"
|
||||
|
||||
source "drivers/staging/zcache/Kconfig"
|
||||
|
||||
source "drivers/staging/goldfish/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
@ -31,7 +31,6 @@ obj-$(CONFIG_VME_BUS) += vme/
|
||||
obj-$(CONFIG_DX_SEP) += sep/
|
||||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_ZRAM) += zram/
|
||||
obj-$(CONFIG_ZCACHE) += zcache/
|
||||
obj-$(CONFIG_ZSMALLOC) += zsmalloc/
|
||||
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
|
||||
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
|
||||
@ -56,10 +55,11 @@ obj-$(CONFIG_USB_G_CCG) += ccg/
|
||||
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
|
||||
obj-$(CONFIG_CSR_WIFI) += csr/
|
||||
obj-$(CONFIG_OMAP_BANDGAP) += omap-thermal/
|
||||
obj-$(CONFIG_ZCACHE2) += ramster/
|
||||
obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/
|
||||
obj-$(CONFIG_CED1401) += ced1401/
|
||||
obj-$(CONFIG_DRM_IMX) += imx-drm/
|
||||
obj-$(CONFIG_DGRP) += dgrp/
|
||||
obj-$(CONFIG_SB105X) += sb105x/
|
||||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||
obj-$(CONFIG_ZCACHE) += zcache/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
|
@ -11,19 +11,42 @@ if ANDROID
|
||||
config ANDROID_BINDER_IPC
|
||||
bool "Android Binder IPC Driver"
|
||||
default n
|
||||
---help---
|
||||
Binder is used in Android for both communication between processes,
|
||||
and remote method invocation.
|
||||
|
||||
This means one Android process can call a method/routine in another
|
||||
Android process, using Binder to identify, invoke and pass arguments
|
||||
between said processes.
|
||||
|
||||
config ASHMEM
|
||||
bool "Enable the Anonymous Shared Memory Subsystem"
|
||||
default n
|
||||
depends on SHMEM || TINY_SHMEM
|
||||
help
|
||||
---help---
|
||||
The ashmem subsystem is a new shared memory allocator, similar to
|
||||
POSIX SHM but with different behavior and sporting a simpler
|
||||
file-based API.
|
||||
|
||||
It is, in theory, a good memory allocator for low-memory devices,
|
||||
because it can discard shared memory units when under memory pressure.
|
||||
|
||||
config ANDROID_LOGGER
|
||||
tristate "Android log driver"
|
||||
default n
|
||||
---help---
|
||||
This adds support for system-wide logging using four log buffers.
|
||||
|
||||
These are:
|
||||
|
||||
1: main
|
||||
2: events
|
||||
3: radio
|
||||
4: system
|
||||
|
||||
Log reading and writing is performed via normal Linux reads and
|
||||
optimized writes. This optimization avoids logging having too
|
||||
much overhead in the system.
|
||||
|
||||
config ANDROID_TIMED_OUTPUT
|
||||
bool "Timed output class driver"
|
||||
@ -38,13 +61,13 @@ config ANDROID_LOW_MEMORY_KILLER
|
||||
bool "Android Low Memory Killer"
|
||||
default N
|
||||
---help---
|
||||
Register processes to be killed when memory is low
|
||||
Registers processes to be killed when memory is low
|
||||
|
||||
config ANDROID_INTF_ALARM_DEV
|
||||
bool "Android alarm driver"
|
||||
depends on RTC_CLASS
|
||||
default n
|
||||
help
|
||||
---help---
|
||||
Provides non-wakeup and rtc backed wakeup alarms based on rtc or
|
||||
elapsed realtime, and a non-wakeup alarm on the monotonic clock.
|
||||
Also exports the alarm interface to user-space.
|
||||
|
@ -42,10 +42,6 @@ do { \
|
||||
ANDROID_ALARM_RTC_WAKEUP_MASK | \
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
|
||||
|
||||
/* support old userspace code */
|
||||
#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */
|
||||
#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t)
|
||||
|
||||
static int alarm_opened;
|
||||
static DEFINE_SPINLOCK(alarm_slock);
|
||||
static struct wakeup_source alarm_wake_lock;
|
||||
@ -96,18 +92,116 @@ static void devalarm_cancel(struct devalarm *alrm)
|
||||
hrtimer_cancel(&alrm->u.hrt);
|
||||
}
|
||||
|
||||
static void alarm_clear(enum android_alarm_type alarm_type)
|
||||
{
|
||||
uint32_t alarm_type_mask = 1U << alarm_type;
|
||||
unsigned long flags;
|
||||
|
||||
static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm %d clear\n", alarm_type);
|
||||
devalarm_try_to_cancel(&alarms[alarm_type]);
|
||||
if (alarm_pending) {
|
||||
alarm_pending &= ~alarm_type_mask;
|
||||
if (!alarm_pending && !wait_pending)
|
||||
__pm_relax(&alarm_wake_lock);
|
||||
}
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void alarm_set(enum android_alarm_type alarm_type,
|
||||
struct timespec *ts)
|
||||
{
|
||||
uint32_t alarm_type_mask = 1U << alarm_type;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm %d set %ld.%09ld\n",
|
||||
alarm_type, ts->tv_sec, ts->tv_nsec);
|
||||
alarm_enabled |= alarm_type_mask;
|
||||
devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts));
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
|
||||
static int alarm_wait(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rv = 0;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm wait\n");
|
||||
if (!alarm_pending && wait_pending) {
|
||||
__pm_relax(&alarm_wake_lock);
|
||||
wait_pending = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
rv = alarm_pending;
|
||||
wait_pending = 1;
|
||||
alarm_pending = 0;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int alarm_set_rtc(struct timespec *ts)
|
||||
{
|
||||
struct rtc_time new_rtc_tm;
|
||||
struct rtc_device *rtc_dev;
|
||||
unsigned long flags;
|
||||
int rv = 0;
|
||||
|
||||
rtc_time_to_tm(ts->tv_sec, &new_rtc_tm);
|
||||
rtc_dev = alarmtimer_get_rtcdev();
|
||||
rv = do_settimeofday(ts);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
if (rtc_dev)
|
||||
rv = rtc_set_time(rtc_dev, &new_rtc_tm);
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
|
||||
wake_up(&alarm_wait_queue);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int alarm_get_time(enum android_alarm_type alarm_type,
|
||||
struct timespec *ts)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
switch (alarm_type) {
|
||||
case ANDROID_ALARM_RTC_WAKEUP:
|
||||
case ANDROID_ALARM_RTC:
|
||||
getnstimeofday(ts);
|
||||
break;
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME:
|
||||
get_monotonic_boottime(ts);
|
||||
break;
|
||||
case ANDROID_ALARM_SYSTEMTIME:
|
||||
ktime_get_ts(ts);
|
||||
break;
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static long alarm_do_ioctl(struct file *file, unsigned int cmd,
|
||||
struct timespec *ts)
|
||||
{
|
||||
int rv = 0;
|
||||
unsigned long flags;
|
||||
struct timespec new_alarm_time;
|
||||
struct timespec new_rtc_time;
|
||||
struct timespec tmp_time;
|
||||
struct rtc_time new_rtc_tm;
|
||||
struct rtc_device *rtc_dev;
|
||||
enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
|
||||
uint32_t alarm_type_mask = 1U << alarm_type;
|
||||
|
||||
if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
|
||||
return -EINVAL;
|
||||
@ -130,115 +224,89 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_CLEAR(0):
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm %d clear\n", alarm_type);
|
||||
devalarm_try_to_cancel(&alarms[alarm_type]);
|
||||
if (alarm_pending) {
|
||||
alarm_pending &= ~alarm_type_mask;
|
||||
if (!alarm_pending && !wait_pending)
|
||||
__pm_relax(&alarm_wake_lock);
|
||||
}
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
alarm_clear(alarm_type);
|
||||
break;
|
||||
|
||||
case ANDROID_ALARM_SET_OLD:
|
||||
case ANDROID_ALARM_SET_AND_WAIT_OLD:
|
||||
if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
new_alarm_time.tv_nsec = 0;
|
||||
goto from_old_alarm_set;
|
||||
|
||||
case ANDROID_ALARM_SET_AND_WAIT(0):
|
||||
case ANDROID_ALARM_SET(0):
|
||||
if (copy_from_user(&new_alarm_time, (void __user *)arg,
|
||||
sizeof(new_alarm_time))) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
from_old_alarm_set:
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm %d set %ld.%09ld\n",
|
||||
alarm_type,
|
||||
new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
|
||||
alarm_enabled |= alarm_type_mask;
|
||||
devalarm_start(&alarms[alarm_type],
|
||||
timespec_to_ktime(new_alarm_time));
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
|
||||
&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
|
||||
break;
|
||||
alarm_set(alarm_type, ts);
|
||||
break;
|
||||
case ANDROID_ALARM_SET_AND_WAIT(0):
|
||||
alarm_set(alarm_type, ts);
|
||||
/* fall though */
|
||||
case ANDROID_ALARM_WAIT:
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_dbg(IO, "alarm wait\n");
|
||||
if (!alarm_pending && wait_pending) {
|
||||
__pm_relax(&alarm_wake_lock);
|
||||
wait_pending = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
|
||||
if (rv)
|
||||
goto err1;
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
rv = alarm_pending;
|
||||
wait_pending = 1;
|
||||
alarm_pending = 0;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
rv = alarm_wait();
|
||||
break;
|
||||
case ANDROID_ALARM_SET_RTC:
|
||||
if (copy_from_user(&new_rtc_time, (void __user *)arg,
|
||||
sizeof(new_rtc_time))) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
rtc_time_to_tm(new_rtc_time.tv_sec, &new_rtc_tm);
|
||||
rtc_dev = alarmtimer_get_rtcdev();
|
||||
rv = do_settimeofday(&new_rtc_time);
|
||||
if (rv < 0)
|
||||
goto err1;
|
||||
if (rtc_dev)
|
||||
rv = rtc_set_time(rtc_dev, &new_rtc_tm);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
|
||||
wake_up(&alarm_wait_queue);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
if (rv < 0)
|
||||
goto err1;
|
||||
rv = alarm_set_rtc(ts);
|
||||
break;
|
||||
case ANDROID_ALARM_GET_TIME(0):
|
||||
switch (alarm_type) {
|
||||
case ANDROID_ALARM_RTC_WAKEUP:
|
||||
case ANDROID_ALARM_RTC:
|
||||
getnstimeofday(&tmp_time);
|
||||
break;
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME:
|
||||
get_monotonic_boottime(&tmp_time);
|
||||
break;
|
||||
case ANDROID_ALARM_SYSTEMTIME:
|
||||
ktime_get_ts(&tmp_time);
|
||||
break;
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
if (copy_to_user((void __user *)arg, &tmp_time,
|
||||
sizeof(tmp_time))) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
rv = alarm_get_time(alarm_type, ts);
|
||||
break;
|
||||
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
}
|
||||
err1:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_SET_AND_WAIT(0):
|
||||
case ANDROID_ALARM_SET(0):
|
||||
case ANDROID_ALARM_SET_RTC:
|
||||
if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = alarm_do_ioctl(file, cmd, &ts);
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_GET_TIME(0):
|
||||
if (copy_to_user((void __user *)arg, &ts, sizeof(ts)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long alarm_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_SET_AND_WAIT_COMPAT(0):
|
||||
case ANDROID_ALARM_SET_COMPAT(0):
|
||||
case ANDROID_ALARM_SET_RTC_COMPAT:
|
||||
if (compat_get_timespec(&ts, (void __user *)arg))
|
||||
return -EFAULT;
|
||||
/* fall through */
|
||||
case ANDROID_ALARM_GET_TIME_COMPAT(0):
|
||||
cmd = ANDROID_ALARM_COMPAT_TO_NORM(cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
rv = alarm_do_ioctl(file, cmd, &ts);
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_GET_TIME(0): /* NOTE: we modified cmd above */
|
||||
if (compat_put_timespec(&ts, (void __user *)arg))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int alarm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = NULL;
|
||||
@ -319,6 +387,9 @@ static const struct file_operations alarm_fops = {
|
||||
.unlocked_ioctl = alarm_ioctl,
|
||||
.open = alarm_open,
|
||||
.release = alarm_release,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = alarm_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct miscdevice alarm_device = {
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
enum android_alarm_type {
|
||||
/* return code bit numbers or set alarm arg */
|
||||
@ -59,4 +60,22 @@ enum android_alarm_return_flags {
|
||||
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
|
||||
#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)
|
||||
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define ANDROID_ALARM_SET_COMPAT(type) ALARM_IOW(2, type, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_SET_AND_WAIT_COMPAT(type) ALARM_IOW(3, type, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_GET_TIME_COMPAT(type) ALARM_IOW(4, type, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_SET_RTC_COMPAT _IOW('a', 5, \
|
||||
struct compat_timespec)
|
||||
#define ANDROID_ALARM_IOCTL_NR(cmd) (_IOC_NR(cmd) & ((1<<4)-1))
|
||||
#define ANDROID_ALARM_COMPAT_TO_NORM(cmd) \
|
||||
ALARM_IOW(ANDROID_ALARM_IOCTL_NR(cmd), \
|
||||
ANDROID_ALARM_IOCTL_TO_TYPE(cmd), \
|
||||
struct timespec)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -3227,7 +3227,7 @@ static void print_binder_proc(struct seq_file *m,
|
||||
m->count = start_pos;
|
||||
}
|
||||
|
||||
static const char *binder_return_strings[] = {
|
||||
static const char * const binder_return_strings[] = {
|
||||
"BR_ERROR",
|
||||
"BR_OK",
|
||||
"BR_TRANSACTION",
|
||||
@ -3248,7 +3248,7 @@ static const char *binder_return_strings[] = {
|
||||
"BR_FAILED_REPLY"
|
||||
};
|
||||
|
||||
static const char *binder_command_strings[] = {
|
||||
static const char * const binder_command_strings[] = {
|
||||
"BC_TRANSACTION",
|
||||
"BC_REPLY",
|
||||
"BC_ACQUIRE_RESULT",
|
||||
@ -3268,7 +3268,7 @@ static const char *binder_command_strings[] = {
|
||||
"BC_DEAD_BINDER_DONE"
|
||||
};
|
||||
|
||||
static const char *binder_objstat_strings[] = {
|
||||
static const char * const binder_objstat_strings[] = {
|
||||
"proc",
|
||||
"thread",
|
||||
"node",
|
||||
|
@ -163,7 +163,7 @@ struct binder_pri_ptr_cookie {
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
enum BinderDriverReturnProtocol {
|
||||
enum binder_driver_return_protocol {
|
||||
BR_ERROR = _IOR('r', 0, int),
|
||||
/*
|
||||
* int: error code
|
||||
@ -251,7 +251,7 @@ enum BinderDriverReturnProtocol {
|
||||
*/
|
||||
};
|
||||
|
||||
enum BinderDriverCommandProtocol {
|
||||
enum binder_driver_command_protocol {
|
||||
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
|
||||
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
|
||||
/*
|
||||
|
@ -164,11 +164,8 @@ static void enable_oled(struct asus_oled_dev *odev, uint8_t enabl)
|
||||
struct asus_oled_packet *packet;
|
||||
|
||||
packet = kzalloc(sizeof(struct asus_oled_packet), GFP_KERNEL);
|
||||
|
||||
if (!packet) {
|
||||
dev_err(&odev->udev->dev, "out of memory\n");
|
||||
if (!packet)
|
||||
return;
|
||||
}
|
||||
|
||||
setup_packet_header(packet, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00);
|
||||
|
||||
@ -323,11 +320,8 @@ static void send_data(struct asus_oled_dev *odev)
|
||||
struct asus_oled_packet *packet;
|
||||
|
||||
packet = kzalloc(sizeof(struct asus_oled_packet), GFP_KERNEL);
|
||||
|
||||
if (!packet) {
|
||||
dev_err(&odev->udev->dev, "out of memory\n");
|
||||
if (!packet)
|
||||
return;
|
||||
}
|
||||
|
||||
if (odev->pack_mode == PACK_MODE_G1) {
|
||||
/* When sending roll-mode data the display updated only
|
||||
@ -665,11 +659,8 @@ static int asus_oled_probe(struct usb_interface *interface,
|
||||
}
|
||||
|
||||
odev = kzalloc(sizeof(struct asus_oled_dev), GFP_KERNEL);
|
||||
|
||||
if (odev == NULL) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
if (odev == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
odev->udev = usb_get_dev(udev);
|
||||
odev->pic_mode = ASUS_OLED_STATIC;
|
||||
|
@ -95,7 +95,7 @@ struct bcm_classifier_rule {
|
||||
UCHAR ucDirection;
|
||||
BOOLEAN bIpv6Protocol;
|
||||
UINT32 u32PHSRuleID;
|
||||
S_PHS_RULE sPhsRule;
|
||||
struct bcm_phs_rule sPhsRule;
|
||||
UCHAR u8AssociatedPHSI;
|
||||
|
||||
/* Classification fields for ETH CS */
|
||||
@ -288,7 +288,7 @@ struct bcm_mini_adapter {
|
||||
wait_queue_head_t ioctl_fw_dnld_wait_queue;
|
||||
BOOLEAN waiting_to_fw_download_done;
|
||||
pid_t fw_download_process_pid;
|
||||
PSTARGETPARAMS pstargetparams;
|
||||
struct bcm_target_params *pstargetparams;
|
||||
BOOLEAN device_removed;
|
||||
BOOLEAN DeviceAccess;
|
||||
BOOLEAN bIsAutoCorrectEnabled;
|
||||
@ -303,10 +303,10 @@ struct bcm_mini_adapter {
|
||||
struct task_struct *transmit_packet_thread;
|
||||
|
||||
/* LED Related Structures */
|
||||
LED_INFO_STRUCT LEDInfo;
|
||||
struct bcm_led_info LEDInfo;
|
||||
|
||||
/* Driver State for LED Blinking */
|
||||
LedEventInfo_t DriverState;
|
||||
enum bcm_led_events DriverState;
|
||||
/* Interface Specific */
|
||||
PVOID pvInterfaceAdapter;
|
||||
int (*bcm_file_download)(PVOID,
|
||||
@ -333,7 +333,7 @@ struct bcm_mini_adapter {
|
||||
/* BOOLEAN bTriedToWakeUpFromShutdown; */
|
||||
BOOLEAN bLinkDownRequested;
|
||||
int downloadDDR;
|
||||
PHS_DEVICE_EXTENSION stBCMPhsContext;
|
||||
struct bcm_phs_extension stBCMPhsContext;
|
||||
struct bcm_hdr_suppression_contextinfo stPhsTxContextInfo;
|
||||
uint8_t ucaPHSPktRestoreBuf[2048];
|
||||
uint8_t bPHSEnabled;
|
||||
@ -345,7 +345,7 @@ struct bcm_mini_adapter {
|
||||
struct bcm_fragmented_packet_info astFragmentedPktClassifierTable[MAX_FRAGMENTEDIP_CLASSIFICATION_ENTRIES];
|
||||
atomic_t uiMBupdate;
|
||||
UINT32 PmuMode;
|
||||
NVM_TYPE eNVMType;
|
||||
enum bcm_nvm_type eNVMType;
|
||||
UINT uiSectorSize;
|
||||
UINT uiSectorSizeInCFG;
|
||||
BOOLEAN bSectorSizeOverride;
|
||||
@ -366,9 +366,9 @@ struct bcm_mini_adapter {
|
||||
struct device *pstCreatedClassDevice;
|
||||
|
||||
/* BOOLEAN InterfaceUpStatus; */
|
||||
PFLASH2X_CS_INFO psFlash2xCSInfo;
|
||||
PFLASH_CS_INFO psFlashCSInfo;
|
||||
PFLASH2X_VENDORSPECIFIC_INFO psFlash2xVendorInfo;
|
||||
struct bcm_flash2x_cs_info *psFlash2xCSInfo;
|
||||
struct bcm_flash_cs_info *psFlashCSInfo;
|
||||
struct bcm_flash2x_vendor_info *psFlash2xVendorInfo;
|
||||
UINT uiFlashBaseAdd; /* Flash start address */
|
||||
UINT uiActiveISOOffset; /* Active ISO offset chosen before f/w download */
|
||||
enum bcm_flash2x_section_val eActiveISO; /* Active ISO section val */
|
||||
@ -392,7 +392,7 @@ struct bcm_mini_adapter {
|
||||
struct semaphore LowPowerModeSync;
|
||||
ULONG liDrainCalculated;
|
||||
UINT gpioBitMap;
|
||||
S_BCM_DEBUG_STATE stDebugState;
|
||||
struct bcm_debug_state stDebugState;
|
||||
};
|
||||
|
||||
#define GET_BCM_ADAPTER(net_dev) netdev_priv(net_dev)
|
||||
|
@ -1013,7 +1013,7 @@ cntrlEnd:
|
||||
}
|
||||
|
||||
case IOCTL_BCM_GET_CURRENT_STATUS: {
|
||||
LINK_STATE link_state;
|
||||
struct bcm_link_state link_state;
|
||||
|
||||
/* Copy Ioctl Buffer structure */
|
||||
if (copy_from_user(&IoBuffer, argp, sizeof(struct bcm_ioctl_buffer))) {
|
||||
@ -1229,13 +1229,13 @@ cntrlEnd:
|
||||
case IOCTL_BCM_SET_DEBUG:
|
||||
#ifdef DEBUG
|
||||
{
|
||||
USER_BCM_DBG_STATE sUserDebugState;
|
||||
struct bcm_user_debug_state sUserDebugState;
|
||||
|
||||
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, OSAL_DBG, DBG_LVL_ALL, "In SET_DEBUG ioctl\n");
|
||||
if (copy_from_user(&IoBuffer, argp, sizeof(struct bcm_ioctl_buffer)))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_from_user(&sUserDebugState, IoBuffer.InputBuffer, sizeof(USER_BCM_DBG_STATE)))
|
||||
if (copy_from_user(&sUserDebugState, IoBuffer.InputBuffer, sizeof(struct bcm_user_debug_state)))
|
||||
return -EFAULT;
|
||||
|
||||
BCM_DEBUG_PRINT (Adapter, DBG_TYPE_PRINTK, 0, 0, "IOCTL_BCM_SET_DEBUG: OnOff=%d Type = 0x%x ",
|
||||
@ -1783,16 +1783,16 @@ cntrlEnd:
|
||||
}
|
||||
|
||||
if (IsFlash2x(Adapter) == TRUE) {
|
||||
if (IoBuffer.OutputLength < sizeof(FLASH2X_CS_INFO))
|
||||
if (IoBuffer.OutputLength < sizeof(struct bcm_flash2x_cs_info))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_to_user(IoBuffer.OutputBuffer, Adapter->psFlash2xCSInfo, sizeof(FLASH2X_CS_INFO)))
|
||||
if (copy_to_user(IoBuffer.OutputBuffer, Adapter->psFlash2xCSInfo, sizeof(struct bcm_flash2x_cs_info)))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (IoBuffer.OutputLength < sizeof(FLASH_CS_INFO))
|
||||
if (IoBuffer.OutputLength < sizeof(struct bcm_flash_cs_info))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_to_user(IoBuffer.OutputBuffer, Adapter->psFlashCSInfo, sizeof(FLASH_CS_INFO)))
|
||||
if (copy_to_user(IoBuffer.OutputBuffer, Adapter->psFlashCSInfo, sizeof(struct bcm_flash_cs_info)))
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ static VOID deleteSFBySfid(struct bcm_mini_adapter *Adapter, UINT uiSearchRuleIn
|
||||
static inline VOID
|
||||
CopyIpAddrToClassifier(struct bcm_classifier_rule *pstClassifierEntry,
|
||||
B_UINT8 u8IpAddressLen, B_UINT8 *pu8IpAddressMaskSrc,
|
||||
BOOLEAN bIpVersion6, E_IPADDR_CONTEXT eIpAddrContext)
|
||||
BOOLEAN bIpVersion6, enum bcm_ipaddr_context eIpAddrContext)
|
||||
{
|
||||
int i = 0;
|
||||
UINT nSizeOfIPAddressInBytes = IP_LENGTH_OF_ADDRESS;
|
||||
@ -440,7 +440,7 @@ static VOID CopyToAdapter(register struct bcm_mini_adapter *Adapter, /* <Pointer
|
||||
B_UINT16 u16PacketClassificationRuleIndex = 0;
|
||||
int i;
|
||||
struct bcm_convergence_types *psfCSType = NULL;
|
||||
S_PHS_RULE sPhsRule;
|
||||
struct bcm_phs_rule sPhsRule;
|
||||
USHORT uVCID = Adapter->PackInfo[uiSearchRuleIndex].usVCID_Value;
|
||||
UINT UGIValue = 0;
|
||||
|
||||
@ -703,7 +703,7 @@ static VOID CopyToAdapter(register struct bcm_mini_adapter *Adapter, /* <Pointer
|
||||
/* Update PHS Rule For the Classifier */
|
||||
if (sPhsRule.u8PHSI) {
|
||||
Adapter->astClassifierTable[uiClassifierIndex].u32PHSRuleID = sPhsRule.u8PHSI;
|
||||
memcpy(&Adapter->astClassifierTable[uiClassifierIndex].sPhsRule, &sPhsRule, sizeof(S_PHS_RULE));
|
||||
memcpy(&Adapter->astClassifierTable[uiClassifierIndex].sPhsRule, &sPhsRule, sizeof(struct bcm_phs_rule));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user