diff options
-rw-r--r-- | asm/asm-offsets.c | 1 | ||||
-rw-r--r-- | asm/head.S | 3 | ||||
-rw-r--r-- | core/opal.c | 21 | ||||
-rw-r--r-- | hw/occ.c | 42 | ||||
-rw-r--r-- | hw/psi.c | 2 | ||||
-rw-r--r-- | include/cpu.h | 1 | ||||
-rw-r--r-- | include/skiboot.h | 5 |
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); @@ -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); } @@ -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 */ @@ -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); |