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