aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-10-24 10:06:49 +1100
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-10-24 10:07:19 +1100
commit531bdefda91102cb3240e4e3289900af0a50e78b (patch)
treecc95231a4ce4a0ae05e7d123eaac4bd9beedef2d
parentbed82938878cff16c539c1c6b3887b2cf65b4f57 (diff)
downloadskiboot-531bdefda91102cb3240e4e3289900af0a50e78b.zip
skiboot-531bdefda91102cb3240e4e3289900af0a50e78b.tar.gz
skiboot-531bdefda91102cb3240e4e3289900af0a50e78b.tar.bz2
irq/occ/opal: Add self-sent dummy interrupt
This makes OPAL use the OCC interrupt facility to send itself an interrupt whenever the OPAL event bit is set as a result of an OPAL call that wasn't itself opal_handle_interrupt() or opal_handle_hmi() (both of which we know the OS will already deal with appropriately). This ensures that OPAL event changes are notified to Linux via its interrupt path which is necessary for it to properly broadcast the state change to its various clients. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-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);