On 20.6.2017 20:08, Jonathan Cameron wrote: > On Thu, 15 Jun 2017 11:54:24 +0300 > Mikko Koivunen <mikko.koivunen@xxxxxxxxxxxxxxxxx> wrote: > >> Set up trigger producer and triggered buffer if there is irq defined for >> device in device tree. Trigger producer triggers from rpr0521 drdy >> interrupt line. Trigger consumer reads rpr0521 data to scan buffer. >> Depends on previous commits of _scale and _offset. >> >> Signed-off-by: Mikko Koivunen <mikko.koivunen@xxxxxxxxxxxxxxxxx> > One more issue inline. Sorry, failed to notice this before I guess. No you didn't, it wasn't there before last change. > You can't call iio_trigger_poll in an interrupt thread. It 'kind' > of works, but will sometimes cause issues. A bug we've had to fix > a few times in the past when it hasn't been picked up in review. Ack. Sending v7. > Jonathan >> --- >> Patch v2->v3 changes: >> .shift = 0 removed >> reordered functions to remove forward declarations >> whitespace changes >> refactored some update_bits->write, no "err = err || *"-pattern >> refactored trigger_consumer_handler >> reordered _probe() and _remove() >> added int clear on poweroff() >> checkpatch.pl OK >> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4. >> Builds ok with torvalds/linux feb 27. >> >> Patch v3->v4 changes: >> Moved sensor on/off commands to buffer preenable and postdisable >> - Since drdy happens only on measurement data ready and register writes >> are cached, the trigger producer doesn't care of suspend/resume state. >> available_scan_masks added >> indent fix >> checkpatch.pl OK >> Lightly re-tested on LeMaker HiKey with AOSP7.1 kernel 4.4. >> Builds ok with torvalds/linux feb 27. >> >> Patch v5->v6 changes: >> Changed base from kernel 4.4 to 4.9 >> - Using iio_device_claim_direct_mode() >> - Using iio_trigger_using_own() >> - Changed trigger/buffer to devm_ >> Added shared irq support >> - divided trigger producer to h/thread >> - divided trigger consumer to h/thread >> - Using irq_timestamp when available >> Removed default trigger >> checkpatch.pl OK >> Lightly re-tested on LeMaker HiKey with AOSP7.1 kernel 4.9.29 >> Builds ok with torvalds/linux feb 27. >> >> drivers/iio/light/rpr0521.c | 332 ++++++++++++++++++++++++++++++++++++++++++- >> 1 file changed, 325 insertions(+), 7 deletions(-) >> >> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c >> index 9d0c2e8..ce576be 100644 >> --- a/drivers/iio/light/rpr0521.c >> +++ b/drivers/iio/light/rpr0521.c >> @@ -9,7 +9,7 @@ >> * >> * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38). >> * >> - * TODO: illuminance channel, buffer >> + * TODO: illuminance channel >> */ >> >> #include <linux/module.h> >> @@ -20,6 +20,10 @@ >> #include <linux/acpi.h> >> >> #include <linux/iio/iio.h> >> +#include <linux/iio/buffer.h> >> +#include <linux/iio/trigger.h> >> +#include <linux/iio/trigger_consumer.h> >> +#include <linux/iio/triggered_buffer.h> >> #include <linux/iio/sysfs.h> >> #include <linux/pm_runtime.h> >> >> @@ -30,6 +34,7 @@ >> #define RPR0521_REG_PXS_DATA 0x44 /* 16-bit, little endian */ >> #define RPR0521_REG_ALS_DATA0 0x46 /* 16-bit, little endian */ >> #define RPR0521_REG_ALS_DATA1 0x48 /* 16-bit, little endian */ >> +#define RPR0521_REG_INTERRUPT 0x4A >> #define RPR0521_REG_PS_OFFSET_LSB 0x53 >> #define RPR0521_REG_ID 0x92 >> >> @@ -42,16 +47,31 @@ >> #define RPR0521_ALS_DATA1_GAIN_SHIFT 2 >> #define RPR0521_PXS_GAIN_MASK GENMASK(5, 4) >> #define RPR0521_PXS_GAIN_SHIFT 4 >> +#define RPR0521_PXS_PERSISTENCE_MASK GENMASK(3, 0) >> +#define RPR0521_INTERRUPT_INT_TRIG_PS_MASK BIT(0) >> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_MASK BIT(1) >> +#define RPR0521_INTERRUPT_INT_REASSERT_MASK BIT(3) >> +#define RPR0521_INTERRUPT_ALS_INT_STATUS_MASK BIT(6) >> +#define RPR0521_INTERRUPT_PS_INT_STATUS_MASK BIT(7) >> >> #define RPR0521_MODE_ALS_ENABLE BIT(7) >> #define RPR0521_MODE_ALS_DISABLE 0x00 >> #define RPR0521_MODE_PXS_ENABLE BIT(6) >> #define RPR0521_MODE_PXS_DISABLE 0x00 >> +#define RPR0521_PXS_PERSISTENCE_DRDY 0x00 >> + >> +#define RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE BIT(0) >> +#define RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE 0x00 >> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_ENABLE BIT(1) >> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE 0x00 >> +#define RPR0521_INTERRUPT_INT_REASSERT_ENABLE BIT(3) >> +#define RPR0521_INTERRUPT_INT_REASSERT_DISABLE 0x00 >> >> #define RPR0521_MANUFACT_ID 0xE0 >> #define RPR0521_DEFAULT_MEAS_TIME 0x06 /* ALS - 100ms, PXS - 100ms */ >> >> #define RPR0521_DRV_NAME "RPR0521" >> +#define RPR0521_IRQ_NAME "rpr0521_event" >> #define RPR0521_REGMAP_NAME "rpr0521_regmap" >> >> #define RPR0521_SLEEP_DELAY_MS 2000 >> @@ -167,6 +187,9 @@ struct rpr0521_data { >> bool als_dev_en; >> bool pxs_dev_en; >> >> + struct iio_trigger *drdy_trigger0; >> + s64 irq_timestamp; >> + >> /* optimize runtime pm ops - enable/disable device only if needed */ >> bool als_ps_need_en; >> bool pxs_ps_need_en; >> @@ -196,6 +219,19 @@ static const struct attribute_group rpr0521_attribute_group = { >> .attrs = rpr0521_attributes, >> }; >> >> +/* Order of the channel data in buffer */ >> +enum rpr0521_scan_index_order { >> + RPR0521_CHAN_INDEX_PXS, >> + RPR0521_CHAN_INDEX_BOTH, >> + RPR0521_CHAN_INDEX_IR, >> +}; >> + >> +static const unsigned long rpr0521_available_scan_masks[] = { >> + BIT(RPR0521_CHAN_INDEX_PXS) | BIT(RPR0521_CHAN_INDEX_BOTH) | >> + BIT(RPR0521_CHAN_INDEX_IR), >> + 0 >> +}; >> + >> static const struct iio_chan_spec rpr0521_channels[] = { >> { >> .type = IIO_PROXIMITY, >> @@ -204,6 +240,13 @@ static const struct iio_chan_spec rpr0521_channels[] = { >> BIT(IIO_CHAN_INFO_OFFSET) | >> BIT(IIO_CHAN_INFO_SCALE), >> .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), >> + .scan_index = RPR0521_CHAN_INDEX_PXS, >> + .scan_type = { >> + .sign = 'u', >> + .realbits = 16, >> + .storagebits = 16, >> + .endianness = IIO_LE, >> + }, >> }, >> { >> .type = IIO_INTENSITY, >> @@ -213,6 +256,13 @@ static const struct iio_chan_spec rpr0521_channels[] = { >> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | >> BIT(IIO_CHAN_INFO_SCALE), >> .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), >> + .scan_index = RPR0521_CHAN_INDEX_BOTH, >> + .scan_type = { >> + .sign = 'u', >> + .realbits = 16, >> + .storagebits = 16, >> + .endianness = IIO_LE, >> + }, >> }, >> { >> .type = IIO_INTENSITY, >> @@ -222,6 +272,13 @@ static const struct iio_chan_spec rpr0521_channels[] = { >> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | >> BIT(IIO_CHAN_INFO_SCALE), >> .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), >> + .scan_index = RPR0521_CHAN_INDEX_IR, >> + .scan_type = { >> + .sign = 'u', >> + .realbits = 16, >> + .storagebits = 16, >> + .endianness = IIO_LE, >> + }, >> }, >> }; >> >> @@ -330,6 +387,194 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, >> return 0; >> } >> >> +/* Interrupt register tells if this sensor caused the interrupt or not. */ >> +static inline bool rpr0521_is_triggered(struct rpr0521_data *data) >> +{ >> + int ret; >> + int reg; >> + >> + ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, ®); >> + if (ret < 0) >> + return false; /* Reg read failed. */ >> + if (reg & >> + (RPR0521_INTERRUPT_ALS_INT_STATUS_MASK | >> + RPR0521_INTERRUPT_PS_INT_STATUS_MASK)) >> + return true; >> + else >> + return false; /* Int not from this sensor. */ >> +} >> + >> +/* IRQ to trigger handler */ >> +static irqreturn_t rpr0521_drdy_irq_handler(int irq, void *private) >> +{ >> + struct iio_dev *indio_dev = private; >> + struct rpr0521_data *data = iio_priv(indio_dev); >> + >> + data->irq_timestamp = iio_get_time_ns(indio_dev); >> + /* >> + * We need to wake the thread to read the interrupt reg. It >> + * is not possible to do that here because regmap_read takes a >> + * mutex. >> + */ >> + >> + return IRQ_WAKE_THREAD; >> +} >> + >> +static irqreturn_t rpr0521_drdy_irq_thread(int irq, void *private) >> +{ >> + struct iio_dev *indio_dev = private; >> + struct rpr0521_data *data = iio_priv(indio_dev); >> + >> + if (rpr0521_is_triggered(data)) { >> + iio_trigger_poll(data->drdy_trigger0); > When called from a thread, you need to use (the rather missnamed) > iio_trigger_poll_chained > > Note it won't then call the top half handler at all, but it's the > best we can do without a really nasty and expensive dance to get > back into interrupt context. Ack. >> + return IRQ_HANDLED; >> + } >> + data->irq_timestamp = 0; >> + >> + return IRQ_NONE; >> +} >> + >> +static irqreturn_t rpr0521_trigger_consumer_store_time(int irq, void *p) >> +{ >> + struct iio_poll_func *pf = p; >> + struct iio_dev *indio_dev = pf->indio_dev; >> + struct rpr0521_data *data = iio_priv(indio_dev); >> + >> + /* Store time if not already stored. */ >> + if (iio_trigger_using_own(indio_dev) && data->irq_timestamp) { > This won't be called if we are calling from the threaded call. Ack -> changed timestamp logic for v7. >> + pf->timestamp = data->irq_timestamp; >> + data->irq_timestamp = 0; >> + } else { >> + pf->timestamp = iio_get_time_ns(indio_dev); >> + } >> + >> + return IRQ_WAKE_THREAD; >> +} >> + >> +static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p) >> +{ >> + struct iio_poll_func *pf = p; >> + struct iio_dev *indio_dev = pf->indio_dev; >> + struct rpr0521_data *data = iio_priv(indio_dev); >> + int err; >> + >> + u8 buffer[16]; /* 3 16-bit channels + padding + ts */ >> + >> + err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA, >> + &buffer, >> + (3 * 2) + 1); /* 3 * 16-bit + (discarded) int clear reg. */ >> + if (!err) >> + iio_push_to_buffers_with_timestamp(indio_dev, >> + buffer, pf->timestamp); >> + else >> + dev_err(&data->client->dev, >> + "Trigger consumer can't read from sensor.\n"); >> + >> + iio_trigger_notify_done(indio_dev->trig); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int rpr0521_write_int_enable(struct rpr0521_data *data) >> +{ >> + int err; >> + >> + /* Interrupt after each measurement */ >> + err = regmap_update_bits(data->regmap, RPR0521_REG_PXS_CTRL, >> + RPR0521_PXS_PERSISTENCE_MASK, >> + RPR0521_PXS_PERSISTENCE_DRDY); >> + if (err) { >> + dev_err(&data->client->dev, "PS control reg write fail.\n"); >> + return -EBUSY; >> + } >> + >> + /* Ignore latch and mode because of drdy */ >> + err = regmap_write(data->regmap, RPR0521_REG_INTERRUPT, >> + RPR0521_INTERRUPT_INT_REASSERT_DISABLE | >> + RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE | >> + RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE >> + ); >> + if (err) { >> + dev_err(&data->client->dev, "Interrupt setup write fail.\n"); >> + return -EBUSY; >> + } >> + >> + return 0; >> +} >> + >> +static int rpr0521_write_int_disable(struct rpr0521_data *data) >> +{ >> + /* Don't care of clearing mode, assert and latch. */ >> + return regmap_write(data->regmap, RPR0521_REG_INTERRUPT, >> + RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE | >> + RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE >> + ); >> +} >> + >> +/* >> + * Trigger producer enable / disable. Note that there will be trigs only when >> + * measurement data is ready to be read. >> + */ >> +static int rpr0521_pxs_drdy_set_state(struct iio_trigger *trigger, >> + bool enable_drdy) >> +{ >> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger); >> + struct rpr0521_data *data = iio_priv(indio_dev); >> + int err; >> + >> + if (enable_drdy) >> + err = rpr0521_write_int_enable(data); >> + else >> + err = rpr0521_write_int_disable(data); >> + if (err) >> + dev_err(&data->client->dev, "rpr0521_pxs_drdy_set_state failed\n"); >> + >> + return err; >> +} >> + >> +static const struct iio_trigger_ops rpr0521_trigger_ops = { >> + .set_trigger_state = rpr0521_pxs_drdy_set_state, >> + .owner = THIS_MODULE, >> + }; >> + >> + >> +static int rpr0521_buffer_preenable(struct iio_dev *indio_dev) >> +{ >> + int err; >> + struct rpr0521_data *data = iio_priv(indio_dev); >> + >> + mutex_lock(&data->lock); >> + err = rpr0521_set_power_state(data, true, >> + (RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK)); >> + mutex_unlock(&data->lock); >> + if (err) >> + dev_err(&data->client->dev, "_buffer_preenable fail\n"); >> + >> + return err; >> +} >> + >> +static int rpr0521_buffer_postdisable(struct iio_dev *indio_dev) >> +{ >> + int err; >> + struct rpr0521_data *data = iio_priv(indio_dev); >> + >> + mutex_lock(&data->lock); >> + err = rpr0521_set_power_state(data, false, >> + (RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK)); >> + mutex_unlock(&data->lock); >> + if (err) >> + dev_err(&data->client->dev, "_buffer_postdisable fail\n"); >> + >> + return err; >> +} >> + >> +static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops = { >> + .preenable = rpr0521_buffer_preenable, >> + .postenable = iio_triggered_buffer_postenable, >> + .predisable = iio_triggered_buffer_predisable, >> + .postdisable = rpr0521_buffer_postdisable, >> +}; >> + >> static int rpr0521_get_gain(struct rpr0521_data *data, int chan, >> int *val, int *val2) >> { >> @@ -473,34 +718,39 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev, >> { >> struct rpr0521_data *data = iio_priv(indio_dev); >> int ret; >> + int busy; >> u8 device_mask; >> __le16 raw_data; >> >> switch (mask) { >> + > Shouldn't be seeing whitespace changes in here... True, ack. >> case IIO_CHAN_INFO_RAW: >> if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY) >> return -EINVAL; >> >> + busy = iio_device_claim_direct_mode(indio_dev); >> + if (busy) >> + return -EBUSY; >> device_mask = rpr0521_data_reg[chan->address].device_mask; >> >> mutex_lock(&data->lock); >> ret = rpr0521_set_power_state(data, true, device_mask); >> - if (ret < 0) { >> - mutex_unlock(&data->lock); >> - return ret; >> - } >> + if (ret < 0) >> + goto rpr0521_read_raw_out; >> >> ret = regmap_bulk_read(data->regmap, >> rpr0521_data_reg[chan->address].address, >> &raw_data, sizeof(raw_data)); >> if (ret < 0) { >> rpr0521_set_power_state(data, false, device_mask); >> - mutex_unlock(&data->lock); >> - return ret; >> + goto rpr0521_read_raw_out; >> } >> >> ret = rpr0521_set_power_state(data, false, device_mask); >> + >> +rpr0521_read_raw_out: >> mutex_unlock(&data->lock); >> + iio_device_release_direct_mode(indio_dev); >> if (ret < 0) >> return ret; >> >> @@ -617,12 +867,15 @@ static int rpr0521_init(struct rpr0521_data *data) >> return ret; >> #endif >> >> + data->irq_timestamp = 0; >> + >> return 0; >> } >> >> static int rpr0521_poweroff(struct rpr0521_data *data) >> { >> int ret; >> + int tmp; >> >> ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL, >> RPR0521_MODE_ALS_MASK | >> @@ -635,6 +888,16 @@ static int rpr0521_poweroff(struct rpr0521_data *data) >> data->als_dev_en = false; >> data->pxs_dev_en = false; >> >> + /* >> + * Int pin keeps state after power off. Set pin to high impedance >> + * mode to prevent power drain. >> + */ >> + ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, &tmp); >> + if (ret) { >> + dev_err(&data->client->dev, "Failed to reset int pin.\n"); >> + return ret; >> + } >> + >> return 0; >> } >> >> @@ -707,6 +970,61 @@ static int rpr0521_probe(struct i2c_client *client, >> pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS); >> pm_runtime_use_autosuspend(&client->dev); >> >> + /* >> + * If sensor write/read is needed in _probe after _use_autosuspend, >> + * sensor needs to be _resumed first using rpr0521_set_power_state(). >> + */ >> + >> + /* IRQ to trigger setup */ >> + if (client->irq) { >> + /* Trigger0 producer setup */ >> + data->drdy_trigger0 = devm_iio_trigger_alloc( >> + indio_dev->dev.parent, >> + "%s-dev%d", indio_dev->name, indio_dev->id); >> + if (!data->drdy_trigger0) { >> + ret = -ENOMEM; >> + goto err_pm_disable; >> + } >> + data->drdy_trigger0->dev.parent = indio_dev->dev.parent; >> + data->drdy_trigger0->ops = &rpr0521_trigger_ops; >> + indio_dev->available_scan_masks = rpr0521_available_scan_masks; >> + iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); >> + >> + /* Ties irq to trigger producer handler. */ >> + ret = devm_request_threaded_irq(&client->dev, client->irq, >> + rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread, >> + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, >> + RPR0521_IRQ_NAME, indio_dev); >> + if (ret < 0) { >> + dev_err(&client->dev, "request irq %d for trigger0 failed\n", >> + client->irq); >> + goto err_pm_disable; >> + } >> + >> + ret = devm_iio_trigger_register(indio_dev->dev.parent, >> + data->drdy_trigger0); >> + if (ret) { >> + dev_err(&client->dev, "iio trigger register failed\n"); >> + goto err_pm_disable; >> + } >> + >> + /* >> + * Now whole pipe from physical interrupt (irq defined by >> + * devicetree to device) to trigger0 output is set up. >> + */ >> + >> + /* Trigger consumer setup */ >> + ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, >> + indio_dev, >> + rpr0521_trigger_consumer_store_time, >> + rpr0521_trigger_consumer_handler, >> + &rpr0521_buffer_setup_ops); >> + if (ret < 0) { >> + dev_err(&client->dev, "iio triggered buffer setup failed\n"); >> + goto err_pm_disable; >> + } >> + } >> + >> ret = iio_device_register(indio_dev); >> if (ret) >> goto err_pm_disable; > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html