2 * wARM - exporting ARM processor specific privileged services to userspace
5 * Author: GraÅžvydas "notaz" Ignotas
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/proc_fs.h>
15 #include <linux/delay.h>
17 #include <linux/uaccess.h>
18 #include <linux/seq_file.h>
24 #ifndef CONFIG_PROC_FS
31 #define MAX_CACHEOP_RANGE 16384
33 /* assume RAM starts at phys addr 0 (this is really machine specific) */
34 #define RAM_PHYS_START 0
35 #define RAM_MAX_SIZE 0x10000000 /* 256M, try to be future proof */
37 extern unsigned long max_mapnr;
39 /* "upper" physical memory, not seen by Linux and to be mmap'ed */
40 static u32 uppermem_start;
41 static u32 uppermem_end;
43 static u32 *get_pgtable(void)
47 /* get the pointer to the translation table base... */
48 asm ("mrc p15, 0, %0, c2, c0, 0" : "=r"(ttb));
50 return __va((ttb) & 0xffffc000);
53 static int do_set_cb_uppermem(int in_cb, int is_set)
59 if (uppermem_end <= uppermem_start)
62 if (in_cb & WCB_C_BIT)
64 if (in_cb & WCB_B_BIT)
67 pgtable = get_pgtable();
69 for (i = 0; i < 4096; i++)
71 if (!(pgtable[i] & 1))
72 /* must be course of fine page table */
75 cpt = __va(pgtable[i] & 0xfffffc00);
77 for (j = 0; j < 256; j++)
86 addr = entry & 0xfffff000;
87 if (uppermem_start <= addr && addr < uppermem_end)
89 pr_debug(PFX "%s C&B bits %08x\n",
90 is_set ? "set" : "clear", entry);
97 /* need to also set AP bits (so that no faults
98 * happen and kernel doesn't touch this after us) */
100 entry |= 0x030; /* tiny page */
111 warm_drain_wb_inval_tlb();
113 pr_info(PFX "%c%c bit(s) %s for phys %08x-%08x (%d pages)\n",
114 bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
115 is_set ? "set" : "cleared",
116 uppermem_start, uppermem_end - 1, count);
121 static int do_set_cb_virt(int in_cb, int is_set, u32 addr, u32 size)
123 int count = 0, bits = 0;
128 if (in_cb & WCB_C_BIT)
130 if (in_cb & WCB_B_BIT)
133 size += addr & ~(PAGE_SIZE - 1);
134 size = ALIGN(size, PAGE_SIZE);
136 addr &= ~(PAGE_SIZE - 1);
140 pgtable = get_pgtable();
142 for (; addr < end; addr += PAGE_SIZE)
144 desc1 = pgtable[addr >> 20];
149 cpt = __va(desc1 & 0xfffffc00);
150 desc2 = cpt[(addr >> 12) & 0xff];
152 if ((desc2 & 3) != 2) {
153 printk(KERN_WARNING PFX "not small page? %08x %08x\n", desc2, addr);
163 cpt[(addr >> 12) & 0xff] = desc2;
168 warm_drain_wb_inval_tlb();
170 pr_info(PFX "%c%c bit(s) %s virt %08x-%08x (%d pages)\n",
171 bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
172 is_set ? "set" : "cleared", start, end - 1, count);
177 static int do_virt2phys(unsigned long *_addr)
183 pgtable = get_pgtable();
184 desc1 = pgtable[addr >> 20];
189 if ((desc1 & 3) == 2) {
191 *_addr = (desc1 & 0xfff00000) | (addr & 0xfffff);
195 cpt = __va(desc1 & 0xfffffc00);
196 desc2 = cpt[(addr >> 12) & 0xff];
198 if ((desc2 & 3) != 2) {
199 printk(KERN_WARNING PFX "not small page? %08x %08x\n", desc2, addr);
203 *_addr = (desc2 & 0xfffffc00) | (addr & 0x3ff);
207 static int do_cache_ops_whole(int ops)
209 if ((ops & (WOP_D_CLEAN|WOP_D_INVALIDATE)) == (WOP_D_CLEAN|WOP_D_INVALIDATE))
210 warm_cop_clean_inval_d();
212 else if (ops & WOP_D_CLEAN)
215 else if (ops & WOP_D_INVALIDATE) {
216 printk(KERN_WARNING PFX "invalidate without clean is dangerous!\n");
220 if (ops & WOP_I_INVALIDATE)
227 static int do_cache_ops(int ops, u32 addr, u32 size)
235 case WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE:
236 warm_cop_r_clean_d_inval_di(addr, size);
238 case WOP_D_CLEAN|WOP_D_INVALIDATE:
239 warm_cop_r_clean_d_inval_d(addr, size);
241 case WOP_D_CLEAN|WOP_I_INVALIDATE:
242 warm_cop_r_clean_d_inval_i(addr, size);
245 warm_cop_r_clean_d(addr, size);
247 case WOP_D_INVALIDATE|WOP_I_INVALIDATE:
248 warm_cop_r_inval_di(addr, size);
250 case WOP_D_INVALIDATE:
251 warm_cop_r_inval_d(addr, size);
253 case WOP_I_INVALIDATE:
254 warm_cop_r_inval_i(addr, size);
265 static long warm_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
267 void __user *arg = (void __user *) __arg;
269 struct warm_cache_op wcop;
270 struct warm_change_cb ccb;
277 if (copy_from_user(&u.wcop, arg, sizeof(u.wcop)))
279 if (u.wcop.ops & ~(WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE))
281 if (u.wcop.size > MAX_CACHEOP_RANGE)
282 ret = do_cache_ops_whole(u.wcop.ops);
284 ret = do_cache_ops(u.wcop.ops, u.wcop.addr, u.wcop.size);
286 case WARMC_CHANGE_CB:
287 if (copy_from_user(&u.ccb, arg, sizeof(u.ccb)))
289 if (u.ccb.cb & ~(WCB_C_BIT|WCB_B_BIT))
291 if (u.ccb.addr == 0 && u.ccb.size == 0)
292 ret = do_set_cb_uppermem(u.ccb.cb, u.ccb.is_set);
294 ret = do_set_cb_virt(u.ccb.cb, u.ccb.is_set, u.ccb.addr, u.ccb.size);
296 case WARMC_VIRT2PHYS:
297 if (copy_from_user(&u.addr, arg, sizeof(u.addr)))
299 ret = do_virt2phys(&u.addr);
300 if (copy_to_user(arg, &u.addr, sizeof(u.addr)))
311 static const char *warm_implementor_name(char code)
321 return "Motorola - Freescale";
328 static const char *warm_arch_name(int code)
349 static int warm_cache_size(int code, int m)
357 static int warm_cache_assoc(int code, int m)
364 return base << (code - 1);
367 static int warm_cache_line(int code)
372 static int warm_seq_show(struct seq_file *seq, void *offset)
376 seq_printf(seq, "wARM: " WARM_VER "\n");
379 asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(tmp));
380 seq_printf(seq, "ID: %08x\n", tmp);
382 /* revised format, not yet documented */
383 } else if ((tmp & 0xf000) == 0) {
385 seq_printf(seq, "Architecture: %d\n",
386 (tmp & 0xf00) == 0x600 ? 3 : 2);
387 seq_printf(seq, "Variant: %d%d0\n", (tmp & 0xf00) >> 8,
390 seq_printf(seq, "Implementor: %c (%s)\n", tmp >> 24,
391 warm_implementor_name(tmp >> 24));
392 if ((tmp & 0xf000) == 7) {
393 seq_printf(seq, "Architecture: %s\n",
394 tmp & 0x800000 ? "4T" : "3");
395 seq_printf(seq, "Variant: 0x%x\n", (tmp & 0x7f0000) >> 16);
397 seq_printf(seq, "Architecture: %s\n",
398 warm_arch_name((tmp & 0x0f0000) >> 16));
399 seq_printf(seq, "Variant: 0x%x\n", (tmp & 0xf00000) >> 20);
401 seq_printf(seq, "Part number: 0x%x\n", (tmp & 0xfff0) >> 4);
403 seq_printf(seq, "Revision: 0x%x\n", tmp & 0xf);
406 asm ("mrc p15, 0, %0, c0, c0, 1" : "=r"(tmp));
407 seq_printf(seq, "Cache ctype: 0x%x\n", (tmp & 0x1e000000) >> 25);
408 seq_printf(seq, "Cache unified: %s\n", (tmp & 0x01000000) ? "no" : "yes");
409 seq_printf(seq, "DCache size: %d\n",
410 warm_cache_size((tmp >> (6+12)) & 0xf, (tmp >> (2+12)) & 1));
411 seq_printf(seq, "DCache associativity: %d\n",
412 warm_cache_assoc((tmp >> (3+12)) & 7, (tmp >> (2+12)) & 1));
413 seq_printf(seq, "DCache line size: %d\n",
414 warm_cache_line((tmp >> (0+12)) & 3));
415 seq_printf(seq, "ICache size: %d\n",
416 warm_cache_size((tmp >> 6) & 0xf, (tmp >> 2) & 1));
417 seq_printf(seq, "ICache associativity: %d\n",
418 warm_cache_assoc((tmp >> 3) & 7, (tmp >> 2) & 1));
419 seq_printf(seq, "ICache line size: %d\n",
420 warm_cache_line((tmp >> 0) & 3));
425 static int warm_open(struct inode *inode, struct file *file)
427 return single_open(file, warm_seq_show, NULL);
430 static int warm_close(struct inode *ino, struct file *file)
432 return single_release(ino, file);
435 static const struct file_operations warm_fops = {
436 .owner = THIS_MODULE,
440 .unlocked_ioctl = warm_ioctl,
441 .release = warm_close,
444 static int __init warm_module_init(void)
446 struct proc_dir_entry *pret;
448 pret = create_proc_entry("warm", S_IWUGO | S_IRUGO, NULL);
450 printk(KERN_ERR PFX "can't create proc entry\n");
454 pret->owner = THIS_MODULE;
455 pret->proc_fops = &warm_fops;
457 uppermem_start = RAM_PHYS_START + (max_mapnr << PAGE_SHIFT);
458 uppermem_end = RAM_PHYS_START + RAM_MAX_SIZE;
460 pr_info(PFX WARM_VER " loaded, ");
461 if (uppermem_end <= uppermem_start)
462 printk("no upper mem");
464 printk("upper mem %08x-%08x", uppermem_start, uppermem_end - 1);
467 /* give time for /proc node to appear */
473 static void __exit warm_module_exit(void)
475 remove_proc_entry("warm", NULL);
477 pr_info(PFX "unloaded.\n");
480 module_init(warm_module_init);
481 module_exit(warm_module_exit);
483 MODULE_LICENSE("GPL");
484 MODULE_DESCRIPTION("ARM processor services");
485 MODULE_AUTHOR("Grazvydas Ignotas");