From 1e3345bc2c118c43f7f8a6db5d71c06e02b989da Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Wed, 18 May 2011 14:41:00 +0100 Subject: [PATCH] staging:iio: lis3l02dq - separate entirely interrupt handling for thesholds from that for the datardy signal. This removes the one and only real user of the rather complex event list management. V3: More trivial rebase fixups. V2: Trivial rebase fixup. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/accel/lis3l02dq.h | 2 + drivers/staging/iio/accel/lis3l02dq_core.c | 97 +++++++++++++++++----- drivers/staging/iio/accel/lis3l02dq_ring.c | 55 ++++++------ 3 files changed, 107 insertions(+), 47 deletions(-) diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h index 76f592bcb63d..d366a97d668c 100644 --- a/drivers/staging/iio/accel/lis3l02dq.h +++ b/drivers/staging/iio/accel/lis3l02dq.h @@ -181,6 +181,8 @@ int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev, u8 reg_address, u8 *val); +int lis3l02dq_disable_all_events(struct iio_dev *indio_dev); + #ifdef CONFIG_IIO_RING_BUFFER /* At the moment triggers are only used for ring buffer * filling. This may change! diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c index a812e3e16536..c764fc906f89 100644 --- a/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -416,26 +416,21 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480"); -static int lis3l02dq_thresh_handler_th(struct iio_dev *indio_dev, - int index, - s64 timestamp, - int no_test) +static irqreturn_t lis3l02dq_event_handler(int irq, void *_int_info) { + struct iio_interrupt *int_info = _int_info; + struct iio_dev *indio_dev = int_info->dev_info; struct iio_sw_ring_helper_state *h = iio_dev_get_devdata(indio_dev); struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); - /* Stash the timestamp somewhere convenient for the bh */ - st->thresh_timestamp = timestamp; + disable_irq_nosync(irq); + st->thresh_timestamp = iio_get_time_ns(); schedule_work(&st->work_thresh); - return 0; + return IRQ_HANDLED; } -/* A shared handler for a number of threshold types */ -IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th); - - #define LIS3L02DQ_INFO_MASK \ ((1 << IIO_CHAN_INFO_SCALE_SHARED) | \ (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE) | \ @@ -448,13 +443,13 @@ IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th); static struct iio_chan_spec lis3l02dq_channels[] = { IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_X, LIS3L02DQ_INFO_MASK, 0, 0, IIO_ST('s', 12, 16, 0), - LIS3L02DQ_EVENT_MASK, &iio_event_threshold), + LIS3L02DQ_EVENT_MASK, NULL), IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Y, LIS3L02DQ_INFO_MASK, 1, 1, IIO_ST('s', 12, 16, 0), - LIS3L02DQ_EVENT_MASK, &iio_event_threshold), + LIS3L02DQ_EVENT_MASK, NULL), IIO_CHAN(IIO_ACCEL, 1, 0, 0, NULL, 0, IIO_MOD_Z, LIS3L02DQ_INFO_MASK, 2, 2, IIO_ST('s', 12, 16, 0), - LIS3L02DQ_EVENT_MASK, &iio_event_threshold), + LIS3L02DQ_EVENT_MASK, NULL), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -477,11 +472,57 @@ static ssize_t lis3l02dq_read_event_config(struct iio_dev *indio_dev, return !!(val & mask); } +int lis3l02dq_disable_all_events(struct iio_dev *indio_dev) +{ + struct iio_sw_ring_helper_state *h + = iio_dev_get_devdata(indio_dev); + struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); + int ret; + u8 control, val; + bool irqtofree; + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + &control); + + irqtofree = !!(control & LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT); + + control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + &control); + if (ret) + goto error_ret; + /* Also for consistency clear the mask */ + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + &val); + if (ret) + goto error_ret; + val &= ~0x3f; + + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + &val); + if (ret) + goto error_ret; + + if (irqtofree) + free_irq(st->us->irq, indio_dev->interrupts[0]); + + ret = control; +error_ret: + return ret; +} + static int lis3l02dq_write_event_config(struct iio_dev *indio_dev, int event_code, struct iio_event_handler_list *list_el, int state) { + struct iio_sw_ring_helper_state *h + = iio_dev_get_devdata(indio_dev); + struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); int ret = 0; u8 val, control; u8 currentlyset; @@ -507,27 +548,39 @@ static int lis3l02dq_write_event_config(struct iio_dev *indio_dev, if (!currentlyset && state) { changed = true; val |= mask; - iio_add_event_to_list(list_el, - &indio_dev->interrupts[0]->ev_list); - } else if (currentlyset && !state) { changed = true; val &= ~mask; - iio_remove_event_from_list(list_el, - &indio_dev->interrupts[0]->ev_list); } + if (changed) { + if (!(control & LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT)) { + ret = request_irq(st->us->irq, + &lis3l02dq_event_handler, + IRQF_TRIGGER_RISING, + "lis3l02dq_event", + indio_dev->interrupts[0]); + if (ret) + goto error_ret; + } + ret = lis3l02dq_spi_write_reg_8(indio_dev, LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, &val); if (ret) goto error_ret; - control = list_el->refcount ? + control = val & 0x3f ? (control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) : (control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT); ret = lis3l02dq_spi_write_reg_8(indio_dev, LIS3L02DQ_REG_CTRL_2_ADDR, &control); + if (ret) + goto error_ret; + + /* remove interrupt handler if nothing is still on */ + if (!(val & 0x3f)) + free_irq(st->us->irq, indio_dev->interrupts[0]); } error_ret: @@ -697,7 +750,6 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi) "lis3l02dq"); if (ret) goto error_uninitialize_ring; - ret = lis3l02dq_probe_trigger(st->help.indio_dev); if (ret) goto error_unregister_line; @@ -768,6 +820,9 @@ static int lis3l02dq_remove(struct spi_device *spi) int ret; struct lis3l02dq_state *st = spi_get_drvdata(spi); struct iio_dev *indio_dev = st->help.indio_dev; + ret = lis3l02dq_disable_all_events(indio_dev); + if (ret) + goto err_ret; ret = lis3l02dq_stop_device(indio_dev); if (ret) diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 2c666f22f4d1..9bc2e5f21c92 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -51,23 +51,14 @@ static void lis3l02dq_poll_func_th(struct iio_dev *indio_dev, s64 time) /** * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig **/ -static int lis3l02dq_data_rdy_trig_poll(struct iio_dev *indio_dev, - int index, - s64 timestamp, - int no_test) +static 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); - - iio_trigger_poll(st->trig, timestamp); + disable_irq_nosync(irq); + iio_trigger_poll(private, iio_get_time_ns()); return IRQ_HANDLED; } -/* This is an event as it is a response to a physical interrupt */ -IIO_EVENT_SH(data_rdy_trig, &lis3l02dq_data_rdy_trig_poll); - /** * lis3l02dq_read_accel_from_ring() individual acceleration read from ring **/ @@ -196,14 +187,15 @@ static int lis3l02dq_get_ring_element(struct iio_sw_ring_helper_state *h, /* 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 iio_sw_ring_helper_state *h + = iio_dev_get_devdata(indio_dev); + struct lis3l02dq_state *st = lis3l02dq_h_to_s(h); /* Get the current event mask register */ ret = lis3l02dq_spi_read_reg_8(indio_dev, @@ -217,8 +209,9 @@ __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(indio_dev, LIS3L02DQ_REG_CTRL_2_ADDR, @@ -231,20 +224,31 @@ __lis3l02dq_write_data_ready_config(struct device *dev, if (ret) goto error_ret; - iio_remove_event_from_list(list, - &indio_dev->interrupts[0] - ->ev_list); - + free_irq(st->us->irq, st->trig); /* 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); + /* 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; + ret = request_irq(st->us->irq, + lis3l02dq_data_rdy_trig_poll, + IRQF_TRIGGER_RISING, "lis3l02dq_datardy", + st->trig); + if (ret) + goto error_ret; + ret = lis3l02dq_spi_write_reg_8(indio_dev, LIS3L02DQ_REG_CTRL_2_ADDR, &valold); - if (ret) + if (ret) { + free_irq(st->us->irq, st->trig); goto error_ret; + } } return 0; @@ -265,9 +269,8 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig, struct lis3l02dq_state *st = 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(&st->help.indio_dev->dev, state); if (state == false) { /* possible quirk with handler currently worked around by ensuring the work queue is empty */ -- 2.20.1