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 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 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 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 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 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 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