@@ -554,6 +554,33 @@ int dctl_core_is_gated(struct cpu_thread *t)
return !!(val & P9_CORE_GATED);
}
+int dctl_clear_all_special_wakeup(void)
+{
+ struct proc_chip *chip;
+ struct cpu_thread *c;
+ int rc;
+
+ if (proc_gen != proc_gen_p9)
+ return OPAL_UNSUPPORTED;
+
+ for_each_chip(chip)
+ for_each_available_core_in_chip(c, chip->id) {
+ lock(&c->dctl_lock);
+ if (c->special_wakeup_count) {
+ rc = p9_core_clear_special_wakeup(c);
+ if (!rc) {
+ c->special_wakeup_count = 0;
+ } else {
+ unlock(&c->dctl_lock);
+ return rc;
+ }
+ }
+ unlock(&c->dctl_lock);
+ }
+
+ return OPAL_SUCCESS;
+}
+
static int dctl_stop(struct cpu_thread *t)
{
struct cpu_thread *c = t->primary;
@@ -697,7 +697,7 @@ static int hservice_clr_special_wakeup(struct cpu_thread *cpu)
return 0;
}
-static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
+static int hservice_wakeup_p8(uint32_t i_core, uint32_t i_mode)
{
struct cpu_thread *cpu;
int rc = OPAL_SUCCESS;
@@ -755,6 +755,50 @@ static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
}
}
+static int hservice_wakeup_p9(u32 core, u32 mode)
+{
+ struct cpu_thread *cpu;
+
+ core &= SPR_PIR_P9_MASK;
+ core <<= 2;
+
+ switch (mode) {
+ case 0: /* Assert special wakeup */
+ cpu = find_cpu_by_pir(core);
+ if (!cpu)
+ return OPAL_PARAMETER;
+ prlog(PR_DEBUG, "HBRT: Special wakeup assert for core 0x%x,"
+ " count=%d\n", core, cpu->special_wakeup_count);
+ return dctl_set_special_wakeup(cpu);
+ case 1: /* Deassert special wakeup */
+ cpu = find_cpu_by_pir(core);
+ if (!cpu)
+ return OPAL_PARAMETER;
+ prlog(PR_DEBUG, "HBRT: Special wakeup release for core"
+ " 0x%x, count=%d\n", core, cpu->special_wakeup_count);
+ return dctl_clear_special_wakeup(cpu);
+ case 2: /* Clear all special wakeups */
+ prlog(PR_DEBUG, "HBRT: Special wakeup release for all cores\n");
+ return dctl_clear_all_special_wakeup();
+ default:
+ return OPAL_PARAMETER;
+ }
+}
+
+int hservice_wakeup(u32 core, u32 mode)
+{
+ switch (proc_gen) {
+ case proc_gen_p8:
+ return hservice_wakeup_p8(core, mode);
+ case proc_gen_p9:
+ return hservice_wakeup_p9(core, mode);
+ default:
+ break;
+ }
+
+ return OPAL_UNSUPPORTED;
+}
+
static struct host_interfaces hinterface = {
.interface_version = HOSTBOOT_RUNTIME_INTERFACE_VERSION,
.puts = hservice_puts,
@@ -163,6 +163,9 @@ struct func_desc {
void *toc;
} hbrt_entry;
+static int nr_chips;
+static u64 chips[256];
+
static int read_prd_msg(struct opal_prd_ctx *ctx);
static struct prd_range *find_range(const char *name, uint32_t instance)
@@ -524,6 +527,24 @@ int hservice_i2c_write(uint64_t i_master, uint16_t i_devAddr,
i_offset, i_length, i_data);
}
+int hservice_wakeup(u32 core, u32 mode)
+{
+ struct opal_prd_msg msg;
+
+ msg.hdr.type = OPAL_PRD_MSG_TYPE_CORE_SPECIAL_WAKEUP;
+ msg.hdr.size = htobe16(sizeof(msg));
+ msg.spl_wakeup.core = htobe32(core);
+ msg.spl_wakeup.mode = htobe32(mode);
+
+ if (write(ctx->fd, &msg, sizeof(msg)) != sizeof(msg)) {
+ pr_log(LOG_ERR, "FW: Failed to send CORE_SPECIAL_WAKEUP msg %x : %m\n",
+ core);
+ return -1;
+ }
+
+ return 0;
+}
+
static void ipmi_init(struct opal_prd_ctx *ctx)
{
insert_module("ipmi_devintf");
@@ -1173,6 +1194,52 @@ static void print_ranges(struct opal_prd_ctx *ctx)
}
}
+static int chip_init(void)
+{
+ struct dirent *dirent;
+ char *path;
+ DIR *dir;
+ __be32 *chipid;
+ void *buf;
+ int rc, len, i;
+
+ dir = opendir(devicetree_base);
+ if (!dir) {
+ pr_log(LOG_ERR, "FW: Can't open %s", devicetree_base);
+ return -1;
+ }
+
+ for (;;) {
+ dirent = readdir(dir);
+ if (!dirent)
+ break;
+
+ if (strncmp("xscom", dirent->d_name, 5))
+ continue;
+
+ rc = asprintf(&path, "%s/%s/ibm,chip-id", devicetree_base,
+ dirent->d_name);
+ if (rc < 0) {
+ pr_log(LOG_ERR, "FW: Failed to create chip-id path");
+ return -1;
+ }
+
+ rc = open_and_read(path, &buf, &len);
+ if (rc) {
+ pr_log(LOG_ERR, "FW; Failed to read chipid");
+ return -1;
+ }
+ chipid = buf;
+ chips[nr_chips++] = be32toh(*chipid);
+ }
+
+ pr_log(LOG_DEBUG, "FW: Chip init");
+ for (i = 0; i < nr_chips; i++)
+ pr_log(LOG_DEBUG, "FW: Chip 0x%lx", chips[i]);
+
+ return 0;
+}
+
static int prd_init_ranges(struct opal_prd_ctx *ctx)
{
struct dirent *dirent;
@@ -1293,6 +1360,10 @@ static int prd_init(struct opal_prd_ctx *ctx)
return -1;
}
+ rc = chip_init();
+ if (rc)
+ pr_log(LOG_ERR, "FW: Failed to initialize chip IDs");
+
return 0;
}
@@ -1436,6 +1507,41 @@ static int handle_msg_sbe_passthrough(struct opal_prd_ctx *ctx,
return rc;
}
+static int handle_msg_fsp_occ_reset(struct opal_prd_msg *msg)
+{
+ struct opal_prd_msg omsg;
+ int rc = -1, i;
+
+ pr_debug("FW: FSP requested OCC reset");
+
+ if (!hservice_runtime->reset_pm_complex) {
+ pr_log_nocall("reset_pm_complex");
+ return rc;
+ }
+
+ for (i = 0; i < nr_chips; i++) {
+ pr_debug("PM: calling pm_complex_reset(0x%lx)", chips[i]);
+ rc = call_reset_pm_complex(chips[i]);
+ if (rc) {
+ pr_log(LOG_ERR, "PM: Failed pm_complex_reset(0x%lx) %m",
+ chips[i]);
+ break;
+ }
+ }
+
+ omsg.hdr.type = OPAL_PRD_MSG_TYPE_FSP_OCC_RESET_STATUS;
+ omsg.hdr.size = htobe16(sizeof(omsg));
+ omsg.fsp_occ_reset_status.chip = msg->occ_reset.chip;
+ omsg.fsp_occ_reset_status.status = htobe64(rc);
+
+ if (write(ctx->fd, &omsg, sizeof(omsg)) != sizeof(omsg)) {
+ pr_log(LOG_ERR, "FW: Failed to send FSP_OCC_RESET_STATUS msg: %m");
+ return -1;
+ }
+
+ return rc;
+}
+
static int handle_prd_msg(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg)
{
int rc = -1;
@@ -1456,6 +1562,9 @@ static int handle_prd_msg(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg)
case OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH:
rc = handle_msg_sbe_passthrough(ctx, msg);
break;
+ case OPAL_PRD_MSG_TYPE_FSP_OCC_RESET:
+ rc = handle_msg_fsp_occ_reset(msg);
+ break;
default:
pr_log(LOG_WARNING, "Invalid incoming message type 0x%x",
msg->hdr.type);
@@ -1988,6 +2097,9 @@ static int run_prd_daemon(struct opal_prd_ctx *ctx)
hinterface.pnor_write = NULL;
}
+ if (!is_fsp_system())
+ hinterface.wakeup = NULL;
+
ipmi_init(ctx);
pr_debug("HBRT: calling hservices_init");
@@ -183,7 +183,7 @@ hinterface:
DISABLED_THUNK(hservice_lid_load)
DISABLED_THUNK(hservice_lid_unload)
CALLBACK_THUNK(hservice_get_reserved_mem)
- DISABLED_THUNK(hservice_wakeup)
+ CALLBACK_THUNK(hservice_wakeup)
CALLBACK_THUNK(hservice_nanosleep)
DISABLED_THUNK(hservice_report_occ_failure)
CALLBACK_THUNK(hservice_clock_gettime)
@@ -1843,6 +1843,44 @@ out:
return rc;
}
+static u32 last_seq_id;
+
+int fsp_occ_reset_status(u64 chipid, s64 status)
+{
+ struct fsp_msg *stat;
+ int rc = OPAL_NO_MEM;
+ int status_word = 0;
+
+ prlog(PR_INFO, "HBRT: OCC stop() completed with %lld\n", status);
+
+ if (status) {
+ struct proc_chip *chip = get_chip(chipid);
+
+ if (!chip)
+ return OPAL_PARAMETER;
+
+ status_word = 0xfe00 | (chip->pcid & 0xff);
+ log_simple_error(&e_info(OPAL_RC_OCC_RESET),
+ "OCC: Error %lld in OCC reset of chip %lld\n",
+ status, chipid);
+ } else {
+ occ_msg_queue_occ_reset();
+ }
+
+ stat = fsp_mkmsg(FSP_CMD_RESET_OCC_STAT, 2, status_word, last_seq_id);
+ if (!stat)
+ return rc;
+
+ rc = fsp_queue_msg(stat, fsp_freemsg);
+ if (rc) {
+ fsp_freemsg(stat);
+ log_simple_error(&e_info(OPAL_RC_OCC_RESET),
+ "OCC: Error %d queueing FSP OCC RESET STATUS message\n",
+ rc);
+ }
+ return rc;
+}
+
static void occ_do_reset(u8 scope, u32 dbob_id, u32 seq_id)
{
struct fsp_msg *rsp, *stat;
@@ -1883,7 +1921,18 @@ static void occ_do_reset(u8 scope, u32 dbob_id, u32 seq_id)
* FSP will request OCC to left in stopped state.
*/
- rc = host_services_occ_stop();
+ switch (proc_gen) {
+ case proc_gen_p8:
+ rc = host_services_occ_stop();
+ break;
+ case proc_gen_p9:
+ last_seq_id = seq_id;
+ chip = next_chip(NULL);
+ prd_fsp_occ_reset(chip->id);
+ return;
+ default:
+ return;
+ }
/* Handle fallback to preload */
if (rc == -ENOENT && chip->homer_base) {
@@ -23,12 +23,14 @@
#include <fsp.h>
#include <mem_region.h>
#include <prd-fw-msg.h>
+#include <hostservices.h>
enum events {
EVENT_ATTN = 1 << 0,
EVENT_OCC_ERROR = 1 << 1,
EVENT_OCC_RESET = 1 << 2,
EVENT_SBE_PASSTHROUGH = 1 << 3,
+ EVENT_FSP_OCC_RESET = 1 << 4,
};
static uint8_t events[MAX_CHIPS];
@@ -114,6 +116,10 @@ static void prd_msg_consumed(void *data)
proc = msg->sbe_passthrough.chip;
event = EVENT_SBE_PASSTHROUGH;
break;
+ case OPAL_PRD_MSG_TYPE_FSP_OCC_RESET:
+ proc = msg->occ_reset.chip;
+ event = EVENT_FSP_OCC_RESET;
+ break;
default:
prlog(PR_ERR, "PRD: invalid msg consumed, type: 0x%x\n",
msg->hdr.type);
@@ -188,6 +194,9 @@ static void send_next_pending_event(void)
} else if (event & EVENT_SBE_PASSTHROUGH) {
prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH;
prd_msg->sbe_passthrough.chip = proc;
+ } else if (event & EVENT_FSP_OCC_RESET) {
+ prd_msg->hdr.type = OPAL_PRD_MSG_TYPE_FSP_OCC_RESET;
+ prd_msg->occ_reset.chip = proc;
}
/*
@@ -274,6 +283,11 @@ void prd_occ_reset(uint32_t proc)
prd_event(proc, EVENT_OCC_RESET);
}
+void prd_fsp_occ_reset(uint32_t proc)
+{
+ prd_event(proc, EVENT_FSP_OCC_RESET);
+}
+
void prd_sbe_passthrough(uint32_t proc)
{
prd_event(proc, EVENT_SBE_PASSTHROUGH);
@@ -430,6 +444,14 @@ static int64_t opal_prd_msg(struct opal_prd_msg *msg)
case OPAL_PRD_MSG_TYPE_FIRMWARE_REQUEST:
rc = prd_msg_handle_firmware_req(msg);
break;
+ case OPAL_PRD_MSG_TYPE_FSP_OCC_RESET_STATUS:
+ rc = fsp_occ_reset_status(msg->fsp_occ_reset_status.chip,
+ msg->fsp_occ_reset_status.status);
+ break;
+ case OPAL_PRD_MSG_TYPE_CORE_SPECIAL_WAKEUP:
+ rc = hservice_wakeup(msg->spl_wakeup.core,
+ msg->spl_wakeup.mode);
+ break;
default:
rc = OPAL_UNSUPPORTED;
}
@@ -310,5 +310,6 @@ extern void cpu_fast_reboot_complete(void);
int dctl_set_special_wakeup(struct cpu_thread *t);
int dctl_clear_special_wakeup(struct cpu_thread *t);
int dctl_core_is_gated(struct cpu_thread *t);
+int dctl_clear_all_special_wakeup(void);
#endif /* __CPU_H */
@@ -39,5 +39,7 @@ void host_services_occ_base_setup(void);
int find_master_and_slave_occ(uint64_t **master, uint64_t **slave,
int *nr_masters, int *nr_slaves);
int hservice_send_error_log(uint32_t plid, uint32_t dsize, void *data);
+int hservice_wakeup(u32 core, u32 mode);
+int fsp_occ_reset_status(u64 chipid, s64 status);
#endif /* __HOSTSERVICES_H */
@@ -1061,6 +1061,9 @@ enum opal_prd_msg_type {
OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE, /* HBRT <-- OPAL */
OPAL_PRD_MSG_TYPE_FIRMWARE_NOTIFY, /* HBRT <-- OPAL */
OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH, /* HBRT <-- OPAL */
+ OPAL_PRD_MSG_TYPE_FSP_OCC_RESET, /* HBRT <-- OPAL */
+ OPAL_PRD_MSG_TYPE_FSP_OCC_RESET_STATUS, /* HBRT --> OPAL */
+ OPAL_PRD_MSG_TYPE_CORE_SPECIAL_WAKEUP, /* HBRT --> OPAL */
};
struct opal_prd_msg_header {
@@ -1108,6 +1111,14 @@ struct opal_prd_msg {
struct {
__be64 chip;
} sbe_passthrough;
+ struct {
+ __be64 chip;
+ __be64 status; /* 0 SUCCESS */
+ } fsp_occ_reset_status;
+ struct {
+ __be32 core;
+ __be32 mode;
+ } spl_wakeup;
};
};
@@ -293,6 +293,7 @@ extern void prd_occ_reset(uint32_t proc);
extern void prd_sbe_passthrough(uint32_t proc);
extern void prd_init(void);
extern void prd_register_reserved_memory(void);
+extern void prd_fsp_occ_reset(uint32_t proc);
/* Flatten device-tree */
extern void *create_dtb(const struct dt_node *root, bool exclusive);
This patch handles OCC_RESET runtime events in host opal-prd and also provides support for calling 'hostinterface->wakeup()' which is required for doing the reset operation. Signed-off-by: Shilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com> --- core/direct-controls.c | 27 +++++++++++ core/hostservices.c | 46 +++++++++++++++++- external/opal-prd/opal-prd.c | 112 +++++++++++++++++++++++++++++++++++++++++++ external/opal-prd/thunk.S | 2 +- hw/occ.c | 51 +++++++++++++++++++- hw/prd.c | 22 +++++++++ include/cpu.h | 1 + include/hostservices.h | 2 + include/opal-api.h | 11 +++++ include/skiboot.h | 1 + 10 files changed, 272 insertions(+), 3 deletions(-)