Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / drivers / sbus.c
1 /*
2  *   OpenBIOS SBus driver
3  *
4  *   (C) 2004 Stefan Reinauer <stepan@openbios.org>
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
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
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 static void
142 ob_tcx_init(unsigned int slot, const char *path)
143 {
144     char buf[6];
145
146     printk("No display device located during SBus probe - falling back to internal TCX driver\n");
147
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");
152
153     fword("new-device");
154     PUSH(0);
155     PUSH(0);
156     snprintf(buf, 6, "%x,0", slot);
157     push_str(buf);
158     fword("set-args");
159     feval("['] tcx-driver-fcode 2 cells + 1 byte-load");
160     fword("finish-device");
161
162     /* Restore */
163     feval("to my-self active-package!");
164 }
165
166 static void
167 ob_apc_init(unsigned int slot, unsigned long base)
168 {
169     push_str("/iommu/sbus");
170     fword("find-device");
171     fword("new-device");
172
173     push_str("power-management");
174     fword("device-name");
175
176     PUSH(slot);
177     fword("encode-int");
178     PUSH(base);
179     fword("encode-int");
180     fword("encode+");
181     PUSH(APC_REGS);
182     fword("encode-int");
183     fword("encode+");
184     push_str("reg");
185     fword("property");
186
187     fword("finish-device");
188 }
189
190 static void
191 ob_cs4231_init(unsigned int slot)
192 {
193     push_str("/iommu/sbus");
194     fword("find-device");
195     fword("new-device");
196
197     push_str("SUNW,CS4231");
198     fword("device-name");
199
200     push_str("serial");
201     fword("device-type");
202
203     PUSH(slot);
204     fword("encode-int");
205     PUSH(CS4231_OFFSET);
206     fword("encode-int");
207     fword("encode+");
208     PUSH(CS4231_REGS);
209     fword("encode-int");
210     fword("encode+");
211     push_str("reg");
212     fword("property");
213
214     PUSH(5);
215     fword("encode-int");
216     PUSH(0);
217     fword("encode-int");
218     fword("encode+");
219     push_str("intr");
220     fword("property");
221
222     PUSH(5);
223     fword("encode-int");
224     push_str("interrupts");
225     fword("property");
226
227     push_str("audio");
228     fword("encode-string");
229     push_str("alias");
230     fword("property");
231
232     fword("finish-device");
233 }
234
235 static void
236 ob_macio_init(unsigned int slot, uint64_t base, unsigned long offset)
237 {
238     // All devices were integrated to NCR89C100, see
239     // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
240
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);
245 #endif
246
247     // NCR 92C990, Am7990, Lance. See http://www.amd.com
248     ob_le_init(slot, base, offset + 0x00c00000, offset + 0x00400010);
249
250     // Parallel port
251     //ob_bpp_init(base);
252 }
253
254 static void
255 sbus_probe_self(unsigned int slot, unsigned long offset)
256 {
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. */
260     char buf[6];
261
262     printk("Probing SBus slot %d offset %ld\n", slot, offset);
263
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");
268
269     PUSH(0);
270     PUSH(0);
271     snprintf(buf, 6, "%x,%lx", slot, offset);
272     push_str(buf);
273     fword("2dup");
274     fword("probe-self-sbus");
275
276     /* Restore */
277     feval("to my-self active-package!");
278 }
279
280 static int
281 sbus_probe_sucess(void)
282 {
283     /* Return true if the last sbus_probe_self() resulted in
284        the successful detection and execution of FCode */
285     fword("probe-fcode?");
286     return POP();
287 }
288
289 static void
290 sbus_probe_slot_ss5(unsigned int slot, uint64_t base)
291 {
292     /* Probe the slot */
293     sbus_probe_self(slot, 0);
294
295     /* If the device was successfully created by FCode then do nothing */
296     if (sbus_probe_sucess()) {
297         return;
298     }
299
300     switch(slot) {
301     case 3: // SUNW,tcx
302         ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
303         break;
304     case 4:
305         // SUNW,CS4231
306         ob_cs4231_init(slot);
307         // Power management (APC)
308         ob_apc_init(slot, APC_OFFSET);
309         break;
310     case 5: // MACIO: le, esp, bpp
311         ob_macio_init(slot, base, 0x08000000);
312         break;
313     default:
314         break;
315     }
316 }
317
318 static void
319 sbus_probe_slot_ss10(unsigned int slot, uint64_t base)
320 {
321     /* Probe the slot */
322     sbus_probe_self(slot, 0);
323
324     /* If the device was successfully created by FCode then do nothing */
325     if (sbus_probe_sucess()) {
326         return;
327     }
328
329     switch(slot) {
330     case 2: // SUNW,tcx
331         ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
332         break;
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);
337         break;
338     default:
339         break;
340     }
341 }
342
343 static void
344 sbus_probe_slot_ss600mp(unsigned int slot, uint64_t base)
345 {
346     /* Probe the slot */
347     sbus_probe_self(slot, 0);
348
349     /* If the device was successfully created by FCode then do nothing */
350     if (sbus_probe_sucess()) {
351         return;
352     }
353
354     switch(slot) {
355     case 2: // SUNW,tcx
356         ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
357         break;
358     case 0xf: // le, esp, bpp, power-management
359 #ifdef CONFIG_DRIVER_ESP
360         ob_esp_init(slot, base, SS600MP_ESP, SS600MP_ESPDMA);
361 #endif
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);
366         break;
367     default:
368         break;
369     }
370 }
371
372 static void
373 ob_sbus_open(void)
374 {
375         int ret=1;
376         RET ( -ret );
377 }
378
379 static void
380 ob_sbus_close(void)
381 {
382         selfword("close-deblocker");
383 }
384
385 static void
386 ob_sbus_initialize(void)
387 {
388 }
389
390
391 NODE_METHODS(ob_sbus_node) = {
392         { NULL,                 ob_sbus_initialize      },
393         { "open",               ob_sbus_open            },
394         { "close",              ob_sbus_close           },
395 };
396
397 struct sbus_offset {
398     int slot, type;
399     uint64_t base;
400     unsigned long size;
401 };
402
403 static const struct sbus_offset sbus_offsets_ss5[SBUS_SLOTS] = {
404     { 0, 0, 0x20000000, 0x10000000,},
405     { 1, 0, 0x30000000, 0x10000000,},
406     { 2, 0, 0x40000000, 0x10000000,},
407     { 3, 0, 0x50000000, 0x10000000,},
408     { 4, 0, 0x60000000, 0x10000000,},
409     { 5, 0, 0x70000000, 0x10000000,},
410 };
411
412 /* Shared with ss600mp */
413 static const struct sbus_offset sbus_offsets_ss10[SBUS_SLOTS] = {
414     { 0, 0, 0xe00000000ULL, 0x10000000,},
415     { 1, 0, 0xe10000000ULL, 0x10000000,},
416     { 2, 0, 0xe20000000ULL, 0x10000000,},
417     { 3, 0, 0xe30000000ULL, 0x10000000,},
418     [0xf] = { 0xf, 0, 0xef0000000ULL, 0x10000000,},
419 };
420
421 static void
422 ob_add_sbus_range(const struct sbus_offset *range, int notfirst)
423 {
424     if (!notfirst) {
425         push_str("/iommu/sbus");
426         fword("find-device");
427     }
428     PUSH(range->slot);
429     fword("encode-int");
430     if (notfirst)
431         fword("encode+");
432     PUSH(range->type);
433     fword("encode-int");
434     fword("encode+");
435     PUSH(range->base >> 32);
436     fword("encode-int");
437     fword("encode+");
438     PUSH(range->base & 0xffffffff);
439     fword("encode-int");
440     fword("encode+");
441     PUSH(range->size);
442     fword("encode-int");
443     fword("encode+");
444 }
445
446 static int
447 ob_sbus_init_ss5(void)
448 {
449     unsigned int slot;
450     int notfirst = 0;
451
452     for (slot = 0; slot < SBUS_SLOTS; slot++) {
453         if (sbus_offsets_ss5[slot].size > 0)
454             ob_add_sbus_range(&sbus_offsets_ss5[slot], notfirst++);
455     }
456     push_str("ranges");
457     fword("property");
458
459     for (slot = 0; slot < SBUS_SLOTS; slot++) {
460         if (sbus_offsets_ss5[slot].size > 0)
461             sbus_probe_slot_ss5(slot, sbus_offsets_ss5[slot].base);
462     }
463
464     return 0;
465 }
466
467 static int
468 ob_sbus_init_ss10(void)
469 {
470     unsigned int slot;
471     int notfirst = 0;
472
473     for (slot = 0; slot < SBUS_SLOTS; slot++) {
474         if (sbus_offsets_ss10[slot].size > 0)
475             ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
476     }
477     push_str("ranges");
478     fword("property");
479
480     for (slot = 0; slot < SBUS_SLOTS; slot++) {
481         if (sbus_offsets_ss10[slot].size > 0)
482             sbus_probe_slot_ss10(slot, sbus_offsets_ss10[slot].base);
483     }
484
485     return 0;
486 }
487
488 static int
489 ob_sbus_init_ss600mp(void)
490 {
491     unsigned int slot;
492     int notfirst = 0;
493
494     for (slot = 0; slot < SBUS_SLOTS; slot++) {
495         if (sbus_offsets_ss10[slot].size > 0)
496             ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
497     }
498     push_str("ranges");
499     fword("property");
500
501     for (slot = 0; slot < SBUS_SLOTS; slot++) {
502         if (sbus_offsets_ss10[slot].size > 0)
503             sbus_probe_slot_ss600mp(slot, sbus_offsets_ss10[slot].base);
504     }
505
506     return 0;
507 }
508
509 int ob_sbus_init(uint64_t base, int machine_id)
510 {
511     ob_sbus_node_init(base);
512
513     switch (machine_id) {
514     case 66:
515         return ob_sbus_init_ss600mp();
516     case 64 ... 65:
517         return ob_sbus_init_ss10();
518     case 32 ... 63:
519         return ob_sbus_init_ss5();
520     default:
521         return -1;
522     }
523 }