diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2016-09-01 05:44:48 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2016-09-18 06:35:59 -0400 |
commit | 9a9a369d6178dd4e263c49085ce1b37e1e8f63a0 (patch) | |
tree | 11561a13081f635b5e84a49cacbb49b52b694efd | |
parent | 79383aaec47a0ea0df49728ad0b8e72c4ac58fd0 (diff) |
iio: accel: kxsd9: Deploy system and runtime PM
This deploys runtime and system PM in the KXSD9 driver:
- Use the force_runtime_suspend/resume callbacks as system PM
operations.
- Add buffer prepare/unprepare callbacks to grab the runtime
PM while we're using buffered reads and put get/put_autosuspend
in these.
- Insert get/put_autosuspend calls anywhere the IO is used from
the raw read/write callbacks.
- Move the fullscale setting to be cached in the state container
so we can restore it properly when coming back from
system/runtime suspend.
- Set the autosuspend delay to two orders of magnitude that of
the sensor start-up time (20ms) so we will autosuspend after
2s.
- Register the callbacks in both the SPI and I2C subdrivers.
Tested with the I2C KXSD9 on the Qualcomm APQ8060 Dragonboard.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/accel/kxsd9-i2c.c | 1 | ||||
-rw-r--r-- | drivers/iio/accel/kxsd9-spi.c | 1 | ||||
-rw-r--r-- | drivers/iio/accel/kxsd9.c | 92 | ||||
-rw-r--r-- | drivers/iio/accel/kxsd9.h | 2 |
4 files changed, 94 insertions, 2 deletions
diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index 4aaa27d0aa32..95e20855d2ef 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c | |||
@@ -55,6 +55,7 @@ static struct i2c_driver kxsd9_i2c_driver = { | |||
55 | .driver = { | 55 | .driver = { |
56 | .name = "kxsd9", | 56 | .name = "kxsd9", |
57 | .of_match_table = of_match_ptr(kxsd9_of_match), | 57 | .of_match_table = of_match_ptr(kxsd9_of_match), |
58 | .pm = &kxsd9_dev_pm_ops, | ||
58 | }, | 59 | }, |
59 | .probe = kxsd9_i2c_probe, | 60 | .probe = kxsd9_i2c_probe, |
60 | .remove = kxsd9_i2c_remove, | 61 | .remove = kxsd9_i2c_remove, |
diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index c5af51b7dd7e..b7d0078fd00e 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c | |||
@@ -43,6 +43,7 @@ MODULE_DEVICE_TABLE(spi, kxsd9_spi_id); | |||
43 | static struct spi_driver kxsd9_spi_driver = { | 43 | static struct spi_driver kxsd9_spi_driver = { |
44 | .driver = { | 44 | .driver = { |
45 | .name = "kxsd9", | 45 | .name = "kxsd9", |
46 | .pm = &kxsd9_dev_pm_ops, | ||
46 | }, | 47 | }, |
47 | .probe = kxsd9_spi_probe, | 48 | .probe = kxsd9_spi_probe, |
48 | .remove = kxsd9_spi_remove, | 49 | .remove = kxsd9_spi_remove, |
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index f18cc9436094..a28163b76e12 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/bitops.h> | 23 | #include <linux/bitops.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/regulator/consumer.h> | 25 | #include <linux/regulator/consumer.h> |
26 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/iio/iio.h> | 27 | #include <linux/iio/iio.h> |
27 | #include <linux/iio/sysfs.h> | 28 | #include <linux/iio/sysfs.h> |
28 | #include <linux/iio/buffer.h> | 29 | #include <linux/iio/buffer.h> |
@@ -68,11 +69,13 @@ | |||
68 | * @dev: pointer to the parent device | 69 | * @dev: pointer to the parent device |
69 | * @map: regmap to the device | 70 | * @map: regmap to the device |
70 | * @regs: regulators for this device, VDD and IOVDD | 71 | * @regs: regulators for this device, VDD and IOVDD |
72 | * @scale: the current scaling setting | ||
71 | */ | 73 | */ |
72 | struct kxsd9_state { | 74 | struct kxsd9_state { |
73 | struct device *dev; | 75 | struct device *dev; |
74 | struct regmap *map; | 76 | struct regmap *map; |
75 | struct regulator_bulk_data regs[2]; | 77 | struct regulator_bulk_data regs[2]; |
78 | u8 scale; | ||
76 | }; | 79 | }; |
77 | 80 | ||
78 | #define KXSD9_SCALE_2G "0.011978" | 81 | #define KXSD9_SCALE_2G "0.011978" |
@@ -111,6 +114,10 @@ static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro) | |||
111 | i); | 114 | i); |
112 | if (ret < 0) | 115 | if (ret < 0) |
113 | goto error_ret; | 116 | goto error_ret; |
117 | |||
118 | /* Cached scale when the sensor is powered down */ | ||
119 | st->scale = i; | ||
120 | |||
114 | error_ret: | 121 | error_ret: |
115 | return ret; | 122 | return ret; |
116 | } | 123 | } |
@@ -133,6 +140,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev, | |||
133 | long mask) | 140 | long mask) |
134 | { | 141 | { |
135 | int ret = -EINVAL; | 142 | int ret = -EINVAL; |
143 | struct kxsd9_state *st = iio_priv(indio_dev); | ||
144 | |||
145 | pm_runtime_get_sync(st->dev); | ||
136 | 146 | ||
137 | if (mask == IIO_CHAN_INFO_SCALE) { | 147 | if (mask == IIO_CHAN_INFO_SCALE) { |
138 | /* Check no integer component */ | 148 | /* Check no integer component */ |
@@ -141,6 +151,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev, | |||
141 | ret = kxsd9_write_scale(indio_dev, val2); | 151 | ret = kxsd9_write_scale(indio_dev, val2); |
142 | } | 152 | } |
143 | 153 | ||
154 | pm_runtime_mark_last_busy(st->dev); | ||
155 | pm_runtime_put_autosuspend(st->dev); | ||
156 | |||
144 | return ret; | 157 | return ret; |
145 | } | 158 | } |
146 | 159 | ||
@@ -154,6 +167,8 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev, | |||
154 | __be16 raw_val; | 167 | __be16 raw_val; |
155 | u16 nval; | 168 | u16 nval; |
156 | 169 | ||
170 | pm_runtime_get_sync(st->dev); | ||
171 | |||
157 | switch (mask) { | 172 | switch (mask) { |
158 | case IIO_CHAN_INFO_RAW: | 173 | case IIO_CHAN_INFO_RAW: |
159 | ret = regmap_bulk_read(st->map, chan->address, &raw_val, | 174 | ret = regmap_bulk_read(st->map, chan->address, &raw_val, |
@@ -184,6 +199,9 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev, | |||
184 | } | 199 | } |
185 | 200 | ||
186 | error_ret: | 201 | error_ret: |
202 | pm_runtime_mark_last_busy(st->dev); | ||
203 | pm_runtime_put_autosuspend(st->dev); | ||
204 | |||
187 | return ret; | 205 | return ret; |
188 | }; | 206 | }; |
189 | 207 | ||
@@ -214,6 +232,32 @@ static irqreturn_t kxsd9_trigger_handler(int irq, void *p) | |||
214 | return IRQ_HANDLED; | 232 | return IRQ_HANDLED; |
215 | } | 233 | } |
216 | 234 | ||
235 | static int kxsd9_buffer_preenable(struct iio_dev *indio_dev) | ||
236 | { | ||
237 | struct kxsd9_state *st = iio_priv(indio_dev); | ||
238 | |||
239 | pm_runtime_get_sync(st->dev); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int kxsd9_buffer_postdisable(struct iio_dev *indio_dev) | ||
245 | { | ||
246 | struct kxsd9_state *st = iio_priv(indio_dev); | ||
247 | |||
248 | pm_runtime_mark_last_busy(st->dev); | ||
249 | pm_runtime_put_autosuspend(st->dev); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static const struct iio_buffer_setup_ops kxsd9_buffer_setup_ops = { | ||
255 | .preenable = kxsd9_buffer_preenable, | ||
256 | .postenable = iio_triggered_buffer_postenable, | ||
257 | .predisable = iio_triggered_buffer_predisable, | ||
258 | .postdisable = kxsd9_buffer_postdisable, | ||
259 | }; | ||
260 | |||
217 | #define KXSD9_ACCEL_CHAN(axis, index) \ | 261 | #define KXSD9_ACCEL_CHAN(axis, index) \ |
218 | { \ | 262 | { \ |
219 | .type = IIO_ACCEL, \ | 263 | .type = IIO_ACCEL, \ |
@@ -285,7 +329,7 @@ static int kxsd9_power_up(struct kxsd9_state *st) | |||
285 | KXSD9_CTRL_C_LP_1000HZ | | 329 | KXSD9_CTRL_C_LP_1000HZ | |
286 | KXSD9_CTRL_C_MOT_LEV | | 330 | KXSD9_CTRL_C_MOT_LEV | |
287 | KXSD9_CTRL_C_MOT_LAT | | 331 | KXSD9_CTRL_C_MOT_LAT | |
288 | KXSD9_CTRL_C_FS_2G); | 332 | st->scale); |
289 | if (ret) | 333 | if (ret) |
290 | return ret; | 334 | return ret; |
291 | 335 | ||
@@ -369,13 +413,15 @@ int kxsd9_common_probe(struct device *dev, | |||
369 | dev_err(dev, "Cannot get regulators\n"); | 413 | dev_err(dev, "Cannot get regulators\n"); |
370 | return ret; | 414 | return ret; |
371 | } | 415 | } |
416 | /* Default scaling */ | ||
417 | st->scale = KXSD9_CTRL_C_FS_2G; | ||
372 | 418 | ||
373 | kxsd9_power_up(st); | 419 | kxsd9_power_up(st); |
374 | 420 | ||
375 | ret = iio_triggered_buffer_setup(indio_dev, | 421 | ret = iio_triggered_buffer_setup(indio_dev, |
376 | iio_pollfunc_store_time, | 422 | iio_pollfunc_store_time, |
377 | kxsd9_trigger_handler, | 423 | kxsd9_trigger_handler, |
378 | NULL); | 424 | &kxsd9_buffer_setup_ops); |
379 | if (ret) { | 425 | if (ret) { |
380 | dev_err(dev, "triggered buffer setup failed\n"); | 426 | dev_err(dev, "triggered buffer setup failed\n"); |
381 | goto err_power_down; | 427 | goto err_power_down; |
@@ -387,6 +433,19 @@ int kxsd9_common_probe(struct device *dev, | |||
387 | 433 | ||
388 | dev_set_drvdata(dev, indio_dev); | 434 | dev_set_drvdata(dev, indio_dev); |
389 | 435 | ||
436 | /* Enable runtime PM */ | ||
437 | pm_runtime_get_noresume(dev); | ||
438 | pm_runtime_set_active(dev); | ||
439 | pm_runtime_enable(dev); | ||
440 | /* | ||
441 | * Set autosuspend to two orders of magnitude larger than the | ||
442 | * start-up time. 20ms start-up time means 2000ms autosuspend, | ||
443 | * i.e. 2 seconds. | ||
444 | */ | ||
445 | pm_runtime_set_autosuspend_delay(dev, 2000); | ||
446 | pm_runtime_use_autosuspend(dev); | ||
447 | pm_runtime_put(dev); | ||
448 | |||
390 | return 0; | 449 | return 0; |
391 | 450 | ||
392 | err_cleanup_buffer: | 451 | err_cleanup_buffer: |
@@ -405,12 +464,41 @@ int kxsd9_common_remove(struct device *dev) | |||
405 | 464 | ||
406 | iio_triggered_buffer_cleanup(indio_dev); | 465 | iio_triggered_buffer_cleanup(indio_dev); |
407 | iio_device_unregister(indio_dev); | 466 | iio_device_unregister(indio_dev); |
467 | pm_runtime_get_sync(dev); | ||
468 | pm_runtime_put_noidle(dev); | ||
469 | pm_runtime_disable(dev); | ||
408 | kxsd9_power_down(st); | 470 | kxsd9_power_down(st); |
409 | 471 | ||
410 | return 0; | 472 | return 0; |
411 | } | 473 | } |
412 | EXPORT_SYMBOL(kxsd9_common_remove); | 474 | EXPORT_SYMBOL(kxsd9_common_remove); |
413 | 475 | ||
476 | #ifdef CONFIG_PM | ||
477 | static int kxsd9_runtime_suspend(struct device *dev) | ||
478 | { | ||
479 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
480 | struct kxsd9_state *st = iio_priv(indio_dev); | ||
481 | |||
482 | return kxsd9_power_down(st); | ||
483 | } | ||
484 | |||
485 | static int kxsd9_runtime_resume(struct device *dev) | ||
486 | { | ||
487 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | ||
488 | struct kxsd9_state *st = iio_priv(indio_dev); | ||
489 | |||
490 | return kxsd9_power_up(st); | ||
491 | } | ||
492 | #endif /* CONFIG_PM */ | ||
493 | |||
494 | const struct dev_pm_ops kxsd9_dev_pm_ops = { | ||
495 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | ||
496 | pm_runtime_force_resume) | ||
497 | SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend, | ||
498 | kxsd9_runtime_resume, NULL) | ||
499 | }; | ||
500 | EXPORT_SYMBOL(kxsd9_dev_pm_ops); | ||
501 | |||
414 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); | 502 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); |
415 | MODULE_DESCRIPTION("Kionix KXSD9 driver"); | 503 | MODULE_DESCRIPTION("Kionix KXSD9 driver"); |
416 | MODULE_LICENSE("GPL v2"); | 504 | MODULE_LICENSE("GPL v2"); |
diff --git a/drivers/iio/accel/kxsd9.h b/drivers/iio/accel/kxsd9.h index 9c0861f6b838..7e8a28168310 100644 --- a/drivers/iio/accel/kxsd9.h +++ b/drivers/iio/accel/kxsd9.h | |||
@@ -8,3 +8,5 @@ int kxsd9_common_probe(struct device *dev, | |||
8 | struct regmap *map, | 8 | struct regmap *map, |
9 | const char *name); | 9 | const char *name); |
10 | int kxsd9_common_remove(struct device *dev); | 10 | int kxsd9_common_remove(struct device *dev); |
11 | |||
12 | extern const struct dev_pm_ops kxsd9_dev_pm_ops; | ||