These changes are the raw update to qemu-2.6.
[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 struct sbus_offset {
373     int slot, type;
374     uint64_t base;
375     unsigned long size;
376 };
377
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,},
385 };
386
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,},
394 };
395
396 static void
397 ob_add_sbus_range(const struct sbus_offset *range, int notfirst)
398 {
399     if (!notfirst) {
400         push_str("/iommu/sbus");
401         fword("find-device");
402     }
403     PUSH(range->slot);
404     fword("encode-int");
405     if (notfirst)
406         fword("encode+");
407     PUSH(range->type);
408     fword("encode-int");
409     fword("encode+");
410     PUSH(range->base >> 32);
411     fword("encode-int");
412     fword("encode+");
413     PUSH(range->base & 0xffffffff);
414     fword("encode-int");
415     fword("encode+");
416     PUSH(range->size);
417     fword("encode-int");
418     fword("encode+");
419 }
420
421 static int
422 ob_sbus_init_ss5(void)
423 {
424     unsigned int slot;
425     int notfirst = 0;
426
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++);
430     }
431     push_str("ranges");
432     fword("property");
433
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);
437     }
438
439     return 0;
440 }
441
442 static int
443 ob_sbus_init_ss10(void)
444 {
445     unsigned int slot;
446     int notfirst = 0;
447
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++);
451     }
452     push_str("ranges");
453     fword("property");
454
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);
458     }
459
460     return 0;
461 }
462
463 static int
464 ob_sbus_init_ss600mp(void)
465 {
466     unsigned int slot;
467     int notfirst = 0;
468
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++);
472     }
473     push_str("ranges");
474     fword("property");
475
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);
479     }
480
481     return 0;
482 }
483
484 int ob_sbus_init(uint64_t base, int machine_id)
485 {
486     ob_sbus_node_init(base);
487
488     switch (machine_id) {
489     case 66:
490         return ob_sbus_init_ss600mp();
491     case 64 ... 65:
492         return ob_sbus_init_ss10();
493     case 32 ... 63:
494         return ob_sbus_init_ss5();
495     default:
496         return -1;
497     }
498 }