/* * System (CPU) Bus device support code * * Copyright (c) 2009 CodeSourcery * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ #include "sysbus.h" #include "monitor.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); struct BusInfo system_bus_info = { .name = "System", .size = sizeof(BusState), .print_dev = sysbus_dev_print, .get_fw_dev_path = sysbus_get_fw_dev_path, }; void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) { assert(n >= 0 && n < dev->num_irq); dev->irqs[n] = NULL; if (dev->irqp[n]) { *dev->irqp[n] = irq; } } void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr) { assert(n >= 0 && n < dev->num_mmio); if (dev->mmio[n].addr == addr) { /* ??? region already mapped here. */ return; } if (dev->mmio[n].addr != (target_phys_addr_t)-1) { /* Unregister previous mapping. */ cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size, IO_MEM_UNASSIGNED); } dev->mmio[n].addr = addr; if (dev->mmio[n].cb) { dev->mmio[n].cb(dev, addr); } else { cpu_register_physical_memory(addr, dev->mmio[n].size, dev->mmio[n].iofunc); } } /* Request an IRQ source. The actual IRQ object may be populated later. */ void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p) { int n; assert(dev->num_irq < QDEV_MAX_IRQ); n = dev->num_irq++; dev->irqp[n] = p; } /* Pass IRQs from a target device. */ void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target) { int i; assert(dev->num_irq == 0); dev->num_irq = target->num_irq; for (i = 0; i < dev->num_irq; i++) { dev->irqp[i] = target->irqp[i]; } } void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, ram_addr_t iofunc) { int n; assert(dev->num_mmio < QDEV_MAX_MMIO); n = dev->num_mmio++; dev->mmio[n].addr = -1; dev->mmio[n].size = size; dev->mmio[n].iofunc = iofunc; } void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size, mmio_mapfunc cb) { int n; assert(dev->num_mmio < QDEV_MAX_MMIO); n = dev->num_mmio++; dev->mmio[n].addr = -1; dev->mmio[n].size = size; dev->mmio[n].cb = cb; } void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size) { pio_addr_t i; for (i = 0; i < size; i++) { assert(dev->num_pio < QDEV_MAX_PIO); dev->pio[dev->num_pio++] = ioport++; } } static int sysbus_device_init(DeviceState *dev, DeviceInfo *base) { SysBusDeviceInfo *info = container_of(base, SysBusDeviceInfo, qdev); return info->init(sysbus_from_qdev(dev)); } void sysbus_register_withprop(SysBusDeviceInfo *info) { info->qdev.init = sysbus_device_init; info->qdev.bus_info = &system_bus_info; assert(info->qdev.size >= sizeof(SysBusDevice)); qdev_register(&info->qdev); } void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init) { SysBusDeviceInfo *info; info = qemu_mallocz(sizeof(*info)); info->qdev.name = qemu_strdup(name); info->qdev.size = size; info->init = init; sysbus_register_withprop(info); } DeviceState *sysbus_create_varargs(const char *name, target_phys_addr_t addr, ...) { DeviceState *dev; SysBusDevice *s; va_list va; qemu_irq irq; int n; dev = qdev_create(NULL, name); s = sysbus_from_qdev(dev); qdev_init_nofail(dev); if (addr != (target_phys_addr_t)-1) { sysbus_mmio_map(s, 0, addr); } va_start(va, addr); n = 0; while (1) { irq = va_arg(va, qemu_irq); if (!irq) { break; } sysbus_connect_irq(s, n, irq); n++; } return dev; } DeviceState *sysbus_try_create_varargs(const char *name, target_phys_addr_t addr, ...) { DeviceState *dev; SysBusDevice *s; va_list va; qemu_irq irq; int n; dev = qdev_try_create(NULL, name); if (!dev) { return NULL; } s = sysbus_from_qdev(dev); qdev_init_nofail(dev); if (addr != (target_phys_addr_t)-1) { sysbus_mmio_map(s, 0, addr); } va_start(va, addr); n = 0; while (1) { irq = va_arg(va, qemu_irq); if (!irq) { break; } sysbus_connect_irq(s, n, irq); n++; } return dev; } static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) { SysBusDevice *s = sysbus_from_qdev(dev); int i; monitor_printf(mon, "%*sirq %d\n", indent, "", s->num_irq); for (i = 0; i < s->num_mmio; i++) { monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n", indent, "", s->mmio[i].addr, s->mmio[i].size); } } static char *sysbus_get_fw_dev_path(DeviceState *dev) { SysBusDevice *s = sysbus_from_qdev(dev); char path[40]; int off; off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev)); if (s->num_mmio) { snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx, s->mmio[0].addr); } else if (s->num_pio) { snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]); } return strdup(path); }