pandora: defconfig: update
[pandora-kernel.git] / drivers / misc / pandora.c
1 /*
2         pandora.c
3         Exports some additional hardware control to userspace.
4         Written by GraÅžvydas "notaz" Ignotas <notasas@gmail.com>
5
6         This program is free software; you can redistribute it and/or modify
7         it under the terms of the GNU General Public License as published by
8         the Free Software Foundation; version 2 of the License.
9 */
10
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/proc_fs.h>
14 #include <linux/uaccess.h>
15 #include <linux/delay.h>
16 #include <linux/clk.h>
17 #include <linux/io.h>
18
19 #ifndef CONFIG_PROC_FS
20 #error need CONFIG_PROC_FS
21 #endif
22
23 #define PND_PROC_CPUMHZ         "pandora/cpu_mhz_max"
24
25 /*
26  * note:
27  * SYS_CLK is 26 MHz (see PRM_CLKSEL)
28  * ARM_FCLK = (SYS_CLK * M * 2) / ([N+1] * M2) / 2
29  * CM_CLKSEL1_PLL_MPU = N | (M << 8) | (M2 << 19)
30  */
31 static int get_fclk(void)
32 {
33         unsigned __iomem *base;
34         unsigned ret;
35
36         base = ioremap(0x48004000, 0x2000);
37         if (base == NULL) {
38                 printk(KERN_ERR "get_fclk: can't ioremap\n");
39                 return -1;
40         }
41
42         ret = base[0x940>>2];
43         iounmap(base);
44
45         if ((ret & ~0x7ff00) != 0x10000c) {
46                 printk(KERN_ERR "get_fclk: unexpected CM_CLKSEL1_PLL_MPU: "
47                                 "%08x\n", ret);
48                 return -1;
49         }
50
51         ret &= 0x7ff00;
52         ret >>= 8;
53
54         return (int)ret;
55 }
56
57 static void set_fclk(int val)
58 {
59         struct clk *pllclk, *fclk;
60         int ret;
61
62         if (val & ~0x7ff) {
63                 printk(KERN_ERR "set_fclk: value %d out of range\n", val);
64                 return;
65         }
66
67
68         pllclk = clk_get(NULL, "dpll1_ck");
69         if (IS_ERR(pllclk)) {
70                 printk(KERN_ERR "set_fclk: clk_get() failed: %li\n",
71                                 PTR_ERR(pllclk));
72                 return;
73         }
74
75         ret = clk_set_rate(pllclk, val * 1000000);
76         if (ret)
77                 printk(KERN_ERR "set_fclk: clk_set_rate(dpll1_ck) "
78                                 "failed: %li\n", PTR_ERR(pllclk));
79         msleep(100);
80
81         printk(KERN_INFO "dpll1_ck rate: %li\n", clk_get_rate(pllclk));
82         clk_put(pllclk);
83
84         fclk = clk_get(NULL, "arm_fck");
85         if (!IS_ERR(pllclk)) {
86                 printk(KERN_INFO "arm_fck  rate: %li\n", clk_get_rate(fclk));
87                 clk_put(fclk);
88         }
89
90         printk(KERN_INFO "PLL_MPU  rate: %i\n", get_fclk() * 1000000);
91 }
92
93 static int cpu_clk_read(char *page, char **start, off_t off, int count,
94                 int *eof, void *data)
95 {
96         char *p = page;
97         int len;
98
99         p += sprintf(p, "%d\n", get_fclk());
100
101         len = (p - page) - off;
102         if (len < 0)
103                 len = 0;
104
105         *eof = (len <= count) ? 1 : 0;
106         *start = page + off;
107
108         return len;
109 }
110
111 static int cpu_clk_write(struct file *file, const char __user *buffer,
112                 unsigned long count, void *data)
113 {
114         char buff[32];
115         unsigned long val;
116         int ret;
117
118         count = strncpy_from_user(buff, buffer,
119                         count < sizeof(buff) ? count : sizeof(buff) - 1);
120         buff[count] = 0;
121
122         ret = strict_strtoul(buff, 0, &val);
123         if (ret < 0) {
124                 printk(KERN_ERR "error %i parsing %s\n", ret, buff);
125                 return ret;
126         }
127
128         set_fclk(val);
129
130         return count;
131 }
132
133 /* ************************************************************************* */
134
135 static int pndctrl_init(void)
136 {
137         struct proc_dir_entry *pret;
138         int ret = -ENOMEM;
139
140         pret = create_proc_entry(PND_PROC_CPUMHZ, S_IWUSR | S_IRUGO, NULL);
141         if (pret == NULL) {
142                 proc_mkdir("pandora", NULL);
143                 pret = create_proc_entry(PND_PROC_CPUMHZ,
144                                         S_IWUSR | S_IRUGO, NULL);
145                 if (pret == NULL) {
146                         printk(KERN_ERR "can't create proc entry\n");
147                         return ret;
148                 }
149         }
150
151         pret->read_proc = cpu_clk_read;
152         pret->write_proc = cpu_clk_write;
153
154         printk(KERN_INFO "pndctrl loaded.\n");
155
156         return 0;
157 }
158
159
160 static void pndctrl_cleanup(void)
161 {
162         remove_proc_entry(PND_PROC_CPUMHZ, NULL);
163         printk(KERN_INFO "pndctrl unloaded.\n");
164 }
165
166
167 module_init(pndctrl_init);
168 module_exit(pndctrl_cleanup);
169
170 MODULE_AUTHOR("Grazvydas Ignotas");
171 MODULE_LICENSE("GPL");
172 MODULE_DESCRIPTION("Pandora Additional hw control");