xref: /openbmc/qemu/hw/misc/aspeed_peci.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
155c57023SPeter Delevoryas /*
255c57023SPeter Delevoryas  * Aspeed PECI Controller
355c57023SPeter Delevoryas  *
455c57023SPeter Delevoryas  * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com)
555c57023SPeter Delevoryas  *
655c57023SPeter Delevoryas  * This code is licensed under the GPL version 2 or later. See the COPYING
755c57023SPeter Delevoryas  * file in the top-level directory.
855c57023SPeter Delevoryas  */
955c57023SPeter Delevoryas 
1055c57023SPeter Delevoryas #include "qemu/osdep.h"
1155c57023SPeter Delevoryas #include "qemu/log.h"
1255c57023SPeter Delevoryas #include "hw/irq.h"
1355c57023SPeter Delevoryas #include "hw/misc/aspeed_peci.h"
1455c57023SPeter Delevoryas #include "hw/registerfields.h"
1555c57023SPeter Delevoryas #include "trace.h"
1655c57023SPeter Delevoryas 
1755c57023SPeter Delevoryas #define ASPEED_PECI_CC_RSP_SUCCESS (0x40U)
1855c57023SPeter Delevoryas 
1955c57023SPeter Delevoryas /* Command Register */
2055c57023SPeter Delevoryas REG32(PECI_CMD, 0x08)
2155c57023SPeter Delevoryas     FIELD(PECI_CMD, FIRE, 0, 1)
2255c57023SPeter Delevoryas 
2355c57023SPeter Delevoryas /* Interrupt Control Register */
2455c57023SPeter Delevoryas REG32(PECI_INT_CTRL, 0x18)
2555c57023SPeter Delevoryas 
2655c57023SPeter Delevoryas /* Interrupt Status Register */
2755c57023SPeter Delevoryas REG32(PECI_INT_STS, 0x1C)
2855c57023SPeter Delevoryas     FIELD(PECI_INT_STS, CMD_DONE, 0, 1)
2955c57023SPeter Delevoryas 
3055c57023SPeter Delevoryas /* Rx/Tx Data Buffer Registers */
3155c57023SPeter Delevoryas REG32(PECI_WR_DATA0, 0x20)
3255c57023SPeter Delevoryas REG32(PECI_RD_DATA0, 0x30)
3355c57023SPeter Delevoryas 
aspeed_peci_raise_interrupt(AspeedPECIState * s,uint32_t status)3455c57023SPeter Delevoryas static void aspeed_peci_raise_interrupt(AspeedPECIState *s, uint32_t status)
3555c57023SPeter Delevoryas {
3655c57023SPeter Delevoryas     trace_aspeed_peci_raise_interrupt(s->regs[R_PECI_INT_CTRL], status);
3755c57023SPeter Delevoryas 
3855c57023SPeter Delevoryas     s->regs[R_PECI_INT_STS] = s->regs[R_PECI_INT_CTRL] & status;
3955c57023SPeter Delevoryas     if (!s->regs[R_PECI_INT_STS]) {
4055c57023SPeter Delevoryas         return;
4155c57023SPeter Delevoryas     }
4255c57023SPeter Delevoryas     qemu_irq_raise(s->irq);
4355c57023SPeter Delevoryas }
4455c57023SPeter Delevoryas 
aspeed_peci_read(void * opaque,hwaddr offset,unsigned size)4555c57023SPeter Delevoryas static uint64_t aspeed_peci_read(void *opaque, hwaddr offset, unsigned size)
4655c57023SPeter Delevoryas {
4755c57023SPeter Delevoryas     AspeedPECIState *s = ASPEED_PECI(opaque);
4855c57023SPeter Delevoryas     uint64_t data;
4955c57023SPeter Delevoryas 
5055c57023SPeter Delevoryas     if (offset >= ASPEED_PECI_NR_REGS << 2) {
5155c57023SPeter Delevoryas         qemu_log_mask(LOG_GUEST_ERROR,
5255c57023SPeter Delevoryas                       "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
5355c57023SPeter Delevoryas                       __func__, offset);
5455c57023SPeter Delevoryas         return 0;
5555c57023SPeter Delevoryas     }
5655c57023SPeter Delevoryas     data = s->regs[offset >> 2];
5755c57023SPeter Delevoryas 
5855c57023SPeter Delevoryas     trace_aspeed_peci_read(offset, data);
5955c57023SPeter Delevoryas     return data;
6055c57023SPeter Delevoryas }
6155c57023SPeter Delevoryas 
aspeed_peci_write(void * opaque,hwaddr offset,uint64_t data,unsigned size)6255c57023SPeter Delevoryas static void aspeed_peci_write(void *opaque, hwaddr offset, uint64_t data,
6355c57023SPeter Delevoryas                               unsigned size)
6455c57023SPeter Delevoryas {
6555c57023SPeter Delevoryas     AspeedPECIState *s = ASPEED_PECI(opaque);
6655c57023SPeter Delevoryas 
6755c57023SPeter Delevoryas     trace_aspeed_peci_write(offset, data);
6855c57023SPeter Delevoryas 
6955c57023SPeter Delevoryas     if (offset >= ASPEED_PECI_NR_REGS << 2) {
7055c57023SPeter Delevoryas         qemu_log_mask(LOG_GUEST_ERROR,
7155c57023SPeter Delevoryas                       "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
7255c57023SPeter Delevoryas                       __func__, offset);
7355c57023SPeter Delevoryas         return;
7455c57023SPeter Delevoryas     }
7555c57023SPeter Delevoryas 
7655c57023SPeter Delevoryas     switch (offset) {
7755c57023SPeter Delevoryas     case A_PECI_INT_STS:
7855c57023SPeter Delevoryas         s->regs[R_PECI_INT_STS] &= ~data;
7955c57023SPeter Delevoryas         if (!s->regs[R_PECI_INT_STS]) {
8055c57023SPeter Delevoryas             qemu_irq_lower(s->irq);
8155c57023SPeter Delevoryas         }
8255c57023SPeter Delevoryas         break;
8355c57023SPeter Delevoryas     case A_PECI_CMD:
8455c57023SPeter Delevoryas         /*
8555c57023SPeter Delevoryas          * Only the FIRE bit is writable. Once the command is complete, it
8655c57023SPeter Delevoryas          * should be cleared. Since we complete the command immediately, the
8755c57023SPeter Delevoryas          * value is not stored in the register array.
8855c57023SPeter Delevoryas          */
8955c57023SPeter Delevoryas         if (!FIELD_EX32(data, PECI_CMD, FIRE)) {
9055c57023SPeter Delevoryas             break;
9155c57023SPeter Delevoryas         }
9255c57023SPeter Delevoryas         if (s->regs[R_PECI_INT_STS]) {
9355c57023SPeter Delevoryas             qemu_log_mask(LOG_GUEST_ERROR, "%s: Interrupt status must be "
9455c57023SPeter Delevoryas                           "cleared before firing another command: 0x%08x\n",
9555c57023SPeter Delevoryas                           __func__, s->regs[R_PECI_INT_STS]);
9655c57023SPeter Delevoryas             break;
9755c57023SPeter Delevoryas         }
9855c57023SPeter Delevoryas         s->regs[R_PECI_RD_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS;
9955c57023SPeter Delevoryas         s->regs[R_PECI_WR_DATA0] = ASPEED_PECI_CC_RSP_SUCCESS;
10055c57023SPeter Delevoryas         aspeed_peci_raise_interrupt(s,
10155c57023SPeter Delevoryas                                     FIELD_DP32(0, PECI_INT_STS, CMD_DONE, 1));
10255c57023SPeter Delevoryas         break;
10355c57023SPeter Delevoryas     default:
10455c57023SPeter Delevoryas         s->regs[offset / sizeof(s->regs[0])] = data;
10555c57023SPeter Delevoryas         break;
10655c57023SPeter Delevoryas     }
10755c57023SPeter Delevoryas }
10855c57023SPeter Delevoryas 
10955c57023SPeter Delevoryas static const MemoryRegionOps aspeed_peci_ops = {
11055c57023SPeter Delevoryas     .read = aspeed_peci_read,
11155c57023SPeter Delevoryas     .write = aspeed_peci_write,
11255c57023SPeter Delevoryas     .endianness = DEVICE_LITTLE_ENDIAN,
11355c57023SPeter Delevoryas };
11455c57023SPeter Delevoryas 
aspeed_peci_realize(DeviceState * dev,Error ** errp)11555c57023SPeter Delevoryas static void aspeed_peci_realize(DeviceState *dev, Error **errp)
11655c57023SPeter Delevoryas {
11755c57023SPeter Delevoryas     AspeedPECIState *s = ASPEED_PECI(dev);
11855c57023SPeter Delevoryas     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
11955c57023SPeter Delevoryas 
12055c57023SPeter Delevoryas     memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_peci_ops, s,
12155c57023SPeter Delevoryas                           TYPE_ASPEED_PECI, 0x1000);
12255c57023SPeter Delevoryas     sysbus_init_mmio(sbd, &s->mmio);
12355c57023SPeter Delevoryas     sysbus_init_irq(sbd, &s->irq);
12455c57023SPeter Delevoryas }
12555c57023SPeter Delevoryas 
aspeed_peci_reset(DeviceState * dev)12655c57023SPeter Delevoryas static void aspeed_peci_reset(DeviceState *dev)
12755c57023SPeter Delevoryas {
12855c57023SPeter Delevoryas     AspeedPECIState *s = ASPEED_PECI(dev);
12955c57023SPeter Delevoryas 
13055c57023SPeter Delevoryas     memset(s->regs, 0, sizeof(s->regs));
13155c57023SPeter Delevoryas }
13255c57023SPeter Delevoryas 
aspeed_peci_class_init(ObjectClass * klass,void * data)13355c57023SPeter Delevoryas static void aspeed_peci_class_init(ObjectClass *klass, void *data)
13455c57023SPeter Delevoryas {
13555c57023SPeter Delevoryas     DeviceClass *dc = DEVICE_CLASS(klass);
13655c57023SPeter Delevoryas 
13755c57023SPeter Delevoryas     dc->realize = aspeed_peci_realize;
138*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, aspeed_peci_reset);
13955c57023SPeter Delevoryas     dc->desc = "Aspeed PECI Controller";
14055c57023SPeter Delevoryas }
14155c57023SPeter Delevoryas 
14255c57023SPeter Delevoryas static const TypeInfo aspeed_peci_types[] = {
14355c57023SPeter Delevoryas     {
14455c57023SPeter Delevoryas         .name = TYPE_ASPEED_PECI,
14555c57023SPeter Delevoryas         .parent = TYPE_SYS_BUS_DEVICE,
14655c57023SPeter Delevoryas         .instance_size = sizeof(AspeedPECIState),
14755c57023SPeter Delevoryas         .class_init = aspeed_peci_class_init,
14855c57023SPeter Delevoryas         .abstract = false,
14955c57023SPeter Delevoryas     },
15055c57023SPeter Delevoryas };
15155c57023SPeter Delevoryas 
15255c57023SPeter Delevoryas DEFINE_TYPES(aspeed_peci_types);
153