1 /*
2 * OpenBIOS SBus driver
3 *
4 * (C) 2004 Stefan Reinauer
5 * (C) 2005 Ed Schouten <ed@fxq.nl>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2
10 *
11 */
12
13 #include "config.h"
14 #include "libopenbios/bindings.h"
15 #include "kernel/kernel.h"
16 #include "libc/byteorder.h"
17 #include "libc/vsprintf.h"
18 #include "drivers/drivers.h"
19 #include "libopenbios/ofmem.h"
20 #include "libopenbios/video.h"
21
22 #define SBUS_REGS 0x28
23 #define SBUS_SLOTS 16
24 #define APC_REGS 0x10
25 #define APC_OFFSET 0x0a000000ULL
26 #define CS4231_REGS 0x40
27 #define CS4231_OFFSET 0x0c000000ULL
28 #define MACIO_ESPDMA 0x00400000ULL /* ESP DMA controller */
29 #define MACIO_ESP 0x00800000ULL /* ESP SCSI */
30 #define SS600MP_ESPDMA 0x00081000ULL
31 #define SS600MP_ESP 0x00080000ULL
32 #define SS600MP_LEBUFFER (SS600MP_ESPDMA + 0x10) // XXX should be 0x40000
33 #define LEDMA_REGS 0x4
34 #define LE_REGS 0x20
35
36 #ifdef CONFIG_DEBUG_SBUS
37 #define DPRINTF(fmt, args...) \
38 do { printk(fmt , ##args); } while (0)
39 #else
40 #define DPRINTF(fmt, args...)
41 #endif
42
43 typedef struct le_private {
44 uint32_t *dmaregs;
45 uint32_t *regs;
46 } le_private_t;
47
48 static void
ob_sbus_node_init(uint64_t base)49 ob_sbus_node_init(uint64_t base)
50 {
51 void *regs;
52
53 push_str("/iommu/sbus");
54 fword("find-device");
55
56 PUSH(base >> 32);
57 fword("encode-int");
58 PUSH(base & 0xffffffff);
59 fword("encode-int");
60 fword("encode+");
61 PUSH(SBUS_REGS);
62 fword("encode-int");
63 fword("encode+");
64 push_str("reg");
65 fword("property");
66
67 regs = (void *)ofmem_map_io(base, SBUS_REGS);
68 PUSH((unsigned long)regs);
69 fword("encode-int");
70 push_str("address");
71 fword("property");
72 }
73
74 static void
ob_le_init(unsigned int slot,uint64_t base,unsigned long leoffset,unsigned long dmaoffset)75 ob_le_init(unsigned int slot, uint64_t base, unsigned long leoffset, unsigned long dmaoffset)
76 {
77 le_private_t *le;
78
79 le = malloc(sizeof(le_private_t));
80 if (!le) {
81 DPRINTF("Can't allocate LANCE private structure\n");
82 return;
83 }
84
85 /* Get the IO region for DMA registers */
86 le->dmaregs = (void *)ofmem_map_io(base + (uint64_t)dmaoffset, LEDMA_REGS);
87 if (le->dmaregs == NULL) {
88 DPRINTF("Can't map LANCE DMA registers\n");
89 return;
90 }
91
92 /* Now it appears that the Solaris kernel forgets to set up the LANCE DMA mapping
93 and so it must inherit the one from OpenBIOS. The symptom of this is that the
94 LANCE DMA base addr register is still zero, and so we start sending network
95 packets containing random areas of memory.
96
97 The correct fix for this should be to use dvma_alloc() to grab a section of
98 memory and point the LANCE DMA buffers to use that instead; this gets
99 slightly further but still crashes. Time-consuming investigation on various
100 hacked versions of QEMU seems to indicate that Solaris always assumes the LANCE
101 DMA base address is fixed 0xff000000 when setting up the IOMMU for the LANCE
102 card. Hence we imitate this behaviour here. */
103 le->dmaregs[3] = 0xff000000;
104
105 push_str("/iommu/sbus/ledma");
106 fword("find-device");
107 PUSH(slot);
108 fword("encode-int");
109 PUSH(dmaoffset);
110 fword("encode-int");
111 fword("encode+");
112 PUSH(0x00000020);
113 fword("encode-int");
114 fword("encode+");
115 push_str("reg");
116 fword("property");
117
118 /* Get the IO region for Lance registers */
119 le->regs = (void *)ofmem_map_io(base + (uint64_t)leoffset, LE_REGS);
120 if (le->regs == NULL) {
121 DPRINTF("Can't map LANCE registers\n");
122 return;
123 }
124
125 push_str("/iommu/sbus/ledma/le");
126 fword("find-device");
127 PUSH(slot);
128 fword("encode-int");
129 PUSH(leoffset);
130 fword("encode-int");
131 fword("encode+");
132 PUSH(0x00000004);
133 fword("encode-int");
134 fword("encode+");
135 push_str("reg");
136 fword("property");
137 }
138
139 uint16_t graphic_depth;
140
141 #if !defined(CONFIG_QEMU)
142 static void
ob_tcx_init(unsigned int slot,const char * path)143 ob_tcx_init(unsigned int slot, const char *path)
144 {
145 char buf[6];
146
147 printk("No display device located during SBus probe - falling back to internal TCX driver\n");
148
149 /* Make the sbus node the current instance and active package for probing */
150 feval("active-package my-self");
151 push_str("/iommu/sbus");
152 feval("2dup find-device open-dev to my-self");
153
154 fword("new-device");
155 PUSH(0);
156 PUSH(0);
157 snprintf(buf, 6, "%x,0", slot);
158 push_str(buf);
159 fword("set-args");
160 feval("['] tcx-driver-fcode 2 cells + 1 byte-load");
161 fword("finish-device");
162
163 /* Restore */
164 feval("to my-self active-package!");
165 }
166 #endif
167
168 static void
ob_apc_init(unsigned int slot,unsigned long base)169 ob_apc_init(unsigned int slot, unsigned long base)
170 {
171 push_str("/iommu/sbus");
172 fword("find-device");
173 fword("new-device");
174
175 push_str("power-management");
176 fword("device-name");
177
178 PUSH(slot);
179 fword("encode-int");
180 PUSH(base);
181 fword("encode-int");
182 fword("encode+");
183 PUSH(APC_REGS);
184 fword("encode-int");
185 fword("encode+");
186 push_str("reg");
187 fword("property");
188
189 fword("finish-device");
190 }
191
192 static void
ob_cs4231_init(unsigned int slot)193 ob_cs4231_init(unsigned int slot)
194 {
195 push_str("/iommu/sbus");
196 fword("find-device");
197 fword("new-device");
198
199 push_str("SUNW,CS4231");
200 fword("device-name");
201
202 push_str("serial");
203 fword("device-type");
204
205 PUSH(slot);
206 fword("encode-int");
207 PUSH(CS4231_OFFSET);
208 fword("encode-int");
209 fword("encode+");
210 PUSH(CS4231_REGS);
211 fword("encode-int");
212 fword("encode+");
213 push_str("reg");
214 fword("property");
215
216 PUSH(5);
217 fword("encode-int");
218 PUSH(0);
219 fword("encode-int");
220 fword("encode+");
221 push_str("intr");
222 fword("property");
223
224 PUSH(5);
225 fword("encode-int");
226 push_str("interrupts");
227 fword("property");
228
229 push_str("audio");
230 fword("encode-string");
231 push_str("alias");
232 fword("property");
233
234 fword("finish-device");
235 }
236
237 static void
ob_macio_init(unsigned int slot,uint64_t base,unsigned long offset)238 ob_macio_init(unsigned int slot, uint64_t base, unsigned long offset)
239 {
240 // All devices were integrated to NCR89C100, see
241 // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
242
243 // NCR 53c9x, aka ESP. See
244 // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
245 #ifdef CONFIG_DRIVER_ESP
246 ob_esp_init(slot, base, offset + MACIO_ESP, offset + MACIO_ESPDMA);
247 #endif
248
249 // NCR 92C990, Am7990, Lance. See http://www.amd.com
250 ob_le_init(slot, base, offset + 0x00c00000, offset + 0x00400010);
251
252 // Parallel port
253 //ob_bpp_init(base);
254 }
255
256 static void
sbus_probe_self(unsigned int slot,unsigned long offset)257 sbus_probe_self(unsigned int slot, unsigned long offset)
258 {
259 /* Wrapper for calling probe-self in Forth. This is mainly because some
260 drivers don't handle properties correctly when the sbus node is set
261 as the current instance during probe. */
262 char buf[6];
263
264 printk("Probing SBus slot %d offset %ld\n", slot, offset);
265
266 /* Make the sbus node the current instance and active package for probing */
267 feval("active-package my-self");
268 push_str("/iommu/sbus");
269 feval("open-dev to my-self");
270
271 PUSH(0);
272 PUSH(0);
273 snprintf(buf, 6, "%x,%lx", slot, offset);
274 push_str(buf);
275 fword("2dup");
276 fword("probe-self-sbus");
277
278 /* Restore */
279 feval("to my-self active-package!");
280 }
281
282 static int
sbus_probe_sucess(void)283 sbus_probe_sucess(void)
284 {
285 /* Return true if the last sbus_probe_self() resulted in
286 the successful detection and execution of FCode */
287 fword("probe-fcode?");
288 return POP();
289 }
290
291 static void
sbus_probe_slot_ss5(unsigned int slot,uint64_t base)292 sbus_probe_slot_ss5(unsigned int slot, uint64_t base)
293 {
294 /* Probe the slot */
295 sbus_probe_self(slot, 0);
296
297 /* If the device was successfully created by FCode then do nothing */
298 if (sbus_probe_sucess()) {
299 return;
300 }
301
302 switch(slot) {
303 #if !defined(CONFIG_QEMU)
304 /* QEMU always uses the FCode driver */
305 case 3: // SUNW,tcx
306 ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
307 break;
308 #endif
309 case 4:
310 // SUNW,CS4231
311 ob_cs4231_init(slot);
312 // Power management (APC)
313 ob_apc_init(slot, APC_OFFSET);
314 break;
315 case 5: // MACIO: le, esp, bpp
316 ob_macio_init(slot, base, 0x08000000);
317 break;
318 default:
319 break;
320 }
321 }
322
323 static void
sbus_probe_slot_ss10(unsigned int slot,uint64_t base)324 sbus_probe_slot_ss10(unsigned int slot, uint64_t base)
325 {
326 /* Probe the slot */
327 sbus_probe_self(slot, 0);
328
329 /* If the device was successfully created by FCode then do nothing */
330 if (sbus_probe_sucess()) {
331 return;
332 }
333
334 switch(slot) {
335 #if !defined(CONFIG_QEMU)
336 /* QEMU always uses the FCode driver */
337 case 2: // SUNW,tcx
338 ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
339 break;
340 #endif
341 case 0xf: // le, esp, bpp, power-management
342 ob_macio_init(slot, base, 0);
343 // Power management (APC) XXX should not exist
344 ob_apc_init(slot, APC_OFFSET);
345 break;
346 default:
347 break;
348 }
349 }
350
351 static void
sbus_probe_slot_ss600mp(unsigned int slot,uint64_t base)352 sbus_probe_slot_ss600mp(unsigned int slot, uint64_t base)
353 {
354 /* Probe the slot */
355 sbus_probe_self(slot, 0);
356
357 /* If the device was successfully created by FCode then do nothing */
358 if (sbus_probe_sucess()) {
359 return;
360 }
361
362 switch(slot) {
363 #if !defined(CONFIG_QEMU)
364 /* QEMU always uses the FCode driver */
365 case 2: // SUNW,tcx
366 ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
367 break;
368 #endif
369 case 0xf: // le, esp, bpp, power-management
370 #ifdef CONFIG_DRIVER_ESP
371 ob_esp_init(slot, base, SS600MP_ESP, SS600MP_ESPDMA);
372 #endif
373 // NCR 92C990, Am7990, Lance. See http://www.amd.com
374 ob_le_init(slot, base, 0x00060000, SS600MP_LEBUFFER);
375 // Power management (APC) XXX should not exist
376 ob_apc_init(slot, APC_OFFSET);
377 break;
378 default:
379 break;
380 }
381 }
382
383 struct sbus_offset {
384 int slot, type;
385 uint64_t base;
386 unsigned long size;
387 };
388
389 static const struct sbus_offset sbus_offsets_ss5[SBUS_SLOTS] = {
390 { 0, 0, 0x20000000, 0x10000000,},
391 { 1, 0, 0x30000000, 0x10000000,},
392 { 2, 0, 0x40000000, 0x10000000,},
393 { 3, 0, 0x50000000, 0x10000000,},
394 { 4, 0, 0x60000000, 0x10000000,},
395 { 5, 0, 0x70000000, 0x10000000,},
396 };
397
398 /* Shared with ss600mp */
399 static const struct sbus_offset sbus_offsets_ss10[SBUS_SLOTS] = {
400 { 0, 0, 0xe00000000ULL, 0x10000000,},
401 { 1, 0, 0xe10000000ULL, 0x10000000,},
402 { 2, 0, 0xe20000000ULL, 0x10000000,},
403 { 3, 0, 0xe30000000ULL, 0x10000000,},
404 [0xf] = { 0xf, 0, 0xef0000000ULL, 0x10000000,},
405 };
406
407 static void
ob_add_sbus_range(const struct sbus_offset * range,int notfirst)408 ob_add_sbus_range(const struct sbus_offset *range, int notfirst)
409 {
410 if (!notfirst) {
411 push_str("/iommu/sbus");
412 fword("find-device");
413 }
414 PUSH(range->slot);
415 fword("encode-int");
416 if (notfirst)
417 fword("encode+");
418 PUSH(range->type);
419 fword("encode-int");
420 fword("encode+");
421 PUSH(range->base >> 32);
422 fword("encode-int");
423 fword("encode+");
424 PUSH(range->base & 0xffffffff);
425 fword("encode-int");
426 fword("encode+");
427 PUSH(range->size);
428 fword("encode-int");
429 fword("encode+");
430 }
431
432 static int
ob_sbus_init_ss5(void)433 ob_sbus_init_ss5(void)
434 {
435 unsigned int slot;
436 int notfirst = 0;
437
438 for (slot = 0; slot < SBUS_SLOTS; slot++) {
439 if (sbus_offsets_ss5[slot].size > 0)
440 ob_add_sbus_range(&sbus_offsets_ss5[slot], notfirst++);
441 }
442 push_str("ranges");
443 fword("property");
444
445 for (slot = 0; slot < SBUS_SLOTS; slot++) {
446 if (sbus_offsets_ss5[slot].size > 0)
447 sbus_probe_slot_ss5(slot, sbus_offsets_ss5[slot].base);
448 }
449
450 return 0;
451 }
452
453 static int
ob_sbus_init_ss10(void)454 ob_sbus_init_ss10(void)
455 {
456 unsigned int slot;
457 int notfirst = 0;
458
459 for (slot = 0; slot < SBUS_SLOTS; slot++) {
460 if (sbus_offsets_ss10[slot].size > 0)
461 ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
462 }
463 push_str("ranges");
464 fword("property");
465
466 for (slot = 0; slot < SBUS_SLOTS; slot++) {
467 if (sbus_offsets_ss10[slot].size > 0)
468 sbus_probe_slot_ss10(slot, sbus_offsets_ss10[slot].base);
469 }
470
471 return 0;
472 }
473
474 static int
ob_sbus_init_ss600mp(void)475 ob_sbus_init_ss600mp(void)
476 {
477 unsigned int slot;
478 int notfirst = 0;
479
480 for (slot = 0; slot < SBUS_SLOTS; slot++) {
481 if (sbus_offsets_ss10[slot].size > 0)
482 ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
483 }
484 push_str("ranges");
485 fword("property");
486
487 for (slot = 0; slot < SBUS_SLOTS; slot++) {
488 if (sbus_offsets_ss10[slot].size > 0)
489 sbus_probe_slot_ss600mp(slot, sbus_offsets_ss10[slot].base);
490 }
491
492 return 0;
493 }
494
ob_sbus_init(uint64_t base,int machine_id)495 int ob_sbus_init(uint64_t base, int machine_id)
496 {
497 ob_sbus_node_init(base);
498
499 switch (machine_id) {
500 case 66:
501 return ob_sbus_init_ss600mp();
502 case 64 ... 65:
503 return ob_sbus_init_ss10();
504 case 32 ... 63:
505 return ob_sbus_init_ss5();
506 default:
507 return -1;
508 }
509 }
510