diff options
author | Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> | 2018-01-24 19:19:58 +0000 |
---|---|---|
committer | Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> | 2018-01-25 13:39:39 +0000 |
commit | 25c5d5acfbaa148b2da64b1f2c1401f87ebb0bb4 (patch) | |
tree | 0f5f315593f29bade68e917227457befc10c6805 /hw | |
parent | be75bbe2d779bd9d4958057fd35172c7eef42258 (diff) |
sun4u: implement power device
This inbuilt device contains a single 4-byte register, of which bit 24 is used
to power down the machine on a real Ultra 5.
The power device exists at offset 0x724000 on a real machine, but due to the
current configuration of the BARs in QEMU it must be located lower in PCI IO
space.
For the moment we place the power device at offset 0x7240 as a reminder of its
original location and raise the base PCI IO address from 0x4000 to 0x8000.
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Reviewed-by: Artyom Tarasenko <atar4qemu@gmail.com>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/sparc64/sun4u.c | 64 |
1 files changed, 63 insertions, 1 deletions
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index c4eff6bea2..a23cb26b0d 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -205,6 +205,59 @@ typedef struct ResetData { uint64_t prom_addr; } ResetData; +#define TYPE_SUN4U_POWER "power" +#define SUN4U_POWER(obj) OBJECT_CHECK(PowerDevice, (obj), TYPE_SUN4U_POWER) + +typedef struct PowerDevice { + SysBusDevice parent_obj; + + MemoryRegion power_mmio; +} PowerDevice; + +/* Power */ +static void power_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + /* According to a real Ultra 5, bit 24 controls the power */ + if (val & 0x1000000) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } +} + +static const MemoryRegionOps power_mem_ops = { + .write = power_mem_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void power_realize(DeviceState *dev, Error **errp) +{ + PowerDevice *d = SUN4U_POWER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&d->power_mmio, OBJECT(dev), &power_mem_ops, d, + "power", sizeof(uint32_t)); + + sysbus_init_mmio(sbd, &d->power_mmio); +} + +static void power_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = power_realize; +} + +static const TypeInfo power_info = { + .name = TYPE_SUN4U_POWER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PowerDevice), + .class_init = power_class_init, +}; + static void ebus_isa_irq_handler(void *opaque, int n, int level) { EbusState *s = EBUS(opaque); @@ -221,6 +274,7 @@ static void ebus_isa_irq_handler(void *opaque, int n, int level) static void ebus_realize(PCIDevice *pci_dev, Error **errp) { EbusState *s = EBUS(pci_dev); + SysBusDevice *sbd; DeviceState *dev; qemu_irq *isa_irq; DriveInfo *fd[MAX_FD]; @@ -270,6 +324,13 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) qdev_prop_set_uint32(dev, "dma", -1); qdev_init_nofail(dev); + /* Power */ + dev = qdev_create(NULL, TYPE_SUN4U_POWER); + qdev_init_nofail(dev); + sbd = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(pci_address_space_io(pci_dev), 0x7240, + sysbus_mmio_get_region(sbd, 0)); + /* PCI */ pci_dev->config[0x04] = 0x06; // command = bus master, pci mem pci_dev->config[0x05] = 0x00; @@ -282,7 +343,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) 0, 0x1000000); pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), - 0, 0x4000); + 0, 0x8000); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); } @@ -694,6 +755,7 @@ static const TypeInfo sun4v_type = { static void sun4u_register_types(void) { + type_register_static(&power_info); type_register_static(&ebus_info); type_register_static(&prom_info); type_register_static(&ram_info); |