diff options
Diffstat (limited to 'hw/ppc/e500.c')
-rw-r--r-- | hw/ppc/e500.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 2157d87c19..2832fc0da4 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -36,6 +36,9 @@ #include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "hw/pci-host/ppce500.h" +#include "qemu/error-report.h" +#include "hw/platform-bus.h" +#include "hw/net/fsl_etsec/etsec.h" #define EPAPR_MAGIC (0x45504150) #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" @@ -61,6 +64,8 @@ #define MPC8544_PCI_IO 0xE1000000ULL #define MPC8544_UTIL_OFFSET 0xe0000ULL #define MPC8544_SPIN_BASE 0xEF000000ULL +#define MPC8XXX_GPIO_OFFSET 0x000FF000ULL +#define MPC8XXX_GPIO_IRQ 43 struct boot_info { @@ -122,6 +127,142 @@ static void dt_serial_create(void *fdt, unsigned long long offset, } } +static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic) +{ + hwaddr mmio0 = MPC8XXX_GPIO_OFFSET; + int irq0 = MPC8XXX_GPIO_IRQ; + gchar *node = g_strdup_printf("%s/gpio@%"PRIx64, soc, mmio0); + gchar *poweroff = g_strdup_printf("%s/power-off", soc); + int gpio_ph; + + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,qoriq-gpio"); + qemu_fdt_setprop_cells(fdt, node, "reg", mmio0, 0x1000); + qemu_fdt_setprop_cells(fdt, node, "interrupts", irq0, 0x2); + qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); + qemu_fdt_setprop_cells(fdt, node, "#gpio-cells", 2); + qemu_fdt_setprop(fdt, node, "gpio-controller", NULL, 0); + gpio_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_cell(fdt, node, "phandle", gpio_ph); + qemu_fdt_setprop_cell(fdt, node, "linux,phandle", gpio_ph); + + /* Power Off Pin */ + qemu_fdt_add_subnode(fdt, poweroff); + qemu_fdt_setprop_string(fdt, poweroff, "compatible", "gpio-poweroff"); + qemu_fdt_setprop_cells(fdt, poweroff, "gpios", gpio_ph, 0, 0); + + g_free(node); + g_free(poweroff); +} + +typedef struct PlatformDevtreeData { + void *fdt; + const char *mpic; + int irq_start; + const char *node; + PlatformBusDevice *pbus; +} PlatformDevtreeData; + +static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data) +{ + eTSEC *etsec = ETSEC_COMMON(sbdev); + PlatformBusDevice *pbus = data->pbus; + hwaddr mmio0 = platform_bus_get_mmio_addr(pbus, sbdev, 0); + int irq0 = platform_bus_get_irqn(pbus, sbdev, 0); + int irq1 = platform_bus_get_irqn(pbus, sbdev, 1); + int irq2 = platform_bus_get_irqn(pbus, sbdev, 2); + gchar *node = g_strdup_printf("/platform/ethernet@%"PRIx64, mmio0); + gchar *group = g_strdup_printf("%s/queue-group", node); + void *fdt = data->fdt; + + assert((int64_t)mmio0 >= 0); + assert(irq0 >= 0); + assert(irq1 >= 0); + assert(irq2 >= 0); + + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop_string(fdt, node, "device_type", "network"); + qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2"); + qemu_fdt_setprop_string(fdt, node, "model", "eTSEC"); + qemu_fdt_setprop(fdt, node, "local-mac-address", etsec->conf.macaddr.a, 6); + qemu_fdt_setprop_cells(fdt, node, "fixed-link", 0, 1, 1000, 0, 0); + + qemu_fdt_add_subnode(fdt, group); + qemu_fdt_setprop_cells(fdt, group, "reg", mmio0, 0x1000); + qemu_fdt_setprop_cells(fdt, group, "interrupts", + data->irq_start + irq0, 0x2, + data->irq_start + irq1, 0x2, + data->irq_start + irq2, 0x2); + + g_free(node); + g_free(group); + + return 0; +} + +static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) +{ + PlatformDevtreeData *data = opaque; + bool matched = false; + + if (object_dynamic_cast(OBJECT(sbdev), TYPE_ETSEC_COMMON)) { + create_devtree_etsec(sbdev, data); + matched = true; + } + + if (!matched) { + error_report("Device %s is not supported by this machine yet.", + qdev_fw_name(DEVICE(sbdev))); + exit(1); + } + + return 0; +} + +static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, + const char *mpic) +{ + gchar *node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); + const char platcomp[] = "qemu,platform\0simple-bus"; + uint64_t addr = params->platform_bus_base; + uint64_t size = params->platform_bus_size; + int irq_start = params->platform_bus_first_irq; + PlatformBusDevice *pbus; + DeviceState *dev; + + /* Create a /platform node that we can put all devices into */ + + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp)); + + /* Our platform bus region is less than 32bit big, so 1 cell is enough for + address and size */ + qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); + qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); + qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size); + + qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); + + dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); + pbus = PLATFORM_BUS_DEVICE(dev); + + /* We can only create dt nodes for dynamic devices when they're ready */ + if (pbus->done_gathering) { + PlatformDevtreeData data = { + .fdt = fdt, + .mpic = mpic, + .irq_start = irq_start, + .node = node, + .pbus = pbus, + }; + + /* Loop through all dynamic sysbus devices and create nodes for them */ + foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); + } + + g_free(node); +} + static int ppce500_load_device_tree(MachineState *machine, PPCE500Params *params, hwaddr addr, @@ -379,6 +520,14 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); + if (params->has_mpc8xxx_gpio) { + create_dt_mpc8xxx_gpio(fdt, soc, mpic); + } + + if (params->has_platform_bus) { + platform_bus_create_devtree(params, fdt, mpic); + } + params->fixup_devtree(params, fdt); if (toplevel_compat) { @@ -407,6 +556,7 @@ typedef struct DeviceTreeParams { hwaddr initrd_size; hwaddr kernel_base; hwaddr kernel_size; + Notifier notifier; } DeviceTreeParams; static void ppce500_reset_device_tree(void *opaque) @@ -417,6 +567,12 @@ static void ppce500_reset_device_tree(void *opaque) false); } +static void ppce500_init_notify(Notifier *notifier, void *data) +{ + DeviceTreeParams *p = container_of(notifier, DeviceTreeParams, notifier); + ppce500_reset_device_tree(p); +} + static int ppce500_prep_device_tree(MachineState *machine, PPCE500Params *params, hwaddr addr, @@ -435,6 +591,8 @@ static int ppce500_prep_device_tree(MachineState *machine, p->kernel_size = kernel_size; qemu_register_reset(ppce500_reset_device_tree, p); + p->notifier.notify = ppce500_init_notify; + qemu_add_machine_init_done_notifier(&p->notifier); /* Issue the device tree loader once, so that we get the size of the blob */ return ppce500_load_device_tree(machine, params, addr, initrd_base, @@ -618,6 +776,13 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr, return mpic; } +static void ppce500_power_off(void *opaque, int line, int on) +{ + if (on) { + qemu_system_shutdown_request(); + } +} + void ppce500_init(MachineState *machine, PPCE500Params *params) { MemoryRegion *address_space_mem = get_system_memory(); @@ -769,6 +934,40 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) cur_base = (32 * 1024 * 1024); } + if (params->has_mpc8xxx_gpio) { + qemu_irq poweroff_irq; + + dev = qdev_create(NULL, "mpc8xxx_gpio"); + s = SYS_BUS_DEVICE(dev); + qdev_init_nofail(dev); + sysbus_connect_irq(s, 0, mpic[MPC8XXX_GPIO_IRQ]); + memory_region_add_subregion(ccsr_addr_space, MPC8XXX_GPIO_OFFSET, + sysbus_mmio_get_region(s, 0)); + + /* Power Off GPIO at Pin 0 */ + poweroff_irq = qemu_allocate_irq(ppce500_power_off, NULL, 0); + qdev_connect_gpio_out(dev, 0, poweroff_irq); + } + + /* Platform Bus Device */ + if (params->has_platform_bus) { + dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); + dev->id = TYPE_PLATFORM_BUS_DEVICE; + qdev_prop_set_uint32(dev, "num_irqs", params->platform_bus_num_irqs); + qdev_prop_set_uint32(dev, "mmio_size", params->platform_bus_size); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + + for (i = 0; i < params->platform_bus_num_irqs; i++) { + int irqn = params->platform_bus_first_irq + i; + sysbus_connect_irq(s, i, mpic[irqn]); + } + + memory_region_add_subregion(address_space_mem, + params->platform_bus_base, + sysbus_mmio_get_region(s, 0)); + } + /* Load kernel. */ if (machine->kernel_filename) { kernel_base = cur_base; |