bcm2835_powermgt.c (4418B)
1/* 2 * BCM2835 Power Management emulation 3 * 4 * Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com> 5 * Copyright (C) 2021 Nolan Leake <nolan@sigbus.net> 6 * 7 * This work is licensed under the terms of the GNU GPL, version 2 or later. 8 * See the COPYING file in the top-level directory. 9 */ 10 11#include "qemu/osdep.h" 12#include "qemu/log.h" 13#include "qemu/module.h" 14#include "hw/misc/bcm2835_powermgt.h" 15#include "migration/vmstate.h" 16#include "sysemu/runstate.h" 17 18#define PASSWORD 0x5a000000 19#define PASSWORD_MASK 0xff000000 20 21#define R_RSTC 0x1c 22#define V_RSTC_RESET 0x20 23#define R_RSTS 0x20 24#define V_RSTS_POWEROFF 0x555 /* Linux uses partition 63 to indicate halt. */ 25#define R_WDOG 0x24 26 27static uint64_t bcm2835_powermgt_read(void *opaque, hwaddr offset, 28 unsigned size) 29{ 30 BCM2835PowerMgtState *s = (BCM2835PowerMgtState *)opaque; 31 uint32_t res = 0; 32 33 switch (offset) { 34 case R_RSTC: 35 res = s->rstc; 36 break; 37 case R_RSTS: 38 res = s->rsts; 39 break; 40 case R_WDOG: 41 res = s->wdog; 42 break; 43 44 default: 45 qemu_log_mask(LOG_UNIMP, 46 "bcm2835_powermgt_read: Unknown offset 0x%08"HWADDR_PRIx 47 "\n", offset); 48 res = 0; 49 break; 50 } 51 52 return res; 53} 54 55static void bcm2835_powermgt_write(void *opaque, hwaddr offset, 56 uint64_t value, unsigned size) 57{ 58 BCM2835PowerMgtState *s = (BCM2835PowerMgtState *)opaque; 59 60 if ((value & PASSWORD_MASK) != PASSWORD) { 61 qemu_log_mask(LOG_GUEST_ERROR, 62 "bcm2835_powermgt_write: Bad password 0x%"PRIx64 63 " at offset 0x%08"HWADDR_PRIx"\n", 64 value, offset); 65 return; 66 } 67 68 value = value & ~PASSWORD_MASK; 69 70 switch (offset) { 71 case R_RSTC: 72 s->rstc = value; 73 if (value & V_RSTC_RESET) { 74 if ((s->rsts & 0xfff) == V_RSTS_POWEROFF) { 75 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); 76 } else { 77 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 78 } 79 } 80 break; 81 case R_RSTS: 82 qemu_log_mask(LOG_UNIMP, 83 "bcm2835_powermgt_write: RSTS\n"); 84 s->rsts = value; 85 break; 86 case R_WDOG: 87 qemu_log_mask(LOG_UNIMP, 88 "bcm2835_powermgt_write: WDOG\n"); 89 s->wdog = value; 90 break; 91 92 default: 93 qemu_log_mask(LOG_UNIMP, 94 "bcm2835_powermgt_write: Unknown offset 0x%08"HWADDR_PRIx 95 "\n", offset); 96 break; 97 } 98} 99 100static const MemoryRegionOps bcm2835_powermgt_ops = { 101 .read = bcm2835_powermgt_read, 102 .write = bcm2835_powermgt_write, 103 .endianness = DEVICE_NATIVE_ENDIAN, 104 .impl.min_access_size = 4, 105 .impl.max_access_size = 4, 106}; 107 108static const VMStateDescription vmstate_bcm2835_powermgt = { 109 .name = TYPE_BCM2835_POWERMGT, 110 .version_id = 1, 111 .minimum_version_id = 1, 112 .fields = (VMStateField[]) { 113 VMSTATE_UINT32(rstc, BCM2835PowerMgtState), 114 VMSTATE_UINT32(rsts, BCM2835PowerMgtState), 115 VMSTATE_UINT32(wdog, BCM2835PowerMgtState), 116 VMSTATE_END_OF_LIST() 117 } 118}; 119 120static void bcm2835_powermgt_init(Object *obj) 121{ 122 BCM2835PowerMgtState *s = BCM2835_POWERMGT(obj); 123 124 memory_region_init_io(&s->iomem, obj, &bcm2835_powermgt_ops, s, 125 TYPE_BCM2835_POWERMGT, 0x200); 126 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 127} 128 129static void bcm2835_powermgt_reset(DeviceState *dev) 130{ 131 BCM2835PowerMgtState *s = BCM2835_POWERMGT(dev); 132 133 /* https://elinux.org/BCM2835_registers#PM */ 134 s->rstc = 0x00000102; 135 s->rsts = 0x00001000; 136 s->wdog = 0x00000000; 137} 138 139static void bcm2835_powermgt_class_init(ObjectClass *klass, void *data) 140{ 141 DeviceClass *dc = DEVICE_CLASS(klass); 142 143 dc->reset = bcm2835_powermgt_reset; 144 dc->vmsd = &vmstate_bcm2835_powermgt; 145} 146 147static TypeInfo bcm2835_powermgt_info = { 148 .name = TYPE_BCM2835_POWERMGT, 149 .parent = TYPE_SYS_BUS_DEVICE, 150 .instance_size = sizeof(BCM2835PowerMgtState), 151 .class_init = bcm2835_powermgt_class_init, 152 .instance_init = bcm2835_powermgt_init, 153}; 154 155static void bcm2835_powermgt_register_types(void) 156{ 157 type_register_static(&bcm2835_powermgt_info); 158} 159 160type_init(bcm2835_powermgt_register_types)