ARM: tegra: power: Add dynamic CPU regulator mode control
[linux-3.10.git] / arch / arm / mach-tegra / dvfs.c
index 8b1a53e..f9885a2 100644 (file)
@@ -419,7 +419,7 @@ static int dvfs_rail_update(struct dvfs_rail *rail)
                return 0;
        /* Keep current voltage if regulator must not be disabled at run time */
        else if (!rail->jmp_to_zero) {
-               WARN(1, "%s cannot be turned off by dvfs\n");
+               WARN(1, "%s cannot be turned off by dvfs\n", rail->reg_id);
                return 0;
        }
        /* else: fall thru if regulator is turned off by side band signaling */
@@ -626,6 +626,13 @@ __tegra_dvfs_set_rate(struct dvfs *d, unsigned long rate)
                                __func__, d->clk_name, mv, detach_mv);
                        return -EINVAL;
                }
+
+               detach_mv = d->dvfs_rail->override_millivolts;
+               if (detach_mv && (mv > detach_mv)) {
+                       pr_warn("%s: %s: voltage %d above override level %d\n",
+                               __func__, d->clk_name, mv, detach_mv);
+                       return -EINVAL;
+               }
                d->cur_millivolts = millivolts[i];
        }
 
@@ -694,6 +701,19 @@ int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate)
        return predict_millivolts(c, millivolts, rate);
 }
 
+int tegra_dvfs_predict_peak_millivolts(struct clk *c, unsigned long rate)
+{
+       const int *millivolts;
+
+       if (!rate || !c->dvfs)
+               return 0;
+
+       millivolts = tegra_dvfs_is_dfll_range(c->dvfs, rate) ?
+                       c->dvfs->dfll_millivolts : c->dvfs->peak_millivolts ? :
+                       tegra_dvfs_get_millivolts_pll(c->dvfs);
+       return predict_millivolts(c, millivolts, rate);
+}
+
 int tegra_dvfs_predict_millivolts_pll(struct clk *c, unsigned long rate)
 {
        const int *millivolts;
@@ -877,7 +897,7 @@ int __init tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d)
         */
        if (i && c->ops && !c->ops->shared_bus_update &&
            !(c->flags & PERIPH_ON_CBUS) && !d->can_override) {
-               int mv = tegra_dvfs_predict_millivolts(c, d->freqs[i-1]);
+               int mv = tegra_dvfs_predict_peak_millivolts(c, d->freqs[i-1]);
                if (d->dvfs_rail->min_override_millivolts < mv)
                        d->dvfs_rail->min_override_millivolts = mv;
        }
@@ -1188,6 +1208,22 @@ bool tegra_dvfs_is_rail_up(struct dvfs_rail *rail)
        return ret;
 }
 
+int tegra_dvfs_rail_set_mode(struct dvfs_rail *rail, unsigned int mode)
+{
+       int ret = -ENOENT;
+
+       pr_debug("%s: updating %s mode from %u to %u\n", __func__,
+                rail->reg_id, regulator_get_mode(rail->reg), mode);
+
+       if (rail && rail->reg)
+               ret = regulator_set_mode(rail->reg, mode);
+
+       if (ret)
+               pr_err("Failed to set dvfs regulator %s mode %u\n",
+                      rail->reg_id, mode);
+       return ret;
+}
+
 bool tegra_dvfs_rail_updating(struct clk *clk)
 {
        return (!clk ? false :
@@ -1275,6 +1311,28 @@ struct tegra_cooling_device *tegra_dvfs_get_gpu_vmin_cdev(void)
        return NULL;
 }
 
+struct tegra_cooling_device *tegra_dvfs_get_gpu_vts_cdev(void)
+{
+       if (tegra_gpu_rail)
+               return tegra_gpu_rail->vts_cdev;
+       return NULL;
+}
+
+static void make_safe_thermal_dvfs(struct dvfs_rail *rail)
+{
+       struct dvfs *d;
+
+       mutex_lock(&dvfs_lock);
+       list_for_each_entry(d, &rail->dvfs, reg_node) {
+               if (d->therm_dvfs) {
+                       BUG_ON(!d->peak_millivolts);
+                       d->millivolts = d->peak_millivolts;
+                       d->therm_dvfs = false;
+               }
+       }
+       mutex_unlock(&dvfs_lock);
+}
+
 #ifdef CONFIG_THERMAL
 /* Cooling device limits minimum rail voltage at cold temperature in pll mode */
 static int tegra_dvfs_rail_get_vmin_cdev_max_state(
@@ -1369,20 +1427,27 @@ static struct thermal_cooling_device_ops tegra_dvfs_vts_cooling_ops = {
 
 static void tegra_dvfs_rail_register_vts_cdev(struct dvfs_rail *rail)
 {
+       struct thermal_cooling_device *dev;
+
        if (!rail->vts_cdev)
                return;
 
-       /* just report error - initialized for cold temperature, anyway */
-       if (IS_ERR_OR_NULL(thermal_cooling_device_register(
-               rail->vts_cdev->cdev_type, (void *)rail,
-               &tegra_dvfs_vts_cooling_ops)))
+       dev = thermal_cooling_device_register(rail->vts_cdev->cdev_type,
+               (void *)rail, &tegra_dvfs_vts_cooling_ops);
+       /* report error & set max limits across thermal ranges as safe dvfs */
+       if (IS_ERR_OR_NULL(dev) || list_empty(&dev->thermal_instances)) {
                pr_err("tegra cooling device %s failed to register\n",
                       rail->vts_cdev->cdev_type);
+               make_safe_thermal_dvfs(rail);
+       }
 }
 
 #else
 #define tegra_dvfs_rail_register_vmin_cdev(rail)
-#define tegra_dvfs_rail_register_vts_cdev(rail)
+static inline void tegra_dvfs_rail_register_vts_cdev(struct dvfs_rail *rail)
+{
+       make_safe_thermal_dvfs(rail);
+}
 #endif
 
 /*
@@ -1497,8 +1562,8 @@ int __init tegra_dvfs_rail_init_thermal_dvfs_trips(
        return 0;
 }
 
-int __init tegra_dvfs_init_thermal_dvfs_voltages(
-       int *therm_voltages, int freqs_num, int ranges_num, struct dvfs *d)
+int __init tegra_dvfs_init_thermal_dvfs_voltages(int *therm_voltages,
+       int *peak_voltages, int freqs_num, int ranges_num, struct dvfs *d)
 {
        int *millivolts;
        int freq_idx, therm_idx;
@@ -1514,10 +1579,13 @@ int __init tegra_dvfs_init_thermal_dvfs_voltages(
                                     d->clk_name, mv, freq_idx, therm_idx);
                                return -EINVAL;
                        }
+                       if (mv > peak_voltages[freq_idx])
+                               peak_voltages[freq_idx] = mv;
                }
        }
 
        d->millivolts = therm_voltages;
+       d->peak_millivolts = peak_voltages;
        d->therm_dvfs = true;
        return 0;
 }
@@ -1737,39 +1805,6 @@ static const struct file_operations rail_stats_fops = {
        .release        = single_release,
 };
 
-static int gpu_dvfs_show(struct seq_file *s, void *data)
-{
-       int idx;
-       int *millivolts;
-       unsigned long *freqs;
-
-       if (read_gpu_dvfs_table(&millivolts, &freqs)) {
-               seq_printf(s, "Only supported for T124 or higher\n");
-               return 0;
-       }
-
-       seq_printf(s, "millivolts \t \t frequency\n");
-       seq_printf(s, "=====================================\n");
-
-       for (idx = 0; millivolts[idx]; idx++)
-               seq_printf(s, "%d mV \t \t %lu Hz\n", millivolts[idx],
-                               freqs[idx]);
-
-       return 0;
-}
-
-static int gpu_dvfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, gpu_dvfs_show, NULL);
-}
-
-static const struct file_operations gpu_dvfs_fops = {
-       .open           = gpu_dvfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
 static int rail_offs_set(struct dvfs_rail *rail, int offs)
 {
        if (rail) {
@@ -1890,6 +1925,12 @@ static int gpu_dvfs_t_show(struct seq_file *s, void *data)
                seq_printf(s, " mV\n");
        }
 
+       seq_printf(s, "%3s%-8s\n", "", "------");
+       seq_printf(s, "%3s%-8s", "", "max(T)");
+       for (i = 0; i < d->num_freqs; i++)
+               seq_printf(s, " %7d", d->peak_millivolts[i]);
+       seq_printf(s, " mV\n");
+
        mutex_unlock(&dvfs_lock);
 
        return 0;
@@ -1912,32 +1953,41 @@ static int dvfs_table_show(struct seq_file *s, void *data)
        int i;
        struct dvfs *d;
        struct dvfs_rail *rail;
+       const int *v_pll, *last_v_pll = NULL;
+       const int *v_dfll, *last_v_dfll = NULL;
 
-       seq_printf(s, "DVFS tables: units mV/MHz\n\n");
+       seq_printf(s, "DVFS tables: units mV/MHz\n");
 
        mutex_lock(&dvfs_lock);
 
        list_for_each_entry(rail, &dvfs_rail_list, node) {
-               bool mv_done = false;
                list_for_each_entry(d, &rail->dvfs, reg_node) {
-                       if (!mv_done) {
-                               const int *m = tegra_dvfs_get_millivolts_pll(d);
-                               mv_done = true;
-                               seq_printf(s, "%-16s", rail->reg_id);
-                               for (i = 0; i < d->num_freqs; i++) {
-                                       int mv = m[i];
-                                       seq_printf(s, "%7d", mv);
+                       bool mv_done = false;
+                       v_pll = tegra_dvfs_get_millivolts_pll(d);
+                       v_dfll = d->dfll_millivolts;
+
+                       if (v_pll && (last_v_pll != v_pll)) {
+                               if (!mv_done) {
+                                       seq_printf(s, "\n");
+                                       mv_done = true;
                                }
+                               last_v_pll = v_pll;
+                               seq_printf(s, "%-16s", rail->reg_id);
+                               for (i = 0; i < d->num_freqs; i++)
+                                       seq_printf(s, "%7d", v_pll[i]);
                                seq_printf(s, "\n");
-                               if (d->dfll_millivolts) {
-                                       seq_printf(s, "%-8s (dfll) ",
-                                                  rail->reg_id);
-                                       for (i = 0; i < d->num_freqs; i++) {
-                                               int mv = d->dfll_millivolts[i];
-                                               seq_printf(s, "%7d", mv);
-                                       }
+                       }
+
+                       if (v_dfll && (last_v_dfll != v_dfll)) {
+                               if (!mv_done) {
                                        seq_printf(s, "\n");
+                                       mv_done = true;
                                }
+                               last_v_dfll = v_dfll;
+                               seq_printf(s, "%-8s (dfll) ", rail->reg_id);
+                               for (i = 0; i < d->num_freqs; i++)
+                                       seq_printf(s, "%7d", v_dfll[i]);
+                               seq_printf(s, "\n");
                        }
 
                        seq_printf(s, "%-16s", d->clk_name);
@@ -1947,7 +1997,6 @@ static int dvfs_table_show(struct seq_file *s, void *data)
                        }
                        seq_printf(s, "\n");
                }
-               seq_printf(s, "\n");
        }
 
        mutex_unlock(&dvfs_lock);
@@ -2001,11 +2050,6 @@ int __init dvfs_debugfs_init(struct dentry *clk_debugfs_root)
        if (!d)
                return -ENOMEM;
 
-       d = debugfs_create_file("gpu_dvfs", S_IRUGO | S_IWUSR,
-               clk_debugfs_root, NULL, &gpu_dvfs_fops);
-       if (!d)
-               return -ENOMEM;
-
        d = debugfs_create_file("gpu_dvfs_t", S_IRUGO | S_IWUSR,
                clk_debugfs_root, NULL, &gpu_dvfs_t_fops);
        if (!d)