bd16d29aaf73c96094c9c6f08c337eff3086d1e7
[openembedded.git] /
1 From d247a112a9ffbad1057a2c8bede5727940ac2045 Mon Sep 17 00:00:00 2001
2 From: Ranjith Lohithakshan <ranjithl@ti.com>
3 Date: Fri, 28 May 2010 15:20:32 +0530
4 Subject: [PATCH 2/9] omap3: Fix voltage and frequency scaling order when changing OPP via mpurate
5
6 When lowering an OPP via mpurate from what is being set by bootloader,
7 the frequency need to be lowered before voltage is ramped down. The
8 current code was not taking this into consideration and was always
9 adjusting the voltage before the frequency adjustments.
10
11 This was leading to crashes on 3730 when mpurate was set to 300MHz
12 because the voltage was lowered before the frequency.
13
14 This patch fixes these issues by tracking the direction of OPP change
15 and doing the voltage and frequency change in the right order.
16
17 Signed-off-by: Ranjith Lohithakshan <ranjithl@ti.com>
18 ---
19  arch/arm/mach-omap2/clock34xx.c   |   51 +++++++++++++++++++++++-------------
20  arch/arm/mach-omap2/smartreflex.c |   11 +++++---
21  2 files changed, 39 insertions(+), 23 deletions(-)
22
23 diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
24 index 2bb7182..dd408eb 100644
25 --- a/arch/arm/mach-omap2/clock34xx.c
26 +++ b/arch/arm/mach-omap2/clock34xx.c
27 @@ -65,6 +65,7 @@ struct clk *sdrc_ick_p, *arm_fck_p;
28   */
29  unsigned int vdd1_opp = 0;
30  unsigned int vdd2_opp = 0;
31 +bool vdd_scale_down = false;
32  
33  /**
34   * omap3430es2_clk_ssi_find_idlest - return CM_IDLEST info for SSI
35 @@ -429,6 +430,24 @@ static void __init omap2_clk_iva_init_to_idle(void)
36  
37  }
38  
39 +static u16 get_opp(struct omap_opp *opp_freq_table,
40 +                                       unsigned long freq)
41 +{
42 +       struct omap_opp *prcm_config;
43 +
44 +       prcm_config = opp_freq_table;
45 +
46 +       if (prcm_config->rate <= freq)
47 +               return prcm_config->opp_id; /* Return the Highest OPP */
48 +       for (; prcm_config->rate; prcm_config--)
49 +               if (prcm_config->rate < freq)
50 +                       return (prcm_config+1)->opp_id;
51 +               else if (prcm_config->rate == freq)
52 +                       return prcm_config->opp_id;
53 +       /* Return the least OPP */
54 +       return (prcm_config+1)->opp_id;
55 +}
56 +
57  /* REVISIT: Move this init stuff out into clock.c */
58  
59  /*
60 @@ -444,8 +463,10 @@ static void __init omap2_clk_iva_init_to_idle(void)
61   */
62  static int __init omap2_clk_arch_init(void)
63  {
64 -       struct omap_opp *opp_table;
65 -       short valid=0, err=0, i;
66 +       short err=0;
67 +       u16     current_vdd1_opp;
68 +       struct clk *arm_fck;
69 +       unsigned long current_mpu_rate;
70  
71         if (!mpurate)
72                 return -EINVAL;
73 @@ -463,6 +484,10 @@ static int __init omap2_clk_arch_init(void)
74         if (WARN((!l3_opps), "OPP table not defined for L3\n"))
75                 err = 1;
76  
77 +       arm_fck = clk_get(NULL, "arm_fck");
78 +       if (WARN(IS_ERR(arm_fck), "Failed to get arm_fck.\n"))
79 +               err = 1;
80 +
81         if (err)
82                 return -ENOENT;
83  
84 @@ -475,24 +500,12 @@ static int __init omap2_clk_arch_init(void)
85                 pr_err("This silicon doesn't support 720MHz\n");
86         }
87  
88 -       /*
89 -        * Select VDD1 OPP corresponding to mpurate
90 -        */
91 -       opp_table = mpu_opps;
92 -
93 -       for (i = 1; opp_table[i].opp_id <= get_max_vdd1(); i++) {
94 -               if (opp_table[i].rate == mpurate) {
95 -                       valid = 1;
96 -                       break;
97 -               }
98 -       }
99 +       current_mpu_rate = clk_get_rate(arm_fck);
100 +       current_vdd1_opp = get_opp(mpu_opps + get_max_vdd1(), current_mpu_rate);
101 +       vdd1_opp = get_opp(mpu_opps + get_max_vdd1(), mpurate);
102  
103 -       if (valid) {
104 -               vdd1_opp = opp_table[i].opp_id;
105 -       } else {
106 -               pr_err("Invalid MPU rate (%u)\n", mpurate);
107 -               return -EINVAL;
108 -       }
109 +       if (vdd1_opp < current_vdd1_opp)
110 +               vdd_scale_down = true;
111  
112         /*
113          * Match lowest OPP setting for VDD1 with lowest OPP for VDD2 as well.
114 diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c
115 index 3b6ddf8..fd34af2 100644
116 --- a/arch/arm/mach-omap2/smartreflex.c
117 +++ b/arch/arm/mach-omap2/smartreflex.c
118 @@ -48,6 +48,7 @@
119   */
120  extern unsigned int vdd1_opp;
121  extern unsigned int vdd2_opp;
122 +extern bool vdd_scale_down;
123  
124  extern int __init omap2_clk_set_freq(void);
125  
126 @@ -1082,6 +1083,10 @@ static int __init omap3_sr_init(void)
127         sr_set_clk_length(&sr1);
128         sr_set_clk_length(&sr2);
129  
130 +       /* For OPP scale down, scale down frequency before voltage */
131 +       if (cpu_is_omap34xx() && vdd_scale_down)
132 +        omap2_clk_set_freq();
133 +
134         /* Call the VPConfig, VCConfig, set N Values. */
135         sr_set_nvalues(&sr1);
136         sr_configure_vp(SR1);
137 @@ -1089,10 +1094,8 @@ static int __init omap3_sr_init(void)
138         sr_set_nvalues(&sr2);
139         sr_configure_vp(SR2);
140  
141 -       /*
142 -        * With voltages matching target OPP, set corresponding frequency.
143 -        */
144 -       if (cpu_is_omap34xx())
145 +       /* For OPP scale up, scale up the frequency after voltage */
146 +       if (cpu_is_omap34xx() && !vdd_scale_down)
147                 omap2_clk_set_freq();
148  
149         ret = sysfs_create_file(power_kobj, &sr_vdd1_autocomp.attr);
150 -- 
151 1.6.2.4
152