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