2 * Aspeed PECI Controller
4 * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com)
6 * This code is licensed under the GPL version 2 or later. See the COPYING
7 * file in the top-level directory.
10 #include "qemu/osdep.h"
13 #include "hw/misc/aspeed_peci.h"
14 #include "hw/registerfields.h"
17 #define ASPEED_PECI_CC_RSP_SUCCESS (0x40U)
19 /* Command Register */
21 FIELD(PECI_CMD
, FIRE
, 0, 1)
23 /* Interrupt Control Register */
24 REG32(PECI_INT_CTRL
, 0x18)
26 /* Interrupt Status Register */
27 REG32(PECI_INT_STS
, 0x1C)
28 FIELD(PECI_INT_STS
, CMD_DONE
, 0, 1)
30 /* Rx/Tx Data Buffer Registers */
31 REG32(PECI_WR_DATA0
, 0x20)
32 REG32(PECI_RD_DATA0
, 0x30)
34 static void aspeed_peci_raise_interrupt(AspeedPECIState
*s
, uint32_t status
)
36 trace_aspeed_peci_raise_interrupt(s
->regs
[R_PECI_INT_CTRL
], status
);
38 s
->regs
[R_PECI_INT_STS
] = s
->regs
[R_PECI_INT_CTRL
] & status
;
39 if (!s
->regs
[R_PECI_INT_STS
]) {
42 qemu_irq_raise(s
->irq
);
45 static uint64_t aspeed_peci_read(void *opaque
, hwaddr offset
, unsigned size
)
47 AspeedPECIState
*s
= ASPEED_PECI(opaque
);
50 if (offset
>= ASPEED_PECI_NR_REGS
<< 2) {
51 qemu_log_mask(LOG_GUEST_ERROR
,
52 "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx
"\n",
56 data
= s
->regs
[offset
>> 2];
58 trace_aspeed_peci_read(offset
, data
);
62 static void aspeed_peci_write(void *opaque
, hwaddr offset
, uint64_t data
,
65 AspeedPECIState
*s
= ASPEED_PECI(opaque
);
67 trace_aspeed_peci_write(offset
, data
);
69 if (offset
>= ASPEED_PECI_NR_REGS
<< 2) {
70 qemu_log_mask(LOG_GUEST_ERROR
,
71 "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx
"\n",
78 s
->regs
[R_PECI_INT_STS
] &= ~data
;
79 if (!s
->regs
[R_PECI_INT_STS
]) {
80 qemu_irq_lower(s
->irq
);
85 * Only the FIRE bit is writable. Once the command is complete, it
86 * should be cleared. Since we complete the command immediately, the
87 * value is not stored in the register array.
89 if (!FIELD_EX32(data
, PECI_CMD
, FIRE
)) {
92 if (s
->regs
[R_PECI_INT_STS
]) {
93 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Interrupt status must be "
94 "cleared before firing another command: 0x%08x\n",
95 __func__
, s
->regs
[R_PECI_INT_STS
]);
98 s
->regs
[R_PECI_RD_DATA0
] = ASPEED_PECI_CC_RSP_SUCCESS
;
99 s
->regs
[R_PECI_WR_DATA0
] = ASPEED_PECI_CC_RSP_SUCCESS
;
100 aspeed_peci_raise_interrupt(s
,
101 FIELD_DP32(0, PECI_INT_STS
, CMD_DONE
, 1));
104 s
->regs
[offset
/ sizeof(s
->regs
[0])] = data
;
109 static const MemoryRegionOps aspeed_peci_ops
= {
110 .read
= aspeed_peci_read
,
111 .write
= aspeed_peci_write
,
112 .endianness
= DEVICE_LITTLE_ENDIAN
,
115 static void aspeed_peci_realize(DeviceState
*dev
, Error
**errp
)
117 AspeedPECIState
*s
= ASPEED_PECI(dev
);
118 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
120 memory_region_init_io(&s
->mmio
, OBJECT(s
), &aspeed_peci_ops
, s
,
121 TYPE_ASPEED_PECI
, 0x1000);
122 sysbus_init_mmio(sbd
, &s
->mmio
);
123 sysbus_init_irq(sbd
, &s
->irq
);
126 static void aspeed_peci_reset(DeviceState
*dev
)
128 AspeedPECIState
*s
= ASPEED_PECI(dev
);
130 memset(s
->regs
, 0, sizeof(s
->regs
));
133 static void aspeed_peci_class_init(ObjectClass
*klass
, void *data
)
135 DeviceClass
*dc
= DEVICE_CLASS(klass
);
137 dc
->realize
= aspeed_peci_realize
;
138 dc
->reset
= aspeed_peci_reset
;
139 dc
->desc
= "Aspeed PECI Controller";
142 static const TypeInfo aspeed_peci_types
[] = {
144 .name
= TYPE_ASPEED_PECI
,
145 .parent
= TYPE_SYS_BUS_DEVICE
,
146 .instance_size
= sizeof(AspeedPECIState
),
147 .class_init
= aspeed_peci_class_init
,
152 DEFINE_TYPES(aspeed_peci_types
);