diff options
Diffstat (limited to 'drivers/staging/iio')
-rw-r--r-- | drivers/staging/iio/gyro/Kconfig | 10 | ||||
-rw-r--r-- | drivers/staging/iio/gyro/Makefile | 3 | ||||
-rw-r--r-- | drivers/staging/iio/gyro/k3g.h | 274 | ||||
-rw-r--r-- | drivers/staging/iio/gyro/k3g_core.c | 846 | ||||
-rw-r--r-- | drivers/staging/iio/gyro/k3g_ring.c | 277 |
5 files changed, 1410 insertions, 0 deletions
diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig index ae2e7d3..1a787ae 100644 --- a/drivers/staging/iio/gyro/Kconfig +++ b/drivers/staging/iio/gyro/Kconfig @@ -45,3 +45,13 @@ config ADXRS450 This driver can also be built as a module. If so, the module will be called adxrs450. + +config K3G + tristate "ST K3G gyroscope sensor" + depends on I2C + depends on IIO_RING_BUFFER + help + Say yes here to build support for ST K3G gyroscope sensor. These devices use a hardware ring buffer. + + To compile this driver as a module, choose M here: the + module will be called k3g. diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile index 2212240..a5aa875 100644 --- a/drivers/staging/iio/gyro/Makefile +++ b/drivers/staging/iio/gyro/Makefile @@ -20,3 +20,6 @@ obj-$(CONFIG_ADIS16251) += adis16251.o adxrs450-y := adxrs450_core.o obj-$(CONFIG_ADXRS450) += adxrs450.o + +k3g-y := k3g_core.o k3g_ring.o +obj-$(CONFIG_K3G) += k3g.o diff --git a/drivers/staging/iio/gyro/k3g.h b/drivers/staging/iio/gyro/k3g.h new file mode 100644 index 0000000..acc0bb1 --- /dev/null +++ b/drivers/staging/iio/gyro/k3g.h @@ -0,0 +1,274 @@ +/* + * k3g.h - ST Microelectronics three-axis gyroscope sensor + * + * Copyright (c) 2010 Samsung Eletronics + * Donggeun Kim <dg77.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _K3G_H_ +#define _K3G_H_ + +#define K3G_WHO_AM_I 0x0f +#define K3G_CTRL_REG1 0x20 +#define K3G_CTRL_REG2 0x21 +#define K3G_CTRL_REG3 0x22 +#define K3G_CTRL_REG4 0x23 +#define K3G_CTRL_REG5 0x24 +#define K3G_REFERENCE 0x25 +#define K3G_OUT_TEMP 0x26 +#define K3G_STATUS_REG 0x27 +#define K3G_OUT_X_L 0x28 +#define K3G_OUT_X_H 0x29 +#define K3G_OUT_Y_L 0x2a +#define K3G_OUT_Y_H 0x2b +#define K3G_OUT_Z_L 0x2c +#define K3G_OUT_Z_H 0x2d +#define K3G_FIFO_CTRL_REG 0x2e +#define K3G_FIFO_SRC_REG 0x2f +#define K3G_INT1_CFG_REG 0x30 +#define K3G_INT1_SRC_REG 0x31 +#define K3G_INT1_THS_XH 0x32 +#define K3G_INT1_THS_XL 0x33 +#define K3G_INT1_THS_YH 0x34 +#define K3G_INT1_THS_YL 0x35 +#define K3G_INT1_THS_ZH 0x36 +#define K3G_INT1_THS_ZL 0x37 +#define K3G_INT1_DURATION_REG 0x38 + +#define K3G_MULTI_OUT_X_L 0xa8 + +#define K3G_DATA_RATE_SHIFT 6 +#define K3G_DATA_RATE_105HZ ((0x0) << K3G_DATA_RATE_SHIFT) +#define K3G_DATA_RATE_210HZ ((0x1) << K3G_DATA_RATE_SHIFT) +#define K3G_DATA_RATE_420HZ ((0x2) << K3G_DATA_RATE_SHIFT) +#define K3G_DATA_RATE_840HZ ((0x3) << K3G_DATA_RATE_SHIFT) +#define K3G_BANDWIDTH_SHIFT 4 +#define K3G_BANDWIDTH_MASK ((0x3) << K3G_BANDWIDTH_SHIFT) +#define K3G_POWERDOWN_SHIFT 3 +#define K3G_POWERDOWN_POWER_DOWN ((0x0) << K3G_POWERDOWN_SHIFT) +#define K3G_POWERDOWN_NORMAL ((0x1) << K3G_POWERDOWN_SHIFT) +#define K3G_POWERDOWN_MASK ((0x1) << K3G_POWERDOWN_SHIFT) +#define K3G_Z_EN_SHIFT 2 +#define K3G_Z_EN ((0x1) << K3G_Z_EN_SHIFT) +#define K3G_Y_EN_SHIFT 1 +#define K3G_Y_EN ((0x1) << K3G_Y_EN_SHIFT) +#define K3G_X_EN_SHIFT 0 +#define K3G_X_EN ((0x1) << K3G_X_EN_SHIFT) + +#define K3G_HIGH_PASS_FILTER_SHIFT 4 +#define K3G_HIGH_PASS_FILTER_NORMAL_MODE_RESET_READING \ + ((0x0) << K3G_HIGH_PASS_FILTER_SHIFT) +#define K3G_HIGH_PASS_FILTER_REFERENCE_SIGNAL \ + ((0x1) << K3G_HIGH_PASS_FILTER_SHIFT) +#define K3G_HIGH_PASS_FILTER_NORMAL_MODE \ + ((0x2) << K3G_HIGH_PASS_FILTER_SHIFT) +#define K3G_HIGH_PASS_FILTER_AUTORESET_ON_INTERRUPT \ + ((0x3) << K3G_HIGH_PASS_FILTER_SHIFT) +#define K3G_HIGH_PASS_FILTER_CUTOFF_FREQ_SHIFT 0 +#define K3G_HIGH_PASS_FILTER_CUTOFF_FREQ_MASK \ + ((0xf) << K3G_HIGH_PASS_FILTER_CUTOFF_FREQ_SHIFT) + +#define K3G_INT1_EN_SHIFT 7 +#define K3G_INT1_EN ((0x1) << K3G_INT1_EN_SHIFT) +#define K3G_INT1_BOOT_SHIFT 6 +#define K3G_INT1_BOOT ((0x1) << K3G_INT1_BOOT_SHIFT) +#define K3G_INT1_ACTIVE_SHIFT 5 +#define K3G_INT1_ACTIVE_HIGH ((0x0) << K3G_INT1_ACTIVE_SHIFT) +#define K3G_INT1_ACTIVE_LOW ((0x1) << K3G_INT1_ACTIVE_SHIFT) +#define K3G_INT_PUSH_PULL_OPEN_DRAIN_SHIFT 4 +#define K3G_INT_PUSH_PULL \ + ((0x0) << K3G_INT_PUSH_PULL_OPEN_DRAIN_SHIFT) +#define K3G_INT_OPEN_DRAIN \ + ((0x1) << K3G_INT_PUSH_PULL_OPEN_DRAIN_SHIFT) +#define K3G_INT2_SRC_SHIFT 0 +#define K3G_INT2_SRC_MASK ((0x0f) << K3G_INT2_SRC_SHIFT) +#define K3G_INT2_DATA_READY_SHIFT 3 +#define K3G_INT2_DATA_READY ((0x1) << K3G_INT2_DATA_READY_SHIFT) +#define K3G_INT2_WATERMARK_SHIFT 2 +#define K3G_INT2_WATERMARK ((0x1) << K3G_INT2_WATERMARK_SHIFT) +#define K3G_INT2_OVERRUN_SHIFT 1 +#define K3G_INT2_OVERRUN ((0x1) << K3G_INT2_OVERRUN_SHIFT) +#define K3G_INT2_EMPTY_SHIFT 0 +#define K3G_INT2_EMPTY ((0x1) << K3G_INT2_EMPTY_SHIFT) + +#define K3G_BLOCK_DATA_UPDATE_SHIFT 7 +#define K3G_BLOCK_DATA_UPDATE \ + ((0x1) << K3G_BLOCK_DATA_UPDATE_SHIFT) +#define K3G_BIG_LITTLE_ENDIAN_SHIFT 6 +#define K3G_BIG_ENDIAN ((0x0) << K3G_BIG_LITTLE_ENDIAN_SHIFT) +#define K3G_LITTLE_ENDIAN ((0x1) << K3G_BIG_LITTLE_ENDIAN_SHIFT) +#define K3G_FULL_SCALE_SHIFT 4 +#define K3G_FULL_SCALE_250DPS ((0x0) << K3G_FULL_SCALE_SHIFT) +#define K3G_FULL_SCALE_500DPS ((0x1) << K3G_FULL_SCALE_SHIFT) +#define K3G_FULL_SCALE_2000DPS ((0x2) << K3G_FULL_SCALE_SHIFT) +#define K3G_SELF_TEST_SHIFT 1 +#define K3G_SELF_TESET_NORMAL ((0x0) << K3G_SELF_TEST_SHIFT) +#define K3G_SELF_TESET_0 ((0x1) << K3G_SELF_TEST_SHIFT) +#define K3G_SELF_TESET_1 ((0x3) << K3G_SELF_TEST_SHIFT) +#define K3G_SPI_MODE_SHIFT 0 +#define K3G_SPI_FOUR_WIRE ((0x0) << K3G_SPI_MODE_SHIFT) +#define K3G_SPI_THREE_WIRE ((0x1) << K3G_SPI_MODE_SHIFT) + +#define K3G_REBOOT_SHIFT 7 +#define K3G_REBOOT ((0x1) << K3G_REBOOT_SHIFT) +#define K3G_FIFO_EN_SHIFT 6 +#define K3G_FIFO_EN ((0x1) << K3G_FIFO_EN_SHIFT) +#define K3G_HIGH_PASS_FILTER_EN_SHIFT 4 +#define K3G_HIGH_PASS_FILTER_EN \ + ((0x1) << K3G_HIGH_PASS_FILTER_EN_SHIFT) +#define K3G_INT1_SELECTION_SHIFT 2 +#define K3G_INT1_SELECTION ((0x3) << K3G_INT1_SELECTION_SHIFT) +#define K3G_OUT_SELECTION_SHIFT 0 +#define K3G_OUT_SELECTION ((0x3) << K3G_OUT_SELECTION_SHIFT) + +#define K3G_ZYX_OVERRUN_SHIFT 7 +#define K3G_ZYX_OVERRUN ((0x1) << K3G_ZYX_OVERRUN_SHIFT) +#define K3G_Z_OVERRUN_SHIFT 6 +#define K3G_Z_OVERRUN ((0x1) << K3G_Z_OVERRUN_SHIFT) +#define K3G_Y_OVERRUN_SHIFT 5 +#define K3G_Y_OVERRUN ((0x1) << K3G_Y_OVERRUN_SHIFT) +#define K3G_X_OVERRUN_SHIFT 4 +#define K3G_X_OVERRUN ((0x1) << K3G_X_OVERRUN_SHIFT) +#define K3G_ZYX_DATA_AVAILABLE_SHIFT 3 +#define K3G_ZYX_DATA_AVAILABEL \ + ((0x1) << K3G_ZYX_DATA_AVAILABLE_SHIFT) +#define K3G_Z_DATA_AVAILABLE_SHIFT 2 +#define K3G_Z_DATA_AVAILABLE ((0x1) << K3G_Z_DATA_AVAILABLE_SHIFT) +#define K3G_Y_DATA_AVAILABLE_SHIFT 1 +#define K3G_Y_DATA_AVAILABLE ((0x1) << K3G_Y_DATA_AVAILABLE_SHIFT) +#define K3G_X_DATA_AVAILABLE_SHIFT 0 +#define K3G_X_DATA_AVAILABLE ((0x1) << K3G_X_DATA_AVAILABLE_SHIFT) + +#define K3G_FIFO_MODE_SHIFT 5 +#define K3G_FIFO_BYPASS_MODE ((0x0) << K3G_FIFO_MODE_SHIFT) +#define K3G_FIFO_FIFO_MODE ((0x1) << K3G_FIFO_MODE_SHIFT) +#define K3G_FIFO_STREAM_MODE ((0x2) << K3G_FIFO_MODE_SHIFT) +#define K3G_FIFO_STREAM_TO_FIFO_MODE ((0x3) << K3G_FIFO_MODE_SHIFT) +#define K3G_FIFO_BYPASS_TO_STREAM_MODE ((0x4) << K3G_FIFO_MODE_SHIFT) +#define K3G_FIFO_MODE_MASK ((0x7) << K3G_FIFO_MODE_SHIFT) +#define K3G_WATERMARK_THRES_SHIFT 0 +#define K3G_WATERMARK_THRES_MASK ((0x1f) << K3G_WATERMARK_THRES_SHIFT) + +#define K3G_WATERMARK_SHIFT 7 +#define K3G_WATERMARK ((0x1) << K3G_WATERMARK_SHIFT) +#define K3G_OVERRUN_SHIFT 6 +#define K3G_OVERRUN ((0x1) << K3G_OVERRUN_SHIFT) +#define K3G_EMPTY_SHIFT 5 +#define K3G_EMPTY ((0x1) << K3G_EMPTY_SHIFT) +#define K3G_FIFO_STORED_SHIFT 0 +#define K3G_FIFO_STORED_MASK ((0x1f) << K3G_FIFO_STORED_SHIFT) + +#define K3G_AND_OR_COMBINATION_SHIFT 7 +#define K3G_OR_COMBINATION ((0x0) << K3G_AND_OR_COMBINATION_SHIFT) +#define K3G_AND_COMBINATION ((0x1) << K3G_AND_OR_COMBINATION_SHIFT) +#define K3G_LATCH_INTERRUPT_SHIFT 6 +#define K3G_INTERRUPT_NO_LATCHED ((0x0) << K3G_LATCH_INTERRUPT_SHIFT) +#define K3G_INTERRUPT_LATCHED ((0x1) << K3G_LATCH_INTERRUPT_SHIFT) +#define K3G_Z_HIGH_INT_EN_SHIFT 5 +#define K3G_Z_HIGH_INT_EN ((0x1) << K3G_Z_HIGH_INT_EN_SHIFT) +#define K3G_Z_LOW_INT_EN_SHIFT 4 +#define K3G_Z_LOW_INT_EN ((0x1) << K3G_Z_LOW_INT_EN_SHIFT) +#define K3G_Y_HIGH_INT_EN_SHIFT 3 +#define K3G_Y_HIGH_INT_EN ((0x1) << K3G_Y_HIGH_INT_EN_SHIFT) +#define K3G_Y_LOW_INT_EN_SHIFT 2 +#define K3G_Y_LOW_INT_EN ((0x1) << K3G_Y_LOW_INT_EN_SHIFT) +#define K3G_X_HIGH_INT_EN_SHIFT 1 +#define K3G_X_HIGH_INT_EN ((0x1) << K3G_X_HIGH_INT_EN_SHIFT) +#define K3G_X_LOW_INT_EN_SHIFT 0 +#define K3G_X_LOW_INT_EN ((0x1) << K3G_X_LOW_INT_EN_SHIFT) + +#define K3G_INTERRUPT_ACTIVE_SHIFT 6 +#define K3G_INTERRUPT_ACTIVE ((0x1) << K3G_INTERRUPT_ACTIVE_SHIFT) +#define K3G_Z_HIGH_SHIFT 5 +#define K3G_Z_HIGH ((0x1) << K3G_Z_HIGH_SHIFT) +#define K3G_Z_LOW_SHIFT 4 +#define K3G_Z_LOW ((0x1) << K3G_Z_LOW_SHIFT) +#define K3G_Y_HIGH_SHIFT 3 +#define K3G_Y_HIGH ((0x1) << K3G_Y_HIGH_SHIFT) +#define K3G_Y_LOW_SHIFT 2 +#define K3G_Y_LOW ((0x1) << K3G_Y_LOW_SHIFT) +#define K3G_X_HIGH_SHIFT 1 +#define K3G_X_HIGH ((0x1) << K3G_X_HIGH_SHIFT) +#define K3G_X_LOW_SHIFT 0 +#define K3G_X_LOW ((0x1) << K3G_X_LOW_SHIFT) + +#define K3G_INT1_WAIT_EN_SHIFT 7 +#define K3G_INT1_WAIT_EN ((0x1) << K3G_INT1_WAIT_EN_SHIFT) +#define K3G_INT1_DURATION_SHIFT 0 +#define K3G_INT1_DURATION_MASK ((0x7f) << K3G_INT1_DURATION_SHIFT) + +#define K3G_TEMP_SHIFT 0 +#define K3G_TEMP_MASK ((0xff) << K3G_TEMP_SHIFT) + +#define K3G_DEV_ID 0xd3 + +struct k3g_platform_data { + int irq2; + u8 data_rate; + u8 bandwidth; + u8 powerdown; + u8 zen; + u8 yen; + u8 xen; + u8 hpmode; + u8 hpcf; + u8 int1_enable; + u8 int1_boot; + u8 int1_hl_active; + u8 int_pp_od; + u8 int2_src; + u8 block_data_update; + u8 endian; + u8 fullscale; + u8 selftest; + u8 spi_mode; + u8 reboot; + u8 fifo_enable; + u8 hp_enable; + u8 int1_sel; + u8 out_sel; + u8 fifo_mode; + u8 fifo_threshold; + u8 int1_combination; + u8 int1_latch; + u8 int1_z_high_enable; + u8 int1_z_low_enable; + u8 int1_y_high_enable; + u8 int1_y_low_enable; + u8 int1_x_high_enable; + u8 int1_x_low_enable; + u8 int1_wait_enable; + u8 int1_wait_duration; + u16 int1_z_threshold; + u16 int1_y_threshold; + u16 int1_x_threshold; +}; + +struct k3g_chip { + struct i2c_client *client; + struct iio_dev *indio_dev; + struct work_struct work_thresh; + struct work_struct work_fifo; + struct iio_trigger *trig; + s64 last_timestamp; + struct mutex lock; + struct mutex ring_lock; + struct k3g_platform_data *pdata; +}; + +int k3g_set_8bit_value(struct k3g_chip *chip, u8 val, + u8 *cached_value, u8 shift, u8 mask, u8 reg); +int k3g_get_raw_value(struct k3g_chip *chip, int reg); +int k3g_get_8bit_value(struct k3g_chip *chip, u8 shift, u8 mask, u8 reg); + +int k3g_configure_ring(struct iio_dev *indio_dev); +void k3g_unconfigure_ring(struct iio_dev *indio_dev); + +void k3g_register_ring_funcs(struct iio_dev *indio_dev); +void k3g_ring_int_process(int val, struct iio_ring_buffer *ring); + +#endif diff --git a/drivers/staging/iio/gyro/k3g_core.c b/drivers/staging/iio/gyro/k3g_core.c new file mode 100644 index 0000000..97891d0 --- /dev/null +++ b/drivers/staging/iio/gyro/k3g_core.c @@ -0,0 +1,846 @@ +/* + * k3g_core.c - ST Microelectronics three-axis gyroscope sensor + * + * Copyright (C) 2010 Samsung Electronics + * Donggeun Kim <dg77.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> + +#include "../iio.h" +#include "../ring_generic.h" +#include "gyro.h" +#include "k3g.h" + +#define K3G_INT_LINE 2 + +int k3g_set_8bit_value(struct k3g_chip *chip, u8 val, + u8 *cached_value, u8 shift, u8 mask, u8 reg) +{ + int ret = 0; + u8 value, temp; + + mutex_lock(&chip->lock); + + temp = (val << shift) & mask; + if (temp == *cached_value) + goto out; + + ret = value = i2c_smbus_read_byte_data(chip->client, reg); + if (ret < 0) + goto out; + + value &= ~mask; + value |= temp; + *cached_value = temp; + + ret = i2c_smbus_write_byte_data(chip->client, reg, value); +out: + mutex_unlock(&chip->lock); + return ret; +} + +int k3g_get_raw_value(struct k3g_chip *chip, int reg) +{ + u8 values[2]; + s16 raw_value; + int ret; + + mutex_lock(&chip->lock); + ret = values[0] = i2c_smbus_read_byte_data(chip->client, reg); + if (ret < 0) + goto out; + + ret = values[1] = i2c_smbus_read_byte_data(chip->client, + reg + 1); + if (ret < 0) + goto out; + + raw_value = (values[1] << BITS_PER_BYTE) | values[0]; +out: + mutex_unlock(&chip->lock); + return raw_value; +} + +int k3g_get_8bit_value(struct k3g_chip *chip, u8 shift, u8 mask, u8 reg) +{ + int ret = 0; + + mutex_lock(&chip->lock); + + ret = i2c_smbus_read_byte_data(chip->client, reg); + if (ret < 0) + goto out; + + ret &= mask; + ret >>= shift; +out: + mutex_unlock(&chip->lock); + return ret; +} + +static int k3g_set_16bit_value(struct k3g_chip *chip, u16 val, + u16 *cached_value, u8 reg) +{ + u8 values[2]; + int ret = 0; + + mutex_lock(&chip->lock); + + values[0] = val >> BITS_PER_BYTE; + values[1] = val & 0xff; + + ret = i2c_smbus_write_byte_data(chip->client, reg, values[0]); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_byte_data(chip->client, + (reg + 1), values[1]); + if (ret < 0) + goto out; + + *cached_value = (values[0] << BITS_PER_BYTE) | values[1]; +out: + mutex_unlock(&chip->lock); + return ret; +} + +static ssize_t k3g_output_raw(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct k3g_chip *chip = indio_dev->dev_data; + int ret = k3g_get_raw_value(chip, this_attr->address); + + return sprintf(buf, "%d\n", ret); +} + +#define K3G_OUTPUT(name, shift, mask, reg) \ +static ssize_t k3g_show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct iio_dev *indio_dev = dev_get_drvdata(dev); \ + struct k3g_chip *chip = indio_dev->dev_data; \ + int ret = k3g_get_8bit_value(chip, shift, mask, reg); \ + return sprintf(buf, "%d\n", ret); \ +} \ +static IIO_DEVICE_ATTR(name, S_IRUGO, k3g_show_##name, NULL, 0); + +#define K3G_INPUT(name, field_name, shift, mask, reg) \ +static ssize_t k3g_store_##name(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct iio_dev *indio_dev = dev_get_drvdata(dev); \ + struct k3g_chip *chip = indio_dev->dev_data; \ + unsigned long val; \ + int ret; \ + \ + if (!count) \ + return -EINVAL; \ + \ + ret = strict_strtoul(buf, 10, &val); \ + if (ret) \ + return -EINVAL; \ + \ + ret = k3g_set_8bit_value(chip, val, \ + (u8 *) &chip->pdata->field_name, shift, mask, reg); \ + \ + if (ret < 0) \ + return ret; \ + \ + return count; \ +} \ +static ssize_t k3g_show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct iio_dev *indio_dev = dev_get_drvdata(dev); \ + struct k3g_chip *chip = indio_dev->dev_data; \ + int ret = chip->pdata->field_name >> shift; \ + \ + return sprintf(buf, "%d\n", ret); \ +} \ +static IIO_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \ + k3g_show_##name, k3g_store_##name, 0); + +#define K3G_INPUT_16BIT(name, field_name, reg) \ +static ssize_t k3g_store_##name(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct iio_dev *indio_dev = dev_get_drvdata(dev); \ + struct k3g_chip *chip = indio_dev->dev_data; \ + unsigned long val; \ + int ret; \ + \ + if (!count) \ + return -EINVAL; \ + \ + ret = strict_strtoul(buf, 10, &val); \ + if (ret) \ + return -EINVAL; \ + \ + ret = k3g_set_16bit_value(chip, val, \ + (u16 *) &chip->pdata->field_name, reg); \ + \ + if (ret < 0) \ + return ret; \ + \ + return count; \ +} \ +static ssize_t k3g_show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct iio_dev *indio_dev = dev_get_drvdata(dev); \ + struct k3g_chip *chip = indio_dev->dev_data; \ + int ret = chip->pdata->field_name; \ + \ + return sprintf(buf, "%d\n", ret); \ +} \ +static IIO_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \ + k3g_show_##name, k3g_store_##name, 0); + +K3G_INPUT(power_down, powerdown, K3G_POWERDOWN_SHIFT, + K3G_POWERDOWN_MASK, K3G_CTRL_REG1); +K3G_INPUT(z_en, zen, K3G_Z_EN_SHIFT, K3G_Z_EN, K3G_CTRL_REG1); +K3G_INPUT(y_en, yen, K3G_Y_EN_SHIFT, K3G_Y_EN, K3G_CTRL_REG1); +K3G_INPUT(x_en, xen, K3G_X_EN_SHIFT, K3G_X_EN, K3G_CTRL_REG1); +K3G_INPUT(reboot, reboot, K3G_REBOOT_SHIFT, K3G_REBOOT, K3G_CTRL_REG5); + +K3G_OUTPUT(temp_raw, K3G_TEMP_SHIFT, K3G_TEMP_MASK, K3G_OUT_TEMP); + +static IIO_DEV_ATTR_GYRO_X(k3g_output_raw, K3G_OUT_X_L); +static IIO_DEV_ATTR_GYRO_Y(k3g_output_raw, K3G_OUT_Y_L); +static IIO_DEV_ATTR_GYRO_Z(k3g_output_raw, K3G_OUT_Z_L); + +static struct attribute *k3g_attributes[] = { + &iio_dev_attr_power_down.dev_attr.attr, + &iio_dev_attr_z_en.dev_attr.attr, + &iio_dev_attr_y_en.dev_attr.attr, + &iio_dev_attr_x_en.dev_attr.attr, + &iio_dev_attr_reboot.dev_attr.attr, + &iio_dev_attr_temp_raw.dev_attr.attr, + &iio_dev_attr_gyro_x_raw.dev_attr.attr, + &iio_dev_attr_gyro_y_raw.dev_attr.attr, + &iio_dev_attr_gyro_z_raw.dev_attr.attr, + NULL +}; + +static const struct attribute_group k3g_group = { + .attrs = k3g_attributes, +}; + +static irqreturn_t k3g_thresh_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct k3g_chip *chip; + int int1_src; + s64 last_timestamp = iio_get_time_ns(); + + chip = indio_dev->dev_data; + int1_src = i2c_smbus_read_byte_data(chip->client, K3G_INT1_SRC_REG); + if (int1_src < 0) + pr_err("K3G_INT1_SRC_REG read fail(%d)\n", int1_src); + else if (int1_src & K3G_INTERRUPT_ACTIVE) { + if ((int1_src & K3G_Z_HIGH) && + (chip->pdata->int1_z_high_enable & K3G_Z_HIGH_INT_EN)) + iio_push_event(chip->indio_dev, 0, + IIO_MOD_EVENT_CODE(IIO_EV_CLASS_GYRO, + 0, + IIO_EV_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + last_timestamp); + + if ((int1_src & K3G_Z_LOW) && + (chip->pdata->int1_z_low_enable & K3G_Z_LOW_INT_EN)) + iio_push_event(chip->indio_dev, 0, + IIO_MOD_EVENT_CODE(IIO_EV_CLASS_GYRO, + 0, + IIO_EV_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + last_timestamp); + + if ((int1_src & K3G_Y_HIGH) && + (chip->pdata->int1_y_high_enable & K3G_Y_HIGH_INT_EN)) + iio_push_event(chip->indio_dev, 0, + IIO_MOD_EVENT_CODE(IIO_EV_CLASS_GYRO, + 0, + IIO_EV_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + last_timestamp); + + if ((int1_src & K3G_Y_LOW) && + (chip->pdata->int1_y_low_enable & K3G_Y_LOW_INT_EN)) + iio_push_event(chip->indio_dev, 0, + IIO_MOD_EVENT_CODE(IIO_EV_CLASS_GYRO, + 0, + IIO_EV_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + last_timestamp); + + if ((int1_src & K3G_X_HIGH) && + (chip->pdata->int1_x_high_enable & K3G_X_HIGH_INT_EN)) + iio_push_event(chip->indio_dev, 0, + IIO_MOD_EVENT_CODE(IIO_EV_CLASS_GYRO, + 0, + IIO_EV_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + last_timestamp); + + if ((int1_src & K3G_X_LOW) && + (chip->pdata->int1_x_low_enable & K3G_X_LOW_INT_EN)) + iio_push_event(chip->indio_dev, 0, + IIO_MOD_EVENT_CODE(IIO_EV_CLASS_GYRO, + 0, + IIO_EV_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + last_timestamp); + } + + return IRQ_HANDLED; +} + +K3G_INPUT(int1_en, int1_enable, K3G_INT1_EN_SHIFT, K3G_INT1_EN, K3G_CTRL_REG3); +K3G_INPUT(wait_en, int1_wait_enable, K3G_INT1_WAIT_EN_SHIFT, + K3G_INT1_WAIT_EN, K3G_INT1_DURATION_REG); +K3G_INPUT(gyro_z_mag_pos_rising_en, int1_z_high_enable, + K3G_Z_HIGH_INT_EN_SHIFT, K3G_Z_HIGH_INT_EN, K3G_INT1_CFG_REG); +K3G_INPUT(gyro_z_mag_neg_rising_en, int1_z_low_enable, + K3G_Z_LOW_INT_EN_SHIFT, K3G_Z_LOW_INT_EN, K3G_INT1_CFG_REG); +K3G_INPUT(gyro_y_mag_pos_rising_en, int1_y_high_enable, + K3G_Y_HIGH_INT_EN_SHIFT, K3G_Y_HIGH_INT_EN, K3G_INT1_CFG_REG); +K3G_INPUT(gyro_y_mag_neg_rising_en, int1_y_low_enable, + K3G_Y_LOW_INT_EN_SHIFT, K3G_Y_LOW_INT_EN, K3G_INT1_CFG_REG); +K3G_INPUT(gyro_x_mag_pos_rising_en, int1_x_high_enable, + K3G_X_HIGH_INT_EN_SHIFT, K3G_X_HIGH_INT_EN, K3G_INT1_CFG_REG); +K3G_INPUT(gyro_x_mag_neg_rising_en, int1_x_low_enable, + K3G_X_LOW_INT_EN_SHIFT, K3G_X_LOW_INT_EN, K3G_INT1_CFG_REG); +K3G_INPUT(gyro_mag_either_rising_period, int1_wait_duration, + K3G_INT1_DURATION_SHIFT, K3G_INT1_DURATION_MASK, + K3G_INT1_DURATION_REG); +K3G_INPUT_16BIT(gyro_x_mag_either_rising_value, int1_x_threshold, + K3G_INT1_THS_XH); +K3G_INPUT_16BIT(gyro_y_mag_either_rising_value, int1_y_threshold, + K3G_INT1_THS_YH); +K3G_INPUT_16BIT(gyro_z_mag_either_rising_value, int1_z_threshold, + K3G_INT1_THS_ZH); + +static struct attribute *k3g_threshold_event_attributes[] = { + &iio_dev_attr_int1_en.dev_attr.attr, + &iio_dev_attr_wait_en.dev_attr.attr, + &iio_dev_attr_gyro_z_mag_pos_rising_en.dev_attr.attr, + &iio_dev_attr_gyro_z_mag_neg_rising_en.dev_attr.attr, + &iio_dev_attr_gyro_y_mag_pos_rising_en.dev_attr.attr, + &iio_dev_attr_gyro_y_mag_neg_rising_en.dev_attr.attr, + &iio_dev_attr_gyro_x_mag_pos_rising_en.dev_attr.attr, + &iio_dev_attr_gyro_x_mag_neg_rising_en.dev_attr.attr, + &iio_dev_attr_gyro_mag_either_rising_period.dev_attr.attr, + &iio_dev_attr_gyro_x_mag_either_rising_value.dev_attr.attr, + &iio_dev_attr_gyro_y_mag_either_rising_value.dev_attr.attr, + &iio_dev_attr_gyro_z_mag_either_rising_value.dev_attr.attr, + NULL +}; + +static irqreturn_t k3g_fifo_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct k3g_chip *chip; + int int2_src, ctrl_reg3; + s64 last_timestamp = iio_get_time_ns(); + + chip = indio_dev->dev_data; + int2_src = i2c_smbus_read_byte_data(chip->client, K3G_FIFO_SRC_REG); + if (int2_src < 0) + goto fifo_done; + + ctrl_reg3 = i2c_smbus_read_byte_data(chip->client, K3G_CTRL_REG3); + if (ctrl_reg3 < 0) + goto fifo_done; + + if (ctrl_reg3 & K3G_INT2_DATA_READY) + iio_push_event(chip->indio_dev, 1, + IIO_MOD_EVENT_CODE(IIO_EV_CLASS_GYRO, + 0, + IIO_EV_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + last_timestamp); + else + k3g_ring_int_process(int2_src, chip->indio_dev->ring); + + enable_irq(chip->pdata->irq2); + +fifo_done: + return IRQ_HANDLED; +} + +static ssize_t k3g_store_int2_src(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct k3g_chip *chip = indio_dev->dev_data; + unsigned long val; + int ret; + + if (!count) + return -EINVAL; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return -EINVAL; + + if (val < 4) + val = 1 << val; + else + return -EINVAL; + + ret = k3g_set_8bit_value(chip, val, &chip->pdata->int2_src, + K3G_INT2_SRC_SHIFT, K3G_INT2_SRC_MASK, K3G_CTRL_REG3); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t k3g_show_int2_src(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct k3g_chip *chip = indio_dev->dev_data; + int val = chip->pdata->int2_src >> K3G_INT2_SRC_SHIFT; + + return sprintf(buf, "%d\n", !!(val & this_attr->address)); +} + +IIO_DEVICE_ATTR(data_rdy, S_IRUGO | S_IWUSR, k3g_show_int2_src, + k3g_store_int2_src, K3G_INT2_DATA_READY); +IIO_DEVICE_ATTR(ring_empty, S_IRUGO | S_IWUSR, k3g_show_int2_src, + k3g_store_int2_src, K3G_INT2_EMPTY); +IIO_DEVICE_ATTR(ring_100_full, S_IRUGO | S_IWUSR, k3g_show_int2_src, + k3g_store_int2_src, K3G_INT2_OVERRUN); +IIO_DEVICE_ATTR(ring_watermark, S_IRUGO | S_IWUSR, k3g_show_int2_src, + k3g_store_int2_src, K3G_INT2_WATERMARK); + +static ssize_t k3g_store_fifo_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct k3g_chip *chip = indio_dev->dev_data; + unsigned long val; + int ret; + + if (!count) + return -EINVAL; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return -EINVAL; + + ret = k3g_set_8bit_value(chip, val, &chip->pdata->fifo_mode, + K3G_FIFO_MODE_SHIFT, K3G_FIFO_MODE_MASK, K3G_FIFO_CTRL_REG); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t k3g_show_fifo_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct k3g_chip *chip = indio_dev->dev_data; + int ret = chip->pdata->fifo_mode >> K3G_FIFO_MODE_SHIFT; + int len = 0; + + ret <<= K3G_FIFO_MODE_SHIFT; + + switch (ret) { + case K3G_FIFO_BYPASS_MODE: + len = sprintf(buf, "BYPASS_MODE\n"); + break; + case K3G_FIFO_FIFO_MODE: + len = sprintf(buf, "FIFO_MODE\n"); + break; + case K3G_FIFO_STREAM_MODE: + len = sprintf(buf, "STREAM_MODE\n"); + break; + case K3G_FIFO_STREAM_TO_FIFO_MODE: + len = sprintf(buf, "STREAM_TO_FIFO_MODE\n"); + break; + case K3G_FIFO_BYPASS_TO_STREAM_MODE: + len = sprintf(buf, "BYPASS_TO_STREAM_MODE\n"); + break; + default: + len = sprintf(buf, "NOTHING\n"); + break; + } + + return len; +} + +static IIO_DEVICE_ATTR(fifo_mode, S_IRUGO | S_IWUSR, + k3g_show_fifo_mode, k3g_store_fifo_mode, 0); + +static struct attribute *k3g_fifo_event_attributes[] = { + &iio_dev_attr_data_rdy.dev_attr.attr, + &iio_dev_attr_ring_empty.dev_attr.attr, + &iio_dev_attr_ring_100_full.dev_attr.attr, + &iio_dev_attr_ring_watermark.dev_attr.attr, + &iio_dev_attr_fifo_mode.dev_attr.attr, + NULL +}; + +static struct attribute_group k3g_event_group[K3G_INT_LINE] = { + { + .attrs = k3g_threshold_event_attributes, + }, { + .attrs = k3g_fifo_event_attributes, + } +}; + +static int k3g_initialize_chip(struct k3g_chip *chip) +{ + u8 value, threshold[6]; + int ret; + + /* CTRL_REG2 */ + value = chip->pdata->hpmode | chip->pdata->hpcf; + ret = i2c_smbus_write_byte_data(chip->client, K3G_CTRL_REG2, value); + if (ret < 0) + return ret; + + /* CTRL_REG3 */ + value = chip->pdata->int1_enable | chip->pdata->int1_boot | + chip->pdata->int1_hl_active | chip->pdata->int_pp_od | + chip->pdata->int2_src; + ret = i2c_smbus_write_byte_data(chip->client, K3G_CTRL_REG3, value); + if (ret < 0) + return ret; + + /* CTRL_REG4 */ + value = chip->pdata->block_data_update | chip->pdata->endian | + chip->pdata->fullscale | chip->pdata->selftest | + chip->pdata->spi_mode; + ret = i2c_smbus_write_byte_data(chip->client, K3G_CTRL_REG4, value); + if (ret < 0) + return ret; + + /* CTRL_REG5 */ + value = chip->pdata->reboot | chip->pdata->fifo_enable | + chip->pdata->hp_enable | chip->pdata->int1_sel | + chip->pdata->out_sel; + ret = i2c_smbus_write_byte_data(chip->client, K3G_CTRL_REG5, value); + if (ret < 0) + return ret; + + /* FIFO_CTRL_REG */ + value = chip->pdata->fifo_mode | chip->pdata->fifo_threshold; + ret = i2c_smbus_write_byte_data(chip->client, K3G_FIFO_CTRL_REG, + value); + if (ret < 0) + return ret; + + /* INT1_CFG_REG */ + value = chip->pdata->int1_combination | chip->pdata->int1_latch | + chip->pdata->int1_z_high_enable | + chip->pdata->int1_z_low_enable | + chip->pdata->int1_y_high_enable | + chip->pdata->int1_y_low_enable | + chip->pdata->int1_x_high_enable | + chip->pdata->int1_x_low_enable; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_CFG_REG, value); + if (ret < 0) + return ret; + + /* INT1_THS_REG */ + threshold[0] = chip->pdata->int1_x_threshold >> BITS_PER_BYTE; + threshold[1] = chip->pdata->int1_x_threshold & 0xff; + threshold[2] = chip->pdata->int1_y_threshold >> BITS_PER_BYTE; + threshold[3] = chip->pdata->int1_y_threshold & 0xff; + threshold[4] = chip->pdata->int1_z_threshold >> BITS_PER_BYTE; + threshold[5] = chip->pdata->int1_z_threshold & 0xff; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_THS_XH, + threshold[0]); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_THS_XL, + threshold[1]); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_THS_YH, + threshold[2]); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_THS_YL, + threshold[3]); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_THS_ZH, + threshold[4]); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_THS_ZL, + threshold[5]); + if (ret < 0) + return ret; + + /* INT1_DURATION_REG */ + value = chip->pdata->int1_wait_enable | + chip->pdata->int1_wait_duration; + ret = i2c_smbus_write_byte_data(chip->client, K3G_INT1_DURATION_REG, + value); + if (ret < 0) + return ret; + + /* CTRL_REG1 */ + value = chip->pdata->data_rate | chip->pdata->bandwidth | + chip->pdata->powerdown | chip->pdata->zen | + chip->pdata->yen | chip->pdata->xen; + ret = i2c_smbus_write_byte_data(chip->client, K3G_CTRL_REG1, value); + if (ret < 0) + return ret; + ret = i2c_smbus_read_byte_data(chip->client, K3G_CTRL_REG1); + return 0; +} + +#define K3G_EVENT_MASK (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER)) + +static struct iio_chan_spec k3g_channels[] = { + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_X, 0, + 0, 0, IIO_ST('s', 16, 16, 0), K3G_EVENT_MASK), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_Y, 0, + 1, 1, IIO_ST('s', 16, 16, 0), K3G_EVENT_MASK), + IIO_CHAN(IIO_GYRO, 1, 0, 0, NULL, 0, IIO_MOD_Z, 0, + 2, 2, IIO_ST('s', 16, 16, 0), K3G_EVENT_MASK), +}; + +static const struct iio_info k3g_info = { + .driver_module = THIS_MODULE, + .num_interrupt_lines = K3G_INT_LINE, + .event_attrs = k3g_event_group, + .attrs = &k3g_group, +}; + +static int __devinit k3g_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct k3g_chip *chip; + int ret, regdone = 0; + + chip = kzalloc(sizeof(struct k3g_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + /* Detect device id */ + ret = i2c_smbus_read_byte_data(client, K3G_WHO_AM_I); + if (ret != K3G_DEV_ID) { + dev_err(&client->dev, "failed to detect device id\n"); + goto error_devid_detect; + } + + chip->pdata = client->dev.platform_data; + if (!chip->pdata) { + ret = -ENOENT; + goto error_no_pdata; + } + chip->client = client; + + i2c_set_clientdata(client, chip); + mutex_init(&chip->lock); + mutex_init(&chip->ring_lock); + + chip->indio_dev = iio_allocate_device(0); + if (!chip->indio_dev) + goto error_allocate_iio; + + chip->indio_dev->name = client->name; + chip->indio_dev->dev.parent = &client->dev; + chip->indio_dev->dev_data = (void *)(chip); + chip->indio_dev->modes = INDIO_DIRECT_MODE; + chip->indio_dev->info = &k3g_info; + + ret = k3g_configure_ring(chip->indio_dev); + if (ret) + goto error_register_iio; + + ret = iio_device_register(chip->indio_dev); + if (ret) + goto error_register_ring_buffer; + regdone = 1; + + ret = iio_ring_buffer_register_ex(chip->indio_dev->ring, 0, + k3g_channels, + ARRAY_SIZE(k3g_channels)); + if (ret) { + dev_err(&client->dev, "failed to initialize the ring\n"); + goto error_register_ring_buffer; + } + + if (client->irq > 0) { + ret = request_threaded_irq(client->irq, + NULL, + &k3g_thresh_handler, + IRQF_TRIGGER_RISING, + "K3G INT1", + chip->indio_dev); + if (ret) + goto error_register_interrupt1; + } + + if (chip->pdata->irq2 > 0) { + ret = request_threaded_irq(chip->pdata->irq2, + NULL, + &k3g_fifo_handler, + IRQF_TRIGGER_RISING, + "K3G INT2", + chip->indio_dev); + if (ret) + goto error_register_interrupt2; + } + + k3g_register_ring_funcs(chip->indio_dev); + + ret = k3g_initialize_chip(chip); + if (ret) + goto error_initialize; + + pm_runtime_set_active(&client->dev); + + dev_info(&client->dev, "%s registered\n", id->name); + + return 0; + +error_initialize: + if (chip->pdata->irq2 > 0) + free_irq(chip->pdata->irq2, chip->indio_dev); +error_register_interrupt2: + if (client->irq > 0) + free_irq(client->irq, chip->indio_dev); +error_register_interrupt1: + iio_ring_buffer_unregister(chip->indio_dev->ring); +error_register_ring_buffer: + k3g_unconfigure_ring(chip->indio_dev); +error_register_iio: + if (regdone) + iio_device_unregister(chip->indio_dev); + else + iio_free_device(chip->indio_dev); +error_allocate_iio: +error_no_pdata: +error_devid_detect: + kfree(chip); + return ret; +} + +static int __devexit k3g_remove(struct i2c_client *client) +{ + struct k3g_chip *chip = i2c_get_clientdata(client); + + disable_irq_nosync(client->irq); + disable_irq_nosync(chip->pdata->irq2); + cancel_work_sync(&chip->work_thresh); + cancel_work_sync(&chip->work_fifo); + + if (client->irq > 0) + free_irq(client->irq, chip->indio_dev); + + if (chip->pdata->irq2 > 0) + free_irq(chip->pdata->irq2, chip->indio_dev); + + iio_ring_buffer_unregister(chip->indio_dev->ring); + k3g_unconfigure_ring(chip->indio_dev); + iio_device_unregister(chip->indio_dev); + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM +static int k3g_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct k3g_chip *chip = i2c_get_clientdata(client); + + disable_irq_nosync(client->irq); + disable_irq_nosync(chip->pdata->irq2); + cancel_work_sync(&chip->work_thresh); + cancel_work_sync(&chip->work_fifo); + + k3g_set_8bit_value(chip, false, &chip->pdata->powerdown, + K3G_POWERDOWN_SHIFT, K3G_POWERDOWN_MASK, K3G_CTRL_REG1); + + return 0; +} + +static int k3g_resume(struct i2c_client *client) +{ + struct k3g_chip *chip = i2c_get_clientdata(client); + + k3g_set_8bit_value(chip, true, &chip->pdata->powerdown, + K3G_POWERDOWN_SHIFT, K3G_POWERDOWN_MASK, K3G_CTRL_REG1); + + enable_irq(client->irq); + enable_irq(chip->pdata->irq2); + + k3g_initialize_chip(chip); + + return 0; +} +#else +#define k3g_suspend NULL +#define k3g_resume NULL +#endif + +static const struct i2c_device_id k3g_id[] = { + { "K3G", 0 }, + { "K3G_1", 1 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, k3g_id); + +static struct i2c_driver k3g_i2c_driver = { + .driver = { + .name = "K3G", + }, + .probe = k3g_probe, + .remove = __exit_p(k3g_remove), + .suspend = k3g_suspend, + .resume = k3g_resume, + .id_table = k3g_id, +}; + +static int __init k3g_init(void) +{ + return i2c_add_driver(&k3g_i2c_driver); +} +module_init(k3g_init); + +static void __exit k3g_exit(void) +{ + i2c_del_driver(&k3g_i2c_driver); +} +module_exit(k3g_exit); + +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); +MODULE_DESCRIPTION("K3G Gyroscope sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/gyro/k3g_ring.c b/drivers/staging/iio/gyro/k3g_ring.c new file mode 100644 index 0000000..538b315 --- /dev/null +++ b/drivers/staging/iio/gyro/k3g_ring.c @@ -0,0 +1,277 @@ +/* + * k3g_ring.c - ST Microelectronics three-axis gyroscope sensor + * + * Copyright (C) 2010 Samsung Electronics + * Donggeun Kim <dg77.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/interrupt.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <linux/uaccess.h> + +#include "../iio.h" +#include "../sysfs.h" +#include "../ring_generic.h" +#include "../ring_hw.h" +#include "gyro.h" +#include "k3g.h" + +/** + * k3g_rip_hw_rb() - main ring access function, pulls data from ring + * @r: the ring + * @count: number of samples to try and pull + * @data: output the actual samples pulled from the hw ring + * + * Currently does not provide timestamps. As the hardware doesn't add them they + * can only be inferred aproximately from ring buffer events such as 50% full + * and knowledge of when buffer was last emptied. This is left to userspace. + **/ +static int k3g_read_first_n_hw_rb(struct iio_ring_buffer *r, + size_t count, char __user *buf) +{ + struct iio_hw_ring_buffer *hw_ring = iio_to_hw_ring_buf(r); + struct iio_dev *indio_dev = hw_ring->private; + struct k3g_chip *chip = indio_dev->dev_data; + int ret = 0, scan_size, i; + int bytes_per_sample = 2, num_channels = 3; + u8 *data; + + mutex_lock(&chip->ring_lock); + scan_size = bytes_per_sample * num_channels; + if ((count % scan_size) || (count < scan_size)) { + ret = -EINVAL; + goto error_ret; + } + + data = kzalloc(count, GFP_KERNEL); + if (data == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + for (i = 0 ; i < count / scan_size; i++) + ret = i2c_smbus_read_i2c_block_data(chip->client, + K3G_MULTI_OUT_X_L, 6, data + (i * scan_size)); + + if (copy_to_user(buf, data, count)) + ret = -EFAULT; + kfree(data); + r->stufftoread = 0; +error_ret: + mutex_unlock(&chip->ring_lock); + + return (ret < 0) ? ret : count; +} + +static int k3g_ring_get_length(struct iio_ring_buffer *r) +{ + return 32; +} + +static int k3g_ring_get_bytes_per_datum(struct iio_ring_buffer *r) +{ + return 6; +} +static void k3g_ring_release(struct device *dev) +{ + struct iio_ring_buffer *r = to_iio_ring_buffer(dev); + kfree(iio_to_hw_ring_buf(r)); +} + +static IIO_RING_ENABLE_ATTR; +static IIO_RING_BYTES_PER_DATUM_ATTR; +static IIO_RING_LENGTH_ATTR; + +static ssize_t k3g_show_ring_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_ring_buffer *ring = dev_get_drvdata(dev); + struct iio_dev *indio_dev = ring->indio_dev; + struct k3g_chip *chip = indio_dev->dev_data; + int ret = k3g_get_8bit_value(chip, K3G_FIFO_STORED_SHIFT, + K3G_FIFO_STORED_MASK, K3G_FIFO_SRC_REG); + return sprintf(buf, "%d\n", ret); +} +static IIO_DEVICE_ATTR(ring_level, S_IRUGO, + k3g_show_ring_level, NULL, 0); + +static ssize_t k3g_store_watermark_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_ring_buffer *ring = dev_get_drvdata(dev); + struct iio_dev *indio_dev = ring->indio_dev; + struct k3g_chip *chip = indio_dev->dev_data; + unsigned long val; + int ret; + + if (!count) + return -EINVAL; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return -EINVAL; + + ret = k3g_set_8bit_value(chip, val, + (u8 *) &chip->pdata->fifo_threshold, K3G_WATERMARK_THRES_SHIFT, + K3G_WATERMARK_THRES_MASK, K3G_FIFO_CTRL_REG); + + if (ret < 0) + return ret; + + return count; +} +static ssize_t k3g_show_watermark_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_ring_buffer *ring = dev_get_drvdata(dev); + struct iio_dev *indio_dev = ring->indio_dev; + struct k3g_chip *chip = indio_dev->dev_data; + int ret = chip->pdata->fifo_threshold >> K3G_WATERMARK_THRES_SHIFT; + + return sprintf(buf, "%d\n", ret); +} +static IIO_DEVICE_ATTR(watermark_level, S_IRUGO | S_IWUSR, + k3g_show_watermark_level, k3g_store_watermark_level, 0); + +/* + * Ring buffer attributes + * This device is a bit unusual in that the sampling frequency and bpse + * only apply to the ring buffer. At all times full rate and accuracy + * is available via direct reading from registers. + */ +static struct attribute *k3g_ring_attributes[] = { + &dev_attr_length.attr, + &dev_attr_bytes_per_datum.attr, + &dev_attr_enable.attr, + &iio_dev_attr_ring_level.dev_attr.attr, + &iio_dev_attr_watermark_level.dev_attr.attr, + NULL, +}; + +static struct attribute_group k3g_ring_attr = { + .attrs = k3g_ring_attributes, +}; + +static const struct attribute_group *k3g_ring_attr_groups[] = { + &k3g_ring_attr, + NULL +}; + +static struct device_type k3g_ring_type = { + .release = k3g_ring_release, + .groups = k3g_ring_attr_groups, +}; + +static struct iio_ring_buffer *k3g_rb_allocate(struct iio_dev *indio_dev) +{ + struct iio_ring_buffer *buf; + struct iio_hw_ring_buffer *ring; + + ring = kzalloc(sizeof *ring, GFP_KERNEL); + if (!ring) + return NULL; + + ring->private = indio_dev; + buf = &ring->buf; + buf->stufftoread = 0; + iio_ring_buffer_init(buf, indio_dev); + buf->dev.type = &k3g_ring_type; + buf->dev.parent = &indio_dev->dev; + dev_set_drvdata(&buf->dev, (void *)buf); + + return buf; +} + +static inline void k3g_rb_free(struct iio_ring_buffer *r) +{ + if (r) + iio_put_ring_buffer(r); +} + +static const struct iio_ring_access_funcs k3g_ring_access_funcs = { + .read_first_n = &k3g_read_first_n_hw_rb, + .get_length = &k3g_ring_get_length, + .get_bytes_per_datum = &k3g_ring_get_bytes_per_datum, +}; + +int k3g_configure_ring(struct iio_dev *indio_dev) +{ + indio_dev->ring = k3g_rb_allocate(indio_dev); + if (indio_dev->ring == NULL) + return -ENOMEM; + indio_dev->modes |= INDIO_RING_HARDWARE_BUFFER; + + indio_dev->ring->access = &k3g_ring_access_funcs; + + iio_scan_mask_set(indio_dev->ring, 0); + iio_scan_mask_set(indio_dev->ring, 1); + iio_scan_mask_set(indio_dev->ring, 2); + + return 0; +} + +void k3g_unconfigure_ring(struct iio_dev *indio_dev) +{ + k3g_rb_free(indio_dev->ring); +} + +static inline +int __k3g_hw_ring_state_set(struct iio_dev *indio_dev, bool state) +{ + struct k3g_chip *chip = indio_dev->dev_data; + int ret; + + ret = k3g_set_8bit_value(chip, state, + &chip->pdata->fifo_enable, + K3G_FIFO_EN_SHIFT, K3G_FIFO_EN, K3G_CTRL_REG5); + + return ret; +} +/** + * k3g_hw_ring_preenable() hw ring buffer preenable function + * + * Very simple enable function as the chip will allows normal reads + * during ring buffer operation so as long as it is indeed running + * before we notify the core, the precise ordering does not matter. + **/ +static int k3g_hw_ring_preenable(struct iio_dev *indio_dev) +{ + return __k3g_hw_ring_state_set(indio_dev, 1); +} + +static int k3g_hw_ring_postdisable(struct iio_dev *indio_dev) +{ + return __k3g_hw_ring_state_set(indio_dev, 0); +} + +static const struct iio_ring_setup_ops k3g_ring_setup_ops = { + .preenable = &k3g_hw_ring_preenable, + .postdisable = &k3g_hw_ring_postdisable, +}; + +void k3g_register_ring_funcs(struct iio_dev *indio_dev) +{ + indio_dev->ring->setup_ops = &k3g_ring_setup_ops; +} + +/** + * k3g_ring_int_process() ring specific interrupt handling. + * + * This is only split from the main interrupt handler so as to + * reduce the amount of code if the ring buffer is not enabled. + **/ +void k3g_ring_int_process(int val, struct iio_ring_buffer *ring) +{ + if (val & (K3G_EMPTY | K3G_OVERRUN | K3G_WATERMARK)) { + ring->stufftoread = true; + wake_up_interruptible(&ring->pollq); + } +} |