aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/opal-api/opal-messages.txt45
-rw-r--r--doc/opal-api/opal-prd-msg-113.txt11
-rw-r--r--hw/Makefile.inc2
-rw-r--r--hw/prd.c360
-rw-r--r--hw/psi.c2
-rw-r--r--include/skiboot.h6
-rw-r--r--platforms/astbmc/habanero.c2
-rw-r--r--platforms/astbmc/palmetto.c2
8 files changed, 428 insertions, 2 deletions
diff --git a/doc/opal-api/opal-messages.txt b/doc/opal-api/opal-messages.txt
index 448e9c7..1091d8b 100644
--- a/doc/opal-api/opal-messages.txt
+++ b/doc/opal-api/opal-messages.txt
@@ -77,3 +77,48 @@ perform a shutdown in the future.
The host OS can use the separate API OPAL_GET_DPO_STATUS to query OPAL for the
number of seconds before a forced shutdown will occur.
+
+OPAL_MSG_PRD
+------------
+
+This message is a OPAL-to-HBRT notification, and contains a
+struct opal_prd_msg:
+
+ enum opal_prd_msg_type {
+ OPAL_PRD_MSG_TYPE_INIT = 0, /* HBRT --> OPAL */
+ OPAL_PRD_MSG_TYPE_FINI, /* HBRT --> OPAL */
+ OPAL_PRD_MSG_TYPE_ATTN, /* HBRT <-- OPAL */
+ OPAL_PRD_MSG_TYPE_ATTN_ACK, /* HBRT --> OPAL */
+ OPAL_PRD_MSG_TYPE_OCC_ERROR, /* HBRT <-- OPAL */
+ OPAL_PRD_MSG_TYPE_OCC_RESET, /* HBRT <-- OPAL */
+ };
+
+ struct opal_prd_msg {
+ uint8_t type;
+ uint8_t pad[3];
+ __be32 token;
+ union {
+ struct {
+ __be64 version;
+ __be64 ipoll;
+ } init;
+ struct {
+ __be64 proc;
+ __be64 ipoll_status;
+ __be64 ipoll_mask;
+ } attn;
+ struct {
+ __be64 proc;
+ __be64 ipoll_ack;
+ } attn_ack;
+ struct {
+ __be64 chip;
+ } occ_error;
+ struct {
+ __be64 chip;
+ } occ_reset;
+ };
+ };
+
+Responses from the kernel use the same message format, but are passed
+through the opal_prd_msg call.
diff --git a/doc/opal-api/opal-prd-msg-113.txt b/doc/opal-api/opal-prd-msg-113.txt
new file mode 100644
index 0000000..b41f17e
--- /dev/null
+++ b/doc/opal-api/opal-prd-msg-113.txt
@@ -0,0 +1,11 @@
+
+OPAL_PRD_MSG call
+-------------
+
+The OPAL_PRD_MSG call is used to pass a struct opal_prd_msg from the HBRT
+code into opal, and is paired with the OPAL_PRD_MSG message type.
+
+Parameters:
+ struct opal_msg *msg
+
+Passes an opal_msg, of type OPAL_PRD_MSG, from the OS to OPAL.
diff --git a/hw/Makefile.inc b/hw/Makefile.inc
index 416d23c..395a65f 100644
--- a/hw/Makefile.inc
+++ b/hw/Makefile.inc
@@ -5,7 +5,7 @@ HW_OBJS = xscom.o chiptod.o gx.o cec.o lpc.o lpc-uart.o psi.o
HW_OBJS += homer.o slw.o occ.o fsi-master.o centaur.o
HW_OBJS += nx.o nx-rng.o nx-crypto.o nx-842.o
HW_OBJS += p7ioc.o p7ioc-inits.o p7ioc-phb.o p5ioc2.o p5ioc2-phb.o
-HW_OBJS += phb3.o sfc-ctrl.o fake-rtc.o bt.o p8-i2c.o
+HW_OBJS += phb3.o sfc-ctrl.o fake-rtc.o bt.o p8-i2c.o prd.o
HW=hw/built-in.o
include $(SRC)/hw/fsp/Makefile.inc
diff --git a/hw/prd.c b/hw/prd.c
new file mode 100644
index 0000000..1d9c617
--- /dev/null
+++ b/hw/prd.c
@@ -0,0 +1,360 @@
+/* Copyright 2014-2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * imitations under the License.
+ */
+
+#include <skiboot.h>
+#include <opal.h>
+#include <lock.h>
+#include <xscom.h>
+#include <chip.h>
+#include <opal-api.h>
+#include <opal-msg.h>
+#include <fsp.h>
+
+enum events {
+ EVENT_ATTN = 1 << 0,
+ EVENT_OCC_ERROR = 1 << 1,
+ EVENT_OCC_RESET = 1 << 2,
+};
+
+static uint8_t events[MAX_CHIPS];
+static uint64_t ipoll_status[MAX_CHIPS];
+static struct opal_prd_msg prd_msg;
+static bool prd_msg_inuse, prd_active;
+
+/* Locking:
+ *
+ * The events lock serialises access to the events, ipoll_status,
+ * prd_msg_inuse, and prd_active variables.
+ *
+ * The ipoll_lock protects against concurrent updates to the ipoll registers.
+ *
+ * The ipoll_lock may be acquired with events_lock held. This order must
+ * be preserved.
+ */
+static struct lock events_lock = LOCK_UNLOCKED;
+static struct lock ipoll_lock = LOCK_UNLOCKED;
+
+/* PRD registers */
+#define PRD_IPOLL_REG_MASK 0x01020013
+#define PRD_IPOLL_REG_STATUS 0x01020014
+#define PRD_IPOLL_XSTOP PPC_BIT(0) /* Xstop for host/core/millicode */
+#define PRD_IPOLL_RECOV PPC_BIT(1) /* Recoverable */
+#define PRD_IPOLL_SPEC_ATTN PPC_BIT(2) /* Special attention */
+#define PRD_IPOLL_HOST_ATTN PPC_BIT(3) /* Host attention */
+#define PRD_IPOLL_MASK PPC_BITMASK(0, 3)
+
+static int queue_prd_msg_hbrt(struct opal_prd_msg *msg,
+ void (*consumed)(void *data))
+{
+ uint64_t *buf;
+
+ BUILD_ASSERT(sizeof(*msg) / sizeof(uint64_t) == 4);
+
+ buf = (uint64_t *)msg;
+
+ return _opal_queue_msg(OPAL_MSG_PRD, msg, consumed, 4, buf);
+}
+
+static int queue_prd_msg_nop(struct opal_prd_msg *msg,
+ void (*consumed)(void *data))
+{
+ (void)msg;
+ (void)consumed;
+ return OPAL_UNSUPPORTED;
+}
+
+static int (*queue_prd_msg)(struct opal_prd_msg *msg,
+ void (*consumed)(void *data)) = queue_prd_msg_nop;
+
+static void send_pending_events(void);
+
+static void prd_msg_consumed(void *data)
+{
+ struct opal_prd_msg *msg = data;
+ uint32_t proc;
+ uint8_t event = 0;
+
+ lock(&events_lock);
+ switch (msg->type) {
+ case OPAL_PRD_MSG_TYPE_ATTN:
+ proc = msg->attn.proc;
+
+ /* If other ipoll events have been received in the time
+ * between prd_msg creation and consumption, we'll need to
+ * raise a separate ATTN message for those. So, we only
+ * clear the event if we don't have any further ipoll_status
+ * bits.
+ */
+ ipoll_status[proc] &= ~msg->attn.ipoll_status;
+ if (!ipoll_status[proc])
+ event = EVENT_ATTN;
+
+ break;
+ case OPAL_PRD_MSG_TYPE_OCC_ERROR:
+ proc = msg->occ_error.chip;
+ event = EVENT_OCC_ERROR;
+ break;
+ case OPAL_PRD_MSG_TYPE_OCC_RESET:
+ proc = msg->occ_reset.chip;
+ event = EVENT_OCC_RESET;
+ break;
+ default:
+ return;
+ }
+
+ events[proc] &= ~event;
+ prd_msg_inuse = false;
+ send_pending_events();
+ unlock(&events_lock);
+}
+
+static int populate_ipoll_msg(struct opal_prd_msg *msg, uint32_t proc)
+{
+ uint64_t ipoll_mask;
+ int rc;
+
+ lock(&ipoll_lock);
+ rc = xscom_read(proc, PRD_IPOLL_REG_MASK, &ipoll_mask);
+ unlock(&ipoll_lock);
+
+ if (rc) {
+ prlog(PR_ERR, "PRD: Unable to read ipoll status (chip %d)!\n",
+ proc);
+ return -1;
+ }
+
+ msg->attn.proc = proc;
+ msg->attn.ipoll_status = ipoll_status[proc];
+ msg->attn.ipoll_mask = ipoll_mask;
+ return 0;
+}
+
+static void send_pending_events(void)
+{
+ struct proc_chip *chip;
+ uint32_t proc;
+ uint8_t event;
+
+ assert(!prd_msg_inuse);
+
+ if (!prd_active)
+ return;
+
+ event = 0;
+
+ for_each_chip(chip) {
+ proc = chip->id;
+ if (events[proc]) {
+ event = events[proc];
+ break;
+ }
+ }
+
+ if (!event)
+ return;
+
+ prd_msg_inuse = true;
+ prd_msg.token = 0;
+
+ if (event & EVENT_ATTN) {
+ prd_msg.type = OPAL_PRD_MSG_TYPE_ATTN;
+ populate_ipoll_msg(&prd_msg, proc);
+ event = EVENT_ATTN;
+
+ } else if (event & EVENT_OCC_ERROR) {
+ prd_msg.type = OPAL_PRD_MSG_TYPE_OCC_ERROR;
+ prd_msg.occ_error.chip = proc;
+ event = EVENT_OCC_ERROR;
+
+ } else if (event & EVENT_OCC_RESET) {
+ prd_msg.type = OPAL_PRD_MSG_TYPE_OCC_RESET;
+ prd_msg.occ_reset.chip = proc;
+ event = EVENT_OCC_RESET;
+
+ }
+
+ queue_prd_msg(&prd_msg, prd_msg_consumed);
+}
+
+static void __prd_event(uint32_t proc, uint8_t event)
+{
+ events[proc] |= event;
+ if (!prd_msg_inuse)
+ send_pending_events();
+}
+
+static void prd_event(uint32_t proc, uint8_t event)
+{
+ lock(&events_lock);
+ __prd_event(proc, event);
+ unlock(&events_lock);
+}
+
+static int __ipoll_update_mask(uint32_t proc, bool set, uint64_t bits)
+{
+ uint64_t mask;
+ int rc;
+
+ rc = xscom_read(proc, PRD_IPOLL_REG_MASK, &mask);
+ if (rc)
+ return rc;
+
+ if (set)
+ mask |= bits;
+ else
+ mask &= ~bits;
+
+ return xscom_write(proc, PRD_IPOLL_REG_MASK, mask);
+}
+
+static int ipoll_record_and_mask_pending(uint32_t proc)
+{
+ uint64_t status;
+ int rc;
+
+ lock(&ipoll_lock);
+ rc = xscom_read(proc, PRD_IPOLL_REG_STATUS, &status);
+ if (!rc)
+ __ipoll_update_mask(proc, true, status);
+ unlock(&ipoll_lock);
+
+ if (!rc)
+ ipoll_status[proc] |= (status & PRD_IPOLL_MASK);
+
+ return rc;
+}
+
+/* Entry point for interrupts */
+void prd_psi_interrupt(uint32_t proc)
+{
+ int rc;
+
+ lock(&events_lock);
+
+ rc = ipoll_record_and_mask_pending(proc);
+ if (rc)
+ prlog(PR_ERR, "PRD: Failed to update IPOLL mask\n");
+
+ __prd_event(proc, EVENT_ATTN);
+
+ unlock(&events_lock);
+}
+
+void prd_tmgt_interrupt(uint32_t proc)
+{
+ prd_event(proc, EVENT_OCC_ERROR);
+}
+
+void prd_occ_reset(uint32_t proc)
+{
+ prd_event(proc, EVENT_OCC_RESET);
+}
+
+/* incoming message handlers */
+static int prd_msg_handle_attn_ack(struct opal_prd_msg *msg)
+{
+ int rc;
+
+ lock(&ipoll_lock);
+ rc = __ipoll_update_mask(msg->attn_ack.proc, false,
+ msg->attn_ack.ipoll_ack & PRD_IPOLL_MASK);
+ unlock(&ipoll_lock);
+
+ if (rc)
+ prlog(PR_ERR, "PRD: Unable to unmask ipoll!\n");
+
+ return rc;
+}
+
+static int prd_msg_handle_init(struct opal_prd_msg *msg)
+{
+ struct proc_chip *chip;
+
+ lock(&ipoll_lock);
+ for_each_chip(chip) {
+ __ipoll_update_mask(chip->id, false, msg->init.ipoll);
+ }
+ unlock(&ipoll_lock);
+
+ /* we're transitioning from inactive to active; send any pending tmgt
+ * interrupts */
+ lock(&events_lock);
+ prd_active = true;
+ if (!prd_msg_inuse)
+ send_pending_events();
+ unlock(&events_lock);
+
+ return OPAL_SUCCESS;
+}
+
+static int prd_msg_handle_fini(void)
+{
+ struct proc_chip *chip;
+
+ lock(&events_lock);
+ prd_active = false;
+ unlock(&events_lock);
+
+ lock(&ipoll_lock);
+ for_each_chip(chip) {
+ __ipoll_update_mask(chip->id, true, -1ul);
+ }
+ unlock(&ipoll_lock);
+
+ return OPAL_SUCCESS;
+}
+
+/* Entry from the host above */
+static int64_t opal_prd_msg(struct opal_prd_msg *msg)
+{
+ int rc;
+
+ switch (msg->type) {
+ case OPAL_PRD_MSG_TYPE_INIT:
+ rc = prd_msg_handle_init(msg);
+ break;
+ case OPAL_PRD_MSG_TYPE_FINI:
+ rc = prd_msg_handle_fini();
+ break;
+ case OPAL_PRD_MSG_TYPE_ATTN_ACK:
+ rc = prd_msg_handle_attn_ack(msg);
+ break;
+ default:
+ rc = OPAL_UNSUPPORTED;
+ }
+
+ return rc;
+}
+
+void prd_init(void)
+{
+ struct proc_chip *chip;
+
+ /* mask everything */
+ lock(&ipoll_lock);
+ for_each_chip(chip) {
+ __ipoll_update_mask(chip->id, true, -1ul);
+ }
+ unlock(&ipoll_lock);
+
+ if (fsp_present()) {
+ /* todo: FSP implementation */
+ queue_prd_msg = queue_prd_msg_nop;
+ } else {
+ queue_prd_msg = queue_prd_msg_hbrt;
+ opal_register(OPAL_PRD_MSG, opal_prd_msg, 1);
+ }
+}
diff --git a/hw/psi.c b/hw/psi.c
index b96cb0f..baba154 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -298,7 +298,7 @@ static void handle_extra_interrupt(struct psi *psi)
p8_i2c_interrupt(psi->chip_id);
}
if (val & PSIHB_IRQ_STAT_LOCAL_ERR)
- printf("PSI: ATTN irq received\n");
+ prd_psi_interrupt(psi->chip_id);
if (val & PSIHB_IRQ_STAT_HOST_ERR) {
if (platform.external_irq)
platform.external_irq(psi->chip_id);
diff --git a/include/skiboot.h b/include/skiboot.h
index 146ff80..9dfe0e7 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -218,6 +218,12 @@ extern void occ_send_dummy_interrupt(void);
/* OCC load support */
extern void occ_poke_load_queue(void);
+/* PRD */
+extern void prd_psi_interrupt(uint32_t proc);
+extern void prd_tmgt_interrupt(uint32_t proc);
+extern void prd_occ_reset(uint32_t proc);
+extern void prd_init(void);
+
/* Flatten device-tree */
extern void *create_dtb(const struct dt_node *root);
diff --git a/platforms/astbmc/habanero.c b/platforms/astbmc/habanero.c
index 0ec867f..8575be5 100644
--- a/platforms/astbmc/habanero.c
+++ b/platforms/astbmc/habanero.c
@@ -38,6 +38,8 @@ static bool habanero_probe(void)
/* Lot of common early inits here */
astbmc_early_init();
+ prd_init();
+
return true;
}
diff --git a/platforms/astbmc/palmetto.c b/platforms/astbmc/palmetto.c
index 3435d0b..dee2b06 100644
--- a/platforms/astbmc/palmetto.c
+++ b/platforms/astbmc/palmetto.c
@@ -39,6 +39,8 @@ static bool palmetto_probe(void)
/* Lot of common early inits here */
astbmc_early_init();
+ prd_init();
+
return true;
}