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 <chalapathi.v@linux.ibm.com>
Signed-off-by: Caleb Schlossin <calebs@linux.ibm.com>
---
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 */
--
2.47.3
On 25/12/18 02:03PM, 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 <chalapathi.v@linux.ibm.com>
> Signed-off-by: Caleb Schlossin <calebs@linux.ibm.com>
> ---
> 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.
nitpick: s/devide/divide
> + */
> + flag_num = (offset - P9_OCCFLG0) / 3;
> + flag_type = (offset - P9_OCCFLG0) % 3;
flag_num can be negative if offset is invalid, but since the function
is static, the current usage ensure that never happens. So, it's okay
with me.
Reviewed-by: Aditya Gupta <adityag@linux.ibm.com>
Thanks,
- Aditya G
Reviewed-by: Glenn Miles <milesg@linux.ibm.com>
Thanks,
Glenn
On Thu, 2025-12-18 at 14:03 -0600, 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 <chalapathi.v@linux.ibm.com>
> Signed-off-by: Caleb Schlossin <calebs@linux.ibm.com>
> ---
> 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 */
Reviewed-by: Chalapathi V <chalapathi.v@linux.ibm.com
<mailto:milesg@linux.ibm.com>
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<chalapathi.v@linux.ibm.com>
> Signed-off-by: Caleb Schlossin<calebs@linux.ibm.com>
> ---
> 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 */
© 2016 - 2026 Red Hat, Inc.