[V3,1/2] opal-prd: Add support for runtime OCC reset in ZZ

Message ID 1512624149-6383-2-git-send-email-shilpa.bhat@linux.vnet.ibm.com
State New
Headers show
Series
  • Support runtime OCC reset/load in ZZ
Related show

Commit Message

Shilpasri G Bhat Dec. 7, 2017, 5:22 a.m.
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/hostservices.c          |  41 +++++++++++-----
 external/opal-prd/opal-prd.c | 112 +++++++++++++++++++++++++++++++++++++++++++
 external/opal-prd/thunk.S    |   2 +-
 hw/occ.c                     |  51 +++++++++++++++++++-
 hw/prd.c                     |  22 +++++++++
 include/hostservices.h       |   2 +
 include/opal-api.h           |  11 +++++
 include/skiboot.h            |   1 +
 8 files changed, 227 insertions(+), 15 deletions(-)

Comments

Nicholas Piggin Dec. 7, 2017, 12:55 p.m. | #1
On Thu,  7 Dec 2017 10:52:28 +0530
Shilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com> wrote:

> -static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
> +int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
>  {
> +	int (*set_wakeup)(struct cpu_thread *cpu);
> +	int (*clear_wakeup)(struct cpu_thread *cpu);
>  	struct cpu_thread *cpu;
>  	int rc = OPAL_SUCCESS;
>  
> -	/*
> -	 * Mask out the top nibble of i_core since it may contain
> -	 * 0x4 (which we use for XSCOM targeting)
> -	 */
> -	i_core &= 0x0fffffff;
> +	switch (proc_gen) {
> +	case proc_gen_p8:
> +		/*
> +		 * Mask out the top nibble of i_core since it may contain
> +		 * 0x4 (which we use for XSCOM targeting)
> +		 */
> +		i_core &= 0x0fffffff;
> +		i_core <<= 3;
> +		set_wakeup = hservice_set_special_wakeup;
> +		clear_wakeup = hservice_clr_special_wakeup;
> +		break;
> +	case proc_gen_p9:
> +		i_core &= SPR_PIR_P9_MASK;
> +		i_core <<= 2;
> +		set_wakeup = dctl_set_special_wakeup;
> +		clear_wakeup = dctl_clear_special_wakeup;
> +		break;
> +	default:
> +		return OPAL_UNSUPPORTED;
> +	}
>  
>  	/* What do we need to do ? */
>  	switch(i_mode) {
>  	case 0: /* Assert special wakeup */
> -		/* XXX Assume P8 */
> -		cpu = find_cpu_by_pir(i_core << 3);
> +		cpu = find_cpu_by_pir(i_core);
>  		if (!cpu)
>  			return OPAL_PARAMETER;
>  		prlog(PR_DEBUG, "HBRT: Special wakeup assert for core 0x%x,"
>  		      " count=%d\n", i_core, cpu->hbrt_spec_wakeup);
>  		if (cpu->hbrt_spec_wakeup == 0)
> -			rc = hservice_set_special_wakeup(cpu);
> +			rc = set_wakeup(cpu);
>  		if (rc == 0)
>  			cpu->hbrt_spec_wakeup++;
>  		return rc;
>  	case 1: /* Deassert special wakeup */
> -		/* XXX Assume P8 */
> -		cpu = find_cpu_by_pir(i_core << 3);
> +		cpu = find_cpu_by_pir(i_core);
>  		if (!cpu)
>  			return OPAL_PARAMETER;
>  		prlog(PR_DEBUG, "HBRT: Special wakeup release for core"
> @@ -738,7 +753,7 @@ static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
>  		/* What to do with count on errors ? */
>  		cpu->hbrt_spec_wakeup--;
>  		if (cpu->hbrt_spec_wakeup == 0)
> -			rc = hservice_clr_special_wakeup(cpu);
> +			rc = clear_wakeup(cpu);
>  		return rc;
>  	case 2: /* Clear all special wakeups */
>  		prlog(PR_DEBUG, "HBRT: Special wakeup release for all cores\n");
> @@ -746,7 +761,7 @@ static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
>  			if (cpu->hbrt_spec_wakeup) {
>  				cpu->hbrt_spec_wakeup = 0;
>  				/* What to do on errors ? */
> -				hservice_clr_special_wakeup(cpu);
> +				clear_wakeup(cpu);
>  			}
>  		}

This looks good, thanks for changing it. We should merge the power8
hostservice special wakeup functoins into direct-controls.c too. That's
not for this patch series, but it would be a good cleanup to make.

Thanks,
Nick

Patch

diff --git a/core/hostservices.c b/core/hostservices.c
index dd8cae2..20334b2 100644
--- a/core/hostservices.c
+++ b/core/hostservices.c
@@ -697,34 +697,49 @@  static int hservice_clr_special_wakeup(struct cpu_thread *cpu)
 	return 0;
 }
 
-static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
+int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
 {
+	int (*set_wakeup)(struct cpu_thread *cpu);
+	int (*clear_wakeup)(struct cpu_thread *cpu);
 	struct cpu_thread *cpu;
 	int rc = OPAL_SUCCESS;
 
-	/*
-	 * Mask out the top nibble of i_core since it may contain
-	 * 0x4 (which we use for XSCOM targeting)
-	 */
-	i_core &= 0x0fffffff;
+	switch (proc_gen) {
+	case proc_gen_p8:
+		/*
+		 * Mask out the top nibble of i_core since it may contain
+		 * 0x4 (which we use for XSCOM targeting)
+		 */
+		i_core &= 0x0fffffff;
+		i_core <<= 3;
+		set_wakeup = hservice_set_special_wakeup;
+		clear_wakeup = hservice_clr_special_wakeup;
+		break;
+	case proc_gen_p9:
+		i_core &= SPR_PIR_P9_MASK;
+		i_core <<= 2;
+		set_wakeup = dctl_set_special_wakeup;
+		clear_wakeup = dctl_clear_special_wakeup;
+		break;
+	default:
+		return OPAL_UNSUPPORTED;
+	}
 
 	/* What do we need to do ? */
 	switch(i_mode) {
 	case 0: /* Assert special wakeup */
-		/* XXX Assume P8 */
-		cpu = find_cpu_by_pir(i_core << 3);
+		cpu = find_cpu_by_pir(i_core);
 		if (!cpu)
 			return OPAL_PARAMETER;
 		prlog(PR_DEBUG, "HBRT: Special wakeup assert for core 0x%x,"
 		      " count=%d\n", i_core, cpu->hbrt_spec_wakeup);
 		if (cpu->hbrt_spec_wakeup == 0)
-			rc = hservice_set_special_wakeup(cpu);
+			rc = set_wakeup(cpu);
 		if (rc == 0)
 			cpu->hbrt_spec_wakeup++;
 		return rc;
 	case 1: /* Deassert special wakeup */
-		/* XXX Assume P8 */
-		cpu = find_cpu_by_pir(i_core << 3);
+		cpu = find_cpu_by_pir(i_core);
 		if (!cpu)
 			return OPAL_PARAMETER;
 		prlog(PR_DEBUG, "HBRT: Special wakeup release for core"
@@ -738,7 +753,7 @@  static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
 		/* What to do with count on errors ? */
 		cpu->hbrt_spec_wakeup--;
 		if (cpu->hbrt_spec_wakeup == 0)
-			rc = hservice_clr_special_wakeup(cpu);
+			rc = clear_wakeup(cpu);
 		return rc;
 	case 2: /* Clear all special wakeups */
 		prlog(PR_DEBUG, "HBRT: Special wakeup release for all cores\n");
@@ -746,7 +761,7 @@  static int hservice_wakeup(uint32_t i_core, uint32_t i_mode)
 			if (cpu->hbrt_spec_wakeup) {
 				cpu->hbrt_spec_wakeup = 0;
 				/* What to do on errors ? */
-				hservice_clr_special_wakeup(cpu);
+				clear_wakeup(cpu);
 			}
 		}
 		return OPAL_SUCCESS;
diff --git a/external/opal-prd/opal-prd.c b/external/opal-prd/opal-prd.c
index b15063f..fb573e4 100644
--- a/external/opal-prd/opal-prd.c
+++ b/external/opal-prd/opal-prd.c
@@ -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");
diff --git a/external/opal-prd/thunk.S b/external/opal-prd/thunk.S
index cca5890..ee3d7c3 100644
--- a/external/opal-prd/thunk.S
+++ b/external/opal-prd/thunk.S
@@ -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)
diff --git a/hw/occ.c b/hw/occ.c
index 0ffa3e8..3507d5f 100644
--- a/hw/occ.c
+++ b/hw/occ.c
@@ -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) {
diff --git a/hw/prd.c b/hw/prd.c
index c00e10a..5f9758d 100644
--- a/hw/prd.c
+++ b/hw/prd.c
@@ -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;
 	}
diff --git a/include/hostservices.h b/include/hostservices.h
index 62ef04b..2e6cd1c 100644
--- a/include/hostservices.h
+++ b/include/hostservices.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(uint32_t i_core, uint32_t i_mode);
+int fsp_occ_reset_status(u64 chipid, s64 status);
 
 #endif /* __HOSTSERVICES_H */
diff --git a/include/opal-api.h b/include/opal-api.h
index 1c1b9cc..ef32f65 100644
--- a/include/opal-api.h
+++ b/include/opal-api.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;
 	};
 };
 
diff --git a/include/skiboot.h b/include/skiboot.h
index db91325..795ee4f 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -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);