From b1b41c78d5a19260605fcb259a51ca7cd71c097a Mon Sep 17 00:00:00 2001
From: Thara Gopinath <thara@ti.com>
Date: Fri, 2 Jul 2010 13:06:57 +0530
Subject: [PATCH 11/20] OMAP: Introduce dependent voltage domain support.

There could be dependencies between various voltage domains for
maintaining system performance or hardware limitation reasons
like VDD<X> should be at voltage v1 when VDD<Y> is at voltage v2.
This patch introduce dependent vdd information structures in the
voltage layer which can be used to populate these dependencies
for a voltage domain. This patch also adds support to scale
the dependent vdd and the scalable devices belonging to it
during the scaling of a main vdd through omap_voltage_scale.

Signed-off-by: Thara Gopinath <thara@ti.com>
---
 arch/arm/mach-omap2/voltage.c |  122 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 122 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c
index 9adf9d1..c83d968 100644
--- a/arch/arm/mach-omap2/voltage.c
+++ b/arch/arm/mach-omap2/voltage.c
@@ -123,6 +123,36 @@ struct vc_reg_info {
 };
 
 /**
+ * omap_vdd_dep_volt - Table containing the parent vdd voltage and the
+ *			dependent vdd voltage corresponding to it.
+ *
+ * @main_vdd_volt	: The main vdd voltage
+ * @dep_vdd_volt	: The voltage at which the dependent vdd should be
+ *			  when the main vdd is at <main_vdd_volt> voltage
+ */
+struct omap_vdd_dep_volt {
+	u32 main_vdd_volt;
+	u32 dep_vdd_volt;
+};
+
+/**
+ * omap_vdd_dep_info - Dependent vdd info
+ *
+ * @name		: Dependent vdd name
+ * @voltdm		: Dependent vdd pointer
+ * @dep_table		: Table containing the dependent vdd voltage
+ *			  corresponding to every main vdd voltage.
+ * @cur_dep_volt	: The voltage to which dependent vdd should be put
+ *			  to for the current main vdd voltage.
+ */
+struct omap_vdd_dep_info {
+	char *name;
+	struct voltagedomain *voltdm;
+	struct omap_vdd_dep_volt *dep_table;
+	unsigned long cur_dep_volt;
+};
+
+/**
  * struct omap_vdd_user_list - The per vdd user list
  *
  * @dev:	The device asking for the vdd to be set at a particular
@@ -174,11 +204,13 @@ struct omap_vdd_info {
 	struct vp_reg_val vp_reg;
 	struct vc_reg_info vc_reg;
 	struct voltagedomain voltdm;
+	struct omap_vdd_dep_info *dep_vdd_info;
 	struct dentry *debug_dir;
 	spinlock_t user_lock;
 	struct plist_head user_list;
 	struct mutex scaling_mutex;
 	struct list_head dev_list;
+	int nr_dep_vdd;
 	u32 curr_volt;
 	u16 ocp_mod;
 	u8 prm_irqst_reg;
@@ -1160,6 +1192,80 @@ static int __init omap4_vdd_data_configure(struct omap_vdd_info *vdd)
 	return 0;
 }
 
+static int calc_dep_vdd_volt(struct device *dev,
+		struct omap_vdd_info *main_vdd, unsigned long main_volt)
+{
+	struct omap_vdd_dep_info *dep_vdds;
+	int i, ret = 0;
+
+	if (!main_vdd->dep_vdd_info) {
+		pr_debug("%s: No dependent VDD's for vdd_%s\n",
+			__func__, main_vdd->voltdm.name);
+		return 0;
+	}
+
+	dep_vdds = main_vdd->dep_vdd_info;
+
+	for (i = 0; i < main_vdd->nr_dep_vdd; i++) {
+		struct omap_vdd_dep_volt *volt_table = dep_vdds[i].dep_table;
+		int nr_volt = 0;
+		unsigned long dep_volt = 0, act_volt = 0;
+
+		while (volt_table[nr_volt].main_vdd_volt != 0) {
+			if (volt_table[nr_volt].main_vdd_volt == main_volt) {
+				dep_volt = volt_table[nr_volt].dep_vdd_volt;
+				break;
+			}
+			nr_volt++;
+		}
+		if (!dep_volt) {
+			pr_warning("%s: Not able to find a matching volt for"
+				"vdd_%s corresponding to vdd_%s %ld volt\n",
+				__func__, dep_vdds[i].name,
+				main_vdd->voltdm.name, main_volt);
+			ret = -EINVAL;
+			continue;
+		}
+
+		if (!dep_vdds[i].voltdm)
+			dep_vdds[i].voltdm =
+				omap_voltage_domain_lookup(dep_vdds[i].name);
+
+		act_volt = dep_volt;
+
+		/* See if dep_volt is possible for the vdd*/
+		ret = omap_voltage_add_request(dep_vdds[i].voltdm, dev,
+				&act_volt);
+
+		/*
+		 * Currently we do not bother if the dep volt and act volt are
+		 * different. We could add a check if needed.
+		 */
+		dep_vdds[i].cur_dep_volt = act_volt;
+	}
+
+	return ret;
+}
+
+static int scale_dep_vdd(struct omap_vdd_info *main_vdd)
+{
+	struct omap_vdd_dep_info *dep_vdds;
+	int i;
+
+	if (!main_vdd->dep_vdd_info) {
+		pr_debug("%s: No dependent VDD's for vdd_%s\n",
+			__func__, main_vdd->voltdm.name);
+		return 0;
+	}
+
+	dep_vdds = main_vdd->dep_vdd_info;
+
+	for (i = 0; i < main_vdd->nr_dep_vdd; i++)
+		omap_voltage_scale(dep_vdds[i].voltdm,
+				dep_vdds[i].cur_dep_volt);
+	return 0;
+}
+
 /* Public functions */
 /**
  * omap_voltage_get_nom_volt() - Gets the current non-auto-compensated voltage
@@ -1675,6 +1781,8 @@ int omap_voltage_scale(struct voltagedomain *voltdm, unsigned long volt)
 	int is_volt_scaled = 0;
 	struct omap_vdd_info *vdd;
 	struct omap_vdd_dev_list *temp_dev;
+	struct plist_node *node;
+	struct omap_vdd_user_list *user;
 
 	if (!voltdm || IS_ERR(voltdm)) {
 		pr_warning("%s: VDD specified does not exist!\n", __func__);
@@ -1687,6 +1795,17 @@ int omap_voltage_scale(struct voltagedomain *voltdm, unsigned long volt)
 
 	curr_volt = omap_voltage_get_nom_volt(voltdm);
 
+	/* Find the device requesting the voltage scaling */
+	node = plist_first(&vdd->user_list);
+	user = container_of(node, struct omap_vdd_user_list, node);
+
+	/* calculate the voltages for dependent vdd's */
+	if (calc_dep_vdd_volt(user->dev, vdd, volt)) {
+		pr_warning("%s: Error in calculating dependent vdd voltages"
+			"for vdd_%s\n", __func__, voltdm->name);
+		return -EINVAL;
+	}
+
 	if (curr_volt == volt) {
 		is_volt_scaled = 1;
 	} else if (curr_volt < volt) {
@@ -1721,6 +1840,9 @@ int omap_voltage_scale(struct voltagedomain *voltdm, unsigned long volt)
 
 	mutex_unlock(&vdd->scaling_mutex);
 
+	/* Scale dependent vdds */
+	scale_dep_vdd(vdd);
+
 	return 0;
 }
 
-- 
1.6.6.1

