ARM: tegra11: power: Add core EDP basic implementation
[linux-3.10.git] / arch / arm / mach-tegra / tegra11_edp.c
1 /*
2  * arch/arm/mach-tegra/tegra11_edp.c
3  *
4  * Copyright (C) 2012 NVIDIA Corporation.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/init.h>
19 #include <linux/string.h>
20 #include <linux/module.h>
21 #include <linux/clk.h>
22 #include <linux/kobject.h>
23 #include <linux/err.h>
24
25 #include <mach/edp.h>
26
27 #include "clock.h"
28 #include "fuse.h"
29
30 #define CORE_MODULES_STATES 1
31 #define TEMPERATURE_RANGES 3
32 #define CAP_CLKS_NUM 2
33 #define TOTAL_CAPS (CORE_EDP_PROFILES_NUM * CORE_MODULES_STATES *\
34                         TEMPERATURE_RANGES * CAP_CLKS_NUM)
35
36 struct core_edp_entry {
37         int sku;
38         unsigned int cap_mA;
39         int mult;
40         unsigned long cap_scpu_on[CORE_EDP_PROFILES_NUM][
41                 CORE_MODULES_STATES][TEMPERATURE_RANGES][CAP_CLKS_NUM];
42         unsigned long cap_scpu_off[CORE_EDP_PROFILES_NUM][
43                 CORE_MODULES_STATES][TEMPERATURE_RANGES][CAP_CLKS_NUM];
44 };
45
46 static int temperatures[] = { 50, 60, 120 };
47
48 #ifdef CONFIG_TEGRA_DUAL_CBUS
49 static char *cap_clks_names[] = { "edp.c2bus", "edp.emc" };
50 #else
51 static char *cap_clks_names[] = { "edp.cbus", "edp.emc" };
52 #endif
53 static struct clk *cap_clks[CAP_CLKS_NUM];
54
55 static struct core_edp_entry core_edp_table[] = {
56         {
57                 .sku            = 0,            /* SKU = 0 */
58                 .cap_mA         = 4000,         /* 4A cap */
59                 .mult           = 1000000,      /* MHZ */
60                 .cap_scpu_on    = {
61                         /* balanced profile */
62                         {       /* core modules power state 0 (all ON) */
63                                 {{ 520, 800 },
64                                  { 456, 550 },
65                                  { 370, 350 },
66                                 },
67                         },
68                         /* favor gpu */
69                         {       /* core modules power state 0 (all ON) */
70                                 {{ 520, 800 },
71                                  { 520, 300 },
72                                  { 520, 150 },
73                                 },
74                         },
75                         /* favor emc */
76                         {       /* core modules power state 0 (all ON) */
77                                 {{ 520, 800 },
78                                  { 372, 800 },
79                                  { 100, 800 },
80                                 }
81                         },
82                 },
83                 .cap_scpu_off   = {
84                         /* balanced profile */
85                         {       /* core modules power state 0 (all ON) */
86                                 {{ 520, 800 },
87                                  { 456, 600 },
88                                  { 450, 400 },
89                                 },
90                         },
91                         /* favor gpu */
92                         {       /* core modules power state 0 (all ON) */
93                                 {{ 520, 800 },
94                                  { 520, 400 },
95                                  { 520, 200 },
96                                 },
97                         },
98                         /* favor emc */
99                         {       /* core modules power state 0 (all ON)  */
100                                 {{ 520, 800 },
101                                  { 450, 800 },
102                                  { 380, 800 },
103                                 },
104                         },
105                 },
106         },
107 };
108
109 static struct core_edp_entry *find_edp_entry(int sku, unsigned int regulator_mA)
110 {
111         int i;
112
113         for (i = 0; i < ARRAY_SIZE(core_edp_table); i++) {
114                 struct core_edp_entry *entry = &core_edp_table[i];
115                 if ((entry->sku == sku) && (entry->cap_mA == regulator_mA))
116                         return entry;
117         }
118         return NULL;
119 }
120
121 static unsigned long clip_cap_rate(struct clk *cap_clk, unsigned long rate)
122 {
123         unsigned long floor, ceiling;
124         struct clk *p = clk_get_parent(cap_clk);
125
126         if (!p || !p->ops || !p->ops->shared_bus_update) {
127                 WARN(1, "%s: edp cap clk %s is not a shared bus user\n",
128                         __func__, cap_clk->name);
129                 return rate;
130         }
131
132         /*
133          * Clip cap rate to shared bus possible rates (going up via shared
134          * bus * ladder since bus clocks always rounds up with resolution of
135          * at least 2kHz)
136          */
137         ceiling = clk_round_rate(p, clk_get_min_rate(p));
138         do {
139                 floor = ceiling;
140                 ceiling = clk_round_rate(p, floor + 2000);
141                 if (IS_ERR_VALUE(ceiling)) {
142                         pr_err("%s: failed to clip %lu to %s possible rates\n",
143                                __func__, rate, p->name);
144                         return rate;
145                 }
146         } while ((floor < ceiling) && (ceiling <= rate));
147
148         if (floor > rate)
149                 WARN(1, "%s: %s cap rate %lu is below %s floor %lu\n",
150                         __func__, cap_clk->name, rate, p->name, floor);
151         return floor;
152 }
153
154 int __init tegra11x_select_core_edp_table(unsigned int regulator_mA,
155                                           struct tegra_core_edp_limits *limits)
156 {
157         int i;
158         int sku = tegra_sku_id;
159         unsigned long *cap_rates;
160         struct core_edp_entry *edp_entry;
161
162         BUG_ON(ARRAY_SIZE(temperatures) != TEMPERATURE_RANGES);
163         BUG_ON(ARRAY_SIZE(cap_clks_names) != CAP_CLKS_NUM);
164         for (i = 0; i < CAP_CLKS_NUM; i++) {
165                 struct clk *c = tegra_get_clock_by_name(cap_clks_names[i]);
166                 if (!c) {
167                         pr_err("%s: failed to find edp cap clock %s\n",
168                                __func__, cap_clks_names[i]);
169                         return -ENODEV;
170                 }
171                 cap_clks[i] = c;
172         }
173
174         edp_entry = find_edp_entry(sku, regulator_mA);
175         if (!edp_entry) {
176                 pr_err("%s: failed to find edp entry for sku %d cap mA %d\n",
177                        __func__, sku, regulator_mA);
178                 return -ENODATA;
179         }
180
181         limits->sku = sku;
182         limits->cap_clocks = cap_clks;
183         limits->cap_clocks_num = CAP_CLKS_NUM;
184         limits->temperatures = temperatures;
185         limits->temperature_ranges = TEMPERATURE_RANGES;
186         limits->core_modules_states = CORE_MODULES_STATES;
187
188         cap_rates = &edp_entry->cap_scpu_on[0][0][0][0];
189         limits->cap_rates_scpu_on = cap_rates;
190         for (i = 0; i < TOTAL_CAPS; i++, cap_rates++) {
191                 unsigned long rate = *cap_rates * edp_entry->mult;
192                 *cap_rates = clip_cap_rate(cap_clks[i % CAP_CLKS_NUM], rate);
193         }
194
195         cap_rates = &edp_entry->cap_scpu_off[0][0][0][0];
196         limits->cap_rates_scpu_off = cap_rates;
197         for (i = 0; i < TOTAL_CAPS; i++, cap_rates++) {
198                 unsigned long rate = *cap_rates * edp_entry->mult;
199                 *cap_rates = clip_cap_rate(cap_clks[i % CAP_CLKS_NUM], rate);
200         }
201
202         return 0;
203 }