1 /* $Id: central.c,v 1.11 1998/12/14 12:18:16 davem Exp $
2 * central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
4 * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
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>
17 struct linux_central
*central_bus
= NULL
;
18 struct linux_fhc
*fhc_list
= NULL
;
20 #define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child)
22 static inline unsigned long long_align(unsigned long addr
)
24 return ((addr
+ (sizeof(unsigned long) - 1)) &
25 ~(sizeof(unsigned long) - 1));
28 extern void prom_central_ranges_init(int cnode
, struct linux_central
*central
);
29 extern void prom_fhc_ranges_init(int fnode
, struct linux_fhc
*fhc
);
31 static unsigned long probe_other_fhcs(unsigned long memory_start
)
33 struct linux_prom64_registers fpregs
[6];
37 node
= prom_getchild(prom_root_node
);
38 node
= prom_searchsiblings(node
, "fhc");
40 prom_printf("FHC: Cannot find any toplevel firehose controllers.\n");
44 struct linux_fhc
*fhc
;
48 fhc
= (struct linux_fhc
*)memory_start
;
49 memory_start
+= sizeof(struct linux_fhc
);
50 memory_start
= long_align(memory_start
);
52 /* Link it into the FHC chain. */
56 /* Toplevel FHCs have no parent. */
59 fhc
->prom_node
= node
;
60 prom_getstring(node
, "name", namebuf
, sizeof(namebuf
));
61 strcpy(fhc
->prom_name
, namebuf
);
62 prom_fhc_ranges_init(node
, fhc
);
64 /* Non-central FHC's have 64-bit OBP format registers. */
65 if(prom_getproperty(node
, "reg",
66 (char *)&fpregs
[0], sizeof(fpregs
)) == -1) {
67 prom_printf("FHC: Fatal error, cannot get fhc regs.\n");
71 /* Only central FHC needs special ranges applied. */
72 fhc
->fhc_regs
.pregs
= (struct fhc_internal_regs
*)
73 __va(fpregs
[0].phys_addr
);
74 fhc
->fhc_regs
.ireg
= (struct fhc_ign_reg
*)
75 __va(fpregs
[1].phys_addr
);
76 fhc
->fhc_regs
.ffregs
= (struct fhc_fanfail_regs
*)
77 __va(fpregs
[2].phys_addr
);
78 fhc
->fhc_regs
.sregs
= (struct fhc_system_regs
*)
79 __va(fpregs
[3].phys_addr
);
80 fhc
->fhc_regs
.uregs
= (struct fhc_uart_regs
*)
81 __va(fpregs
[4].phys_addr
);
82 fhc
->fhc_regs
.tregs
= (struct fhc_tod_regs
*)
83 __va(fpregs
[5].phys_addr
);
85 board
= prom_getintdefault(node
, "board#", -1);
88 tmp
= fhc
->fhc_regs
.pregs
->fhc_jtag_ctrl
;
89 if((tmp
& FHC_JTAG_CTRL_MENAB
) != 0)
94 tmp
= fhc
->fhc_regs
.pregs
->fhc_id
;
95 printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n",
97 (tmp
& FHC_ID_VERS
) >> 28,
98 (tmp
& FHC_ID_PARTID
) >> 12,
99 (tmp
& FHC_ID_MANUF
) >> 1,
100 (fhc
->jtag_master
? "(JTAG Master)" : ""));
102 /* This bit must be set in all non-central FHC's in
103 * the system. When it is clear, this identifies
106 fhc
->fhc_regs
.pregs
->fhc_control
|= FHC_CONTROL_IXIST
;
108 /* Look for the next FHC. */
109 node
= prom_getsibling(node
);
112 node
= prom_searchsiblings(node
, "fhc");
120 static void probe_clock_board(struct linux_central
*central
,
121 struct linux_fhc
*fhc
,
122 int cnode
, int fnode
)
124 struct linux_prom_registers cregs
[3];
125 int clknode
, nslots
, tmp
, nregs
;
127 clknode
= prom_searchsiblings(prom_getchild(fnode
), "clock-board");
128 if(clknode
== 0 || clknode
== -1) {
129 prom_printf("Critical error, central lacks clock-board.\n");
132 nregs
= prom_getproperty(clknode
, "reg", (char *)&cregs
[0], sizeof(cregs
));
134 prom_printf("CENTRAL: Fatal error, cannot map clock-board regs.\n");
137 nregs
/= sizeof(struct linux_prom_registers
);
138 prom_apply_fhc_ranges(fhc
, &cregs
[0], nregs
);
139 prom_apply_central_ranges(central
, &cregs
[0], nregs
);
140 central
->cfreg
= (volatile u8
*)
141 __va((((unsigned long)cregs
[0].which_io
) << 32) |
142 (((unsigned long)cregs
[0].phys_addr
)+0x02));
143 central
->clkregs
= (struct clock_board_regs
*)
144 __va((((unsigned long)cregs
[1].which_io
) << 32) |
145 (((unsigned long)cregs
[1].phys_addr
)));
147 central
->clkver
= NULL
;
149 central
->clkver
= (volatile u8
*)
150 __va((((unsigned long)cregs
[2].which_io
) << 32) |
151 (((unsigned long)cregs
[2].phys_addr
)));
153 tmp
= central
->clkregs
->stat1
;
163 if(central
->clkver
!= NULL
&&
164 *(central
->clkver
) != 0) {
165 if((*(central
->clkver
) & 0x80) != 0)
175 central
->slots
= nslots
;
176 printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n",
177 central
->slots
, *(central
->cfreg
),
178 (central
->clkver
? *(central
->clkver
) : 0x00));
181 unsigned long central_probe(unsigned long memory_start
)
183 struct linux_prom_registers fpregs
[6];
184 struct linux_fhc
*fhc
;
186 int cnode
, fnode
, err
;
188 cnode
= prom_finddevice("/central");
189 if(cnode
== 0 || cnode
== -1) {
190 extern void starfire_check(void);
196 /* Ok we got one, grab some memory for software state. */
197 memory_start
= long_align(memory_start
);
198 central_bus
= (struct linux_central
*) (memory_start
);
200 memory_start
+= sizeof(struct linux_central
);
201 memory_start
= long_align(memory_start
);
202 fhc
= (struct linux_fhc
*)(memory_start
);
203 memory_start
+= sizeof(struct linux_fhc
);
204 memory_start
= long_align(memory_start
);
206 /* First init central. */
207 central_bus
->child
= fhc
;
208 central_bus
->prom_node
= cnode
;
210 prom_getstring(cnode
, "name", namebuf
, sizeof(namebuf
));
211 strcpy(central_bus
->prom_name
, namebuf
);
213 prom_central_ranges_init(cnode
, central_bus
);
215 /* And then central's FHC. */
216 fhc
->next
= fhc_list
;
219 fhc
->parent
= central_bus
;
220 fnode
= prom_searchsiblings(prom_getchild(cnode
), "fhc");
221 if(fnode
== 0 || fnode
== -1) {
222 prom_printf("Critical error, central board lacks fhc.\n");
225 fhc
->prom_node
= fnode
;
226 prom_getstring(fnode
, "name", namebuf
, sizeof(namebuf
));
227 strcpy(fhc
->prom_name
, namebuf
);
229 prom_fhc_ranges_init(fnode
, fhc
);
231 /* Now, map in FHC register set. */
232 if (prom_getproperty(fnode
, "reg", (char *)&fpregs
[0], sizeof(fpregs
)) == -1) {
233 prom_printf("CENTRAL: Fatal error, cannot get fhc regs.\n");
236 prom_apply_central_ranges(central_bus
, &fpregs
[0], 6);
238 fhc
->fhc_regs
.pregs
= (struct fhc_internal_regs
*)
239 __va((((unsigned long)fpregs
[0].which_io
)<<32) |
240 (((unsigned long)fpregs
[0].phys_addr
)));
241 fhc
->fhc_regs
.ireg
= (struct fhc_ign_reg
*)
242 __va((((unsigned long)fpregs
[1].which_io
)<<32) |
243 (((unsigned long)fpregs
[1].phys_addr
)));
244 fhc
->fhc_regs
.ffregs
= (struct fhc_fanfail_regs
*)
245 __va((((unsigned long)fpregs
[2].which_io
)<<32) |
246 (((unsigned long)fpregs
[2].phys_addr
)));
247 fhc
->fhc_regs
.sregs
= (struct fhc_system_regs
*)
248 __va((((unsigned long)fpregs
[3].which_io
)<<32) |
249 (((unsigned long)fpregs
[3].phys_addr
)));
250 fhc
->fhc_regs
.uregs
= (struct fhc_uart_regs
*)
251 __va((((unsigned long)fpregs
[4].which_io
)<<32) |
252 (((unsigned long)fpregs
[4].phys_addr
)));
253 fhc
->fhc_regs
.tregs
= (struct fhc_tod_regs
*)
254 __va((((unsigned long)fpregs
[5].which_io
)<<32) |
255 (((unsigned long)fpregs
[5].phys_addr
)));
257 /* Obtain board number from board status register, Central's
258 * FHC lacks "board#" property.
260 err
= fhc
->fhc_regs
.pregs
->fhc_bsr
;
261 fhc
->board
= (((err
>> 16) & 0x01) |
262 ((err
>> 12) & 0x0e));
264 fhc
->jtag_master
= 0;
266 /* Attach the clock board registers for CENTRAL. */
267 probe_clock_board(central_bus
, fhc
, cnode
, fnode
);
269 err
= fhc
->fhc_regs
.pregs
->fhc_id
;
270 printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n",
272 ((err
& FHC_ID_VERS
) >> 28),
273 ((err
& FHC_ID_PARTID
) >> 12),
274 ((err
& FHC_ID_MANUF
) >> 1));
276 return probe_other_fhcs(memory_start
);
279 static __inline__
void fhc_ledblink(struct linux_fhc
*fhc
, int on
)
281 volatile u32
*ctrl
= (volatile u32
*)
282 &fhc
->fhc_regs
.pregs
->fhc_control
;
287 /* NOTE: reverse logic on this bit */
289 tmp
&= ~(FHC_CONTROL_RLED
);
291 tmp
|= FHC_CONTROL_RLED
;
292 tmp
&= ~(FHC_CONTROL_AOFF
| FHC_CONTROL_BOFF
| FHC_CONTROL_SLINE
);
298 static __inline__
void central_ledblink(struct linux_central
*central
, int on
)
300 volatile u8
*ctrl
= (volatile u8
*) ¢ral
->clkregs
->control
;
305 /* NOTE: reverse logic on this bit */
307 tmp
&= ~(CLOCK_CTRL_RLED
);
309 tmp
|= CLOCK_CTRL_RLED
;
315 static struct timer_list sftimer
;
316 static int led_state
;
318 static void sunfire_timer(unsigned long __ignored
)
320 struct linux_fhc
*fhc
;
322 central_ledblink(central_bus
, led_state
);
323 for(fhc
= fhc_list
; fhc
!= NULL
; fhc
= fhc
->next
)
324 if(! IS_CENTRAL_FHC(fhc
))
325 fhc_ledblink(fhc
, led_state
);
326 led_state
= ! led_state
;
327 sftimer
.expires
= jiffies
+ (HZ
>> 1);
331 /* After PCI/SBUS busses have been probed, this is called to perform
332 * final initialization of all FireHose Controllers in the system.
334 void firetruck_init(void)
336 struct linux_central
*central
= central_bus
;
337 struct linux_fhc
*fhc
;
339 /* No central bus, nothing to do. */
343 for(fhc
= fhc_list
; fhc
!= NULL
; fhc
= fhc
->next
) {
344 volatile u32
*ctrl
= (volatile u32
*)
345 &fhc
->fhc_regs
.pregs
->fhc_control
;
348 /* Clear all of the interrupt mapping registers
349 * just in case OBP left them in a foul state.
351 #define ZAP(REG1, REG2) \
352 do { volatile u32 *__iclr = (volatile u32 *)(&(REG1)); \
353 volatile u32 *__imap = (volatile u32 *)(&(REG2)); \
356 *(__imap) &= ~(0x80000000); \
360 ZAP(fhc
->fhc_regs
.ffregs
->fhc_ff_iclr
,
361 fhc
->fhc_regs
.ffregs
->fhc_ff_imap
);
362 ZAP(fhc
->fhc_regs
.sregs
->fhc_sys_iclr
,
363 fhc
->fhc_regs
.sregs
->fhc_sys_imap
);
364 ZAP(fhc
->fhc_regs
.uregs
->fhc_uart_iclr
,
365 fhc
->fhc_regs
.uregs
->fhc_uart_imap
);
366 ZAP(fhc
->fhc_regs
.tregs
->fhc_tod_iclr
,
367 fhc
->fhc_regs
.tregs
->fhc_tod_imap
);
371 /* Setup FHC control register. */
374 /* All non-central boards have this bit set. */
375 if(! IS_CENTRAL_FHC(fhc
))
376 tmp
|= FHC_CONTROL_IXIST
;
378 /* For all FHCs, clear the firmware synchronization
379 * line and both low power mode enables.
381 tmp
&= ~(FHC_CONTROL_AOFF
| FHC_CONTROL_BOFF
| FHC_CONTROL_SLINE
);
383 tmp
= *ctrl
; /* Ensure completion */
386 /* OBP leaves it on, turn it off so clock board timer LED
387 * is in sync with FHC ones.
389 central
->clkregs
->control
&= ~(CLOCK_CTRL_RLED
);
392 init_timer(&sftimer
);
394 sftimer
.function
= &sunfire_timer
;
395 sftimer
.expires
= jiffies
+ (HZ
>> 1);