From b1b41c78d5a19260605fcb259a51ca7cd71c097a Mon Sep 17 00:00:00 2001 From: Thara Gopinath 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 should be at voltage v1 when VDD 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 --- 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 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