diff options
author | Irina Tirdea <irina.tirdea@intel.com> | 2015-01-11 14:10:14 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2015-01-27 13:49:56 -0500 |
commit | 6da93a6710a3b1eb3d15b88bf96efaac322c893f (patch) | |
tree | 20559b13c30692024e43d6d2165b1f4832ba17a7 | |
parent | 17a2cbc27981b85a09a48425c2614ae0cb7be8cd (diff) |
iio: accel: mma9551: Add runtime pm support
Add support for runtime pm to reduce the power consumed by the device
when not used.
If CONFIG_PM is not enabled, the device will be powered on at
init and only powered off on system suspend.
If CONFIG_PM is enabled, runtime pm autosuspend is used:
- for raw reads will keep the device on for a specified time
- for events it will keep the device on as long as we have at least
one event active
Signed-off-by: Irina Tirdea <irina.tirdea@intel.com>
Reviewed-by: Vlad Dogaru <vlad.dogaru@intel.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/accel/mma9551.c | 162 |
1 files changed, 139 insertions, 23 deletions
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 6563e267b8ae..f1a5a06a0726 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/iio/iio.h> | 22 | #include <linux/iio/iio.h> |
23 | #include <linux/iio/sysfs.h> | 23 | #include <linux/iio/sysfs.h> |
24 | #include <linux/iio/events.h> | 24 | #include <linux/iio/events.h> |
25 | #include <linux/pm_runtime.h> | ||
25 | 26 | ||
26 | #define MMA9551_DRV_NAME "mma9551" | 27 | #define MMA9551_DRV_NAME "mma9551" |
27 | #define MMA9551_IRQ_NAME "mma9551_event" | 28 | #define MMA9551_IRQ_NAME "mma9551_event" |
@@ -71,6 +72,7 @@ enum mma9551_gpio_pin { | |||
71 | /* Sleep/Wake application */ | 72 | /* Sleep/Wake application */ |
72 | #define MMA9551_SLEEP_CFG 0x06 | 73 | #define MMA9551_SLEEP_CFG 0x06 |
73 | #define MMA9551_SLEEP_CFG_SNCEN BIT(0) | 74 | #define MMA9551_SLEEP_CFG_SNCEN BIT(0) |
75 | #define MMA9551_SLEEP_CFG_FLEEN BIT(1) | ||
74 | #define MMA9551_SLEEP_CFG_SCHEN BIT(2) | 76 | #define MMA9551_SLEEP_CFG_SCHEN BIT(2) |
75 | 77 | ||
76 | /* AFE application */ | 78 | /* AFE application */ |
@@ -114,6 +116,9 @@ enum mma9551_tilt_axis { | |||
114 | #define MMA9551_I2C_READ_RETRIES 5 | 116 | #define MMA9551_I2C_READ_RETRIES 5 |
115 | #define MMA9551_I2C_READ_DELAY 50 /* us */ | 117 | #define MMA9551_I2C_READ_DELAY 50 /* us */ |
116 | 118 | ||
119 | #define MMA9551_DEFAULT_SAMPLE_RATE 122 /* Hz */ | ||
120 | #define MMA9551_AUTO_SUSPEND_DELAY_MS 2000 | ||
121 | |||
117 | struct mma9551_mbox_request { | 122 | struct mma9551_mbox_request { |
118 | u8 start_mbox; /* Always 0. */ | 123 | u8 start_mbox; /* Always 0. */ |
119 | u8 app_id; | 124 | u8 app_id; |
@@ -387,16 +392,55 @@ static int mma9551_read_version(struct i2c_client *client) | |||
387 | } | 392 | } |
388 | 393 | ||
389 | /* | 394 | /* |
395 | * Power on chip and enable doze mode. | ||
390 | * Use 'false' as the second parameter to cause the device to enter | 396 | * Use 'false' as the second parameter to cause the device to enter |
391 | * sleep. | 397 | * sleep. |
392 | */ | 398 | */ |
393 | static int mma9551_set_device_state(struct i2c_client *client, | 399 | static int mma9551_set_device_state(struct i2c_client *client, bool enable) |
394 | bool enable) | ||
395 | { | 400 | { |
396 | return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE, | 401 | return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE, |
397 | MMA9551_SLEEP_CFG, | 402 | MMA9551_SLEEP_CFG, |
398 | MMA9551_SLEEP_CFG_SNCEN, | 403 | MMA9551_SLEEP_CFG_SNCEN | |
399 | enable ? 0 : MMA9551_SLEEP_CFG_SNCEN); | 404 | MMA9551_SLEEP_CFG_FLEEN | |
405 | MMA9551_SLEEP_CFG_SCHEN, | ||
406 | enable ? MMA9551_SLEEP_CFG_SCHEN | | ||
407 | MMA9551_SLEEP_CFG_FLEEN : | ||
408 | MMA9551_SLEEP_CFG_SNCEN); | ||
409 | } | ||
410 | |||
411 | static int mma9551_set_power_state(struct i2c_client *client, bool on) | ||
412 | { | ||
413 | #ifdef CONFIG_PM | ||
414 | int ret; | ||
415 | |||
416 | if (on) | ||
417 | ret = pm_runtime_get_sync(&client->dev); | ||
418 | else { | ||
419 | pm_runtime_mark_last_busy(&client->dev); | ||
420 | ret = pm_runtime_put_autosuspend(&client->dev); | ||
421 | } | ||
422 | |||
423 | if (ret < 0) { | ||
424 | dev_err(&client->dev, | ||
425 | "failed to change power state to %d\n", on); | ||
426 | if (on) | ||
427 | pm_runtime_put_noidle(&client->dev); | ||
428 | |||
429 | return ret; | ||
430 | } | ||
431 | #endif | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static void mma9551_sleep(int freq) | ||
437 | { | ||
438 | int sleep_val = 1000 / freq; | ||
439 | |||
440 | if (sleep_val < 20) | ||
441 | usleep_range(sleep_val * 1000, 20000); | ||
442 | else | ||
443 | msleep_interruptible(sleep_val); | ||
400 | } | 444 | } |
401 | 445 | ||
402 | static int mma9551_read_incli_chan(struct i2c_client *client, | 446 | static int mma9551_read_incli_chan(struct i2c_client *client, |
@@ -424,15 +468,19 @@ static int mma9551_read_incli_chan(struct i2c_client *client, | |||
424 | return -EINVAL; | 468 | return -EINVAL; |
425 | } | 469 | } |
426 | 470 | ||
471 | ret = mma9551_set_power_state(client, true); | ||
472 | if (ret < 0) | ||
473 | return ret; | ||
474 | |||
427 | ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, | 475 | ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, |
428 | reg_addr, &angle); | 476 | reg_addr, &angle); |
429 | if (ret < 0) | 477 | if (ret < 0) |
430 | return ret; | 478 | goto out_poweroff; |
431 | 479 | ||
432 | ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, | 480 | ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, |
433 | MMA9551_TILT_QUAD_REG, &quadrant); | 481 | MMA9551_TILT_QUAD_REG, &quadrant); |
434 | if (ret < 0) | 482 | if (ret < 0) |
435 | return ret; | 483 | goto out_poweroff; |
436 | 484 | ||
437 | angle &= ~MMA9551_TILT_ANGFLG; | 485 | angle &= ~MMA9551_TILT_ANGFLG; |
438 | quadrant = (quadrant >> quad_shift) & 0x03; | 486 | quadrant = (quadrant >> quad_shift) & 0x03; |
@@ -442,7 +490,11 @@ static int mma9551_read_incli_chan(struct i2c_client *client, | |||
442 | else | 490 | else |
443 | *val = angle + 90 * quadrant; | 491 | *val = angle + 90 * quadrant; |
444 | 492 | ||
445 | return IIO_VAL_INT; | 493 | ret = IIO_VAL_INT; |
494 | |||
495 | out_poweroff: | ||
496 | mma9551_set_power_state(client, false); | ||
497 | return ret; | ||
446 | } | 498 | } |
447 | 499 | ||
448 | static int mma9551_read_accel_chan(struct i2c_client *client, | 500 | static int mma9551_read_accel_chan(struct i2c_client *client, |
@@ -467,14 +519,22 @@ static int mma9551_read_accel_chan(struct i2c_client *client, | |||
467 | return -EINVAL; | 519 | return -EINVAL; |
468 | } | 520 | } |
469 | 521 | ||
522 | ret = mma9551_set_power_state(client, true); | ||
523 | if (ret < 0) | ||
524 | return ret; | ||
525 | |||
470 | ret = mma9551_read_status_word(client, MMA9551_APPID_AFE, | 526 | ret = mma9551_read_status_word(client, MMA9551_APPID_AFE, |
471 | reg_addr, &raw_accel); | 527 | reg_addr, &raw_accel); |
472 | if (ret < 0) | 528 | if (ret < 0) |
473 | return ret; | 529 | goto out_poweroff; |
474 | 530 | ||
475 | *val = raw_accel; | 531 | *val = raw_accel; |
476 | 532 | ||
477 | return IIO_VAL_INT; | 533 | ret = IIO_VAL_INT; |
534 | |||
535 | out_poweroff: | ||
536 | mma9551_set_power_state(client, false); | ||
537 | return ret; | ||
478 | } | 538 | } |
479 | 539 | ||
480 | static int mma9551_read_raw(struct iio_dev *indio_dev, | 540 | static int mma9551_read_raw(struct iio_dev *indio_dev, |
@@ -556,6 +616,10 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev, | |||
556 | MMA9551_APPID_NONE, 0, 0); | 616 | MMA9551_APPID_NONE, 0, 0); |
557 | if (ret < 0) | 617 | if (ret < 0) |
558 | return ret; | 618 | return ret; |
619 | |||
620 | ret = mma9551_set_power_state(data->client, false); | ||
621 | if (ret < 0) | ||
622 | return ret; | ||
559 | } else { | 623 | } else { |
560 | int bitnum; | 624 | int bitnum; |
561 | 625 | ||
@@ -574,11 +638,18 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev, | |||
574 | return -EINVAL; | 638 | return -EINVAL; |
575 | } | 639 | } |
576 | 640 | ||
641 | |||
642 | ret = mma9551_set_power_state(data->client, true); | ||
643 | if (ret < 0) | ||
644 | return ret; | ||
645 | |||
577 | ret = mma9551_gpio_config(data->client, | 646 | ret = mma9551_gpio_config(data->client, |
578 | (enum mma9551_gpio_pin)mma_axis, | 647 | (enum mma9551_gpio_pin)mma_axis, |
579 | MMA9551_APPID_TILT, bitnum, 0); | 648 | MMA9551_APPID_TILT, bitnum, 0); |
580 | if (ret < 0) | 649 | if (ret < 0) { |
650 | mma9551_set_power_state(data->client, false); | ||
581 | return ret; | 651 | return ret; |
652 | } | ||
582 | } | 653 | } |
583 | 654 | ||
584 | data->event_enabled[mma_axis] = state; | 655 | data->event_enabled[mma_axis] = state; |
@@ -771,12 +842,7 @@ static int mma9551_init(struct mma9551_data *data) | |||
771 | if (ret) | 842 | if (ret) |
772 | return ret; | 843 | return ret; |
773 | 844 | ||
774 | /* Power on chip and enable doze mode. */ | 845 | return mma9551_set_device_state(data->client, true); |
775 | return mma9551_update_config_bits(data->client, | ||
776 | MMA9551_APPID_SLEEP_WAKE, | ||
777 | MMA9551_SLEEP_CFG, | ||
778 | MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN, | ||
779 | MMA9551_SLEEP_CFG_SCHEN); | ||
780 | } | 846 | } |
781 | 847 | ||
782 | static int mma9551_gpio_probe(struct iio_dev *indio_dev) | 848 | static int mma9551_gpio_probe(struct iio_dev *indio_dev) |
@@ -869,8 +935,19 @@ static int mma9551_probe(struct i2c_client *client, | |||
869 | goto out_poweroff; | 935 | goto out_poweroff; |
870 | } | 936 | } |
871 | 937 | ||
938 | ret = pm_runtime_set_active(&client->dev); | ||
939 | if (ret < 0) | ||
940 | goto out_iio_unregister; | ||
941 | |||
942 | pm_runtime_enable(&client->dev); | ||
943 | pm_runtime_set_autosuspend_delay(&client->dev, | ||
944 | MMA9551_AUTO_SUSPEND_DELAY_MS); | ||
945 | pm_runtime_use_autosuspend(&client->dev); | ||
946 | |||
872 | return 0; | 947 | return 0; |
873 | 948 | ||
949 | out_iio_unregister: | ||
950 | iio_device_unregister(indio_dev); | ||
874 | out_poweroff: | 951 | out_poweroff: |
875 | mma9551_set_device_state(client, false); | 952 | mma9551_set_device_state(client, false); |
876 | 953 | ||
@@ -882,6 +959,10 @@ static int mma9551_remove(struct i2c_client *client) | |||
882 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | 959 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
883 | struct mma9551_data *data = iio_priv(indio_dev); | 960 | struct mma9551_data *data = iio_priv(indio_dev); |
884 | 961 | ||
962 | pm_runtime_disable(&client->dev); | ||
963 | pm_runtime_set_suspended(&client->dev); | ||
964 | pm_runtime_put_noidle(&client->dev); | ||
965 | |||
885 | iio_device_unregister(indio_dev); | 966 | iio_device_unregister(indio_dev); |
886 | mutex_lock(&data->mutex); | 967 | mutex_lock(&data->mutex); |
887 | mma9551_set_device_state(data->client, false); | 968 | mma9551_set_device_state(data->client, false); |
@@ -890,37 +971,72 @@ static int mma9551_remove(struct i2c_client *client) | |||
890 | return 0; | 971 | return 0; |
891 | } | 972 | } |
892 | 973 | ||
974 | #ifdef CONFIG_PM | ||
975 | static int mma9551_runtime_suspend(struct device *dev) | ||
976 | { | ||
977 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | ||
978 | struct mma9551_data *data = iio_priv(indio_dev); | ||
979 | int ret; | ||
980 | |||
981 | mutex_lock(&data->mutex); | ||
982 | ret = mma9551_set_device_state(data->client, false); | ||
983 | mutex_unlock(&data->mutex); | ||
984 | if (ret < 0) { | ||
985 | dev_err(&data->client->dev, "powering off device failed\n"); | ||
986 | return -EAGAIN; | ||
987 | } | ||
988 | |||
989 | return 0; | ||
990 | } | ||
991 | |||
992 | static int mma9551_runtime_resume(struct device *dev) | ||
993 | { | ||
994 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | ||
995 | struct mma9551_data *data = iio_priv(indio_dev); | ||
996 | int ret; | ||
997 | |||
998 | ret = mma9551_set_device_state(data->client, true); | ||
999 | if (ret < 0) | ||
1000 | return ret; | ||
1001 | |||
1002 | mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE); | ||
1003 | |||
1004 | return 0; | ||
1005 | } | ||
1006 | #endif | ||
1007 | |||
893 | #ifdef CONFIG_PM_SLEEP | 1008 | #ifdef CONFIG_PM_SLEEP |
894 | static int mma9551_suspend(struct device *dev) | 1009 | static int mma9551_suspend(struct device *dev) |
895 | { | 1010 | { |
896 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | 1011 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
897 | struct mma9551_data *data = iio_priv(indio_dev); | 1012 | struct mma9551_data *data = iio_priv(indio_dev); |
1013 | int ret; | ||
898 | 1014 | ||
899 | mutex_lock(&data->mutex); | 1015 | mutex_lock(&data->mutex); |
900 | mma9551_set_device_state(data->client, false); | 1016 | ret = mma9551_set_device_state(data->client, false); |
901 | mutex_unlock(&data->mutex); | 1017 | mutex_unlock(&data->mutex); |
902 | 1018 | ||
903 | return 0; | 1019 | return ret; |
904 | } | 1020 | } |
905 | 1021 | ||
906 | static int mma9551_resume(struct device *dev) | 1022 | static int mma9551_resume(struct device *dev) |
907 | { | 1023 | { |
908 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); | 1024 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
909 | struct mma9551_data *data = iio_priv(indio_dev); | 1025 | struct mma9551_data *data = iio_priv(indio_dev); |
1026 | int ret; | ||
910 | 1027 | ||
911 | mutex_lock(&data->mutex); | 1028 | mutex_lock(&data->mutex); |
912 | mma9551_set_device_state(data->client, true); | 1029 | ret = mma9551_set_device_state(data->client, true); |
913 | mutex_unlock(&data->mutex); | 1030 | mutex_unlock(&data->mutex); |
914 | 1031 | ||
915 | return 0; | 1032 | return ret; |
916 | } | 1033 | } |
917 | #else | ||
918 | #define mma9551_suspend NULL | ||
919 | #define mma9551_resume NULL | ||
920 | #endif | 1034 | #endif |
921 | 1035 | ||
922 | static const struct dev_pm_ops mma9551_pm_ops = { | 1036 | static const struct dev_pm_ops mma9551_pm_ops = { |
923 | SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) | 1037 | SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) |
1038 | SET_RUNTIME_PM_OPS(mma9551_runtime_suspend, | ||
1039 | mma9551_runtime_resume, NULL) | ||
924 | }; | 1040 | }; |
925 | 1041 | ||
926 | static const struct acpi_device_id mma9551_acpi_match[] = { | 1042 | static const struct acpi_device_id mma9551_acpi_match[] = { |