--- trunk/src/devices/dev_lca.c 2007/10/08 16:19:56 24 +++ trunk/src/devices/dev_lca.c 2007/10/08 16:20:58 32 @@ -25,9 +25,9 @@ * SUCH DAMAGE. * * - * $Id: dev_lca.c,v 1.1 2006/05/30 19:49:39 debug Exp $ + * $Id: dev_lca.c,v 1.4 2006/08/29 15:55:10 debug Exp $ * - * LCA bus (for Alpha machines). + * LCA PCI bus (for Alpha machines). */ #include @@ -35,6 +35,7 @@ #include #include "bus_isa.h" +#include "bus_pci.h" #include "cpu.h" #include "device.h" #include "emul.h" @@ -50,10 +51,82 @@ struct lca_data { - int dummy; + struct pci_data *pci_data; + + uint64_t ioc_conf; + uint64_t tlb_enable; + uint64_t window_base_0; + uint64_t window_mask_0; + uint64_t window_t_base_0; + uint64_t window_base_1; + uint64_t window_mask_1; + uint64_t window_t_base_1; }; +DEVICE_ACCESS(lca_pci_conf) +{ + uint64_t idata = 0, odata = 0; + int tag, bus, dev, func, reg; + struct lca_data *d = extra; + + if (writeflag == MEM_WRITE) + idata = memory_readmax64(cpu, data, len); + + /* + * 1. Decompose the address into a tag. + * + * According to NetBSD's lca_pci.c, the address is composed like this: + * + * addr = tag << 5 | (regoffset & ~0x03) << 5 | 0x3 << 3 + */ + reg = (relative_addr >> 5) & 0xfc; + tag = (relative_addr >> 5) & ~0xff; + + /* + * 2. Decompose the tag into bus, dev, and func. + * + * The tag can be constructed in one of two ways. On the primary + * bus (nr 0): + * + * tag = (1 << (device + 11)) | (function << 8); + * + * and on other busses, the tag is a normal: + * + * tag = (bus << 16) | (device << 11) | (function << 8) + */ + /* printf("tag = 0x%x\n", (int)tag); */ + bus = d->ioc_conf & 1; + + if (bus == 0) { + for (dev=0; dev<21; dev++) + if (tag & (0x800 << dev)) + break; + if (dev >= 21) { + /* fatal("[ LCA: No bus 0 device? TODO ]\n"); + exit(1); */ + dev = 0; + } + } else { + fatal("TODO. Non-zero bus.\n"); + exit(1); + } + + func = (tag >> 8) & 7; + /* printf("bus=%i dev=%i func=%i reg=%i\n", bus,dev,func,reg); */ + + /* Pass PCI accesses onto bus_pci: */ + bus_pci_setaddr(cpu, d->pci_data, bus, dev, func, reg); + bus_pci_data_access(cpu, d->pci_data, writeflag == MEM_READ? + &odata : &idata, len, writeflag); + + if (writeflag == MEM_READ) + memory_writemax64(cpu, data, len, odata); + + return 1; +} + + DEVICE_ACCESS(lca_isa) { int ofs, i; @@ -84,6 +157,149 @@ } +DEVICE_ACCESS(lca_ioc) +{ + uint64_t idata = 0, odata = 0; + struct lca_data *d = extra; + + if (writeflag == MEM_WRITE) + idata = memory_readmax64(cpu, data, len); + + switch (relative_addr + LCA_IOC_BASE) { + + case LCA_IOC_BASE: + /* Ignore? Linux reads from the base at startup. */ + break; + + case LCA_IOC_CONF: + if (writeflag == MEM_READ) { + odata = d->ioc_conf; + } else { + d->ioc_conf = idata; + /* Only bit 0 is implemented so far, the PCI bus 0 vs + bus non-0 selection bit. */ + if (idata & ~1) { + fatal("TODO: Write to unimplemented bit of" + " IOC_CONF: 0x%x\n", (int)idata); + exit(1); + } + } + break; + + case LCA_IOC_TBIA: + /* TLB Invalidate All. */ + /* TODO: For now, let's just ignore it. */ + break; + + case LCA_IOC_TB_ENA: + if (writeflag == MEM_READ) { + odata = d->tlb_enable; + } else { + d->tlb_enable = idata; + /* TODO: Actually implement this. */ + if (idata & ~IOC_TB_ENA_TEN) { + fatal("TODO: LCA_IOC_TB_ENA value " + " (0x%"PRIx64") has unimplemented " + "bits.\n", (uint64_t)idata); + exit(1); + } + } + break; + + case LCA_IOC_W_BASE0: + if (writeflag == MEM_READ) { + odata = d->window_base_0; + } else { + d->window_base_0 = idata; + /* TODO: Actually implement this. */ + if (idata != 0ULL && idata != 0x300800000ULL) { + fatal("TODO: LCA_IOC_W_BASE0 value differs" + " (0x%"PRIx64") from the only implemented" + " values\n", (uint64_t)idata); + exit(1); + } + } + break; + + case LCA_IOC_W_MASK0: + if (writeflag == MEM_READ) { + odata = d->window_mask_0; + } else { + d->window_mask_0 = idata; + /* TODO: Actually implement this. */ + if (idata != 0x700000ULL) { + fatal("TODO: LCA_IOC_W_MASK0 value differs" + " (0x%"PRIx64") from the only implemented" + " value\n", (uint64_t)idata); + exit(1); + } + } + break; + + case LCA_IOC_W_T_BASE0: + if (writeflag == MEM_READ) { + odata = d->window_t_base_0; + } else { + d->window_t_base_0 = idata; + /* TODO: Actually implement this. */ + } + break; + + case LCA_IOC_W_BASE1: + if (writeflag == MEM_READ) { + odata = d->window_base_1; + } else { + d->window_base_1 = idata; + /* TODO: Actually implement this. */ + if (idata != 0x240000000ULL) { + fatal("TODO: LCA_IOC_W_BASE1 value differs" + " (0x%"PRIx64") from the only implemented" + " value\n", (uint64_t)idata); + exit(1); + } + } + break; + + case LCA_IOC_W_MASK1: + if (writeflag == MEM_READ) { + odata = d->window_mask_1; + } else { + d->window_mask_1 = idata; + /* TODO: Actually implement this. */ + if (idata != 0x3ff00000ULL) { + fatal("TODO: LCA_IOC_W_MASK1 value differs" + " (0x%"PRIx64") from the only implemented" + " value\n", (uint64_t)idata); + exit(1); + } + } + break; + + case LCA_IOC_W_T_BASE1: + if (writeflag == MEM_READ) { + odata = d->window_t_base_1; + } else { + d->window_t_base_1 = idata; + /* TODO: Actually implement this. */ + } + break; + + default:fatal("[ lca_ioc: unimplemented %s to offset 0x%x", + writeflag == MEM_WRITE? "write" : "read", (int) + relative_addr); + if (writeflag == MEM_WRITE) + fatal(": 0x%x", (int)idata); + fatal(" ]\n"); + exit(1); + } + + if (writeflag == MEM_READ) + memory_writemax64(cpu, data, len, odata); + + return 1; +} + + DEVINIT(lca) { struct lca_data *d = malloc(sizeof(struct lca_data)); @@ -93,10 +309,35 @@ } memset(d, 0, sizeof(struct lca_data)); + /* Register a PCI bus: */ + d->pci_data = bus_pci_init( + devinit->machine, + 0 /* pciirq: TODO */, + LCA_PCI_SIO, /* pci device io offset */ + 0x00000000, /* pci device mem offset: TODO */ + 0x00000000, /* PCI portbase: TODO */ + 0x00000000, /* PCI membase: TODO */ + 0x00000000, /* PCI irqbase: TODO */ + LCA_ISA_BASE, /* ISA portbase */ + LCA_ISA_MEMBASE, /* ISA membase */ + 8); /* ISA irqbase: TODO */ + + /* Add the "sio0" controller (as seen by NetBSD): */ + bus_pci_add(devinit->machine, d->pci_data, devinit->machine->memory, + 0, 7, 0, "i82378zb"); + + memory_device_register(devinit->machine->memory, "lca_pci_conf", + LCA_PCI_CONF, 0x20000000, dev_lca_pci_conf_access, (void *)d, + DM_DEFAULT, NULL); + memory_device_register(devinit->machine->memory, "lca_isa", LCA_PCI_SIO, 0x10000 << 5, dev_lca_isa_access, (void *)d, DM_DEFAULT, NULL); + memory_device_register(devinit->machine->memory, "lca_ioc", + LCA_IOC_BASE, 0x20000000, dev_lca_ioc_access, (void *)d, + DM_DEFAULT, NULL); + /* TODO: IRQs etc. */ bus_isa_init(devinit->machine, BUS_ISA_IDE0 | BUS_ISA_IDE1, LCA_ISA_BASE, LCA_ISA_MEMBASE, 32, 48);