diff options
Diffstat (limited to 'arch/powerpc/platforms/embedded6xx/holly.c')
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/holly.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c new file mode 100644 index 000000000000..3a0b4a01401c --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/holly.c | |||
@@ -0,0 +1,317 @@ | |||
1 | /* | ||
2 | * Board setup routines for the IBM 750GX/CL platform w/ TSI10x bridge | ||
3 | * | ||
4 | * Copyright 2007 IBM Corporation | ||
5 | * | ||
6 | * Stephen Winiecki <stevewin@us.ibm.com> | ||
7 | * Josh Boyer <jwboyer@linux.vnet.ibm.com> | ||
8 | * | ||
9 | * Based on code from mpc7448_hpc2.c | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * version 2 as published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/stddef.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/kdev_t.h> | ||
20 | #include <linux/console.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/irq.h> | ||
23 | #include <linux/ide.h> | ||
24 | #include <linux/seq_file.h> | ||
25 | #include <linux/root_dev.h> | ||
26 | #include <linux/serial.h> | ||
27 | #include <linux/tty.h> | ||
28 | #include <linux/serial_core.h> | ||
29 | |||
30 | #include <asm/system.h> | ||
31 | #include <asm/time.h> | ||
32 | #include <asm/machdep.h> | ||
33 | #include <asm/prom.h> | ||
34 | #include <asm/udbg.h> | ||
35 | #include <asm/tsi108.h> | ||
36 | #include <asm/pci-bridge.h> | ||
37 | #include <asm/reg.h> | ||
38 | #include <mm/mmu_decl.h> | ||
39 | #include <asm/tsi108_irq.h> | ||
40 | #include <asm/tsi108_pci.h> | ||
41 | #include <asm/mpic.h> | ||
42 | #include <asm/of_platform.h> | ||
43 | |||
44 | #undef DEBUG | ||
45 | |||
46 | #define HOLLY_PCI_CFG_PHYS 0x7c000000 | ||
47 | |||
48 | int holly_exclude_device(u_char bus, u_char devfn) | ||
49 | { | ||
50 | if (bus == 0 && PCI_SLOT(devfn) == 0) | ||
51 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
52 | else | ||
53 | return PCIBIOS_SUCCESSFUL; | ||
54 | } | ||
55 | |||
56 | static void holly_remap_bridge(void) | ||
57 | { | ||
58 | u32 lut_val, lut_addr; | ||
59 | int i; | ||
60 | |||
61 | printk(KERN_INFO "Remapping PCI bridge\n"); | ||
62 | |||
63 | /* Re-init the PCI bridge and LUT registers to have mappings that don't | ||
64 | * rely on PIBS | ||
65 | */ | ||
66 | lut_addr = 0x900; | ||
67 | for (i = 0; i < 31; i++) { | ||
68 | tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000201); | ||
69 | lut_addr += 4; | ||
70 | tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0); | ||
71 | lut_addr += 4; | ||
72 | } | ||
73 | |||
74 | /* Reserve the last LUT entry for PCI I/O space */ | ||
75 | tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000241); | ||
76 | lut_addr += 4; | ||
77 | tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0); | ||
78 | |||
79 | /* Map PCI I/O space */ | ||
80 | tsi108_write_reg(TSI108_PCI_PFAB_IO_UPPER, 0x0); | ||
81 | tsi108_write_reg(TSI108_PCI_PFAB_IO, 0x1); | ||
82 | |||
83 | /* Map PCI CFG space */ | ||
84 | tsi108_write_reg(TSI108_PCI_PFAB_BAR0_UPPER, 0x0); | ||
85 | tsi108_write_reg(TSI108_PCI_PFAB_BAR0, 0x7c000000 | 0x01); | ||
86 | |||
87 | /* We don't need MEM32 and PRM remapping so disable them */ | ||
88 | tsi108_write_reg(TSI108_PCI_PFAB_MEM32, 0x0); | ||
89 | tsi108_write_reg(TSI108_PCI_PFAB_PFM3, 0x0); | ||
90 | tsi108_write_reg(TSI108_PCI_PFAB_PFM4, 0x0); | ||
91 | |||
92 | /* Set P2O_BAR0 */ | ||
93 | tsi108_write_reg(TSI108_PCI_P2O_BAR0_UPPER, 0x0); | ||
94 | tsi108_write_reg(TSI108_PCI_P2O_BAR0, 0xc0000000); | ||
95 | |||
96 | /* Init the PCI LUTs to do no remapping */ | ||
97 | lut_addr = 0x500; | ||
98 | lut_val = 0x00000002; | ||
99 | |||
100 | for (i = 0; i < 32; i++) { | ||
101 | tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, lut_val); | ||
102 | lut_addr += 4; | ||
103 | tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, 0x40000000); | ||
104 | lut_addr += 4; | ||
105 | lut_val += 0x02000000; | ||
106 | } | ||
107 | tsi108_write_reg(TSI108_PCI_P2O_PAGE_SIZES, 0x00007900); | ||
108 | |||
109 | /* Set 64-bit PCI bus address for system memory */ | ||
110 | tsi108_write_reg(TSI108_PCI_P2O_BAR2_UPPER, 0x0); | ||
111 | tsi108_write_reg(TSI108_PCI_P2O_BAR2, 0x0); | ||
112 | } | ||
113 | |||
114 | static void __init holly_setup_arch(void) | ||
115 | { | ||
116 | struct device_node *cpu; | ||
117 | struct device_node *np; | ||
118 | |||
119 | if (ppc_md.progress) | ||
120 | ppc_md.progress("holly_setup_arch():set_bridge", 0); | ||
121 | |||
122 | cpu = of_find_node_by_type(NULL, "cpu"); | ||
123 | if (cpu) { | ||
124 | const unsigned int *fp; | ||
125 | |||
126 | fp = of_get_property(cpu, "clock-frequency", NULL); | ||
127 | if (fp) | ||
128 | loops_per_jiffy = *fp / HZ; | ||
129 | else | ||
130 | loops_per_jiffy = 50000000 / HZ; | ||
131 | of_node_put(cpu); | ||
132 | } | ||
133 | tsi108_csr_vir_base = get_vir_csrbase(); | ||
134 | |||
135 | /* setup PCI host bridge */ | ||
136 | holly_remap_bridge(); | ||
137 | |||
138 | np = of_find_node_by_type(NULL, "pci"); | ||
139 | if (np) | ||
140 | tsi108_setup_pci(np, HOLLY_PCI_CFG_PHYS, 1); | ||
141 | |||
142 | ppc_md.pci_exclude_device = holly_exclude_device; | ||
143 | if (ppc_md.progress) | ||
144 | ppc_md.progress("tsi108: resources set", 0x100); | ||
145 | |||
146 | printk(KERN_INFO "PPC750GX/CL Platform\n"); | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Interrupt setup and service. Interrrupts on the holly come | ||
151 | * from the four external INT pins, PCI interrupts are routed via | ||
152 | * PCI interrupt control registers, it generates internal IRQ23 | ||
153 | * | ||
154 | * Interrupt routing on the Holly Board: | ||
155 | * TSI108:PB_INT[0] -> CPU0:INT# | ||
156 | * TSI108:PB_INT[1] -> CPU0:MCP# | ||
157 | * TSI108:PB_INT[2] -> N/C | ||
158 | * TSI108:PB_INT[3] -> N/C | ||
159 | */ | ||
160 | static void __init holly_init_IRQ(void) | ||
161 | { | ||
162 | struct mpic *mpic; | ||
163 | phys_addr_t mpic_paddr = 0; | ||
164 | struct device_node *tsi_pic; | ||
165 | #ifdef CONFIG_PCI | ||
166 | unsigned int cascade_pci_irq; | ||
167 | struct device_node *tsi_pci; | ||
168 | struct device_node *cascade_node = NULL; | ||
169 | #endif | ||
170 | |||
171 | tsi_pic = of_find_node_by_type(NULL, "open-pic"); | ||
172 | if (tsi_pic) { | ||
173 | unsigned int size; | ||
174 | const void *prop = of_get_property(tsi_pic, "reg", &size); | ||
175 | mpic_paddr = of_translate_address(tsi_pic, prop); | ||
176 | } | ||
177 | |||
178 | if (mpic_paddr == 0) { | ||
179 | printk(KERN_ERR "%s: No tsi108 PIC found !\n", __func__); | ||
180 | return; | ||
181 | } | ||
182 | |||
183 | pr_debug("%s: tsi108 pic phys_addr = 0x%x\n", __func__, (u32) mpic_paddr); | ||
184 | |||
185 | mpic = mpic_alloc(tsi_pic, mpic_paddr, | ||
186 | MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_WANTS_RESET | | ||
187 | MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108, | ||
188 | 24, | ||
189 | NR_IRQS-4, /* num_sources used */ | ||
190 | "Tsi108_PIC"); | ||
191 | |||
192 | BUG_ON(mpic == NULL); | ||
193 | |||
194 | mpic_assign_isu(mpic, 0, mpic_paddr + 0x100); | ||
195 | |||
196 | mpic_init(mpic); | ||
197 | |||
198 | #ifdef CONFIG_PCI | ||
199 | tsi_pci = of_find_node_by_type(NULL, "pci"); | ||
200 | if (tsi_pci == NULL) { | ||
201 | printk(KERN_ERR "%s: No tsi108 pci node found !\n", __func__); | ||
202 | return; | ||
203 | } | ||
204 | |||
205 | cascade_node = of_find_node_by_type(NULL, "pic-router"); | ||
206 | if (cascade_node == NULL) { | ||
207 | printk(KERN_ERR "%s: No tsi108 pci cascade node found !\n", __func__); | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | cascade_pci_irq = irq_of_parse_and_map(tsi_pci, 0); | ||
212 | pr_debug("%s: tsi108 cascade_pci_irq = 0x%x\n", __func__, (u32) cascade_pci_irq); | ||
213 | tsi108_pci_int_init(cascade_node); | ||
214 | set_irq_data(cascade_pci_irq, mpic); | ||
215 | set_irq_chained_handler(cascade_pci_irq, tsi108_irq_cascade); | ||
216 | #endif | ||
217 | /* Configure MPIC outputs to CPU0 */ | ||
218 | tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0); | ||
219 | of_node_put(tsi_pic); | ||
220 | } | ||
221 | |||
222 | void holly_show_cpuinfo(struct seq_file *m) | ||
223 | { | ||
224 | seq_printf(m, "vendor\t\t: IBM\n"); | ||
225 | seq_printf(m, "machine\t\t: PPC750 GX/CL\n"); | ||
226 | } | ||
227 | |||
228 | void holly_restart(char *cmd) | ||
229 | { | ||
230 | __be32 __iomem *ocn_bar1 = NULL; | ||
231 | unsigned long bar; | ||
232 | struct device_node *bridge = NULL; | ||
233 | const void *prop; | ||
234 | int size; | ||
235 | phys_addr_t addr = 0xc0000000; | ||
236 | |||
237 | local_irq_disable(); | ||
238 | |||
239 | bridge = of_find_node_by_type(NULL, "tsi-bridge"); | ||
240 | if (bridge) { | ||
241 | prop = of_get_property(bridge, "reg", &size); | ||
242 | addr = of_translate_address(bridge, prop); | ||
243 | } | ||
244 | addr += (TSI108_PB_OFFSET + 0x414); | ||
245 | |||
246 | ocn_bar1 = ioremap(addr, 0x4); | ||
247 | |||
248 | /* Turn on the BOOT bit so the addresses are correctly | ||
249 | * routed to the HLP interface */ | ||
250 | bar = ioread32be(ocn_bar1); | ||
251 | bar |= 2; | ||
252 | iowrite32be(bar, ocn_bar1); | ||
253 | iosync(); | ||
254 | |||
255 | /* Set SRR0 to the reset vector and turn on MSR_IP */ | ||
256 | mtspr(SPRN_SRR0, 0xfff00100); | ||
257 | mtspr(SPRN_SRR1, MSR_IP); | ||
258 | |||
259 | /* Do an rfi to jump back to firmware. Somewhat evil, | ||
260 | * but it works | ||
261 | */ | ||
262 | __asm__ __volatile__("rfi" : : : "memory"); | ||
263 | |||
264 | /* Spin until reset happens. Shouldn't really get here */ | ||
265 | for (;;) ; | ||
266 | } | ||
267 | |||
268 | void holly_power_off(void) | ||
269 | { | ||
270 | local_irq_disable(); | ||
271 | /* No way to shut power off with software */ | ||
272 | for (;;) ; | ||
273 | } | ||
274 | |||
275 | void holly_halt(void) | ||
276 | { | ||
277 | holly_power_off(); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Called very early, device-tree isn't unflattened | ||
282 | */ | ||
283 | static int __init holly_probe(void) | ||
284 | { | ||
285 | unsigned long root = of_get_flat_dt_root(); | ||
286 | |||
287 | if (!of_flat_dt_is_compatible(root, "ibm,holly")) | ||
288 | return 0; | ||
289 | return 1; | ||
290 | } | ||
291 | |||
292 | static int ppc750_machine_check_exception(struct pt_regs *regs) | ||
293 | { | ||
294 | const struct exception_table_entry *entry; | ||
295 | |||
296 | /* Are we prepared to handle this fault */ | ||
297 | if ((entry = search_exception_tables(regs->nip)) != NULL) { | ||
298 | tsi108_clear_pci_cfg_error(); | ||
299 | regs->msr |= MSR_RI; | ||
300 | regs->nip = entry->fixup; | ||
301 | return 1; | ||
302 | } | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | define_machine(holly){ | ||
307 | .name = "PPC750 GX/CL TSI", | ||
308 | .probe = holly_probe, | ||
309 | .setup_arch = holly_setup_arch, | ||
310 | .init_IRQ = holly_init_IRQ, | ||
311 | .show_cpuinfo = holly_show_cpuinfo, | ||
312 | .get_irq = mpic_get_irq, | ||
313 | .restart = holly_restart, | ||
314 | .calibrate_decr = generic_calibrate_decr, | ||
315 | .machine_check_exception = ppc750_machine_check_exception, | ||
316 | .progress = udbg_progress, | ||
317 | }; | ||