1 /** 2 * Copyright (c) ???? Jochen Schäuble <psionic@psionic.de> 3 * Copyright (c) 2003-2004 Joern Engel <joern@wh.fh-wedel.de> 4 * 5 * Usage: 6 * 7 * one commend line parameter per device, each in the form: 8 * phram=<name>,<start>,<len> 9 * <name> may be up to 63 characters. 10 * <start> and <len> can be octal, decimal or hexadecimal. If followed 11 * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or 12 * gigabytes. 13 * 14 * Example: 15 * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi 16 */ 17 18 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 19 20 #include <asm/io.h> 21 #include <linux/init.h> 22 #include <linux/kernel.h> 23 #include <linux/list.h> 24 #include <linux/module.h> 25 #include <linux/moduleparam.h> 26 #include <linux/slab.h> 27 #include <linux/mtd/mtd.h> 28 29 struct phram_mtd_list { 30 struct mtd_info mtd; 31 struct list_head list; 32 }; 33 34 static LIST_HEAD(phram_list); 35 36 37 static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) 38 { 39 u_char *start = mtd->priv; 40 41 memset(start + instr->addr, 0xff, instr->len); 42 43 /* This'll catch a few races. Free the thing before returning :) 44 * I don't feel at all ashamed. This kind of thing is possible anyway 45 * with flash, but unlikely. 46 */ 47 instr->state = MTD_ERASE_DONE; 48 mtd_erase_callback(instr); 49 return 0; 50 } 51 52 static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, 53 size_t *retlen, void **virt, resource_size_t *phys) 54 { 55 /* can we return a physical address with this driver? */ 56 if (phys) 57 return -EINVAL; 58 59 *virt = mtd->priv + from; 60 *retlen = len; 61 return 0; 62 } 63 64 static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) 65 { 66 return 0; 67 } 68 69 static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, 70 size_t *retlen, u_char *buf) 71 { 72 u_char *start = mtd->priv; 73 74 memcpy(buf, start + from, len); 75 *retlen = len; 76 return 0; 77 } 78 79 static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, 80 size_t *retlen, const u_char *buf) 81 { 82 u_char *start = mtd->priv; 83 84 memcpy(start + to, buf, len); 85 *retlen = len; 86 return 0; 87 } 88 89 90 91 static void unregister_devices(void) 92 { 93 struct phram_mtd_list *this, *safe; 94 95 list_for_each_entry_safe(this, safe, &phram_list, list) { 96 mtd_device_unregister(&this->mtd); 97 iounmap(this->mtd.priv); 98 kfree(this->mtd.name); 99 kfree(this); 100 } 101 } 102 103 static int register_device(char *name, unsigned long start, unsigned long len) 104 { 105 struct phram_mtd_list *new; 106 int ret = -ENOMEM; 107 108 new = kzalloc(sizeof(*new), GFP_KERNEL); 109 if (!new) 110 goto out0; 111 112 ret = -EIO; 113 new->mtd.priv = ioremap(start, len); 114 if (!new->mtd.priv) { 115 pr_err("ioremap failed\n"); 116 goto out1; 117 } 118 119 120 new->mtd.name = name; 121 new->mtd.size = len; 122 new->mtd.flags = MTD_CAP_RAM; 123 new->mtd._erase = phram_erase; 124 new->mtd._point = phram_point; 125 new->mtd._unpoint = phram_unpoint; 126 new->mtd._read = phram_read; 127 new->mtd._write = phram_write; 128 new->mtd.owner = THIS_MODULE; 129 new->mtd.type = MTD_RAM; 130 new->mtd.erasesize = PAGE_SIZE; 131 new->mtd.writesize = 1; 132 133 ret = -EAGAIN; 134 if (mtd_device_register(&new->mtd, NULL, 0)) { 135 pr_err("Failed to register new device\n"); 136 goto out2; 137 } 138 139 list_add_tail(&new->list, &phram_list); 140 return 0; 141 142 out2: 143 iounmap(new->mtd.priv); 144 out1: 145 kfree(new); 146 out0: 147 return ret; 148 } 149 150 static int ustrtoul(const char *cp, char **endp, unsigned int base) 151 { 152 unsigned long result = simple_strtoul(cp, endp, base); 153 154 switch (**endp) { 155 case 'G': 156 result *= 1024; 157 case 'M': 158 result *= 1024; 159 case 'k': 160 result *= 1024; 161 /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ 162 if ((*endp)[1] == 'i') 163 (*endp) += 2; 164 } 165 return result; 166 } 167 168 static int parse_num32(uint32_t *num32, const char *token) 169 { 170 char *endp; 171 unsigned long n; 172 173 n = ustrtoul(token, &endp, 0); 174 if (*endp) 175 return -EINVAL; 176 177 *num32 = n; 178 return 0; 179 } 180 181 static int parse_name(char **pname, const char *token) 182 { 183 size_t len; 184 char *name; 185 186 len = strlen(token) + 1; 187 if (len > 64) 188 return -ENOSPC; 189 190 name = kmalloc(len, GFP_KERNEL); 191 if (!name) 192 return -ENOMEM; 193 194 strcpy(name, token); 195 196 *pname = name; 197 return 0; 198 } 199 200 201 static inline void kill_final_newline(char *str) 202 { 203 char *newline = strrchr(str, '\n'); 204 if (newline && !newline[1]) 205 *newline = 0; 206 } 207 208 209 #define parse_err(fmt, args...) do { \ 210 pr_err(fmt , ## args); \ 211 return 1; \ 212 } while (0) 213 214 static int phram_setup(const char *val, struct kernel_param *kp) 215 { 216 char buf[64+12+12], *str = buf; 217 char *token[3]; 218 char *name; 219 uint32_t start; 220 uint32_t len; 221 int i, ret; 222 223 if (strnlen(val, sizeof(buf)) >= sizeof(buf)) 224 parse_err("parameter too long\n"); 225 226 strcpy(str, val); 227 kill_final_newline(str); 228 229 for (i=0; i<3; i++) 230 token[i] = strsep(&str, ","); 231 232 if (str) 233 parse_err("too many arguments\n"); 234 235 if (!token[2]) 236 parse_err("not enough arguments\n"); 237 238 ret = parse_name(&name, token[0]); 239 if (ret) 240 return ret; 241 242 ret = parse_num32(&start, token[1]); 243 if (ret) { 244 kfree(name); 245 parse_err("illegal start address\n"); 246 } 247 248 ret = parse_num32(&len, token[2]); 249 if (ret) { 250 kfree(name); 251 parse_err("illegal device length\n"); 252 } 253 254 ret = register_device(name, start, len); 255 if (!ret) 256 pr_info("%s device: %#x at %#x\n", name, len, start); 257 else 258 kfree(name); 259 260 return ret; 261 } 262 263 module_param_call(phram, phram_setup, NULL, NULL, 000); 264 MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\""); 265 266 267 static int __init init_phram(void) 268 { 269 return 0; 270 } 271 272 static void __exit cleanup_phram(void) 273 { 274 unregister_devices(); 275 } 276 277 module_init(init_phram); 278 module_exit(cleanup_phram); 279 280 MODULE_LICENSE("GPL"); 281 MODULE_AUTHOR("Joern Engel <joern@wh.fh-wedel.de>"); 282 MODULE_DESCRIPTION("MTD driver for physical RAM"); 283