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