aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/i2c-hid
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2014-01-29 04:24:36 -0500
committerJiri Kosina <jkosina@suse.cz>2014-01-30 05:18:30 -0500
commit34f439e4afcdf4bdb42fda62428535a843bca02d (patch)
treee21748add1b93e789520ab4503e9552d09fbca4e /drivers/hid/i2c-hid
parent4988abf1749241bc80600a6b3283d03898d2717c (diff)
HID: i2c-hid: add runtime PM support
This patch adds runtime PM support for the HID over I2C driver. When the i2c-hid device is first opened we power it on and on the last close we power it off. This is actually what the driver is already doing but in addition it allows subsystems, like ACPI power domain to power off the device during runtime PM suspend, which should save even more power. The implementation is not the most power efficient because it needs some interaction from the userspace (e.g close the device node whenever we are no more interested in getting events), nevertheless it allows us to save some power and works with devices that are not wake capable. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/i2c-hid')
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c68
1 files changed, 57 insertions, 11 deletions
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index e914f2755491..360674272507 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -25,6 +25,7 @@
25#include <linux/delay.h> 25#include <linux/delay.h>
26#include <linux/slab.h> 26#include <linux/slab.h>
27#include <linux/pm.h> 27#include <linux/pm.h>
28#include <linux/pm_runtime.h>
28#include <linux/device.h> 29#include <linux/device.h>
29#include <linux/wait.h> 30#include <linux/wait.h>
30#include <linux/err.h> 31#include <linux/err.h>
@@ -454,10 +455,18 @@ static void i2c_hid_init_reports(struct hid_device *hid)
454 return; 455 return;
455 } 456 }
456 457
458 /*
459 * The device must be powered on while we fetch initial reports
460 * from it.
461 */
462 pm_runtime_get_sync(&client->dev);
463
457 list_for_each_entry(report, 464 list_for_each_entry(report,
458 &hid->report_enum[HID_FEATURE_REPORT].report_list, list) 465 &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
459 i2c_hid_init_report(report, inbuf, ihid->bufsize); 466 i2c_hid_init_report(report, inbuf, ihid->bufsize);
460 467
468 pm_runtime_put(&client->dev);
469
461 kfree(inbuf); 470 kfree(inbuf);
462} 471}
463 472
@@ -703,8 +712,8 @@ static int i2c_hid_open(struct hid_device *hid)
703 712
704 mutex_lock(&i2c_hid_open_mut); 713 mutex_lock(&i2c_hid_open_mut);
705 if (!hid->open++) { 714 if (!hid->open++) {
706 ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); 715 ret = pm_runtime_get_sync(&client->dev);
707 if (ret) { 716 if (ret < 0) {
708 hid->open--; 717 hid->open--;
709 goto done; 718 goto done;
710 } 719 }
@@ -712,7 +721,7 @@ static int i2c_hid_open(struct hid_device *hid)
712 } 721 }
713done: 722done:
714 mutex_unlock(&i2c_hid_open_mut); 723 mutex_unlock(&i2c_hid_open_mut);
715 return ret; 724 return ret < 0 ? ret : 0;
716} 725}
717 726
718static void i2c_hid_close(struct hid_device *hid) 727static void i2c_hid_close(struct hid_device *hid)
@@ -729,7 +738,7 @@ static void i2c_hid_close(struct hid_device *hid)
729 clear_bit(I2C_HID_STARTED, &ihid->flags); 738 clear_bit(I2C_HID_STARTED, &ihid->flags);
730 739
731 /* Save some power */ 740 /* Save some power */
732 i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); 741 pm_runtime_put(&client->dev);
733 } 742 }
734 mutex_unlock(&i2c_hid_open_mut); 743 mutex_unlock(&i2c_hid_open_mut);
735} 744}
@@ -738,19 +747,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)
738{ 747{
739 struct i2c_client *client = hid->driver_data; 748 struct i2c_client *client = hid->driver_data;
740 struct i2c_hid *ihid = i2c_get_clientdata(client); 749 struct i2c_hid *ihid = i2c_get_clientdata(client);
741 int ret = 0;
742 750
743 i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl); 751 i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
744 752
745 switch (lvl) { 753 switch (lvl) {
746 case PM_HINT_FULLON: 754 case PM_HINT_FULLON:
747 ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); 755 pm_runtime_get_sync(&client->dev);
748 break; 756 break;
749 case PM_HINT_NORMAL: 757 case PM_HINT_NORMAL:
750 ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); 758 pm_runtime_put(&client->dev);
751 break; 759 break;
752 } 760 }
753 return ret; 761 return 0;
754} 762}
755 763
756static struct hid_ll_driver i2c_hid_ll_driver = { 764static struct hid_ll_driver i2c_hid_ll_driver = {
@@ -987,13 +995,17 @@ static int i2c_hid_probe(struct i2c_client *client,
987 if (ret < 0) 995 if (ret < 0)
988 goto err; 996 goto err;
989 997
998 pm_runtime_get_noresume(&client->dev);
999 pm_runtime_set_active(&client->dev);
1000 pm_runtime_enable(&client->dev);
1001
990 ret = i2c_hid_fetch_hid_descriptor(ihid); 1002 ret = i2c_hid_fetch_hid_descriptor(ihid);
991 if (ret < 0) 1003 if (ret < 0)
992 goto err; 1004 goto err_pm;
993 1005
994 ret = i2c_hid_init_irq(client); 1006 ret = i2c_hid_init_irq(client);
995 if (ret < 0) 1007 if (ret < 0)
996 goto err; 1008 goto err_pm;
997 1009
998 hid = hid_allocate_device(); 1010 hid = hid_allocate_device();
999 if (IS_ERR(hid)) { 1011 if (IS_ERR(hid)) {
@@ -1024,6 +1036,7 @@ static int i2c_hid_probe(struct i2c_client *client,
1024 goto err_mem_free; 1036 goto err_mem_free;
1025 } 1037 }
1026 1038
1039 pm_runtime_put(&client->dev);
1027 return 0; 1040 return 0;
1028 1041
1029err_mem_free: 1042err_mem_free:
@@ -1032,6 +1045,10 @@ err_mem_free:
1032err_irq: 1045err_irq:
1033 free_irq(client->irq, ihid); 1046 free_irq(client->irq, ihid);
1034 1047
1048err_pm:
1049 pm_runtime_put_noidle(&client->dev);
1050 pm_runtime_disable(&client->dev);
1051
1035err: 1052err:
1036 i2c_hid_free_buffers(ihid); 1053 i2c_hid_free_buffers(ihid);
1037 kfree(ihid); 1054 kfree(ihid);
@@ -1043,6 +1060,11 @@ static int i2c_hid_remove(struct i2c_client *client)
1043 struct i2c_hid *ihid = i2c_get_clientdata(client); 1060 struct i2c_hid *ihid = i2c_get_clientdata(client);
1044 struct hid_device *hid; 1061 struct hid_device *hid;
1045 1062
1063 pm_runtime_get_sync(&client->dev);
1064 pm_runtime_disable(&client->dev);
1065 pm_runtime_set_suspended(&client->dev);
1066 pm_runtime_put_noidle(&client->dev);
1067
1046 hid = ihid->hid; 1068 hid = ihid->hid;
1047 hid_destroy_device(hid); 1069 hid_destroy_device(hid);
1048 1070
@@ -1088,7 +1110,31 @@ static int i2c_hid_resume(struct device *dev)
1088} 1110}
1089#endif 1111#endif
1090 1112
1091static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume); 1113#ifdef CONFIG_PM_RUNTIME
1114static int i2c_hid_runtime_suspend(struct device *dev)
1115{
1116 struct i2c_client *client = to_i2c_client(dev);
1117
1118 i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
1119 disable_irq(client->irq);
1120 return 0;
1121}
1122
1123static int i2c_hid_runtime_resume(struct device *dev)
1124{
1125 struct i2c_client *client = to_i2c_client(dev);
1126
1127 enable_irq(client->irq);
1128 i2c_hid_set_power(client, I2C_HID_PWR_ON);
1129 return 0;
1130}
1131#endif
1132
1133static const struct dev_pm_ops i2c_hid_pm = {
1134 SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
1135 SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
1136 NULL)
1137};
1092 1138
1093static const struct i2c_device_id i2c_hid_id_table[] = { 1139static const struct i2c_device_id i2c_hid_id_table[] = {
1094 { "hid", 0 }, 1140 { "hid", 0 },