dvfs: add leakage adjust volt support
authorcl <cl@rock-chips.com>
Wed, 5 Nov 2014 09:07:54 +0000 (17:07 +0800)
committercl <cl@rock-chips.com>
Wed, 5 Nov 2014 09:08:12 +0000 (17:08 +0800)
Signed-off-by: cl <cl@rock-chips.com>
arch/arm/boot/dts/rk312x.dtsi
arch/arm/mach-rockchip/dvfs.c
include/linux/rockchip/dvfs.h

index fb0a437537e6ec49bce3fafae6dbb5d2b9967243..795e112edb1611ec2745f5933954245e39fe2ca1 100755 (executable)
                                                0          3
                                        >;
                                        regu-mode-en = <0>;
+                                       lkg_adjust_volt_en = <1>;
+                                       channel = <0>;
+                                       def_table_lkg = <35>;
+                                       min_adjust_freq = <1200000>;
+                                       lkg_adjust_volt_table = <
+                                               /*lkg(mA)  volt(uV)*/
+                                               60         25000
+                                               >;
                                };
                        };
                };
index fe12ce36f41c7450e253bfb6e60f9aa2408f5c9d..0944f56230a68162efa3668cc7fbfa047b9173df 100644 (file)
@@ -915,8 +915,7 @@ static struct pvtm_info rk3288v1_arm_pvtm_info = {
 
 static struct pvtm_info *pvtm_info_table[] = {
        &rk3288v0_arm_pvtm_info,
-       &rk3288v1_arm_pvtm_info,
-       NULL
+       &rk3288v1_arm_pvtm_info
 };
 
 static int pvtm_set_single_dvfs(struct dvfs_node *dvfs_node, u32 idx,
@@ -1292,6 +1291,68 @@ int dvfs_set_freq_volt_table(struct dvfs_node *clk_dvfs_node, struct cpufreq_fre
 }
 EXPORT_SYMBOL(dvfs_set_freq_volt_table);
 
+static int get_adjust_volt_by_leakage(struct dvfs_node *dvfs_node)
+{
+       int leakage = 0;
+       int delta_leakage = 0;
+       int i = 0;
+       int adjust_volt = 0;
+
+       if (!dvfs_node->vd)
+               return 0;
+
+       if (dvfs_node->lkg_info.def_table_lkg == -1)
+               return 0;
+
+       leakage = rockchip_get_leakage(dvfs_node->channel);
+       if (!leakage || (leakage == 0xff))
+               return 0;
+
+       delta_leakage = leakage - dvfs_node->lkg_info.def_table_lkg;
+       if (delta_leakage <= 0) {
+               for (i = 0; (dvfs_node->lkg_info.table[i].dlt_volt !=
+                       CPUFREQ_TABLE_END); i++) {
+                       if (leakage > dvfs_node->lkg_info.table[i].lkg) {
+                               adjust_volt =
+                                       dvfs_node->lkg_info.table[i].dlt_volt;
+                       } else {
+                               return adjust_volt;
+                       }
+               }
+       } else if (delta_leakage > 0) {
+               for (i = 0; (dvfs_node->lkg_info.table[i].dlt_volt !=
+                       CPUFREQ_TABLE_END); i++) {
+                       if (leakage <= dvfs_node->lkg_info.table[i].lkg) {
+                               adjust_volt =
+                                       -dvfs_node->lkg_info.table[i].dlt_volt;
+                               return adjust_volt;
+                       }
+               }
+       }
+       return adjust_volt;
+}
+
+static void adjust_table_by_leakage(struct dvfs_node *dvfs_node)
+{
+       int i, adjust_volt = get_adjust_volt_by_leakage(dvfs_node);
+
+       if (!adjust_volt)
+               return;
+
+       if (!dvfs_node->dvfs_table)
+               return;
+
+       if (dvfs_node->lkg_info.min_adjust_freq == -1)
+               return;
+
+       for (i = 0;
+       (dvfs_node->dvfs_table[i].frequency != CPUFREQ_TABLE_END); i++) {
+               if (dvfs_node->dvfs_table[i].frequency >=
+                       dvfs_node->lkg_info.min_adjust_freq)
+                       dvfs_node->dvfs_table[i].index += adjust_volt;
+       }
+}
+
 int clk_enable_dvfs(struct dvfs_node *clk_dvfs_node)
 {
        struct cpufreq_frequency_table clk_fv;
@@ -1338,6 +1399,8 @@ int clk_enable_dvfs(struct dvfs_node *clk_dvfs_node)
                dvfs_table_round_clk_rate(clk_dvfs_node);
                dvfs_get_rate_range(clk_dvfs_node);
                clk_dvfs_node->freq_limit_en = 1;
+               if (clk_dvfs_node->lkg_adjust_volt_en)
+                       adjust_table_by_leakage(clk_dvfs_node);
                if (clk_dvfs_node->support_pvtm)
                        pvtm_set_dvfs_table(clk_dvfs_node);
                dvfs_table_round_volt(clk_dvfs_node);
@@ -1867,6 +1930,44 @@ static int of_get_dvfs_pvtm_table(struct device_node *dev_node,
        return 0;
 }
 
+static struct lkg_adjust_volt_table
+       *of_get_lkg_adjust_volt_table(struct device_node *np,
+       const char *propname)
+{
+       struct lkg_adjust_volt_table *lkg_adjust_volt_table = NULL;
+       const struct property *prop;
+       const __be32 *val;
+       int nr, i;
+
+       prop = of_find_property(np, propname, NULL);
+       if (!prop)
+               return NULL;
+       if (!prop->value)
+               return NULL;
+
+       nr = prop->length / sizeof(s32);
+       if (nr % 2) {
+               pr_err("%s: Invalid freq list\n", __func__);
+               return NULL;
+       }
+
+       lkg_adjust_volt_table =
+               kzalloc(sizeof(struct lkg_adjust_volt_table) *
+               (nr/2 + 1), GFP_KERNEL);
+
+       val = prop->value;
+
+       for (i = 0; i < nr/2; i++) {
+               lkg_adjust_volt_table[i].lkg = be32_to_cpup(val++);
+               lkg_adjust_volt_table[i].dlt_volt = be32_to_cpup(val++);
+       }
+
+       lkg_adjust_volt_table[i].lkg = 0;
+       lkg_adjust_volt_table[i].dlt_volt = CPUFREQ_TABLE_END;
+
+       return lkg_adjust_volt_table;
+}
+
 static int dvfs_node_parse_dt(struct device_node *np,
                              struct dvfs_node *dvfs_node)
 {
@@ -1937,6 +2038,23 @@ static int dvfs_node_parse_dt(struct device_node *np,
                        return -EINVAL;
        }
 
+       of_property_read_u32_index(np, "lkg_adjust_volt_en", 0,
+                                  &dvfs_node->lkg_adjust_volt_en);
+       if (dvfs_node->lkg_adjust_volt_en) {
+               dvfs_node->lkg_info.def_table_lkg = -1;
+               of_property_read_u32_index(np, "def_table_lkg", 0,
+                                          &dvfs_node->lkg_info.def_table_lkg);
+
+               dvfs_node->lkg_info.min_adjust_freq = -1;
+               of_property_read_u32_index(np, "min_adjust_freq", 0,
+                                          &dvfs_node->lkg_info.min_adjust_freq
+                                          );
+
+               dvfs_node->lkg_info.table =
+                       of_get_lkg_adjust_volt_table(np,
+                                                    "lkg_adjust_volt_table");
+       }
+
        return 0;
 }
 
index 47c3d084fd9d59b6e1615de0981df4a2ddc7e3b6..594f9986cbd3512a424218e80c05b87e77d8c6e6 100644 (file)
@@ -95,6 +95,17 @@ struct pvtm_info {
        int max_volt_uv;
 };
 
+struct lkg_adjust_volt_table {
+       int     lkg;
+       int     dlt_volt;
+};
+
+struct lkg_info {
+       int     def_table_lkg;
+       int     min_adjust_freq;
+       struct  lkg_adjust_volt_table *table;
+};
+
 /**
  * struct dvfs_node:   To Store All dvfs clocks' info
  * @name:              Dvfs clock's Name
@@ -137,6 +148,8 @@ struct dvfs_node {
        int                     regu_mode_en;
        unsigned int            regu_mode;
        struct pvtm_info        *pvtm_info;
+       int                 lkg_adjust_volt_en;
+       struct lkg_info         lkg_info;
 };