aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--asm/asm-offsets.c1
-rw-r--r--asm/head.S3
-rw-r--r--core/opal.c21
-rw-r--r--hw/occ.c42
-rw-r--r--hw/psi.c2
-rw-r--r--include/cpu.h1
-rw-r--r--include/skiboot.h5
7 files changed, 70 insertions, 5 deletions
diff --git a/asm/asm-offsets.c b/asm/asm-offsets.c
index 3440054..e33c181 100644
--- a/asm/asm-offsets.c
+++ b/asm/asm-offsets.c
@@ -35,6 +35,7 @@ int main(void)
OFFSET(CPUTHREAD_PIR, cpu_thread, pir);
OFFSET(CPUTHREAD_SAVE_R1, cpu_thread, save_r1);
OFFSET(CPUTHREAD_STATE, cpu_thread, state);
+ OFFSET(CPUTHREAD_CUR_TOKEN, cpu_thread, current_token);
OFFSET(STACK_TYPE, stack_frame, type);
OFFSET(STACK_LOCALS, stack_frame, locals);
diff --git a/asm/head.S b/asm/head.S
index 37a059d..a56f2bb 100644
--- a/asm/head.S
+++ b/asm/head.S
@@ -777,6 +777,9 @@ opal_entry:
/* Get the CPU thread */
GET_CPU()
+ /* Store token in CPU thread */
+ std %r0,CPUTHREAD_CUR_TOKEN(%r13)
+
/* Mark the stack frame */
li %r12,STACK_ENTRY_OPAL_API
std %r12,STACK_TYPE(%r1)
diff --git a/core/opal.c b/core/opal.c
index 4ad322c..f6bd00d 100644
--- a/core/opal.c
+++ b/core/opal.c
@@ -157,11 +157,26 @@ void opal_update_pending_evt(uint64_t evt_mask, uint64_t evt_values)
/* XXX FIXME: Use atomics instead ??? Or caller locks (con_lock ?) */
lock(&evt_lock);
new_evts = (opal_pending_events & ~evt_mask) | evt_values;
+ if (opal_pending_events != new_evts) {
+ uint64_t tok;
+
#ifdef OPAL_TRACE_EVT_CHG
- printf("OPAL: Evt change: 0x%016llx -> 0x%016llx\n",
- opal_pending_events, new_evts);
+ printf("OPAL: Evt change: 0x%016llx -> 0x%016llx\n",
+ opal_pending_events, new_evts);
#endif
- opal_pending_events = new_evts;
+ /*
+ * If an event gets *set* while we are in a different call chain
+ * than opal_handle_interrupt() or opal_handle_hmi(), then we
+ * artificially generate an interrupt (OCC interrupt specifically)
+ * to ensure that Linux properly broadcast the event change internally
+ */
+ if ((new_evts & ~opal_pending_events) != 0) {
+ tok = this_cpu()->current_token;
+ if (tok != OPAL_HANDLE_INTERRUPT && tok != OPAL_HANDLE_HMI)
+ occ_send_dummy_interrupt();
+ }
+ opal_pending_events = new_evts;
+ }
unlock(&evt_lock);
}
diff --git a/hw/occ.c b/hw/occ.c
index f1a7a27..74c9deb 100644
--- a/hw/occ.c
+++ b/hw/occ.c
@@ -480,6 +480,48 @@ static struct fsp_client fsp_occ_client = {
.message = fsp_occ_msg,
};
+#define OCB_OCI_OCCMISC 0x6a020
+#define OCB_OCI_OCCMISC_AND 0x6a021
+#define OCB_OCI_OCCMISC_OR 0x6a022
+#define OCB_OCI_OCIMISC_IRQ PPC_BIT(0)
+#define OCB_OCI_OCIMISC_IRQ_TMGT PPC_BIT(1)
+#define OCB_OCI_OCIMISC_IRQ_OPAL_DUMMY PPC_BIT(15)
+
+void occ_send_dummy_interrupt(void)
+{
+ xscom_writeme(OCB_OCI_OCCMISC_OR,
+ OCB_OCI_OCIMISC_IRQ |
+ OCB_OCI_OCIMISC_IRQ_OPAL_DUMMY);
+}
+
+static void occ_tmgt_interrupt(void)
+{
+ /* Not currently expected */
+ printf("OCC: TMGT interrupt !\n");
+}
+
+void occ_interrupt(void)
+{
+ uint64_t ireg;
+ int64_t rc;
+
+ /* The OCC interrupt is used to mux up to 15 different sources */
+ rc = xscom_readme(OCB_OCI_OCCMISC, &ireg);
+ if (rc) {
+ prerror("OCC: Failed to read interrupt status !\n");
+ /* Should we mask it in the XIVR ? */
+ return;
+ }
+ prlog(PR_TRACE, "OCC: IRQ received: %04llx\n", ireg >> 48);
+
+ /* Clear the bits */
+ xscom_writeme(OCB_OCI_OCCMISC_AND, ~ireg);
+
+ /* Dispatch */
+ if (ireg & OCB_OCI_OCIMISC_IRQ_TMGT)
+ occ_tmgt_interrupt();
+}
+
void occ_fsp_init(void)
{
/* OCC is P8 only */
diff --git a/hw/psi.c b/hw/psi.c
index 6752bf4..b96b611 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -276,7 +276,7 @@ static void handle_extra_interrupt(struct psi *psi)
* when available.
*/
if (val & PSIHB_IRQ_STAT_OCC)
- printf("PSI: OCC irq received\n");
+ occ_interrupt();
if (val & PSIHB_IRQ_STAT_FSI)
printf("PSI: FSI irq received\n");
if (val & PSIHB_IRQ_STAT_LPC)
diff --git a/include/cpu.h b/include/cpu.h
index 1f92b9c..7895e96 100644
--- a/include/cpu.h
+++ b/include/cpu.h
@@ -58,6 +58,7 @@ struct cpu_thread {
bool con_need_flush;
uint32_t hbrt_spec_wakeup; /* primary only */
uint64_t save_l2_fir_action1;
+ uint64_t current_token;
struct lock job_lock;
struct list_head job_queue;
diff --git a/include/skiboot.h b/include/skiboot.h
index 41180ae..1a1f96f 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -204,12 +204,15 @@ extern void nvram_read_complete(bool success);
struct flash_chip;
extern int flash_nvram_init(struct flash_chip *chip, uint32_t start,
uint32_t size);
-
/* UART stuff */
extern void uart_irq(void);
extern void uart_setup_linux_passthrough(void);
extern void uart_setup_opal_console(void);
+/* OCC interrupt */
+extern void occ_interrupt(void);
+extern void occ_send_dummy_interrupt(void);
+
/* Flatten device-tree */
extern void *create_dtb(const struct dt_node *root);