[Skiboot] [PATCH v3 3/4] Add SBE driver support
Vasant Hegde
hegdevasant at linux.vnet.ibm.com
Tue Mar 14 16:01:57 AEDT 2017
SBE (Self Boot Engine) on P9 has two different jobs:
- Boot the chip up to the point the core is functional
- Provide various services like timer, scom, etc., at runtime
OPAL can communicate to SBE via a set of data and control registers provided
by the PSU block in P9 chip.
- Four 8 byte registers for Host to send command packets to SBE.
- Four 8 byte registers for SBE to send response packets to Host
- Two doorbell registers (1 on each side) to alert either party
when data is placed in above mentioned data register.
Protocol constraints:
Only one command is accepted in the command buffer until the response for the
command is enqueued in the response buffer by SBE.
Usage:
We will use SBE for various purposes like timer, sreset, MPIPL, etc.
This patch implements the SBE MBOX spec for OPAL to communicate with
SBE.
Design consideration:
- SBE works on request - response basis. It will not send any notification
without request.
- Each chip has SBE. We need to track SBE messages per chip. Hence added
per chip sbe structure and list of messages to that chip.
- SBE accepts only one command at a time. Hence serialized MBOX commands.
- OPAL gets interrupted once SBE sets doorbell register
- OPAL has to clear doorbell register after reading response.
- Every command class has timeout option. Timed out messages are discarded.
- SBE MBOX commands can be classified into four types :
- Those that must be sent to the master only (ex: sending MDST/MDDT info)
- Those that must be sent to slaves only (ex: continue MPIPL)
- Those that must be sent to all chips (ex: close insecure window)
- Those that can be sent to any chip (ex: timer)
Signed-off-by: Vasant Hegde <hegdevasant at linux.vnet.ibm.com>
Testing:
I have tested driver with timer chip op and its working fine.
---
core/init.c | 7 +
hw/Makefile.inc | 2 +-
hw/psi.c | 3 +-
hw/sbe_p9.c | 614 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/chip.h | 4 +
include/sbe_p9.h | 231 +++++++++++++++++++++
6 files changed, 859 insertions(+), 2 deletions(-)
create mode 100644 hw/sbe_p9.c
create mode 100644 include/sbe_p9.h
diff --git a/core/init.c b/core/init.c
index 983ead5..6e6d8b2 100644
--- a/core/init.c
+++ b/core/init.c
@@ -47,6 +47,7 @@
#include <nvram.h>
#include <libstb/stb.h>
#include <libstb/container.h>
+#include <sbe_p9.h>
enum proc_gen proc_gen;
@@ -882,6 +883,12 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
*/
chiptod_init();
+ /*
+ * SBE uses TB value for scheduling timer. Hence init after
+ * chiptod init
+ */
+ sbe_init();
+
/* Initialize i2c */
p8_i2c_init();
diff --git a/hw/Makefile.inc b/hw/Makefile.inc
index 96a99e1..9564ebc 100644
--- a/hw/Makefile.inc
+++ b/hw/Makefile.inc
@@ -6,7 +6,7 @@ HW_OBJS += nx.o nx-rng.o nx-crypto.o nx-842.o
HW_OBJS += p7ioc.o p7ioc-inits.o p7ioc-phb.o
HW_OBJS += phb3.o sfc-ctrl.o fake-rtc.o bt.o p8-i2c.o prd.o
HW_OBJS += dts.o lpc-rtc.o npu.o npu-hw-procedures.o xive.o phb4.o
-HW_OBJS += fake-nvram.o lpc-mbox.o sbe_p8.o
+HW_OBJS += fake-nvram.o lpc-mbox.o sbe_p8.o sbe_p9.o
HW=hw/built-in.o
# FIXME hack this for now
diff --git a/hw/psi.c b/hw/psi.c
index 089f429..3e87ade 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -33,6 +33,7 @@
#include <platform.h>
#include <errorlog.h>
#include <xive.h>
+#include <sbe_p9.h>
static LIST_HEAD(psis);
static u64 psi_link_timer;
@@ -603,7 +604,7 @@ static void psihb_p9_interrupt(struct irq_source *is, uint32_t isn)
printf("PSI: DIO irq received\n");
break;
case P9_PSI_IRQ_PSU:
- printf("PSI: PSU irq received\n");
+ sbe_interrupt(psi->chip_id);
break;
}
}
diff --git a/hw/sbe_p9.c b/hw/sbe_p9.c
new file mode 100644
index 0000000..3e81187
--- /dev/null
+++ b/hw/sbe_p9.c
@@ -0,0 +1,614 @@
+/* Copyright 2017 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
+ * limitations under the License.
+ */
+
+#define pr_fmt(fmt) "SBE: " fmt
+
+#include <chip.h>
+#include <errorlog.h>
+#include <lock.h>
+#include <opal.h>
+#include <sbe_p9.h>
+#include <skiboot.h>
+#include <timebase.h>
+#include <trace.h>
+#include <xscom.h>
+
+struct sbe {
+ /* Chip ID to send message */
+ u32 chip_id;
+
+ /* List to hold SBE queue messages */
+ struct list_head msg_list;
+
+ struct lock lock;
+
+ /* SBE MBOX message sequence number */
+ u16 cur_seq;
+
+ /*
+ * Sending messages to SBE is serialized. We can use
+ * single buffer per SBE to hold response message.
+ */
+ struct sbe_msg *resp;
+};
+
+/* Default SBE chip ID */
+static u32 sbe_default_chip_id = -1;
+
+#define SBE_STATUS_PRI_SHIFT 0x30
+#define SBE_STATUS_SEC_SHIFT 0x20
+#define SBE_FFDC_PRESENT PPC_BIT(1)
+
+static inline bool sbe_ffdc_present(struct sbe_msg *resp)
+{
+ return (resp->reg[0] & SBE_FFDC_PRESENT);
+}
+
+/*
+ * bit 0 : PCBPIB status present in response
+ * bit 1 : FFDC package(s) present in response
+ * bit 2-3 : Reserved
+ * bit 4-15 : Primary status code
+ */
+static inline u16 sbe_get_primary_rc(struct sbe_msg *resp)
+{
+ return ((resp->reg[0] >> SBE_STATUS_PRI_SHIFT) & 0xfff);
+}
+
+static inline void sbe_set_primary_rc(struct sbe_msg *resp, u64 rc)
+{
+ resp->reg[0] |= (rc << SBE_STATUS_PRI_SHIFT);
+}
+
+static u64 sbe_rreg(u32 chip_id, u64 reg)
+{
+ u64 data = 0;
+
+ xscom_read(chip_id, reg, &data);
+ return be64_to_cpu(data);
+}
+
+static void sbe_reg_dump(u32 chip_id)
+{
+#define SBE_DUMP_REG_ONE(chip_id, x) \
+ prlog(PR_DEBUG, " %20s: %llx\n", #x, sbe_rreg(chip_id, x));
+
+ prlog(PR_DEBUG, "MBOX register dump for chip : %x\n", chip_id);
+ SBE_DUMP_REG_ONE(chip_id, PSU_SBE_DOORBELL_REG_RW);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG0);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG1);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG2);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG3);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_DOORBELL_REG_RW);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG4);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG5);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG6);
+ SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG7);
+}
+
+static u64 sbe_get_cmdclass_timeout(u16 cmdclass)
+{
+ u8 class = (u8)(cmdclass >> 8) & 0xff;
+
+ switch (class) {
+ case SBE_MCLASS_CORE_STATE:
+ case SBE_MCLASS_TIMER:
+ case SBE_MCLASS_MPIPL:
+ case SBE_MCLASS_SECURITY:
+ return SBE_CMD_TIMEOUT_SHORT_MS;
+ case SBE_MCLASS_GENERIC:
+ if (cmdclass == SBE_CMD_QUIESCE_SBE) /* Special case */
+ return SBE_CMD_TIMEOUT_LONG_MS;
+ return SBE_CMD_TIMEOUT_SHORT_MS;
+ case SBE_MCLASS_SCOM:
+ case SBE_MCLASS_RING:
+ return SBE_CMD_TIMEOUT_LONG_MS;
+ default:
+ prlog(PR_ERR, "Invalid command class\n");
+ return 0;
+ }
+}
+
+void sbe_freemsg(struct sbe_msg *msg)
+{
+ free(msg);
+}
+
+static void sbe_fillmsg(struct sbe_msg *msg, u16 cmd,
+ u16 ctrl_flag, u64 reg1, u64 reg2, u64 reg3)
+{
+ bool response = !!(ctrl_flag & SBE_CMD_CTRL_RESP_REQ);
+
+ /* Seqence ID is filled by sbe_queue_msg() */
+ msg->reg[0] = ((u64)ctrl_flag << 32) | cmd;
+ msg->reg[1] = reg1;
+ msg->reg[2] = reg2;
+ msg->reg[3] = reg3;
+
+ prlog(PR_TRACE, "MBOX message :\n\t Reg0 : %llx\n\t "
+ "Reg1 : %llx\n\t Reg2 : %llx\n\t Reg3 : %lld\n",
+ msg->reg[0], msg->reg[1], msg->reg[2], msg->reg[3]);
+
+ msg->state = sbe_msg_unused;
+ msg->response = response;
+}
+
+/*
+ * Handles "command with direct data" format only.
+ *
+ * Note: All mbox messages of our interest uses direct data format.
+ * If we need indirect data format then we have to enhance
+ * this function.
+ */
+struct sbe_msg *sbe_mkmsg(u16 cmd, u16 ctrl_flag,
+ u64 reg1, u64 reg2, u64 reg3)
+{
+ struct sbe_msg *msg;
+
+ msg = zalloc(sizeof(struct sbe_msg));
+ if (!msg) {
+ prlog(PR_ERR, "Failed to allocate memory for SBE message\n");
+ return NULL;
+ }
+
+ sbe_fillmsg(msg, cmd, ctrl_flag, reg1, reg2, reg3);
+ return msg;
+}
+
+static inline bool sbe_busy(struct sbe_msg *msg)
+{
+ switch (msg->state) {
+ case sbe_msg_unused:
+ case sbe_msg_queued:
+ case sbe_msg_response:
+ case sbe_msg_done:
+ case sbe_msg_timeout:
+ case sbe_msg_cancelled:
+ return false;
+ default: /* + sbe_msg_sent, sbe_msg_wresp */
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * Read SBE doorbell to confirm its ready to accept command. Since the device
+ * driver single threads the requests, we should never see not being ready to
+ * send a request.
+ */
+static bool sbe_hw_ready(u32 chip_id)
+{
+ int rc;
+ u64 data;
+
+ rc = xscom_read(chip_id, PSU_SBE_DOORBELL_REG_RW, &data);
+ if (rc) {
+ prlog(PR_ERR, "Failed to read Host to SBE doorbell register "
+ "[chip id = %x]\n", chip_id);
+ return false;
+ }
+
+ if (data & HOST_SBE_MSG_WAITING) {
+ prlog(PR_ERR, "Not ready to accept command [chip id = %x]."
+ " Doorbell reg : %llx\n", chip_id, data);
+ /* Dump register content */
+ sbe_reg_dump(chip_id);
+ return false;
+ }
+
+ return true;
+}
+
+static int sbe_msg_send(u32 chip_id, struct sbe_msg *msg)
+{
+ int rc, i;
+ u64 addr, *data;
+
+ addr = PSU_HOST_SBE_MBOX_REG0;
+ data = &msg->reg[0];
+
+ for (i = 0; i < NR_HOST_SBE_MBOX_REG; i++) {
+ rc = xscom_write(chip_id, addr, *data);
+ if (rc)
+ return rc;
+
+ addr++;
+ data++;
+ }
+
+ rc = xscom_write(chip_id, PSU_SBE_DOORBELL_REG_OR,
+ HOST_SBE_MSG_WAITING);
+ if (rc == OPAL_SUCCESS)
+ msg->state = sbe_msg_sent;
+
+ return rc;
+}
+
+static int sbe_msg_receive(u32 chip_id, struct sbe_msg *resp)
+{
+ int i;
+ int rc = OPAL_SUCCESS;
+ u64 addr, *data;
+
+ addr = PSU_HOST_SBE_MBOX_REG4;
+ data = &resp->reg[0];
+
+ for (i = 0; i < NR_HOST_SBE_MBOX_REG; i++) {
+ rc = xscom_read(chip_id, addr, data);
+ if (rc)
+ return rc;
+
+ addr++;
+ data++;
+ }
+
+ return rc;
+}
+
+/* This is called with sbe->lock */
+static void sbe_msg_complete(struct sbe *sbe, struct sbe_msg *msg)
+{
+ prlog(PR_INSANE, "Completing msg [chip id = %x], reg0 : 0x%llx\n",
+ sbe->chip_id, msg->reg[0]);
+
+ msg->state = sbe_msg_done;
+ if (msg->response)
+ msg->resp = sbe->resp;
+ list_del_from(&sbe->msg_list, &msg->link);
+
+ if (msg->complete) {
+ unlock(&sbe->lock);
+ msg->complete(msg);
+ lock(&sbe->lock);
+ }
+}
+
+static void sbe_msg_send_complete(struct sbe *sbe, struct sbe_msg *msg)
+{
+ u64 timeout;
+
+ prlog(PR_INSANE, "Completing send [chip id = %x], reg0 : 0x%llx\n",
+ sbe->chip_id, msg->reg[0]);
+
+ /* Need response */
+ if (msg->response) {
+ timeout = sbe_get_cmdclass_timeout(msg->reg[0] & 0xffff);
+ msg->timeout = mftb() + msecs_to_tb(timeout);
+ msg->state = sbe_msg_wresp;
+ } else {
+ sbe_msg_complete(sbe, msg);
+ }
+}
+
+static void sbe_poke_queue(struct sbe *sbe)
+{
+ int rc;
+ struct sbe_msg *msg;
+
+ if (list_empty(&sbe->msg_list))
+ return;
+
+ msg = list_top(&sbe->msg_list, struct sbe_msg, link);
+ if (sbe_busy(msg))
+ return;
+
+ if (!sbe_hw_ready(sbe->chip_id))
+ return;
+
+ /* Send message */
+ rc = sbe_msg_send(sbe->chip_id, msg);
+ if (rc) {
+ /* XXX commit error log */
+ prlog(PR_ERR, "Failed to send message to SBE "
+ "[chip id = %x]\n", sbe->chip_id);
+
+ if (msg->response)
+ sbe_set_primary_rc(sbe->resp, SBE_STATUS_PRI_GENERIC_ERR);
+
+ sbe_msg_complete(sbe, msg);
+ } else {
+ sbe_msg_send_complete(sbe, msg);
+ }
+
+ /* Its safe to make recursive call here as its limited to sending
+ * messages to SBE.
+ */
+ sbe_poke_queue(sbe);
+}
+
+/*
+ * WARNING:
+ * Only one command is accepted in the command buffer until the response
+ * for the command is enqueued in the response buffer by SBE.
+ *
+ * Head of msg_list contains in-flight message. Hence we should always
+ * add new message to tail of the list.
+ */
+int sbe_queue_msg(u32 chip_id, struct sbe_msg *msg,
+ void (*comp)(struct sbe_msg *msg))
+{
+ struct proc_chip *chip;
+ struct sbe *sbe;
+
+ if (!msg)
+ return OPAL_PARAMETER;
+
+ if (sbe_default_chip_id == -1)
+ return OPAL_INTERNAL_ERROR;
+
+ /* Default to SBE on master chip */
+ if (chip_id == -1)
+ chip = get_chip(sbe_default_chip_id);
+ else
+ chip = get_chip(chip_id);
+
+ if (chip == NULL || chip->sbe == NULL)
+ return OPAL_PARAMETER;
+
+ sbe = chip->sbe;
+
+ /* Set completion */
+ msg->complete = comp;
+ msg->state = sbe_msg_queued;
+
+ lock(&sbe->lock);
+ /* Update sequence number */
+ msg->reg[0] = msg->reg[0] | (sbe->cur_seq << 16);
+ sbe->cur_seq++;
+
+ /* Reset sequence number */
+ if (sbe->cur_seq == 0xffff)
+ sbe->cur_seq = 1;
+
+ /* Add message to queue */
+ list_add_tail(&sbe->msg_list, &msg->link);
+ sbe_poke_queue(sbe);
+ unlock(&sbe->lock);
+
+ return OPAL_SUCCESS;
+}
+
+int sbe_sync_msg(u32 chip_id, struct sbe_msg *msg, bool autofree)
+{
+ int rc = 0;
+ struct proc_chip *chip;
+
+ chip = get_chip(chip_id);
+ if (chip == NULL || chip->sbe == NULL)
+ return OPAL_PARAMETER;
+
+ rc = sbe_queue_msg(chip_id, msg, NULL);
+ if (rc)
+ goto free_msg;
+
+ while (msg->state != sbe_msg_done &&
+ msg->state != sbe_msg_cancelled) {
+ time_wait_us(10);
+ sbe_poke_queue(chip->sbe);
+ }
+
+ if (msg->response)
+ rc = sbe_get_primary_rc(msg->resp);
+
+free_msg:
+ if (autofree)
+ sbe_freemsg(msg);
+
+ return rc;
+}
+
+int sbe_cancelmsg(u32 chip_id, struct sbe_msg *msg)
+{
+ struct proc_chip *chip;
+ struct sbe *sbe;
+
+ chip = get_chip(chip_id);
+ if (chip == NULL || chip->sbe == NULL)
+ return OPAL_PARAMETER;
+
+ sbe = chip->sbe;
+
+ lock(&sbe->lock);
+ if (msg->state != sbe_msg_queued) {
+ unlock(&sbe->lock);
+ return OPAL_BUSY;
+ }
+
+ list_del_from(&sbe->msg_list, &msg->link);
+ msg->state = sbe_msg_cancelled;
+ unlock(&sbe->lock);
+
+ return OPAL_SUCCESS;
+}
+
+static void sbe_handle_response(struct sbe *sbe)
+{
+ u16 send_seq, resp_seq;
+ int rc;
+ struct sbe_msg *msg;
+
+ if (list_empty(&sbe->msg_list))
+ return;
+
+ /* Clear response registers */
+ memset(sbe->resp, 0, sizeof(struct sbe_msg));
+
+ msg = list_top(&sbe->msg_list, struct sbe_msg, link);
+ rc = sbe_msg_receive(sbe->chip_id, sbe->resp);
+ if (rc != OPAL_SUCCESS) {
+ sbe_set_primary_rc(sbe->resp, SBE_STATUS_PRI_GENERIC_ERR);
+ /* XXX commit error log */
+ prlog(PR_ERR, "Failed to read response message "
+ "[chip id = %x]\n", sbe->chip_id);
+ sbe_msg_complete(sbe, msg);
+ return;
+ }
+
+ /* Update message state */
+ msg->state = sbe_msg_response;
+
+ /* Validate sequence number */
+ send_seq = (msg->reg[0] >> 16) & 0xffff;
+ resp_seq = (sbe->resp->reg[0] >> 16) & 0xffff;
+ if (send_seq != resp_seq) {
+ /* XXX commit error log, reset SBE and repost message.
+ * For now lets send sequence error to caller.
+ */
+ sbe_set_primary_rc(msg, SBE_STATUS_PRI_SEQ_ERR);
+ prlog(PR_ERR, "Invalid sequence id [chip id = %x]\n",
+ sbe->chip_id);
+ }
+
+ sbe_msg_complete(sbe, msg);
+}
+
+void sbe_interrupt(uint32_t chip_id)
+{
+ int rc;
+ u64 data;
+ struct proc_chip *chip;
+ struct sbe_msg *msg;
+ struct sbe *sbe;
+
+ chip = get_chip(chip_id);
+ if (chip == NULL || chip->sbe == NULL)
+ goto clr_interrupt;
+ sbe = chip->sbe;
+
+ /* Read doorbell register */
+ rc = xscom_read(chip_id, PSU_HOST_DOORBELL_REG_RW, &data);
+ if (rc) {
+ prlog(PR_ERR, "Failed to read SBE to Host doorbell register "
+ "[chip id = %x]\n", chip_id);
+ sbe_reg_dump(chip_id);
+ goto clr_interrupt;
+ }
+
+ /* SBE came back from reset */
+ if (data & SBE_HOST_RESET) {
+ prlog(PR_NOTICE, "Came back from reset [chip id = %x]\n",
+ chip_id);
+
+ /* Repost message */
+ lock(&sbe->lock);
+ if (!list_empty(&sbe->msg_list)) {
+ msg = list_top(&sbe->msg_list, struct sbe_msg, link);
+ msg->state = sbe_msg_queued;
+ sbe_poke_queue(sbe);
+ }
+ unlock(&sbe->lock);
+ goto clr_interrupt;
+ }
+
+ /* Handle SBE response */
+ if (data & SBE_HOST_RESPONSE_WAITING) {
+ lock(&sbe->lock);
+ sbe_handle_response(sbe);
+ sbe_poke_queue(sbe);
+ unlock(&sbe->lock);
+ }
+
+clr_interrupt:
+ rc = xscom_write(chip_id, PSU_HOST_DOORBELL_REG_AND,
+ SBE_HOST_RESPONSE_CLEAR);
+ if (rc) {
+ prlog(PR_ERR, "Failed to clear SBE to Host doorbell "
+ "register [chip id = %x]\n", chip_id);
+ }
+}
+
+static void sbe_timeout_poll(void *user_data __unused)
+{
+ u64 now = mftb();
+ struct sbe *sbe;
+ struct sbe_msg *msg;
+ struct proc_chip *chip;
+
+ for_each_chip(chip) {
+ if (chip->sbe == NULL)
+ continue;
+
+ sbe = chip->sbe;
+ lock(&sbe->lock);
+ if (list_empty(&sbe->msg_list)) {
+ unlock(&sbe->lock);
+ continue;
+ }
+
+ msg = list_top(&sbe->msg_list, struct sbe_msg, link);
+ if (!sbe_busy(msg)) { /* Message not yet sent to SBE */
+ unlock(&sbe->lock);
+ continue;
+ }
+
+ if (tb_compare(now, msg->timeout) != TB_AAFTERB) {
+ unlock(&sbe->lock);
+ continue;
+ }
+
+ /* Message timeout */
+ if (msg->resp) {
+ msg->resp->state = sbe_msg_timeout;
+ sbe_set_primary_rc(msg, SBE_STATUS_PRI_GENERIC_ERR);
+
+ prlog(PR_ERR, "Message timeout [chip id = %x], "
+ "Reg0 = %llx\n", sbe->chip_id, msg->reg[0]);
+ sbe_reg_dump(sbe->chip_id);
+ }
+
+ sbe_msg_complete(sbe, msg);
+ sbe_poke_queue(sbe);
+ unlock(&sbe->lock);
+ }
+}
+
+void sbe_init(void)
+{
+ struct dt_node *xn;
+ struct proc_chip *chip;
+ struct sbe *sbe;
+
+ if (proc_gen < proc_gen_p9)
+ return;
+
+ dt_for_each_compatible(dt_root, xn, "ibm,xscom") {
+ sbe = zalloc(sizeof(struct sbe));
+ assert(sbe);
+ sbe->resp = zalloc(sizeof(struct sbe_msg));
+ assert(sbe->resp);
+ sbe->chip_id = dt_get_chip_id(xn);
+ sbe->cur_seq = 1;
+ list_head_init(&sbe->msg_list);
+ init_lock(&sbe->lock);
+
+ chip = get_chip(sbe->chip_id);
+ assert(chip);
+ chip->sbe = sbe;
+
+ if (dt_has_node_property(xn, "primary", NULL))
+ sbe_default_chip_id = sbe->chip_id;
+ }
+
+ if (sbe_default_chip_id == -1) {
+ /* XXX Commit error log */
+ prlog(PR_ERR, "Master chip ID not found.\n");
+ return;
+ }
+
+ /* Initiate SBE timeout poller */
+ opal_add_poller(sbe_timeout_poll, NULL);
+}
diff --git a/include/chip.h b/include/chip.h
index 588db9f..ddd8734 100644
--- a/include/chip.h
+++ b/include/chip.h
@@ -108,6 +108,7 @@ struct centaur_chip;
struct mfsi;
struct xive;
struct lpcm;
+struct sbe;
/* Chip type */
enum proc_chip_type {
@@ -206,6 +207,9 @@ struct proc_chip {
/* Used by hw/xive.c */
struct xive *xive;
+
+ /* Used by hw/sbe.c */
+ struct sbe *sbe;
};
extern uint32_t pir_to_chip_id(uint32_t pir);
diff --git a/include/sbe_p9.h b/include/sbe_p9.h
new file mode 100644
index 0000000..1283498
--- /dev/null
+++ b/include/sbe_p9.h
@@ -0,0 +1,231 @@
+/* Copyright 2017 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
+ * limitations under the License.
+ */
+
+#ifndef __SBE_P9_H
+#define __SBE_P9_H
+
+#include <bitutils.h>
+#include <ccan/list/list.h>
+#include <ccan/short_types/short_types.h>
+
+/*
+ * Timeout value for short running commands is 100 msecs and
+ * the timeout value for long running commands is 30 secs.
+ */
+#define SBE_CMD_TIMEOUT_SHORT_MS 100
+#define SBE_CMD_TIMEOUT_LONG_MS (30 * 1000)
+
+/* Primary response status code */
+#define SBE_STATUS_PRI_SUCCESS 0x00
+#define SBE_STATUS_PRI_INVALID_CMD 0x01
+#define SBE_STATUS_PRI_INVALID_DATA 0x02
+#define SBE_STATUS_PRI_SEQ_ERR 0x03
+#define SBE_STATUS_PRI_INTERNAL_ERR 0x04
+#define SBE_STATUS_PRI_GENERIC_ERR 0xFE
+
+/* Secondary response status code */
+#define SBE_STATUS_SEC_SUCCESS 0x00
+#define SBE_STATUS_SEC_CMD_CLASS_UNSUPPORTED 0x01
+#define SBE_STATUS_SEC_CMD_UNSUPPORTED 0x02
+#define SBE_STATUS_SEC_INV_ADDR 0x03
+#define SBE_STATUS_SEC_INV_TARGET_TYPE 0x04
+#define SBE_STATUS_SEC_INV_TARGET_ID 0x05
+#define SBE_STATUS_SEC_TARGET_NOT_PRESENT 0x06
+#define SBE_STATUS_SEC_TARGET_NOT_FUNC 0x07
+#define SBE_STATUS_SEC_CMD_NOT_ALLOW 0x08
+#define SBE_STATUS_SEC_FUNC_NOT_SUPPORTED 0x09
+#define SBE_STATUS_SEC_GENERIC_ERR 0x0A
+#define SBE_STATUS_SEC_BLACKLIST_REG 0x0B
+#define SBE_STATUS_SEC_OS_FAILURE 0x0C
+#define SBE_STATUS_SEC_MBX_REG_FAILURE 0x0D
+#define SBE_STATUS_SEC_INSUFFICIENT_DATA 0x0E
+#define SBE_STATUS_SEC_EXCESS_DATA 0x0F
+#define SBE_STATUS_SEC_BUSY 0x10
+
+/* Number of MBOX register on each side */
+#define NR_HOST_SBE_MBOX_REG 0x04
+
+/*
+ * SBE MBOX register address
+ * Reg 0 - 3 : Host to send command packets to SBE
+ * Reg 4 - 7 : SBE to send response packets to Host
+ */
+#define PSU_HOST_SBE_MBOX_REG0 0x000D0050
+#define PSU_HOST_SBE_MBOX_REG1 0x000D0051
+#define PSU_HOST_SBE_MBOX_REG2 0x000D0052
+#define PSU_HOST_SBE_MBOX_REG3 0x000D0053
+#define PSU_HOST_SBE_MBOX_REG4 0x000D0054
+#define PSU_HOST_SBE_MBOX_REG5 0x000D0055
+#define PSU_HOST_SBE_MBOX_REG6 0x000D0056
+#define PSU_HOST_SBE_MBOX_REG7 0x000D0057
+#define PSU_SBE_DOORBELL_REG_RW 0x000D0060
+#define PSU_SBE_DOORBELL_REG_AND 0x000D0061
+#define PSU_SBE_DOORBELL_REG_OR 0x000D0062
+#define PSU_HOST_DOORBELL_REG_RW 0x000D0063
+#define PSU_HOST_DOORBELL_REG_AND 0x000D0064
+#define PSU_HOST_DOORBELL_REG_OR 0x000D0065
+
+/*
+ * Doorbell Register to trigger SBE interrupt. Set by OPAL to inform
+ * the SBE about a waiting message in the Host/SBE Mailbox Registers
+ */
+#define HOST_SBE_MSG_WAITING PPC_BIT(0)
+
+/*
+ * Doorbell Register for Host Bridge interrupt. Set by the SBE to inform
+ * host about a response message in the Host/SBE Mailbox Registers
+ */
+#define SBE_HOST_RESPONSE_WAITING PPC_BIT(0)
+#define SBE_HOST_MSG_READ PPC_BIT(1)
+#define SBE_HOST_STOP15_EXIT PPC_BIT(2)
+#define SBE_HOST_RESET PPC_BIT(3)
+#define SBE_HOST_TIMER_EXPIRY PPC_BIT(14)
+#define SBE_HOST_RESPONSE_CLEAR 0x00
+
+/* SBE Target Type */
+#define SBE_TARGET_TYPE_PROC 0x00
+#define SBE_TARGET_TYPE_EX 0x01
+#define SBE_TARGET_TYPE_PERV 0x02
+#define SBE_TARGET_TYPE_MCS 0x03
+#define SBE_TARGET_TYPE_EQ 0x04
+#define SBE_TARGET_TYPE_CORE 0x05
+
+/* SBE MBOX command class */
+#define SBE_MCLASS_FIRST 0xD1
+#define SBE_MCLASS_CORE_STATE 0xD1
+#define SBE_MCLASS_SCOM 0xD2
+#define SBE_MCLASS_RING 0xD3
+#define SBE_MCLASS_TIMER 0xD4
+#define SBE_MCLASS_MPIPL 0xD5
+#define SBE_MCLASS_SECURITY 0xD6
+#define SBE_MCLASS_GENERIC 0xD7
+#define SBE_MCLASS_LAST 0xD7
+
+/*
+ * Commands are provided in xxyy form where:
+ * - xx : command class
+ * - yy : command
+ *
+ * Both request and response message uses same Seq ID,
+ * command class and command.
+ */
+#define SBE_CMD_CTRL_DEADMAN_LOOP 0xD101
+#define SBE_CMD_MULTI_SCOM 0xD201
+#define SBE_CMD_PUT_RING_FORM_IMAGE 0xD301
+#define SBE_CMD_CONTROL_TIMER 0xD401
+#define SBE_CMD_CONTINUE_MPIPL 0xD501
+#define SBE_CMD_GET_ARCHITECTED_REG 0xD502
+#define SBE_CMD_CLR_ARCHITECTED_REG 0xD503
+#define SBE_CMD_SET_UNSEC_MEM_WINDOW 0xD601
+#define SBE_CMD_GET_SBE_FFDC 0xD701
+#define SBE_CMD_GET_CAPABILITY 0xD702
+#define SBE_CMD_GET_FREQUENCIES 0xD703
+#define SBE_CMD_SET_FFDC_ADDR 0xD704
+#define SBE_CMD_QUIESCE_SBE 0xD705
+#define SBE_CMD_SET_FABRIC_ID_MAP 0xD706
+#define SBE_CMD_STASH_MPIPL_CONFIG 0xD707
+
+/* SBE MBOX control flags */
+
+/* Generic flags */
+#define SBE_CMD_CTRL_RESP_REQ 0x0100
+#define SBE_CMD_CTRL_ACK_REQ 0x0200
+
+/* Deadman loop */
+#define CTRL_DEADMAN_LOOP_START 0x0001
+#define CTRL_DEADMAN_LOOP_STOP 0x0002
+
+/* Control timer */
+#define CONTROL_TIMER_START 0x0001
+#define CONTROL_TIMER_STOP 0x0002
+
+/* SBE message state */
+enum sbe_msg_state {
+ sbe_msg_unused = 0, /* Free */
+ sbe_msg_queued, /* Queued to SBE list */
+ sbe_msg_sent, /* Sent to SBE */
+ sbe_msg_wresp, /* Waiting for response */
+ sbe_msg_response, /* Got response */
+ sbe_msg_done, /* Complete */
+ sbe_msg_timeout, /* Message timeout */
+ sbe_msg_cancelled, /* Message removed from queue */
+};
+
+/* SBE message */
+struct sbe_msg {
+ /*
+ * Reg[0] :
+ * word0 :
+ * direct cmd : reserved << 16 | ctrl flag
+ * indrect cmd: mem_addr_size_dword
+ * response : primary status << 16 | secondary satus
+ *
+ * word1 : seq id << 16 | cmd class << 8 | cmd
+ *
+ * WARNING:
+ * - Don't populate reg[0].seq (byte 4,5). This will be populated by
+ * sbe_queue_msg().
+ */
+ u64 reg[4];
+
+ /* cmd timout : mftb() + msecs_to_tb(SBE_CMD_TIMEOUT_[SHORT/LONG]MS) */
+ u64 timeout;
+
+ /* Completion function */
+ void (*complete)(struct sbe_msg *msg);
+ void *user_data;
+
+ /* Current msg state */
+ enum sbe_msg_state state;
+
+ /* Set if the message expects a response */
+ bool response;
+
+ /* Points to sbe->resp, which contains response message */
+ struct sbe_msg *resp;
+
+ /* Internal queuing */
+ struct list_node link;
+} __packed;
+
+
+/* Allocate and populate sbe_msg structure */
+extern struct sbe_msg *sbe_mkmsg(u16 cmd, u16 ctrl_flag, u64 reg1,
+ u64 reg2, u64 reg3) __warn_unused_result;
+
+/*
+ * Free sbe_msg structure.
+ * This will free an attached response if any
+ */
+extern void sbe_freemsg(struct sbe_msg *msg);
+
+/* Add new message to sbe queue */
+extern int sbe_queue_msg(uint32_t chip_id, struct sbe_msg *msg,
+ void (*comp)(struct sbe_msg *msg)) __warn_unused_result;
+
+/* Synchronously send message to SBE */
+extern int sbe_sync_msg(u32 chip_id, struct sbe_msg *msg, bool autofree);
+
+/* Remove message from SBE queue, it will not remove inflight message */
+extern int sbe_cancelmsg(u32 chip_id, struct sbe_msg *msg);
+
+/* Initialize the SBE mailbox driver */
+extern void sbe_init(void);
+
+/* SBE interrupt */
+extern void sbe_interrupt(uint32_t chip_id);
+
+#endif /* __SBE_P9_H */
--
2.5.5
More information about the Skiboot
mailing list