qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

aspeed_peci.c (4282B)


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