diff options
author | Samu Onkalo <samu.p.onkalo@nokia.com> | 2010-10-22 07:57:23 -0400 |
---|---|---|
committer | Guenter Roeck <guenter.roeck@ericsson.com> | 2010-10-25 17:11:38 -0400 |
commit | 2a346996626ecbb4269c239e9ff7372a182907e9 (patch) | |
tree | 9e1cd9d3f2a44e6dfaae7726613c0c13f2ab33e5 /drivers | |
parent | 0ab83a7ce5c566b84d492d598dc64a19bfaef9ab (diff) |
hwmon: lis3: pm_runtime support
Add pm_runtime support to lis3 core driver.
Add pm_runtime support to lis3 i2c driver.
spi and hp_accel drivers are not yet supported. Old always
on functionality remains for those.
For sysfs there is 5 second delay before turning off the
chip to avoid long ramp up delay.
Signed-off-by: Samu Onkalo <samu.p.onkalo@nokia.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Acked-by: Eric Piel <eric.piel@tremplin-utc.net>
Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/lis3lv02d.c | 60 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d.h | 1 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d_i2c.c | 54 |
3 files changed, 102 insertions, 13 deletions
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 25f38501095..383a84938a9 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/freezer.h> | 34 | #include <linux/freezer.h> |
35 | #include <linux/uaccess.h> | 35 | #include <linux/uaccess.h> |
36 | #include <linux/miscdevice.h> | 36 | #include <linux/miscdevice.h> |
37 | #include <linux/pm_runtime.h> | ||
37 | #include <asm/atomic.h> | 38 | #include <asm/atomic.h> |
38 | #include "lis3lv02d.h" | 39 | #include "lis3lv02d.h" |
39 | 40 | ||
@@ -43,6 +44,9 @@ | |||
43 | #define MDPS_POLL_INTERVAL 50 | 44 | #define MDPS_POLL_INTERVAL 50 |
44 | #define MDPS_POLL_MIN 0 | 45 | #define MDPS_POLL_MIN 0 |
45 | #define MDPS_POLL_MAX 2000 | 46 | #define MDPS_POLL_MAX 2000 |
47 | |||
48 | #define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */ | ||
49 | |||
46 | /* | 50 | /* |
47 | * The sensor can also generate interrupts (DRDY) but it's pretty pointless | 51 | * The sensor can also generate interrupts (DRDY) but it's pretty pointless |
48 | * because they are generated even if the data do not change. So it's better | 52 | * because they are generated even if the data do not change. So it's better |
@@ -296,6 +300,18 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) | |||
296 | mutex_unlock(&lis3_dev.mutex); | 300 | mutex_unlock(&lis3_dev.mutex); |
297 | } | 301 | } |
298 | 302 | ||
303 | static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) | ||
304 | { | ||
305 | if (lis3_dev.pm_dev) | ||
306 | pm_runtime_get_sync(lis3_dev.pm_dev); | ||
307 | } | ||
308 | |||
309 | static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) | ||
310 | { | ||
311 | if (lis3_dev.pm_dev) | ||
312 | pm_runtime_put(lis3_dev.pm_dev); | ||
313 | } | ||
314 | |||
299 | static irqreturn_t lis302dl_interrupt(int irq, void *dummy) | 315 | static irqreturn_t lis302dl_interrupt(int irq, void *dummy) |
300 | { | 316 | { |
301 | if (!test_bit(0, &lis3_dev.misc_opened)) | 317 | if (!test_bit(0, &lis3_dev.misc_opened)) |
@@ -390,6 +406,9 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file) | |||
390 | if (test_and_set_bit(0, &lis3_dev.misc_opened)) | 406 | if (test_and_set_bit(0, &lis3_dev.misc_opened)) |
391 | return -EBUSY; /* already open */ | 407 | return -EBUSY; /* already open */ |
392 | 408 | ||
409 | if (lis3_dev.pm_dev) | ||
410 | pm_runtime_get_sync(lis3_dev.pm_dev); | ||
411 | |||
393 | atomic_set(&lis3_dev.count, 0); | 412 | atomic_set(&lis3_dev.count, 0); |
394 | return 0; | 413 | return 0; |
395 | } | 414 | } |
@@ -398,6 +417,8 @@ static int lis3lv02d_misc_release(struct inode *inode, struct file *file) | |||
398 | { | 417 | { |
399 | fasync_helper(-1, file, 0, &lis3_dev.async_queue); | 418 | fasync_helper(-1, file, 0, &lis3_dev.async_queue); |
400 | clear_bit(0, &lis3_dev.misc_opened); /* release the device */ | 419 | clear_bit(0, &lis3_dev.misc_opened); /* release the device */ |
420 | if (lis3_dev.pm_dev) | ||
421 | pm_runtime_put(lis3_dev.pm_dev); | ||
401 | return 0; | 422 | return 0; |
402 | } | 423 | } |
403 | 424 | ||
@@ -494,6 +515,8 @@ int lis3lv02d_joystick_enable(void) | |||
494 | return -ENOMEM; | 515 | return -ENOMEM; |
495 | 516 | ||
496 | lis3_dev.idev->poll = lis3lv02d_joystick_poll; | 517 | lis3_dev.idev->poll = lis3lv02d_joystick_poll; |
518 | lis3_dev.idev->open = lis3lv02d_joystick_open; | ||
519 | lis3_dev.idev->close = lis3lv02d_joystick_close; | ||
497 | lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; | 520 | lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; |
498 | lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; | 521 | lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; |
499 | lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; | 522 | lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; |
@@ -546,12 +569,30 @@ void lis3lv02d_joystick_disable(void) | |||
546 | EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); | 569 | EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); |
547 | 570 | ||
548 | /* Sysfs stuff */ | 571 | /* Sysfs stuff */ |
572 | static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3) | ||
573 | { | ||
574 | /* | ||
575 | * SYSFS functions are fast visitors so put-call | ||
576 | * immediately after the get-call. However, keep | ||
577 | * chip running for a while and schedule delayed | ||
578 | * suspend. This way periodic sysfs calls doesn't | ||
579 | * suffer from relatively long power up time. | ||
580 | */ | ||
581 | |||
582 | if (lis3->pm_dev) { | ||
583 | pm_runtime_get_sync(lis3->pm_dev); | ||
584 | pm_runtime_put_noidle(lis3->pm_dev); | ||
585 | pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY); | ||
586 | } | ||
587 | } | ||
588 | |||
549 | static ssize_t lis3lv02d_selftest_show(struct device *dev, | 589 | static ssize_t lis3lv02d_selftest_show(struct device *dev, |
550 | struct device_attribute *attr, char *buf) | 590 | struct device_attribute *attr, char *buf) |
551 | { | 591 | { |
552 | int result; | 592 | int result; |
553 | s16 values[3]; | 593 | s16 values[3]; |
554 | 594 | ||
595 | lis3lv02d_sysfs_poweron(&lis3_dev); | ||
555 | result = lis3lv02d_selftest(&lis3_dev, values); | 596 | result = lis3lv02d_selftest(&lis3_dev, values); |
556 | return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL", | 597 | return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL", |
557 | values[0], values[1], values[2]); | 598 | values[0], values[1], values[2]); |
@@ -562,6 +603,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, | |||
562 | { | 603 | { |
563 | int x, y, z; | 604 | int x, y, z; |
564 | 605 | ||
606 | lis3lv02d_sysfs_poweron(&lis3_dev); | ||
565 | mutex_lock(&lis3_dev.mutex); | 607 | mutex_lock(&lis3_dev.mutex); |
566 | lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); | 608 | lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); |
567 | mutex_unlock(&lis3_dev.mutex); | 609 | mutex_unlock(&lis3_dev.mutex); |
@@ -571,6 +613,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, | |||
571 | static ssize_t lis3lv02d_rate_show(struct device *dev, | 613 | static ssize_t lis3lv02d_rate_show(struct device *dev, |
572 | struct device_attribute *attr, char *buf) | 614 | struct device_attribute *attr, char *buf) |
573 | { | 615 | { |
616 | lis3lv02d_sysfs_poweron(&lis3_dev); | ||
574 | return sprintf(buf, "%d\n", lis3lv02d_get_odr()); | 617 | return sprintf(buf, "%d\n", lis3lv02d_get_odr()); |
575 | } | 618 | } |
576 | 619 | ||
@@ -583,6 +626,7 @@ static ssize_t lis3lv02d_rate_set(struct device *dev, | |||
583 | if (strict_strtoul(buf, 0, &rate)) | 626 | if (strict_strtoul(buf, 0, &rate)) |
584 | return -EINVAL; | 627 | return -EINVAL; |
585 | 628 | ||
629 | lis3lv02d_sysfs_poweron(&lis3_dev); | ||
586 | if (lis3lv02d_set_odr(rate)) | 630 | if (lis3lv02d_set_odr(rate)) |
587 | return -EINVAL; | 631 | return -EINVAL; |
588 | 632 | ||
@@ -619,6 +663,17 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3) | |||
619 | { | 663 | { |
620 | sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); | 664 | sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); |
621 | platform_device_unregister(lis3->pdev); | 665 | platform_device_unregister(lis3->pdev); |
666 | if (lis3->pm_dev) { | ||
667 | /* Barrier after the sysfs remove */ | ||
668 | pm_runtime_barrier(lis3->pm_dev); | ||
669 | |||
670 | /* SYSFS may have left chip running. Turn off if necessary */ | ||
671 | if (!pm_runtime_suspended(lis3->pm_dev)) | ||
672 | lis3lv02d_poweroff(&lis3_dev); | ||
673 | |||
674 | pm_runtime_disable(lis3->pm_dev); | ||
675 | pm_runtime_set_suspended(lis3->pm_dev); | ||
676 | } | ||
622 | return 0; | 677 | return 0; |
623 | } | 678 | } |
624 | EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); | 679 | EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); |
@@ -728,6 +783,11 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) | |||
728 | lis3lv02d_add_fs(dev); | 783 | lis3lv02d_add_fs(dev); |
729 | lis3lv02d_poweron(dev); | 784 | lis3lv02d_poweron(dev); |
730 | 785 | ||
786 | if (dev->pm_dev) { | ||
787 | pm_runtime_set_active(dev->pm_dev); | ||
788 | pm_runtime_enable(dev->pm_dev); | ||
789 | } | ||
790 | |||
731 | if (lis3lv02d_joystick_enable()) | 791 | if (lis3lv02d_joystick_enable()) |
732 | printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); | 792 | printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); |
733 | 793 | ||
diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index eb5db584eb0..633cf1debc5 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h | |||
@@ -232,6 +232,7 @@ union axis_conversion { | |||
232 | 232 | ||
233 | struct lis3lv02d { | 233 | struct lis3lv02d { |
234 | void *bus_priv; /* used by the bus layer only */ | 234 | void *bus_priv; /* used by the bus layer only */ |
235 | struct device *pm_dev; /* for pm_runtime purposes */ | ||
235 | int (*init) (struct lis3lv02d *lis3); | 236 | int (*init) (struct lis3lv02d *lis3); |
236 | int (*write) (struct lis3lv02d *lis3, int reg, u8 val); | 237 | int (*write) (struct lis3lv02d *lis3, int reg, u8 val); |
237 | int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); | 238 | int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); |
diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c index c6f3f341127..d52095603e0 100644 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ b/drivers/hwmon/lis3lv02d_i2c.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/init.h> | 29 | #include <linux/init.h> |
30 | #include <linux/err.h> | 30 | #include <linux/err.h> |
31 | #include <linux/i2c.h> | 31 | #include <linux/i2c.h> |
32 | #include <linux/pm_runtime.h> | ||
32 | #include "lis3lv02d.h" | 33 | #include "lis3lv02d.h" |
33 | 34 | ||
34 | #define DRV_NAME "lis3lv02d_i2c" | 35 | #define DRV_NAME "lis3lv02d_i2c" |
@@ -94,6 +95,7 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, | |||
94 | lis3_dev.write = lis3_i2c_write; | 95 | lis3_dev.write = lis3_i2c_write; |
95 | lis3_dev.irq = client->irq; | 96 | lis3_dev.irq = client->irq; |
96 | lis3_dev.ac = lis3lv02d_axis_map; | 97 | lis3_dev.ac = lis3lv02d_axis_map; |
98 | lis3_dev.pm_dev = &client->dev; | ||
97 | 99 | ||
98 | i2c_set_clientdata(client, &lis3_dev); | 100 | i2c_set_clientdata(client, &lis3_dev); |
99 | ret = lis3lv02d_init_device(&lis3_dev); | 101 | ret = lis3lv02d_init_device(&lis3_dev); |
@@ -103,21 +105,20 @@ fail: | |||
103 | 105 | ||
104 | static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) | 106 | static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) |
105 | { | 107 | { |
106 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); | ||
107 | struct lis3lv02d_platform_data *pdata = client->dev.platform_data; | 108 | struct lis3lv02d_platform_data *pdata = client->dev.platform_data; |
108 | 109 | ||
109 | if (pdata && pdata->release_resources) | 110 | if (pdata && pdata->release_resources) |
110 | pdata->release_resources(); | 111 | pdata->release_resources(); |
111 | 112 | ||
112 | lis3lv02d_joystick_disable(); | 113 | lis3lv02d_joystick_disable(); |
113 | lis3lv02d_poweroff(lis3); | ||
114 | 114 | ||
115 | return lis3lv02d_remove_fs(&lis3_dev); | 115 | return lis3lv02d_remove_fs(&lis3_dev); |
116 | } | 116 | } |
117 | 117 | ||
118 | #ifdef CONFIG_PM | 118 | #ifdef CONFIG_PM |
119 | static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) | 119 | static int lis3lv02d_i2c_suspend(struct device *dev) |
120 | { | 120 | { |
121 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); | ||
121 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); | 122 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); |
122 | 123 | ||
123 | if (!lis3->pdata || !lis3->pdata->wakeup_flags) | 124 | if (!lis3->pdata || !lis3->pdata->wakeup_flags) |
@@ -125,18 +126,21 @@ static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) | |||
125 | return 0; | 126 | return 0; |
126 | } | 127 | } |
127 | 128 | ||
128 | static int lis3lv02d_i2c_resume(struct i2c_client *client) | 129 | static int lis3lv02d_i2c_resume(struct device *dev) |
129 | { | 130 | { |
131 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); | ||
130 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); | 132 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); |
131 | 133 | ||
132 | if (!lis3->pdata || !lis3->pdata->wakeup_flags) | 134 | /* |
135 | * pm_runtime documentation says that devices should always | ||
136 | * be powered on at resume. Pm_runtime turns them off after system | ||
137 | * wide resume is complete. | ||
138 | */ | ||
139 | if (!lis3->pdata || !lis3->pdata->wakeup_flags || | ||
140 | pm_runtime_suspended(dev)) | ||
133 | lis3lv02d_poweron(lis3); | 141 | lis3lv02d_poweron(lis3); |
134 | return 0; | ||
135 | } | ||
136 | 142 | ||
137 | static void lis3lv02d_i2c_shutdown(struct i2c_client *client) | 143 | return 0; |
138 | { | ||
139 | lis3lv02d_i2c_suspend(client, PMSG_SUSPEND); | ||
140 | } | 144 | } |
141 | #else | 145 | #else |
142 | #define lis3lv02d_i2c_suspend NULL | 146 | #define lis3lv02d_i2c_suspend NULL |
@@ -144,6 +148,24 @@ static void lis3lv02d_i2c_shutdown(struct i2c_client *client) | |||
144 | #define lis3lv02d_i2c_shutdown NULL | 148 | #define lis3lv02d_i2c_shutdown NULL |
145 | #endif | 149 | #endif |
146 | 150 | ||
151 | static int lis3_i2c_runtime_suspend(struct device *dev) | ||
152 | { | ||
153 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); | ||
154 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); | ||
155 | |||
156 | lis3lv02d_poweroff(lis3); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int lis3_i2c_runtime_resume(struct device *dev) | ||
161 | { | ||
162 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); | ||
163 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); | ||
164 | |||
165 | lis3lv02d_poweron(lis3); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
147 | static const struct i2c_device_id lis3lv02d_id[] = { | 169 | static const struct i2c_device_id lis3lv02d_id[] = { |
148 | {"lis3lv02d", 0 }, | 170 | {"lis3lv02d", 0 }, |
149 | {} | 171 | {} |
@@ -151,14 +173,20 @@ static const struct i2c_device_id lis3lv02d_id[] = { | |||
151 | 173 | ||
152 | MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); | 174 | MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); |
153 | 175 | ||
176 | static const struct dev_pm_ops lis3_pm_ops = { | ||
177 | SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, | ||
178 | lis3lv02d_i2c_resume) | ||
179 | SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, | ||
180 | lis3_i2c_runtime_resume, | ||
181 | NULL) | ||
182 | }; | ||
183 | |||
154 | static struct i2c_driver lis3lv02d_i2c_driver = { | 184 | static struct i2c_driver lis3lv02d_i2c_driver = { |
155 | .driver = { | 185 | .driver = { |
156 | .name = DRV_NAME, | 186 | .name = DRV_NAME, |
157 | .owner = THIS_MODULE, | 187 | .owner = THIS_MODULE, |
188 | .pm = &lis3_pm_ops, | ||
158 | }, | 189 | }, |
159 | .suspend = lis3lv02d_i2c_suspend, | ||
160 | .shutdown = lis3lv02d_i2c_shutdown, | ||
161 | .resume = lis3lv02d_i2c_resume, | ||
162 | .probe = lis3lv02d_i2c_probe, | 190 | .probe = lis3lv02d_i2c_probe, |
163 | .remove = __devexit_p(lis3lv02d_i2c_remove), | 191 | .remove = __devexit_p(lis3lv02d_i2c_remove), |
164 | .id_table = lis3lv02d_id, | 192 | .id_table = lis3lv02d_id, |