Reviewed-by: Chalapathi V <[email protected]
<mailto:[email protected]>
On 19/12/25 1:33 am, Caleb Schlossin wrote:
OCCFLG are scratch registers that can be shared with OCC firmware.
Log reads and writes to the registers as a reminder when we run
into more OCC code.
Add RW, WO_CLEAR and WO_OR SCOM Type enums in pnv_occ.c
Signed-off-by: Chalapathi V<[email protected]>
Signed-off-by: Caleb Schlossin<[email protected]>
---
hw/ppc/pnv_occ.c | 55 +++++++++++++++++++++++++++++++++++++---
include/hw/ppc/pnv_occ.h | 4 +++
2 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c
index 24b789c191..e605ae0fbc 100644
--- a/hw/ppc/pnv_occ.c
+++ b/hw/ppc/pnv_occ.c
@@ -195,6 +195,49 @@ static const TypeInfo pnv_occ_power8_type_info = {
#define P9_OCB_OCI_OCCMISC_CLEAR 0x6081
#define P9_OCB_OCI_OCCMISC_OR 0x6082
+/* OCC scratch registers for flag setting */
+#define P9_OCCFLG0 0x60ac
+#define P9_OCCFLG7_OR 0x60c3
+
+enum ScomType {
+ SCOM_TYPE_RW = 0,
+ SCOM_TYPE_WO_CLEAR = 1,
+ SCOM_TYPE_WO_OR = 2,
+};
+
+static void rw_occ_flag_regs(PnvOCC *occ, uint32_t offset, bool read,
+ uint64_t *val)
+{
+ int flag_num;
+ int flag_type;
+
+ /*
+ * Each OCCFLG register has SCOM0 - RW, SCOM1 - WO_CLEAR, SCOM2 - WO_OR
+ * hence devide by 3 to get flag index and mod 3 to get SCOM type.
+ */
+ flag_num = (offset - P9_OCCFLG0) / 3;
+ flag_type = (offset - P9_OCCFLG0) % 3;
+
+ if (read) {
+ if (flag_type) {
+ qemu_log_mask(LOG_GUEST_ERROR, "OCC: Write only register: Ox%"
+ PRIx32 "\n", offset);
+ return;
+ }
+ *val = occ->occflags[flag_num];
+ } else {
+ switch (flag_type) {
+ case SCOM_TYPE_RW:
+ occ->occflags[flag_num] = *val;
+ break;
+ case SCOM_TYPE_WO_CLEAR:
+ occ->occflags[flag_num] &= ~(*val);
+ break;
+ case SCOM_TYPE_WO_OR:
+ occ->occflags[flag_num] |= *val;
+ }
+ }
+}
static uint64_t pnv_occ_power9_xscom_read(void *opaque, hwaddr addr,
unsigned size)
@@ -207,8 +250,11 @@ static uint64_t pnv_occ_power9_xscom_read(void *opaque,
hwaddr addr,
case P9_OCB_OCI_OCCMISC:
val = occ->occmisc;
break;
+ case P9_OCCFLG0 ... P9_OCCFLG7_OR:
+ rw_occ_flag_regs(occ, offset, 1, &val);
+ break;
default:
- qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
+ qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register read: Ox%"
HWADDR_PRIx "\n", addr >> 3);
}
return val;
@@ -229,9 +275,12 @@ static void pnv_occ_power9_xscom_write(void *opaque,
hwaddr addr,
break;
case P9_OCB_OCI_OCCMISC:
pnv_occ_set_misc(occ, val);
- break;
+ break;
+ case P9_OCCFLG0 ... P9_OCCFLG7_OR:
+ rw_occ_flag_regs(occ, offset, 0, &val);
+ break;
default:
- qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
+ qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register write: Ox%"
HWADDR_PRIx "\n", addr >> 3);
}
}
diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h
index 013ea2e53e..8c9f1416eb 100644
--- a/include/hw/ppc/pnv_occ.h
+++ b/include/hw/ppc/pnv_occ.h
@@ -47,6 +47,10 @@ struct PnvOCC {
/* OCC Misc interrupt */
uint64_t occmisc;
+ /* OCC Flags */
+#define NR_FLAG_REGS 8
+ uint32_t occflags[NR_FLAG_REGS];
+
qemu_irq psi_irq;
/* OCCs operate on regions of HOMER memory */