diff options
Diffstat (limited to 'drivers/staging/iio/accel/lis3l02dq_ring.c')
-rw-r--r-- | drivers/staging/iio/accel/lis3l02dq_ring.c | 506 |
1 files changed, 223 insertions, 283 deletions
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 2c461a31f129..8d5c8ac7db51 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -1,13 +1,11 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/gpio.h> -#include <linux/workqueue.h> #include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> #include <linux/sysfs.h> -#include <linux/list.h> #include <linux/slab.h> #include "../iio.h" @@ -29,180 +27,49 @@ static inline u16 combine_8_to_16(u8 lower, u8 upper) } /** - * lis3l02dq_scan_el_set_state() set whether a scan contains a given channel - * @scan_el: associtate iio scan element attribute - * @indio_dev: the device structure - * @bool: desired state - * - * mlock already held when this is called. - **/ -static int lis3l02dq_scan_el_set_state(struct iio_scan_el *scan_el, - struct iio_dev *indio_dev, - bool state) -{ - u8 t, mask; - int ret; - - ret = lis3l02dq_spi_read_reg_8(&indio_dev->dev, - LIS3L02DQ_REG_CTRL_1_ADDR, - &t); - if (ret) - goto error_ret; - switch (scan_el->label) { - case LIS3L02DQ_REG_OUT_X_L_ADDR: - mask = LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; - break; - case LIS3L02DQ_REG_OUT_Y_L_ADDR: - mask = LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; - break; - case LIS3L02DQ_REG_OUT_Z_L_ADDR: - mask = LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; - break; - default: - ret = -EINVAL; - goto error_ret; - } - - if (!(mask & t) == state) { - if (state) - t |= mask; - else - t &= ~mask; - ret = lis3l02dq_spi_write_reg_8(&indio_dev->dev, - LIS3L02DQ_REG_CTRL_1_ADDR, - &t); - } -error_ret: - return ret; - -} -static IIO_SCAN_EL_C(accel_x, 0, - LIS3L02DQ_REG_OUT_X_L_ADDR, - &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_y, 1, - LIS3L02DQ_REG_OUT_Y_L_ADDR, - &lis3l02dq_scan_el_set_state); -static IIO_SCAN_EL_C(accel_z, 2, - LIS3L02DQ_REG_OUT_Z_L_ADDR, - &lis3l02dq_scan_el_set_state); -static IIO_CONST_ATTR_SCAN_EL_TYPE(accel, s, 12, 16); -static IIO_SCAN_EL_TIMESTAMP(3); -static IIO_CONST_ATTR_SCAN_EL_TYPE(timestamp, s, 64, 64); - -static struct attribute *lis3l02dq_scan_el_attrs[] = { - &iio_scan_el_accel_x.dev_attr.attr, - &iio_const_attr_accel_x_index.dev_attr.attr, - &iio_scan_el_accel_y.dev_attr.attr, - &iio_const_attr_accel_y_index.dev_attr.attr, - &iio_scan_el_accel_z.dev_attr.attr, - &iio_const_attr_accel_z_index.dev_attr.attr, - &iio_const_attr_accel_type.dev_attr.attr, - &iio_scan_el_timestamp.dev_attr.attr, - &iio_const_attr_timestamp_index.dev_attr.attr, - &iio_const_attr_timestamp_type.dev_attr.attr, - NULL, -}; - -static struct attribute_group lis3l02dq_scan_el_group = { - .attrs = lis3l02dq_scan_el_attrs, - .name = "scan_elements", -}; - -/** - * lis3l02dq_poll_func_th() top half interrupt handler called by trigger - * @private_data: iio_dev + * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig **/ -static void lis3l02dq_poll_func_th(struct iio_dev *indio_dev, s64 time) +irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private) { - struct iio_sw_ring_helper_state *h - = iio_dev_get_devdata(indio_dev); - struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); - /* in this case we need to slightly extend the helper function */ - iio_sw_poll_func_th(indio_dev, time); - - /* Indicate that this interrupt is being handled */ - /* Technically this is trigger related, but without this - * handler running there is currently now way for the interrupt - * to clear. - */ - st->inter = 1; + struct iio_dev *indio_dev = private; + struct lis3l02dq_state *st = iio_priv(indio_dev); + + if (st->trigger_on) { + iio_trigger_poll(st->trig, iio_get_time_ns()); + return IRQ_HANDLED; + } else + return IRQ_WAKE_THREAD; } /** - * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig + * lis3l02dq_read_accel_from_ring() individual acceleration read from ring **/ -static int lis3l02dq_data_rdy_trig_poll(struct iio_dev *indio_dev, +ssize_t lis3l02dq_read_accel_from_ring(struct iio_ring_buffer *ring, int index, - s64 timestamp, - int no_test) + int *val) { - struct iio_sw_ring_helper_state *h - = iio_dev_get_devdata(indio_dev); - struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); - - iio_trigger_poll(st->trig, timestamp); + int ret; + s16 *data; - return IRQ_HANDLED; -} + if (!iio_scan_mask_query(ring, index)) + return -EINVAL; -/* This is an event as it is a response to a physical interrupt */ -IIO_EVENT_SH(data_rdy_trig, &lis3l02dq_data_rdy_trig_poll); + if (!ring->access->read_last) + return -EBUSY; -/** - * lis3l02dq_read_accel_from_ring() individual acceleration read from ring - **/ -ssize_t lis3l02dq_read_accel_from_ring(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_scan_el *el = NULL; - int ret, len = 0, i = 0; - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - struct iio_dev *dev_info = dev_get_drvdata(dev); - struct iio_ring_buffer *ring = dev_info->ring; - struct attribute_group *scan_el_attrs = ring->scan_el_attrs; - s16 *data; + data = kmalloc(ring->access->get_bytes_per_datum(ring), + GFP_KERNEL); + if (data == NULL) + return -ENOMEM; - while (scan_el_attrs->attrs[i]) { - el = to_iio_scan_el((struct device_attribute *) - (scan_el_attrs->attrs[i])); - /* label is in fact the address */ - if (el->label == this_attr->address) - break; - i++; - } - if (!scan_el_attrs->attrs[i]) { - ret = -EINVAL; - goto error_ret; - } - /* If this element is in the scan mask */ - ret = iio_scan_mask_query(ring, el->number); - if (ret < 0) - goto error_ret; - if (ret) { - data = kmalloc(ring->access.get_bytes_per_datum(ring), - GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - ret = ring->access.read_last(ring, - (u8 *)data); - if (ret) - goto error_free_data; - } else { - ret = -EINVAL; - goto error_ret; - } - len = iio_scan_mask_count_to_right(ring, el->number); - if (len < 0) { - ret = len; + ret = ring->access->read_last(ring, (u8 *)data); + if (ret) goto error_free_data; - } - len = sprintf(buf, "ring %d\n", data[len]); + *val = data[bitmap_weight(&ring->scan_mask, index)]; error_free_data: kfree(data); -error_ret: - return ret ? ret : len; + return ret; } static const u8 read_all_tx_array[] = { @@ -217,12 +84,13 @@ static const u8 read_all_tx_array[] = { /** * lis3l02dq_read_all() Reads all channels currently selected * @st: device specific state - * @rx_array: (dma capable) recieve array, must be at least + * @rx_array: (dma capable) receive array, must be at least * 4*number of channels **/ -static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) +static int lis3l02dq_read_all(struct iio_dev *indio_dev, u8 *rx_array) { - struct iio_ring_buffer *ring = st->help.indio_dev->ring; + struct iio_ring_buffer *ring = indio_dev->ring; + struct lis3l02dq_state *st = iio_priv(indio_dev); struct spi_transfer *xfers; struct spi_message msg; int ret, i, j = 0; @@ -234,7 +102,7 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) mutex_lock(&st->buf_lock); - for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) { + for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) if (ring->scan_mask & (1 << i)) { /* lower byte */ xfers[j].tx_buf = st->tx + 2*j; @@ -258,7 +126,7 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) xfers[j].cs_change = 1; j++; } - } + /* After these are transmitted, the rx_buff should have * values in alternate bytes */ @@ -273,31 +141,20 @@ static int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array) return ret; } -static void lis3l02dq_trigger_bh_to_ring(struct work_struct *work_s) -{ - struct iio_sw_ring_helper_state *h - = container_of(work_s, struct iio_sw_ring_helper_state, - work_trigger_to_ring); - struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); - - st->inter = 0; - iio_sw_trigger_bh_to_ring(work_s); -} - -static int lis3l02dq_get_ring_element(struct iio_sw_ring_helper_state *h, +static int lis3l02dq_get_ring_element(struct iio_dev *indio_dev, u8 *buf) { int ret, i; u8 *rx_array ; s16 *data = (s16 *)buf; - rx_array = kzalloc(4 * (h->indio_dev->ring->scan_count), GFP_KERNEL); + rx_array = kzalloc(4 * (indio_dev->ring->scan_count), GFP_KERNEL); if (rx_array == NULL) return -ENOMEM; - ret = lis3l02dq_read_all(lis3l02dq_h_to_s(h), rx_array); + ret = lis3l02dq_read_all(indio_dev, rx_array); if (ret < 0) return ret; - for (i = 0; i < h->indio_dev->ring->scan_count; i++) + for (i = 0; i < indio_dev->ring->scan_count; i++) data[i] = combine_8_to_16(rx_array[i*4+1], rx_array[i*4+3]); kfree(rx_array); @@ -305,19 +162,48 @@ static int lis3l02dq_get_ring_element(struct iio_sw_ring_helper_state *h, return i*sizeof(data[0]); } +static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->private_data; + struct iio_ring_buffer *ring = indio_dev->ring; + int len = 0; + size_t datasize = ring->access->get_bytes_per_datum(ring); + char *data = kmalloc(datasize, GFP_KERNEL); + + if (data == NULL) { + dev_err(indio_dev->dev.parent, + "memory alloc failed in ring bh"); + return -ENOMEM; + } + + if (ring->scan_count) + len = lis3l02dq_get_ring_element(indio_dev, data); + + /* Guaranteed to be aligned with 8 byte boundary */ + if (ring->scan_timestamp) + *(s64 *)(((phys_addr_t)data + len + + sizeof(s64) - 1) & ~(sizeof(s64) - 1)) + = pf->timestamp; + ring->access->store_to(ring, (u8 *)data, pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + kfree(data); + return IRQ_HANDLED; +} + /* Caller responsible for locking as necessary. */ static int -__lis3l02dq_write_data_ready_config(struct device *dev, - struct iio_event_handler_list *list, - bool state) +__lis3l02dq_write_data_ready_config(struct device *dev, bool state) { int ret; u8 valold; bool currentlyset; struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct lis3l02dq_state *st = iio_priv(indio_dev); /* Get the current event mask register */ - ret = lis3l02dq_spi_read_reg_8(dev, + ret = lis3l02dq_spi_read_reg_8(indio_dev, LIS3L02DQ_REG_CTRL_2_ADDR, &valold); if (ret) @@ -328,32 +214,36 @@ __lis3l02dq_write_data_ready_config(struct device *dev, /* Disable requested */ if (!state && currentlyset) { - + /* disable the data ready signal */ valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; + /* The double write is to overcome a hardware bug?*/ - ret = lis3l02dq_spi_write_reg_8(dev, + ret = lis3l02dq_spi_write_reg_8(indio_dev, LIS3L02DQ_REG_CTRL_2_ADDR, - &valold); + valold); if (ret) goto error_ret; - ret = lis3l02dq_spi_write_reg_8(dev, + ret = lis3l02dq_spi_write_reg_8(indio_dev, LIS3L02DQ_REG_CTRL_2_ADDR, - &valold); + valold); if (ret) goto error_ret; - - iio_remove_event_from_list(list, - &indio_dev->interrupts[0] - ->ev_list); - + st->trigger_on = false; /* Enable requested */ } else if (state && !currentlyset) { /* if not set, enable requested */ - valold |= LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; - iio_add_event_to_list(list, &indio_dev->interrupts[0]->ev_list); - ret = lis3l02dq_spi_write_reg_8(dev, + /* first disable all events */ + ret = lis3l02dq_disable_all_events(indio_dev); + if (ret < 0) + goto error_ret; + + valold = ret | + LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; + + st->trigger_on = true; + ret = lis3l02dq_spi_write_reg_8(indio_dev, LIS3L02DQ_REG_CTRL_2_ADDR, - &valold); + valold); if (ret) goto error_ret; } @@ -373,65 +263,45 @@ error_ret: static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig, bool state) { - struct lis3l02dq_state *st = trig->private_data; + struct iio_dev *indio_dev = trig->private_data; int ret = 0; u8 t; - __lis3l02dq_write_data_ready_config(&st->help.indio_dev->dev, - &iio_event_data_rdy_trig, - state); + + __lis3l02dq_write_data_ready_config(&indio_dev->dev, state); if (state == false) { - /* possible quirk with handler currently worked around - by ensuring the work queue is empty */ - flush_scheduled_work(); - /* Clear any outstanding ready events */ - ret = lis3l02dq_read_all(st, NULL); + /* + * A possible quirk with teh handler is currently worked around + * by ensuring outstanding read events are cleared. + */ + ret = lis3l02dq_read_all(indio_dev, NULL); } - lis3l02dq_spi_read_reg_8(&st->help.indio_dev->dev, + lis3l02dq_spi_read_reg_8(indio_dev, LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, &t); return ret; } -static IIO_TRIGGER_NAME_ATTR; - -static struct attribute *lis3l02dq_trigger_attrs[] = { - &dev_attr_name.attr, - NULL, -}; - -static const struct attribute_group lis3l02dq_trigger_attr_group = { - .attrs = lis3l02dq_trigger_attrs, -}; - /** * lis3l02dq_trig_try_reen() try renabling irq for data rdy trigger * @trig: the datardy trigger - * - * As the trigger may occur on any data element being updated it is - * really rather likely to occur during the read from the previous - * trigger event. The only way to discover if this has occured on - * boards not supporting level interrupts is to take a look at the line. - * If it is indicating another interrupt and we don't seem to have a - * handler looking at it, then we need to notify the core that we need - * to tell the triggering core to try reading all these again. - **/ + */ static int lis3l02dq_trig_try_reen(struct iio_trigger *trig) { - struct lis3l02dq_state *st = trig->private_data; - enable_irq(st->us->irq); + struct iio_dev *indio_dev = trig->private_data; + struct lis3l02dq_state *st = iio_priv(indio_dev); + int i; + /* If gpio still high (or high again) */ - if (gpio_get_value(irq_to_gpio(st->us->irq))) - if (st->inter == 0) { - /* already interrupt handler dealing with it */ - disable_irq_nosync(st->us->irq); - if (st->inter == 1) { - /* interrupt handler snuck in between test - * and disable */ - enable_irq(st->us->irq); - return 0; - } - return -EAGAIN; - } + /* In theory possible we will need to do this several times */ + for (i = 0; i < 5; i++) + if (gpio_get_value(irq_to_gpio(st->us->irq))) + lis3l02dq_read_all(indio_dev, NULL); + else + break; + if (i == 5) + printk(KERN_INFO + "Failed to clear the interrupt for lis3l02dq\n"); + /* irq reenabled so success! */ return 0; } @@ -439,62 +309,124 @@ static int lis3l02dq_trig_try_reen(struct iio_trigger *trig) int lis3l02dq_probe_trigger(struct iio_dev *indio_dev) { int ret; - struct lis3l02dq_state *state = indio_dev->dev_data; + struct lis3l02dq_state *st = iio_priv(indio_dev); - state->trig = iio_allocate_trigger(); - if (!state->trig) - return -ENOMEM; - - state->trig->name = kasprintf(GFP_KERNEL, - "lis3l02dq-dev%d", - indio_dev->id); - if (!state->trig->name) { + st->trig = iio_allocate_trigger("lis3l02dq-dev%d", indio_dev->id); + if (!st->trig) { ret = -ENOMEM; - goto error_free_trig; + goto error_ret; } - state->trig->dev.parent = &state->us->dev; - state->trig->owner = THIS_MODULE; - state->trig->private_data = state; - state->trig->set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state; - state->trig->try_reenable = &lis3l02dq_trig_try_reen; - state->trig->control_attrs = &lis3l02dq_trigger_attr_group; - ret = iio_trigger_register(state->trig); + st->trig->dev.parent = &st->us->dev; + st->trig->owner = THIS_MODULE; + st->trig->private_data = indio_dev; + st->trig->set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state; + st->trig->try_reenable = &lis3l02dq_trig_try_reen; + ret = iio_trigger_register(st->trig); if (ret) - goto error_free_trig_name; + goto error_free_trig; return 0; -error_free_trig_name: - kfree(state->trig->name); error_free_trig: - iio_free_trigger(state->trig); - + iio_free_trigger(st->trig); +error_ret: return ret; } void lis3l02dq_remove_trigger(struct iio_dev *indio_dev) { - struct lis3l02dq_state *state = indio_dev->dev_data; + struct lis3l02dq_state *st = iio_priv(indio_dev); - iio_trigger_unregister(state->trig); - kfree(state->trig->name); - iio_free_trigger(state->trig); + iio_trigger_unregister(st->trig); + iio_free_trigger(st->trig); } void lis3l02dq_unconfigure_ring(struct iio_dev *indio_dev) { - kfree(indio_dev->pollfunc); + iio_dealloc_pollfunc(indio_dev->pollfunc); lis3l02dq_free_buf(indio_dev->ring); } +static int lis3l02dq_ring_postenable(struct iio_dev *indio_dev) +{ + /* Disable unwanted channels otherwise the interrupt will not clear */ + u8 t; + int ret; + bool oneenabled = false; + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + &t); + if (ret) + goto error_ret; + + if (iio_scan_mask_query(indio_dev->ring, 0)) { + t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; + oneenabled = true; + } else + t &= ~LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; + if (iio_scan_mask_query(indio_dev->ring, 1)) { + t |= LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; + oneenabled = true; + } else + t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; + if (iio_scan_mask_query(indio_dev->ring, 2)) { + t |= LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + oneenabled = true; + } else + t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + + if (!oneenabled) /* what happens in this case is unknown */ + return -EINVAL; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + t); + if (ret) + goto error_ret; + + return iio_triggered_ring_postenable(indio_dev); +error_ret: + return ret; +} + +/* Turn all channels on again */ +static int lis3l02dq_ring_predisable(struct iio_dev *indio_dev) +{ + u8 t; + int ret; + + ret = iio_triggered_ring_predisable(indio_dev); + if (ret) + goto error_ret; + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + &t); + if (ret) + goto error_ret; + t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE | + LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE | + LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + t); + +error_ret: + return ret; +} + +static const struct iio_ring_setup_ops lis3l02dq_ring_setup_ops = { + .preenable = &iio_sw_ring_preenable, + .postenable = &lis3l02dq_ring_postenable, + .predisable = &lis3l02dq_ring_predisable, +}; + int lis3l02dq_configure_ring(struct iio_dev *indio_dev) { int ret; - struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev); struct iio_ring_buffer *ring; - INIT_WORK(&h->work_trigger_to_ring, lis3l02dq_trigger_bh_to_ring); - h->get_ring_element = &lis3l02dq_get_ring_element; ring = lis3l02dq_alloc_buf(indio_dev); if (!ring) @@ -502,23 +434,31 @@ int lis3l02dq_configure_ring(struct iio_dev *indio_dev) indio_dev->ring = ring; /* Effectively select the ring buffer implementation */ - lis3l02dq_register_buf_funcs(&ring->access); + indio_dev->ring->access = &lis3l02dq_access_funcs; ring->bpe = 2; - ring->scan_el_attrs = &lis3l02dq_scan_el_group; + ring->scan_timestamp = true; - ring->preenable = &iio_sw_ring_preenable; - ring->postenable = &iio_triggered_ring_postenable; - ring->predisable = &iio_triggered_ring_predisable; + ring->setup_ops = &lis3l02dq_ring_setup_ops; ring->owner = THIS_MODULE; /* Set default scan mode */ - iio_scan_mask_set(ring, iio_scan_el_accel_x.number); - iio_scan_mask_set(ring, iio_scan_el_accel_y.number); - iio_scan_mask_set(ring, iio_scan_el_accel_z.number); - - ret = iio_alloc_pollfunc(indio_dev, NULL, &lis3l02dq_poll_func_th); - if (ret) + iio_scan_mask_set(ring, 0); + iio_scan_mask_set(ring, 1); + iio_scan_mask_set(ring, 2); + + /* Functions are NULL as we set handler below */ + indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time, + &lis3l02dq_trigger_handler, + 0, + indio_dev, + "lis3l02dq_consumer%d", + indio_dev->id); + + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; goto error_iio_sw_rb_free; + } + indio_dev->modes |= INDIO_RING_TRIGGERED; return 0; |