This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / mtd / devices / phram.c
1 /**
2  *
3  * $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $
4  *
5  * Copyright (c) Jochen Schaeuble <psionic@psionic.de>
6  * 07/2003      rewritten by Joern Engel <joern@wh.fh-wedel.de>
7  *
8  * DISCLAIMER:  This driver makes use of Rusty's excellent module code,
9  * so it will not work for 2.4 without changes and it wont work for 2.4
10  * as a module without major changes.  Oh well!
11  *
12  * Usage:
13  *
14  * one commend line parameter per device, each in the form:
15  *   phram=<name>,<start>,<len>
16  * <name> may be up to 63 characters.
17  * <start> and <len> can be octal, decimal or hexadecimal.  If followed
18  * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or
19  * gigabytes.
20  *
21  */
22
23 #include <asm/io.h>
24 #include <linux/init.h>
25 #include <linux/kernel.h>
26 #include <linux/list.h>
27 #include <linux/module.h>
28 #include <linux/moduleparam.h>
29 #include <linux/mtd/mtd.h>
30
31 #define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
32
33 struct phram_mtd_list {
34         struct list_head list;
35         struct mtd_info *mtdinfo;
36 };
37
38 static LIST_HEAD(phram_list);
39
40
41
42 int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
43 {
44         u_char *start = (u_char *)mtd->priv;
45
46         if (instr->addr + instr->len > mtd->size)
47                 return -EINVAL;
48         
49         memset(start + instr->addr, 0xff, instr->len);
50
51         /* This'll catch a few races. Free the thing before returning :) 
52          * I don't feel at all ashamed. This kind of thing is possible anyway
53          * with flash, but unlikely.
54          */
55
56         instr->state = MTD_ERASE_DONE;
57
58         if (instr->callback)
59                 (*(instr->callback))(instr);
60         else
61                 kfree(instr);
62
63         return 0;
64 }
65
66 int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
67                 size_t *retlen, u_char **mtdbuf)
68 {
69         u_char *start = (u_char *)mtd->priv;
70
71         if (from + len > mtd->size)
72                 return -EINVAL;
73         
74         *mtdbuf = start + from;
75         *retlen = len;
76         return 0;
77 }
78
79 void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
80 {
81 }
82
83 int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
84                 size_t *retlen, u_char *buf)
85 {
86         u_char *start = (u_char *)mtd->priv;
87
88         if (from + len > mtd->size)
89                 return -EINVAL;
90         
91         memcpy(buf, start + from, len);
92
93         *retlen = len;
94         return 0;
95 }
96
97 int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
98                 size_t *retlen, const u_char *buf)
99 {
100         u_char *start = (u_char *)mtd->priv;
101
102         if (to + len > mtd->size)
103                 return -EINVAL;
104         
105         memcpy(start + to, buf, len);
106
107         *retlen = len;
108         return 0;
109 }
110
111
112
113 static void unregister_devices(void)
114 {
115         struct phram_mtd_list *this;
116
117         list_for_each_entry(this, &phram_list, list) {
118                 del_mtd_device(this->mtdinfo);
119                 iounmap(this->mtdinfo->priv);
120                 kfree(this->mtdinfo);
121                 kfree(this);
122         }
123 }
124
125 static int register_device(char *name, unsigned long start, unsigned long len)
126 {
127         struct phram_mtd_list *new;
128         int ret = -ENOMEM;
129
130         new = kmalloc(sizeof(*new), GFP_KERNEL);
131         if (!new)
132                 goto out0;
133
134         new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
135         if (!new->mtdinfo)
136                 goto out1;
137         
138         memset(new->mtdinfo, 0, sizeof(struct mtd_info));
139
140         ret = -EIO;
141         new->mtdinfo->priv = ioremap(start, len);
142         if (!new->mtdinfo->priv) {
143                 ERROR("ioremap failed\n");
144                 goto out2;
145         }
146
147
148         new->mtdinfo->name = name;
149         new->mtdinfo->size = len;
150         new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
151         new->mtdinfo->erase = phram_erase;
152         new->mtdinfo->point = phram_point;
153         new->mtdinfo->unpoint = phram_unpoint;
154         new->mtdinfo->read = phram_read;
155         new->mtdinfo->write = phram_write;
156         new->mtdinfo->owner = THIS_MODULE;
157         new->mtdinfo->type = MTD_RAM;
158         new->mtdinfo->erasesize = 0x0;
159
160         ret = -EAGAIN;
161         if (add_mtd_device(new->mtdinfo)) {
162                 ERROR("Failed to register new device\n");
163                 goto out3;
164         }
165
166         list_add_tail(&new->list, &phram_list);
167         return 0;       
168
169 out3:
170         iounmap(new->mtdinfo->priv);
171 out2:
172         kfree(new->mtdinfo);
173 out1:
174         kfree(new);
175 out0:
176         return ret;
177 }
178
179 static int ustrtoul(const char *cp, char **endp, unsigned int base)
180 {
181         unsigned long result = simple_strtoul(cp, endp, base);
182
183         switch (**endp) {
184         case 'G':
185                 result *= 1024;
186         case 'M':
187                 result *= 1024;
188         case 'k':
189                 result *= 1024;
190                 endp++;
191         }
192         return result;
193 }
194
195 static int parse_num32(uint32_t *num32, const char *token)
196 {
197         char *endp;
198         unsigned long n;
199
200         n = ustrtoul(token, &endp, 0);
201         if (*endp)
202                 return -EINVAL;
203
204         *num32 = n;
205         return 0;
206 }
207
208 static int parse_name(char **pname, const char *token)
209 {
210         size_t len;
211         char *name;
212
213         len = strlen(token) + 1;
214         if (len > 64)
215                 return -ENOSPC;
216
217         name = kmalloc(len, GFP_KERNEL);
218         if (!name)
219                 return -ENOMEM;
220
221         strcpy(name, token);
222
223         *pname = name;
224         return 0;
225 }
226
227 #define parse_err(fmt, args...) do {    \
228         ERROR(fmt , ## args);   \
229         return 0;               \
230 } while (0)
231
232 static int phram_setup(const char *val, struct kernel_param *kp)
233 {
234         char buf[64+12+12], *str = buf;
235         char *token[3];
236         char *name;
237         uint32_t start;
238         uint32_t len;
239         int i, ret;
240
241         if (strnlen(val, sizeof(str)) >= sizeof(str))
242                 parse_err("parameter too long\n");
243
244         strcpy(str, val);
245
246         for (i=0; i<3; i++)
247                 token[i] = strsep(&str, ",");
248
249         if (str)
250                 parse_err("too many arguments\n");
251
252         if (!token[2])
253                 parse_err("not enough arguments\n");
254
255         ret = parse_name(&name, token[0]);
256         if (ret == -ENOMEM)
257                 parse_err("out of memory\n");
258         if (ret == -ENOSPC)
259                 parse_err("name too long\n");
260         if (ret)
261                 return 0;
262
263         ret = parse_num32(&start, token[1]);
264         if (ret)
265                 parse_err("illegal start address\n");
266
267         ret = parse_num32(&len, token[2]);
268         if (ret)
269                 parse_err("illegal device length\n");
270
271         register_device(name, start, len);
272
273         return 0;
274 }
275
276 module_param_call(phram, phram_setup, NULL, NULL, 000);
277 MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\"");
278
279 /*
280  * Just for compatibility with slram, this is horrible and should go someday.
281  */
282 static int __init slram_setup(const char *val, struct kernel_param *kp)
283 {
284         char buf[256], *str = buf;
285
286         if (!val || !val[0])
287                 parse_err("no arguments to \"slram=\"\n");
288
289         if (strnlen(val, sizeof(str)) >= sizeof(str))
290                 parse_err("parameter too long\n");
291
292         strcpy(str, val);
293
294         while (str) {
295                 char *token[3];
296                 char *name;
297                 uint32_t start;
298                 uint32_t len;
299                 int i, ret;
300
301                 for (i=0; i<3; i++) {
302                         token[i] = strsep(&str, ",");
303                         if (token[i])
304                                 continue;
305                         parse_err("wrong number of arguments to \"slram=\"\n");
306                 }
307
308                 /* name */
309                 ret = parse_name(&name, token[0]);
310                 if (ret == -ENOMEM)
311                         parse_err("of memory\n");
312                 if (ret == -ENOSPC)
313                         parse_err("too long\n");
314                 if (ret)
315                         return 1;
316
317                 /* start */
318                 ret = parse_num32(&start, token[1]);
319                 if (ret)
320                         parse_err("illegal start address\n");
321
322                 /* len */
323                 if (token[2][0] == '+')
324                         ret = parse_num32(&len, token[2] + 1);
325                 else
326                         ret = parse_num32(&len, token[2]);
327
328                 if (ret)
329                         parse_err("illegal device length\n");
330
331                 if (token[2][0] != '+') {
332                         if (len < start)
333                                 parse_err("end < start\n");
334                         len -= start;
335                 }
336
337                 register_device(name, start, len);
338         }
339         return 1;
340 }
341
342 module_param_call(slram, slram_setup, NULL, NULL, 000);
343 MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\"");
344
345
346 int __init init_phram(void)
347 {
348         printk(KERN_ERR "phram loaded\n");
349         return 0;
350 }
351
352 static void __exit cleanup_phram(void)
353 {
354         unregister_devices();
355 }
356
357 module_init(init_phram);
358 module_exit(cleanup_phram);
359
360 MODULE_LICENSE("GPL");
361 MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
362 MODULE_DESCRIPTION("MTD driver for physical RAM");