|  | /* | 
|  | * ASPEED SLI Controller | 
|  | * | 
|  | * Copyright (C) 2024 ASPEED Technology Inc. | 
|  | * | 
|  | * SPDX-License-Identifier: GPL-2.0-or-later | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/log.h" | 
|  | #include "qemu/error-report.h" | 
|  | #include "hw/qdev-properties.h" | 
|  | #include "hw/misc/aspeed_sli.h" | 
|  | #include "qapi/error.h" | 
|  | #include "migration/vmstate.h" | 
|  | #include "trace.h" | 
|  |  | 
|  | #define SLI_REGION_SIZE 0x500 | 
|  | #define TO_REG(addr) ((addr) >> 2) | 
|  |  | 
|  | static uint64_t aspeed_sli_read(void *opaque, hwaddr addr, unsigned int size) | 
|  | { | 
|  | AspeedSLIState *s = ASPEED_SLI(opaque); | 
|  | int reg = TO_REG(addr); | 
|  |  | 
|  | if (reg >= ARRAY_SIZE(s->regs)) { | 
|  | qemu_log_mask(LOG_GUEST_ERROR, | 
|  | "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", | 
|  | __func__, addr); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | trace_aspeed_sli_read(addr, size, s->regs[reg]); | 
|  | return s->regs[reg]; | 
|  | } | 
|  |  | 
|  | static void aspeed_sli_write(void *opaque, hwaddr addr, uint64_t data, | 
|  | unsigned int size) | 
|  | { | 
|  | AspeedSLIState *s = ASPEED_SLI(opaque); | 
|  | int reg = TO_REG(addr); | 
|  |  | 
|  | if (reg >= ARRAY_SIZE(s->regs)) { | 
|  | qemu_log_mask(LOG_GUEST_ERROR, | 
|  | "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", | 
|  | __func__, addr); | 
|  | return; | 
|  | } | 
|  |  | 
|  | trace_aspeed_sli_write(addr, size, data); | 
|  | s->regs[reg] = data; | 
|  | } | 
|  |  | 
|  | static uint64_t aspeed_sliio_read(void *opaque, hwaddr addr, unsigned int size) | 
|  | { | 
|  | AspeedSLIState *s = ASPEED_SLI(opaque); | 
|  | int reg = TO_REG(addr); | 
|  |  | 
|  | if (reg >= ARRAY_SIZE(s->regs)) { | 
|  | qemu_log_mask(LOG_GUEST_ERROR, | 
|  | "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", | 
|  | __func__, addr); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | trace_aspeed_sliio_read(addr, size, s->regs[reg]); | 
|  | return s->regs[reg]; | 
|  | } | 
|  |  | 
|  | static void aspeed_sliio_write(void *opaque, hwaddr addr, uint64_t data, | 
|  | unsigned int size) | 
|  | { | 
|  | AspeedSLIState *s = ASPEED_SLI(opaque); | 
|  | int reg = TO_REG(addr); | 
|  |  | 
|  | if (reg >= ARRAY_SIZE(s->regs)) { | 
|  | qemu_log_mask(LOG_GUEST_ERROR, | 
|  | "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", | 
|  | __func__, addr); | 
|  | return; | 
|  | } | 
|  |  | 
|  | trace_aspeed_sliio_write(addr, size, data); | 
|  | s->regs[reg] = data; | 
|  | } | 
|  |  | 
|  | static const MemoryRegionOps aspeed_sli_ops = { | 
|  | .read = aspeed_sli_read, | 
|  | .write = aspeed_sli_write, | 
|  | .endianness = DEVICE_LITTLE_ENDIAN, | 
|  | .valid = { | 
|  | .min_access_size = 1, | 
|  | .max_access_size = 4, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const MemoryRegionOps aspeed_sliio_ops = { | 
|  | .read = aspeed_sliio_read, | 
|  | .write = aspeed_sliio_write, | 
|  | .endianness = DEVICE_LITTLE_ENDIAN, | 
|  | .valid = { | 
|  | .min_access_size = 1, | 
|  | .max_access_size = 4, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static void aspeed_sli_realize(DeviceState *dev, Error **errp) | 
|  | { | 
|  | AspeedSLIState *s = ASPEED_SLI(dev); | 
|  | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | 
|  |  | 
|  | memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sli_ops, s, | 
|  | TYPE_ASPEED_SLI, SLI_REGION_SIZE); | 
|  | sysbus_init_mmio(sbd, &s->iomem); | 
|  | } | 
|  |  | 
|  | static void aspeed_sliio_realize(DeviceState *dev, Error **errp) | 
|  | { | 
|  | AspeedSLIState *s = ASPEED_SLI(dev); | 
|  | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | 
|  |  | 
|  | memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sliio_ops, s, | 
|  | TYPE_ASPEED_SLI, SLI_REGION_SIZE); | 
|  | sysbus_init_mmio(sbd, &s->iomem); | 
|  | } | 
|  |  | 
|  | static void aspeed_sli_class_init(ObjectClass *klass, const void *data) | 
|  | { | 
|  | DeviceClass *dc = DEVICE_CLASS(klass); | 
|  |  | 
|  | dc->desc = "Aspeed SLI Controller"; | 
|  | dc->realize = aspeed_sli_realize; | 
|  | } | 
|  |  | 
|  | static const TypeInfo aspeed_sli_info = { | 
|  | .name          = TYPE_ASPEED_SLI, | 
|  | .parent        = TYPE_SYS_BUS_DEVICE, | 
|  | .instance_size = sizeof(AspeedSLIState), | 
|  | .class_init    = aspeed_sli_class_init, | 
|  | .abstract      = true, | 
|  | }; | 
|  |  | 
|  | static void aspeed_2700_sli_class_init(ObjectClass *klass, const void *data) | 
|  | { | 
|  | DeviceClass *dc = DEVICE_CLASS(klass); | 
|  |  | 
|  | dc->desc = "AST2700 SLI Controller"; | 
|  | } | 
|  |  | 
|  | static void aspeed_2700_sliio_class_init(ObjectClass *klass, const void *data) | 
|  | { | 
|  | DeviceClass *dc = DEVICE_CLASS(klass); | 
|  |  | 
|  | dc->desc = "AST2700 I/O SLI Controller"; | 
|  | dc->realize = aspeed_sliio_realize; | 
|  | } | 
|  |  | 
|  | static const TypeInfo aspeed_2700_sli_info = { | 
|  | .name           = TYPE_ASPEED_2700_SLI, | 
|  | .parent         = TYPE_ASPEED_SLI, | 
|  | .class_init     = aspeed_2700_sli_class_init, | 
|  | }; | 
|  |  | 
|  | static const TypeInfo aspeed_2700_sliio_info = { | 
|  | .name           = TYPE_ASPEED_2700_SLIIO, | 
|  | .parent         = TYPE_ASPEED_SLI, | 
|  | .class_init     = aspeed_2700_sliio_class_init, | 
|  | }; | 
|  |  | 
|  | static void aspeed_sli_register_types(void) | 
|  | { | 
|  | type_register_static(&aspeed_sli_info); | 
|  | type_register_static(&aspeed_2700_sli_info); | 
|  | type_register_static(&aspeed_2700_sliio_info); | 
|  | } | 
|  |  | 
|  | type_init(aspeed_sli_register_types); |