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