diff options
Diffstat (limited to 'arch/powerpc/platforms/pasemi')
-rw-r--r-- | arch/powerpc/platforms/pasemi/Kconfig | 10 | ||||
-rw-r--r-- | arch/powerpc/platforms/pasemi/Makefile | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/pasemi/idle.c | 88 | ||||
-rw-r--r-- | arch/powerpc/platforms/pasemi/iommu.c | 281 | ||||
-rw-r--r-- | arch/powerpc/platforms/pasemi/pasemi.h | 12 | ||||
-rw-r--r-- | arch/powerpc/platforms/pasemi/pci.c | 13 | ||||
-rw-r--r-- | arch/powerpc/platforms/pasemi/powersave.S | 80 | ||||
-rw-r--r-- | arch/powerpc/platforms/pasemi/setup.c | 105 |
8 files changed, 572 insertions, 20 deletions
diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig new file mode 100644 index 000000000000..68dc529dfd2f --- /dev/null +++ b/arch/powerpc/platforms/pasemi/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | menu "PA Semi PWRficient options" | ||
2 | depends on PPC_PASEMI | ||
3 | |||
4 | config PPC_PASEMI_IOMMU | ||
5 | bool "PA Semi IOMMU support" | ||
6 | depends on PPC_PASEMI | ||
7 | help | ||
8 | IOMMU support for PA6T-1682M | ||
9 | |||
10 | endmenu | ||
diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile index 1be1a993c5f5..e657ccae90a9 100644 --- a/arch/powerpc/platforms/pasemi/Makefile +++ b/arch/powerpc/platforms/pasemi/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-y += setup.o pci.o time.o | 1 | obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o |
2 | |||
diff --git a/arch/powerpc/platforms/pasemi/idle.c b/arch/powerpc/platforms/pasemi/idle.c new file mode 100644 index 000000000000..1ca3ff381591 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/idle.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2007 PA Semi, Inc | ||
3 | * | ||
4 | * Maintained by: Olof Johansson <olof@lixom.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #undef DEBUG | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/string.h> | ||
25 | |||
26 | #include <asm/machdep.h> | ||
27 | #include <asm/reg.h> | ||
28 | |||
29 | #include "pasemi.h" | ||
30 | |||
31 | struct sleep_mode { | ||
32 | char *name; | ||
33 | void (*entry)(void); | ||
34 | }; | ||
35 | |||
36 | static struct sleep_mode modes[] = { | ||
37 | { .name = "spin", .entry = &idle_spin }, | ||
38 | { .name = "doze", .entry = &idle_doze }, | ||
39 | }; | ||
40 | |||
41 | static int current_mode = 0; | ||
42 | |||
43 | static int pasemi_system_reset_exception(struct pt_regs *regs) | ||
44 | { | ||
45 | /* If we were woken up from power savings, we need to return | ||
46 | * to the calling function, since nip is not saved across | ||
47 | * all modes. | ||
48 | */ | ||
49 | |||
50 | if (regs->msr & SRR1_WAKEMASK) | ||
51 | regs->nip = regs->link; | ||
52 | |||
53 | switch (regs->msr & SRR1_WAKEMASK) { | ||
54 | case SRR1_WAKEEE: | ||
55 | do_IRQ(regs); | ||
56 | break; | ||
57 | case SRR1_WAKEDEC: | ||
58 | timer_interrupt(regs); | ||
59 | break; | ||
60 | default: | ||
61 | /* do system reset */ | ||
62 | return 0; | ||
63 | } | ||
64 | /* everything handled */ | ||
65 | regs->msr |= MSR_RI; | ||
66 | return 1; | ||
67 | } | ||
68 | |||
69 | void __init pasemi_idle_init(void) | ||
70 | { | ||
71 | ppc_md.system_reset_exception = pasemi_system_reset_exception; | ||
72 | ppc_md.power_save = modes[current_mode].entry; | ||
73 | printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name); | ||
74 | } | ||
75 | |||
76 | static int __init idle_param(char *p) | ||
77 | { | ||
78 | int i; | ||
79 | for (i = 0; i < sizeof(modes)/sizeof(struct sleep_mode); i++) { | ||
80 | if (!strcmp(modes[i].name, p)) { | ||
81 | current_mode = i; | ||
82 | break; | ||
83 | } | ||
84 | } | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | early_param("idle", idle_param); | ||
diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c new file mode 100644 index 000000000000..459a53b7d24d --- /dev/null +++ b/arch/powerpc/platforms/pasemi/iommu.c | |||
@@ -0,0 +1,281 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2007, PA Semi, Inc | ||
3 | * | ||
4 | * Maintained by: Olof Johansson <olof@lixom.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #undef DEBUG | ||
21 | |||
22 | #include <linux/types.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/pci.h> | ||
25 | #include <asm/iommu.h> | ||
26 | #include <asm/machdep.h> | ||
27 | #include <asm/abs_addr.h> | ||
28 | |||
29 | |||
30 | #define IOBMAP_PAGE_SHIFT 12 | ||
31 | #define IOBMAP_PAGE_SIZE (1 << IOBMAP_PAGE_SHIFT) | ||
32 | #define IOBMAP_PAGE_MASK (IOBMAP_PAGE_SIZE - 1) | ||
33 | |||
34 | #define IOBMAP_PAGE_FACTOR (PAGE_SHIFT - IOBMAP_PAGE_SHIFT) | ||
35 | |||
36 | #define IOB_BASE 0xe0000000 | ||
37 | #define IOB_SIZE 0x3000 | ||
38 | /* Configuration registers */ | ||
39 | #define IOBCAP_REG 0x10 | ||
40 | #define IOBCOM_REG 0x40 | ||
41 | /* Enable IOB address translation */ | ||
42 | #define IOBCOM_ATEN 0x00000100 | ||
43 | |||
44 | /* Address decode configuration register */ | ||
45 | #define IOB_AD_REG 0x53 | ||
46 | /* IOBCOM_AD_REG fields */ | ||
47 | #define IOB_AD_VGPRT 0x00000e00 | ||
48 | #define IOB_AD_VGAEN 0x00000100 | ||
49 | /* Direct mapping settings */ | ||
50 | #define IOB_AD_MPSEL_MASK 0x00000030 | ||
51 | #define IOB_AD_MPSEL_B38 0x00000000 | ||
52 | #define IOB_AD_MPSEL_B40 0x00000010 | ||
53 | #define IOB_AD_MPSEL_B42 0x00000020 | ||
54 | /* Translation window size / enable */ | ||
55 | #define IOB_AD_TRNG_MASK 0x00000003 | ||
56 | #define IOB_AD_TRNG_256M 0x00000000 | ||
57 | #define IOB_AD_TRNG_2G 0x00000001 | ||
58 | #define IOB_AD_TRNG_128G 0x00000003 | ||
59 | |||
60 | #define IOB_TABLEBASE_REG 0x55 | ||
61 | |||
62 | /* Base of the 64 4-byte L1 registers */ | ||
63 | #define IOB_XLT_L1_REGBASE 0xac0 | ||
64 | |||
65 | /* Register to invalidate TLB entries */ | ||
66 | #define IOB_AT_INVAL_TLB_REG 0xb40 | ||
67 | |||
68 | /* The top two bits of the level 1 entry contains valid and type flags */ | ||
69 | #define IOBMAP_L1E_V 0x40000000 | ||
70 | #define IOBMAP_L1E_V_B 0x80000000 | ||
71 | |||
72 | /* For big page entries, the bottom two bits contains flags */ | ||
73 | #define IOBMAP_L1E_BIG_CACHED 0x00000002 | ||
74 | #define IOBMAP_L1E_BIG_PRIORITY 0x00000001 | ||
75 | |||
76 | /* For regular level 2 entries, top 2 bits contain valid and cache flags */ | ||
77 | #define IOBMAP_L2E_V 0x80000000 | ||
78 | #define IOBMAP_L2E_V_CACHED 0xc0000000 | ||
79 | |||
80 | static u32 *iob; | ||
81 | static u32 iob_l1_emptyval; | ||
82 | static u32 iob_l2_emptyval; | ||
83 | static u32 *iob_l2_base; | ||
84 | |||
85 | static struct iommu_table iommu_table_iobmap; | ||
86 | static int iommu_table_iobmap_inited; | ||
87 | |||
88 | static void iobmap_build(struct iommu_table *tbl, long index, | ||
89 | long npages, unsigned long uaddr, | ||
90 | enum dma_data_direction direction) | ||
91 | { | ||
92 | u32 *ip; | ||
93 | u32 rpn; | ||
94 | unsigned long bus_addr; | ||
95 | |||
96 | pr_debug("iobmap: build at: %lx, %lx, addr: %lx\n", index, npages, uaddr); | ||
97 | |||
98 | bus_addr = (tbl->it_offset + index) << PAGE_SHIFT; | ||
99 | |||
100 | npages <<= IOBMAP_PAGE_FACTOR; | ||
101 | index <<= IOBMAP_PAGE_FACTOR; | ||
102 | |||
103 | ip = ((u32 *)tbl->it_base) + index; | ||
104 | |||
105 | while (npages--) { | ||
106 | rpn = virt_to_abs(uaddr) >> IOBMAP_PAGE_SHIFT; | ||
107 | |||
108 | *(ip++) = IOBMAP_L2E_V | rpn; | ||
109 | /* invalidate tlb, can be optimized more */ | ||
110 | out_le32(iob+IOB_AT_INVAL_TLB_REG, bus_addr >> 14); | ||
111 | |||
112 | uaddr += IOBMAP_PAGE_SIZE; | ||
113 | bus_addr += IOBMAP_PAGE_SIZE; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | |||
118 | static void iobmap_free(struct iommu_table *tbl, long index, | ||
119 | long npages) | ||
120 | { | ||
121 | u32 *ip; | ||
122 | unsigned long bus_addr; | ||
123 | |||
124 | pr_debug("iobmap: free at: %lx, %lx\n", index, npages); | ||
125 | |||
126 | bus_addr = (tbl->it_offset + index) << PAGE_SHIFT; | ||
127 | |||
128 | npages <<= IOBMAP_PAGE_FACTOR; | ||
129 | index <<= IOBMAP_PAGE_FACTOR; | ||
130 | |||
131 | ip = ((u32 *)tbl->it_base) + index; | ||
132 | |||
133 | while (npages--) { | ||
134 | *(ip++) = iob_l2_emptyval; | ||
135 | /* invalidate tlb, can be optimized more */ | ||
136 | out_le32(iob+IOB_AT_INVAL_TLB_REG, bus_addr >> 14); | ||
137 | bus_addr += IOBMAP_PAGE_SIZE; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | |||
142 | static void iommu_table_iobmap_setup(void) | ||
143 | { | ||
144 | pr_debug(" -> %s\n", __func__); | ||
145 | iommu_table_iobmap.it_busno = 0; | ||
146 | iommu_table_iobmap.it_offset = 0; | ||
147 | /* it_size is in number of entries */ | ||
148 | iommu_table_iobmap.it_size = 0x80000000 >> PAGE_SHIFT; | ||
149 | |||
150 | /* Initialize the common IOMMU code */ | ||
151 | iommu_table_iobmap.it_base = (unsigned long)iob_l2_base; | ||
152 | iommu_table_iobmap.it_index = 0; | ||
153 | /* XXXOJN tune this to avoid IOB cache invals. | ||
154 | * Should probably be 8 (64 bytes) | ||
155 | */ | ||
156 | iommu_table_iobmap.it_blocksize = 4; | ||
157 | iommu_init_table(&iommu_table_iobmap, 0); | ||
158 | pr_debug(" <- %s\n", __func__); | ||
159 | } | ||
160 | |||
161 | |||
162 | |||
163 | static void pci_dma_bus_setup_pasemi(struct pci_bus *bus) | ||
164 | { | ||
165 | struct device_node *dn; | ||
166 | |||
167 | pr_debug("pci_dma_bus_setup, bus %p, bus->self %p\n", bus, bus->self); | ||
168 | |||
169 | if (!iommu_table_iobmap_inited) { | ||
170 | iommu_table_iobmap_inited = 1; | ||
171 | iommu_table_iobmap_setup(); | ||
172 | } | ||
173 | |||
174 | dn = pci_bus_to_OF_node(bus); | ||
175 | |||
176 | if (dn) | ||
177 | PCI_DN(dn)->iommu_table = &iommu_table_iobmap; | ||
178 | |||
179 | } | ||
180 | |||
181 | |||
182 | static void pci_dma_dev_setup_pasemi(struct pci_dev *dev) | ||
183 | { | ||
184 | pr_debug("pci_dma_dev_setup, dev %p (%s)\n", dev, pci_name(dev)); | ||
185 | |||
186 | /* DMA device is untranslated, but all other PCI-e goes through | ||
187 | * the IOMMU | ||
188 | */ | ||
189 | if (dev->vendor == 0x1959 && dev->device == 0xa007) | ||
190 | dev->dev.archdata.dma_ops = &dma_direct_ops; | ||
191 | else | ||
192 | dev->dev.archdata.dma_data = &iommu_table_iobmap; | ||
193 | } | ||
194 | |||
195 | static void pci_dma_bus_setup_null(struct pci_bus *b) { } | ||
196 | static void pci_dma_dev_setup_null(struct pci_dev *d) { } | ||
197 | |||
198 | int iob_init(struct device_node *dn) | ||
199 | { | ||
200 | unsigned long tmp; | ||
201 | u32 regword; | ||
202 | int i; | ||
203 | |||
204 | pr_debug(" -> %s\n", __func__); | ||
205 | |||
206 | /* Allocate a spare page to map all invalid IOTLB pages. */ | ||
207 | tmp = lmb_alloc(IOBMAP_PAGE_SIZE, IOBMAP_PAGE_SIZE); | ||
208 | if (!tmp) | ||
209 | panic("IOBMAP: Cannot allocate spare page!"); | ||
210 | /* Empty l1 is marked invalid */ | ||
211 | iob_l1_emptyval = 0; | ||
212 | /* Empty l2 is mapped to dummy page */ | ||
213 | iob_l2_emptyval = IOBMAP_L2E_V | (tmp >> IOBMAP_PAGE_SHIFT); | ||
214 | |||
215 | iob = ioremap(IOB_BASE, IOB_SIZE); | ||
216 | if (!iob) | ||
217 | panic("IOBMAP: Cannot map registers!"); | ||
218 | |||
219 | /* setup direct mapping of the L1 entries */ | ||
220 | for (i = 0; i < 64; i++) { | ||
221 | /* Each L1 covers 32MB, i.e. 8K entries = 32K of ram */ | ||
222 | regword = IOBMAP_L1E_V | (__pa(iob_l2_base + i*0x2000) >> 12); | ||
223 | out_le32(iob+IOB_XLT_L1_REGBASE+i, regword); | ||
224 | } | ||
225 | |||
226 | /* set 2GB translation window, based at 0 */ | ||
227 | regword = in_le32(iob+IOB_AD_REG); | ||
228 | regword &= ~IOB_AD_TRNG_MASK; | ||
229 | regword |= IOB_AD_TRNG_2G; | ||
230 | out_le32(iob+IOB_AD_REG, regword); | ||
231 | |||
232 | /* Enable translation */ | ||
233 | regword = in_le32(iob+IOBCOM_REG); | ||
234 | regword |= IOBCOM_ATEN; | ||
235 | out_le32(iob+IOBCOM_REG, regword); | ||
236 | |||
237 | pr_debug(" <- %s\n", __func__); | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | |||
243 | /* These are called very early. */ | ||
244 | void iommu_init_early_pasemi(void) | ||
245 | { | ||
246 | int iommu_off; | ||
247 | |||
248 | #ifndef CONFIG_PPC_PASEMI_IOMMU | ||
249 | iommu_off = 1; | ||
250 | #else | ||
251 | iommu_off = of_chosen && | ||
252 | get_property(of_chosen, "linux,iommu-off", NULL); | ||
253 | #endif | ||
254 | if (iommu_off) { | ||
255 | /* Direct I/O, IOMMU off */ | ||
256 | ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_null; | ||
257 | ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_null; | ||
258 | pci_dma_ops = &dma_direct_ops; | ||
259 | |||
260 | return; | ||
261 | } | ||
262 | |||
263 | iob_init(NULL); | ||
264 | |||
265 | ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pasemi; | ||
266 | ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pasemi; | ||
267 | ppc_md.tce_build = iobmap_build; | ||
268 | ppc_md.tce_free = iobmap_free; | ||
269 | pci_dma_ops = &dma_iommu_ops; | ||
270 | } | ||
271 | |||
272 | void __init alloc_iobmap_l2(void) | ||
273 | { | ||
274 | #ifndef CONFIG_PPC_PASEMI_IOMMU | ||
275 | return; | ||
276 | #endif | ||
277 | /* For 2G space, 8x64 pages (2^21 bytes) is max total l2 size */ | ||
278 | iob_l2_base = (u32 *)abs_to_virt(lmb_alloc_base(1UL<<21, 1UL<<21, 0x80000000)); | ||
279 | |||
280 | printk(KERN_INFO "IOBMAP L2 allocated at: %p\n", iob_l2_base); | ||
281 | } | ||
diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h index 51c2a2397ecf..2d3927e6edb0 100644 --- a/arch/powerpc/platforms/pasemi/pasemi.h +++ b/arch/powerpc/platforms/pasemi/pasemi.h | |||
@@ -3,5 +3,17 @@ | |||
3 | 3 | ||
4 | extern unsigned long pas_get_boot_time(void); | 4 | extern unsigned long pas_get_boot_time(void); |
5 | extern void pas_pci_init(void); | 5 | extern void pas_pci_init(void); |
6 | extern void __devinit pas_pci_irq_fixup(struct pci_dev *dev); | ||
7 | extern void __devinit pas_pci_dma_dev_setup(struct pci_dev *dev); | ||
8 | |||
9 | extern void __init alloc_iobmap_l2(void); | ||
10 | |||
11 | extern void __init pasemi_idle_init(void); | ||
12 | |||
13 | /* Power savings modes, implemented in asm */ | ||
14 | extern void idle_spin(void); | ||
15 | extern void idle_doze(void); | ||
16 | |||
17 | |||
6 | 18 | ||
7 | #endif /* _PASEMI_PASEMI_H */ | 19 | #endif /* _PASEMI_PASEMI_H */ |
diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index faa618e04047..7ecb2ba24db9 100644 --- a/arch/powerpc/platforms/pasemi/pci.c +++ b/arch/powerpc/platforms/pasemi/pci.c | |||
@@ -163,6 +163,19 @@ static void __init pas_fixup_phb_resources(void) | |||
163 | } | 163 | } |
164 | 164 | ||
165 | 165 | ||
166 | void __devinit pas_pci_irq_fixup(struct pci_dev *dev) | ||
167 | { | ||
168 | /* DMA is special, 84 interrupts (128 -> 211), all but 128 | ||
169 | * need to be mapped by hand here. | ||
170 | */ | ||
171 | if (dev->vendor == 0x1959 && dev->device == 0xa007) { | ||
172 | int i; | ||
173 | for (i = 129; i < 212; i++) | ||
174 | irq_create_mapping(NULL, i); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | |||
166 | void __init pas_pci_init(void) | 179 | void __init pas_pci_init(void) |
167 | { | 180 | { |
168 | struct device_node *np, *root; | 181 | struct device_node *np, *root; |
diff --git a/arch/powerpc/platforms/pasemi/powersave.S b/arch/powerpc/platforms/pasemi/powersave.S new file mode 100644 index 000000000000..6d0fba6aab17 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/powersave.S | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2007 PA Semi, Inc | ||
3 | * | ||
4 | * Maintained by: Olof Johansson <olof@lixom.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <asm/processor.h> | ||
22 | #include <asm/page.h> | ||
23 | #include <asm/ppc_asm.h> | ||
24 | #include <asm/cputable.h> | ||
25 | #include <asm/cache.h> | ||
26 | #include <asm/thread_info.h> | ||
27 | #include <asm/asm-offsets.h> | ||
28 | |||
29 | /* Power savings opcodes since not all binutils have them at this time */ | ||
30 | #define DOZE .long 0x4c000324 | ||
31 | #define NAP .long 0x4c000364 | ||
32 | #define SLEEP .long 0x4c0003a4 | ||
33 | #define RVW .long 0x4c0003e4 | ||
34 | |||
35 | /* Common sequence to do before going to any of the | ||
36 | * powersavings modes. | ||
37 | */ | ||
38 | |||
39 | #define PRE_SLEEP_SEQUENCE \ | ||
40 | std r3,8(r1); \ | ||
41 | ptesync ; \ | ||
42 | ld r3,8(r1); \ | ||
43 | 1: cmpd r3,r3; \ | ||
44 | bne 1b | ||
45 | |||
46 | _doze: | ||
47 | PRE_SLEEP_SEQUENCE | ||
48 | DOZE | ||
49 | b . | ||
50 | |||
51 | |||
52 | _GLOBAL(idle_spin) | ||
53 | blr | ||
54 | |||
55 | _GLOBAL(idle_doze) | ||
56 | LOAD_REG_ADDR(r3, _doze) | ||
57 | b sleep_common | ||
58 | |||
59 | /* Add more modes here later */ | ||
60 | |||
61 | sleep_common: | ||
62 | mflr r0 | ||
63 | std r0, 16(r1) | ||
64 | stdu r1,-64(r1) | ||
65 | |||
66 | LOAD_REG_IMMEDIATE(r6,MSR_DR|MSR_IR|MSR_ME|MSR_EE) | ||
67 | mfmsr r4 | ||
68 | andc r5,r4,r6 | ||
69 | mtmsrd r5,0 | ||
70 | |||
71 | mtctr r3 | ||
72 | bctrl | ||
73 | |||
74 | mtmsrd r4,0 | ||
75 | |||
76 | addi r1,r1,64 | ||
77 | ld r0,16(r1) | ||
78 | mtlr r0 | ||
79 | blr | ||
80 | |||
diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index bea7d1bb1a3b..449cf1a08291 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2006 PA Semi, Inc | 2 | * Copyright (C) 2006-2007 PA Semi, Inc |
3 | * | 3 | * |
4 | * Authors: Kip Walker, PA Semi | 4 | * Authors: Kip Walker, PA Semi |
5 | * Olof Johansson, PA Semi | 5 | * Olof Johansson, PA Semi |
@@ -38,31 +38,46 @@ | |||
38 | 38 | ||
39 | #include "pasemi.h" | 39 | #include "pasemi.h" |
40 | 40 | ||
41 | static void __iomem *reset_reg; | ||
42 | |||
41 | static void pas_restart(char *cmd) | 43 | static void pas_restart(char *cmd) |
42 | { | 44 | { |
43 | printk("restart unimplemented, looping...\n"); | 45 | printk("Restarting...\n"); |
44 | for (;;) ; | 46 | while (1) |
47 | out_le32(reset_reg, 0x6000000); | ||
45 | } | 48 | } |
46 | 49 | ||
47 | static void pas_power_off(void) | 50 | #ifdef CONFIG_SMP |
51 | static DEFINE_SPINLOCK(timebase_lock); | ||
52 | |||
53 | static void __devinit pas_give_timebase(void) | ||
48 | { | 54 | { |
49 | printk("power off unimplemented, looping...\n"); | 55 | unsigned long tb; |
50 | for (;;) ; | 56 | |
57 | spin_lock(&timebase_lock); | ||
58 | mtspr(SPRN_TBCTL, TBCTL_FREEZE); | ||
59 | tb = mftb(); | ||
60 | mtspr(SPRN_TBCTL, TBCTL_UPDATE_LOWER | (tb & 0xffffffff)); | ||
61 | mtspr(SPRN_TBCTL, TBCTL_UPDATE_UPPER | (tb >> 32)); | ||
62 | mtspr(SPRN_TBCTL, TBCTL_RESTART); | ||
63 | spin_unlock(&timebase_lock); | ||
64 | pr_debug("pas_give_timebase: cpu %d gave tb %lx\n", | ||
65 | smp_processor_id(), tb); | ||
51 | } | 66 | } |
52 | 67 | ||
53 | static void pas_halt(void) | 68 | static void __devinit pas_take_timebase(void) |
54 | { | 69 | { |
55 | pas_power_off(); | 70 | pr_debug("pas_take_timebase: cpu %d has tb %lx\n", |
71 | smp_processor_id(), mftb()); | ||
56 | } | 72 | } |
57 | 73 | ||
58 | #ifdef CONFIG_SMP | ||
59 | struct smp_ops_t pas_smp_ops = { | 74 | struct smp_ops_t pas_smp_ops = { |
60 | .probe = smp_mpic_probe, | 75 | .probe = smp_mpic_probe, |
61 | .message_pass = smp_mpic_message_pass, | 76 | .message_pass = smp_mpic_message_pass, |
62 | .kick_cpu = smp_generic_kick_cpu, | 77 | .kick_cpu = smp_generic_kick_cpu, |
63 | .setup_cpu = smp_mpic_setup_cpu, | 78 | .setup_cpu = smp_mpic_setup_cpu, |
64 | .give_timebase = smp_generic_give_timebase, | 79 | .give_timebase = pas_give_timebase, |
65 | .take_timebase = smp_generic_take_timebase, | 80 | .take_timebase = pas_take_timebase, |
66 | }; | 81 | }; |
67 | #endif /* CONFIG_SMP */ | 82 | #endif /* CONFIG_SMP */ |
68 | 83 | ||
@@ -72,9 +87,6 @@ void __init pas_setup_arch(void) | |||
72 | /* Setup SMP callback */ | 87 | /* Setup SMP callback */ |
73 | smp_ops = &pas_smp_ops; | 88 | smp_ops = &pas_smp_ops; |
74 | #endif | 89 | #endif |
75 | /* no iommu yet */ | ||
76 | pci_dma_ops = &dma_direct_ops; | ||
77 | |||
78 | /* Lookup PCI hosts */ | 90 | /* Lookup PCI hosts */ |
79 | pas_pci_init(); | 91 | pas_pci_init(); |
80 | 92 | ||
@@ -82,7 +94,11 @@ void __init pas_setup_arch(void) | |||
82 | conswitchp = &dummy_con; | 94 | conswitchp = &dummy_con; |
83 | #endif | 95 | #endif |
84 | 96 | ||
85 | printk(KERN_DEBUG "Using default idle loop\n"); | 97 | /* Remap SDC register for doing reset */ |
98 | /* XXXOJN This should maybe come out of the device tree */ | ||
99 | reset_reg = ioremap(0xfc101100, 4); | ||
100 | |||
101 | pasemi_idle_init(); | ||
86 | } | 102 | } |
87 | 103 | ||
88 | /* No legacy IO on our parts */ | 104 | /* No legacy IO on our parts */ |
@@ -130,8 +146,9 @@ static __init void pas_init_IRQ(void) | |||
130 | openpic_addr = of_read_number(opprop, naddr); | 146 | openpic_addr = of_read_number(opprop, naddr); |
131 | printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); | 147 | printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); |
132 | 148 | ||
133 | mpic = mpic_alloc(mpic_node, openpic_addr, MPIC_PRIMARY, 0, 0, | 149 | mpic = mpic_alloc(mpic_node, openpic_addr, |
134 | " PAS-OPIC "); | 150 | MPIC_PRIMARY|MPIC_LARGE_VECTORS, |
151 | 0, 0, " PAS-OPIC "); | ||
135 | BUG_ON(!mpic); | 152 | BUG_ON(!mpic); |
136 | 153 | ||
137 | mpic_assign_isu(mpic, 0, openpic_addr + 0x10000); | 154 | mpic_assign_isu(mpic, 0, openpic_addr + 0x10000); |
@@ -146,6 +163,53 @@ static void __init pas_progress(char *s, unsigned short hex) | |||
146 | } | 163 | } |
147 | 164 | ||
148 | 165 | ||
166 | static int pas_machine_check_handler(struct pt_regs *regs) | ||
167 | { | ||
168 | int cpu = smp_processor_id(); | ||
169 | unsigned long srr0, srr1, dsisr; | ||
170 | |||
171 | srr0 = regs->nip; | ||
172 | srr1 = regs->msr; | ||
173 | dsisr = mfspr(SPRN_DSISR); | ||
174 | printk(KERN_ERR "Machine Check on CPU %d\n", cpu); | ||
175 | printk(KERN_ERR "SRR0 0x%016lx SRR1 0x%016lx\n", srr0, srr1); | ||
176 | printk(KERN_ERR "DSISR 0x%016lx DAR 0x%016lx\n", dsisr, regs->dar); | ||
177 | printk(KERN_ERR "Cause:\n"); | ||
178 | |||
179 | if (srr1 & 0x200000) | ||
180 | printk(KERN_ERR "Signalled by SDC\n"); | ||
181 | if (srr1 & 0x100000) { | ||
182 | printk(KERN_ERR "Load/Store detected error:\n"); | ||
183 | if (dsisr & 0x8000) | ||
184 | printk(KERN_ERR "D-cache ECC double-bit error or bus error\n"); | ||
185 | if (dsisr & 0x4000) | ||
186 | printk(KERN_ERR "LSU snoop response error\n"); | ||
187 | if (dsisr & 0x2000) | ||
188 | printk(KERN_ERR "MMU SLB multi-hit or invalid B field\n"); | ||
189 | if (dsisr & 0x1000) | ||
190 | printk(KERN_ERR "Recoverable Duptags\n"); | ||
191 | if (dsisr & 0x800) | ||
192 | printk(KERN_ERR "Recoverable D-cache parity error count overflow\n"); | ||
193 | if (dsisr & 0x400) | ||
194 | printk(KERN_ERR "TLB parity error count overflow\n"); | ||
195 | } | ||
196 | if (srr1 & 0x80000) | ||
197 | printk(KERN_ERR "Bus Error\n"); | ||
198 | if (srr1 & 0x40000) | ||
199 | printk(KERN_ERR "I-side SLB multiple hit\n"); | ||
200 | if (srr1 & 0x20000) | ||
201 | printk(KERN_ERR "I-cache parity error hit\n"); | ||
202 | |||
203 | /* SRR1[62] is from MSR[62] if recoverable, so pass that back */ | ||
204 | return !!(srr1 & 0x2); | ||
205 | } | ||
206 | |||
207 | static void __init pas_init_early(void) | ||
208 | { | ||
209 | iommu_init_early_pasemi(); | ||
210 | } | ||
211 | |||
212 | |||
149 | /* | 213 | /* |
150 | * Called very early, MMU is off, device-tree isn't unflattened | 214 | * Called very early, MMU is off, device-tree isn't unflattened |
151 | */ | 215 | */ |
@@ -158,6 +222,8 @@ static int __init pas_probe(void) | |||
158 | 222 | ||
159 | hpte_init_native(); | 223 | hpte_init_native(); |
160 | 224 | ||
225 | alloc_iobmap_l2(); | ||
226 | |||
161 | return 1; | 227 | return 1; |
162 | } | 228 | } |
163 | 229 | ||
@@ -165,13 +231,14 @@ define_machine(pas) { | |||
165 | .name = "PA Semi PA6T-1682M", | 231 | .name = "PA Semi PA6T-1682M", |
166 | .probe = pas_probe, | 232 | .probe = pas_probe, |
167 | .setup_arch = pas_setup_arch, | 233 | .setup_arch = pas_setup_arch, |
234 | .init_early = pas_init_early, | ||
168 | .init_IRQ = pas_init_IRQ, | 235 | .init_IRQ = pas_init_IRQ, |
169 | .get_irq = mpic_get_irq, | 236 | .get_irq = mpic_get_irq, |
170 | .restart = pas_restart, | 237 | .restart = pas_restart, |
171 | .power_off = pas_power_off, | ||
172 | .halt = pas_halt, | ||
173 | .get_boot_time = pas_get_boot_time, | 238 | .get_boot_time = pas_get_boot_time, |
174 | .calibrate_decr = generic_calibrate_decr, | 239 | .calibrate_decr = generic_calibrate_decr, |
175 | .check_legacy_ioport = pas_check_legacy_ioport, | 240 | .check_legacy_ioport = pas_check_legacy_ioport, |
176 | .progress = pas_progress, | 241 | .progress = pas_progress, |
242 | .machine_check_exception = pas_machine_check_handler, | ||
243 | .pci_irq_fixup = pas_pci_irq_fixup, | ||
177 | }; | 244 | }; |