1/*====================================================================== 2 3 $Id: slram.c,v 1.1.1.1 2008/10/15 03:26:35 james26_jang Exp $ 4 5======================================================================*/ 6 7 8#include <linux/module.h> 9#include <asm/uaccess.h> 10#include <linux/types.h> 11#include <linux/kernel.h> 12#include <linux/sched.h> 13#include <linux/ptrace.h> 14#include <linux/slab.h> 15#include <linux/string.h> 16#include <linux/timer.h> 17#include <linux/major.h> 18#include <linux/fs.h> 19#include <linux/ioctl.h> 20#include <linux/init.h> 21#include <asm/io.h> 22#include <asm/system.h> 23#include <asm/segment.h> 24#include <stdarg.h> 25 26#include <linux/mtd/mtd.h> 27 28#define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ 29 30#define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) 31#define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) 32 33typedef struct slram_priv { 34 u_char *start; 35 u_char *end; 36} slram_priv_t; 37 38typedef struct slram_mtd_list { 39 struct mtd_info *mtdinfo; 40 struct slram_mtd_list *next; 41} slram_mtd_list_t; 42 43#ifdef MODULE 44static char *map[SLRAM_MAX_DEVICES_PARAMS]; 45#else 46static char *map; 47#endif 48 49MODULE_PARM(map, "3-" __MODULE_STRING(SLRAM_MAX_DEVICES_PARAMS) "s"); 50MODULE_PARM_DESC(map, "List of memory regions to map. \"map=<name>, <start>, <length / end>\""); 51 52static slram_mtd_list_t *slram_mtdlist = NULL; 53 54int slram_erase(struct mtd_info *, struct erase_info *); 55int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); 56void slram_unpoint(struct mtd_info *, u_char *); 57int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); 58int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 59 60int slram_erase(struct mtd_info *mtd, struct erase_info *instr) 61{ 62 slram_priv_t *priv = mtd->priv; 63 64 if (instr->addr + instr->len > mtd->size) { 65 return(-EINVAL); 66 } 67 68 memset(priv->start + instr->addr, 0xff, instr->len); 69 70 /* This'll catch a few races. Free the thing before returning :) 71 * I don't feel at all ashamed. This kind of thing is possible anyway 72 * with flash, but unlikely. 73 */ 74 75 instr->state = MTD_ERASE_DONE; 76 77 if (instr->callback) { 78 (*(instr->callback))(instr); 79 } 80 else { 81 kfree(instr); 82 } 83 84 return(0); 85} 86 87int slram_point(struct mtd_info *mtd, loff_t from, size_t len, 88 size_t *retlen, u_char **mtdbuf) 89{ 90 slram_priv_t *priv = (slram_priv_t *)mtd->priv; 91 92 *mtdbuf = priv->start + from; 93 *retlen = len; 94 return(0); 95} 96 97void slram_unpoint(struct mtd_info *mtd, u_char *addr) 98{ 99} 100 101int slram_read(struct mtd_info *mtd, loff_t from, size_t len, 102 size_t *retlen, u_char *buf) 103{ 104 slram_priv_t *priv = (slram_priv_t *)mtd->priv; 105 106 memcpy(buf, priv->start + from, len); 107 108 *retlen = len; 109 return(0); 110} 111 112int slram_write(struct mtd_info *mtd, loff_t to, size_t len, 113 size_t *retlen, const u_char *buf) 114{ 115 slram_priv_t *priv = (slram_priv_t *)mtd->priv; 116 117 memcpy(priv->start + to, buf, len); 118 119 *retlen = len; 120 return(0); 121} 122 123/*====================================================================*/ 124 125int register_device(char *name, unsigned long start, unsigned long length) 126{ 127 slram_mtd_list_t **curmtd; 128 129 curmtd = &slram_mtdlist; 130 while (*curmtd) { 131 curmtd = &(*curmtd)->next; 132 } 133 134 *curmtd = kmalloc(sizeof(slram_mtd_list_t), GFP_KERNEL); 135 if (!(*curmtd)) { 136 E("slram: Cannot allocate new MTD device.\n"); 137 return(-ENOMEM); 138 } 139 (*curmtd)->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); 140 (*curmtd)->next = NULL; 141 142 if ((*curmtd)->mtdinfo) { 143 memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); 144 (*curmtd)->mtdinfo->priv = 145 (void *)kmalloc(sizeof(slram_priv_t), GFP_KERNEL); 146 147 if (!(*curmtd)->mtdinfo->priv) { 148 kfree((*curmtd)->mtdinfo); 149 (*curmtd)->mtdinfo = NULL; 150 } else { 151 memset((*curmtd)->mtdinfo->priv,0,sizeof(slram_priv_t)); 152 } 153 } 154 155 if (!(*curmtd)->mtdinfo) { 156 E("slram: Cannot allocate new MTD device.\n"); 157 return(-ENOMEM); 158 } 159 160 if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = 161 ioremap(start, length))) { 162 E("slram: ioremap failed\n"); 163 return -EIO; 164 } 165 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = 166 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; 167 168 169 (*curmtd)->mtdinfo->name = name; 170 (*curmtd)->mtdinfo->size = length; 171 (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | 172 MTD_WRITEB_WRITEABLE | MTD_VOLATILE; 173 (*curmtd)->mtdinfo->erase = slram_erase; 174 (*curmtd)->mtdinfo->point = slram_point; 175 (*curmtd)->mtdinfo->unpoint = slram_unpoint; 176 (*curmtd)->mtdinfo->read = slram_read; 177 (*curmtd)->mtdinfo->write = slram_write; 178 (*curmtd)->mtdinfo->module = THIS_MODULE; 179 (*curmtd)->mtdinfo->type = MTD_RAM; 180 (*curmtd)->mtdinfo->erasesize = 0x10000; 181 182 if (add_mtd_device((*curmtd)->mtdinfo)) { 183 E("slram: Failed to register new device\n"); 184 iounmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); 185 kfree((*curmtd)->mtdinfo->priv); 186 kfree((*curmtd)->mtdinfo); 187 return(-EAGAIN); 188 } 189 T("slram: Registered device %s from %luKiB to %luKiB\n", name, 190 (start / 1024), ((start + length) / 1024)); 191 T("slram: Mapped from 0x%p to 0x%p\n", 192 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, 193 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); 194 return(0); 195} 196 197void unregister_devices(void) 198{ 199 slram_mtd_list_t *nextitem; 200 201 while (slram_mtdlist) { 202 nextitem = slram_mtdlist->next; 203 del_mtd_device(slram_mtdlist->mtdinfo); 204 iounmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start); 205 kfree(slram_mtdlist->mtdinfo->priv); 206 kfree(slram_mtdlist->mtdinfo); 207 kfree(slram_mtdlist); 208 slram_mtdlist = nextitem; 209 } 210} 211 212unsigned long handle_unit(unsigned long value, char *unit) 213{ 214 if ((*unit == 'M') || (*unit == 'm')) { 215 return(value * 1024 * 1024); 216 } else if ((*unit == 'K') || (*unit == 'k')) { 217 return(value * 1024); 218 } 219 return(value); 220} 221 222int parse_cmdline(char *devname, char *szstart, char *szlength) 223{ 224 char *buffer; 225 unsigned long devstart; 226 unsigned long devlength; 227 228 if ((!devname) || (!szstart) || (!szlength)) { 229 unregister_devices(); 230 return(-EINVAL); 231 } 232 233 devstart = simple_strtoul(szstart, &buffer, 0); 234 devstart = handle_unit(devstart, buffer); 235 236 if (*(szlength) != '+') { 237 devlength = simple_strtoul(szlength, &buffer, 0); 238 devlength = handle_unit(devlength, buffer) - devstart; 239 } else { 240 devlength = simple_strtoul(szlength + 1, &buffer, 0); 241 devlength = handle_unit(devlength, buffer); 242 } 243 T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", 244 devname, devstart, devlength); 245 if ((devstart < 0) || (devlength < 0)) { 246 E("slram: Illegal start / length parameter.\n"); 247 return(-EINVAL); 248 } 249 250 if ((devstart = register_device(devname, devstart, devlength))){ 251 unregister_devices(); 252 return((int)devstart); 253 } 254 return(0); 255} 256 257#ifndef MODULE 258 259static int __init mtd_slram_setup(char *str) 260{ 261 map = str; 262 return(1); 263} 264 265__setup("slram=", mtd_slram_setup); 266 267#endif 268 269int init_slram(void) 270{ 271 char *devname; 272 int i; 273 274#ifndef MODULE 275 char *devstart; 276 char *devlength; 277 278 i = 0; 279 280 if (!map) { 281 E("slram: not enough parameters.\n"); 282 return(-EINVAL); 283 } 284 while (map) { 285 devname = devstart = devlength = NULL; 286 287 if (!(devname = strsep(&map, ","))) { 288 E("slram: No devicename specified.\n"); 289 break; 290 } 291 T("slram: devname = %s\n", devname); 292 if ((!map) || (!(devstart = strsep(&map, ",")))) { 293 E("slram: No devicestart specified.\n"); 294 } 295 T("slram: devstart = %s\n", devstart); 296 if ((!map) || (!(devlength = strsep(&map, ",")))) { 297 E("slram: No devicelength / -end specified.\n"); 298 } 299 T("slram: devlength = %s\n", devlength); 300 if (parse_cmdline(devname, devstart, devlength) != 0) { 301 return(-EINVAL); 302 } 303 } 304#else 305 int count; 306 307 for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); 308 count++) { 309 } 310 311 if ((count % 3 != 0) || (count == 0)) { 312 E("slram: not enough parameters.\n"); 313 return(-EINVAL); 314 } 315 for (i = 0; i < (count / 3); i++) { 316 devname = map[i * 3]; 317 318 if (parse_cmdline(devname, map[i * 3 + 1], map[i * 3 + 2])!=0) { 319 return(-EINVAL); 320 } 321 322 } 323#endif /* !MODULE */ 324 325 return(0); 326} 327 328static void __exit cleanup_slram(void) 329{ 330 unregister_devices(); 331} 332 333module_init(init_slram); 334module_exit(cleanup_slram); 335 336MODULE_LICENSE("GPL"); 337MODULE_AUTHOR("Jochen Schaeuble <psionic@psionic.de>"); 338MODULE_DESCRIPTION("MTD driver for uncached system RAM"); 339