diff mbox series

[RFC,3/4] ppc/pnv: add support for the PC MMIOs

Message ID 20230704134921.2626692-4-clg@kaod.org (mailing list archive)
State New, archived
Headers show
Series ppc: Improve multisocket support | expand

Commit Message

Cédric Le Goater July 4, 2023, 1:49 p.m. UTC
Associated with each NVT is a CI page address that is intended for use
by privileged interrupt management code to adjust the backlog counts
of a logical server and interrupt pending buffer for a specific
virtual processor. This backlog count adjustment function is valuable
to avoid extraneous physical interrupts when the hardware accumulates
the backlog count per event queue post while the software handles
multiple event queue entries on a single physical interrupt. Likewise
adjusting the Interrupt Pending Buffer allows a virtual processor to
process event queues of other priorities during one physical interrupt
cycle.

The NVT adjustment is initiated by a store byte (stb) or a double word
load instruction.

For the store byte operations that increment/decrement a backlog count
the value of the data byte is the amount added (counter saturates at
maximum value) / subtracted from the backlog counter (the counter does
not go negative).

For the store byte operations that set/reset an IPB priority bit, the
data byte is ignored.

The load double word operations that target a backlog counter
increment/decrement the backlog count by one count (counter saturates
at maximum value / does not go negative).

Load operations to an IPB return the pre-operation value, while load
operations to a backlog counter return the post-operation value, in
both cases right justified in the double word.

Programs may use the load operations if they need to know when the
operation has completed; this may be accomplished by introducing a
data dependency upon the returned load data. Other operation lengths
(other than store byte and load double word) are not supported –
results are boundedly undefined.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/pnv_xive.c | 85 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 80 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c
index b41ab85e01bd..844965cfe281 100644
--- a/hw/intc/pnv_xive.c
+++ b/hw/intc/pnv_xive.c
@@ -1773,17 +1773,92 @@  static uint64_t pnv_xive_pc_read(void *opaque, hwaddr addr,
                                  unsigned size)
 {
     PnvXive *xive = PNV_XIVE(opaque);
+    uint32_t offset = (addr & 0x1F0) >> 4;
+    uint8_t nvt_blk;
+    uint32_t nvt_idx;
+    XiveNVT nvt;
+    uint8_t ipb;
+    uint64_t ret = -1;
 
-    xive_error(xive, "PC: invalid read @%"HWADDR_PRIx, addr);
-    return -1;
+    if (size != 8) {
+        xive_error(xive, "PC: invalid read size %d @%"HWADDR_PRIx"\n",
+                   size, addr);
+        return -1;
+    }
+
+    /* TODO: support multi block */
+    nvt_blk = pnv_xive_block_id(xive);
+    nvt_idx = addr >> TM_SHIFT;
+
+    if (xive_router_get_nvt(XIVE_ROUTER(xive), nvt_blk, nvt_idx, &nvt)) {
+        xive_error(xive, "PC: invalid NVT %x/%x\n", nvt_blk, nvt_idx);
+        return -1;
+    }
+
+    ipb = xive_get_field32(NVT_W4_IPB, nvt.w4);
+
+    switch (offset) {
+    case  0x0 ... 0x7: /* set IBP bit x */
+        ret = ipb;
+        ipb |= 1 << offset;
+        break;
+    case 0x10 ... 0x17: /* reset IBP bit x */
+        ret = ipb;
+        ipb &= ~(1 << (offset - 0x10));
+        break;
+
+    case  0x8 ... 0xF: /* TODO: increment backlog */
+        /* backlog = offset - 0x8; */
+    case 0x18 ... 0x1F: /* TODO: decrement backlog */
+        /* backlog = offset - 0x18; */
+    default:
+        xive_error(xive, "PC: invalid write @%"HWADDR_PRIx"\n", addr);
+    }
+
+    if (ipb != xive_get_field32(NVT_W4_IPB, nvt.w4)) {
+        nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, ipb);
+        xive_router_write_nvt(XIVE_ROUTER(xive), nvt_blk, nvt_idx, &nvt, 4);
+    }
+
+    return ret;
 }
 
 static void pnv_xive_pc_write(void *opaque, hwaddr addr,
                               uint64_t value, unsigned size)
 {
     PnvXive *xive = PNV_XIVE(opaque);
+    uint32_t offset = (addr & 0x1F0) >> 4;
+    uint8_t nvt_blk;
+    uint32_t nvt_idx;
+    XiveNVT nvt;
+
+    if (size != 1) {
+        xive_error(xive, "PC: invalid write size %d @%"HWADDR_PRIx"\n",
+                   size, addr);
+        return;
+    }
+
+    /* TODO: support multi block */
+    nvt_blk = pnv_xive_block_id(xive);
+    nvt_idx = addr >> TM_SHIFT;
+
+    if (xive_router_get_nvt(XIVE_ROUTER(xive), nvt_blk, nvt_idx, &nvt)) {
+        xive_error(xive, "PC: invalid NVT %x/%x\n", nvt_blk, nvt_idx);
+        return;
+    }
 
-    xive_error(xive, "PC: invalid write to VC @%"HWADDR_PRIx, addr);
+    switch (offset) {
+    case  0x0 ... 0x7: /* ignored */
+    case 0x10 ... 0x17: /* ignored */
+        break;
+
+    case  0x8 ... 0xF: /* TODO: Add to backlog */
+        /* backlog = offset - 0x8; */
+    case 0x18 ... 0x1F: /* TODO: substract to backlog */
+        /* backlog = offset - 0x18; */
+    default:
+        xive_error(xive, "PC: invalid write @%"HWADDR_PRIx"\n", addr);
+    }
 }
 
 static const MemoryRegionOps pnv_xive_pc_ops = {
@@ -1791,11 +1866,11 @@  static const MemoryRegionOps pnv_xive_pc_ops = {
     .write = pnv_xive_pc_write,
     .endianness = DEVICE_BIG_ENDIAN,
     .valid = {
-        .min_access_size = 8,
+        .min_access_size = 1,
         .max_access_size = 8,
     },
     .impl = {
-        .min_access_size = 8,
+        .min_access_size = 1,
         .max_access_size = 8,
     },
 };