3 Exports some additional hardware control to userspace.
4 Written by GraÅžvydas "notaz" Ignotas <notasas@gmail.com>
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.
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>
20 #include <../drivers/video/omap/dispc.h>
22 #ifndef CONFIG_PROC_FS
23 #error need CONFIG_PROC_FS
26 #define PND_PROC_DIR "pandora"
27 #define PND_PROC_CPUMHZ "cpu_mhz_max"
28 #define PND_PROC_VSYNC "wait_vsync"
32 * SYS_CLK is 26 MHz (see PRM_CLKSEL)
33 * ARM_FCLK = (SYS_CLK * M * 2) / ([N+1] * M2) / 2
34 * CM_CLKSEL1_PLL_MPU = N | (M << 8) | (M2 << 19)
36 static int get_fclk(void)
38 unsigned __iomem *base;
41 base = ioremap(0x48004000, 0x2000);
43 printk(KERN_ERR "get_fclk: can't ioremap\n");
50 if ((ret & ~0x7ff00) != 0x10000c) {
51 printk(KERN_ERR "get_fclk: unexpected CM_CLKSEL1_PLL_MPU: "
62 static void set_fclk(int val)
64 struct clk *pllclk, *fclk;
68 printk(KERN_ERR "set_fclk: value %d out of range\n", val);
73 pllclk = clk_get(NULL, "dpll1_ck");
75 printk(KERN_ERR "set_fclk: clk_get() failed: %li\n",
80 ret = clk_set_rate(pllclk, val * 1000000);
82 printk(KERN_ERR "set_fclk: clk_set_rate(dpll1_ck) "
83 "failed: %li\n", PTR_ERR(pllclk));
86 printk(KERN_INFO "dpll1_ck rate: %li\n", clk_get_rate(pllclk));
89 fclk = clk_get(NULL, "arm_fck");
90 if (!IS_ERR(pllclk)) {
91 printk(KERN_INFO "arm_fck rate: %li\n", clk_get_rate(fclk));
95 printk(KERN_INFO "PLL_MPU rate: %i\n", get_fclk() * 1000000);
98 static int cpu_clk_read(char *page, char **start, off_t off, int count,
104 p += sprintf(p, "%d\n", get_fclk());
106 len = (p - page) - off;
110 *eof = (len <= count) ? 1 : 0;
116 static int cpu_clk_write(struct file *file, const char __user *buffer,
117 unsigned long count, void *data)
123 count = strncpy_from_user(buff, buffer,
124 count < sizeof(buff) ? count : sizeof(buff) - 1);
127 ret = strict_strtoul(buff, 0, &val);
129 printk(KERN_ERR "error %i parsing %s\n", ret, buff);
138 /* ************************************************************************* */
140 static DECLARE_WAIT_QUEUE_HEAD(pnd_vsync_waitq);
141 static u32 pnd_vsync_counter;
142 static u32 pnd_vsync_last;
143 static u32 pnd_vsync_active;
145 static void pnd_vsync_callback(void *data)
148 if (pnd_vsync_active)
149 wake_up_interruptible(&pnd_vsync_waitq);
152 static int pnd_hook_vsync(void)
154 unsigned __iomem *base;
158 ret = omap_dispc_request_irq(2, pnd_vsync_callback, NULL);
160 printk(KERN_ERR "failed to get irq from dispc driver\n");
164 base = ioremap(0x48050400, 0x400);
166 omap_dispc_free_irq(2, pnd_vsync_callback, NULL);
167 printk(KERN_ERR "can't ioremap DISPC\n");
171 val = __raw_readl(base + 0x1c);
172 //printk("val: %08x\n", val);
174 __raw_writel(val, base + 0x1c);
180 static void pnd_unhook_vsync(void)
182 unsigned __iomem *base;
184 base = ioremap(0x48050400, 0x400);
186 printk(KERN_ERR "can't ioremap DISPC\n");
189 u32 val = __raw_readl(base + 0x1c);
191 __raw_writel(val, base + 0x1c);
195 omap_dispc_free_irq(2, pnd_vsync_callback, NULL);
198 static int pnd_vsync_read(char *page, char **start, off_t off, int count,
199 int *eof, void *data)
202 int len, val = -1, ret, vcount;
204 vcount = pnd_vsync_counter;
205 pnd_vsync_active = 1;
206 ret = wait_event_interruptible_timeout(pnd_vsync_waitq,
207 (vcount != pnd_vsync_counter), msecs_to_jiffies(250));
208 pnd_vsync_active = 0;
210 val = pnd_vsync_counter - pnd_vsync_last;
212 p += sprintf(p, "%d\n", val);
213 pnd_vsync_last = pnd_vsync_counter;
215 len = (p - page) - off;
219 *eof = (len <= count) ? 1 : 0;
225 /* ************************************************************************* */
227 static struct proc_dir_entry *proc_dir;
229 static int pndctrl_init(void)
231 struct proc_dir_entry *pret;
234 proc_dir = proc_mkdir(PND_PROC_DIR, NULL);
235 if (proc_dir == NULL) {
236 printk(KERN_ERR "can't create proc dir.\n");
240 pret = create_proc_entry(PND_PROC_CPUMHZ,
241 S_IWUSR | S_IRUGO, proc_dir);
243 printk(KERN_ERR "can't create proc\n");
247 pret->read_proc = cpu_clk_read;
248 pret->write_proc = cpu_clk_write;
250 pret = create_proc_entry(PND_PROC_VSYNC,
253 printk(KERN_ERR "can't create proc\n");
257 pret->read_proc = pnd_vsync_read;
259 ret = pnd_hook_vsync();
261 printk(KERN_ERR "couldn't hook vsync\n");
265 printk(KERN_INFO "pndctrl loaded.\n");
270 remove_proc_entry(PND_PROC_VSYNC, proc_dir);
272 remove_proc_entry(PND_PROC_CPUMHZ, proc_dir);
274 remove_proc_entry(PND_PROC_DIR, NULL);
279 static void pndctrl_cleanup(void)
282 remove_proc_entry(PND_PROC_VSYNC, proc_dir);
283 remove_proc_entry(PND_PROC_CPUMHZ, proc_dir);
284 remove_proc_entry(PND_PROC_DIR, NULL);
285 printk("pndctrl unloaded.\n");
289 module_init(pndctrl_init);
290 module_exit(pndctrl_cleanup);
292 MODULE_AUTHOR("Grazvydas Ignotas");
293 MODULE_LICENSE("GPL");
294 MODULE_DESCRIPTION("Pandora Additional hw control");