Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 1 | /* |
| 2 | * STMicroelectronics sensors trigger library driver |
| 3 | * |
| 4 | * Copyright 2012-2013 STMicroelectronics Inc. |
| 5 | * |
| 6 | * Denis Ciocca <denis.ciocca@st.com> |
| 7 | * |
| 8 | * Licensed under the GPL-2. |
| 9 | */ |
| 10 | |
| 11 | #include <linux/kernel.h> |
| 12 | #include <linux/module.h> |
| 13 | #include <linux/slab.h> |
| 14 | #include <linux/iio/iio.h> |
| 15 | #include <linux/iio/trigger.h> |
| 16 | #include <linux/interrupt.h> |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 17 | #include <linux/iio/common/st_sensors.h> |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 18 | #include "st_sensors_core.h" |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 19 | |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 20 | /** |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 21 | * st_sensors_new_samples_available() - check if more samples came in |
| 22 | * returns: |
| 23 | * 0 - no new samples available |
| 24 | * 1 - new samples available |
| 25 | * negative - error or unknown |
| 26 | */ |
| 27 | static int st_sensors_new_samples_available(struct iio_dev *indio_dev, |
| 28 | struct st_sensor_data *sdata) |
| 29 | { |
| 30 | u8 status; |
| 31 | int ret; |
| 32 | |
| 33 | /* How would I know if I can't check it? */ |
| 34 | if (!sdata->sensor_settings->drdy_irq.addr_stat_drdy) |
| 35 | return -EINVAL; |
| 36 | |
| 37 | /* No scan mask, no interrupt */ |
| 38 | if (!indio_dev->active_scan_mask) |
| 39 | return 0; |
| 40 | |
| 41 | ret = sdata->tf->read_byte(&sdata->tb, sdata->dev, |
| 42 | sdata->sensor_settings->drdy_irq.addr_stat_drdy, |
| 43 | &status); |
| 44 | if (ret < 0) { |
| 45 | dev_err(sdata->dev, |
| 46 | "error checking samples available\n"); |
| 47 | return ret; |
| 48 | } |
| 49 | /* |
| 50 | * the lower bits of .active_scan_mask[0] is directly mapped |
| 51 | * to the channels on the sensor: either bit 0 for |
| 52 | * one-dimensional sensors, or e.g. x,y,z for accelerometers, |
| 53 | * gyroscopes or magnetometers. No sensor use more than 3 |
| 54 | * channels, so cut the other status bits here. |
| 55 | */ |
| 56 | status &= 0x07; |
| 57 | |
| 58 | if (status & (u8)indio_dev->active_scan_mask[0]) |
| 59 | return 1; |
| 60 | |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | /** |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 65 | * st_sensors_irq_handler() - top half of the IRQ-based triggers |
| 66 | * @irq: irq number |
| 67 | * @p: private handler data |
| 68 | */ |
| 69 | irqreturn_t st_sensors_irq_handler(int irq, void *p) |
| 70 | { |
| 71 | struct iio_trigger *trig = p; |
| 72 | struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); |
| 73 | struct st_sensor_data *sdata = iio_priv(indio_dev); |
| 74 | |
| 75 | /* Get the time stamp as close in time as possible */ |
Gregor Boirie | bc2b7da | 2016-03-09 19:05:49 +0100 | [diff] [blame] | 76 | sdata->hw_timestamp = iio_get_time_ns(indio_dev); |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 77 | return IRQ_WAKE_THREAD; |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * st_sensors_irq_thread() - bottom half of the IRQ-based triggers |
| 82 | * @irq: irq number |
| 83 | * @p: private handler data |
| 84 | */ |
| 85 | irqreturn_t st_sensors_irq_thread(int irq, void *p) |
| 86 | { |
| 87 | struct iio_trigger *trig = p; |
| 88 | struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); |
| 89 | struct st_sensor_data *sdata = iio_priv(indio_dev); |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 90 | |
| 91 | /* |
| 92 | * If this trigger is backed by a hardware interrupt and we have a |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 93 | * status register, check if this IRQ came from us. Notice that |
| 94 | * we will process also if st_sensors_new_samples_available() |
| 95 | * returns negative: if we can't check status, then poll |
| 96 | * unconditionally. |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 97 | */ |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 98 | if (sdata->hw_irq_trigger && |
| 99 | st_sensors_new_samples_available(indio_dev, sdata)) { |
| 100 | iio_trigger_poll_chained(p); |
| 101 | } else { |
| 102 | dev_dbg(sdata->dev, "spurious IRQ\n"); |
| 103 | return IRQ_NONE; |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 104 | } |
| 105 | |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 106 | /* |
| 107 | * If we have proper level IRQs the handler will be re-entered if |
| 108 | * the line is still active, so return here and come back in through |
| 109 | * the top half if need be. |
| 110 | */ |
| 111 | if (!sdata->edge_irq) |
| 112 | return IRQ_HANDLED; |
| 113 | |
| 114 | /* |
| 115 | * If we are using egde IRQs, new samples arrived while processing |
| 116 | * the IRQ and those may be missed unless we pick them here, so poll |
| 117 | * again. If the sensor delivery frequency is very high, this thread |
| 118 | * turns into a polled loop handler. |
| 119 | */ |
| 120 | while (sdata->hw_irq_trigger && |
| 121 | st_sensors_new_samples_available(indio_dev, sdata)) { |
| 122 | dev_dbg(sdata->dev, "more samples came in during polling\n"); |
| 123 | sdata->hw_timestamp = iio_get_time_ns(indio_dev); |
| 124 | iio_trigger_poll_chained(p); |
| 125 | } |
| 126 | |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 127 | return IRQ_HANDLED; |
| 128 | } |
| 129 | |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 130 | int st_sensors_allocate_trigger(struct iio_dev *indio_dev, |
| 131 | const struct iio_trigger_ops *trigger_ops) |
| 132 | { |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 133 | int err, irq; |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 134 | struct st_sensor_data *sdata = iio_priv(indio_dev); |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 135 | unsigned long irq_trig; |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 136 | |
| 137 | sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); |
| 138 | if (sdata->trig == NULL) { |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 139 | dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 140 | return -ENOMEM; |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 141 | } |
| 142 | |
Crestez Dan Leonard | ff05916 | 2016-05-13 21:43:33 +0300 | [diff] [blame] | 143 | iio_trigger_set_drvdata(sdata->trig, indio_dev); |
| 144 | sdata->trig->ops = trigger_ops; |
| 145 | sdata->trig->dev.parent = sdata->dev; |
| 146 | |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 147 | irq = sdata->get_irq_data_ready(indio_dev); |
| 148 | irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); |
| 149 | /* |
| 150 | * If the IRQ is triggered on falling edge, we need to mark the |
| 151 | * interrupt as active low, if the hardware supports this. |
| 152 | */ |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 153 | switch(irq_trig) { |
| 154 | case IRQF_TRIGGER_FALLING: |
| 155 | case IRQF_TRIGGER_LOW: |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 156 | if (!sdata->sensor_settings->drdy_irq.addr_ihl) { |
| 157 | dev_err(&indio_dev->dev, |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 158 | "falling/low specified for IRQ " |
| 159 | "but hardware only support rising/high: " |
| 160 | "will request rising/high\n"); |
| 161 | if (irq_trig == IRQF_TRIGGER_FALLING) |
| 162 | irq_trig = IRQF_TRIGGER_RISING; |
| 163 | if (irq_trig == IRQF_TRIGGER_LOW) |
| 164 | irq_trig = IRQF_TRIGGER_HIGH; |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 165 | } else { |
| 166 | /* Set up INT active low i.e. falling edge */ |
| 167 | err = st_sensors_write_data_with_mask(indio_dev, |
| 168 | sdata->sensor_settings->drdy_irq.addr_ihl, |
| 169 | sdata->sensor_settings->drdy_irq.mask_ihl, 1); |
| 170 | if (err < 0) |
| 171 | goto iio_trigger_free; |
| 172 | dev_info(&indio_dev->dev, |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 173 | "interrupts on the falling edge or " |
| 174 | "active low level\n"); |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 175 | } |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 176 | break; |
| 177 | case IRQF_TRIGGER_RISING: |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 178 | dev_info(&indio_dev->dev, |
| 179 | "interrupts on the rising edge\n"); |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 180 | break; |
| 181 | case IRQF_TRIGGER_HIGH: |
| 182 | dev_info(&indio_dev->dev, |
| 183 | "interrupts active high level\n"); |
| 184 | break; |
| 185 | default: |
| 186 | /* This is the most preferred mode, if possible */ |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 187 | dev_err(&indio_dev->dev, |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 188 | "unsupported IRQ trigger specified (%lx), enforce " |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 189 | "rising edge\n", irq_trig); |
| 190 | irq_trig = IRQF_TRIGGER_RISING; |
| 191 | } |
Linus Walleij | 0e6f687 | 2016-04-14 10:45:21 +0200 | [diff] [blame] | 192 | |
Linus Walleij | 90efe055 | 2016-06-29 15:14:42 +0200 | [diff] [blame] | 193 | /* Tell the interrupt handler that we're dealing with edges */ |
| 194 | if (irq_trig == IRQF_TRIGGER_FALLING || |
| 195 | irq_trig == IRQF_TRIGGER_RISING) |
| 196 | sdata->edge_irq = true; |
| 197 | else |
| 198 | /* |
| 199 | * If we're not using edges (i.e. level interrupts) we |
| 200 | * just mask off the IRQ, handle one interrupt, then |
| 201 | * if the line is still low, we return to the |
| 202 | * interrupt handler top half again and start over. |
| 203 | */ |
| 204 | irq_trig |= IRQF_ONESHOT; |
| 205 | |
Linus Walleij | 0e6f687 | 2016-04-14 10:45:21 +0200 | [diff] [blame] | 206 | /* |
| 207 | * If the interrupt pin is Open Drain, by definition this |
| 208 | * means that the interrupt line may be shared with other |
| 209 | * peripherals. But to do this we also need to have a status |
| 210 | * register and mask to figure out if this sensor was firing |
| 211 | * the IRQ or not, so we can tell the interrupt handle that |
| 212 | * it was "our" interrupt. |
| 213 | */ |
| 214 | if (sdata->int_pin_open_drain && |
| 215 | sdata->sensor_settings->drdy_irq.addr_stat_drdy) |
| 216 | irq_trig |= IRQF_SHARED; |
| 217 | |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 218 | err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), |
| 219 | st_sensors_irq_handler, |
| 220 | st_sensors_irq_thread, |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 221 | irq_trig, |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 222 | sdata->trig->name, |
| 223 | sdata->trig); |
Linus Walleij | 3337c9f | 2015-04-30 15:15:46 +0200 | [diff] [blame] | 224 | if (err) { |
| 225 | dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n"); |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 226 | goto iio_trigger_free; |
Linus Walleij | 3337c9f | 2015-04-30 15:15:46 +0200 | [diff] [blame] | 227 | } |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 228 | |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 229 | err = iio_trigger_register(sdata->trig); |
| 230 | if (err < 0) { |
| 231 | dev_err(&indio_dev->dev, "failed to register iio trigger.\n"); |
| 232 | goto iio_trigger_register_error; |
| 233 | } |
Srinivas Pandruvada | f0e84ac | 2014-08-22 21:48:00 +0100 | [diff] [blame] | 234 | indio_dev->trig = iio_trigger_get(sdata->trig); |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 235 | |
| 236 | return 0; |
| 237 | |
| 238 | iio_trigger_register_error: |
| 239 | free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); |
Linus Walleij | a9fd053 | 2015-11-19 10:15:17 +0100 | [diff] [blame] | 240 | iio_trigger_free: |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 241 | iio_trigger_free(sdata->trig); |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 242 | return err; |
| 243 | } |
| 244 | EXPORT_SYMBOL(st_sensors_allocate_trigger); |
| 245 | |
| 246 | void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) |
| 247 | { |
| 248 | struct st_sensor_data *sdata = iio_priv(indio_dev); |
| 249 | |
| 250 | iio_trigger_unregister(sdata->trig); |
| 251 | free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); |
| 252 | iio_trigger_free(sdata->trig); |
| 253 | } |
| 254 | EXPORT_SYMBOL(st_sensors_deallocate_trigger); |
| 255 | |
Linus Walleij | 65925b6 | 2016-05-21 20:43:16 +0200 | [diff] [blame] | 256 | int st_sensors_validate_device(struct iio_trigger *trig, |
| 257 | struct iio_dev *indio_dev) |
| 258 | { |
| 259 | struct iio_dev *indio = iio_trigger_get_drvdata(trig); |
| 260 | |
| 261 | if (indio != indio_dev) |
| 262 | return -EINVAL; |
| 263 | |
| 264 | return 0; |
| 265 | } |
| 266 | EXPORT_SYMBOL(st_sensors_validate_device); |
| 267 | |
Denis Ciocca | 23491b5 | 2013-01-25 23:44:00 +0000 | [diff] [blame] | 268 | MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); |
| 269 | MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger"); |
| 270 | MODULE_LICENSE("GPL v2"); |