4 * (C) 2004 Stefan Reinauer <stepan@openbios.org>
5 * (C) 2005 Ed Schouten <ed@fxq.nl>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
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"
22 #define SBUS_REGS 0x28
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
36 #ifdef CONFIG_DEBUG_SBUS
37 #define DPRINTF(fmt, args...) \
38 do { printk(fmt , ##args); } while (0)
40 #define DPRINTF(fmt, args...)
43 typedef struct le_private {
49 ob_sbus_node_init(uint64_t base)
53 push_str("/iommu/sbus");
58 PUSH(base & 0xffffffff);
67 regs = (void *)ofmem_map_io(base, SBUS_REGS);
68 PUSH((unsigned long)regs);
75 ob_le_init(unsigned int slot, uint64_t base, unsigned long leoffset, unsigned long dmaoffset)
79 le = malloc(sizeof(le_private_t));
81 DPRINTF("Can't allocate LANCE private structure\n");
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");
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.
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;
105 push_str("/iommu/sbus/ledma");
106 fword("find-device");
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");
125 push_str("/iommu/sbus/ledma/le");
126 fword("find-device");
139 uint16_t graphic_depth;
142 ob_tcx_init(unsigned int slot, const char *path)
146 printk("No display device located during SBus probe - falling back to internal TCX driver\n");
148 /* Make the sbus node the current instance and active package for probing */
149 feval("active-package my-self");
150 push_str("/iommu/sbus");
151 feval("2dup find-device open-dev to my-self");
156 snprintf(buf, 6, "%x,0", slot);
159 feval("['] tcx-driver-fcode 2 cells + 1 byte-load");
160 fword("finish-device");
163 feval("to my-self active-package!");
167 ob_apc_init(unsigned int slot, unsigned long base)
169 push_str("/iommu/sbus");
170 fword("find-device");
173 push_str("power-management");
174 fword("device-name");
187 fword("finish-device");
191 ob_cs4231_init(unsigned int slot)
193 push_str("/iommu/sbus");
194 fword("find-device");
197 push_str("SUNW,CS4231");
198 fword("device-name");
201 fword("device-type");
224 push_str("interrupts");
228 fword("encode-string");
232 fword("finish-device");
236 ob_macio_init(unsigned int slot, uint64_t base, unsigned long offset)
238 // All devices were integrated to NCR89C100, see
239 // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
241 // NCR 53c9x, aka ESP. See
242 // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
243 #ifdef CONFIG_DRIVER_ESP
244 ob_esp_init(slot, base, offset + MACIO_ESP, offset + MACIO_ESPDMA);
247 // NCR 92C990, Am7990, Lance. See http://www.amd.com
248 ob_le_init(slot, base, offset + 0x00c00000, offset + 0x00400010);
255 sbus_probe_self(unsigned int slot, unsigned long offset)
257 /* Wrapper for calling probe-self in Forth. This is mainly because some
258 drivers don't handle properties correctly when the sbus node is set
259 as the current instance during probe. */
262 printk("Probing SBus slot %d offset %ld\n", slot, offset);
264 /* Make the sbus node the current instance and active package for probing */
265 feval("active-package my-self");
266 push_str("/iommu/sbus");
267 feval("open-dev to my-self");
271 snprintf(buf, 6, "%x,%lx", slot, offset);
274 fword("probe-self-sbus");
277 feval("to my-self active-package!");
281 sbus_probe_sucess(void)
283 /* Return true if the last sbus_probe_self() resulted in
284 the successful detection and execution of FCode */
285 fword("probe-fcode?");
290 sbus_probe_slot_ss5(unsigned int slot, uint64_t base)
293 sbus_probe_self(slot, 0);
295 /* If the device was successfully created by FCode then do nothing */
296 if (sbus_probe_sucess()) {
302 ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
306 ob_cs4231_init(slot);
307 // Power management (APC)
308 ob_apc_init(slot, APC_OFFSET);
310 case 5: // MACIO: le, esp, bpp
311 ob_macio_init(slot, base, 0x08000000);
319 sbus_probe_slot_ss10(unsigned int slot, uint64_t base)
322 sbus_probe_self(slot, 0);
324 /* If the device was successfully created by FCode then do nothing */
325 if (sbus_probe_sucess()) {
331 ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
333 case 0xf: // le, esp, bpp, power-management
334 ob_macio_init(slot, base, 0);
335 // Power management (APC) XXX should not exist
336 ob_apc_init(slot, APC_OFFSET);
344 sbus_probe_slot_ss600mp(unsigned int slot, uint64_t base)
347 sbus_probe_self(slot, 0);
349 /* If the device was successfully created by FCode then do nothing */
350 if (sbus_probe_sucess()) {
356 ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
358 case 0xf: // le, esp, bpp, power-management
359 #ifdef CONFIG_DRIVER_ESP
360 ob_esp_init(slot, base, SS600MP_ESP, SS600MP_ESPDMA);
362 // NCR 92C990, Am7990, Lance. See http://www.amd.com
363 ob_le_init(slot, base, 0x00060000, SS600MP_LEBUFFER);
364 // Power management (APC) XXX should not exist
365 ob_apc_init(slot, APC_OFFSET);
378 static const struct sbus_offset sbus_offsets_ss5[SBUS_SLOTS] = {
379 { 0, 0, 0x20000000, 0x10000000,},
380 { 1, 0, 0x30000000, 0x10000000,},
381 { 2, 0, 0x40000000, 0x10000000,},
382 { 3, 0, 0x50000000, 0x10000000,},
383 { 4, 0, 0x60000000, 0x10000000,},
384 { 5, 0, 0x70000000, 0x10000000,},
387 /* Shared with ss600mp */
388 static const struct sbus_offset sbus_offsets_ss10[SBUS_SLOTS] = {
389 { 0, 0, 0xe00000000ULL, 0x10000000,},
390 { 1, 0, 0xe10000000ULL, 0x10000000,},
391 { 2, 0, 0xe20000000ULL, 0x10000000,},
392 { 3, 0, 0xe30000000ULL, 0x10000000,},
393 [0xf] = { 0xf, 0, 0xef0000000ULL, 0x10000000,},
397 ob_add_sbus_range(const struct sbus_offset *range, int notfirst)
400 push_str("/iommu/sbus");
401 fword("find-device");
410 PUSH(range->base >> 32);
413 PUSH(range->base & 0xffffffff);
422 ob_sbus_init_ss5(void)
427 for (slot = 0; slot < SBUS_SLOTS; slot++) {
428 if (sbus_offsets_ss5[slot].size > 0)
429 ob_add_sbus_range(&sbus_offsets_ss5[slot], notfirst++);
434 for (slot = 0; slot < SBUS_SLOTS; slot++) {
435 if (sbus_offsets_ss5[slot].size > 0)
436 sbus_probe_slot_ss5(slot, sbus_offsets_ss5[slot].base);
443 ob_sbus_init_ss10(void)
448 for (slot = 0; slot < SBUS_SLOTS; slot++) {
449 if (sbus_offsets_ss10[slot].size > 0)
450 ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
455 for (slot = 0; slot < SBUS_SLOTS; slot++) {
456 if (sbus_offsets_ss10[slot].size > 0)
457 sbus_probe_slot_ss10(slot, sbus_offsets_ss10[slot].base);
464 ob_sbus_init_ss600mp(void)
469 for (slot = 0; slot < SBUS_SLOTS; slot++) {
470 if (sbus_offsets_ss10[slot].size > 0)
471 ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
476 for (slot = 0; slot < SBUS_SLOTS; slot++) {
477 if (sbus_offsets_ss10[slot].size > 0)
478 sbus_probe_slot_ss600mp(slot, sbus_offsets_ss10[slot].base);
484 int ob_sbus_init(uint64_t base, int machine_id)
486 ob_sbus_node_init(base);
488 switch (machine_id) {
490 return ob_sbus_init_ss600mp();
492 return ob_sbus_init_ss10();
494 return ob_sbus_init_ss5();