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.
4 * Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com)
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>
18 #include <asm/starfire.h>
20 struct linux_central
*central_bus
= NULL
;
21 struct linux_fhc
*fhc_list
= NULL
;
23 #define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child)
25 static void central_probe_failure(int line
)
27 prom_printf("CENTRAL: Critical device probe failure at central.c:%d\n",
32 static void central_ranges_init(int cnode
, struct linux_central
*central
)
36 central
->num_central_ranges
= 0;
37 success
= prom_getproperty(central
->prom_node
, "ranges",
38 (char *) central
->central_ranges
,
39 sizeof (central
->central_ranges
));
41 central
->num_central_ranges
= (success
/sizeof(struct linux_prom_ranges
));
44 static void fhc_ranges_init(int fnode
, struct linux_fhc
*fhc
)
48 fhc
->num_fhc_ranges
= 0;
49 success
= prom_getproperty(fhc
->prom_node
, "ranges",
50 (char *) fhc
->fhc_ranges
,
51 sizeof (fhc
->fhc_ranges
));
53 fhc
->num_fhc_ranges
= (success
/sizeof(struct linux_prom_ranges
));
56 /* Range application routines are exported to various drivers,
57 * so do not __init this.
59 static void adjust_regs(struct linux_prom_registers
*regp
, int nregs
,
60 struct linux_prom_ranges
*rangep
, int nranges
)
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
)
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
;
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
,
81 if (fhc
->num_fhc_ranges
)
82 adjust_regs(regs
, nregs
, fhc
->fhc_ranges
,
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
)
90 if (central
->num_central_ranges
)
91 adjust_regs(regs
, nregs
, central
->central_ranges
,
92 central
->num_central_ranges
);
95 void * __init
central_alloc_bootmem(unsigned long size
)
99 ret
= __alloc_bootmem(size
, SMP_CACHE_BYTES
, 0UL);
101 memset(ret
, 0, size
);
106 static unsigned long prom_reg_to_paddr(struct linux_prom_registers
*r
)
108 unsigned long ret
= ((unsigned long) r
->which_io
) << 32;
110 return ret
| (unsigned long) r
->phys_addr
;
113 static void probe_other_fhcs(void)
115 struct linux_prom64_registers fpregs
[6];
119 node
= prom_getchild(prom_root_node
);
120 node
= prom_searchsiblings(node
, "fhc");
122 central_probe_failure(__LINE__
);
124 struct linux_fhc
*fhc
;
128 fhc
= (struct linux_fhc
*)
129 central_alloc_bootmem(sizeof(struct linux_fhc
));
131 central_probe_failure(__LINE__
);
133 /* Link it into the FHC chain. */
134 fhc
->next
= fhc_list
;
137 /* Toplevel FHCs have no parent. */
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
);
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__
);
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
;
158 board
= prom_getintdefault(node
, "board#", -1);
161 tmp
= upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_JCTRL
);
162 if ((tmp
& FHC_JTAG_CTRL_MENAB
) != 0)
163 fhc
->jtag_master
= 1;
165 fhc
->jtag_master
= 0;
167 tmp
= upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_ID
);
168 printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n",
170 (tmp
& FHC_ID_VERS
) >> 28,
171 (tmp
& FHC_ID_PARTID
) >> 12,
172 (tmp
& FHC_ID_MANUF
) >> 1,
173 (fhc
->jtag_master
? "(JTAG Master)" : ""));
175 /* This bit must be set in all non-central FHC's in
176 * the system. When it is clear, this identifies
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
);
183 /* Look for the next FHC. */
184 node
= prom_getsibling(node
);
187 node
= prom_searchsiblings(node
, "fhc");
193 static void probe_clock_board(struct linux_central
*central
,
194 struct linux_fhc
*fhc
,
195 int cnode
, int fnode
)
197 struct linux_prom_registers cregs
[3];
198 int clknode
, nslots
, tmp
, nregs
;
200 clknode
= prom_searchsiblings(prom_getchild(fnode
), "clock-board");
201 if (clknode
== 0 || clknode
== -1)
202 central_probe_failure(__LINE__
);
204 nregs
= prom_getproperty(clknode
, "reg", (char *)&cregs
[0], sizeof(cregs
));
206 central_probe_failure(__LINE__
);
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]);
215 central
->clkver
= 0UL;
217 central
->clkver
= prom_reg_to_paddr(&cregs
[2]);
219 tmp
= upa_readb(central
->clkregs
+ CLOCK_STAT1
);
229 if (central
->clkver
!= 0UL &&
230 upa_readb(central
->clkver
) != 0) {
231 if ((upa_readb(central
->clkver
) & 0x80) != 0)
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));
247 static void ZAP(unsigned long iclr
, unsigned long imap
)
253 imap_tmp
= upa_readl(imap
);
254 imap_tmp
&= ~(0x80000000);
255 upa_writel(imap_tmp
, imap
);
259 static void init_all_fhc_hw(void)
261 struct linux_fhc
*fhc
;
263 for (fhc
= fhc_list
; fhc
!= NULL
; fhc
= fhc
->next
) {
266 /* Clear all of the interrupt mapping registers
267 * just in case OBP left them in a foul state.
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
);
278 /* Setup FHC control register. */
279 tmp
= upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_CTRL
);
281 /* All non-central boards have this bit set. */
282 if (! IS_CENTRAL_FHC(fhc
))
283 tmp
|= FHC_CONTROL_IXIST
;
285 /* For all FHCs, clear the firmware synchronization
286 * line and both low power mode enables.
288 tmp
&= ~(FHC_CONTROL_AOFF
| FHC_CONTROL_BOFF
|
291 upa_writel(tmp
, fhc
->fhc_regs
.pregs
+ FHC_PREGS_CTRL
);
292 upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_CTRL
);
297 void central_probe(void)
299 struct linux_prom_registers fpregs
[6];
300 struct linux_fhc
*fhc
;
302 int cnode
, fnode
, err
;
304 cnode
= prom_finddevice("/central");
305 if (cnode
== 0 || cnode
== -1) {
306 if (this_is_starfire
)
307 starfire_cpu_setup();
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__
);
317 fhc
= (struct linux_fhc
*)
318 central_alloc_bootmem(sizeof(struct linux_fhc
));
320 central_probe_failure(__LINE__
);
322 /* First init central. */
323 central_bus
->child
= fhc
;
324 central_bus
->prom_node
= cnode
;
326 prom_getstring(cnode
, "name", namebuf
, sizeof(namebuf
));
327 strcpy(central_bus
->prom_name
, namebuf
);
329 central_ranges_init(cnode
, central_bus
);
331 /* And then central's FHC. */
332 fhc
->next
= fhc_list
;
335 fhc
->parent
= central_bus
;
336 fnode
= prom_searchsiblings(prom_getchild(cnode
), "fhc");
337 if (fnode
== 0 || fnode
== -1)
338 central_probe_failure(__LINE__
);
340 fhc
->prom_node
= fnode
;
341 prom_getstring(fnode
, "name", namebuf
, sizeof(namebuf
));
342 strcpy(fhc
->prom_name
, namebuf
);
344 fhc_ranges_init(fnode
, fhc
);
346 /* Now, map in FHC register set. */
347 if (prom_getproperty(fnode
, "reg", (char *)&fpregs
[0], sizeof(fpregs
)) == -1)
348 central_probe_failure(__LINE__
);
350 apply_central_ranges(central_bus
, &fpregs
[0], 6);
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]);
359 /* Obtain board number from board status register, Central's
360 * FHC lacks "board#" property.
362 err
= upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_BSR
);
363 fhc
->board
= (((err
>> 16) & 0x01) |
364 ((err
>> 12) & 0x0e));
366 fhc
->jtag_master
= 0;
368 /* Attach the clock board registers for CENTRAL. */
369 probe_clock_board(central_bus
, fhc
, cnode
, fnode
);
371 err
= upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_ID
);
372 printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n",
374 ((err
& FHC_ID_VERS
) >> 28),
375 ((err
& FHC_ID_PARTID
) >> 12),
376 ((err
& FHC_ID_MANUF
) >> 1));
383 static __inline__
void fhc_ledblink(struct linux_fhc
*fhc
, int on
)
387 tmp
= upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_CTRL
);
389 /* NOTE: reverse logic on this bit */
391 tmp
&= ~(FHC_CONTROL_RLED
);
393 tmp
|= FHC_CONTROL_RLED
;
394 tmp
&= ~(FHC_CONTROL_AOFF
| FHC_CONTROL_BOFF
| FHC_CONTROL_SLINE
);
396 upa_writel(tmp
, fhc
->fhc_regs
.pregs
+ FHC_PREGS_CTRL
);
397 upa_readl(fhc
->fhc_regs
.pregs
+ FHC_PREGS_CTRL
);
400 static __inline__
void central_ledblink(struct linux_central
*central
, int on
)
404 tmp
= upa_readb(central
->clkregs
+ CLOCK_CTRL
);
406 /* NOTE: reverse logic on this bit */
408 tmp
&= ~(CLOCK_CTRL_RLED
);
410 tmp
|= CLOCK_CTRL_RLED
;
412 upa_writeb(tmp
, central
->clkregs
+ CLOCK_CTRL
);
413 upa_readb(central
->clkregs
+ CLOCK_CTRL
);
416 static struct timer_list sftimer
;
417 static int led_state
;
419 static void sunfire_timer(unsigned long __ignored
)
421 struct linux_fhc
*fhc
;
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);
432 /* After PCI/SBUS busses have been probed, this is called to perform
433 * final initialization of all FireHose Controllers in the system.
435 void firetruck_init(void)
437 struct linux_central
*central
= central_bus
;
440 /* No central bus, nothing to do. */
444 /* OBP leaves it on, turn it off so clock board timer LED
445 * is in sync with FHC ones.
447 ctrl
= upa_readb(central
->clkregs
+ CLOCK_CTRL
);
448 ctrl
&= ~(CLOCK_CTRL_RLED
);
449 upa_writeb(ctrl
, central
->clkregs
+ CLOCK_CTRL
);
452 init_timer(&sftimer
);
454 sftimer
.function
= &sunfire_timer
;
455 sftimer
.expires
= jiffies
+ (HZ
>> 1);