diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-10-28 09:44:56 -0700 |
commit | e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7 (patch) | |
tree | ea51b391f7d74ca695dcb9f5e46eb02688a92ed9 /drivers/staging/iio/adc/ad799x_ring.c | |
parent | 81280572ca6f54009edfa4deee563e8678784218 (diff) | |
parent | a4ac0d847af9dd34d5953a5e264400326144b6b2 (diff) | |
download | kernel_samsung_smdk4412-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.zip kernel_samsung_smdk4412-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.tar.gz kernel_samsung_smdk4412-e4c5bf8e3dca827a1b3a6fac494eae8c74b7e1e7.tar.bz2 |
Merge 'staging-next' to Linus's tree
This merges the staging-next tree to Linus's tree and resolves
some conflicts that were present due to changes in other trees that were
affected by files here.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/iio/adc/ad799x_ring.c')
-rw-r--r-- | drivers/staging/iio/adc/ad799x_ring.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c new file mode 100644 index 0000000..975cdcb --- /dev/null +++ b/drivers/staging/iio/adc/ad799x_ring.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010 Michael Hennerich, Analog Devices Inc. + * Copyright (C) 2008-2010 Jonathan Cameron + * + * 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. + * + * ad799x_ring.c + */ + +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/bitops.h> + +#include "../iio.h" +#include "../ring_generic.h" +#include "../ring_sw.h" +#include "../trigger.h" +#include "../sysfs.h" + +#include "ad799x.h" + +int ad799x_single_channel_from_ring(struct ad799x_state *st, long mask) +{ + struct iio_ring_buffer *ring = st->indio_dev->ring; + int count = 0, ret; + u16 *ring_data; + + if (!(ring->scan_mask & mask)) { + ret = -EBUSY; + goto error_ret; + } + + ring_data = kmalloc(ring->access.get_bytes_per_datum(ring), GFP_KERNEL); + if (ring_data == NULL) { + ret = -ENOMEM; + goto error_ret; + } + ret = ring->access.read_last(ring, (u8 *) ring_data); + if (ret) + goto error_free_ring_data; + /* Need a count of channels prior to this one */ + mask >>= 1; + while (mask) { + if (mask & ring->scan_mask) + count++; + mask >>= 1; + } + + ret = be16_to_cpu(ring_data[count]); + +error_free_ring_data: + kfree(ring_data); +error_ret: + return ret; +} + +/** + * ad799x_ring_preenable() setup the parameters of the ring before enabling + * + * The complex nature of the setting of the nuber of bytes per datum is due + * to this driver currently ensuring that the timestamp is stored at an 8 + * byte boundary. + **/ +static int ad799x_ring_preenable(struct iio_dev *indio_dev) +{ + struct iio_ring_buffer *ring = indio_dev->ring; + struct ad799x_state *st = indio_dev->dev_data; + size_t d_size; + unsigned long numvals; + + /* + * Need to figure out the current mode based upon the requested + * scan mask in iio_dev + */ + + if (st->id == ad7997 || st->id == ad7998) + ad799x_set_scan_mode(st, ring->scan_mask); + + numvals = ring->scan_count; + + if (ring->access.set_bytes_per_datum) { + d_size = numvals*2 + sizeof(s64); + if (d_size % 8) + d_size += 8 - (d_size % 8); + ring->access.set_bytes_per_datum(ring, d_size); + } + + return 0; +} + +/** + * ad799x_poll_func_th() th of trigger launched polling to ring buffer + * + * As sampling only occurs on i2c comms occuring, leave timestamping until + * then. Some triggers will generate their own time stamp. Currently + * there is no way of notifying them when no one cares. + **/ +static void ad799x_poll_func_th(struct iio_dev *indio_dev, s64 time) +{ + struct ad799x_state *st = indio_dev->dev_data; + + schedule_work(&st->poll_work); + + return; +} +/** + * ad799x_poll_bh_to_ring() bh of trigger launched polling to ring buffer + * @work_s: the work struct through which this was scheduled + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + * I think the one copy of this at a time was to avoid problems if the + * trigger was set far too high and the reads then locked up the computer. + **/ +static void ad799x_poll_bh_to_ring(struct work_struct *work_s) +{ + struct ad799x_state *st = container_of(work_s, struct ad799x_state, + poll_work); + struct iio_dev *indio_dev = st->indio_dev; + struct iio_ring_buffer *ring = indio_dev->ring; + struct iio_sw_ring_buffer *ring_sw = iio_to_sw_ring(indio_dev->ring); + s64 time_ns; + __u8 *rxbuf; + int b_sent; + size_t d_size; + u8 cmd; + + unsigned long numvals = ring->scan_count; + + /* Ensure the timestamp is 8 byte aligned */ + d_size = numvals*2 + sizeof(s64); + + if (d_size % sizeof(s64)) + d_size += sizeof(s64) - (d_size % sizeof(s64)); + + /* Ensure only one copy of this function running at a time */ + if (atomic_inc_return(&st->protect_ring) > 1) + return; + + /* Monitor mode prevents reading. Whilst not currently implemented + * might as well have this test in here in the meantime as it does + * no harm. + */ + if (numvals == 0) + return; + + rxbuf = kmalloc(d_size, GFP_KERNEL); + if (rxbuf == NULL) + return; + + switch (st->id) { + case ad7991: + case ad7995: + case ad7999: + cmd = st->config | (ring->scan_mask << AD799X_CHANNEL_SHIFT); + break; + case ad7992: + case ad7993: + case ad7994: + cmd = (ring->scan_mask << AD799X_CHANNEL_SHIFT) | + AD7998_CONV_RES_REG; + break; + case ad7997: + case ad7998: + cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG; + break; + default: + cmd = 0; + } + + b_sent = i2c_smbus_read_i2c_block_data(st->client, + cmd, numvals*2, rxbuf); + if (b_sent < 0) + goto done; + + time_ns = iio_get_time_ns(); + + memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); + + ring->access.store_to(&ring_sw->buf, rxbuf, time_ns); +done: + kfree(rxbuf); + atomic_dec(&st->protect_ring); +} + + +int ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev) +{ + struct ad799x_state *st = indio_dev->dev_data; + int ret = 0; + + indio_dev->ring = iio_sw_rb_allocate(indio_dev); + if (!indio_dev->ring) { + ret = -ENOMEM; + goto error_ret; + } + /* Effectively select the ring buffer implementation */ + iio_ring_sw_register_funcs(&st->indio_dev->ring->access); + ret = iio_alloc_pollfunc(indio_dev, NULL, &ad799x_poll_func_th); + if (ret) + goto error_deallocate_sw_rb; + + /* Ring buffer functions - here trigger setup related */ + + indio_dev->ring->preenable = &ad799x_ring_preenable; + indio_dev->ring->postenable = &iio_triggered_ring_postenable; + indio_dev->ring->predisable = &iio_triggered_ring_predisable; + + INIT_WORK(&st->poll_work, &ad799x_poll_bh_to_ring); + + indio_dev->ring->scan_el_attrs = st->chip_info->scan_attrs; + + /* Flag that polled ring buffering is possible */ + indio_dev->modes |= INDIO_RING_TRIGGERED; + return 0; +error_deallocate_sw_rb: + iio_sw_rb_free(indio_dev->ring); +error_ret: + return ret; +} + +void ad799x_ring_cleanup(struct iio_dev *indio_dev) +{ + /* ensure that the trigger has been detached */ + if (indio_dev->trig) { + iio_put_trigger(indio_dev->trig); + iio_trigger_dettach_poll_func(indio_dev->trig, + indio_dev->pollfunc); + } + kfree(indio_dev->pollfunc); + iio_sw_rb_free(indio_dev->ring); +} |