diff options
Diffstat (limited to 'arch/sparc64/kernel/central.c')
-rw-r--r-- | arch/sparc64/kernel/central.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c new file mode 100644 index 000000000000..3d184a784968 --- /dev/null +++ b/arch/sparc64/kernel/central.c | |||
@@ -0,0 +1,457 @@ | |||
1 | /* $Id: central.c,v 1.15 2001/12/19 00:29:51 davem Exp $ | ||
2 | * central.c: Central FHC driver for Sunfire/Starfire/Wildfire. | ||
3 | * | ||
4 | * Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com) | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/string.h> | ||
10 | #include <linux/timer.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/bootmem.h> | ||
15 | |||
16 | #include <asm/page.h> | ||
17 | #include <asm/fhc.h> | ||
18 | #include <asm/starfire.h> | ||
19 | |||
20 | struct linux_central *central_bus = NULL; | ||
21 | struct linux_fhc *fhc_list = NULL; | ||
22 | |||
23 | #define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child) | ||
24 | |||
25 | static void central_probe_failure(int line) | ||
26 | { | ||
27 | prom_printf("CENTRAL: Critical device probe failure at central.c:%d\n", | ||
28 | line); | ||
29 | prom_halt(); | ||
30 | } | ||
31 | |||
32 | static void central_ranges_init(int cnode, struct linux_central *central) | ||
33 | { | ||
34 | int success; | ||
35 | |||
36 | central->num_central_ranges = 0; | ||
37 | success = prom_getproperty(central->prom_node, "ranges", | ||
38 | (char *) central->central_ranges, | ||
39 | sizeof (central->central_ranges)); | ||
40 | if (success != -1) | ||
41 | central->num_central_ranges = (success/sizeof(struct linux_prom_ranges)); | ||
42 | } | ||
43 | |||
44 | static void fhc_ranges_init(int fnode, struct linux_fhc *fhc) | ||
45 | { | ||
46 | int success; | ||
47 | |||
48 | fhc->num_fhc_ranges = 0; | ||
49 | success = prom_getproperty(fhc->prom_node, "ranges", | ||
50 | (char *) fhc->fhc_ranges, | ||
51 | sizeof (fhc->fhc_ranges)); | ||
52 | if (success != -1) | ||
53 | fhc->num_fhc_ranges = (success/sizeof(struct linux_prom_ranges)); | ||
54 | } | ||
55 | |||
56 | /* Range application routines are exported to various drivers, | ||
57 | * so do not __init this. | ||
58 | */ | ||
59 | static void adjust_regs(struct linux_prom_registers *regp, int nregs, | ||
60 | struct linux_prom_ranges *rangep, int nranges) | ||
61 | { | ||
62 | int regc, rngc; | ||
63 | |||
64 | for (regc = 0; regc < nregs; regc++) { | ||
65 | for (rngc = 0; rngc < nranges; rngc++) | ||
66 | if (regp[regc].which_io == rangep[rngc].ot_child_space) | ||
67 | break; /* Fount it */ | ||
68 | if (rngc == nranges) /* oops */ | ||
69 | central_probe_failure(__LINE__); | ||
70 | regp[regc].which_io = rangep[rngc].ot_parent_space; | ||
71 | regp[regc].phys_addr -= rangep[rngc].ot_child_base; | ||
72 | regp[regc].phys_addr += rangep[rngc].ot_parent_base; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | /* Apply probed fhc ranges to registers passed, if no ranges return. */ | ||
77 | void apply_fhc_ranges(struct linux_fhc *fhc, | ||
78 | struct linux_prom_registers *regs, | ||
79 | int nregs) | ||
80 | { | ||
81 | if (fhc->num_fhc_ranges) | ||
82 | adjust_regs(regs, nregs, fhc->fhc_ranges, | ||
83 | fhc->num_fhc_ranges); | ||
84 | } | ||
85 | |||
86 | /* Apply probed central ranges to registers passed, if no ranges return. */ | ||
87 | void apply_central_ranges(struct linux_central *central, | ||
88 | struct linux_prom_registers *regs, int nregs) | ||
89 | { | ||
90 | if (central->num_central_ranges) | ||
91 | adjust_regs(regs, nregs, central->central_ranges, | ||
92 | central->num_central_ranges); | ||
93 | } | ||
94 | |||
95 | void * __init central_alloc_bootmem(unsigned long size) | ||
96 | { | ||
97 | void *ret; | ||
98 | |||
99 | ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL); | ||
100 | if (ret != NULL) | ||
101 | memset(ret, 0, size); | ||
102 | |||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | static unsigned long prom_reg_to_paddr(struct linux_prom_registers *r) | ||
107 | { | ||
108 | unsigned long ret = ((unsigned long) r->which_io) << 32; | ||
109 | |||
110 | return ret | (unsigned long) r->phys_addr; | ||
111 | } | ||
112 | |||
113 | static void probe_other_fhcs(void) | ||
114 | { | ||
115 | struct linux_prom64_registers fpregs[6]; | ||
116 | char namebuf[128]; | ||
117 | int node; | ||
118 | |||
119 | node = prom_getchild(prom_root_node); | ||
120 | node = prom_searchsiblings(node, "fhc"); | ||
121 | if (node == 0) | ||
122 | central_probe_failure(__LINE__); | ||
123 | while (node) { | ||
124 | struct linux_fhc *fhc; | ||
125 | int board; | ||
126 | u32 tmp; | ||
127 | |||
128 | fhc = (struct linux_fhc *) | ||
129 | central_alloc_bootmem(sizeof(struct linux_fhc)); | ||
130 | if (fhc == NULL) | ||
131 | central_probe_failure(__LINE__); | ||
132 | |||
133 | /* Link it into the FHC chain. */ | ||
134 | fhc->next = fhc_list; | ||
135 | fhc_list = fhc; | ||
136 | |||
137 | /* Toplevel FHCs have no parent. */ | ||
138 | fhc->parent = NULL; | ||
139 | |||
140 | fhc->prom_node = node; | ||
141 | prom_getstring(node, "name", namebuf, sizeof(namebuf)); | ||
142 | strcpy(fhc->prom_name, namebuf); | ||
143 | fhc_ranges_init(node, fhc); | ||
144 | |||
145 | /* Non-central FHC's have 64-bit OBP format registers. */ | ||
146 | if (prom_getproperty(node, "reg", | ||
147 | (char *)&fpregs[0], sizeof(fpregs)) == -1) | ||
148 | central_probe_failure(__LINE__); | ||
149 | |||
150 | /* Only central FHC needs special ranges applied. */ | ||
151 | fhc->fhc_regs.pregs = fpregs[0].phys_addr; | ||
152 | fhc->fhc_regs.ireg = fpregs[1].phys_addr; | ||
153 | fhc->fhc_regs.ffregs = fpregs[2].phys_addr; | ||
154 | fhc->fhc_regs.sregs = fpregs[3].phys_addr; | ||
155 | fhc->fhc_regs.uregs = fpregs[4].phys_addr; | ||
156 | fhc->fhc_regs.tregs = fpregs[5].phys_addr; | ||
157 | |||
158 | board = prom_getintdefault(node, "board#", -1); | ||
159 | fhc->board = board; | ||
160 | |||
161 | tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_JCTRL); | ||
162 | if ((tmp & FHC_JTAG_CTRL_MENAB) != 0) | ||
163 | fhc->jtag_master = 1; | ||
164 | else | ||
165 | fhc->jtag_master = 0; | ||
166 | |||
167 | tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID); | ||
168 | printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n", | ||
169 | board, | ||
170 | (tmp & FHC_ID_VERS) >> 28, | ||
171 | (tmp & FHC_ID_PARTID) >> 12, | ||
172 | (tmp & FHC_ID_MANUF) >> 1, | ||
173 | (fhc->jtag_master ? "(JTAG Master)" : "")); | ||
174 | |||
175 | /* This bit must be set in all non-central FHC's in | ||
176 | * the system. When it is clear, this identifies | ||
177 | * the central board. | ||
178 | */ | ||
179 | tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
180 | tmp |= FHC_CONTROL_IXIST; | ||
181 | upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
182 | |||
183 | /* Look for the next FHC. */ | ||
184 | node = prom_getsibling(node); | ||
185 | if (node == 0) | ||
186 | break; | ||
187 | node = prom_searchsiblings(node, "fhc"); | ||
188 | if (node == 0) | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static void probe_clock_board(struct linux_central *central, | ||
194 | struct linux_fhc *fhc, | ||
195 | int cnode, int fnode) | ||
196 | { | ||
197 | struct linux_prom_registers cregs[3]; | ||
198 | int clknode, nslots, tmp, nregs; | ||
199 | |||
200 | clknode = prom_searchsiblings(prom_getchild(fnode), "clock-board"); | ||
201 | if (clknode == 0 || clknode == -1) | ||
202 | central_probe_failure(__LINE__); | ||
203 | |||
204 | nregs = prom_getproperty(clknode, "reg", (char *)&cregs[0], sizeof(cregs)); | ||
205 | if (nregs == -1) | ||
206 | central_probe_failure(__LINE__); | ||
207 | |||
208 | nregs /= sizeof(struct linux_prom_registers); | ||
209 | apply_fhc_ranges(fhc, &cregs[0], nregs); | ||
210 | apply_central_ranges(central, &cregs[0], nregs); | ||
211 | central->cfreg = prom_reg_to_paddr(&cregs[0]); | ||
212 | central->clkregs = prom_reg_to_paddr(&cregs[1]); | ||
213 | |||
214 | if (nregs == 2) | ||
215 | central->clkver = 0UL; | ||
216 | else | ||
217 | central->clkver = prom_reg_to_paddr(&cregs[2]); | ||
218 | |||
219 | tmp = upa_readb(central->clkregs + CLOCK_STAT1); | ||
220 | tmp &= 0xc0; | ||
221 | switch(tmp) { | ||
222 | case 0x40: | ||
223 | nslots = 16; | ||
224 | break; | ||
225 | case 0xc0: | ||
226 | nslots = 8; | ||
227 | break; | ||
228 | case 0x80: | ||
229 | if (central->clkver != 0UL && | ||
230 | upa_readb(central->clkver) != 0) { | ||
231 | if ((upa_readb(central->clkver) & 0x80) != 0) | ||
232 | nslots = 4; | ||
233 | else | ||
234 | nslots = 5; | ||
235 | break; | ||
236 | } | ||
237 | default: | ||
238 | nslots = 4; | ||
239 | break; | ||
240 | }; | ||
241 | central->slots = nslots; | ||
242 | printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n", | ||
243 | central->slots, upa_readb(central->cfreg), | ||
244 | (central->clkver ? upa_readb(central->clkver) : 0x00)); | ||
245 | } | ||
246 | |||
247 | static void ZAP(unsigned long iclr, unsigned long imap) | ||
248 | { | ||
249 | u32 imap_tmp; | ||
250 | |||
251 | upa_writel(0, iclr); | ||
252 | upa_readl(iclr); | ||
253 | imap_tmp = upa_readl(imap); | ||
254 | imap_tmp &= ~(0x80000000); | ||
255 | upa_writel(imap_tmp, imap); | ||
256 | upa_readl(imap); | ||
257 | } | ||
258 | |||
259 | static void init_all_fhc_hw(void) | ||
260 | { | ||
261 | struct linux_fhc *fhc; | ||
262 | |||
263 | for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) { | ||
264 | u32 tmp; | ||
265 | |||
266 | /* Clear all of the interrupt mapping registers | ||
267 | * just in case OBP left them in a foul state. | ||
268 | */ | ||
269 | ZAP(fhc->fhc_regs.ffregs + FHC_FFREGS_ICLR, | ||
270 | fhc->fhc_regs.ffregs + FHC_FFREGS_IMAP); | ||
271 | ZAP(fhc->fhc_regs.sregs + FHC_SREGS_ICLR, | ||
272 | fhc->fhc_regs.sregs + FHC_SREGS_IMAP); | ||
273 | ZAP(fhc->fhc_regs.uregs + FHC_UREGS_ICLR, | ||
274 | fhc->fhc_regs.uregs + FHC_UREGS_IMAP); | ||
275 | ZAP(fhc->fhc_regs.tregs + FHC_TREGS_ICLR, | ||
276 | fhc->fhc_regs.tregs + FHC_TREGS_IMAP); | ||
277 | |||
278 | /* Setup FHC control register. */ | ||
279 | tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
280 | |||
281 | /* All non-central boards have this bit set. */ | ||
282 | if (! IS_CENTRAL_FHC(fhc)) | ||
283 | tmp |= FHC_CONTROL_IXIST; | ||
284 | |||
285 | /* For all FHCs, clear the firmware synchronization | ||
286 | * line and both low power mode enables. | ||
287 | */ | ||
288 | tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | | ||
289 | FHC_CONTROL_SLINE); | ||
290 | |||
291 | upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
292 | upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
293 | } | ||
294 | |||
295 | } | ||
296 | |||
297 | void central_probe(void) | ||
298 | { | ||
299 | struct linux_prom_registers fpregs[6]; | ||
300 | struct linux_fhc *fhc; | ||
301 | char namebuf[128]; | ||
302 | int cnode, fnode, err; | ||
303 | |||
304 | cnode = prom_finddevice("/central"); | ||
305 | if (cnode == 0 || cnode == -1) { | ||
306 | if (this_is_starfire) | ||
307 | starfire_cpu_setup(); | ||
308 | return; | ||
309 | } | ||
310 | |||
311 | /* Ok we got one, grab some memory for software state. */ | ||
312 | central_bus = (struct linux_central *) | ||
313 | central_alloc_bootmem(sizeof(struct linux_central)); | ||
314 | if (central_bus == NULL) | ||
315 | central_probe_failure(__LINE__); | ||
316 | |||
317 | fhc = (struct linux_fhc *) | ||
318 | central_alloc_bootmem(sizeof(struct linux_fhc)); | ||
319 | if (fhc == NULL) | ||
320 | central_probe_failure(__LINE__); | ||
321 | |||
322 | /* First init central. */ | ||
323 | central_bus->child = fhc; | ||
324 | central_bus->prom_node = cnode; | ||
325 | |||
326 | prom_getstring(cnode, "name", namebuf, sizeof(namebuf)); | ||
327 | strcpy(central_bus->prom_name, namebuf); | ||
328 | |||
329 | central_ranges_init(cnode, central_bus); | ||
330 | |||
331 | /* And then central's FHC. */ | ||
332 | fhc->next = fhc_list; | ||
333 | fhc_list = fhc; | ||
334 | |||
335 | fhc->parent = central_bus; | ||
336 | fnode = prom_searchsiblings(prom_getchild(cnode), "fhc"); | ||
337 | if (fnode == 0 || fnode == -1) | ||
338 | central_probe_failure(__LINE__); | ||
339 | |||
340 | fhc->prom_node = fnode; | ||
341 | prom_getstring(fnode, "name", namebuf, sizeof(namebuf)); | ||
342 | strcpy(fhc->prom_name, namebuf); | ||
343 | |||
344 | fhc_ranges_init(fnode, fhc); | ||
345 | |||
346 | /* Now, map in FHC register set. */ | ||
347 | if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) | ||
348 | central_probe_failure(__LINE__); | ||
349 | |||
350 | apply_central_ranges(central_bus, &fpregs[0], 6); | ||
351 | |||
352 | fhc->fhc_regs.pregs = prom_reg_to_paddr(&fpregs[0]); | ||
353 | fhc->fhc_regs.ireg = prom_reg_to_paddr(&fpregs[1]); | ||
354 | fhc->fhc_regs.ffregs = prom_reg_to_paddr(&fpregs[2]); | ||
355 | fhc->fhc_regs.sregs = prom_reg_to_paddr(&fpregs[3]); | ||
356 | fhc->fhc_regs.uregs = prom_reg_to_paddr(&fpregs[4]); | ||
357 | fhc->fhc_regs.tregs = prom_reg_to_paddr(&fpregs[5]); | ||
358 | |||
359 | /* Obtain board number from board status register, Central's | ||
360 | * FHC lacks "board#" property. | ||
361 | */ | ||
362 | err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_BSR); | ||
363 | fhc->board = (((err >> 16) & 0x01) | | ||
364 | ((err >> 12) & 0x0e)); | ||
365 | |||
366 | fhc->jtag_master = 0; | ||
367 | |||
368 | /* Attach the clock board registers for CENTRAL. */ | ||
369 | probe_clock_board(central_bus, fhc, cnode, fnode); | ||
370 | |||
371 | err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID); | ||
372 | printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n", | ||
373 | fhc->board, | ||
374 | ((err & FHC_ID_VERS) >> 28), | ||
375 | ((err & FHC_ID_PARTID) >> 12), | ||
376 | ((err & FHC_ID_MANUF) >> 1)); | ||
377 | |||
378 | probe_other_fhcs(); | ||
379 | |||
380 | init_all_fhc_hw(); | ||
381 | } | ||
382 | |||
383 | static __inline__ void fhc_ledblink(struct linux_fhc *fhc, int on) | ||
384 | { | ||
385 | u32 tmp; | ||
386 | |||
387 | tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
388 | |||
389 | /* NOTE: reverse logic on this bit */ | ||
390 | if (on) | ||
391 | tmp &= ~(FHC_CONTROL_RLED); | ||
392 | else | ||
393 | tmp |= FHC_CONTROL_RLED; | ||
394 | tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE); | ||
395 | |||
396 | upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
397 | upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); | ||
398 | } | ||
399 | |||
400 | static __inline__ void central_ledblink(struct linux_central *central, int on) | ||
401 | { | ||
402 | u8 tmp; | ||
403 | |||
404 | tmp = upa_readb(central->clkregs + CLOCK_CTRL); | ||
405 | |||
406 | /* NOTE: reverse logic on this bit */ | ||
407 | if (on) | ||
408 | tmp &= ~(CLOCK_CTRL_RLED); | ||
409 | else | ||
410 | tmp |= CLOCK_CTRL_RLED; | ||
411 | |||
412 | upa_writeb(tmp, central->clkregs + CLOCK_CTRL); | ||
413 | upa_readb(central->clkregs + CLOCK_CTRL); | ||
414 | } | ||
415 | |||
416 | static struct timer_list sftimer; | ||
417 | static int led_state; | ||
418 | |||
419 | static void sunfire_timer(unsigned long __ignored) | ||
420 | { | ||
421 | struct linux_fhc *fhc; | ||
422 | |||
423 | central_ledblink(central_bus, led_state); | ||
424 | for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) | ||
425 | if (! IS_CENTRAL_FHC(fhc)) | ||
426 | fhc_ledblink(fhc, led_state); | ||
427 | led_state = ! led_state; | ||
428 | sftimer.expires = jiffies + (HZ >> 1); | ||
429 | add_timer(&sftimer); | ||
430 | } | ||
431 | |||
432 | /* After PCI/SBUS busses have been probed, this is called to perform | ||
433 | * final initialization of all FireHose Controllers in the system. | ||
434 | */ | ||
435 | void firetruck_init(void) | ||
436 | { | ||
437 | struct linux_central *central = central_bus; | ||
438 | u8 ctrl; | ||
439 | |||
440 | /* No central bus, nothing to do. */ | ||
441 | if (central == NULL) | ||
442 | return; | ||
443 | |||
444 | /* OBP leaves it on, turn it off so clock board timer LED | ||
445 | * is in sync with FHC ones. | ||
446 | */ | ||
447 | ctrl = upa_readb(central->clkregs + CLOCK_CTRL); | ||
448 | ctrl &= ~(CLOCK_CTRL_RLED); | ||
449 | upa_writeb(ctrl, central->clkregs + CLOCK_CTRL); | ||
450 | |||
451 | led_state = 0; | ||
452 | init_timer(&sftimer); | ||
453 | sftimer.data = 0; | ||
454 | sftimer.function = &sunfire_timer; | ||
455 | sftimer.expires = jiffies + (HZ >> 1); | ||
456 | add_timer(&sftimer); | ||
457 | } | ||