sensors: dts: Assert special wakeup on idle cores while reading temperature

Submitted by Shilpasri G Bhat on Aug. 8, 2017, 6:32 p.m.

Details

Message ID 1502217156-31878-1-git-send-email-shilpa.bhat@linux.vnet.ibm.com
State New
Headers show

Commit Message

Shilpasri G Bhat Aug. 8, 2017, 6:32 p.m.
In P9, when a core enters a stop state, its clocks will be stopped
to save power and hence we will not be able to perform a scom
operation to read the DTS temperature sensor.  Hence, assert
a special wakeup on cores that have entered a stop state in order to
successfully complete the scom operation.

Signed-off-by: Shilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com>
---
TODO:
- Make the dts_read_core_temp_p9() call async

 core/hostservices.c    | 79 +++++++++++++++++++++++++++++++-------------------
 hw/dts.c               | 32 ++++++++++++++++++--
 hw/slw.c               | 20 +++++++++++++
 include/hostservices.h |  4 +++
 include/skiboot.h      |  3 ++
 include/xscom.h        | 12 ++++++++
 6 files changed, 118 insertions(+), 32 deletions(-)

Patch hide | download patch | download mbox

diff --git a/core/hostservices.c b/core/hostservices.c
index d1f6fda..345c8c0 100644
--- a/core/hostservices.c
+++ b/core/hostservices.c
@@ -545,9 +545,10 @@  static void hservice_nanosleep(uint64_t i_seconds, uint64_t i_nano_seconds)
 	nanosleep_nopoll(&ts, NULL);
 }
 
-static int hservice_set_special_wakeup(struct cpu_thread *cpu)
+int hservice_set_special_wakeup(struct cpu_thread *cpu)
 {
 	uint64_t val, core_id, poll_target, stamp;
+	u64 wkup_addr, wkup_mask, pm_reg, idle_hist_reg;
 	int rc;
 
 	/*
@@ -558,20 +559,36 @@  static int hservice_set_special_wakeup(struct cpu_thread *cpu)
 	/* Grab core ID once */
 	core_id = pir_to_core_id(cpu->pir);
 
+	switch (proc_gen) {
+	case proc_gen_p8:
+		wkup_addr = XSCOM_ADDR_P8_EX_SLAVE(core_id,
+						   EX_PM_SPECIAL_WAKEUP_PHYP);
+		wkup_mask = EX_PM_GP0_SPECIAL_WAKEUP_DONE;
+		pm_reg = XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0);
+		idle_hist_reg = XSCOM_ADDR_P8_EX_SLAVE(core_id,
+					EX_PM_IDLE_STATE_HISTORY_PHYP);
+		break;
+	case proc_gen_p9:
+		wkup_addr = XSCOM_ADDR_P9_EC_SLAVE(core_id,
+						   EC_PPM_SPECIAL_WKUP_HYP);
+		wkup_mask = EC_PPM_GPPMR_SPECIAL_WAKEUP_DONE;
+		pm_reg = XSCOM_ADDR_P9_EC_SLAVE(core_id, EC_PPM_GPPMR_SCOM);
+		idle_hist_reg = XSCOM_ADDR_P9_EC_SLAVE(core_id,
+						EC_PM_IDLE_STATE_HISTORY_PHYP);
+		break;
+	default:
+		return OPAL_UNSUPPORTED;
+	}
+
 	/*
 	 * The original HWp reads the XSCOM first but ignores the result
 	 * and error, let's do the same until I know for sure that is
 	 * not necessary
 	 */
-	xscom_read(cpu->chip_id,
-		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
-		   &val);
+	xscom_read(cpu->chip_id, wkup_addr, &val);
 
 	/* Then we write special wakeup */
-	rc = xscom_write(cpu->chip_id,
-			 XSCOM_ADDR_P8_EX_SLAVE(core_id,
-						EX_PM_SPECIAL_WAKEUP_PHYP),
-			 PPC_BIT(0));
+	rc = xscom_write(cpu->chip_id, wkup_addr, PPC_BIT(0));
 	if (rc) {
 		prerror("HBRT: XSCOM error %d asserting special"
 			" wakeup on 0x%x\n", rc, cpu->pir);
@@ -602,14 +619,13 @@  static int hservice_set_special_wakeup(struct cpu_thread *cpu)
 	stamp = mftb();
 	poll_target = stamp + msecs_to_tb(200);
 	val = 0;
-	while (!(val & EX_PM_GP0_SPECIAL_WAKEUP_DONE)) {
+
+	while (!(val & wkup_mask)) {
 		/* Wait 1 us */
 		hservice_nanosleep(0, 1000);
 
 		/* Read PM state */
-		rc = xscom_read(cpu->chip_id,
-				XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0),
-				&val);
+		rc = xscom_read(cpu->chip_id, pm_reg, &val);
 		if (rc) {
 			prerror("HBRT: XSCOM error %d reading PM state on"
 				" 0x%x\n", rc, cpu->pir);
@@ -621,7 +637,7 @@  static int hservice_set_special_wakeup(struct cpu_thread *cpu)
 	}
 
 	/* Success ? */
-	if (val & EX_PM_GP0_SPECIAL_WAKEUP_DONE) {
+	if (val & wkup_mask) {
 		uint64_t now = mftb();
 		prlog(PR_TRACE, "HBRT: Special wakeup complete after %ld us\n",
 		      tb_to_usecs(now - stamp));
@@ -639,23 +655,19 @@  static int hservice_set_special_wakeup(struct cpu_thread *cpu)
 	prerror("HBRT: Timeout on special wakeup of 0x%0x\n", cpu->pir);
 	prerror("HBRT:      PM0 = 0x%016llx\n", val);
 	val = -1;
-	xscom_read(cpu->chip_id,
-		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
-		   &val);
+	xscom_read(cpu->chip_id, wkup_addr, &val);
 	prerror("HBRT: SPC_WKUP = 0x%016llx\n", val);
 	val = -1;
-	xscom_read(cpu->chip_id,
-		   XSCOM_ADDR_P8_EX_SLAVE(core_id,
-					  EX_PM_IDLE_STATE_HISTORY_PHYP),
-		   &val);
+	xscom_read(cpu->chip_id, idle_hist_reg, &val);
 	prerror("HBRT:  HISTORY = 0x%016llx\n", val);
 
 	return OPAL_HARDWARE;
 }
 
-static int hservice_clr_special_wakeup(struct cpu_thread *cpu)
+int hservice_clr_special_wakeup(struct cpu_thread *cpu)
 {
 	uint64_t val, core_id;
+	u64 addr;
 	int rc;
 
 	/*
@@ -671,14 +683,23 @@  static int hservice_clr_special_wakeup(struct cpu_thread *cpu)
 	 * and error, let's do the same until I know for sure that is
 	 * not necessary
 	 */
-	xscom_read(cpu->chip_id,
-		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
-		   &val);
+	switch (proc_gen) {
+	case proc_gen_p8:
+		addr = XSCOM_ADDR_P8_EX_SLAVE(core_id,
+					      EX_PM_SPECIAL_WAKEUP_PHYP);
+		break;
+	case proc_gen_p9:
+		addr = XSCOM_ADDR_P9_EC_SLAVE(core_id,
+					      EC_PPM_SPECIAL_WKUP_HYP);
+		break;
+	default:
+		return OPAL_UNSUPPORTED;
+	}
+
+	xscom_read(cpu->chip_id, addr, &val);
 
 	/* Then we write special wakeup */
-	rc = xscom_write(cpu->chip_id,
-			 XSCOM_ADDR_P8_EX_SLAVE(core_id,
-						EX_PM_SPECIAL_WAKEUP_PHYP), 0);
+	rc = xscom_write(cpu->chip_id, addr, 0);
 	if (rc) {
 		prerror("HBRT: XSCOM error %d deasserting"
 			" special wakeup on 0x%x\n", rc, cpu->pir);
@@ -690,9 +711,7 @@  static int hservice_clr_special_wakeup(struct cpu_thread *cpu)
 	 * "This puts an inherent delay in the propagation of the reset
 	 * transition"
 	 */
-	xscom_read(cpu->chip_id,
-		   XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
-		   &val);
+	xscom_read(cpu->chip_id, addr, &val);
 
 	return 0;
 }
diff --git a/hw/dts.c b/hw/dts.c
index a10df58..a19f7d3 100644
--- a/hw/dts.c
+++ b/hw/dts.c
@@ -20,6 +20,7 @@ 
 #include <dts.h>
 #include <skiboot.h>
 #include <opal-api.h>
+#include <hostservices.h>
 
 struct dts {
 	uint8_t		valid;
@@ -198,16 +199,32 @@  static int dts_read_core_temp_p8(uint32_t pir, struct dts *dts)
  */
 static int dts_read_core_temp_p9(uint32_t pir, struct dts *dts)
 {
+	struct cpu_thread *cpu = find_cpu_by_pir(pir);
 	int32_t chip_id = pir_to_chip_id(pir);
 	int32_t core = pir_to_core_id(pir);
 	uint64_t dts0;
 	struct dts temps[P9_CORE_ZONES];
 	int rc;
+	bool stopped;
+
+	/* Check if CPU is idle */
+	stopped = check_core_stopped(pir);
+
+	/* Assert special wakeup */
+	if (stopped) {
+		rc = hservice_set_special_wakeup(cpu);
+		if (rc) {
+			prerror("Failed to set special wakeup on %d (%d)\n",
+				core, rc);
+			return rc;
+		}
+	}
 
+	/* Read temperature */
 	rc = xscom_read(chip_id, XSCOM_ADDR_P9_EC(core, EC_THERM_P9_DTS_RESULT0),
 			&dts0);
 	if (rc)
-		return rc;
+		goto clr;
 
 	dts_decode_one_dts(dts0 >> 48, &temps[P9_CORE_DTS0]);
 	dts_decode_one_dts(dts0 >> 32, &temps[P9_CORE_DTS1]);
@@ -222,7 +239,18 @@  static int dts_read_core_temp_p9(uint32_t pir, struct dts *dts)
 	 * them for the moment until we understand why.
 	 */
 	dts->trip = 0;
-	return 0;
+clr:
+	/* Release special wakeup */
+	if (stopped) {
+		rc = hservice_clr_special_wakeup(cpu);
+		if (rc) {
+			prerror("Failed to clear special wakeup on %d (%d)\n",
+				core, rc);
+			return rc;
+		}
+	}
+
+	return rc;
 }
 
 static int dts_read_core_temp(uint32_t pir, struct dts *dts)
diff --git a/hw/slw.c b/hw/slw.c
index c0ab9de..32fc6d4 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -1521,3 +1521,23 @@  void slw_init(void)
 			slw_init_chip_p9(chip);
 	}
 }
+
+bool check_core_stopped(u32 pir)
+{
+	u64 val;
+	u32 chip_id = pir_to_chip_id(pir);
+	u32 core = pir_to_core_id(pir);
+	int rc;
+
+	rc = xscom_read(chip_id, XSCOM_ADDR_P9_EX(core, EX_PM_SISR), &val);
+	if (rc) {
+		prerror("Failed to read Stop Interface Status Register %d\n",
+			rc);
+		return true;
+	}
+
+	if (pir & 0x4)
+		return (!(val & EX_PM_SISR_INST_C1));
+	else
+		return (!(val & EX_PM_SISR_INST_C0));
+}
diff --git a/include/hostservices.h b/include/hostservices.h
index d6bb3e3..82303b8 100644
--- a/include/hostservices.h
+++ b/include/hostservices.h
@@ -17,6 +17,8 @@ 
 #ifndef __HOSTSERVICES_H
 #define __HOSTSERVICES_H
 
+#include <cpu.h>
+
 bool hservices_init(void);
 void hservices_lid_preload(void);
 bool hservices_lid_preload_complete(void);
@@ -39,4 +41,6 @@  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_clr_special_wakeup(struct cpu_thread *cpu);
+int hservice_set_special_wakeup(struct cpu_thread *cpu);
 #endif /* __HOSTSERVICES_H */
diff --git a/include/skiboot.h b/include/skiboot.h
index 4b7d519..c1e2aa3 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -317,4 +317,7 @@  extern int occ_sensor_group_clear(u32 group_hndl, int token);
 extern void occ_add_sensor_groups(struct dt_node *sg, u32  *phandles,
 				  int nr_phandles, int chipid);
 
+/* Check if a core has entered a stop state */
+bool check_core_stopped(u32 pir);
+
 #endif /* __SKIBOOT_H */
diff --git a/include/xscom.h b/include/xscom.h
index 5a5d0b9..da72dd5 100644
--- a/include/xscom.h
+++ b/include/xscom.h
@@ -148,6 +148,18 @@ 
 #define EC_PPM_SPECIAL_WKUP_FSP		0x010B
 #define EC_PPM_SPECIAL_WKUP_OCC		0x010C
 #define EC_PPM_SPECIAL_WKUP_HYP		0x010D
+#define EC_PM_IDLE_STATE_HISTORY_PHYP	0x0114
+#define EC_PPM_GPPMR_SCOM		0x0100
+#define EC_PPM_GPPMR_SCOM1		0x0101
+#define EC_PPM_GPPMR_SCOM2		0x0102
+
+#define EC_PPM_GPPMR_SPECIAL_WAKEUP_DONE	PPC_BIT(0)
+
+/* P9 CME Local Stop Interface Status Register */
+#define EX_PM_SISR			0x1204C
+#define EX_PM_SISR_INST_C0		PPC_BIT(46)
+#define EX_PM_SISR_INST_C1		PPC_BIT(47)
+
 
 /************* XXXX Move these P8 only registers elswhere !!! ****************/