aspeed_peci.c 4.2 KB

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