msm: acpuclock-krait: Support alternate PVS and speed bin format

msm8974 and future targets use a different eFuse register layout
than targets from previous SoC generations. Instead of hard-coding
acpuclock-krait to assume the old format, allow the format to
be decided on a per-SoC basis via function pointer assignments.

Two formats are currently supported:
 - "Format A", used for 8974
 - "Format B", used for older Krait-based targets

Change-Id: Id9e8a6c83e3003f2b19ac56bb7aee5dc6b1d00cf
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index 359a156..a0727b7 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -650,6 +650,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0x007000C0,
+	.get_bin_info = get_krait_bin_format_a,
 	.stby_khz = 384000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-8627.c b/arch/arm/mach-msm/acpuclock-8627.c
index ac29cac..405b26b 100644
--- a/arch/arm/mach-msm/acpuclock-8627.c
+++ b/arch/arm/mach-msm/acpuclock-8627.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -142,6 +142,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0x007000C0,
+	.get_bin_info = get_krait_bin_format_a,
 	.stby_khz = 384000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-8930.c b/arch/arm/mach-msm/acpuclock-8930.c
index 40326cb..6915343 100644
--- a/arch/arm/mach-msm/acpuclock-8930.c
+++ b/arch/arm/mach-msm/acpuclock-8930.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -227,6 +227,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0x007000C0,
+	.get_bin_info = get_krait_bin_format_a,
 	.stby_khz = 384000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-8930aa.c b/arch/arm/mach-msm/acpuclock-8930aa.c
index 7d1ce09..9aebac9 100644
--- a/arch/arm/mach-msm/acpuclock-8930aa.c
+++ b/arch/arm/mach-msm/acpuclock-8930aa.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -203,6 +203,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0x007000C0,
+	.get_bin_info = get_krait_bin_format_a,
 	.stby_khz = 384000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-8930ab.c b/arch/arm/mach-msm/acpuclock-8930ab.c
index 5003862..bddac00 100644
--- a/arch/arm/mach-msm/acpuclock-8930ab.c
+++ b/arch/arm/mach-msm/acpuclock-8930ab.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -257,6 +257,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0x007000C0,
+	.get_bin_info = get_krait_bin_format_a,
 	.stby_khz = 384000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index d7d3edd..317729f 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -210,6 +210,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0x007000C0,
+	.get_bin_info = get_krait_bin_format_a,
 	.stby_khz = 384000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-8960ab.c b/arch/arm/mach-msm/acpuclock-8960ab.c
index d2e88fb..38658a2 100644
--- a/arch/arm/mach-msm/acpuclock-8960ab.c
+++ b/arch/arm/mach-msm/acpuclock-8960ab.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -252,6 +252,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0x007000C0,
+	.get_bin_info = get_krait_bin_format_a,
 	.stby_khz = 384000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index 62c7cc7..acb296a 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -169,6 +169,7 @@
 	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
 	.bus_scale = &bus_scale_data,
 	.pte_efuse_phys = 0xFC4B80B0,
+	.get_bin_info = get_krait_bin_format_b,
 	.stby_khz = 300000,
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index 10c4d6c..bc7f20e 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1040,62 +1040,78 @@
 	}
 }
 
-static int __init get_speed_bin(u32 pte_efuse)
+void __init get_krait_bin_format_a(void __iomem *base, struct bin_info *bin)
 {
-	uint32_t speed_bin;
+	u32 pte_efuse = readl_relaxed(base);
 
-	speed_bin = pte_efuse & 0xF;
-	if (speed_bin == 0xF)
-		speed_bin = (pte_efuse >> 4) & 0xF;
+	bin->speed = pte_efuse & 0xF;
+	if (bin->speed == 0xF)
+		bin->speed = (pte_efuse >> 4) & 0xF;
+	bin->speed_valid = bin->speed != 0xF;
 
-	if (speed_bin == 0xF) {
-		speed_bin = 0;
-		dev_warn(drv.dev, "SPEED BIN: Defaulting to %d\n", speed_bin);
-	} else {
-		dev_info(drv.dev, "SPEED BIN: %d\n", speed_bin);
-	}
-
-	return speed_bin;
+	bin->pvs = (pte_efuse >> 10) & 0x7;
+	if (bin->pvs == 0x7)
+		bin->pvs = (pte_efuse >> 13) & 0x7;
+	bin->pvs_valid = bin->pvs != 0x7;
 }
 
-static int __init get_pvs_bin(u32 pte_efuse)
+void __init get_krait_bin_format_b(void __iomem *base, struct bin_info *bin)
 {
-	uint32_t pvs_bin;
+	u32 pte_efuse, redundant_sel;
 
-	pvs_bin = (pte_efuse >> 10) & 0x7;
-	if (pvs_bin == 0x7)
-		pvs_bin = (pte_efuse >> 13) & 0x7;
+	pte_efuse = readl_relaxed(base);
+	redundant_sel = (pte_efuse >> 24) & 0x7;
+	bin->speed = pte_efuse & 0x7;
+	bin->pvs = (pte_efuse >> 6) & 0x7;
 
-	if (pvs_bin == 0x7) {
-		pvs_bin = 0;
-		dev_warn(drv.dev, "ACPU PVS: Defaulting to %d\n", pvs_bin);
-	} else {
-		dev_info(drv.dev, "ACPU PVS: %d\n", pvs_bin);
+	switch (redundant_sel) {
+	case 1:
+		bin->speed = (pte_efuse >> 27) & 0x7;
+		break;
+	case 2:
+		bin->pvs = (pte_efuse >> 27) & 0x7;
+		break;
 	}
+	bin->speed_valid = true;
 
-	return pvs_bin;
+	/* Check PVS_BLOW_STATUS */
+	pte_efuse = readl_relaxed(base + 0x4);
+	bin->pvs_valid = !!(pte_efuse & BIT(21));
 }
 
-static struct pvs_table * __init select_freq_plan(u32 pte_efuse_phys,
-			struct pvs_table (*pvs_tables)[NUM_PVS])
+static struct pvs_table * __init select_freq_plan(
+		const struct acpuclk_krait_params *params)
 {
-	void __iomem *pte_efuse;
-	u32 pte_efuse_val;
+	void __iomem *pte_efuse_base;
+	struct bin_info bin;
 
-	pte_efuse = ioremap(pte_efuse_phys, 4);
-	if (!pte_efuse) {
-		dev_err(drv.dev, "Unable to map QFPROM base\n");
+	pte_efuse_base = ioremap(params->pte_efuse_phys, 8);
+	if (!pte_efuse_base) {
+		dev_err(drv.dev, "Unable to map PTE eFuse base\n");
 		return NULL;
 	}
+	params->get_bin_info(pte_efuse_base, &bin);
+	iounmap(pte_efuse_base);
 
-	pte_efuse_val = readl_relaxed(pte_efuse);
-	iounmap(pte_efuse);
+	if (bin.speed_valid) {
+		drv.speed_bin = bin.speed;
+		dev_info(drv.dev, "SPEED BIN: %d\n", drv.speed_bin);
+	} else {
+		drv.speed_bin = 0;
+		dev_warn(drv.dev, "SPEED BIN: Defaulting to %d\n",
+			 drv.speed_bin);
+	}
 
-	/* Select frequency tables. */
-	drv.speed_bin = get_speed_bin(pte_efuse_val);
-	drv.pvs_bin = get_pvs_bin(pte_efuse_val);
+	if (bin.pvs_valid) {
+		drv.pvs_bin = bin.pvs;
+		dev_info(drv.dev, "ACPU PVS: %d\n", drv.pvs_bin);
+	} else {
+		drv.pvs_bin = 0;
+		dev_warn(drv.dev, "ACPU PVS: Defaulting to %d\n",
+			 drv.pvs_bin);
+	}
 
-	return &pvs_tables[drv.speed_bin][drv.pvs_bin];
+	return &params->pvs_tables[drv.speed_bin][drv.pvs_bin];
 }
 
 static void __init drv_data_init(struct device *dev,
@@ -1124,7 +1140,7 @@
 		GFP_KERNEL);
 	BUG_ON(!drv.bus_scale->usecase);
 
-	pvs = select_freq_plan(params->pte_efuse_phys, params->pvs_tables);
+	pvs = select_freq_plan(params);
 	BUG_ON(!pvs->table);
 
 	drv.acpu_freq_tbl = kmemdup(pvs->table, pvs->size, GFP_KERNEL);
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index 7947ea8..00f64fc 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -227,6 +227,20 @@
 };
 
 /**
+ * struct bin_info - Hardware speed and voltage binning info.
+ * @speed_valid: @speed field is valid
+ * @pvs_valid: @pvs field is valid
+ * @speed: Speed bin ID
+ * @pvs: PVS bin ID
+ */
+struct bin_info {
+	bool speed_valid;
+	bool pvs_valid;
+	int speed;
+	int pvs;
+};
+
+/**
  * struct pvs_table - CPU performance level table and size.
  * @table: CPU performance level table
  * @size: sizeof(@table)
@@ -247,6 +261,7 @@
  * @l2_freq_tbl: L2 frequency table.
  * @l2_freq_tbl_size: Size of @l2_freq_tbl.
  * @pte_efuse_phys: Physical address of PTE EFUSE.
+ * @get_bin_info: Function to populate bin_info from pte_efuse.
  * @bus_scale: MSM bus driver parameters.
  * @stby_khz: KHz value corresponding to an always-on clock source.
  */
@@ -258,6 +273,7 @@
 	struct l2_level *l2_freq_tbl;
 	size_t l2_freq_tbl_size;
 	phys_addr_t pte_efuse_phys;
+	void (*get_bin_info)(void __iomem *base, struct bin_info *bin);
 	struct msm_bus_scale_pdata *bus_scale;
 	unsigned long stby_khz;
 };
@@ -297,6 +313,16 @@
 };
 
 /**
+ * get_krait_bin_format_a - Populate bin_info from a 'Format A' pte_efuse
+ */
+void __init get_krait_bin_format_a(void __iomem *base, struct bin_info *bin);
+
+/**
+ * get_krait_bin_format_b - Populate bin_info from a 'Format B' pte_efuse
+ */
+void __init get_krait_bin_format_b(void __iomem *base, struct bin_info *bin);
+
+/**
  * acpuclk_krait_init - Initialize the Krait CPU clock driver give SoC params.
  */
 extern int acpuclk_krait_init(struct device *dev,