diff options
author | Ben Dooks <ben-linux@fluff.org> | 2007-06-23 20:16:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-06-24 11:59:11 -0400 |
commit | 331d74750e69a2202f857d3af9323335d0d6879f (patch) | |
tree | 1508b5d183c18d29a54271153e0f8c49546d9013 | |
parent | 1ed8a2b3c501bedd4b35130c8a52662ccf78abad (diff) |
SM501: suspend support
This patch adds support for suspending the core (mfd driver) of the SM501.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/mfd/sm501.c | 113 |
1 files changed, 105 insertions, 8 deletions
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index b0b4458ae90b..72bbecf4b451 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c | |||
@@ -41,6 +41,9 @@ struct sm501_devdata { | |||
41 | struct resource *regs_claim; | 41 | struct resource *regs_claim; |
42 | struct sm501_platdata *platdata; | 42 | struct sm501_platdata *platdata; |
43 | 43 | ||
44 | unsigned int in_suspend; | ||
45 | unsigned long pm_misc; | ||
46 | |||
44 | int unit_power[20]; | 47 | int unit_power[20]; |
45 | unsigned int pdev_id; | 48 | unsigned int pdev_id; |
46 | unsigned int irq; | 49 | unsigned int irq; |
@@ -169,10 +172,41 @@ x "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", | |||
169 | fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15, misc_div)), | 172 | fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15, misc_div)), |
170 | fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15, misc_div))); | 173 | fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15, misc_div))); |
171 | } | 174 | } |
172 | #else | 175 | |
173 | static void sm501_dump_clk(struct sm501_devdata *sm) | 176 | static void sm501_dump_regs(struct sm501_devdata *sm) |
177 | { | ||
178 | void __iomem *regs = sm->regs; | ||
179 | |||
180 | dev_info(sm->dev, "System Control %08x\n", | ||
181 | readl(regs + SM501_SYSTEM_CONTROL)); | ||
182 | dev_info(sm->dev, "Misc Control %08x\n", | ||
183 | readl(regs + SM501_MISC_CONTROL)); | ||
184 | dev_info(sm->dev, "GPIO Control Low %08x\n", | ||
185 | readl(regs + SM501_GPIO31_0_CONTROL)); | ||
186 | dev_info(sm->dev, "GPIO Control Hi %08x\n", | ||
187 | readl(regs + SM501_GPIO63_32_CONTROL)); | ||
188 | dev_info(sm->dev, "DRAM Control %08x\n", | ||
189 | readl(regs + SM501_DRAM_CONTROL)); | ||
190 | dev_info(sm->dev, "Arbitration Ctrl %08x\n", | ||
191 | readl(regs + SM501_ARBTRTN_CONTROL)); | ||
192 | dev_info(sm->dev, "Misc Timing %08x\n", | ||
193 | readl(regs + SM501_MISC_TIMING)); | ||
194 | } | ||
195 | |||
196 | static void sm501_dump_gate(struct sm501_devdata *sm) | ||
174 | { | 197 | { |
198 | dev_info(sm->dev, "CurrentGate %08x\n", | ||
199 | readl(sm->regs + SM501_CURRENT_GATE)); | ||
200 | dev_info(sm->dev, "CurrentClock %08x\n", | ||
201 | readl(sm->regs + SM501_CURRENT_CLOCK)); | ||
202 | dev_info(sm->dev, "PowerModeControl %08x\n", | ||
203 | readl(sm->regs + SM501_POWER_MODE_CONTROL)); | ||
175 | } | 204 | } |
205 | |||
206 | #else | ||
207 | static inline void sm501_dump_gate(struct sm501_devdata *sm) { } | ||
208 | static inline void sm501_dump_regs(struct sm501_devdata *sm) { } | ||
209 | static inline void sm501_dump_clk(struct sm501_devdata *sm) { } | ||
176 | #endif | 210 | #endif |
177 | 211 | ||
178 | /* sm501_sync_regs | 212 | /* sm501_sync_regs |
@@ -185,9 +219,21 @@ static void sm501_sync_regs(struct sm501_devdata *sm) | |||
185 | readl(sm->regs); | 219 | readl(sm->regs); |
186 | } | 220 | } |
187 | 221 | ||
222 | static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay) | ||
223 | { | ||
224 | /* during suspend/resume, we are currently not allowed to sleep, | ||
225 | * so change to using mdelay() instead of msleep() if we | ||
226 | * are in one of these paths */ | ||
227 | |||
228 | if (sm->in_suspend) | ||
229 | mdelay(delay); | ||
230 | else | ||
231 | msleep(delay); | ||
232 | } | ||
233 | |||
188 | /* sm501_misc_control | 234 | /* sm501_misc_control |
189 | * | 235 | * |
190 | * alters the misceleneous control parameters | 236 | * alters the miscellaneous control parameters |
191 | */ | 237 | */ |
192 | 238 | ||
193 | int sm501_misc_control(struct device *dev, | 239 | int sm501_misc_control(struct device *dev, |
@@ -368,7 +414,7 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) | |||
368 | dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", | 414 | dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", |
369 | gate, clock, mode); | 415 | gate, clock, mode); |
370 | 416 | ||
371 | msleep(16); | 417 | sm501_mdelay(sm, 16); |
372 | 418 | ||
373 | already: | 419 | already: |
374 | mutex_unlock(&sm->clock_lock); | 420 | mutex_unlock(&sm->clock_lock); |
@@ -538,7 +584,7 @@ unsigned long sm501_set_clock(struct device *dev, | |||
538 | dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", | 584 | dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", |
539 | gate, clock, mode); | 585 | gate, clock, mode); |
540 | 586 | ||
541 | msleep(16); | 587 | sm501_mdelay(sm, 16); |
542 | mutex_unlock(&sm->clock_lock); | 588 | mutex_unlock(&sm->clock_lock); |
543 | 589 | ||
544 | sm501_dump_clk(sm); | 590 | sm501_dump_clk(sm); |
@@ -841,9 +887,7 @@ static int sm501_init_dev(struct sm501_devdata *sm) | |||
841 | sm->regs, readl(sm->regs + SM501_DEVICEID), | 887 | sm->regs, readl(sm->regs + SM501_DEVICEID), |
842 | (unsigned long)mem_avail >> 20, sm->irq); | 888 | (unsigned long)mem_avail >> 20, sm->irq); |
843 | 889 | ||
844 | dev_info(sm->dev, "CurrentGate %08x\n", readl(sm->regs+0x38)); | 890 | sm501_dump_gate(sm); |
845 | dev_info(sm->dev, "CurrentClock %08x\n", readl(sm->regs+0x3c)); | ||
846 | dev_info(sm->dev, "PowerModeControl %08x\n", readl(sm->regs+0x54)); | ||
847 | 891 | ||
848 | ret = device_create_file(sm->dev, &dev_attr_dbg_regs); | 892 | ret = device_create_file(sm->dev, &dev_attr_dbg_regs); |
849 | if (ret) | 893 | if (ret) |
@@ -933,6 +977,57 @@ static int sm501_plat_probe(struct platform_device *dev) | |||
933 | 977 | ||
934 | } | 978 | } |
935 | 979 | ||
980 | #ifdef CONFIG_PM | ||
981 | /* power management support */ | ||
982 | |||
983 | static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) | ||
984 | { | ||
985 | struct sm501_devdata *sm = platform_get_drvdata(pdev); | ||
986 | |||
987 | sm->in_suspend = 1; | ||
988 | sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL); | ||
989 | |||
990 | sm501_dump_regs(sm); | ||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | static int sm501_plat_resume(struct platform_device *pdev) | ||
995 | { | ||
996 | struct sm501_devdata *sm = platform_get_drvdata(pdev); | ||
997 | |||
998 | sm501_dump_regs(sm); | ||
999 | sm501_dump_gate(sm); | ||
1000 | sm501_dump_clk(sm); | ||
1001 | |||
1002 | /* check to see if we are in the same state as when suspended */ | ||
1003 | |||
1004 | if (readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) { | ||
1005 | dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n"); | ||
1006 | writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL); | ||
1007 | |||
1008 | /* our suspend causes the controller state to change, | ||
1009 | * either by something attempting setup, power loss, | ||
1010 | * or an external reset event on power change */ | ||
1011 | |||
1012 | if (sm->platdata && sm->platdata->init) { | ||
1013 | sm501_init_regs(sm, sm->platdata->init); | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | /* dump our state from resume */ | ||
1018 | |||
1019 | sm501_dump_regs(sm); | ||
1020 | sm501_dump_clk(sm); | ||
1021 | |||
1022 | sm->in_suspend = 0; | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | #else | ||
1027 | #define sm501_plat_suspend NULL | ||
1028 | #define sm501_plat_resume NULL | ||
1029 | #endif | ||
1030 | |||
936 | /* Initialisation data for PCI devices */ | 1031 | /* Initialisation data for PCI devices */ |
937 | 1032 | ||
938 | static struct sm501_initdata sm501_pci_initdata = { | 1033 | static struct sm501_initdata sm501_pci_initdata = { |
@@ -1126,6 +1221,8 @@ static struct platform_driver sm501_plat_drv = { | |||
1126 | }, | 1221 | }, |
1127 | .probe = sm501_plat_probe, | 1222 | .probe = sm501_plat_probe, |
1128 | .remove = sm501_plat_remove, | 1223 | .remove = sm501_plat_remove, |
1224 | .suspend = sm501_plat_suspend, | ||
1225 | .resume = sm501_plat_resume, | ||
1129 | }; | 1226 | }; |
1130 | 1227 | ||
1131 | static int __init sm501_base_init(void) | 1228 | static int __init sm501_base_init(void) |