/* * Copyright (C) 2005-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: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * $Id: dev_i80321.c,v 1.23 2007/06/15 19:11:15 debug Exp $ * * COMMENT: Intel i80321 (ARM) core functionality * * o) Interrupt controller * o) Timer * o) PCI controller * o) Memory controller * * TODO: * o) LOTS of things left to implement. * o) This is hardcoded for little endian emulation. */ #include #include #include #include "bus_pci.h" #include "cpu.h" #include "device.h" #include "machine.h" #include "memory.h" #include "misc.h" #include "timer.h" #include "i80321reg.h" #define TICK_SHIFT 15 #define DEV_I80321_LENGTH VERDE_PMMR_SIZE struct i80321_data { /* Interrupt Controller */ struct interrupt irq; uint32_t *status; /* Note: these point to i80321_isrc */ uint32_t *enable; /* and i80321_inten in the CPU! */ /* Timer: */ struct timer *timer; double hz; int pending_tmr0_interrupts; /* PCI Controller: */ uint32_t pci_addr; struct pci_data *pci_bus; /* Memory Controller: */ uint32_t mcu_reg[0x100 / sizeof(uint32_t)]; }; static void i80321_assert(struct i80321_data *d, uint32_t linemask) { *d->status |= linemask; if (*d->status & *d->enable) INTERRUPT_ASSERT(d->irq); } static void i80321_deassert(struct i80321_data *d, uint32_t linemask) { *d->status &= ~linemask; if (!(*d->status & *d->enable)) INTERRUPT_DEASSERT(d->irq); } /* * i80321_interrupt_assert(): * i80321_interrupt_deassert(): * * Called whenever an i80321 interrupt is asserted/deasserted. */ void i80321_interrupt_assert(struct interrupt *interrupt) { i80321_assert(interrupt->extra, interrupt->line); } void i80321_interrupt_deassert(struct interrupt *interrupt) { struct i80321_data *d = interrupt->extra; /* Ack. timer interrupts: */ if (interrupt->line == 1 << 9 && d->pending_tmr0_interrupts > 0) d->pending_tmr0_interrupts --; i80321_deassert(d, interrupt->line); } /* TMR0 ticks, called d->hz times per second. */ static void tmr0_tick(struct timer *t, void *extra) { struct i80321_data *d = extra; d->pending_tmr0_interrupts ++; } DEVICE_TICK(i80321) { struct i80321_data *d = extra; if (cpu->cd.arm.tmr0 & TMRx_ENABLE && d->pending_tmr0_interrupts > 0) { i80321_assert(d, 1 << 9); cpu->cd.arm.tisr |= TISR_TMR0; } else { i80321_deassert(d, 1 << 9); cpu->cd.arm.tisr &= ~TISR_TMR0; } } DEVICE_ACCESS(i80321) { struct i80321_data *d = extra; uint64_t idata = 0, odata = 0; char *n = NULL; int bus, dev, func, reg; if (writeflag == MEM_WRITE) idata = memory_readmax64(cpu, data, len); /* PCI configuration space: */ if (relative_addr >= 0x100 && relative_addr < 0x140) { /* TODO */ goto ret; } /* MCU registers: */ if (relative_addr >= VERDE_MCU_BASE && relative_addr < VERDE_MCU_BASE + VERDE_MCU_SIZE) { int regnr = (relative_addr - VERDE_MCU_BASE) / sizeof(uint32_t); if (writeflag == MEM_WRITE) d->mcu_reg[regnr] = idata; else odata = d->mcu_reg[regnr]; } switch (relative_addr) { /* Address Translation Unit: */ case VERDE_ATU_BASE + ATU_IALR0: case VERDE_ATU_BASE + ATU_IATVR0: case VERDE_ATU_BASE + ATU_IALR1: case VERDE_ATU_BASE + ATU_IALR2: case VERDE_ATU_BASE + ATU_IATVR2: case VERDE_ATU_BASE + ATU_OIOWTVR: case VERDE_ATU_BASE + ATU_OMWTVR0: case VERDE_ATU_BASE + ATU_OUMWTVR0: case VERDE_ATU_BASE + ATU_OMWTVR1: case VERDE_ATU_BASE + ATU_OUMWTVR1: /* Ignoring these for now. TODO */ break; case VERDE_ATU_BASE + ATU_ATUCR: /* ATU configuration register; ignored for now. TODO */ break; case VERDE_ATU_BASE + ATU_PCSR: /* TODO: Temporary hack to allow NetBSD/evbarm to reboot itself. Should be rewritten as soon as possible! */ if (writeflag == MEM_WRITE && idata == 0x30) { int j; for (j=0; jmachine->ncpus; j++) cpu->machine->cpus[j]->running = 0; cpu->machine->exit_without_entering_debugger = 1; } break; case VERDE_ATU_BASE + ATU_ATUIMR: case VERDE_ATU_BASE + ATU_IABAR3: case VERDE_ATU_BASE + ATU_IAUBAR3: case VERDE_ATU_BASE + ATU_IALR3: case VERDE_ATU_BASE + ATU_IATVR3: /* Ignoring these for now. TODO */ break; case VERDE_ATU_BASE + ATU_OCCAR: /* PCI address */ if (writeflag == MEM_WRITE) { d->pci_addr = idata; bus_pci_decompose_1(idata, &bus, &dev, &func, ®); bus = 0; /* NOTE */ bus_pci_setaddr(cpu, d->pci_bus, bus, dev, func, reg); } else { odata = d->pci_addr; } break; case VERDE_ATU_BASE + ATU_OCCDR: case VERDE_ATU_BASE + ATU_OCCDR + 1: case VERDE_ATU_BASE + ATU_OCCDR + 2: case VERDE_ATU_BASE + ATU_OCCDR + 3: /* PCI data */ if (writeflag == MEM_READ) { uint64_t tmp; bus_pci_data_access(cpu, d->pci_bus, &tmp, sizeof(uint32_t), MEM_READ); switch (relative_addr) { case VERDE_ATU_BASE + ATU_OCCDR + 1: odata = tmp >> 8; break; case VERDE_ATU_BASE + ATU_OCCDR + 2: odata = tmp >> 16; break; case VERDE_ATU_BASE + ATU_OCCDR + 3: odata = tmp >> 24; break; default:odata = tmp; } } else { uint64_t tmp; unsigned int i; int r = relative_addr - (VERDE_ATU_BASE + ATU_OCCDR); bus_pci_data_access(cpu, d->pci_bus, &tmp, sizeof(uint32_t), MEM_READ); for (i=0; i> (i*8); tmp &= ~(0xff << ((r+i)*8)); tmp |= b << ((r+i)*8); } tmp &= 0xffffffff; /* needed because << is 32-bit */ bus_pci_data_access(cpu, d->pci_bus, &tmp, sizeof(uint32_t), MEM_WRITE); } break; case VERDE_ATU_BASE + ATU_PCIXSR: odata = 0; /* TODO */ break; /* Memory Controller Unit: */ case VERDE_MCU_BASE + MCU_SDBR: n = "MCU_SDBR"; break; case VERDE_MCU_BASE + MCU_SBR0: n = "MCU_SBR0"; break; case VERDE_MCU_BASE + MCU_SBR1: n = "MCU_SBR1"; break; default:if (writeflag == MEM_READ) { fatal("[ i80321: read from 0x%x ]\n", (int)relative_addr); } else { fatal("[ i80321: write to 0x%x: 0x%llx ]\n", (int)relative_addr, (long long)idata); } } if (n != NULL) { if (writeflag == MEM_READ) { debug("[ i80321: read from %s ]\n", n); } else { debug("[ i80321: write to %s: 0x%llx ]\n", n, (long long)idata); } } ret: if (writeflag == MEM_READ) memory_writemax64(cpu, data, len, odata); return 1; } DEVINIT(i80321) { struct i80321_data *d; uint32_t memsize = devinit->machine->physical_ram_in_mb * 1048576; uint32_t base; char tmpstr[300]; struct cpu *cpu = devinit->machine->cpus[devinit-> machine->bootstrap_cpu]; int i; CHECK_ALLOCATION(d = malloc(sizeof(struct i80321_data))); memset(d, 0, sizeof(struct i80321_data)); /* Connect to the CPU interrupt pin: */ INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); /* Register 32 i80321 interrupts: */ for (i=0; i<32; i++) { struct interrupt template; char tmpstr[300]; snprintf(tmpstr, sizeof(tmpstr), "%s.i80321.%i", devinit->interrupt_path, i); memset(&template, 0, sizeof(template)); template.line = 1 << i; template.name = tmpstr; template.extra = d; template.interrupt_assert = i80321_interrupt_assert; template.interrupt_deassert = i80321_interrupt_deassert; interrupt_handler_register(&template); /* * Connect the CPU's TMR0 and TMR1 interrupts to these * i80321 timer interrupts (nr 9 and 10): */ if (i == 9) INTERRUPT_CONNECT(tmpstr, cpu->cd.arm.tmr0_irq); if (i == 10) INTERRUPT_CONNECT(tmpstr, cpu->cd.arm.tmr1_irq); } d->status = &cpu->cd.arm.i80321_isrc; d->enable = &cpu->cd.arm.i80321_inten; d->mcu_reg[MCU_SDBR / sizeof(uint32_t)] = base = 0xa0000000; d->mcu_reg[MCU_SBR0 / sizeof(uint32_t)] = (base + memsize) >> 25; d->mcu_reg[MCU_SBR1 / sizeof(uint32_t)] = (base + memsize) >> 25; snprintf(tmpstr, sizeof(tmpstr), "%s.i80321", devinit->interrupt_path); d->pci_bus = bus_pci_init(devinit->machine, tmpstr /* pciirq */, 0x90000000 /* TODO: pci_io_offset */, 0x90010000 /* TODO: pci_mem_offset */, 0xffff0000 /* TODO: pci_portbase */, 0x00000000 /* TODO: pci_membase */, tmpstr /* pci_irqbase */, 0x90000000 /* TODO: isa_portbase */, 0x90010000 /* TODO: isa_membase */, "TODO: isa_irqbase" /* TODO: isa_irqbase */); memory_device_register(devinit->machine->memory, devinit->name, devinit->addr, DEV_I80321_LENGTH, dev_i80321_access, d, DM_DEFAULT, NULL); /* TODO: Don't hardcode to 100 Hz! */ d->hz = 100; d->timer = timer_add(d->hz, tmr0_tick, d); machine_add_tickfunction(devinit->machine, dev_i80321_tick, d, TICK_SHIFT); devinit->return_ptr = d->pci_bus; return 1; }