[RFC,V2] sensors: dts: Assert special wakeup on idle cores while reading temperature

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

Commit Message

Shilpasri G Bhat Aug. 14, 2017, 7:28 a.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>
---
Changes from V1:
- Make dts_sensor_read() async call for P9
- Remove reading from Stop-Interface-Status-Register as it can be racy
  to decide if a core is in stop and then read DTS scom.
- Issue unconditional special wakeup while reading temperature.

 core/hostservices.c    | 79 +++++++++++++++++++++++++++++-------------------
 core/sensor.c          |  2 +-
 hw/dts.c               | 81 ++++++++++++++++++++++++++++++++++++++++++--------
 include/dts.h          |  2 +-
 include/hostservices.h |  4 +++
 include/xscom.h        |  6 ++++
 6 files changed, 130 insertions(+), 44 deletions(-)

Patch

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/core/sensor.c b/core/sensor.c
index 57b21bc..0e2a5ca 100644
--- a/core/sensor.c
+++ b/core/sensor.c
@@ -28,7 +28,7 @@  static int64_t opal_sensor_read(uint32_t sensor_hndl, int token,
 {
 	switch (sensor_get_family(sensor_hndl)) {
 	case SENSOR_DTS:
-		return dts_sensor_read(sensor_hndl, sensor_data);
+		return dts_sensor_read(sensor_hndl, token, sensor_data);
 	case SENSOR_OCC:
 		return occ_sensor_read(sensor_hndl, sensor_data);
 	default:
diff --git a/hw/dts.c b/hw/dts.c
index a10df58..3524102 100644
--- a/hw/dts.c
+++ b/hw/dts.c
@@ -20,6 +20,10 @@ 
 #include <dts.h>
 #include <skiboot.h>
 #include <opal-api.h>
+#include <hostservices.h>
+#include <opal-msg.h>
+#include <timer.h>
+#include <timebase.h>
 
 struct dts {
 	uint8_t		valid;
@@ -27,6 +31,14 @@  struct dts {
 	int16_t		temp;
 };
 
+/*
+ * Attributes for the core temperature sensor
+ */
+enum {
+	SENSOR_DTS_ATTR_TEMP_MAX,
+	SENSOR_DTS_ATTR_TEMP_TRIP
+};
+
 /* Different sensor locations */
 #define P7_CT_ZONE_LSU	0
 #define P7_CT_ZONE_ISU	1
@@ -196,7 +208,7 @@  static int dts_read_core_temp_p8(uint32_t pir, struct dts *dts)
  * Returns the temperature as the max of all zones and a global trip
  * attribute.
  */
-static int dts_read_core_temp_p9(uint32_t pir, struct dts *dts)
+static int _dts_read_core_temp_p9(u32 pir, struct dts *dts)
 {
 	int32_t chip_id = pir_to_chip_id(pir);
 	int32_t core = pir_to_core_id(pir);
@@ -222,7 +234,55 @@  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;
+	return rc;
+}
+
+static struct timer dts_timer;
+static struct dts_async_data {
+	u32 *sensor_data;
+	u32 pir;
+	int token;
+	u8 attr;
+} dts_async_data;
+
+static void dts_spl_wkup(struct timer *t __unused, void *data,
+			 u64 now __unused)
+{
+	struct dts_async_data *async_data = data;
+	struct dts dts;
+	u32 pir;
+	int rc;
+
+	pir = async_data->pir;
+	rc = hservice_set_special_wakeup(find_cpu_by_pir(pir));
+	if (rc) {
+		prerror("Failed to set special wakeup on %d (%d)\n",
+			pir_to_core_id(pir), rc);
+		goto out;
+	}
+
+	if (!_dts_read_core_temp_p9(pir, &dts)) {
+		if (async_data->attr == SENSOR_DTS_ATTR_TEMP_MAX)
+			*async_data->sensor_data = dts.temp;
+		else if (async_data->attr == SENSOR_DTS_ATTR_TEMP_TRIP)
+			*async_data->sensor_data = dts.trip;
+	}
+
+	rc = hservice_clr_special_wakeup(find_cpu_by_pir(pir));
+	if (rc)
+		prerror("Failed to clear special wakeup on %d (%d)\n",
+			pir_to_core_id(pir), rc);
+out:
+	rc = opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, async_data->token,
+			    rc);
+	if (rc)
+		prerror("Failed to queue async message\n");
+}
+
+static int dts_read_core_temp_p9(void)
+{
+	schedule_timer(&dts_timer, usecs_to_tb(1));
+	return OPAL_ASYNC_COMPLETION;
 }
 
 static int dts_read_core_temp(uint32_t pir, struct dts *dts)
@@ -237,7 +297,7 @@  static int dts_read_core_temp(uint32_t pir, struct dts *dts)
 		rc = dts_read_core_temp_p8(pir, dts);
 		break;
 	case proc_gen_p9:
-		rc = dts_read_core_temp_p9(pir, dts);
+		rc = dts_read_core_temp_p9();
 		break;
 	default:
 		rc = OPAL_UNSUPPORTED;
@@ -303,20 +363,12 @@  enum sensor_dts_class {
 };
 
 /*
- * Attributes for the core temperature sensor
- */
-enum {
-	SENSOR_DTS_ATTR_TEMP_MAX,
-	SENSOR_DTS_ATTR_TEMP_TRIP
-};
-
-/*
  * Extract the centaur chip id which was truncated to fit in the
  * resource identifier field of the sensor handler
  */
 #define centaur_get_id(rid) (0x80000000 | ((rid) & 0x3ff))
 
-int64_t dts_sensor_read(uint32_t sensor_hndl, uint32_t *sensor_data)
+int64_t dts_sensor_read(u32 sensor_hndl, int token, u32 *sensor_data)
 {
 	uint8_t	attr = sensor_get_attr(sensor_hndl);
 	uint32_t rid = sensor_get_rid(sensor_hndl);
@@ -330,6 +382,10 @@  int64_t dts_sensor_read(uint32_t sensor_hndl, uint32_t *sensor_data)
 
 	switch (sensor_get_frc(sensor_hndl)) {
 	case SENSOR_DTS_CORE_TEMP:
+		dts_async_data.attr = attr;
+		dts_async_data.pir = rid;
+		dts_async_data.sensor_data = sensor_data;
+		dts_async_data.token = token;
 		rc = dts_read_core_temp(rid, &dts);
 		break;
 	case SENSOR_DTS_MEM_TEMP:
@@ -431,5 +487,6 @@  bool dts_sensor_create_nodes(struct dt_node *sensors)
 		dt_add_property_string(node, "label", "Centaur");
 	}
 
+	init_timer(&dts_timer, dts_spl_wkup, &dts_async_data);
 	return true;
 }
diff --git a/include/dts.h b/include/dts.h
index b37919f..17e2e15 100644
--- a/include/dts.h
+++ b/include/dts.h
@@ -19,7 +19,7 @@ 
 
 #include <stdint.h>
 
-extern int64_t dts_sensor_read(uint32_t sensor_hndl, uint32_t *sensor_data);
+extern int64_t dts_sensor_read(u32 sensor_hndl, int token, u32 *sensor_data);
 extern bool dts_sensor_create_nodes(struct dt_node *sensors);
 
 #endif /* __DTS_H */
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/xscom.h b/include/xscom.h
index 5a5d0b9..a3ce99e 100644
--- a/include/xscom.h
+++ b/include/xscom.h
@@ -148,6 +148,12 @@ 
 #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)
 
 /************* XXXX Move these P8 only registers elswhere !!! ****************/