qemu/hw/misc/aspeed_peci.c
<<
>>
Prefs
   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 */
  20REG32(PECI_CMD, 0x08)
  21    FIELD(PECI_CMD, FIRE, 0, 1)
  22
  23/* Interrupt Control Register */
  24REG32(PECI_INT_CTRL, 0x18)
  25
  26/* Interrupt Status Register */
  27REG32(PECI_INT_STS, 0x1C)
  28    FIELD(PECI_INT_STS, CMD_DONE, 0, 1)
  29
  30/* Rx/Tx Data Buffer Registers */
  31REG32(PECI_WR_DATA0, 0x20)
  32REG32(PECI_RD_DATA0, 0x30)
  33
  34static 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
  45static 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
  62static 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
 109static const MemoryRegionOps aspeed_peci_ops = {
 110    .read = aspeed_peci_read,
 111    .write = aspeed_peci_write,
 112    .endianness = DEVICE_LITTLE_ENDIAN,
 113};
 114
 115static 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
 126static void aspeed_peci_reset(DeviceState *dev)
 127{
 128    AspeedPECIState *s = ASPEED_PECI(dev);
 129
 130    memset(s->regs, 0, sizeof(s->regs));
 131}
 132
 133static 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
 142static 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
 152DEFINE_TYPES(aspeed_peci_types);
 153