misc: add OMAP overclocking 'driver'
[pandora-kernel.git] / drivers / misc / omap_overclocking.c
1 /*
2  * OMAP CPU overclocking hacks
3  *
4  * Licensed under the GPL-2 or later.
5  */
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/proc_fs.h>
9 #include <linux/opp.h>
10 #include <linux/uaccess.h>
11
12 #include <plat/omap_device.h>
13
14 #define PROC_DIR        "pandora"
15 #define PROC_CPUMHZ     "pandora/cpu_mhz_max"
16 #define PROC_CPUOPP     "pandora/cpu_opp_max"
17
18 /* FIXME: could use opp3xxx_data.c, but that's initdata.. */
19 static const unsigned long nominal_freqs_35xx[] = {
20         125000000, 250000000, 500000000, 550000000, 600000000,
21 };
22
23 static const unsigned long nominal_freqs_36xx[] = {
24         300000000, 600000000, 800000000, 1000000000,
25 };
26
27 static const unsigned long *nominal_freqs;
28
29 static int opp_max_avail, opp_max;
30
31 static int set_opp_max(int new_opp_max)
32 {
33         struct device *mpu_dev;
34         int i, ret;
35
36         if (new_opp_max == opp_max)
37                 return 0;
38
39         mpu_dev = omap_device_get_by_hwmod_name("mpu");
40         if (IS_ERR(mpu_dev)) {
41                 pr_err("%s: mpu device not available (%ld)\n",
42                         __func__, PTR_ERR(mpu_dev));
43                 return -ENODEV;
44         }
45
46         for (i = 1; i < new_opp_max; i++) {
47                 ret = opp_enable_i(mpu_dev, i);
48                 if (ret != 0)
49                         dev_err(mpu_dev, "%s: opp_enable returned %d\n",
50                                 __func__, ret);
51         }
52
53         for (i = new_opp_max; i < opp_max_avail; i++) {
54                 ret = opp_disable_i(mpu_dev, i);
55                 if (ret != 0)
56                         dev_err(mpu_dev, "%s: opp_disable returned %d\n",
57                                 __func__, ret);
58         }
59
60         opp_max = new_opp_max;
61         dev_info(mpu_dev, "max OPP set to %d\n", opp_max);
62
63         return 0;
64 }
65
66 static int set_mhz_max(unsigned long new_mhz_max)
67 {
68         unsigned long cur_mhz_max = 0;
69         struct device *mpu_dev;
70         int index, ret;
71
72         new_mhz_max *= 1000000;
73
74         if (opp_max < 1 || opp_max > opp_max_avail) {
75                 pr_err("%s: corrupt opp_max: %d\n", __func__, opp_max);
76                 return -EINVAL;
77         }
78         index = opp_max - 1;
79
80         /* going below nominal makes no sense, it will save us nothing,
81          * user should reduce max OPP instead */
82         if (new_mhz_max < nominal_freqs[index])
83                 new_mhz_max = nominal_freqs[index];
84
85         mpu_dev = omap_device_get_by_hwmod_name("mpu");
86         if (IS_ERR(mpu_dev)) {
87                 pr_err("%s: mpu device not available (%ld)\n",
88                         __func__, PTR_ERR(mpu_dev));
89                 return -ENODEV;
90         }
91
92         opp_hack_get_freq(mpu_dev, index, &cur_mhz_max);
93         if (cur_mhz_max == new_mhz_max)
94                 return 0;
95
96         ret = opp_hack_set_freq(mpu_dev, index, new_mhz_max);
97         if (ret != 0) {
98                 dev_err(mpu_dev, "%s: opp_hack_set_freq returned %d\n",
99                         __func__, ret);
100                 return ret;
101         }
102
103         return 0;
104 }
105
106 static int get_mhz_max(void)
107 {
108         unsigned long cur_mhz_max = 0;
109         struct device *mpu_dev;
110
111         if (opp_max < 1 || opp_max > opp_max_avail) {
112                 pr_err("%s: corrupt opp_max: %d\n", __func__, opp_max);
113                 return -EINVAL;
114         }
115
116         mpu_dev = omap_device_get_by_hwmod_name("mpu");
117         if (IS_ERR(mpu_dev)) {
118                 pr_err("%s: mpu device not available (%ld)\n",
119                         __func__, PTR_ERR(mpu_dev));
120                 return -ENODEV;
121         }
122
123         opp_hack_get_freq(mpu_dev, opp_max - 1, &cur_mhz_max);
124
125         return cur_mhz_max / 1000000;
126 }
127
128 static int init_opp_hacks(void)
129 {
130         struct device *mpu_dev;
131
132         mpu_dev = omap_device_get_by_hwmod_name("mpu");
133         if (IS_ERR(mpu_dev)) {
134                 pr_err("%s: mpu device not available (%ld)\n",
135                         __func__, PTR_ERR(mpu_dev));
136                 return -ENODEV;
137         }
138
139         if (cpu_is_omap3630()) {
140                 nominal_freqs = nominal_freqs_36xx;
141                 opp_max_avail = sizeof(nominal_freqs_36xx) / sizeof(nominal_freqs_36xx[0]);
142                 opp_max = 2;
143         } else if (cpu_is_omap34xx()) {
144                 nominal_freqs = nominal_freqs_35xx;
145                 opp_max_avail = sizeof(nominal_freqs_35xx) / sizeof(nominal_freqs_35xx[0]);
146                 opp_max = opp_max_avail;
147         } else {
148                 dev_err(mpu_dev, "%s: unsupported CPU\n", __func__);
149                 return -ENODEV;
150         }
151
152         return 0;
153 }
154
155 static int proc_read_val(char *page, char **start, off_t off, int count,
156                 int *eof, int val)
157 {
158         char *p = page;
159         int len;
160
161         p += sprintf(p, "%d\n", val);
162
163         len = (p - page) - off;
164         if (len < 0)
165                 len = 0;
166
167         *eof = (len <= count) ? 1 : 0;
168         *start = page + off;
169
170         return len;
171 }
172
173 static int proc_write_val(struct file *file, const char __user *buffer,
174                 unsigned long count, unsigned long *val)
175 {
176         char buff[32];
177         int ret;
178
179         count = strncpy_from_user(buff, buffer,
180                         count < sizeof(buff) ? count : sizeof(buff) - 1);
181         buff[count] = 0;
182
183         ret = strict_strtoul(buff, 0, val);
184         if (ret < 0) {
185                 pr_err("error %i parsing %s\n", ret, buff);
186                 return ret;
187         }
188
189         return count;
190 }
191
192 static int cpu_clk_read(char *page, char **start, off_t off, int count,
193                 int *eof, void *data)
194 {
195         return proc_read_val(page, start, off, count, eof, get_mhz_max());
196 }
197
198 static int cpu_clk_write(struct file *file, const char __user *buffer,
199                 unsigned long count, void *data)
200 {
201         unsigned long val;
202         int ret, retval;
203
204         retval = proc_write_val(file, buffer, count, &val);
205         if (retval < 0)
206                 return retval;
207
208         ret = set_mhz_max(val);
209         if (ret < 0)
210                 return ret;
211
212         return retval;
213 }
214
215 static int cpu_maxopp_read(char *page, char **start, off_t off, int count,
216                 int *eof, void *data)
217 {
218         return proc_read_val(page, start, off, count, eof, opp_max);
219 }
220
221 static int cpu_maxopp_write(struct file *file, const char __user *buffer,
222                 unsigned long count, void *data)
223 {
224         unsigned long val;
225         int ret, retval;
226
227         retval = proc_write_val(file, buffer, count, &val);
228         if (retval < 0)
229                 return retval;
230
231         if (val < 1 || val > opp_max_avail)
232                 return -EINVAL;
233
234         ret = set_opp_max(val);
235         if (ret != 0)
236                 return ret;
237
238         return retval;
239 }
240
241 static void proc_create_rw(const char *name, void *pdata,
242                            read_proc_t *read_proc, write_proc_t *write_proc)
243 {
244         struct proc_dir_entry *pret;
245
246         pret = create_proc_entry(name, S_IWUGO | S_IRUGO, NULL);
247         if (pret == NULL) {
248                 proc_mkdir(PROC_DIR, NULL);
249                 pret = create_proc_entry(name, S_IWUGO | S_IRUGO, NULL);
250                 if (pret == NULL) {
251                         pr_err("%s: failed to create proc file %s\n",
252                                 __func__, name);
253                         return;
254                 }
255         }
256
257         pret->data = pdata;
258         pret->read_proc = read_proc;
259         pret->write_proc = write_proc;
260 }
261
262 static int pndctrl_init(void)
263 {
264         int ret;
265
266         ret = init_opp_hacks();
267         if (ret != 0) {
268                 pr_err("init_opp_hacks failed: %d\n", ret);
269                 return -EFAULT;
270         }
271
272         proc_create_rw(PROC_CPUMHZ, NULL, cpu_clk_read, cpu_clk_write);
273         proc_create_rw(PROC_CPUOPP, NULL, cpu_maxopp_read, cpu_maxopp_write);
274
275         pr_info("OMAP overclocker loaded.\n");
276         return 0;
277 }
278
279
280 static void pndctrl_cleanup(void)
281 {
282         remove_proc_entry(PROC_CPUOPP, NULL);
283         remove_proc_entry(PROC_CPUMHZ, NULL);
284 }
285
286 module_init(pndctrl_init);
287 module_exit(pndctrl_cleanup);
288
289 MODULE_AUTHOR("GraÅžvydas Ignotas");
290 MODULE_LICENSE("GPL");
291 MODULE_DESCRIPTION("OMAP overclocking support");