diff options
author | Daniel Baluta <daniel.baluta@intel.com> | 2014-12-03 08:31:50 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2014-12-12 08:44:30 -0500 |
commit | aff8609addd00efa3d907f3523823693f95686fd (patch) | |
tree | c059d724d8a32a8b35113cd1a497e31e6c35750c /drivers/iio | |
parent | b25862c577979659020f3575838d366f480ec3bf (diff) |
iio: imu: kmx61: Add PM runtime support
By default both sensors are ACTIVE, in this way the driver
will work even if CONFIG_PM_RUNTIME is not selected.
Signed-off-by: Daniel Baluta <daniel.baluta@intel.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r-- | drivers/iio/imu/kmx61.c | 114 |
1 files changed, 112 insertions, 2 deletions
diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index efb2f8b450c1..f3007dd664fc 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include <linux/i2c.h> | 15 | #include <linux/i2c.h> |
16 | #include <linux/acpi.h> | 16 | #include <linux/acpi.h> |
17 | #include <linux/gpio/consumer.h> | 17 | #include <linux/gpio/consumer.h> |
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/pm_runtime.h> | ||
18 | #include <linux/iio/iio.h> | 20 | #include <linux/iio/iio.h> |
19 | #include <linux/iio/sysfs.h> | 21 | #include <linux/iio/sysfs.h> |
20 | 22 | ||
@@ -69,6 +71,8 @@ | |||
69 | #define KMX61_ACC_ODR_MASK 0x0F | 71 | #define KMX61_ACC_ODR_MASK 0x0F |
70 | #define KMX61_MAG_ODR_MASK 0xF0 | 72 | #define KMX61_MAG_ODR_MASK 0xF0 |
71 | 73 | ||
74 | #define KMX61_SLEEP_DELAY_MS 2000 | ||
75 | |||
72 | #define KMX61_CHIP_ID 0x12 | 76 | #define KMX61_CHIP_ID 0x12 |
73 | 77 | ||
74 | /* KMX61 devices */ | 78 | /* KMX61 devices */ |
@@ -85,6 +89,10 @@ struct kmx61_data { | |||
85 | bool acc_stby; | 89 | bool acc_stby; |
86 | bool mag_stby; | 90 | bool mag_stby; |
87 | 91 | ||
92 | /* power state */ | ||
93 | bool acc_ps; | ||
94 | bool mag_ps; | ||
95 | |||
88 | /* config bits */ | 96 | /* config bits */ |
89 | u8 range; | 97 | u8 range; |
90 | u8 odr_bits; | 98 | u8 odr_bits; |
@@ -457,6 +465,58 @@ static int kmx61_chip_init(struct kmx61_data *data) | |||
457 | return 0; | 465 | return 0; |
458 | } | 466 | } |
459 | 467 | ||
468 | /** | ||
469 | * kmx61_set_power_state() - set power state for kmx61 @device | ||
470 | * @data - kmx61 device private pointer | ||
471 | * @on - power state to be set for @device | ||
472 | * @device - bitmask indicating device for which @on state needs to be set | ||
473 | * | ||
474 | * Notice that when ACC power state needs to be set to ON and MAG is in | ||
475 | * OPERATION then we know that kmx61_runtime_resume was already called | ||
476 | * so we must set ACC OPERATION mode here. The same happens when MAG power | ||
477 | * state needs to be set to ON and ACC is in OPERATION. | ||
478 | */ | ||
479 | static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device) | ||
480 | { | ||
481 | #ifdef CONFIG_PM_RUNTIME | ||
482 | int ret; | ||
483 | |||
484 | if (device & KMX61_ACC) { | ||
485 | if (on && !data->acc_ps && !data->mag_stby) { | ||
486 | ret = kmx61_set_mode(data, 0, KMX61_ACC, true); | ||
487 | if (ret < 0) | ||
488 | return ret; | ||
489 | } | ||
490 | data->acc_ps = on; | ||
491 | } | ||
492 | if (device & KMX61_MAG) { | ||
493 | if (on && !data->mag_ps && !data->acc_stby) { | ||
494 | ret = kmx61_set_mode(data, 0, KMX61_MAG, true); | ||
495 | if (ret < 0) | ||
496 | return ret; | ||
497 | } | ||
498 | data->mag_ps = on; | ||
499 | } | ||
500 | |||
501 | if (on) { | ||
502 | ret = pm_runtime_get_sync(&data->client->dev); | ||
503 | } else { | ||
504 | pm_runtime_mark_last_busy(&data->client->dev); | ||
505 | ret = pm_runtime_put_autosuspend(&data->client->dev); | ||
506 | } | ||
507 | if (ret < 0) { | ||
508 | dev_err(&data->client->dev, | ||
509 | "Failed: kmx61_set_power_state for %d, ret %d\n", | ||
510 | on, ret); | ||
511 | if (on) | ||
512 | pm_runtime_put_noidle(&data->client->dev); | ||
513 | |||
514 | return ret; | ||
515 | } | ||
516 | #endif | ||
517 | return 0; | ||
518 | } | ||
519 | |||
460 | static int kmx61_read_measurement(struct kmx61_data *data, u8 base, u8 offset) | 520 | static int kmx61_read_measurement(struct kmx61_data *data, u8 base, u8 offset) |
461 | { | 521 | { |
462 | int ret; | 522 | int ret; |
@@ -491,13 +551,16 @@ static int kmx61_read_raw(struct iio_dev *indio_dev, | |||
491 | } | 551 | } |
492 | mutex_lock(&data->lock); | 552 | mutex_lock(&data->lock); |
493 | 553 | ||
554 | kmx61_set_power_state(data, true, chan->address); | ||
494 | ret = kmx61_read_measurement(data, base_reg, chan->scan_index); | 555 | ret = kmx61_read_measurement(data, base_reg, chan->scan_index); |
495 | if (ret < 0) { | 556 | if (ret < 0) { |
557 | kmx61_set_power_state(data, false, chan->address); | ||
496 | mutex_unlock(&data->lock); | 558 | mutex_unlock(&data->lock); |
497 | return ret; | 559 | return ret; |
498 | } | 560 | } |
499 | *val = sign_extend32(ret >> chan->scan_type.shift, | 561 | *val = sign_extend32(ret >> chan->scan_type.shift, |
500 | chan->scan_type.realbits - 1); | 562 | chan->scan_type.realbits - 1); |
563 | kmx61_set_power_state(data, false, chan->address); | ||
501 | 564 | ||
502 | mutex_unlock(&data->lock); | 565 | mutex_unlock(&data->lock); |
503 | return IIO_VAL_INT; | 566 | return IIO_VAL_INT; |
@@ -693,12 +756,22 @@ static int kmx61_probe(struct i2c_client *client, | |||
693 | ret = iio_device_register(data->mag_indio_dev); | 756 | ret = iio_device_register(data->mag_indio_dev); |
694 | if (ret < 0) { | 757 | if (ret < 0) { |
695 | dev_err(&client->dev, "Failed to register mag iio device\n"); | 758 | dev_err(&client->dev, "Failed to register mag iio device\n"); |
696 | goto err_iio_unregister; | 759 | goto err_iio_unregister_acc; |
697 | } | 760 | } |
698 | 761 | ||
762 | ret = pm_runtime_set_active(&client->dev); | ||
763 | if (ret < 0) | ||
764 | goto err_iio_unregister_mag; | ||
765 | |||
766 | pm_runtime_enable(&client->dev); | ||
767 | pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS); | ||
768 | pm_runtime_use_autosuspend(&client->dev); | ||
769 | |||
699 | return 0; | 770 | return 0; |
700 | 771 | ||
701 | err_iio_unregister: | 772 | err_iio_unregister_mag: |
773 | iio_device_unregister(data->mag_indio_dev); | ||
774 | err_iio_unregister_acc: | ||
702 | iio_device_unregister(data->acc_indio_dev); | 775 | iio_device_unregister(data->acc_indio_dev); |
703 | err_chip_uninit: | 776 | err_chip_uninit: |
704 | kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); | 777 | kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); |
@@ -709,6 +782,10 @@ static int kmx61_remove(struct i2c_client *client) | |||
709 | { | 782 | { |
710 | struct kmx61_data *data = i2c_get_clientdata(client); | 783 | struct kmx61_data *data = i2c_get_clientdata(client); |
711 | 784 | ||
785 | pm_runtime_disable(&client->dev); | ||
786 | pm_runtime_set_suspended(&client->dev); | ||
787 | pm_runtime_put_noidle(&client->dev); | ||
788 | |||
712 | iio_device_unregister(data->acc_indio_dev); | 789 | iio_device_unregister(data->acc_indio_dev); |
713 | iio_device_unregister(data->mag_indio_dev); | 790 | iio_device_unregister(data->mag_indio_dev); |
714 | 791 | ||
@@ -719,6 +796,38 @@ static int kmx61_remove(struct i2c_client *client) | |||
719 | return 0; | 796 | return 0; |
720 | } | 797 | } |
721 | 798 | ||
799 | |||
800 | #ifdef CONFIG_PM_RUNTIME | ||
801 | static int kmx61_runtime_suspend(struct device *dev) | ||
802 | { | ||
803 | struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); | ||
804 | int ret; | ||
805 | |||
806 | mutex_lock(&data->lock); | ||
807 | ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true); | ||
808 | mutex_unlock(&data->lock); | ||
809 | |||
810 | return ret; | ||
811 | } | ||
812 | |||
813 | static int kmx61_runtime_resume(struct device *dev) | ||
814 | { | ||
815 | struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev)); | ||
816 | u8 stby = 0; | ||
817 | |||
818 | if (!data->acc_ps) | ||
819 | stby |= KMX61_ACC_STBY_BIT; | ||
820 | if (!data->mag_ps) | ||
821 | stby |= KMX61_MAG_STBY_BIT; | ||
822 | |||
823 | return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true); | ||
824 | } | ||
825 | #endif | ||
826 | |||
827 | static const struct dev_pm_ops kmx61_pm_ops = { | ||
828 | SET_RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL) | ||
829 | }; | ||
830 | |||
722 | static const struct acpi_device_id kmx61_acpi_match[] = { | 831 | static const struct acpi_device_id kmx61_acpi_match[] = { |
723 | {"KMX61021", 0}, | 832 | {"KMX61021", 0}, |
724 | {} | 833 | {} |
@@ -737,6 +846,7 @@ static struct i2c_driver kmx61_driver = { | |||
737 | .driver = { | 846 | .driver = { |
738 | .name = KMX61_DRV_NAME, | 847 | .name = KMX61_DRV_NAME, |
739 | .acpi_match_table = ACPI_PTR(kmx61_acpi_match), | 848 | .acpi_match_table = ACPI_PTR(kmx61_acpi_match), |
849 | .pm = &kmx61_pm_ops, | ||
740 | }, | 850 | }, |
741 | .probe = kmx61_probe, | 851 | .probe = kmx61_probe, |
742 | .remove = kmx61_remove, | 852 | .remove = kmx61_remove, |