msm: footswitch-8x60: Update clock management code

Some footswitches (such as FS_MDP) require more than the standard
three clocks to be managed as part of the collapse and restore
sequences.  Additionally, the list of required clocks may differ
from SoC to SoC.

Rewrite the clock management to support arbitrarily long lists
of clocks with and without rate requirements. Also, update the
list of MDP-related clocks to include all of those required for
reliable collapses and restores.

CRs-Fixed: 317383
Change-Id: If92d589104095b240e8f95085868e90d3e22f48d
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 101658c..95c93ad 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -5298,13 +5298,16 @@
 	CLK_LOOKUP("mdp_clk",		mdp_clk.c,		NULL),
 	CLK_LOOKUP("core_clk",		mdp_clk.c,	 "footswitch-8x60.4"),
 	CLK_LOOKUP("mdp_vsync_clk",	mdp_vsync_clk.c,	NULL),
+	CLK_LOOKUP("vsync_clk",		mdp_vsync_clk.c, "footswitch-8x60.4"),
 	CLK_LOOKUP("lut_mdp",		lut_mdp_clk.c,		NULL),
+	CLK_LOOKUP("lut_clk",		lut_mdp_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("core_clk",		rot_clk.c,	"msm_rotator.0"),
 	CLK_LOOKUP("core_clk",		rot_clk.c,	"footswitch-8x60.6"),
 	CLK_DUMMY("tv_src_clk",		TV_SRC_CLK,		NULL, OFF),
 	CLK_LOOKUP("core_clk",		vcodec_clk.c,		"msm_vidc.0"),
 	CLK_LOOKUP("core_clk",		vcodec_clk.c,	"footswitch-8x60.7"),
 	CLK_DUMMY("mdp_tv_clk",		MDP_TV_CLK,		NULL, OFF),
+	CLK_DUMMY("tv_clk",		MDP_TV_CLK, "footswitch-8x60.4", OFF),
 	CLK_DUMMY("hdmi_clk",		HDMI_TV_CLK,		NULL, OFF),
 	CLK_LOOKUP("core_clk",		hdmi_app_clk.c,		NULL),
 	CLK_LOOKUP("vpe_clk",		vpe_clk.c,		NULL),
@@ -5531,7 +5534,9 @@
 	CLK_LOOKUP("mdp_clk",		mdp_clk.c,		NULL),
 	CLK_LOOKUP("core_clk",		mdp_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("mdp_vsync_clk",	mdp_vsync_clk.c,	NULL),
+	CLK_LOOKUP("vsync_clk",		mdp_vsync_clk.c, "footswitch-8x60.4"),
 	CLK_LOOKUP("lut_mdp",		lut_mdp_clk.c,		NULL),
+	CLK_LOOKUP("lut_clk",		lut_mdp_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("qdss_pclk",		qdss_p_clk.c,		NULL),
 	CLK_LOOKUP("qdss_at_clk",	qdss_at_clk.c,		NULL),
 	CLK_LOOKUP("qdss_pclkdbg_clk",	qdss_pclkdbg_clk.c,	NULL),
@@ -5541,11 +5546,13 @@
 	CLK_LOOKUP("core_clk",		rot_clk.c,	"msm_rotator.0"),
 	CLK_LOOKUP("core_clk",		rot_clk.c,	"footswitch-8x60.6"),
 	CLK_LOOKUP("tv_src_clk",	tv_src_clk.c,		NULL),
+	CLK_LOOKUP("tv_src_clk",	tv_src_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("tv_enc_clk",	tv_enc_clk.c,		NULL),
 	CLK_LOOKUP("tv_dac_clk",	tv_dac_clk.c,		NULL),
 	CLK_LOOKUP("core_clk",		vcodec_clk.c,	"msm_vidc.0"),
 	CLK_LOOKUP("core_clk",		vcodec_clk.c,	"footswitch-8x60.7"),
 	CLK_LOOKUP("mdp_tv_clk",	mdp_tv_clk.c,		NULL),
+	CLK_LOOKUP("tv_clk",		mdp_tv_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("hdmi_clk",		hdmi_tv_clk.c,		NULL),
 	CLK_LOOKUP("core_clk",		hdmi_app_clk.c,	"hdmi_msm.1"),
 	CLK_LOOKUP("vpe_clk",		vpe_clk.c,		"msm_vpe.0"),
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index 9af21d3..9da8bb4 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -3704,8 +3704,11 @@
 	CLK_LOOKUP("mdp_clk",		mdp_clk.c,		NULL),
 	CLK_LOOKUP("core_clk",		mdp_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("mdp_vsync_clk",	mdp_vsync_clk.c,		NULL),
+	CLK_LOOKUP("vsync_clk",		mdp_vsync_clk.c, "footswitch-8x60.4"),
 	CLK_LOOKUP("pixel_lcdc_clk",	pixel_lcdc_clk.c,		NULL),
+	CLK_LOOKUP("pixel_lcdc_clk",	pixel_lcdc_clk.c, "footswitch-8x60.4"),
 	CLK_LOOKUP("pixel_mdp_clk",	pixel_mdp_clk.c,		NULL),
+	CLK_LOOKUP("pixel_mdp_clk",	pixel_mdp_clk.c, "footswitch-8x60.4"),
 	CLK_LOOKUP("core_clk",		rot_clk.c,	"msm_rotator.0"),
 	CLK_LOOKUP("core_clk",		rot_clk.c,	"footswitch-8x60.6"),
 	CLK_LOOKUP("tv_enc_clk",	tv_enc_clk.c,		NULL),
@@ -3713,8 +3716,10 @@
 	CLK_LOOKUP("core_clk",		vcodec_clk.c,	"msm_vidc.0"),
 	CLK_LOOKUP("core_clk",		vcodec_clk.c,	"footswitch-8x60.7"),
 	CLK_LOOKUP("mdp_tv_clk",	mdp_tv_clk.c,		NULL),
+	CLK_LOOKUP("tv_clk",		mdp_tv_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("hdmi_clk",		hdmi_tv_clk.c,		NULL),
 	CLK_LOOKUP("tv_src_clk",	tv_src_clk.c,		NULL),
+	CLK_LOOKUP("tv_src_clk",	tv_src_clk.c,	"footswitch-8x60.4"),
 	CLK_LOOKUP("core_clk",		hdmi_app_clk.c,	"hdmi_msm.1"),
 	CLK_LOOKUP("vpe_clk",		vpe_clk.c,		NULL),
 	CLK_LOOKUP("core_clk",		vpe_clk.c,	"footswitch-8x60.9"),
diff --git a/arch/arm/mach-msm/footswitch-8x60.c b/arch/arm/mach-msm/footswitch-8x60.c
index 5006419..51f0336 100644
--- a/arch/arm/mach-msm/footswitch-8x60.c
+++ b/arch/arm/mach-msm/footswitch-8x60.c
@@ -10,6 +10,8 @@
  * GNU General Public License for more details.
  */
 
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
 #include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/delay.h>
@@ -22,6 +24,7 @@
 #include <mach/msm_bus_board.h>
 #include <mach/msm_bus.h>
 #include <mach/scm-io.h>
+#include <mach/socinfo.h>
 #include "clock.h"
 #include "footswitch.h"
 
@@ -49,8 +52,9 @@
 #define RETENTION_BIT		BIT(9)
 
 #define RESET_DELAY_US		1
-/* Core clock rate to use if one has not previously been set. */
-#define DEFAULT_CLK_RATE	27000000
+/* Clock rate to use if one has not previously been set. */
+#define DEFAULT_RATE		27000000
+#define MAX_CLKS		10
 
 /*
  * Lock is only needed to protect against the first footswitch_enable()
@@ -58,72 +62,76 @@
  */
 static DEFINE_MUTEX(claim_lock);
 
-struct clock_state {
-	int ahb_clk_en;
-	int axi_clk_en;
-	int core_clk_rate;
+struct clk_data {
+	const char *name;
+	struct clk *clk;
+	unsigned long rate;
+	unsigned long reset_rate;
+	bool enabled;
 };
 
 struct footswitch {
 	struct regulator_dev	*rdev;
 	struct regulator_desc	desc;
 	void			*gfs_ctl_reg;
-	int			bus_port1, bus_port2;
+	int			bus_port0, bus_port1;
 	bool			is_enabled;
 	bool			is_claimed;
-	const bool		has_axi_clk;
+	struct clk_data		*clk_data;
 	struct clk		*core_clk;
-	struct clk		*ahb_clk;
-	struct clk		*axi_clk;
-	unsigned int		reset_rate;
-	struct clock_state	clk_state;
 	unsigned int		gfs_delay_cnt:5;
 };
 
 static int setup_clocks(struct footswitch *fs)
 {
 	int rc = 0;
+	struct clk_data *clock;
+	long rate;
 
 	/*
-	 * Enable all clocks in the power domain. If a core requires a
-	 * specific clock rate when being reset, apply it.
+	 * Enable all clocks in the power domain. If a specific clock rate is
+	 * required for reset timing, set that rate before enabling the clocks.
 	 */
-	fs->clk_state.core_clk_rate = clk_get_rate(fs->core_clk);
-	if (!fs->clk_state.core_clk_rate || fs->reset_rate) {
-		int rate = fs->reset_rate ? fs->reset_rate : DEFAULT_CLK_RATE;
-		rc = clk_set_rate(fs->core_clk, rate);
-		if (rc) {
-			pr_err("%s: Failed to set core_clk rate to %d Hz.\n",
-				__func__, fs->reset_rate);
-			return rc;
+	for (clock = fs->clk_data; clock->clk; clock++) {
+		clock->rate = clk_get_rate(clock->clk);
+		if (!clock->rate || clock->reset_rate) {
+			rate = clock->reset_rate ?
+					clock->reset_rate : DEFAULT_RATE;
+			rc = clk_set_rate(clock->clk, rate);
+			if (rc && rc != -ENOSYS) {
+				pr_err("Failed to set %s rate to %lu Hz.\n",
+					clock->name, clock->rate);
+				for (clock--; clock >= fs->clk_data; clock--) {
+					if (clock->enabled)
+						clk_disable(clock->clk);
+					clk_set_rate(clock->clk, clock->rate);
+				}
+				return rc;
+			}
 		}
+		/*
+		 * Some clocks are for reset purposes only. These clocks will
+		 * fail to enable. Ignore the failures but keep track of them so
+		 * we don't try to disable them later and crash due to
+		 * unbalanced calls.
+		 */
+		clock->enabled = !clk_enable(clock->clk);
 	}
-	clk_enable(fs->core_clk);
 
-	/*
-	 * Some AHB and AXI clocks are for reset purposes only. These clocks
-	 * will fail to enable. Keep track of them so we don't try to disable
-	 * them later and crash.
-	 */
-	fs->clk_state.ahb_clk_en = !clk_enable(fs->ahb_clk);
-	if (fs->axi_clk)
-		fs->clk_state.axi_clk_en = !clk_enable(fs->axi_clk);
-
-	return rc;
+	return 0;
 }
 
 static void restore_clocks(struct footswitch *fs)
 {
+	struct clk_data *clock;
+
 	/* Restore clocks to their orignal states before setup_clocks(). */
-	if (fs->axi_clk && fs->clk_state.axi_clk_en)
-		clk_disable(fs->axi_clk);
-	if (fs->clk_state.ahb_clk_en)
-		clk_disable(fs->ahb_clk);
-	clk_disable(fs->core_clk);
-	if (fs->clk_state.core_clk_rate) {
-		if (clk_set_rate(fs->core_clk, fs->clk_state.core_clk_rate))
-			pr_err("%s: Failed to restore core_clk rate.\n",
-				__func__);
+	for (clock = fs->clk_data; clock->clk; clock++) {
+		if (clock->enabled)
+			clk_disable(clock->clk);
+		if (clock->rate && clk_set_rate(clock->clk, clock->rate))
+			pr_err("Failed to restore %s rate to %lu Hz.\n",
+				clock->name, clock->rate);
 	}
 }
 
@@ -137,6 +145,7 @@
 static int footswitch_enable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
+	struct clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	mutex_lock(&claim_lock);
@@ -154,17 +163,17 @@
 		return rc;
 
 	/* Un-halt all bus ports in the power domain. */
-	if (fs->bus_port1) {
-		rc = msm_bus_axi_portunhalt(fs->bus_port1);
+	if (fs->bus_port0) {
+		rc = msm_bus_axi_portunhalt(fs->bus_port0);
 		if (rc) {
-			pr_err("%s: Port 1 unhalt failed.\n", __func__);
+			pr_err("Port 0 unhalt failed.\n");
 			goto err;
 		}
 	}
-	if (fs->bus_port2) {
-		rc = msm_bus_axi_portunhalt(fs->bus_port2);
+	if (fs->bus_port1) {
+		rc = msm_bus_axi_portunhalt(fs->bus_port1);
 		if (rc) {
-			pr_err("%s: Port 2 unhalt failed.\n", __func__);
+			pr_err("Port 1 unhalt failed.\n");
 			goto err_port2_halt;
 		}
 	}
@@ -174,10 +183,8 @@
 	 * footswitch_enable() is first called before footswitch_disable()
 	 * and resets should be asserted before power is restored.
 	 */
-	if (fs->axi_clk)
-		clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->core_clk, CLK_RESET_ASSERT);
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_reset(clock->clk, CLK_RESET_ASSERT);
 	/* Wait for synchronous resets to propagate. */
 	udelay(RESET_DELAY_US);
 
@@ -193,10 +200,8 @@
 	writel_relaxed(regval, fs->gfs_ctl_reg);
 
 	/* Deassert resets for all clocks in the power domain. */
-	clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
-	clk_reset(fs->ahb_clk, CLK_RESET_DEASSERT);
-	if (fs->axi_clk)
-		clk_reset(fs->axi_clk, CLK_RESET_DEASSERT);
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_reset(clock->clk, CLK_RESET_DEASSERT);
 	/* Toggle core reset again after first power-on (required for GFX3D). */
 	if (fs->desc.id == FS_GFX3D) {
 		clk_reset(fs->core_clk, CLK_RESET_ASSERT);
@@ -212,7 +217,7 @@
 	return 0;
 
 err_port2_halt:
-	msm_bus_axi_porthalt(fs->bus_port1);
+	msm_bus_axi_porthalt(fs->bus_port0);
 err:
 	restore_clocks(fs);
 	return rc;
@@ -221,6 +226,7 @@
 static int footswitch_disable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
+	struct clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	/* Return early if already disabled. */
@@ -234,17 +240,17 @@
 		return rc;
 
 	/* Halt all bus ports in the power domain. */
-	if (fs->bus_port1) {
-		rc = msm_bus_axi_porthalt(fs->bus_port1);
+	if (fs->bus_port0) {
+		rc = msm_bus_axi_porthalt(fs->bus_port0);
 		if (rc) {
-			pr_err("%s: Port 1 halt failed.\n", __func__);
+			pr_err("Port 0 halt failed.\n");
 			goto err;
 		}
 	}
-	if (fs->bus_port2) {
-		rc = msm_bus_axi_porthalt(fs->bus_port2);
+	if (fs->bus_port1) {
+		rc = msm_bus_axi_porthalt(fs->bus_port1);
 		if (rc) {
-			pr_err("%s: Port 1 halt failed.\n", __func__);
+			pr_err("Port 1 halt failed.\n");
 			goto err_port2_halt;
 		}
 	}
@@ -253,10 +259,8 @@
 	 * Assert resets for all clocks in the clock domain so that
 	 * outputs settle prior to clamping.
 	 */
-	if (fs->axi_clk)
-		clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->core_clk, CLK_RESET_ASSERT);
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_reset(clock->clk, CLK_RESET_ASSERT);
 	/* Wait for synchronous resets to propagate. */
 	udelay(RESET_DELAY_US);
 
@@ -283,7 +287,7 @@
 	return 0;
 
 err_port2_halt:
-	msm_bus_axi_portunhalt(fs->bus_port1);
+	msm_bus_axi_portunhalt(fs->bus_port0);
 err:
 	restore_clocks(fs);
 	return rc;
@@ -292,6 +296,7 @@
 static int gfx2d_footswitch_enable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
+	struct clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	mutex_lock(&claim_lock);
@@ -309,10 +314,10 @@
 		return rc;
 
 	/* Un-halt all bus ports in the power domain. */
-	if (fs->bus_port1) {
-		rc = msm_bus_axi_portunhalt(fs->bus_port1);
+	if (fs->bus_port0) {
+		rc = msm_bus_axi_portunhalt(fs->bus_port0);
 		if (rc) {
-			pr_err("%s: Port 1 unhalt failed.\n", __func__);
+			pr_err("Port 0 unhalt failed.\n");
 			goto err;
 		}
 	}
@@ -325,12 +330,10 @@
 	 * footswitch_enable() is first called before footswitch_disable()
 	 * and resets should be asserted before power is restored.
 	 */
-	if (fs->axi_clk)
-		clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->core_clk, CLK_RESET_ASSERT);
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_reset(clock->clk, CLK_RESET_ASSERT);
 	/* Wait for synchronous resets to propagate. */
-	udelay(20);
+	udelay(RESET_DELAY_US);
 
 	/* Enable the power rail at the footswitch. */
 	regval |= ENABLE_BIT;
@@ -343,11 +346,9 @@
 	writel_relaxed(regval, fs->gfs_ctl_reg);
 
 	/* Deassert resets for all clocks in the power domain. */
-	if (fs->axi_clk)
-		clk_reset(fs->axi_clk, CLK_RESET_DEASSERT);
-	clk_reset(fs->ahb_clk, CLK_RESET_DEASSERT);
-	clk_reset(fs->core_clk, CLK_RESET_DEASSERT);
-	udelay(20);
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_reset(clock->clk, CLK_RESET_DEASSERT);
+	udelay(RESET_DELAY_US);
 
 	/* Re-enable core clock. */
 	clk_enable(fs->core_clk);
@@ -366,6 +367,7 @@
 static int gfx2d_footswitch_disable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
+	struct clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	/* Return early if already disabled. */
@@ -379,10 +381,10 @@
 		return rc;
 
 	/* Halt all bus ports in the power domain. */
-	if (fs->bus_port1) {
-		rc = msm_bus_axi_porthalt(fs->bus_port1);
+	if (fs->bus_port0) {
+		rc = msm_bus_axi_porthalt(fs->bus_port0);
 		if (rc) {
-			pr_err("%s: Port 1 halt failed.\n", __func__);
+			pr_err("Port 0 halt failed.\n");
 			goto err;
 		}
 	}
@@ -394,12 +396,10 @@
 	 * Assert resets for all clocks in the clock domain so that
 	 * outputs settle prior to clamping.
 	 */
-	if (fs->axi_clk)
-		clk_reset(fs->axi_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->ahb_clk, CLK_RESET_ASSERT);
-	clk_reset(fs->core_clk, CLK_RESET_ASSERT);
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_reset(clock->clk, CLK_RESET_ASSERT);
 	/* Wait for synchronous resets to propagate. */
-	udelay(20);
+	udelay(5);
 
 	/*
 	 * Clamp the I/O ports of the core to ensure the values
@@ -438,8 +438,102 @@
 	.disable = gfx2d_footswitch_disable,
 };
 
-#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg, _dc, _axi_clk, \
-		   _reset_rate, _bp1, _bp2) \
+/*
+ * Lists of required clocks for the collapse and restore sequences.
+ *
+ * Order matters here. Clocks are listed in the same order as their
+ * resets will be de-asserted when the core is restored. Also, rate-
+ * settable clocks must be listed before any of the branches that
+ * are derived from them. Otherwise, the branches may fail to enable
+ * if their parent's rate is not yet set.
+ */
+
+static struct clk_data gfx2d0_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ 0 }
+};
+
+static struct clk_data gfx2d1_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ 0 }
+};
+
+static struct clk_data gfx3d_clks[] = {
+	{ .name = "core_clk", .reset_rate = 27000000 },
+	{ .name = "iface_clk" },
+	{ 0 }
+};
+
+
+static struct clk_data ijpeg_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ 0 }
+};
+
+static struct clk_data mdp_8960_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ .name = "vsync_clk" },
+	{ .name = "lut_clk" },
+	{ .name = "tv_src_clk" },
+	{ .name = "tv_clk" },
+	{ 0 }
+};
+
+static struct clk_data mdp_8660_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ .name = "vsync_clk" },
+	{ .name = "tv_src_clk" },
+	{ .name = "tv_clk" },
+	{ .name = "pixel_mdp_clk" },
+	{ .name = "pixel_lcdc_clk" },
+	{ 0 }
+};
+
+static struct clk_data rot_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ 0 }
+};
+
+static struct clk_data ved_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ 0 }
+};
+
+static struct clk_data vfe_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ 0 }
+};
+
+static struct clk_data vpe_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ 0 }
+};
+
+static struct clk_data vcap_clks[] = {
+	{ .name = "core_clk" },
+	{ .name = "iface_clk" },
+	{ .name = "bus_clk" },
+	{ 0 }
+};
+
+#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg, _dc, _clk_data, \
+		   _bp1, _bp2) \
 	[(_id)] = { \
 		.desc = { \
 			.id = (_id), \
@@ -450,43 +544,42 @@
 		}, \
 		.gfs_ctl_reg = (_gfs_ctl_reg), \
 		.gfs_delay_cnt = (_dc), \
-		.bus_port1 = (_bp1), \
-		.bus_port2 = (_bp2), \
-		.has_axi_clk = (_axi_clk), \
-		.reset_rate = (_reset_rate), \
+		.clk_data = (_clk_data), \
+		.bus_port0 = (_bp1), \
+		.bus_port1 = (_bp2), \
 	}
 static struct footswitch footswitches[] = {
 	FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops,
-		GFX2D0_GFS_CTL_REG, 31, false, 0,
+		GFX2D0_GFS_CTL_REG, 31, gfx2d0_clks,
 		MSM_BUS_MASTER_GRAPHICS_2D_CORE0, 0),
 	FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops,
-		GFX2D1_GFS_CTL_REG, 31, false, 0,
+		GFX2D1_GFS_CTL_REG, 31, gfx2d1_clks,
 		MSM_BUS_MASTER_GRAPHICS_2D_CORE1, 0),
 	FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops,
-		GFX3D_GFS_CTL_REG, 31, false, 27000000,
+		GFX3D_GFS_CTL_REG, 31, gfx3d_clks,
 		MSM_BUS_MASTER_GRAPHICS_3D, 0),
 	FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops,
-		GEMINI_GFS_CTL_REG, 31, true, 0,
+		GEMINI_GFS_CTL_REG, 31, ijpeg_clks,
 		MSM_BUS_MASTER_JPEG_ENC, 0),
 	FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops,
-		MDP_GFS_CTL_REG, 31, true, 0,
+		MDP_GFS_CTL_REG, 31, NULL,
 		MSM_BUS_MASTER_MDP_PORT0,
 		MSM_BUS_MASTER_MDP_PORT1),
 	FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops,
-		ROT_GFS_CTL_REG, 31, true, 0,
+		ROT_GFS_CTL_REG, 31, rot_clks,
 		MSM_BUS_MASTER_ROTATOR, 0),
 	FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops,
-		VED_GFS_CTL_REG, 31, true, 0,
+		VED_GFS_CTL_REG, 31, ved_clks,
 		MSM_BUS_MASTER_HD_CODEC_PORT0,
 		MSM_BUS_MASTER_HD_CODEC_PORT1),
 	FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops,
-		VFE_GFS_CTL_REG, 31, true, 0,
+		VFE_GFS_CTL_REG, 31, vfe_clks,
 		MSM_BUS_MASTER_VFE, 0),
 	FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops,
-		VPE_GFS_CTL_REG, 31, true, 0,
+		VPE_GFS_CTL_REG, 31, vpe_clks,
 		MSM_BUS_MASTER_VPE, 0),
 	FOOTSWITCH(FS_VCAP, "fs_vcap", &standard_fs_ops,
-		VCAP_GFS_CTL_REG, 31, true, 0,
+		VCAP_GFS_CTL_REG, 31, vcap_clks,
 		MSM_BUS_MASTER_VIDEO_CAP, 0),
 };
 
@@ -494,6 +587,7 @@
 {
 	struct footswitch *fs;
 	struct regulator_init_data *init_data;
+	struct clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	if (pdev == NULL)
@@ -505,30 +599,24 @@
 	fs = &footswitches[pdev->id];
 	init_data = pdev->dev.platform_data;
 
-	/* Setup core clock. */
-	fs->core_clk = clk_get(&pdev->dev, "core_clk");
-	if (IS_ERR(fs->core_clk)) {
-		pr_err("%s: clk_get(core_clk) failed\n", __func__);
-		rc = PTR_ERR(fs->core_clk);
-		goto err_core_clk;
+	if (pdev->id == FS_MDP) {
+		if (cpu_is_msm8960() || cpu_is_msm8930())
+			fs->clk_data = mdp_8960_clks;
+		else if (cpu_is_msm8x60())
+			fs->clk_data = mdp_8660_clks;
+		else
+			BUG();
 	}
 
-	/* Setup AHB clock. */
-	fs->ahb_clk = clk_get(&pdev->dev, "iface_clk");
-	if (IS_ERR(fs->ahb_clk)) {
-		pr_err("%s: clk_get(iface_clk) failed\n", __func__);
-		rc = PTR_ERR(fs->ahb_clk);
-		goto err_ahb_clk;
-	}
-
-	/* Setup AXI clock. */
-	if (fs->has_axi_clk) {
-		fs->axi_clk = clk_get(&pdev->dev, "bus_clk");
-		if (IS_ERR(fs->axi_clk)) {
-			pr_err("%s: clk_get(bus_clk) failed\n", __func__);
-			rc = PTR_ERR(fs->axi_clk);
-			goto err_axi_clk;
+	for (clock = fs->clk_data; clock->name; clock++) {
+		clock->clk = clk_get(&pdev->dev, clock->name);
+		if (IS_ERR(clock->clk)) {
+			rc = PTR_ERR(clock->clk);
+			pr_err("clk_get(%s) failed\n", clock->name);
+			goto err;
 		}
+		if (!strncmp(clock->name, "core_clk", 8))
+			fs->core_clk = clock->clk;
 	}
 
 	/*
@@ -543,34 +631,28 @@
 
 	fs->rdev = regulator_register(&fs->desc, &pdev->dev, init_data, fs);
 	if (IS_ERR(footswitches[pdev->id].rdev)) {
-		pr_err("%s: regulator_register(\"%s\") failed\n",
-			__func__, fs->desc.name);
+		pr_err("regulator_register(\"%s\") failed\n",
+			fs->desc.name);
 		rc = PTR_ERR(footswitches[pdev->id].rdev);
-		goto err_register;
+		goto err;
 	}
 
 	return 0;
 
-err_register:
-	if (fs->has_axi_clk)
-		clk_put(fs->axi_clk);
-err_axi_clk:
-	clk_put(fs->ahb_clk);
-err_ahb_clk:
-	clk_put(fs->core_clk);
-err_core_clk:
+err:
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_put(clock->clk);
+
 	return rc;
 }
 
 static int __devexit footswitch_remove(struct platform_device *pdev)
 {
 	struct footswitch *fs = &footswitches[pdev->id];
+	struct clk_data *clock;
 
-	clk_put(fs->core_clk);
-	clk_put(fs->ahb_clk);
-	if (fs->axi_clk)
-		clk_put(fs->axi_clk);
-
+	for (clock = fs->clk_data; clock->clk; clock++)
+		clk_put(clock->clk);
 	regulator_unregister(fs->rdev);
 
 	return 0;