|  | /* | 
|  | * ARM SBSA Reference Platform Embedded Controller | 
|  | * | 
|  | * A device to allow PSCI running in the secure side of sbsa-ref machine | 
|  | * to communicate platform power states to qemu. | 
|  | * | 
|  | * Copyright (c) 2020 Nuvia Inc | 
|  | * Written by Graeme Gregory <graeme@nuviainc.com> | 
|  | * | 
|  | * SPDX-License-Identifier: GPL-2.0-or-later | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/log.h" | 
|  | #include "hw/sysbus.h" | 
|  | #include "sysemu/runstate.h" | 
|  |  | 
|  | typedef struct SECUREECState { | 
|  | SysBusDevice parent_obj; | 
|  | MemoryRegion iomem; | 
|  | } SECUREECState; | 
|  |  | 
|  | #define TYPE_SBSA_SECURE_EC "sbsa-ec" | 
|  | OBJECT_DECLARE_SIMPLE_TYPE(SECUREECState, SBSA_SECURE_EC) | 
|  |  | 
|  | enum sbsa_ec_powerstates { | 
|  | SBSA_EC_CMD_POWEROFF = 0x01, | 
|  | SBSA_EC_CMD_REBOOT = 0x02, | 
|  | }; | 
|  |  | 
|  | static uint64_t sbsa_ec_read(void *opaque, hwaddr offset, unsigned size) | 
|  | { | 
|  | /* No use for this currently */ | 
|  | qemu_log_mask(LOG_GUEST_ERROR, "sbsa-ec: no readable registers"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void sbsa_ec_write(void *opaque, hwaddr offset, | 
|  | uint64_t value, unsigned size) | 
|  | { | 
|  | if (offset == 0) { /* PSCI machine power command register */ | 
|  | switch (value) { | 
|  | case SBSA_EC_CMD_POWEROFF: | 
|  | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); | 
|  | break; | 
|  | case SBSA_EC_CMD_REBOOT: | 
|  | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); | 
|  | break; | 
|  | default: | 
|  | qemu_log_mask(LOG_GUEST_ERROR, | 
|  | "sbsa-ec: unknown power command"); | 
|  | } | 
|  | } else { | 
|  | qemu_log_mask(LOG_GUEST_ERROR, "sbsa-ec: unknown EC register"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static const MemoryRegionOps sbsa_ec_ops = { | 
|  | .read = sbsa_ec_read, | 
|  | .write = sbsa_ec_write, | 
|  | .endianness = DEVICE_NATIVE_ENDIAN, | 
|  | .valid.min_access_size = 4, | 
|  | .valid.max_access_size = 4, | 
|  | }; | 
|  |  | 
|  | static void sbsa_ec_init(Object *obj) | 
|  | { | 
|  | SECUREECState *s = SBSA_SECURE_EC(obj); | 
|  | SysBusDevice *dev = SYS_BUS_DEVICE(obj); | 
|  |  | 
|  | memory_region_init_io(&s->iomem, obj, &sbsa_ec_ops, s, "sbsa-ec", | 
|  | 0x1000); | 
|  | sysbus_init_mmio(dev, &s->iomem); | 
|  | } | 
|  |  | 
|  | static void sbsa_ec_class_init(ObjectClass *klass, void *data) | 
|  | { | 
|  | DeviceClass *dc = DEVICE_CLASS(klass); | 
|  |  | 
|  | /* No vmstate or reset required: device has no internal state */ | 
|  | dc->user_creatable = false; | 
|  | } | 
|  |  | 
|  | static const TypeInfo sbsa_ec_info = { | 
|  | .name          = TYPE_SBSA_SECURE_EC, | 
|  | .parent        = TYPE_SYS_BUS_DEVICE, | 
|  | .instance_size = sizeof(SECUREECState), | 
|  | .instance_init = sbsa_ec_init, | 
|  | .class_init    = sbsa_ec_class_init, | 
|  | }; | 
|  |  | 
|  | static void sbsa_ec_register_type(void) | 
|  | { | 
|  | type_register_static(&sbsa_ec_info); | 
|  | } | 
|  |  | 
|  | type_init(sbsa_ec_register_type); |