diff options
Diffstat (limited to 'drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c')
-rw-r--r-- | drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c new file mode 100644 index 000000000000..ed91cf49516f --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c @@ -0,0 +1,163 @@ +/* +* 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. +* +*/ + +/** + * @addtogroup DRIVERS + * @brief Hardware drivers. + * + * @{ + * @file inv_ami306_ring.c + * @brief Invensense implementation for AMI306 + * @details This driver currently works for the AMI306 + */ + +#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 <linux/miscdevice.h> + +#include "iio.h" +#include "kfifo_buf.h" +#include "trigger_consumer.h" +#include "sysfs.h" + +#include "inv_ami306_iio.h" + +static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d, + short *s, int scan_index) { + struct iio_buffer *ring = indio_dev->buffer; + int st; + int i, d_ind; + d_ind = 0; + for (i = 0; i < 3; i++) { + st = iio_scan_mask_query(indio_dev, ring, scan_index + i); + if (st) { + memcpy(&d[d_ind], &s[i], sizeof(s[i])); + d_ind += sizeof(s[i]); + } + } + return d_ind; +} + +/** + * inv_read_fifo() - Transfer data from FIFO to ring buffer. + */ +int inv_read_ami306_fifo(struct iio_dev *indio_dev) +{ + struct inv_ami306_state_s *st = iio_priv(indio_dev); + struct iio_buffer *ring = indio_dev->buffer; + int result, status, d_ind; + char b; + char *tmp; + s64 tmp_buf[2]; + + result = i2c_smbus_read_i2c_block_data(st->i2c, REG_AMI_STA1, 1, &b); + if (result < 0) + goto end_session; + if (b & AMI_STA1_DRDY_BIT) { + status = ami306_read_raw_data(st, st->compass_data); + if (status) { + pr_err("error reading raw\n"); + goto end_session; + } + tmp = (unsigned char *)tmp_buf; + d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data, + INV_AMI306_SCAN_MAGN_X); + if (ring->scan_timestamp) + tmp_buf[(d_ind + 7)/8] = st->timestamp; + ring->access->store_to(indio_dev->buffer, tmp, st->timestamp); + } else if (b & AMI_STA1_DOR_BIT) + pr_err("not ready\n"); +end_session: + b = AMI_CTRL3_FORCE_BIT; + result = i2c_smbus_write_i2c_block_data(st->i2c, REG_AMI_CTRL3, 1, &b); + + return IRQ_HANDLED; +} + +void inv_ami306_unconfigure_ring(struct iio_dev *indio_dev) +{ + iio_kfifo_free(indio_dev->buffer); +}; +static int inv_ami306_postenable(struct iio_dev *indio_dev) +{ + struct inv_ami306_state_s *st = iio_priv(indio_dev); + struct iio_buffer *ring = indio_dev->buffer; + int result; + + /* when all the outputs are disabled, even though buffer/enable is on, + do nothing */ + if (!(iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_X) || + iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Y) || + iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Z))) + return 0; + + result = set_ami306_enable(indio_dev, true); + if (result) + return result; + schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay)); + + return 0; +} + +static int inv_ami306_predisable(struct iio_dev *indio_dev) +{ + struct iio_buffer *ring = indio_dev->buffer; + struct inv_ami306_state_s *st = iio_priv(indio_dev); + + cancel_delayed_work_sync(&st->work); + clear_bit(INV_AMI306_SCAN_MAGN_X, ring->scan_mask); + clear_bit(INV_AMI306_SCAN_MAGN_Y, ring->scan_mask); + clear_bit(INV_AMI306_SCAN_MAGN_Z, ring->scan_mask); + + return 0; +} + +static const struct iio_buffer_setup_ops inv_ami306_ring_setup_ops = { + .preenable = &iio_sw_buffer_preenable, + .postenable = &inv_ami306_postenable, + .predisable = &inv_ami306_predisable, +}; + +int inv_ami306_configure_ring(struct iio_dev *indio_dev) +{ + int ret = 0; + struct iio_buffer *ring; + + ring = iio_kfifo_allocate(indio_dev); + if (!ring) { + ret = -ENOMEM; + return ret; + } + indio_dev->buffer = ring; + /* setup ring buffer */ + ring->scan_timestamp = true; + indio_dev->setup_ops = &inv_ami306_ring_setup_ops; + + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + return 0; +} +/** + * @} + */ + |