--- trunk/src/devices/dev_gt.c 2007/10/08 16:18:38 12 +++ trunk/src/devices/dev_gt.c 2007/10/08 16:22:56 44 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2005 Anders Gavare. All rights reserved. + * Copyright (C) 2003-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,16 +25,13 @@ * SUCH DAMAGE. * * - * $Id: dev_gt.c,v 1.26 2005/08/05 07:50:37 debug Exp $ + * $Id: dev_gt.c,v 1.53 2007/06/16 05:09:55 debug Exp $ * - * Galileo Technology GT-64xxx PCI controller. + * COMMENT: Galileo Technology GT-64xxx PCI controller * * GT-64011 Used in Cobalt machines. * GT-64120 Used in evbmips machines (Malta). - * - * TODO: This more or less just a dummy device, so far. It happens to work - * with NetBSD/cobalt and /evbmips, and in some cases it might happen - * to work with Linux as well, but don't rely on it for anything else. + * GT-64260 Used in mvmeppc machines. */ #include @@ -44,120 +41,158 @@ #include "bus_pci.h" #include "cpu.h" #include "devices.h" +#include "interrupt.h" #include "machine.h" #include "memory.h" #include "misc.h" +#include "timer.h" + +#include "gtreg.h" #define TICK_SHIFT 14 /* #define debug fatal */ -#define PCI_VENDOR_GALILEO 0x11ab /* Galileo Technology */ -#define PCI_PRODUCT_GALILEO_GT64011 0x4146 /* GT-64011 System Controller */ -#define PCI_PRODUCT_GALILEO_GT64120 0x4620 /* GT-64120 */ +#define PCI_PRODUCT_GALILEO_GT64011 0x4146 /* GT-64011 */ +#define PCI_PRODUCT_GALILEO_GT64120 0x4620 /* GT-64120 */ +#define PCI_PRODUCT_GALILEO_GT64260 0x6430 /* GT-64260 */ + struct gt_data { - int irqnr; - int pciirq; - int type; + int type; + + struct timer *timer; + struct interrupt timer0_irq; + int interrupt_hz; + int pending_timer0_interrupts; + + /* Address decode registers: */ + uint32_t decode[GT_N_DECODE_REGS]; - struct pci_data *pci_data; + struct pci_data *pci_data; }; /* - * dev_gt_tick(): + * timer_tick(): + * + * Called d->interrupt_hz times per (real-world) second. */ -void dev_gt_tick(struct cpu *cpu, void *extra) +static void timer_tick(struct timer *timer, void *extra) { - struct gt_data *gt_data = extra; + struct gt_data *d = extra; + d->pending_timer0_interrupts ++; +} + - cpu_interrupt(cpu, gt_data->irqnr); +DEVICE_TICK(gt) +{ + struct gt_data *d = extra; + if (d->pending_timer0_interrupts > 0) + INTERRUPT_ASSERT(d->timer0_irq); } -/* - * dev_gt_access(): - */ -int dev_gt_access(struct cpu *cpu, struct memory *mem, uint64_t relative_addr, - unsigned char *data, size_t len, int writeflag, void *extra) +DEVICE_ACCESS(gt) { - uint64_t idata = 0, odata = 0; - int i, asserted; struct gt_data *d = extra; + uint64_t idata = 0, odata = 0; + int bus, dev, func, reg; + size_t i; - idata = memory_readmax64(cpu, data, len); + if (writeflag == MEM_WRITE) + idata = memory_readmax64(cpu, data, len); switch (relative_addr) { - case 0x48: - switch (d->type) { - case PCI_PRODUCT_GALILEO_GT64120: - /* - * This is needed for Linux on Malta, according - * to Alec Voropay. (TODO: Remove this hack when - * things have stabilized.) - */ - if (writeflag == MEM_READ) { - odata = 0x18000000 >> 21; - debug("[ gt: read from 0x48: 0x%08x ]\n", - (int)odata); - } - break; - default: - fatal("[ gt: access to 0x48? (type %i) ]\n", d->type); + case GT_PCI0IOLD_OFS: + case GT_PCI0IOHD_OFS: + case GT_PCI0M0LD_OFS: + case GT_PCI0M0HD_OFS: + case GT_PCI0M1LD_OFS: + case GT_PCI0M1HD_OFS: + case GT_PCI0IOREMAP_OFS: + case GT_PCI0M0REMAP_OFS: + case GT_PCI0M1REMAP_OFS: + if (writeflag == MEM_READ) { + odata = d->decode[relative_addr / 8]; + debug("[ gt: read from offset 0x%x: 0x%x ]\n", + (int)relative_addr, (int)odata); + } else { + d->decode[relative_addr / 8] = idata; + fatal("[ gt: write to offset 0x%x: 0x%x (TODO) ]\n", + (int)relative_addr, (int)idata); } break; - case 0xc18: + case GT_PCI0_CMD_OFS: if (writeflag == MEM_WRITE) { - debug("[ gt: write to 0xc18: 0x%08x ]\n", (int)idata); - return 1; + debug("[ gt: write to GT_PCI0_CMD: 0x%08x (TODO) ]\n", + (int)idata); } else { - odata = 0xffffffffULL; - /* ??? interrupt something... */ + debug("[ gt: read from GT_PCI0_CMD (0x%08x) (TODO) ]\n", + (int)odata); + } + break; -/* - * TODO: Remove this hack when things have stabilized. - */ -odata = 0x00000100; /* netbsd/cobalt cobalt/machdep.c:cpu_intr() */ + case GT_INTR_CAUSE: + if (writeflag == MEM_WRITE) { + debug("[ gt: write to GT_INTR_CAUSE: 0x%08x ]\n", + (int)idata); + return 1; + } else { + odata = GTIC_T0EXP; + INTERRUPT_DEASSERT(d->timer0_irq); -cpu_interrupt_ack(cpu, d->irqnr); + if (d->pending_timer0_interrupts > 0) + d->pending_timer0_interrupts --; - debug("[ gt: read from 0xc18 (0x%08x) ]\n", (int)odata); + debug("[ gt: read from GT_INTR_CAUSE (0x%08x) ]\n", + (int)odata); } break; - case 0xc34: /* GT_PCI0_INTR_ACK */ - /* - * Ugly hack, which works for at least evbmips/Malta: - */ - asserted = - (cpu->machine->md_int.isa_pic_data.pic1->irr & - ~cpu->machine->md_int.isa_pic_data.pic1->ier) | - ((cpu->machine->md_int.isa_pic_data.pic2->irr & - ~cpu->machine->md_int.isa_pic_data.pic2->ier) << 8); - odata = 7; /* "Spurious interrupt" defaults to 7. */ - for (i=0; i<16; i++) - if ((asserted >> i) & 1) { - odata = i; - break; - } + case GT_PCI0_INTR_ACK: + odata = cpu->machine->isa_pic_data.last_int; + /* TODO: Actually ack the interrupt? */ break; - case 0xcf8: /* PCI ADDR */ - case 0xcfc: /* PCI DATA */ + case GT_TIMER_CTRL: if (writeflag == MEM_WRITE) { - bus_pci_access(cpu, mem, relative_addr, &idata, - writeflag, d->pci_data); - } else { - bus_pci_access(cpu, mem, relative_addr, &odata, - writeflag, d->pci_data); + if (idata & ENTC0) { + /* TODO: Don't hardcode this. */ + d->interrupt_hz = 100; + if (d->timer == NULL) + d->timer = timer_add(d->interrupt_hz, + timer_tick, d); + else + timer_update_frequency(d->timer, + d->interrupt_hz); + } + } + break; + + case GT_PCI0_CFG_ADDR: + if (cpu->byte_order != EMUL_LITTLE_ENDIAN) { + fatal("[ gt: TODO: big endian PCI access ]\n"); + exit(1); } + bus_pci_decompose_1(idata, &bus, &dev, &func, ®); + bus_pci_setaddr(cpu, d->pci_data, bus, dev, func, reg); break; + + case GT_PCI0_CFG_DATA: + if (cpu->byte_order != EMUL_LITTLE_ENDIAN) { + fatal("[ gt: TODO: big endian PCI access ]\n"); + exit(1); + } + bus_pci_data_access(cpu, d->pci_data, writeflag == MEM_READ? + &odata : &idata, len, writeflag); + break; + default: - if (writeflag==MEM_READ) { + if (writeflag == MEM_READ) { debug("[ gt: read from addr 0x%x ]\n", (int)relative_addr); } else { @@ -176,89 +211,91 @@ /* - * pci_gt_rr_011(): - */ -static uint32_t pci_gt_rr_011(int reg) -{ - switch (reg) { - case 0x00: - return PCI_VENDOR_GALILEO + (PCI_PRODUCT_GALILEO_GT64011 << 16); - case 0x08: - return 0x06000001; /* Revision 1 */ - default: - return 0; - } -} - - -/* - * pci_gt_rr_120(): - */ -static uint32_t pci_gt_rr_120(int reg) -{ - switch (reg) { - case 0x00: - return PCI_VENDOR_GALILEO + (PCI_PRODUCT_GALILEO_GT64120 << 16); - case 0x08: - return 0x06000002; /* Revision 2? */ - default: - return 0; - } -} - - -/* - * pci_gt_init(): - */ -void pci_gt_init(struct machine *machine, struct memory *mem) -{ -} - - -/* * dev_gt_init(): * - * Initialize a GT device. Return a pointer to the pci_data used, so that - * the caller may add PCI devices. First, however, we add the GT device - * itself. + * Initialize a Gallileo PCI controller device. First, the controller itself + * is added to the bus, then a pointer to the bus is returned. */ struct pci_data *dev_gt_init(struct machine *machine, struct memory *mem, - uint64_t baseaddr, int irq_nr, int pciirq, int type) + uint64_t baseaddr, char *timer_irq_path, char *isa_irq_path, int type) { struct gt_data *d; + uint64_t pci_portbase = 0, pci_membase = 0; + uint64_t isa_portbase = 0, isa_membase = 0; + uint64_t pci_io_offset = 0, pci_mem_offset = 0; + char *gt_name = "NO"; - d = malloc(sizeof(struct gt_data)); - if (d == NULL) { - fprintf(stderr, "out of memory\n"); - exit(1); - } + CHECK_ALLOCATION(d = malloc(sizeof(struct gt_data))); memset(d, 0, sizeof(struct gt_data)); - d->irqnr = irq_nr; - d->pciirq = pciirq; - d->pci_data = bus_pci_init(pciirq); + + INTERRUPT_CONNECT(timer_irq_path, d->timer0_irq); switch (type) { case 11: + /* Cobalt: */ d->type = PCI_PRODUCT_GALILEO_GT64011; + gt_name = "gt64011"; + pci_io_offset = 0; + pci_mem_offset = 0; + pci_portbase = 0x10000000ULL; + pci_membase = 0x10100000ULL; + isa_portbase = 0x10000000ULL; + isa_membase = 0x10100000ULL; break; case 120: + /* EVBMIPS (Malta): */ d->type = PCI_PRODUCT_GALILEO_GT64120; + gt_name = "gt64120"; + pci_io_offset = 0; + pci_mem_offset = 0; + pci_portbase = 0x18000000ULL; + pci_membase = 0x10000000ULL; + isa_portbase = 0x18000000ULL; + isa_membase = 0x10000000ULL; + break; + case 260: + /* MVMEPPC (mvme5500): */ + d->type = PCI_PRODUCT_GALILEO_GT64260; + gt_name = "gt64260"; + pci_io_offset = 0; + pci_mem_offset = 0; + pci_portbase = 0x18000000ULL; + pci_membase = 0x10000000ULL; + isa_portbase = 0x18000000ULL; + isa_membase = 0x10000000ULL; break; - default:fatal("dev_gt_init(): type must be 11 or 120.\n"); + default:fatal("dev_gt_init(): unimplemented GT type (%i).\n", type); exit(1); } + + /* + * TODO: FIX THESE! Hardcoded numbers = bad. + */ + d->decode[GT_PCI0IOLD_OFS / 8] = pci_portbase >> 21; + d->decode[GT_PCI0IOHD_OFS / 8] = 0x40; + d->decode[GT_PCI0M0LD_OFS / 8] = 0x80; + d->decode[GT_PCI0M0HD_OFS / 8] = 0x3f; + d->decode[GT_PCI0M1LD_OFS / 8] = 0xc1; + d->decode[GT_PCI0M1HD_OFS / 8] = 0x5e; + d->decode[GT_PCI0IOREMAP_OFS / 8] = d->decode[GT_PCI0IOLD_OFS / 8]; + d->decode[GT_PCI0M0REMAP_OFS / 8] = d->decode[GT_PCI0M0LD_OFS / 8]; + d->decode[GT_PCI0M1REMAP_OFS / 8] = d->decode[GT_PCI0M1LD_OFS / 8]; + + d->pci_data = bus_pci_init(machine, + "TODO_gt_irq", pci_io_offset, pci_mem_offset, + pci_portbase, pci_membase, "TODO_pci_irqbase", + isa_portbase, isa_membase, isa_irq_path); + /* * According to NetBSD/cobalt: * pchb0 at pci0 dev 0 function 0: Galileo GT-64011 * System Controller, rev 1 */ - bus_pci_add(machine, d->pci_data, mem, 0, 0, 0, pci_gt_init, - d->type == PCI_PRODUCT_GALILEO_GT64011? - pci_gt_rr_011 : pci_gt_rr_120); + bus_pci_add(machine, d->pci_data, mem, 0, 0, 0, gt_name); memory_device_register(mem, "gt", baseaddr, DEV_GT_LENGTH, - dev_gt_access, d, MEM_DEFAULT, NULL); + dev_gt_access, d, DM_DEFAULT, NULL); machine_add_tickfunction(machine, dev_gt_tick, d, TICK_SHIFT); return d->pci_data;