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