diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/openbios/drivers/sbus.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/openbios/drivers/sbus.c')
-rw-r--r-- | qemu/roms/openbios/drivers/sbus.c | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/qemu/roms/openbios/drivers/sbus.c b/qemu/roms/openbios/drivers/sbus.c new file mode 100644 index 000000000..a9b26c0a0 --- /dev/null +++ b/qemu/roms/openbios/drivers/sbus.c @@ -0,0 +1,523 @@ +/* + * OpenBIOS SBus driver + * + * (C) 2004 Stefan Reinauer <stepan@openbios.org> + * (C) 2005 Ed Schouten <ed@fxq.nl> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 + * + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "kernel/kernel.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" +#include "drivers/drivers.h" +#include "libopenbios/ofmem.h" +#include "libopenbios/video.h" + +#define SBUS_REGS 0x28 +#define SBUS_SLOTS 16 +#define APC_REGS 0x10 +#define APC_OFFSET 0x0a000000ULL +#define CS4231_REGS 0x40 +#define CS4231_OFFSET 0x0c000000ULL +#define MACIO_ESPDMA 0x00400000ULL /* ESP DMA controller */ +#define MACIO_ESP 0x00800000ULL /* ESP SCSI */ +#define SS600MP_ESPDMA 0x00081000ULL +#define SS600MP_ESP 0x00080000ULL +#define SS600MP_LEBUFFER (SS600MP_ESPDMA + 0x10) // XXX should be 0x40000 +#define LEDMA_REGS 0x4 +#define LE_REGS 0x20 + +#ifdef CONFIG_DEBUG_SBUS +#define DPRINTF(fmt, args...) \ + do { printk(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +typedef struct le_private { + uint32_t *dmaregs; + uint32_t *regs; +} le_private_t; + +static void +ob_sbus_node_init(uint64_t base) +{ + void *regs; + + push_str("/iommu/sbus"); + fword("find-device"); + + PUSH(base >> 32); + fword("encode-int"); + PUSH(base & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(SBUS_REGS); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + regs = (void *)ofmem_map_io(base, SBUS_REGS); + PUSH((unsigned long)regs); + fword("encode-int"); + push_str("address"); + fword("property"); +} + +static void +ob_le_init(unsigned int slot, uint64_t base, unsigned long leoffset, unsigned long dmaoffset) +{ + le_private_t *le; + + le = malloc(sizeof(le_private_t)); + if (!le) { + DPRINTF("Can't allocate LANCE private structure\n"); + return; + } + + /* Get the IO region for DMA registers */ + le->dmaregs = (void *)ofmem_map_io(base + (uint64_t)dmaoffset, LEDMA_REGS); + if (le->dmaregs == NULL) { + DPRINTF("Can't map LANCE DMA registers\n"); + return; + } + + /* Now it appears that the Solaris kernel forgets to set up the LANCE DMA mapping + and so it must inherit the one from OpenBIOS. The symptom of this is that the + LANCE DMA base addr register is still zero, and so we start sending network + packets containing random areas of memory. + + The correct fix for this should be to use dvma_alloc() to grab a section of + memory and point the LANCE DMA buffers to use that instead; this gets + slightly further but still crashes. Time-consuming investigation on various + hacked versions of QEMU seems to indicate that Solaris always assumes the LANCE + DMA base address is fixed 0xff000000 when setting up the IOMMU for the LANCE + card. Hence we imitate this behaviour here. */ + le->dmaregs[3] = 0xff000000; + + push_str("/iommu/sbus/ledma"); + fword("find-device"); + PUSH(slot); + fword("encode-int"); + PUSH(dmaoffset); + fword("encode-int"); + fword("encode+"); + PUSH(0x00000020); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + /* Get the IO region for Lance registers */ + le->regs = (void *)ofmem_map_io(base + (uint64_t)leoffset, LE_REGS); + if (le->regs == NULL) { + DPRINTF("Can't map LANCE registers\n"); + return; + } + + push_str("/iommu/sbus/ledma/le"); + fword("find-device"); + PUSH(slot); + fword("encode-int"); + PUSH(leoffset); + fword("encode-int"); + fword("encode+"); + PUSH(0x00000004); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); +} + +uint16_t graphic_depth; + +static void +ob_tcx_init(unsigned int slot, const char *path) +{ + char buf[6]; + + printk("No display device located during SBus probe - falling back to internal TCX driver\n"); + + /* Make the sbus node the current instance and active package for probing */ + feval("active-package my-self"); + push_str("/iommu/sbus"); + feval("2dup find-device open-dev to my-self"); + + fword("new-device"); + PUSH(0); + PUSH(0); + snprintf(buf, 6, "%x,0", slot); + push_str(buf); + fword("set-args"); + feval("['] tcx-driver-fcode 2 cells + 1 byte-load"); + fword("finish-device"); + + /* Restore */ + feval("to my-self active-package!"); +} + +static void +ob_apc_init(unsigned int slot, unsigned long base) +{ + push_str("/iommu/sbus"); + fword("find-device"); + fword("new-device"); + + push_str("power-management"); + fword("device-name"); + + PUSH(slot); + fword("encode-int"); + PUSH(base); + fword("encode-int"); + fword("encode+"); + PUSH(APC_REGS); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + fword("finish-device"); +} + +static void +ob_cs4231_init(unsigned int slot) +{ + push_str("/iommu/sbus"); + fword("find-device"); + fword("new-device"); + + push_str("SUNW,CS4231"); + fword("device-name"); + + push_str("serial"); + fword("device-type"); + + PUSH(slot); + fword("encode-int"); + PUSH(CS4231_OFFSET); + fword("encode-int"); + fword("encode+"); + PUSH(CS4231_REGS); + fword("encode-int"); + fword("encode+"); + push_str("reg"); + fword("property"); + + PUSH(5); + fword("encode-int"); + PUSH(0); + fword("encode-int"); + fword("encode+"); + push_str("intr"); + fword("property"); + + PUSH(5); + fword("encode-int"); + push_str("interrupts"); + fword("property"); + + push_str("audio"); + fword("encode-string"); + push_str("alias"); + fword("property"); + + fword("finish-device"); +} + +static void +ob_macio_init(unsigned int slot, uint64_t base, unsigned long offset) +{ + // All devices were integrated to NCR89C100, see + // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt + + // NCR 53c9x, aka ESP. See + // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt +#ifdef CONFIG_DRIVER_ESP + ob_esp_init(slot, base, offset + MACIO_ESP, offset + MACIO_ESPDMA); +#endif + + // NCR 92C990, Am7990, Lance. See http://www.amd.com + ob_le_init(slot, base, offset + 0x00c00000, offset + 0x00400010); + + // Parallel port + //ob_bpp_init(base); +} + +static void +sbus_probe_self(unsigned int slot, unsigned long offset) +{ + /* Wrapper for calling probe-self in Forth. This is mainly because some + drivers don't handle properties correctly when the sbus node is set + as the current instance during probe. */ + char buf[6]; + + printk("Probing SBus slot %d offset %ld\n", slot, offset); + + /* Make the sbus node the current instance and active package for probing */ + feval("active-package my-self"); + push_str("/iommu/sbus"); + feval("open-dev to my-self"); + + PUSH(0); + PUSH(0); + snprintf(buf, 6, "%x,%lx", slot, offset); + push_str(buf); + fword("2dup"); + fword("probe-self-sbus"); + + /* Restore */ + feval("to my-self active-package!"); +} + +static int +sbus_probe_sucess(void) +{ + /* Return true if the last sbus_probe_self() resulted in + the successful detection and execution of FCode */ + fword("probe-fcode?"); + return POP(); +} + +static void +sbus_probe_slot_ss5(unsigned int slot, uint64_t base) +{ + /* Probe the slot */ + sbus_probe_self(slot, 0); + + /* If the device was successfully created by FCode then do nothing */ + if (sbus_probe_sucess()) { + return; + } + + switch(slot) { + case 3: // SUNW,tcx + ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx"); + break; + case 4: + // SUNW,CS4231 + ob_cs4231_init(slot); + // Power management (APC) + ob_apc_init(slot, APC_OFFSET); + break; + case 5: // MACIO: le, esp, bpp + ob_macio_init(slot, base, 0x08000000); + break; + default: + break; + } +} + +static void +sbus_probe_slot_ss10(unsigned int slot, uint64_t base) +{ + /* Probe the slot */ + sbus_probe_self(slot, 0); + + /* If the device was successfully created by FCode then do nothing */ + if (sbus_probe_sucess()) { + return; + } + + switch(slot) { + case 2: // SUNW,tcx + ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx"); + break; + case 0xf: // le, esp, bpp, power-management + ob_macio_init(slot, base, 0); + // Power management (APC) XXX should not exist + ob_apc_init(slot, APC_OFFSET); + break; + default: + break; + } +} + +static void +sbus_probe_slot_ss600mp(unsigned int slot, uint64_t base) +{ + /* Probe the slot */ + sbus_probe_self(slot, 0); + + /* If the device was successfully created by FCode then do nothing */ + if (sbus_probe_sucess()) { + return; + } + + switch(slot) { + case 2: // SUNW,tcx + ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx"); + break; + case 0xf: // le, esp, bpp, power-management +#ifdef CONFIG_DRIVER_ESP + ob_esp_init(slot, base, SS600MP_ESP, SS600MP_ESPDMA); +#endif + // NCR 92C990, Am7990, Lance. See http://www.amd.com + ob_le_init(slot, base, 0x00060000, SS600MP_LEBUFFER); + // Power management (APC) XXX should not exist + ob_apc_init(slot, APC_OFFSET); + break; + default: + break; + } +} + +static void +ob_sbus_open(void) +{ + int ret=1; + RET ( -ret ); +} + +static void +ob_sbus_close(void) +{ + selfword("close-deblocker"); +} + +static void +ob_sbus_initialize(void) +{ +} + + +NODE_METHODS(ob_sbus_node) = { + { NULL, ob_sbus_initialize }, + { "open", ob_sbus_open }, + { "close", ob_sbus_close }, +}; + +struct sbus_offset { + int slot, type; + uint64_t base; + unsigned long size; +}; + +static const struct sbus_offset sbus_offsets_ss5[SBUS_SLOTS] = { + { 0, 0, 0x20000000, 0x10000000,}, + { 1, 0, 0x30000000, 0x10000000,}, + { 2, 0, 0x40000000, 0x10000000,}, + { 3, 0, 0x50000000, 0x10000000,}, + { 4, 0, 0x60000000, 0x10000000,}, + { 5, 0, 0x70000000, 0x10000000,}, +}; + +/* Shared with ss600mp */ +static const struct sbus_offset sbus_offsets_ss10[SBUS_SLOTS] = { + { 0, 0, 0xe00000000ULL, 0x10000000,}, + { 1, 0, 0xe10000000ULL, 0x10000000,}, + { 2, 0, 0xe20000000ULL, 0x10000000,}, + { 3, 0, 0xe30000000ULL, 0x10000000,}, + [0xf] = { 0xf, 0, 0xef0000000ULL, 0x10000000,}, +}; + +static void +ob_add_sbus_range(const struct sbus_offset *range, int notfirst) +{ + if (!notfirst) { + push_str("/iommu/sbus"); + fword("find-device"); + } + PUSH(range->slot); + fword("encode-int"); + if (notfirst) + fword("encode+"); + PUSH(range->type); + fword("encode-int"); + fword("encode+"); + PUSH(range->base >> 32); + fword("encode-int"); + fword("encode+"); + PUSH(range->base & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(range->size); + fword("encode-int"); + fword("encode+"); +} + +static int +ob_sbus_init_ss5(void) +{ + unsigned int slot; + int notfirst = 0; + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss5[slot].size > 0) + ob_add_sbus_range(&sbus_offsets_ss5[slot], notfirst++); + } + push_str("ranges"); + fword("property"); + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss5[slot].size > 0) + sbus_probe_slot_ss5(slot, sbus_offsets_ss5[slot].base); + } + + return 0; +} + +static int +ob_sbus_init_ss10(void) +{ + unsigned int slot; + int notfirst = 0; + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++); + } + push_str("ranges"); + fword("property"); + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + sbus_probe_slot_ss10(slot, sbus_offsets_ss10[slot].base); + } + + return 0; +} + +static int +ob_sbus_init_ss600mp(void) +{ + unsigned int slot; + int notfirst = 0; + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++); + } + push_str("ranges"); + fword("property"); + + for (slot = 0; slot < SBUS_SLOTS; slot++) { + if (sbus_offsets_ss10[slot].size > 0) + sbus_probe_slot_ss600mp(slot, sbus_offsets_ss10[slot].base); + } + + return 0; +} + +int ob_sbus_init(uint64_t base, int machine_id) +{ + ob_sbus_node_init(base); + + switch (machine_id) { + case 66: + return ob_sbus_init_ss600mp(); + case 64 ... 65: + return ob_sbus_init_ss10(); + case 32 ... 63: + return ob_sbus_init_ss5(); + default: + return -1; + } +} |