[Skiboot] [PATCH V5 3/3] occ: Add support for Version 0x90 OCC_OPAL shared memory region

Shilpasri G Bhat shilpa.bhat at linux.vnet.ibm.com
Wed Nov 23 17:58:36 AEDT 2016


This patch enables pstate table parsing support for P9. It
introduces below device tree changes.
- Add a new node per chip in /ibm,opal/power-mgt called occ.
	occ at 3ffd9f8000 {
		reg = <0x3f 0xfd9f8000>;
		ibm,chip-id = <0x0>;
		ibm,pstate-vdds = <0x4e4e4f4f 0x4f505152 0x53545556
		0x5758595a 0x5b5c5d5e 0x5f606162 0x63646565 0x66676868
		0x696a6a6b 0x6c6d6d6e 0x6f6f7071 0x72727374 0x74757677
		0x7778797a 0x7a7b7c7c>;
		ibm,pstate-vcss = <0x4446484a 0x4c4e4f50 0x50515253
		0x54555556 0x5758595a 0x5a5b5c5d 0x5d5e5e5e 0x5e5f5f5f
		0x5f606060 0x61616161 0x62626263 0x63636364 0x64646465
		0x65656666 0x66666767>;
		ibm,pstate-core-max = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>;
		phandle = <0x10000326>;
	};
- Move VID (ibm,pstate-vdds, ibm,pstate-vcss) and max pstate for
  #n active cores array (ibm,pstate-core-max) in to the new per-chip
  /ibm,opal/power-mgt/occ node as these properties are unique to
  chip.
- WOF is supported from version 0x02. Till now we have been adding
  max ultra-turbo pstate(ibm,pstate-ultra-turbo), max turbo pstate
  (ibm,pstate-turbo) and max pstate-per-n-core (ibm,pstate-core-max)
  only when WOF is enabled. This patch will add these properties
  even when WOF is disabled. When WOF is disabled max ultra turbo pstate
  equals to max turbo pstate and max pstate-per-n-core array has all
  entries equal to max turbo pstate. So the above three properties
  are added whenever WOF is supported to ease the reporting of these
  data in host.

Signed-off-by: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
---
Changes from V4:
- s/prerror/prlog(PR_ERR) which have fwts-annotations
- Removed fwts_annotations in macro definitions
- Added out-of-bound sanity checks for nominal and turbo pstate
- Added size to the "reg" property of /ibm,opal/power-mgt/occ

 hw/occ.c | 588 +++++++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 402 insertions(+), 186 deletions(-)

diff --git a/hw/occ.c b/hw/occ.c
index 54ff954..1a02cf1 100644
--- a/hw/occ.c
+++ b/hw/occ.c
@@ -30,44 +30,137 @@
 
 /* OCC Communication Area for PStates */
 
-#define P8_HOMER_SAPPHIRE_DATA_OFFSET	0x1F8000
+#define P8_HOMER_OPAL_DATA_OFFSET	0x1F8000
+#define P9_HOMER_OPAL_DATA_OFFSET	0x0E2000
+#define MAX_PSTATES			256
+#define MAX_P8_CORES			12
+#define MAX_P9_CORES			24
 
-#define MAX_PSTATES 256
-
-#define chip_occ_data(chip) \
-		((struct occ_pstate_table *)(chip->homer_base + \
-				P8_HOMER_SAPPHIRE_DATA_OFFSET))
-
-static bool occ_reset;
-static struct lock occ_lock = LOCK_UNLOCKED;
-
-struct occ_pstate_entry {
-	s8 id;
-	u8 flags;
-	u8 vdd;
-	u8 vcs;
-	u32 freq_khz;
-} __packed;
-
-/*
- * OCC-OPAL Shared Memory Region Version 2
- * https://github.com/open-power/occ/blob/master/src/occ/proc/proc_pstate.h
- * Interface defined in 'sapphire_table_t'
+/**
+ * OCC-OPAL Shared Memory Region
+ *
+ * Reference document :
+ * https://github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf
+ *
+ * Supported layout versions:
+ * - 0x01 : P8
+ * - 0x02 : P8
+ * - 0x90 : P9
+ *   In 0x90 the data is separated into :-
+ *   -- Static Data (struct occ_pstate_table): Data is written once by OCC
+ *   -- Dynamic Data (struct occ_dynamic_data): Data is updated at runtime
+ *
+ * struct occ_pstate_table -	Pstate table layout
+ * @valid:			Indicates if data is valid
+ * @version:			Layout version
+ * @v2.throttle:		Reason for limiting the max pstate
+ * @v90.occ_role:		OCC role (Master/Slave)
+ * @v#.pstate_min:		Minimum pstate ever allowed
+ * @v#.pstate_nom:		Nominal pstate
+ * @v#.pstate_turbo:		Maximum turbo pstate
+ * @v#.pstate_ultra_turbo:	Maximum ultra turbo pstate and the maximum
+ *				pstate ever allowed
+ * @v#.pstates:			Pstate-id and frequency list from Pmax to Pmin
+ * @v#.pstates.id:		Pstate-id
+ * @v#.pstates.flags:		Pstate-flag(reserved)
+ * @v2.pstates.vdd:		Voltage Identifier
+ * @v2.pstates.vcs:		Voltage Identifier
+ * @v#.pstates.freq_khz:	Frequency in KHz
+ * @v#.core_max[1..N]:		Max pstate with N active cores
+ * @spare/reserved/pad:		Unused data
  */
 struct occ_pstate_table {
 	u8 valid;
 	u8 version;
-	u8 throttle;
-	s8 pstate_min;
-	s8 pstate_nom;
-	s8 pstate_turbo;
-	s8 pstate_ultra_turbo;
-	u8 spare;
+	union __packed {
+		struct __packed { /* Version 0x01 and 0x02 */
+			u8 throttle;
+			s8 pstate_min;
+			s8 pstate_nom;
+			s8 pstate_turbo;
+			s8 pstate_ultra_turbo;
+			u8 spare;
+			u64 reserved;
+			struct __packed {
+				s8 id;
+				u8 flags;
+				u8 vdd;
+				u8 vcs;
+				u32 freq_khz;
+			} pstates[MAX_PSTATES];
+			s8 core_max[MAX_P8_CORES];
+			u8 pad[100];
+		} v2;
+		struct __packed { /* Version 0x90 */
+			u8 occ_role;
+			u8 pstate_min;
+			u8 pstate_nom;
+			u8 pstate_turbo;
+			u8 pstate_ultra_turbo;
+			u8 spare;
+			u64 reserved1;
+			u64 reserved2;
+			struct __packed {
+				u8 id;
+				u8 flags;
+				u16 reserved;
+				u32 freq_khz;
+			} pstates[MAX_PSTATES];
+			u8 core_max[MAX_P9_CORES];
+			u8 pad[1024];
+		} v90;
+	};
+} __packed;
+
+/**
+ * OCC-OPAL Shared Memory Interface Dynamic Data Vx90
+ *
+ * struct occ_dynamic_data -	Contains runtime attributes
+ * @occ_state:			Current state of OCC
+ * @cpu_throttle:		Reason for limiting the max pstate
+ * @mem_throttle:		Reason for throttling memory
+ * @quick_pwr_drop:		Indicates if QPD is asserted
+ * @pwr_shifting_ratio:		Indicates the current percentage of power to
+ *				take away from the CPU vs GPU when shifting
+ *				power to maintain a power cap. Value of 100
+ *				means take all power from CPU.
+ * @pwr_cap_type:		Indicates type of power cap in effect
+ * @min_pwr_cap:		Minimum allowed system power cap in Watts
+ * @max_pwr_cap:		Maximum allowed system power cap in Watts
+ * @cur_pwr_cap:		Current system power cap
+ * @spare/reserved:		Unused data
+ */
+struct occ_dynamic_data {
+	u8 occ_state;
+	u8 spare1;
+	u8 spare2;
+	u8 spare3;
+	u8 spare4;
+	u8 cpu_throttle;
+	u8 mem_throttle;
+	u8 quick_pwr_drop;
+	u8 pwr_shifting_ratio;
+	u8 pwr_cap_type;
+	u16 min_pwr_cap;
+	u16 max_pwr_cap;
+	u16 cur_pwr_cap;
 	u64 reserved;
-	struct occ_pstate_entry pstates[MAX_PSTATES];
-	s8 core_max[16];
 } __packed;
 
+#define get_homer_opal_data(chip)					\
+	(chip->homer_base + homer_opal_data_offset)
+
+#define get_occ_pstate_table(chip)					\
+	((struct occ_pstate_table *)(get_homer_opal_data(chip)))
+
+#define get_occ_dynamic_data(chip)					\
+	((struct occ_dynamic_data *)(get_homer_opal_data(chip) +	\
+				sizeof(struct occ_pstate_table)))
+
+static bool occ_reset;
+static struct lock occ_lock = LOCK_UNLOCKED;
+static unsigned long homer_opal_data_offset;
+
 DEFINE_LOG_ENTRY(OPAL_RC_OCC_LOAD, OPAL_PLATFORM_ERR_EVT, OPAL_OCC,
 		OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL,
 		OPAL_NA);
@@ -122,7 +215,6 @@ static int cmp_negative_pstates(int a, int b)
 static bool wait_for_all_occ_init(void)
 {
 	struct proc_chip *chip;
-	uint64_t occ_data_area;
 	struct occ_pstate_table *occ_data;
 	int tries;
 	uint64_t start_time, end_time;
@@ -156,8 +248,7 @@ static bool wait_for_all_occ_init(void)
 		}
 
 		/* Get PState table address */
-		occ_data_area = chip->homer_base + P8_HOMER_SAPPHIRE_DATA_OFFSET;
-		occ_data = (struct occ_pstate_table *)occ_data_area;
+		occ_data = get_occ_pstate_table(chip);
 
 		/*
 		 * Checking for occ_data->valid == 1 is ok because we clear all
@@ -184,8 +275,7 @@ static bool wait_for_all_occ_init(void)
 			return false;
 		}
 		prlog(PR_DEBUG, "OCC: Chip %02x Data (%016llx) = %016llx\n",
-		      chip->id, occ_data_area,
-		      *(uint64_t *)occ_data_area);
+		      chip->id, (uint64_t)occ_data, *(uint64_t *)occ_data);
 	}
 	end_time = mftb();
 	prlog(PR_NOTICE, "OCC: All Chip Rdy after %lu ms\n",
@@ -193,39 +283,118 @@ static bool wait_for_all_occ_init(void)
 	return true;
 }
 
+#define parse_pstate_limits(pstate_data)				  \
+do {									  \
+	pmin = pstate_data.pstate_min;					  \
+	pnom = pstate_data.pstate_nom;					  \
+	pmax = ultra_turbo_supported ? pstate_data.pstate_ultra_turbo :	  \
+					pstate_data.pstate_turbo;	  \
+} while (0)
+
+/*
+ * OCC provides pstate table entries in continuous descending order.
+ * Parse the pstate table to skip pstate_ids that are greater
+ * than Pmax. If a pstate_id is equal to Pmin then add it to
+ * the list and break from the loop as this is the last valid
+ * element in the pstate table.
+ */
+#define parse_cpu_pstate_properties(pstate_data)			  \
+do {									  \
+	for (i = 0, j = 0; i < MAX_PSTATES && j < nr_pstates; i++) {	  \
+		if (cmp_pstates(pstate_data.pstates[i].id, pmax) > 0)	  \
+			continue;					  \
+									  \
+		dt_id[j] = pstate_data.pstates[i].id;			  \
+		dt_freq[j] = pstate_data.pstates[i].freq_khz / 1000;	  \
+		j++;							  \
+		if (pstate_data.pstates[i].id == pmin)			  \
+			break;						  \
+	}								  \
+									  \
+	if (j != nr_pstates) {						  \
+		prerror("OCC: Expected pstates(%d) is not equal to"	  \
+			"parsed pstates(%d)\n", nr_pstates, j);		  \
+		goto out_free_vcs;					  \
+	}								  \
+									  \
+	if (!ultra_turbo_supported)					  \
+		break;							  \
+									  \
+	if (cmp_pstates(pstate_data.pstate_turbo, pmax) > 0) {		  \
+		prlog(PR_WARNING, "OCC: Invalid turbo pstate. Clipping "  \
+		      "turbo pstate(%d) to Pmax(%d)\n",			  \
+		      pstate_data.pstate_turbo, pmax);			  \
+		dt_add_property_cells(power_mgt, "ibm,pstate-turbo",	  \
+				      pmax);				  \
+	} else {							  \
+		dt_add_property_cells(power_mgt, "ibm,pstate-turbo",	  \
+				      pstate_data.pstate_turbo);	  \
+		dt_add_property_cells(power_mgt, "ibm,pstate-ultra-turbo",\
+				      pstate_data.pstate_ultra_turbo);	  \
+	}								  \
+} while (0)
+
+/* core_max[1..n] array provides the max sustainable pstate that can be
+ * achieved with i active cores in the chip.
+ */
+#define parse_core_max(pstate_data)					  \
+do {									  \
+	for (i = 0; i < nr_cores; i++)					  \
+		dt_core_max[i] = pstate_data.core_max[i];		  \
+} while (0)
+
+#define parse_vid(pstate_data)						  \
+do {									  \
+	for (i = 0, j = 0; i < MAX_PSTATES && j < nr_pstates; i++) {	  \
+		if (cmp_pstates(pstate_data.pstates[i].id, pmax) > 0)	  \
+			continue;					  \
+									  \
+		dt_vdd[j] = pstate_data.pstates[i].vdd;			  \
+		dt_vcs[j] = pstate_data.pstates[i].vcs;			  \
+		j++;							  \
+									  \
+		if (pstate_data.pstates[i].id == pmin)			  \
+			break;						  \
+	}								  \
+} while (0)
+
+#define allocate(ptr, size, fail_label)					  \
+do {									  \
+	ptr = malloc((size));						  \
+	if (!ptr) {							  \
+		prerror("OCC:" #ptr "  array alloc failure\n");		  \
+		goto fail_label;					  \
+	}								  \
+} while (0)
+
 /* Add device tree properties to describe pstates states */
-/* Retrun nominal pstate to set in each core */
-static bool add_cpu_pstate_properties(s8 *pstate_nom)
+/* Return nominal pstate to set in each core */
+static bool add_cpu_pstate_properties(int *pstate_nom)
 {
 	struct proc_chip *chip;
 	uint64_t occ_data_area;
 	struct occ_pstate_table *occ_data;
 	struct dt_node *power_mgt;
-	u8 nr_pstates, nr_cores = 0;
-	s8 pmax;
-	/* Arrays for device tree */
-	u32 *dt_id, *dt_freq;
-	u8 *dt_vdd, *dt_vcs;
-	s8 *dt_core_max = NULL;
-	bool rc, ultra_turbo_en;
+	u32 *dt_id, *dt_freq, *dt_core_max = NULL;
+	int pmax, pmin, pnom;
+	u8 *dt_vdd = NULL, *dt_vcs = NULL, nr_pstates, max_cores;
+	bool ultra_turbo_supported, rc = false;
 	int i, j;
 
 	prlog(PR_DEBUG, "OCC: CPU pstate state device tree init\n");
 
-	/* Find first chip and core */
+	/* Find first chip */
 	chip = next_chip(NULL);
 
 	/* Extract PState information from OCC */
+	occ_data = get_occ_pstate_table(chip);
 
-	/* Dump state table */
-	occ_data_area = chip->homer_base + P8_HOMER_SAPPHIRE_DATA_OFFSET;
-
+	/* Dump first 16 bytes of PState table */
+	occ_data_area = (uint64_t)occ_data;
 	prlog(PR_DEBUG, "OCC: Data (%16llx) = %16llx %16llx\n",
 	      occ_data_area,
 	      *(uint64_t *)occ_data_area,
 	      *(uint64_t *)(occ_data_area+8));
-	
-	occ_data = (struct occ_pstate_table *)occ_data_area;
 
 	if (!occ_data->valid) {
 		/**
@@ -243,21 +412,61 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
 
 	/*
 	 * Workload-Optimized-Frequency(WOF) or Ultra-Turbo is supported
-	 * from version 2 onwards. If WOF is disabled then, the max
+	 * from version 0x02 onwards. If WOF is disabled then, the max
 	 * ultra_turbo pstate will be equal to max turbo pstate.
 	 */
-	if (occ_data->version > 1 &&
-	    cmp_pstates(occ_data->pstate_ultra_turbo,
-			occ_data->pstate_turbo) > 0)
-		ultra_turbo_en = true;
-	else
-		ultra_turbo_en = false;
+	ultra_turbo_supported = true;
 
-	pmax = ultra_turbo_en ? occ_data->pstate_ultra_turbo :
-				occ_data->pstate_turbo;
+	switch (occ_data->version) {
+	case 0x01:
+		ultra_turbo_supported = false;
+	case 0x02:
+		if (proc_gen == proc_gen_p9) {
+			/**
+			 * @fwts-label OCCInvalidVersion02
+			 * @fwts-advice The PState table layout version is not
+			 * supported in P9. So OPAL will not parse the PState
+			 * table. CPU frequency scaling will not be functional
+			 * as frequency and pstate-ids are not added to DT.
+			 */
+			prlog(PR_ERR, "OCC: Version %x is not supported in P9\n",
+			      occ_data->version);
+			return false;
+		}
+		parse_pstate_limits(occ_data->v2);
+		max_cores = MAX_P8_CORES;
+		break;
+	case 0x90:
+		if (proc_gen == proc_gen_p8) {
+			/**
+			 * @fwts-label OCCInvalidVersion90
+			 * @fwts-advice The PState table layout version is not
+			 * supported in P8. So OPAL will not parse the PState
+			 * table. CPU frequency scaling will not be functional
+			 * as frequency and pstate-ids are not added to DT.
+			 */
+			prlog(PR_ERR, "OCC: Version %x is not supported in P8\n",
+			      occ_data->version);
+			return false;
+		}
+		parse_pstate_limits(occ_data->v90);
+		max_cores = MAX_P9_CORES;
+		break;
+	default:
+		/**
+		 * @fwts-label OCCUnsupportedVersion
+		 * @fwts-advice The PState table layout version is not
+		 * supported. So OPAL will not parse the PState table.
+		 * CPU frequency scaling will not be functional as OPAL
+		 * doesn't populate the device tree with pstates.
+		 */
+		prlog(PR_ERR, "OCC: Unsupported pstate table layout version %d\n",
+		      occ_data->version);
+		return false;
+	}
 
 	/* Sanity check for pstate limits */
-	if (cmp_pstates(occ_data->pstate_min, pmax) > 0) {
+	if (cmp_pstates(pmin, pmax) > 0) {
 		/**
 		 * @fwts-label OCCInvalidPStateLimits
 		 * @fwts-advice The min pstate is greater than the
@@ -267,16 +476,27 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
 		 * CPU Frequency management will not be functional in
 		 * the host.
 		 */
-		prlog(PR_ERR, "OCC: Invalid Pstate Limits. Pmin(%d) > Pmax (%d)\n",
-		      occ_data->pstate_min, pmax);
+		prlog(PR_ERR, "OCC: Invalid pstate limits. Pmin(%d) > Pmax (%d)\n",
+		      pmin, pmax);
 		return false;
 	}
 
-	nr_pstates = labs(pmax - occ_data->pstate_min) + 1;
-	prlog(PR_DEBUG, "OCC: Min %d Nom %d Max %d Nr States %d\n", 
-	      occ_data->pstate_min, occ_data->pstate_nom,
-	      pmax, nr_pstates);
+	if (cmp_pstates(pnom, pmax) > 0) {
+		/**
+		 * @fwts-label OCCInvalidNominalPState
+		 * @fwts-advice The nominal pstate is greater than the
+		 * max pstate, this could be due to corrupted/invalid
+		 * data in OCC-OPAL shared memory region. So OPAL has
+		 * limited the nominal pstate to max pstate.
+		 */
+		prlog(PR_WARNING, "OCC: Invalid nominal pstate. Clipping "
+		      "nominal pstate(%d) to Pmax(%d)\n", pnom, pmax);
+		pnom = pmax;
+	}
 
+	nr_pstates = labs(pmax - pmin) + 1;
+	prlog(PR_DEBUG, "OCC: Version %x Min %d Nom %d Max %d Nr States %d\n",
+	      occ_data->version, pmin, pnom, pmax, nr_pstates);
 	if (nr_pstates <= 1 || nr_pstates > 128) {
 		/**
 		 * @fwts-label OCCInvalidPStateRange
@@ -287,7 +507,8 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
 		 * that CPU idle states and CPU frequency scaling
 		 * will not be functional.
 		 */
-		prlog(PR_ERR, "OCC: OCC range is not valid\n");
+		prlog(PR_ERR, "OCC: OCC range is not valid; No of pstates = %d\n",
+			nr_pstates);
 		return false;
 	}
 
@@ -303,97 +524,26 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
 		return false;
 	}
 
-	rc = false;
-
 	/* Setup arrays for device-tree */
 	/* Allocate memory */
-	dt_id = malloc(nr_pstates * sizeof(u32));
-	if (!dt_id) {
-		/**
-		 * @fwts-label OCCdt_idENOMEM
-		 * @fwts-advice Out of memory when allocating pstates array.
-		 * No Pstates added to device tree, pstates not functional.
-		 */
-		prlog(PR_ERR, "OCC: dt_id array alloc failure\n");
-		goto out;
-	}
-
-	dt_freq = malloc(nr_pstates * sizeof(u32));
-	if (!dt_freq) {
-		/**
-		 * @fwts-label OCCdt_freqENOMEM
-		 * @fwts-advice Out of memory when allocating pstates array.
-		 * No Pstates added to device tree, pstates not functional.
-		 */
-		prlog(PR_ERR, "OCC: dt_freq array alloc failure\n");
-		goto out_free_id;
-	}
-
-	dt_vdd = malloc(nr_pstates * sizeof(u8));
-	if (!dt_vdd) {
-		/**
-		 * @fwts-label OCCdt_vddENOMEM
-		 * @fwts-advice Out of memory when allocating pstates array.
-		 * No Pstates added to device tree, pstates not functional.
-		 */
-		prlog(PR_ERR, "OCC: dt_vdd array alloc failure\n");
-		goto out_free_freq;
-	}
-
-	dt_vcs = malloc(nr_pstates * sizeof(u8));
-	if (!dt_vcs) {
-		/**
-		 * @fwts-label OCCdt_vcsENOMEM
-		 * @fwts-advice Out of memory when allocating pstates array.
-		 * No Pstates added to device tree, pstates not functional.
-		 */
-		prlog(PR_ERR, "OCC: dt_vcs array alloc failure\n");
-		goto out_free_vdd;
-	}
-
-	if (ultra_turbo_en) {
-		nr_cores = get_available_nr_cores_in_chip(chip->id);
-		dt_core_max = malloc(nr_cores * sizeof(s8));
-		if (!dt_core_max) {
-			/**
-			 * @fwts-label OCCdt_core_maxENOMEM
-			 * @fwts-advice Out of memory allocating dt_core_max
-			 * array. No PStates in Device Tree: non-functional
-			 * power/frequency management.
-			 */
-			prlog(PR_ERR, "OCC: dt_core_max alloc failure\n");
-			goto out_free_vcs;
-		}
-
-		for (i = 0; i < nr_cores; i++)
-			dt_core_max[i] = occ_data->core_max[i];
-	}
-
-	/*
-	 * OCC provides pstate table entries in continuous descending order.
-	 * Parse the pstate table to skip pstate_ids that are greater
-	 * than Pmax. If a pstate_id is equal to Pmin then add it to
-	 * the list and break from the loop as this is the last valid
-	 * element in the pstate table.
-	 */
-	for (i = 0, j = 0; i < MAX_PSTATES && j < nr_pstates; i++) {
-		if (cmp_pstates(occ_data->pstates[i].id, pmax) > 0)
-			continue;
-
-		dt_id[j] = occ_data->pstates[i].id;
-		dt_freq[j] = occ_data->pstates[i].freq_khz / 1000;
-		dt_vdd[j] = occ_data->pstates[i].vdd;
-		dt_vcs[j] = occ_data->pstates[i].vcs;
-		j++;
-
-		if (occ_data->pstates[i].id == occ_data->pstate_min)
-			break;
-	}
-
-	if (j != nr_pstates) {
-		prerror("OCC: Expected pstates(%d) is not equal to parsed pstates(%d)\n",
-			nr_pstates, j);
-		goto out_free_vcs;
+	allocate(dt_id, nr_pstates * sizeof(u32), out);
+	allocate(dt_freq, nr_pstates * sizeof(u32), out_free_id);
+
+	if (ultra_turbo_supported)
+		allocate(dt_core_max, max_cores * sizeof(u32), out_free_freq);
+
+	switch (occ_data->version) {
+	case 0x01:
+	case 0x02:
+		allocate(dt_vdd, nr_pstates, out_free_core_max);
+		allocate(dt_vcs, nr_pstates, out_free_vdd);
+		parse_cpu_pstate_properties(occ_data->v2);
+		break;
+	case 0x90:
+		parse_cpu_pstate_properties(occ_data->v90);
+		break;
+	default:
+		return false;
 	}
 
 	/* Add the device-tree entries */
@@ -401,34 +551,76 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
 			nr_pstates * sizeof(u32));
 	dt_add_property(power_mgt, "ibm,pstate-frequencies-mhz", dt_freq,
 			nr_pstates * sizeof(u32));
-	dt_add_property(power_mgt, "ibm,pstate-vdds", dt_vdd, nr_pstates);
-	dt_add_property(power_mgt, "ibm,pstate-vcss", dt_vcs, nr_pstates);
-	dt_add_property_cells(power_mgt, "ibm,pstate-min", occ_data->pstate_min);
-	dt_add_property_cells(power_mgt, "ibm,pstate-nominal", occ_data->pstate_nom);
+	dt_add_property_cells(power_mgt, "ibm,pstate-min", pmin);
+	dt_add_property_cells(power_mgt, "ibm,pstate-nominal", pnom);
 	dt_add_property_cells(power_mgt, "ibm,pstate-max", pmax);
 
-	if (ultra_turbo_en) {
-		dt_add_property_cells(power_mgt, "ibm,pstate-turbo",
-				      occ_data->pstate_turbo);
-		dt_add_property_cells(power_mgt, "ibm,pstate-ultra-turbo",
-				      occ_data->pstate_ultra_turbo);
-		dt_add_property(power_mgt, "ibm,pstate-core-max", dt_core_max,
-				nr_cores);
-		free(dt_core_max);
+	dt_add_property_cells(power_mgt, "#address-cells", 2);
+	dt_add_property_cells(power_mgt, "#size-cells", 1);
+	/* Add chip specific pstate properties */
+	for_each_chip(chip) {
+		struct dt_node *occ_node;
+		u8 nr_cores = get_available_nr_cores_in_chip(chip->id);
+
+		occ_data = get_occ_pstate_table(chip);
+		occ_node = dt_new_addr(power_mgt, "occ", (uint64_t)occ_data);
+		if (!occ_node) {
+			/**
+			 * @fwts-label OCCDTFailedNodeCreation
+			 * @fwts-advice Failed to create
+			 * /ibm,opal/power-mgt/occ. Per-chip pstate properties
+			 * are not added to Device Tree.
+			 */
+			prlog(PR_ERR, "OCC: Failed to create /ibm,opal/power-mgt/occ@%llx\n",
+				(uint64_t)occ_data);
+			goto out_free_vcs;
+		}
+		dt_add_property_cells(occ_node, "reg",
+				      hi32((uint64_t)occ_data),
+				      lo32((uint64_t)occ_data),
+				      sizeof(struct occ_pstate_table) +
+				      sizeof(struct occ_dynamic_data));
+		dt_add_property_cells(occ_node, "ibm,chip-id", chip->id);
+		/*
+		 * Parse and add pstate Voltage Identifiers (VID) to DT which
+		 * are provided by OCC in version 0x01 and 0x02
+		 */
+		if (occ_data->version <= 0x02) {
+			parse_vid(occ_data->v2);
+			dt_add_property(occ_node, "ibm,pstate-vdds", dt_vdd,
+					nr_pstates);
+			dt_add_property(occ_node, "ibm,pstate-vcss", dt_vcs,
+					nr_pstates);
+		}
+		/* Parse and add core_max array */
+		if (ultra_turbo_supported) {
+			if (occ_data->version == 0x02)
+				parse_core_max(occ_data->v2);
+			else if (occ_data->version == 0x90)
+				parse_core_max(occ_data->v90);
+
+			dt_add_property(occ_node, "ibm,pstate-core-max",
+					dt_core_max, nr_cores * sizeof(u32));
+		}
 	}
 
 	/* Return pstate to set for each core */
-	*pstate_nom = occ_data->pstate_nom;
+	*pstate_nom = pnom;
 	rc = true;
 
 out_free_vcs:
-	free(dt_vcs);
+	if (occ_data->version <= 0x02)
+		free(dt_vcs);
 out_free_vdd:
-	free(dt_vdd);
-out_free_id:
-	free(dt_id);
+	if (occ_data->version <= 0x02)
+		free(dt_vdd);
+out_free_core_max:
+	if (ultra_turbo_supported)
+		free(dt_core_max);
 out_free_freq:
 	free(dt_freq);
+out_free_id:
+	free(dt_id);
 out:
 	return rc;
 }
@@ -437,7 +629,9 @@ out:
  * Prepare chip for pstate transitions
  */
 
-static bool cpu_pstates_prepare_core(struct proc_chip *chip, struct cpu_thread *c, s8 pstate_nom)
+static bool cpu_pstates_prepare_core(struct proc_chip *chip,
+				     struct cpu_thread *c,
+				     int pstate_nom)
 {
 	uint32_t core = pir_to_core_id(c->pir);
 	uint64_t tmp, pstate;
@@ -525,6 +719,23 @@ static void occ_msg_consumed(void *data __unused)
 	unlock(&occ_lock);
 }
 
+static inline u8 get_cpu_throttle(struct proc_chip *chip)
+{
+	struct occ_pstate_table *pdata = get_occ_pstate_table(chip);
+	struct occ_dynamic_data *data;
+
+	switch (pdata->version) {
+	case 0x01:
+	case 0x02:
+		return pdata->v2.throttle;
+	case 0x90:
+		data = get_occ_dynamic_data(chip);
+		return data->cpu_throttle;
+	default:
+		return 0;
+	};
+}
+
 static void occ_throttle_poll(void *data __unused)
 {
 	struct proc_chip *chip;
@@ -538,7 +749,7 @@ static void occ_throttle_poll(void *data __unused)
 		int inactive = 0;
 
 		for_each_chip(chip) {
-			occ_data = chip_occ_data(chip);
+			occ_data = get_occ_pstate_table(chip);
 			if (occ_data->valid != 1) {
 				inactive = 1;
 				break;
@@ -561,18 +772,21 @@ static void occ_throttle_poll(void *data __unused)
 		if (occ_opal_msg_outstanding)
 			goto done;
 		for_each_chip(chip) {
-			occ_data = chip_occ_data(chip);
+			u8 throttle;
+
+			occ_data = get_occ_pstate_table(chip);
+			throttle = get_cpu_throttle(chip);
 			if ((occ_data->valid == 1) &&
-			    (chip->throttle != occ_data->throttle) &&
-			    (occ_data->throttle <= OCC_MAX_THROTTLE_STATUS)) {
+			    (chip->throttle != throttle) &&
+			    (throttle <= OCC_MAX_THROTTLE_STATUS)) {
 				occ_msg.type = cpu_to_be64(OCC_THROTTLE);
 				occ_msg.chip = cpu_to_be64(chip->id);
-				occ_msg.throttle_status = cpu_to_be64(occ_data->throttle);
+				occ_msg.throttle_status = cpu_to_be64(throttle);
 				rc = _opal_queue_msg(OPAL_MSG_OCC, NULL,
 						     occ_msg_consumed,
 						     3, (uint64_t *)&occ_msg);
 				if (!rc) {
-					chip->throttle = occ_data->throttle;
+					chip->throttle = throttle;
 					occ_opal_msg_outstanding = true;
 					break;
 				}
@@ -584,16 +798,16 @@ done:
 }
 
 /* CPU-OCC PState init */
-/* Called after OCC init on P8 */
+/* Called after OCC init on P8 and P9 */
 void occ_pstates_init(void)
 {
 	struct proc_chip *chip;
 	struct cpu_thread *c;
-	s8 pstate_nom;
+	int pstate_nom;
 	static bool occ_pstates_initialized;
 
-	/* OCC is P8 only */
-	if (proc_gen != proc_gen_p8)
+	/* OCC is supported in P8 and P9 */
+	if (proc_gen < proc_gen_p8)
 		return;
 	/* Handle fast reboots */
 	if (occ_pstates_initialized)
@@ -602,9 +816,11 @@ void occ_pstates_init(void)
 	switch (proc_gen) {
 	case proc_gen_p8:
 		cmp_pstates = cmp_negative_pstates;
+		homer_opal_data_offset = P8_HOMER_OPAL_DATA_OFFSET;
 		break;
 	case proc_gen_p9:
 		cmp_pstates = cmp_positive_pstates;
+		homer_opal_data_offset = P9_HOMER_OPAL_DATA_OFFSET;
 		break;
 	default:
 		return;
@@ -845,7 +1061,7 @@ int occ_msg_queue_occ_reset(void)
 		goto out;
 	}
 	/*
-	 * Set 'valid' byte of chip_occ_data to 0 since OCC
+	 * Set 'valid' byte of occ_pstate_table to 0 since OCC
 	 * may not clear this byte on a reset.
 	 * OCC will set the 'valid' byte to 1 when it becomes
 	 * active again.
@@ -853,7 +1069,7 @@ int occ_msg_queue_occ_reset(void)
 	for_each_chip(chip) {
 		struct occ_pstate_table *occ_data;
 
-		occ_data = chip_occ_data(chip);
+		occ_data = get_occ_pstate_table(chip);
 		occ_data->valid = 0;
 		chip->throttle = 0;
 	}
-- 
1.8.3.1



More information about the Skiboot mailing list