diff options
-rw-r--r-- | doc/opal-api/opal-messages.txt | 45 | ||||
-rw-r--r-- | doc/opal-api/opal-prd-msg-113.txt | 11 | ||||
-rw-r--r-- | hw/Makefile.inc | 2 | ||||
-rw-r--r-- | hw/prd.c | 360 | ||||
-rw-r--r-- | hw/psi.c | 2 | ||||
-rw-r--r-- | include/skiboot.h | 6 | ||||
-rw-r--r-- | platforms/astbmc/habanero.c | 2 | ||||
-rw-r--r-- | platforms/astbmc/palmetto.c | 2 |
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); + } +} @@ -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; } |