diff options
author | Dirk Brandewie <dirk.brandewie@gmail.com> | 2011-10-06 14:26:36 -0400 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2011-10-29 06:03:53 -0400 |
commit | 18dbdda89f5cf0540e577280395f16079308e87d (patch) | |
tree | 1d633ed3f2885b5b831f95762fc1d74fe2f6b388 /drivers/i2c/busses | |
parent | fe20ff5c7e9ca7f5369aacc7d7ca3efeda3b90fe (diff) |
i2c-designware: Add runtime power management support
Add runtime power management to the PCI driver.
Signed-off-by: Dirk Brandewie <dirk.brandewie@gmail.com>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r-- | drivers/i2c/busses/i2c-designware-core.c | 10 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware-core.h | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware-pcidrv.c | 89 |
3 files changed, 98 insertions, 2 deletions
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index b9f18ddaaa76..df8799241009 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/i2c.h> | 31 | #include <linux/i2c.h> |
32 | #include <linux/interrupt.h> | 32 | #include <linux/interrupt.h> |
33 | #include <linux/io.h> | 33 | #include <linux/io.h> |
34 | #include <linux/pm_runtime.h> | ||
34 | #include <linux/delay.h> | 35 | #include <linux/delay.h> |
35 | #include "i2c-designware-core.h" | 36 | #include "i2c-designware-core.h" |
36 | 37 | ||
@@ -501,6 +502,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) | |||
501 | dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); | 502 | dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); |
502 | 503 | ||
503 | mutex_lock(&dev->lock); | 504 | mutex_lock(&dev->lock); |
505 | pm_runtime_get_sync(dev->dev); | ||
504 | 506 | ||
505 | INIT_COMPLETION(dev->cmd_complete); | 507 | INIT_COMPLETION(dev->cmd_complete); |
506 | dev->msgs = msgs; | 508 | dev->msgs = msgs; |
@@ -550,6 +552,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) | |||
550 | ret = -EIO; | 552 | ret = -EIO; |
551 | 553 | ||
552 | done: | 554 | done: |
555 | pm_runtime_put(dev->dev); | ||
553 | mutex_unlock(&dev->lock); | 556 | mutex_unlock(&dev->lock); |
554 | 557 | ||
555 | return ret; | 558 | return ret; |
@@ -671,10 +674,13 @@ void i2c_dw_enable(struct dw_i2c_dev *dev) | |||
671 | dw_writel(dev, 1, DW_IC_ENABLE); | 674 | dw_writel(dev, 1, DW_IC_ENABLE); |
672 | } | 675 | } |
673 | 676 | ||
674 | void i2c_dw_disable(struct dw_i2c_dev *dev) | 677 | u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev) |
675 | { | 678 | { |
676 | int ret; | 679 | return dw_readl(dev, DW_IC_ENABLE); |
680 | } | ||
677 | 681 | ||
682 | void i2c_dw_disable(struct dw_i2c_dev *dev) | ||
683 | { | ||
678 | /* Disable controller */ | 684 | /* Disable controller */ |
679 | dw_writel(dev, 0, DW_IC_ENABLE); | 685 | dw_writel(dev, 0, DW_IC_ENABLE); |
680 | 686 | ||
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 601dd67a6c27..02d1a2ddd853 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h | |||
@@ -98,6 +98,7 @@ extern int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], | |||
98 | extern u32 i2c_dw_func(struct i2c_adapter *adap); | 98 | extern u32 i2c_dw_func(struct i2c_adapter *adap); |
99 | extern irqreturn_t i2c_dw_isr(int this_irq, void *dev_id); | 99 | extern irqreturn_t i2c_dw_isr(int this_irq, void *dev_id); |
100 | extern void i2c_dw_enable(struct dw_i2c_dev *dev); | 100 | extern void i2c_dw_enable(struct dw_i2c_dev *dev); |
101 | extern u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev); | ||
101 | extern void i2c_dw_disable(struct dw_i2c_dev *dev); | 102 | extern void i2c_dw_disable(struct dw_i2c_dev *dev); |
102 | extern void i2c_dw_clear_int(struct dw_i2c_dev *dev); | 103 | extern void i2c_dw_clear_int(struct dw_i2c_dev *dev); |
103 | extern void i2c_dw_disable_int(struct dw_i2c_dev *dev); | 104 | extern void i2c_dw_disable_int(struct dw_i2c_dev *dev); |
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 83307ef3b41f..d2223b5f922b 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/io.h> | 38 | #include <linux/io.h> |
39 | #include <linux/slab.h> | 39 | #include <linux/slab.h> |
40 | #include <linux/pci.h> | 40 | #include <linux/pci.h> |
41 | #include <linux/pm_runtime.h> | ||
41 | #include "i2c-designware-core.h" | 42 | #include "i2c-designware-core.h" |
42 | 43 | ||
43 | #define DRIVER_NAME "i2c-designware-pci" | 44 | #define DRIVER_NAME "i2c-designware-pci" |
@@ -137,6 +138,82 @@ static struct i2c_algorithm i2c_dw_algo = { | |||
137 | .functionality = i2c_dw_func, | 138 | .functionality = i2c_dw_func, |
138 | }; | 139 | }; |
139 | 140 | ||
141 | static int i2c_dw_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) | ||
142 | { | ||
143 | struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); | ||
144 | int err; | ||
145 | |||
146 | |||
147 | i2c_dw_disable(i2c); | ||
148 | |||
149 | err = pci_save_state(pdev); | ||
150 | if (err) { | ||
151 | dev_err(&pdev->dev, "pci_save_state failed\n"); | ||
152 | return err; | ||
153 | } | ||
154 | |||
155 | err = pci_set_power_state(pdev, PCI_D3hot); | ||
156 | if (err) { | ||
157 | dev_err(&pdev->dev, "pci_set_power_state failed\n"); | ||
158 | return err; | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int i2c_dw_pci_runtime_suspend(struct device *dev) | ||
165 | { | ||
166 | struct pci_dev *pdev = to_pci_dev(dev); | ||
167 | dev_dbg(dev, "PCI suspend called\n"); | ||
168 | return i2c_dw_pci_suspend(pdev, PMSG_SUSPEND); | ||
169 | } | ||
170 | |||
171 | static int i2c_dw_pci_resume(struct pci_dev *pdev) | ||
172 | { | ||
173 | struct dw_i2c_dev *i2c = pci_get_drvdata(pdev); | ||
174 | int err; | ||
175 | u32 enabled; | ||
176 | |||
177 | enabled = i2c_dw_is_enabled(i2c); | ||
178 | if (enabled) | ||
179 | return 0; | ||
180 | |||
181 | err = pci_set_power_state(pdev, PCI_D0); | ||
182 | if (err) { | ||
183 | dev_err(&pdev->dev, "pci_set_power_state() failed\n"); | ||
184 | return err; | ||
185 | } | ||
186 | |||
187 | pci_restore_state(pdev); | ||
188 | |||
189 | i2c_dw_init(i2c); | ||
190 | i2c_dw_enable(i2c); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int i2c_dw_pci_runtime_resume(struct device *dev) | ||
195 | { | ||
196 | struct pci_dev *pdev = to_pci_dev(dev); | ||
197 | dev_dbg(dev, "runtime_resume called\n"); | ||
198 | return i2c_dw_pci_resume(pdev); | ||
199 | } | ||
200 | |||
201 | static int i2c_dw_pci_runtime_idle(struct device *dev) | ||
202 | { | ||
203 | int err = pm_schedule_suspend(dev, 500); | ||
204 | dev_dbg(dev, "runtime_idle called\n"); | ||
205 | |||
206 | if (err != 0) | ||
207 | return 0; | ||
208 | return -EBUSY; | ||
209 | } | ||
210 | |||
211 | static const struct dev_pm_ops i2c_dw_pm_ops = { | ||
212 | .runtime_suspend = i2c_dw_pci_runtime_suspend, | ||
213 | .runtime_resume = i2c_dw_pci_runtime_resume, | ||
214 | .runtime_idle = i2c_dw_pci_runtime_idle, | ||
215 | }; | ||
216 | |||
140 | static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) | 217 | static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) |
141 | { | 218 | { |
142 | return dev->controller->clk_khz; | 219 | return dev->controller->clk_khz; |
@@ -245,6 +322,9 @@ const struct pci_device_id *id) | |||
245 | goto err_free_irq; | 322 | goto err_free_irq; |
246 | } | 323 | } |
247 | 324 | ||
325 | pm_runtime_put_noidle(&pdev->dev); | ||
326 | pm_runtime_allow(&pdev->dev); | ||
327 | |||
248 | return 0; | 328 | return 0; |
249 | 329 | ||
250 | err_free_irq: | 330 | err_free_irq: |
@@ -264,6 +344,10 @@ static void __devexit i2c_dw_pci_remove(struct pci_dev *pdev) | |||
264 | { | 344 | { |
265 | struct dw_i2c_dev *dev = pci_get_drvdata(pdev); | 345 | struct dw_i2c_dev *dev = pci_get_drvdata(pdev); |
266 | 346 | ||
347 | i2c_dw_disable(dev); | ||
348 | pm_runtime_forbid(&pdev->dev); | ||
349 | pm_runtime_get_noresume(&pdev->dev); | ||
350 | |||
267 | pci_set_drvdata(pdev, NULL); | 351 | pci_set_drvdata(pdev, NULL); |
268 | i2c_del_adapter(&dev->adapter); | 352 | i2c_del_adapter(&dev->adapter); |
269 | put_device(&pdev->dev); | 353 | put_device(&pdev->dev); |
@@ -297,6 +381,11 @@ static struct pci_driver dw_i2c_driver = { | |||
297 | .id_table = i2_designware_pci_ids, | 381 | .id_table = i2_designware_pci_ids, |
298 | .probe = i2c_dw_pci_probe, | 382 | .probe = i2c_dw_pci_probe, |
299 | .remove = __devexit_p(i2c_dw_pci_remove), | 383 | .remove = __devexit_p(i2c_dw_pci_remove), |
384 | .resume = i2c_dw_pci_resume, | ||
385 | .suspend = i2c_dw_pci_suspend, | ||
386 | .driver = { | ||
387 | .pm = &i2c_dw_pm_ops, | ||
388 | }, | ||
300 | }; | 389 | }; |
301 | 390 | ||
302 | static int __init dw_i2c_init_driver(void) | 391 | static int __init dw_i2c_init_driver(void) |