Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 1 | #include <linux/init.h> |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 2 | #include <linux/suspend.h> |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 3 | #include <asm/io.h> |
| 4 | #include <asm/time.h> |
| 5 | #include <asm/mpc52xx.h> |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 6 | |
| 7 | /* defined in lite5200_sleep.S and only used here */ |
| 8 | extern void lite5200_low_power(void __iomem *sram, void __iomem *mbar); |
| 9 | |
| 10 | static struct mpc52xx_cdm __iomem *cdm; |
| 11 | static struct mpc52xx_intr __iomem *pic; |
| 12 | static struct mpc52xx_sdma __iomem *bes; |
| 13 | static struct mpc52xx_xlb __iomem *xlb; |
| 14 | static struct mpc52xx_gpio __iomem *gps; |
| 15 | static struct mpc52xx_gpio_wkup __iomem *gpw; |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 16 | static void __iomem *pci; |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 17 | static void __iomem *sram; |
| 18 | static const int sram_size = 0x4000; /* 16 kBytes */ |
| 19 | static void __iomem *mbar; |
| 20 | |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 21 | static suspend_state_t lite5200_pm_target_state; |
| 22 | |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 23 | static int lite5200_pm_valid(suspend_state_t state) |
| 24 | { |
| 25 | switch (state) { |
| 26 | case PM_SUSPEND_STANDBY: |
| 27 | case PM_SUSPEND_MEM: |
| 28 | return 1; |
| 29 | default: |
| 30 | return 0; |
| 31 | } |
| 32 | } |
| 33 | |
Rafael J. Wysocki | c697eec | 2008-01-08 00:04:17 +0100 | [diff] [blame] | 34 | static int lite5200_pm_begin(suspend_state_t state) |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 35 | { |
| 36 | if (lite5200_pm_valid(state)) { |
| 37 | lite5200_pm_target_state = state; |
| 38 | return 0; |
| 39 | } |
| 40 | return -EINVAL; |
| 41 | } |
| 42 | |
| 43 | static int lite5200_pm_prepare(void) |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 44 | { |
Grant Likely | 75ca399 | 2008-01-18 09:30:37 -0700 | [diff] [blame] | 45 | struct device_node *np; |
Grant Likely | 66ffbe4 | 2008-01-24 22:25:31 -0700 | [diff] [blame] | 46 | const struct of_device_id immr_ids[] = { |
| 47 | { .compatible = "fsl,mpc5200-immr", }, |
| 48 | { .compatible = "fsl,mpc5200b-immr", }, |
| 49 | { .type = "soc", .compatible = "mpc5200", }, /* lite5200 */ |
| 50 | { .type = "builtin", .compatible = "mpc5200", }, /* efika */ |
| 51 | {} |
| 52 | }; |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 53 | u64 regaddr64 = 0; |
| 54 | const u32 *regaddr_p; |
Grant Likely | 75ca399 | 2008-01-18 09:30:37 -0700 | [diff] [blame] | 55 | |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 56 | /* deep sleep? let mpc52xx code handle that */ |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 57 | if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) |
| 58 | return mpc52xx_pm_prepare(); |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 59 | |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 60 | if (lite5200_pm_target_state != PM_SUSPEND_MEM) |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 61 | return -EINVAL; |
| 62 | |
| 63 | /* map registers */ |
Grant Likely | 66ffbe4 | 2008-01-24 22:25:31 -0700 | [diff] [blame] | 64 | np = of_find_matching_node(NULL, immr_ids); |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 65 | regaddr_p = of_get_address(np, 0, NULL, NULL); |
| 66 | if (regaddr_p) |
| 67 | regaddr64 = of_translate_address(np, regaddr_p); |
Grant Likely | 75ca399 | 2008-01-18 09:30:37 -0700 | [diff] [blame] | 68 | of_node_put(np); |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 69 | |
| 70 | mbar = ioremap((u32) regaddr64, 0xC000); |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 71 | if (!mbar) { |
| 72 | printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); |
| 73 | return -ENOSYS; |
| 74 | } |
| 75 | |
| 76 | cdm = mbar + 0x200; |
| 77 | pic = mbar + 0x500; |
| 78 | gps = mbar + 0xb00; |
| 79 | gpw = mbar + 0xc00; |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 80 | pci = mbar + 0xd00; |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 81 | bes = mbar + 0x1200; |
| 82 | xlb = mbar + 0x1f00; |
| 83 | sram = mbar + 0x8000; |
| 84 | |
| 85 | return 0; |
| 86 | } |
| 87 | |
| 88 | /* save and restore registers not bound to any real devices */ |
| 89 | static struct mpc52xx_cdm scdm; |
| 90 | static struct mpc52xx_intr spic; |
| 91 | static struct mpc52xx_sdma sbes; |
| 92 | static struct mpc52xx_xlb sxlb; |
| 93 | static struct mpc52xx_gpio sgps; |
| 94 | static struct mpc52xx_gpio_wkup sgpw; |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 95 | static char spci[0x200]; |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 96 | |
| 97 | static void lite5200_save_regs(void) |
| 98 | { |
| 99 | _memcpy_fromio(&spic, pic, sizeof(*pic)); |
| 100 | _memcpy_fromio(&sbes, bes, sizeof(*bes)); |
| 101 | _memcpy_fromio(&scdm, cdm, sizeof(*cdm)); |
| 102 | _memcpy_fromio(&sxlb, xlb, sizeof(*xlb)); |
| 103 | _memcpy_fromio(&sgps, gps, sizeof(*gps)); |
| 104 | _memcpy_fromio(&sgpw, gpw, sizeof(*gpw)); |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 105 | _memcpy_fromio(spci, pci, 0x200); |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 106 | |
| 107 | _memcpy_fromio(saved_sram, sram, sram_size); |
| 108 | } |
| 109 | |
| 110 | static void lite5200_restore_regs(void) |
| 111 | { |
| 112 | int i; |
| 113 | _memcpy_toio(sram, saved_sram, sram_size); |
| 114 | |
Tim Yamin | 18d76ac | 2008-06-17 09:33:14 +0100 | [diff] [blame] | 115 | /* PCI Configuration */ |
| 116 | _memcpy_toio(pci, spci, 0x200); |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 117 | |
| 118 | /* |
| 119 | * GPIOs. Interrupt Master Enable has higher address then other |
| 120 | * registers, so just memcpy is ok. |
| 121 | */ |
| 122 | _memcpy_toio(gpw, &sgpw, sizeof(*gpw)); |
| 123 | _memcpy_toio(gps, &sgps, sizeof(*gps)); |
| 124 | |
| 125 | |
| 126 | /* XLB Arbitrer */ |
| 127 | out_be32(&xlb->snoop_window, sxlb.snoop_window); |
| 128 | out_be32(&xlb->master_priority, sxlb.master_priority); |
| 129 | out_be32(&xlb->master_pri_enable, sxlb.master_pri_enable); |
| 130 | |
| 131 | /* enable */ |
| 132 | out_be32(&xlb->int_enable, sxlb.int_enable); |
| 133 | out_be32(&xlb->config, sxlb.config); |
| 134 | |
| 135 | |
| 136 | /* CDM - Clock Distribution Module */ |
| 137 | out_8(&cdm->ipb_clk_sel, scdm.ipb_clk_sel); |
| 138 | out_8(&cdm->pci_clk_sel, scdm.pci_clk_sel); |
| 139 | |
| 140 | out_8(&cdm->ext_48mhz_en, scdm.ext_48mhz_en); |
| 141 | out_8(&cdm->fd_enable, scdm.fd_enable); |
| 142 | out_be16(&cdm->fd_counters, scdm.fd_counters); |
| 143 | |
| 144 | out_be32(&cdm->clk_enables, scdm.clk_enables); |
| 145 | |
| 146 | out_8(&cdm->osc_disable, scdm.osc_disable); |
| 147 | |
| 148 | out_be16(&cdm->mclken_div_psc1, scdm.mclken_div_psc1); |
| 149 | out_be16(&cdm->mclken_div_psc2, scdm.mclken_div_psc2); |
| 150 | out_be16(&cdm->mclken_div_psc3, scdm.mclken_div_psc3); |
| 151 | out_be16(&cdm->mclken_div_psc6, scdm.mclken_div_psc6); |
| 152 | |
| 153 | |
| 154 | /* BESTCOMM */ |
| 155 | out_be32(&bes->taskBar, sbes.taskBar); |
| 156 | out_be32(&bes->currentPointer, sbes.currentPointer); |
| 157 | out_be32(&bes->endPointer, sbes.endPointer); |
| 158 | out_be32(&bes->variablePointer, sbes.variablePointer); |
| 159 | |
| 160 | out_8(&bes->IntVect1, sbes.IntVect1); |
| 161 | out_8(&bes->IntVect2, sbes.IntVect2); |
| 162 | out_be16(&bes->PtdCntrl, sbes.PtdCntrl); |
| 163 | |
| 164 | for (i=0; i<32; i++) |
| 165 | out_8(&bes->ipr[i], sbes.ipr[i]); |
| 166 | |
| 167 | out_be32(&bes->cReqSelect, sbes.cReqSelect); |
| 168 | out_be32(&bes->task_size0, sbes.task_size0); |
| 169 | out_be32(&bes->task_size1, sbes.task_size1); |
| 170 | out_be32(&bes->MDEDebug, sbes.MDEDebug); |
| 171 | out_be32(&bes->ADSDebug, sbes.ADSDebug); |
| 172 | out_be32(&bes->Value1, sbes.Value1); |
| 173 | out_be32(&bes->Value2, sbes.Value2); |
| 174 | out_be32(&bes->Control, sbes.Control); |
| 175 | out_be32(&bes->Status, sbes.Status); |
| 176 | out_be32(&bes->PTDDebug, sbes.PTDDebug); |
| 177 | |
| 178 | /* restore tasks */ |
| 179 | for (i=0; i<16; i++) |
| 180 | out_be16(&bes->tcr[i], sbes.tcr[i]); |
| 181 | |
| 182 | /* enable interrupts */ |
| 183 | out_be32(&bes->IntPend, sbes.IntPend); |
| 184 | out_be32(&bes->IntMask, sbes.IntMask); |
| 185 | |
| 186 | |
| 187 | /* PIC */ |
| 188 | out_be32(&pic->per_pri1, spic.per_pri1); |
| 189 | out_be32(&pic->per_pri2, spic.per_pri2); |
| 190 | out_be32(&pic->per_pri3, spic.per_pri3); |
| 191 | |
| 192 | out_be32(&pic->main_pri1, spic.main_pri1); |
| 193 | out_be32(&pic->main_pri2, spic.main_pri2); |
| 194 | |
| 195 | out_be32(&pic->enc_status, spic.enc_status); |
| 196 | |
| 197 | /* unmask and enable interrupts */ |
| 198 | out_be32(&pic->per_mask, spic.per_mask); |
| 199 | out_be32(&pic->main_mask, spic.main_mask); |
| 200 | out_be32(&pic->ctrl, spic.ctrl); |
| 201 | } |
| 202 | |
| 203 | static int lite5200_pm_enter(suspend_state_t state) |
| 204 | { |
| 205 | /* deep sleep? let mpc52xx code handle that */ |
| 206 | if (state == PM_SUSPEND_STANDBY) { |
| 207 | return mpc52xx_pm_enter(state); |
| 208 | } |
| 209 | |
| 210 | lite5200_save_regs(); |
| 211 | |
| 212 | /* effectively save FP regs */ |
| 213 | enable_kernel_fp(); |
| 214 | |
| 215 | lite5200_low_power(sram, mbar); |
| 216 | |
| 217 | lite5200_restore_regs(); |
| 218 | |
| 219 | /* restart jiffies */ |
| 220 | wakeup_decrementer(); |
| 221 | |
| 222 | iounmap(mbar); |
| 223 | return 0; |
| 224 | } |
| 225 | |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 226 | static void lite5200_pm_finish(void) |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 227 | { |
| 228 | /* deep sleep? let mpc52xx code handle that */ |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 229 | if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) |
| 230 | mpc52xx_pm_finish(); |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 231 | } |
| 232 | |
Rafael J. Wysocki | c697eec | 2008-01-08 00:04:17 +0100 | [diff] [blame] | 233 | static void lite5200_pm_end(void) |
| 234 | { |
| 235 | lite5200_pm_target_state = PM_SUSPEND_ON; |
| 236 | } |
| 237 | |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 238 | static struct platform_suspend_ops lite5200_pm_ops = { |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 239 | .valid = lite5200_pm_valid, |
Rafael J. Wysocki | c697eec | 2008-01-08 00:04:17 +0100 | [diff] [blame] | 240 | .begin = lite5200_pm_begin, |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 241 | .prepare = lite5200_pm_prepare, |
| 242 | .enter = lite5200_pm_enter, |
| 243 | .finish = lite5200_pm_finish, |
Rafael J. Wysocki | c697eec | 2008-01-08 00:04:17 +0100 | [diff] [blame] | 244 | .end = lite5200_pm_end, |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 245 | }; |
| 246 | |
| 247 | int __init lite5200_pm_init(void) |
| 248 | { |
Rafael J. Wysocki | e6c5eb9 | 2007-10-18 03:04:41 -0700 | [diff] [blame] | 249 | suspend_set_ops(&lite5200_pm_ops); |
Domen Puncer | ee98307 | 2007-07-18 06:32:31 +1000 | [diff] [blame] | 250 | return 0; |
| 251 | } |