2 * BCM2835 Power Management emulation
4 * Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com>
5 * Copyright (C) 2021 Nolan Leake <nolan@sigbus.net>
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.
11 #include "qemu/osdep.h"
13 #include "qemu/module.h"
14 #include "hw/misc/bcm2835_powermgt.h"
15 #include "migration/vmstate.h"
16 #include "sysemu/runstate.h"
18 #define PASSWORD 0x5a000000
19 #define PASSWORD_MASK 0xff000000
22 #define V_RSTC_RESET 0x20
24 #define V_RSTS_POWEROFF 0x555 /* Linux uses partition 63 to indicate halt. */
27 static uint64_t bcm2835_powermgt_read(void *opaque
, hwaddr offset
,
30 BCM2835PowerMgtState
*s
= (BCM2835PowerMgtState
*)opaque
;
45 qemu_log_mask(LOG_UNIMP
,
46 "bcm2835_powermgt_read: Unknown offset 0x%08"HWADDR_PRIx
55 static void bcm2835_powermgt_write(void *opaque
, hwaddr offset
,
56 uint64_t value
, unsigned size
)
58 BCM2835PowerMgtState
*s
= (BCM2835PowerMgtState
*)opaque
;
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",
68 value
= value
& ~PASSWORD_MASK
;
73 if (value
& V_RSTC_RESET
) {
74 if ((s
->rsts
& 0xfff) == V_RSTS_POWEROFF
) {
75 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN
);
77 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET
);
82 qemu_log_mask(LOG_UNIMP
,
83 "bcm2835_powermgt_write: RSTS\n");
87 qemu_log_mask(LOG_UNIMP
,
88 "bcm2835_powermgt_write: WDOG\n");
93 qemu_log_mask(LOG_UNIMP
,
94 "bcm2835_powermgt_write: Unknown offset 0x%08"HWADDR_PRIx
100 static 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,
108 static const VMStateDescription vmstate_bcm2835_powermgt
= {
109 .name
= TYPE_BCM2835_POWERMGT
,
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()
120 static void bcm2835_powermgt_init(Object
*obj
)
122 BCM2835PowerMgtState
*s
= BCM2835_POWERMGT(obj
);
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
);
129 static void bcm2835_powermgt_reset(DeviceState
*dev
)
131 BCM2835PowerMgtState
*s
= BCM2835_POWERMGT(dev
);
133 /* https://elinux.org/BCM2835_registers#PM */
134 s
->rstc
= 0x00000102;
135 s
->rsts
= 0x00001000;
136 s
->wdog
= 0x00000000;
139 static void bcm2835_powermgt_class_init(ObjectClass
*klass
, void *data
)
141 DeviceClass
*dc
= DEVICE_CLASS(klass
);
143 dc
->reset
= bcm2835_powermgt_reset
;
144 dc
->vmsd
= &vmstate_bcm2835_powermgt
;
147 static 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
,
155 static void bcm2835_powermgt_register_types(void)
157 type_register_static(&bcm2835_powermgt_info
);
160 type_init(bcm2835_powermgt_register_types
)