1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
/*
* Aspeed PECI Controller
*
* Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com)
*
* This code is licensed under the GPL version 2 or later. See the COPYING
* file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/irq.h"
#include "hw/misc/aspeed_peci.h"
#include "hw/registerfields.h"
#include "trace.h"
#define ASPEED_PECI_CC_RSP_SUCCESS (0x40U)
/* Command Register */
REG32(PECI_CMD, 0x08)
FIELD(PECI_CMD, FIRE, 0, 1)
/* Interrupt Control Register */
REG32(PECI_INT_CTRL, 0x18)
/* Interrupt Status Register */
REG32(PECI_INT_STS, 0x1C)
FIELD(PECI_INT_STS, CMD_DONE, 0, 1)
/* Rx/Tx Data Buffer Registers */
REG32(PECI_WR_DATA0, 0x20)
REG32(PECI_RD_DATA0, 0x30)
static void aspeed_peci_raise_interrupt(AspeedPECIState *s, uint32_t status)
{
trace_aspeed_peci_raise_interrupt(s->regs[R_PECI_INT_CTRL], status);
s->regs[R_PECI_INT_STS] = s->regs[R_PECI_INT_CTRL] & status;
if (!s->regs[R_PECI_INT_STS]) {
return;
}
qemu_irq_raise(s->irq);
}
static uint64_t aspeed_peci_read(void *opaque, hwaddr offset, unsigned size)
{
AspeedPECIState *s = ASPEED_PECI(opaque);
uint64_t data;
if (offset >= ASPEED_PECI_NR_REGS << 2) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
return 0;
}
data = s->regs[offset >> 2];
trace_aspeed_peci_read(offset, data);
return data;
}
static void aspeed_peci_write(void *opaque, hwaddr offset, uint64_t data,
unsigned size)
{
AspeedPECIState *s = ASPEED_PECI(opaque);
trace_aspeed_peci_write(offset, data);
if (offset >= ASPEED_PECI_NR_REGS << 2) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
return;
}
switch (offset) {
case A_PECI_INT_STS:
s->regs[R_PECI_INT_STS] &= ~data;
if (!s->regs[R_PECI_INT_STS]) {
qemu_irq_lower(s->irq);
}
break;
case A_PECI_CMD:
/*
* Only the FIRE bit is writable. Once the command is complete, it
* should be cleared. Since we complete the command immediately, the
* value is not stored in the register array.
*/
if (!FIELD_EX32(data, PECI_CMD, FIRE)) {
break;
}
if (s->regs[R_PECI_INT_STS]) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Interrupt status must be "
"cleared before firing another command: 0x%08x\n",
__func__, s->regs[R_PECI_INT_STS]);
break;
}
s->regs[R_PECI_RD_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS;
s->regs[R_PECI_WR_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS;
aspeed_peci_raise_interrupt(s,
FIELD_DP32(0, PECI_INT_STS, CMD_DONE, 1));
break;
default:
s->regs[offset / sizeof(s->regs[0])] = data;
break;
}
}
static const MemoryRegionOps aspeed_peci_ops = {
.read = aspeed_peci_read,
.write = aspeed_peci_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void aspeed_peci_realize(DeviceState *dev, Error **errp)
{
AspeedPECIState *s = ASPEED_PECI(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_peci_ops, s,
TYPE_ASPEED_PECI, 0x1000);
sysbus_init_mmio(sbd, &s->mmio);
sysbus_init_irq(sbd, &s->irq);
}
static void aspeed_peci_reset(DeviceState *dev)
{
AspeedPECIState *s = ASPEED_PECI(dev);
memset(s->regs, 0, sizeof(s->regs));
}
static void aspeed_peci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = aspeed_peci_realize;
device_class_set_legacy_reset(dc, aspeed_peci_reset);
dc->desc = "Aspeed PECI Controller";
}
static const TypeInfo aspeed_peci_types[] = {
{
.name = TYPE_ASPEED_PECI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AspeedPECIState),
.class_init = aspeed_peci_class_init,
.abstract = false,
},
};
DEFINE_TYPES(aspeed_peci_types);
|