diff options
author | Domen Puncer <domen.puncer@telargo.com> | 2007-07-17 16:32:31 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-09-19 01:25:34 -0400 |
commit | ee983079ce04641523b23b8ed02cc3503632351e (patch) | |
tree | 3c6b975e47fb38ee51cf3d57e8f5abb1a7699f66 /arch | |
parent | 104f0cc2dcf7ce0ca7da041177233747d6aa0136 (diff) |
[POWERPC] MPC5200 low power mode
Low-power mode implementation for Lite5200b.
Some I/O registers are also saved here.
A recent U-Boot that supports this (lite5200b_PM_config) is needed.
Signed-off-by: Domen Puncer <domen.puncer@telargo.com>
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/platforms/52xx/Makefile | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/lite5200.c | 14 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/lite5200_pm.c | 213 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/lite5200_sleep.S | 412 |
4 files changed, 637 insertions, 5 deletions
diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile index b91e39c84d46..307dbc178091 100644 --- a/arch/powerpc/platforms/52xx/Makefile +++ b/arch/powerpc/platforms/52xx/Makefile | |||
@@ -10,3 +10,6 @@ obj-$(CONFIG_PPC_EFIKA) += efika.o | |||
10 | obj-$(CONFIG_PPC_LITE5200) += lite5200.o | 10 | obj-$(CONFIG_PPC_LITE5200) += lite5200.o |
11 | 11 | ||
12 | obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o | 12 | obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o |
13 | ifeq ($(CONFIG_PPC_LITE5200),y) | ||
14 | obj-$(CONFIG_PM) += lite5200_sleep.o lite5200_pm.o | ||
15 | endif | ||
diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index ce3f6951828e..e11d27f9c4f0 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c | |||
@@ -85,7 +85,6 @@ error: | |||
85 | } | 85 | } |
86 | 86 | ||
87 | #ifdef CONFIG_PM | 87 | #ifdef CONFIG_PM |
88 | static u32 descr_a; | ||
89 | static void lite5200_suspend_prepare(void __iomem *mbar) | 88 | static void lite5200_suspend_prepare(void __iomem *mbar) |
90 | { | 89 | { |
91 | u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */ | 90 | u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */ |
@@ -96,13 +95,18 @@ static void lite5200_suspend_prepare(void __iomem *mbar) | |||
96 | * power down usb port | 95 | * power down usb port |
97 | * this needs to be called before of-ohci suspend code | 96 | * this needs to be called before of-ohci suspend code |
98 | */ | 97 | */ |
99 | descr_a = in_be32(mbar + 0x1048); | 98 | |
100 | out_be32(mbar + 0x1048, (descr_a & ~0x200) | 0x100); | 99 | /* set ports to "power switched" and "powered at the same time" |
100 | * USB Rh descriptor A: NPS = 0, PSM = 0 */ | ||
101 | out_be32(mbar + 0x1048, in_be32(mbar + 0x1048) & ~0x300); | ||
102 | /* USB Rh status: LPS = 1 - turn off power */ | ||
103 | out_be32(mbar + 0x1050, 0x00000001); | ||
101 | } | 104 | } |
102 | 105 | ||
103 | static void lite5200_resume_finish(void __iomem *mbar) | 106 | static void lite5200_resume_finish(void __iomem *mbar) |
104 | { | 107 | { |
105 | out_be32(mbar + 0x1048, descr_a); | 108 | /* USB Rh status: LPSC = 1 - turn on power */ |
109 | out_be32(mbar + 0x1050, 0x00010000); | ||
106 | } | 110 | } |
107 | #endif | 111 | #endif |
108 | 112 | ||
@@ -122,7 +126,7 @@ static void __init lite5200_setup_arch(void) | |||
122 | #ifdef CONFIG_PM | 126 | #ifdef CONFIG_PM |
123 | mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare; | 127 | mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare; |
124 | mpc52xx_suspend.board_resume_finish = lite5200_resume_finish; | 128 | mpc52xx_suspend.board_resume_finish = lite5200_resume_finish; |
125 | mpc52xx_pm_init(); | 129 | lite5200_pm_init(); |
126 | #endif | 130 | #endif |
127 | 131 | ||
128 | #ifdef CONFIG_PCI | 132 | #ifdef CONFIG_PCI |
diff --git a/arch/powerpc/platforms/52xx/lite5200_pm.c b/arch/powerpc/platforms/52xx/lite5200_pm.c new file mode 100644 index 000000000000..f26afcd41757 --- /dev/null +++ b/arch/powerpc/platforms/52xx/lite5200_pm.c | |||
@@ -0,0 +1,213 @@ | |||
1 | #include <linux/init.h> | ||
2 | #include <linux/pm.h> | ||
3 | #include <asm/io.h> | ||
4 | #include <asm/time.h> | ||
5 | #include <asm/mpc52xx.h> | ||
6 | #include "mpc52xx_pic.h" | ||
7 | |||
8 | /* defined in lite5200_sleep.S and only used here */ | ||
9 | extern void lite5200_low_power(void __iomem *sram, void __iomem *mbar); | ||
10 | |||
11 | static struct mpc52xx_cdm __iomem *cdm; | ||
12 | static struct mpc52xx_intr __iomem *pic; | ||
13 | static struct mpc52xx_sdma __iomem *bes; | ||
14 | static struct mpc52xx_xlb __iomem *xlb; | ||
15 | static struct mpc52xx_gpio __iomem *gps; | ||
16 | static struct mpc52xx_gpio_wkup __iomem *gpw; | ||
17 | static void __iomem *sram; | ||
18 | static const int sram_size = 0x4000; /* 16 kBytes */ | ||
19 | static void __iomem *mbar; | ||
20 | |||
21 | static int lite5200_pm_valid(suspend_state_t state) | ||
22 | { | ||
23 | switch (state) { | ||
24 | case PM_SUSPEND_STANDBY: | ||
25 | case PM_SUSPEND_MEM: | ||
26 | return 1; | ||
27 | default: | ||
28 | return 0; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | static int lite5200_pm_prepare(suspend_state_t state) | ||
33 | { | ||
34 | /* deep sleep? let mpc52xx code handle that */ | ||
35 | if (state == PM_SUSPEND_STANDBY) | ||
36 | return mpc52xx_pm_prepare(state); | ||
37 | |||
38 | if (state != PM_SUSPEND_MEM) | ||
39 | return -EINVAL; | ||
40 | |||
41 | /* map registers */ | ||
42 | mbar = mpc52xx_find_and_map("mpc5200"); | ||
43 | if (!mbar) { | ||
44 | printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); | ||
45 | return -ENOSYS; | ||
46 | } | ||
47 | |||
48 | cdm = mbar + 0x200; | ||
49 | pic = mbar + 0x500; | ||
50 | gps = mbar + 0xb00; | ||
51 | gpw = mbar + 0xc00; | ||
52 | bes = mbar + 0x1200; | ||
53 | xlb = mbar + 0x1f00; | ||
54 | sram = mbar + 0x8000; | ||
55 | |||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | /* save and restore registers not bound to any real devices */ | ||
60 | static struct mpc52xx_cdm scdm; | ||
61 | static struct mpc52xx_intr spic; | ||
62 | static struct mpc52xx_sdma sbes; | ||
63 | static struct mpc52xx_xlb sxlb; | ||
64 | static struct mpc52xx_gpio sgps; | ||
65 | static struct mpc52xx_gpio_wkup sgpw; | ||
66 | |||
67 | static void lite5200_save_regs(void) | ||
68 | { | ||
69 | _memcpy_fromio(&spic, pic, sizeof(*pic)); | ||
70 | _memcpy_fromio(&sbes, bes, sizeof(*bes)); | ||
71 | _memcpy_fromio(&scdm, cdm, sizeof(*cdm)); | ||
72 | _memcpy_fromio(&sxlb, xlb, sizeof(*xlb)); | ||
73 | _memcpy_fromio(&sgps, gps, sizeof(*gps)); | ||
74 | _memcpy_fromio(&sgpw, gpw, sizeof(*gpw)); | ||
75 | |||
76 | _memcpy_fromio(saved_sram, sram, sram_size); | ||
77 | } | ||
78 | |||
79 | static void lite5200_restore_regs(void) | ||
80 | { | ||
81 | int i; | ||
82 | _memcpy_toio(sram, saved_sram, sram_size); | ||
83 | |||
84 | |||
85 | /* | ||
86 | * GPIOs. Interrupt Master Enable has higher address then other | ||
87 | * registers, so just memcpy is ok. | ||
88 | */ | ||
89 | _memcpy_toio(gpw, &sgpw, sizeof(*gpw)); | ||
90 | _memcpy_toio(gps, &sgps, sizeof(*gps)); | ||
91 | |||
92 | |||
93 | /* XLB Arbitrer */ | ||
94 | out_be32(&xlb->snoop_window, sxlb.snoop_window); | ||
95 | out_be32(&xlb->master_priority, sxlb.master_priority); | ||
96 | out_be32(&xlb->master_pri_enable, sxlb.master_pri_enable); | ||
97 | |||
98 | /* enable */ | ||
99 | out_be32(&xlb->int_enable, sxlb.int_enable); | ||
100 | out_be32(&xlb->config, sxlb.config); | ||
101 | |||
102 | |||
103 | /* CDM - Clock Distribution Module */ | ||
104 | out_8(&cdm->ipb_clk_sel, scdm.ipb_clk_sel); | ||
105 | out_8(&cdm->pci_clk_sel, scdm.pci_clk_sel); | ||
106 | |||
107 | out_8(&cdm->ext_48mhz_en, scdm.ext_48mhz_en); | ||
108 | out_8(&cdm->fd_enable, scdm.fd_enable); | ||
109 | out_be16(&cdm->fd_counters, scdm.fd_counters); | ||
110 | |||
111 | out_be32(&cdm->clk_enables, scdm.clk_enables); | ||
112 | |||
113 | out_8(&cdm->osc_disable, scdm.osc_disable); | ||
114 | |||
115 | out_be16(&cdm->mclken_div_psc1, scdm.mclken_div_psc1); | ||
116 | out_be16(&cdm->mclken_div_psc2, scdm.mclken_div_psc2); | ||
117 | out_be16(&cdm->mclken_div_psc3, scdm.mclken_div_psc3); | ||
118 | out_be16(&cdm->mclken_div_psc6, scdm.mclken_div_psc6); | ||
119 | |||
120 | |||
121 | /* BESTCOMM */ | ||
122 | out_be32(&bes->taskBar, sbes.taskBar); | ||
123 | out_be32(&bes->currentPointer, sbes.currentPointer); | ||
124 | out_be32(&bes->endPointer, sbes.endPointer); | ||
125 | out_be32(&bes->variablePointer, sbes.variablePointer); | ||
126 | |||
127 | out_8(&bes->IntVect1, sbes.IntVect1); | ||
128 | out_8(&bes->IntVect2, sbes.IntVect2); | ||
129 | out_be16(&bes->PtdCntrl, sbes.PtdCntrl); | ||
130 | |||
131 | for (i=0; i<32; i++) | ||
132 | out_8(&bes->ipr[i], sbes.ipr[i]); | ||
133 | |||
134 | out_be32(&bes->cReqSelect, sbes.cReqSelect); | ||
135 | out_be32(&bes->task_size0, sbes.task_size0); | ||
136 | out_be32(&bes->task_size1, sbes.task_size1); | ||
137 | out_be32(&bes->MDEDebug, sbes.MDEDebug); | ||
138 | out_be32(&bes->ADSDebug, sbes.ADSDebug); | ||
139 | out_be32(&bes->Value1, sbes.Value1); | ||
140 | out_be32(&bes->Value2, sbes.Value2); | ||
141 | out_be32(&bes->Control, sbes.Control); | ||
142 | out_be32(&bes->Status, sbes.Status); | ||
143 | out_be32(&bes->PTDDebug, sbes.PTDDebug); | ||
144 | |||
145 | /* restore tasks */ | ||
146 | for (i=0; i<16; i++) | ||
147 | out_be16(&bes->tcr[i], sbes.tcr[i]); | ||
148 | |||
149 | /* enable interrupts */ | ||
150 | out_be32(&bes->IntPend, sbes.IntPend); | ||
151 | out_be32(&bes->IntMask, sbes.IntMask); | ||
152 | |||
153 | |||
154 | /* PIC */ | ||
155 | out_be32(&pic->per_pri1, spic.per_pri1); | ||
156 | out_be32(&pic->per_pri2, spic.per_pri2); | ||
157 | out_be32(&pic->per_pri3, spic.per_pri3); | ||
158 | |||
159 | out_be32(&pic->main_pri1, spic.main_pri1); | ||
160 | out_be32(&pic->main_pri2, spic.main_pri2); | ||
161 | |||
162 | out_be32(&pic->enc_status, spic.enc_status); | ||
163 | |||
164 | /* unmask and enable interrupts */ | ||
165 | out_be32(&pic->per_mask, spic.per_mask); | ||
166 | out_be32(&pic->main_mask, spic.main_mask); | ||
167 | out_be32(&pic->ctrl, spic.ctrl); | ||
168 | } | ||
169 | |||
170 | static int lite5200_pm_enter(suspend_state_t state) | ||
171 | { | ||
172 | /* deep sleep? let mpc52xx code handle that */ | ||
173 | if (state == PM_SUSPEND_STANDBY) { | ||
174 | return mpc52xx_pm_enter(state); | ||
175 | } | ||
176 | |||
177 | lite5200_save_regs(); | ||
178 | |||
179 | /* effectively save FP regs */ | ||
180 | enable_kernel_fp(); | ||
181 | |||
182 | lite5200_low_power(sram, mbar); | ||
183 | |||
184 | lite5200_restore_regs(); | ||
185 | |||
186 | /* restart jiffies */ | ||
187 | wakeup_decrementer(); | ||
188 | |||
189 | iounmap(mbar); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int lite5200_pm_finish(suspend_state_t state) | ||
194 | { | ||
195 | /* deep sleep? let mpc52xx code handle that */ | ||
196 | if (state == PM_SUSPEND_STANDBY) { | ||
197 | return mpc52xx_pm_finish(state); | ||
198 | } | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static struct pm_ops lite5200_pm_ops = { | ||
203 | .valid = lite5200_pm_valid, | ||
204 | .prepare = lite5200_pm_prepare, | ||
205 | .enter = lite5200_pm_enter, | ||
206 | .finish = lite5200_pm_finish, | ||
207 | }; | ||
208 | |||
209 | int __init lite5200_pm_init(void) | ||
210 | { | ||
211 | pm_set_ops(&lite5200_pm_ops); | ||
212 | return 0; | ||
213 | } | ||
diff --git a/arch/powerpc/platforms/52xx/lite5200_sleep.S b/arch/powerpc/platforms/52xx/lite5200_sleep.S new file mode 100644 index 000000000000..08ab6fefcf7a --- /dev/null +++ b/arch/powerpc/platforms/52xx/lite5200_sleep.S | |||
@@ -0,0 +1,412 @@ | |||
1 | #include <asm/reg.h> | ||
2 | #include <asm/ppc_asm.h> | ||
3 | #include <asm/processor.h> | ||
4 | #include <asm/cache.h> | ||
5 | |||
6 | |||
7 | #define SDRAM_CTRL 0x104 | ||
8 | #define SC_MODE_EN (1<<31) | ||
9 | #define SC_CKE (1<<30) | ||
10 | #define SC_REF_EN (1<<28) | ||
11 | #define SC_SOFT_PRE (1<<1) | ||
12 | |||
13 | #define GPIOW_GPIOE 0xc00 | ||
14 | #define GPIOW_DDR 0xc08 | ||
15 | #define GPIOW_DVO 0xc0c | ||
16 | |||
17 | #define CDM_CE 0x214 | ||
18 | #define CDM_SDRAM (1<<3) | ||
19 | |||
20 | |||
21 | /* helpers... beware: r10 and r4 are overwritten */ | ||
22 | #define SAVE_SPRN(reg, addr) \ | ||
23 | mfspr r10, SPRN_##reg; \ | ||
24 | stw r10, ((addr)*4)(r4); | ||
25 | |||
26 | #define LOAD_SPRN(reg, addr) \ | ||
27 | lwz r10, ((addr)*4)(r4); \ | ||
28 | mtspr SPRN_##reg, r10; \ | ||
29 | sync; \ | ||
30 | isync; | ||
31 | |||
32 | |||
33 | .data | ||
34 | registers: | ||
35 | .space 0x5c*4 | ||
36 | .text | ||
37 | |||
38 | /* ---------------------------------------------------------------------- */ | ||
39 | /* low-power mode with help of M68HLC908QT1 */ | ||
40 | |||
41 | .globl lite5200_low_power | ||
42 | lite5200_low_power: | ||
43 | |||
44 | mr r7, r3 /* save SRAM va */ | ||
45 | mr r8, r4 /* save MBAR va */ | ||
46 | |||
47 | /* setup wakeup address for u-boot at physical location 0x0 */ | ||
48 | lis r3, CONFIG_KERNEL_START@h | ||
49 | lis r4, lite5200_wakeup@h | ||
50 | ori r4, r4, lite5200_wakeup@l | ||
51 | sub r4, r4, r3 | ||
52 | stw r4, 0(r3) | ||
53 | |||
54 | |||
55 | /* | ||
56 | * save stuff BDI overwrites | ||
57 | * 0xf0 (0xe0->0x100 gets overwritten when BDI connected; | ||
58 | * even when CONFIG_BDI* is disabled and MMU XLAT commented; heisenbug?)) | ||
59 | * WARNING: self-refresh doesn't seem to work when BDI2000 is connected, | ||
60 | * possibly because BDI sets SDRAM registers before wakeup code does | ||
61 | */ | ||
62 | lis r4, registers@h | ||
63 | ori r4, r4, registers@l | ||
64 | lwz r10, 0xf0(r3) | ||
65 | stw r10, (0x1d*4)(r4) | ||
66 | |||
67 | /* save registers to r4 [destroys r10] */ | ||
68 | SAVE_SPRN(LR, 0x1c) | ||
69 | bl save_regs | ||
70 | |||
71 | /* flush caches [destroys r3, r4] */ | ||
72 | bl flush_data_cache | ||
73 | |||
74 | |||
75 | /* copy code to sram */ | ||
76 | mr r4, r7 | ||
77 | li r3, (sram_code_end - sram_code)/4 | ||
78 | mtctr r3 | ||
79 | lis r3, sram_code@h | ||
80 | ori r3, r3, sram_code@l | ||
81 | 1: | ||
82 | lwz r5, 0(r3) | ||
83 | stw r5, 0(r4) | ||
84 | addi r3, r3, 4 | ||
85 | addi r4, r4, 4 | ||
86 | bdnz 1b | ||
87 | |||
88 | /* get tb_ticks_per_usec */ | ||
89 | lis r3, tb_ticks_per_usec@h | ||
90 | lwz r11, tb_ticks_per_usec@l(r3) | ||
91 | |||
92 | /* disable I and D caches */ | ||
93 | mfspr r3, SPRN_HID0 | ||
94 | ori r3, r3, HID0_ICE | HID0_DCE | ||
95 | xori r3, r3, HID0_ICE | HID0_DCE | ||
96 | sync; isync; | ||
97 | mtspr SPRN_HID0, r3 | ||
98 | sync; isync; | ||
99 | |||
100 | /* jump to sram */ | ||
101 | mtlr r7 | ||
102 | blrl | ||
103 | /* doesn't return */ | ||
104 | |||
105 | |||
106 | sram_code: | ||
107 | /* self refresh */ | ||
108 | lwz r4, SDRAM_CTRL(r8) | ||
109 | |||
110 | /* send NOP (precharge) */ | ||
111 | oris r4, r4, SC_MODE_EN@h /* mode_en */ | ||
112 | stw r4, SDRAM_CTRL(r8) | ||
113 | sync | ||
114 | |||
115 | ori r4, r4, SC_SOFT_PRE /* soft_pre */ | ||
116 | stw r4, SDRAM_CTRL(r8) | ||
117 | sync | ||
118 | xori r4, r4, SC_SOFT_PRE | ||
119 | |||
120 | xoris r4, r4, SC_MODE_EN@h /* !mode_en */ | ||
121 | stw r4, SDRAM_CTRL(r8) | ||
122 | sync | ||
123 | |||
124 | /* delay (for NOP to finish) */ | ||
125 | li r12, 1 | ||
126 | bl udelay | ||
127 | |||
128 | /* | ||
129 | * mode_en must not be set when enabling self-refresh | ||
130 | * send AR with CKE low (self-refresh) | ||
131 | */ | ||
132 | oris r4, r4, (SC_REF_EN | SC_CKE)@h | ||
133 | xoris r4, r4, (SC_CKE)@h /* ref_en !cke */ | ||
134 | stw r4, SDRAM_CTRL(r8) | ||
135 | sync | ||
136 | |||
137 | /* delay (after !CKE there should be two cycles) */ | ||
138 | li r12, 1 | ||
139 | bl udelay | ||
140 | |||
141 | /* disable clock */ | ||
142 | lwz r4, CDM_CE(r8) | ||
143 | ori r4, r4, CDM_SDRAM | ||
144 | xori r4, r4, CDM_SDRAM | ||
145 | stw r4, CDM_CE(r8) | ||
146 | sync | ||
147 | |||
148 | /* delay a bit */ | ||
149 | li r12, 1 | ||
150 | bl udelay | ||
151 | |||
152 | |||
153 | /* turn off with QT chip */ | ||
154 | li r4, 0x02 | ||
155 | stb r4, GPIOW_GPIOE(r8) /* enable gpio_wkup1 */ | ||
156 | sync | ||
157 | |||
158 | stb r4, GPIOW_DVO(r8) /* "output" high */ | ||
159 | sync | ||
160 | stb r4, GPIOW_DDR(r8) /* output */ | ||
161 | sync | ||
162 | stb r4, GPIOW_DVO(r8) /* output high */ | ||
163 | sync | ||
164 | |||
165 | /* 10uS delay */ | ||
166 | li r12, 10 | ||
167 | bl udelay | ||
168 | |||
169 | /* turn off */ | ||
170 | li r4, 0 | ||
171 | stb r4, GPIOW_DVO(r8) /* output low */ | ||
172 | sync | ||
173 | |||
174 | /* wait until we're offline */ | ||
175 | 1: | ||
176 | b 1b | ||
177 | |||
178 | |||
179 | /* local udelay in sram is needed */ | ||
180 | udelay: /* r11 - tb_ticks_per_usec, r12 - usecs, overwrites r13 */ | ||
181 | mullw r12, r12, r11 | ||
182 | mftb r13 /* start */ | ||
183 | addi r12, r13, r12 /* end */ | ||
184 | 1: | ||
185 | mftb r13 /* current */ | ||
186 | cmp cr0, r13, r12 | ||
187 | blt 1b | ||
188 | blr | ||
189 | |||
190 | sram_code_end: | ||
191 | |||
192 | |||
193 | |||
194 | /* uboot jumps here on resume */ | ||
195 | lite5200_wakeup: | ||
196 | bl restore_regs | ||
197 | |||
198 | |||
199 | /* HIDs, MSR */ | ||
200 | LOAD_SPRN(HID1, 0x19) | ||
201 | LOAD_SPRN(HID2, 0x1a) | ||
202 | |||
203 | |||
204 | /* address translation is tricky (see turn_on_mmu) */ | ||
205 | mfmsr r10 | ||
206 | ori r10, r10, MSR_DR | MSR_IR | ||
207 | |||
208 | |||
209 | mtspr SPRN_SRR1, r10 | ||
210 | lis r10, mmu_on@h | ||
211 | ori r10, r10, mmu_on@l | ||
212 | mtspr SPRN_SRR0, r10 | ||
213 | sync | ||
214 | rfi | ||
215 | mmu_on: | ||
216 | /* kernel offset (r4 is still set from restore_registers) */ | ||
217 | addis r4, r4, CONFIG_KERNEL_START@h | ||
218 | |||
219 | |||
220 | /* restore MSR */ | ||
221 | lwz r10, (4*0x1b)(r4) | ||
222 | mtmsr r10 | ||
223 | sync; isync; | ||
224 | |||
225 | /* invalidate caches */ | ||
226 | mfspr r10, SPRN_HID0 | ||
227 | ori r5, r10, HID0_ICFI | HID0_DCI | ||
228 | mtspr SPRN_HID0, r5 /* invalidate caches */ | ||
229 | sync; isync; | ||
230 | mtspr SPRN_HID0, r10 | ||
231 | sync; isync; | ||
232 | |||
233 | /* enable caches */ | ||
234 | lwz r10, (4*0x18)(r4) | ||
235 | mtspr SPRN_HID0, r10 /* restore (enable caches, DPM) */ | ||
236 | /* ^ this has to be after address translation set in MSR */ | ||
237 | sync | ||
238 | isync | ||
239 | |||
240 | |||
241 | /* restore 0xf0 (BDI2000) */ | ||
242 | lis r3, CONFIG_KERNEL_START@h | ||
243 | lwz r10, (0x1d*4)(r4) | ||
244 | stw r10, 0xf0(r3) | ||
245 | |||
246 | LOAD_SPRN(LR, 0x1c) | ||
247 | |||
248 | |||
249 | blr | ||
250 | |||
251 | |||
252 | /* ---------------------------------------------------------------------- */ | ||
253 | /* boring code: helpers */ | ||
254 | |||
255 | /* save registers */ | ||
256 | #define SAVE_BAT(n, addr) \ | ||
257 | SAVE_SPRN(DBAT##n##L, addr); \ | ||
258 | SAVE_SPRN(DBAT##n##U, addr+1); \ | ||
259 | SAVE_SPRN(IBAT##n##L, addr+2); \ | ||
260 | SAVE_SPRN(IBAT##n##U, addr+3); | ||
261 | |||
262 | #define SAVE_SR(n, addr) \ | ||
263 | mfsr r10, n; \ | ||
264 | stw r10, ((addr)*4)(r4); | ||
265 | |||
266 | #define SAVE_4SR(n, addr) \ | ||
267 | SAVE_SR(n, addr); \ | ||
268 | SAVE_SR(n+1, addr+1); \ | ||
269 | SAVE_SR(n+2, addr+2); \ | ||
270 | SAVE_SR(n+3, addr+3); | ||
271 | |||
272 | save_regs: | ||
273 | stw r0, 0(r4) | ||
274 | stw r1, 0x4(r4) | ||
275 | stw r2, 0x8(r4) | ||
276 | stmw r11, 0xc(r4) /* 0xc -> 0x5f, (0x18*4-1) */ | ||
277 | |||
278 | SAVE_SPRN(HID0, 0x18) | ||
279 | SAVE_SPRN(HID1, 0x19) | ||
280 | SAVE_SPRN(HID2, 0x1a) | ||
281 | mfmsr r10 | ||
282 | stw r10, (4*0x1b)(r4) | ||
283 | /*SAVE_SPRN(LR, 0x1c) have to save it before the call */ | ||
284 | /* 0x1d reserved by 0xf0 */ | ||
285 | SAVE_SPRN(RPA, 0x1e) | ||
286 | SAVE_SPRN(SDR1, 0x1f) | ||
287 | |||
288 | /* save MMU regs */ | ||
289 | SAVE_BAT(0, 0x20) | ||
290 | SAVE_BAT(1, 0x24) | ||
291 | SAVE_BAT(2, 0x28) | ||
292 | SAVE_BAT(3, 0x2c) | ||
293 | SAVE_BAT(4, 0x30) | ||
294 | SAVE_BAT(5, 0x34) | ||
295 | SAVE_BAT(6, 0x38) | ||
296 | SAVE_BAT(7, 0x3c) | ||
297 | |||
298 | SAVE_4SR(0, 0x40) | ||
299 | SAVE_4SR(4, 0x44) | ||
300 | SAVE_4SR(8, 0x48) | ||
301 | SAVE_4SR(12, 0x4c) | ||
302 | |||
303 | SAVE_SPRN(SPRG0, 0x50) | ||
304 | SAVE_SPRN(SPRG1, 0x51) | ||
305 | SAVE_SPRN(SPRG2, 0x52) | ||
306 | SAVE_SPRN(SPRG3, 0x53) | ||
307 | SAVE_SPRN(SPRG4, 0x54) | ||
308 | SAVE_SPRN(SPRG5, 0x55) | ||
309 | SAVE_SPRN(SPRG6, 0x56) | ||
310 | SAVE_SPRN(SPRG7, 0x57) | ||
311 | |||
312 | SAVE_SPRN(IABR, 0x58) | ||
313 | SAVE_SPRN(DABR, 0x59) | ||
314 | SAVE_SPRN(TBRL, 0x5a) | ||
315 | SAVE_SPRN(TBRU, 0x5b) | ||
316 | |||
317 | blr | ||
318 | |||
319 | |||
320 | /* restore registers */ | ||
321 | #define LOAD_BAT(n, addr) \ | ||
322 | LOAD_SPRN(DBAT##n##L, addr); \ | ||
323 | LOAD_SPRN(DBAT##n##U, addr+1); \ | ||
324 | LOAD_SPRN(IBAT##n##L, addr+2); \ | ||
325 | LOAD_SPRN(IBAT##n##U, addr+3); | ||
326 | |||
327 | #define LOAD_SR(n, addr) \ | ||
328 | lwz r10, ((addr)*4)(r4); \ | ||
329 | mtsr n, r10; | ||
330 | |||
331 | #define LOAD_4SR(n, addr) \ | ||
332 | LOAD_SR(n, addr); \ | ||
333 | LOAD_SR(n+1, addr+1); \ | ||
334 | LOAD_SR(n+2, addr+2); \ | ||
335 | LOAD_SR(n+3, addr+3); | ||
336 | |||
337 | restore_regs: | ||
338 | lis r4, registers@h | ||
339 | ori r4, r4, registers@l | ||
340 | |||
341 | /* MMU is not up yet */ | ||
342 | subis r4, r4, CONFIG_KERNEL_START@h | ||
343 | |||
344 | lwz r0, 0(r4) | ||
345 | lwz r1, 0x4(r4) | ||
346 | lwz r2, 0x8(r4) | ||
347 | lmw r11, 0xc(r4) | ||
348 | |||
349 | /* | ||
350 | * these are a bit tricky | ||
351 | * | ||
352 | * 0x18 - HID0 | ||
353 | * 0x19 - HID1 | ||
354 | * 0x1a - HID2 | ||
355 | * 0x1b - MSR | ||
356 | * 0x1c - LR | ||
357 | * 0x1d - reserved by 0xf0 (BDI2000) | ||
358 | */ | ||
359 | LOAD_SPRN(RPA, 0x1e); | ||
360 | LOAD_SPRN(SDR1, 0x1f); | ||
361 | |||
362 | /* restore MMU regs */ | ||
363 | LOAD_BAT(0, 0x20) | ||
364 | LOAD_BAT(1, 0x24) | ||
365 | LOAD_BAT(2, 0x28) | ||
366 | LOAD_BAT(3, 0x2c) | ||
367 | LOAD_BAT(4, 0x30) | ||
368 | LOAD_BAT(5, 0x34) | ||
369 | LOAD_BAT(6, 0x38) | ||
370 | LOAD_BAT(7, 0x3c) | ||
371 | |||
372 | LOAD_4SR(0, 0x40) | ||
373 | LOAD_4SR(4, 0x44) | ||
374 | LOAD_4SR(8, 0x48) | ||
375 | LOAD_4SR(12, 0x4c) | ||
376 | |||
377 | /* rest of regs */ | ||
378 | LOAD_SPRN(SPRG0, 0x50); | ||
379 | LOAD_SPRN(SPRG1, 0x51); | ||
380 | LOAD_SPRN(SPRG2, 0x52); | ||
381 | LOAD_SPRN(SPRG3, 0x53); | ||
382 | LOAD_SPRN(SPRG4, 0x54); | ||
383 | LOAD_SPRN(SPRG5, 0x55); | ||
384 | LOAD_SPRN(SPRG6, 0x56); | ||
385 | LOAD_SPRN(SPRG7, 0x57); | ||
386 | |||
387 | LOAD_SPRN(IABR, 0x58); | ||
388 | LOAD_SPRN(DABR, 0x59); | ||
389 | LOAD_SPRN(TBWL, 0x5a); /* these two have separate R/W regs */ | ||
390 | LOAD_SPRN(TBWU, 0x5b); | ||
391 | |||
392 | blr | ||
393 | |||
394 | |||
395 | |||
396 | /* cache flushing code. copied from arch/ppc/boot/util.S */ | ||
397 | #define NUM_CACHE_LINES (128*8) | ||
398 | |||
399 | /* | ||
400 | * Flush data cache | ||
401 | * Do this by just reading lots of stuff into the cache. | ||
402 | */ | ||
403 | flush_data_cache: | ||
404 | lis r3,CONFIG_KERNEL_START@h | ||
405 | ori r3,r3,CONFIG_KERNEL_START@l | ||
406 | li r4,NUM_CACHE_LINES | ||
407 | mtctr r4 | ||
408 | 1: | ||
409 | lwz r4,0(r3) | ||
410 | addi r3,r3,L1_CACHE_BYTES /* Next line, please */ | ||
411 | bdnz 1b | ||
412 | blr | ||