tools/power turbostat: Skip printing disabled package C-states

Replaced previously open-coded Package C-state Limit decoding
with table-driven decoding.  In doing so, updated to match January 2015
"Intel(R) 64 and IA-23 Architectures Software Developer's Manual".

In the past, turbostat would print package C-state residency columns
for all package states supported by the model's architecture, even though
a particular SKU may not support them, or they may be disabled by the BIOS.
Now turbostat will skip printing colunns if MSRs indicate that they are not enabled.
eg. many SKUs don't support PC7, and so that column will no longer be printed.

Signed-off-by: Len Brown <len.brown@intel.com>
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index a02c02f..ba8846b 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -53,6 +53,10 @@
 unsigned int skip_c1;
 unsigned int do_nhm_cstates;
 unsigned int do_snb_cstates;
+unsigned int do_pc2;
+unsigned int do_pc3;
+unsigned int do_pc6;
+unsigned int do_pc7;
 unsigned int do_c8_c9_c10;
 unsigned int do_slm_cstates;
 unsigned int use_c1_residency_msr;
@@ -313,13 +317,13 @@
 	if (do_ptm)
 		outp += sprintf(outp, "  PkgTmp");
 
-	if (do_snb_cstates)
+	if (do_pc2)
 		outp += sprintf(outp, " Pkg%%pc2");
-	if (do_nhm_cstates && !do_slm_cstates)
+	if (do_pc3)
 		outp += sprintf(outp, " Pkg%%pc3");
-	if (do_nhm_cstates && !do_slm_cstates)
+	if (do_pc6)
 		outp += sprintf(outp, " Pkg%%pc6");
-	if (do_snb_cstates)
+	if (do_pc7)
 		outp += sprintf(outp, " Pkg%%pc7");
 	if (do_c8_c9_c10) {
 		outp += sprintf(outp, " Pkg%%pc8");
@@ -394,9 +398,12 @@
 	if (p) {
 		outp += sprintf(outp, "package: %d\n", p->package_id);
 		outp += sprintf(outp, "pc2: %016llX\n", p->pc2);
-		outp += sprintf(outp, "pc3: %016llX\n", p->pc3);
-		outp += sprintf(outp, "pc6: %016llX\n", p->pc6);
-		outp += sprintf(outp, "pc7: %016llX\n", p->pc7);
+		if (do_pc3)
+			outp += sprintf(outp, "pc3: %016llX\n", p->pc3);
+		if (do_pc6)
+			outp += sprintf(outp, "pc6: %016llX\n", p->pc6);
+		if (do_pc7)
+			outp += sprintf(outp, "pc7: %016llX\n", p->pc7);
 		outp += sprintf(outp, "pc8: %016llX\n", p->pc8);
 		outp += sprintf(outp, "pc9: %016llX\n", p->pc9);
 		outp += sprintf(outp, "pc10: %016llX\n", p->pc10);
@@ -528,13 +535,13 @@
 	if (do_ptm)
 		outp += sprintf(outp, "%8d", p->pkg_temp_c);
 
-	if (do_snb_cstates)
+	if (do_pc2)
 		outp += sprintf(outp, "%8.2f", 100.0 * p->pc2/t->tsc);
-	if (do_nhm_cstates && !do_slm_cstates)
+	if (do_pc3)
 		outp += sprintf(outp, "%8.2f", 100.0 * p->pc3/t->tsc);
-	if (do_nhm_cstates && !do_slm_cstates)
+	if (do_pc6)
 		outp += sprintf(outp, "%8.2f", 100.0 * p->pc6/t->tsc);
-	if (do_snb_cstates)
+	if (do_pc7)
 		outp += sprintf(outp, "%8.2f", 100.0 * p->pc7/t->tsc);
 	if (do_c8_c9_c10) {
 		outp += sprintf(outp, "%8.2f", 100.0 * p->pc8/t->tsc);
@@ -631,9 +638,12 @@
 delta_package(struct pkg_data *new, struct pkg_data *old)
 {
 	old->pc2 = new->pc2 - old->pc2;
-	old->pc3 = new->pc3 - old->pc3;
-	old->pc6 = new->pc6 - old->pc6;
-	old->pc7 = new->pc7 - old->pc7;
+	if (do_pc3)
+		old->pc3 = new->pc3 - old->pc3;
+	if (do_pc6)
+		old->pc6 = new->pc6 - old->pc6;
+	if (do_pc7)
+		old->pc7 = new->pc7 - old->pc7;
 	old->pc8 = new->pc8 - old->pc8;
 	old->pc9 = new->pc9 - old->pc9;
 	old->pc10 = new->pc10 - old->pc10;
@@ -774,9 +784,12 @@
 	c->core_temp_c = 0;
 
 	p->pc2 = 0;
-	p->pc3 = 0;
-	p->pc6 = 0;
-	p->pc7 = 0;
+	if (do_pc3)
+		p->pc3 = 0;
+	if (do_pc6)
+		p->pc6 = 0;
+	if (do_pc7)
+		p->pc7 = 0;
 	p->pc8 = 0;
 	p->pc9 = 0;
 	p->pc10 = 0;
@@ -815,9 +828,12 @@
 		return 0;
 
 	average.packages.pc2 += p->pc2;
-	average.packages.pc3 += p->pc3;
-	average.packages.pc6 += p->pc6;
-	average.packages.pc7 += p->pc7;
+	if (do_pc3)
+		average.packages.pc3 += p->pc3;
+	if (do_pc6)
+		average.packages.pc6 += p->pc6;
+	if (do_pc7)
+		average.packages.pc7 += p->pc7;
 	average.packages.pc8 += p->pc8;
 	average.packages.pc9 += p->pc9;
 	average.packages.pc10 += p->pc10;
@@ -859,9 +875,12 @@
 	average.cores.c7 /= topo.num_cores;
 
 	average.packages.pc2 /= topo.num_packages;
-	average.packages.pc3 /= topo.num_packages;
-	average.packages.pc6 /= topo.num_packages;
-	average.packages.pc7 /= topo.num_packages;
+	if (do_pc3)
+		average.packages.pc3 /= topo.num_packages;
+	if (do_pc6)
+		average.packages.pc6 /= topo.num_packages;
+	if (do_pc7)
+		average.packages.pc7 /= topo.num_packages;
 
 	average.packages.pc8 /= topo.num_packages;
 	average.packages.pc9 /= topo.num_packages;
@@ -961,18 +980,18 @@
 	if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE))
 		return 0;
 
-	if (do_nhm_cstates && !do_slm_cstates) {
+	if (do_pc3)
 		if (get_msr(cpu, MSR_PKG_C3_RESIDENCY, &p->pc3))
 			return -9;
+	if (do_pc6)
 		if (get_msr(cpu, MSR_PKG_C6_RESIDENCY, &p->pc6))
 			return -10;
-	}
-	if (do_snb_cstates) {
+	if (do_pc2)
 		if (get_msr(cpu, MSR_PKG_C2_RESIDENCY, &p->pc2))
 			return -11;
+	if (do_pc7)
 		if (get_msr(cpu, MSR_PKG_C7_RESIDENCY, &p->pc7))
 			return -12;
-	}
 	if (do_c8_c9_c10) {
 		if (get_msr(cpu, MSR_PKG_C8_RESIDENCY, &p->pc8))
 			return -13;
@@ -1019,6 +1038,37 @@
 	return 0;
 }
 
+/*
+ * MSR_PKG_CST_CONFIG_CONTROL decoding for pkg_cstate_limit:
+ * If you change the values, note they are used both in comparisons
+ * (>= PCL__7) and to index pkg_cstate_limit_strings[].
+ */
+
+#define PCLUKN 0 /* Unknown */
+#define PCLRSV 1 /* Reserved */
+#define PCL__0 2 /* PC0 */
+#define PCL__1 3 /* PC1 */
+#define PCL__2 4 /* PC2 */
+#define PCL__3 5 /* PC3 */
+#define PCL__4 6 /* PC4 */
+#define PCL__6 7 /* PC6 */
+#define PCL_6N 8 /* PC6 No Retention */
+#define PCL_6R 9 /* PC6 Retention */
+#define PCL__7 10 /* PC7 */
+#define PCL_7S 11 /* PC7 Shrink */
+#define PCLUNL 12 /* Unlimited */
+
+int pkg_cstate_limit = PCLUKN;
+char *pkg_cstate_limit_strings[] = { "reserved", "unknown", "pc0", "pc1", "pc2",
+	"pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "unlimited"};
+
+int nhm_pkg_cstate_limits[8] = {PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL};
+int snb_pkg_cstate_limits[8] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL};
+int hsw_pkg_cstate_limits[8] = {PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S, PCLRSV, PCLUNL};
+int slv_pkg_cstate_limits[8] = {PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7};
+int amt_pkg_cstate_limits[8] = {PCL__0, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7};
+int phi_pkg_cstate_limits[8] = {PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL};
+
 void print_verbose_header(void)
 {
 	unsigned long long msr;
@@ -1098,44 +1148,14 @@
 
 	fprintf(stderr, "cpu0: MSR_NHM_SNB_PKG_CST_CFG_CTL: 0x%08llx", msr);
 
-	fprintf(stderr, " (%s%s%s%s%slocked: pkg-cstate-limit=%d: ",
+	fprintf(stderr, " (%s%s%s%s%slocked: pkg-cstate-limit=%d: %s)\n",
 		(msr & SNB_C3_AUTO_UNDEMOTE) ? "UNdemote-C3, " : "",
 		(msr & SNB_C1_AUTO_UNDEMOTE) ? "UNdemote-C1, " : "",
 		(msr & NHM_C3_AUTO_DEMOTE) ? "demote-C3, " : "",
 		(msr & NHM_C1_AUTO_DEMOTE) ? "demote-C1, " : "",
 		(msr & (1 << 15)) ? "" : "UN",
-		(unsigned int)msr & 7);
-
-
-	switch(msr & 0x7) {
-	case 0:
-		fprintf(stderr, do_slm_cstates ? "no pkg states" : "pc0");
-		break;
-	case 1:
-		fprintf(stderr, do_slm_cstates ? "no pkg states" : do_snb_cstates ? "pc2" : "pc0");
-		break;
-	case 2:
-		fprintf(stderr, do_slm_cstates ? "invalid" : do_snb_cstates ? "pc6-noret" : "pc3");
-		break;
-	case 3:
-		fprintf(stderr, do_slm_cstates ? "invalid" : "pc6");
-		break;
-	case 4:
-		fprintf(stderr, do_slm_cstates ? "pc4" : "pc7");
-		break;
-	case 5:
-		fprintf(stderr, do_slm_cstates ? "invalid" : do_snb_cstates ? "pc7s" : "invalid");
-		break;
-	case 6:
-		fprintf(stderr, do_slm_cstates ? "pc6" : "invalid");
-		break;
-	case 7:
-		fprintf(stderr, do_slm_cstates ? "pc7" : "unlimited");
-		break;
-	default:
-		fprintf(stderr, "invalid");
-	}
-	fprintf(stderr, ")\n");
+		(unsigned int)msr & 7,
+		pkg_cstate_limit_strings[pkg_cstate_limit]);
 
 	if (!do_nhm_turbo_ratio_limit)
 		return;
@@ -1516,9 +1536,14 @@
  * MSR_CORE_C3_RESIDENCY           0x000003fc
  * MSR_CORE_C6_RESIDENCY           0x000003fd
  *
+ * Side effect:
+ * sets global pkg_cstate_limit to decode MSR_NHM_SNB_PKG_CST_CFG_CTL
  */
-int has_nhm_msrs(unsigned int family, unsigned int model)
+int probe_nhm_msrs(unsigned int family, unsigned int model)
 {
+	unsigned long long msr;
+	int *pkg_cstate_limits;
+
 	if (!genuine_intel)
 		return 0;
 
@@ -1531,31 +1556,46 @@
 	case 0x1F:	/* Core i7 and i5 Processor - Nehalem */
 	case 0x25:	/* Westmere Client - Clarkdale, Arrandale */
 	case 0x2C:	/* Westmere EP - Gulftown */
+	case 0x2E:	/* Nehalem-EX Xeon - Beckton */
+	case 0x2F:	/* Westmere-EX Xeon - Eagleton */
+		pkg_cstate_limits = nhm_pkg_cstate_limits;
+		break;
 	case 0x2A:	/* SNB */
 	case 0x2D:	/* SNB Xeon */
 	case 0x3A:	/* IVB */
 	case 0x3E:	/* IVB Xeon */
+		pkg_cstate_limits = snb_pkg_cstate_limits;
+		break;
 	case 0x3C:	/* HSW */
 	case 0x3F:	/* HSX */
 	case 0x45:	/* HSW */
 	case 0x46:	/* HSW */
-	case 0x37:	/* BYT */
-	case 0x4D:	/* AVN */
 	case 0x3D:	/* BDW */
 	case 0x4F:	/* BDX */
 	case 0x56:	/* BDX-DE */
-	case 0x2E:	/* Nehalem-EX Xeon - Beckton */
-	case 0x2F:	/* Westmere-EX Xeon - Eagleton */
-		return 1;
+		pkg_cstate_limits = hsw_pkg_cstate_limits;
+		break;
+	case 0x37:	/* BYT */
+	case 0x4D:	/* AVN */
+		pkg_cstate_limits = slv_pkg_cstate_limits;
+		break;
+	case 0x4C:	/* AMT */
+		pkg_cstate_limits = amt_pkg_cstate_limits;
+		break;
+	case 0x57:	/* PHI */
+		pkg_cstate_limits = phi_pkg_cstate_limits;
+		break;
 	default:
 		return 0;
 	}
+	get_msr(0, MSR_NHM_SNB_PKG_CST_CFG_CTL, &msr);
+
+	pkg_cstate_limit = pkg_cstate_limits[msr & 0x7];
+
+	return 1;
 }
 int has_nhm_turbo_ratio_limit(unsigned int family, unsigned int model)
 {
-	if (!has_nhm_msrs(family, model))
-		return 0;
-
 	switch (model) {
 	/* Nehalem compatible, but do not include turbo-ratio limit support */
 	case 0x2E:	/* Nehalem-EX Xeon - Beckton */
@@ -2252,13 +2292,17 @@
 			do_ptm ? "" : "No ",
 			has_epb ? "" : "No ");
 
-	do_nhm_platform_info = do_nhm_cstates = do_smi = has_nhm_msrs(family, model);
+	do_nhm_platform_info = do_nhm_cstates = do_smi = probe_nhm_msrs(family, model);
 	do_snb_cstates = has_snb_msrs(family, model);
+	do_pc2 = do_snb_cstates && (pkg_cstate_limit >= PCL__2);
+	do_pc3 = (pkg_cstate_limit >= PCL__3);
+	do_pc6 = (pkg_cstate_limit >= PCL__6);
+	do_pc7 = do_snb_cstates && (pkg_cstate_limit >= PCL__7);
 	do_c8_c9_c10 = has_hsw_msrs(family, model);
 	do_slm_cstates = is_slm(family, model);
 	bclk = discover_bclk(family, model);
 
-	do_nhm_turbo_ratio_limit = has_nhm_turbo_ratio_limit(family, model);
+	do_nhm_turbo_ratio_limit = do_nhm_platform_info && has_nhm_turbo_ratio_limit(family, model);
 	do_ivt_turbo_ratio_limit = has_ivt_turbo_ratio_limit(family, model);
 	rapl_probe(family, model);
 	perf_limit_reasons_probe(family, model);
@@ -2631,7 +2675,7 @@
 	cmdline(argc, argv);
 
 	if (verbose)
-		fprintf(stderr, "turbostat v3.9 23-Jan, 2015"
+		fprintf(stderr, "turbostat v3.10 9-Feb, 2015"
 			" - Len Brown <lenb@kernel.org>\n");
 
 	turbostat_init();