diff options
Diffstat (limited to 'hw/sh_pci.c')
-rw-r--r-- | hw/sh_pci.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/hw/sh_pci.c b/hw/sh_pci.c new file mode 100644 index 0000000..e99d8db --- /dev/null +++ b/hw/sh_pci.c @@ -0,0 +1,161 @@ +/* + * SuperH on-chip PCIC emulation. + * + * Copyright (c) 2008 Takashi YOSHII + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "sysbus.h" +#include "sh.h" +#include "pci.h" +#include "pci_host.h" +#include "bswap.h" + +typedef struct SHPCIState { + SysBusDevice busdev; + PCIBus *bus; + PCIDevice *dev; + qemu_irq irq[4]; + int memconfig; + uint32_t par; + uint32_t mbr; + uint32_t iobr; +} SHPCIState; + +static void sh_pci_reg_write (void *p, target_phys_addr_t addr, uint32_t val) +{ + SHPCIState *pcic = p; + switch(addr) { + case 0 ... 0xfc: + cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val); + break; + case 0x1c0: + pcic->par = val; + break; + case 0x1c4: + pcic->mbr = val & 0xff000001; + break; + case 0x1c8: + if ((val & 0xfffc0000) != (pcic->iobr & 0xfffc0000)) { + cpu_register_physical_memory(pcic->iobr & 0xfffc0000, 0x40000, + IO_MEM_UNASSIGNED); + pcic->iobr = val & 0xfffc0001; + isa_mmio_init(pcic->iobr & 0xfffc0000, 0x40000); + } + break; + case 0x220: + pci_data_write(pcic->bus, pcic->par, val, 4); + break; + } +} + +static uint32_t sh_pci_reg_read (void *p, target_phys_addr_t addr) +{ + SHPCIState *pcic = p; + switch(addr) { + case 0 ... 0xfc: + return le32_to_cpup((uint32_t*)(pcic->dev->config + addr)); + case 0x1c0: + return pcic->par; + case 0x1c4: + return pcic->mbr; + case 0x1c8: + return pcic->iobr; + case 0x220: + return pci_data_read(pcic->bus, pcic->par, 4); + } + return 0; +} + +typedef struct { + CPUReadMemoryFunc * const r[3]; + CPUWriteMemoryFunc * const w[3]; +} MemOp; + +static MemOp sh_pci_reg = { + { NULL, NULL, sh_pci_reg_read }, + { NULL, NULL, sh_pci_reg_write }, +}; + +static int sh_pci_map_irq(PCIDevice *d, int irq_num) +{ + return (d->devfn >> 3); +} + +static void sh_pci_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + qemu_set_irq(pic[irq_num], level); +} + +static void sh_pci_map(SysBusDevice *dev, target_phys_addr_t base) +{ + SHPCIState *s = FROM_SYSBUS(SHPCIState, dev); + + cpu_register_physical_memory(P4ADDR(base), 0x224, s->memconfig); + cpu_register_physical_memory(A7ADDR(base), 0x224, s->memconfig); + + s->iobr = 0xfe240000; + isa_mmio_init(s->iobr, 0x40000); +} + +static int sh_pci_init_device(SysBusDevice *dev) +{ + SHPCIState *s; + int i; + + s = FROM_SYSBUS(SHPCIState, dev); + for (i = 0; i < 4; i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + s->bus = pci_register_bus(&s->busdev.qdev, "pci", + sh_pci_set_irq, sh_pci_map_irq, + s->irq, PCI_DEVFN(0, 0), 4); + s->memconfig = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w, + s, DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio_cb(dev, 0x224, sh_pci_map); + s->dev = pci_create_simple(s->bus, PCI_DEVFN(0, 0), "sh_pci_host"); + return 0; +} + +static int sh_pci_host_init(PCIDevice *d) +{ + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_HITACHI); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_HITACHI_SH7751R); + pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT); + pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST | + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + return 0; +} + +static PCIDeviceInfo sh_pci_host_info = { + .qdev.name = "sh_pci_host", + .qdev.size = sizeof(PCIDevice), + .init = sh_pci_host_init, +}; + +static void sh_pci_register_devices(void) +{ + sysbus_register_dev("sh_pci", sizeof(SHPCIState), + sh_pci_init_device); + pci_qdev_register(&sh_pci_host_info); +} + +device_init(sh_pci_register_devices) |