diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/mtd/devices/slram.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/mtd/devices/slram.c')
-rw-r--r-- | drivers/mtd/devices/slram.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c new file mode 100644 index 000000000000..5ab15e643be7 --- /dev/null +++ b/drivers/mtd/devices/slram.c | |||
@@ -0,0 +1,357 @@ | |||
1 | /*====================================================================== | ||
2 | |||
3 | $Id: slram.c,v 1.33 2005/01/05 18:05:13 dwmw2 Exp $ | ||
4 | |||
5 | This driver provides a method to access memory not used by the kernel | ||
6 | itself (i.e. if the kernel commandline mem=xxx is used). To actually | ||
7 | use slram at least mtdblock or mtdchar is required (for block or | ||
8 | character device access). | ||
9 | |||
10 | Usage: | ||
11 | |||
12 | if compiled as loadable module: | ||
13 | modprobe slram map=<name>,<start>,<end/offset> | ||
14 | if statically linked into the kernel use the following kernel cmd.line | ||
15 | slram=<name>,<start>,<end/offset> | ||
16 | |||
17 | <name>: name of the device that will be listed in /proc/mtd | ||
18 | <start>: start of the memory region, decimal or hex (0xabcdef) | ||
19 | <end/offset>: end of the memory region. It's possible to use +0x1234 | ||
20 | to specify the offset instead of the absolute address | ||
21 | |||
22 | NOTE: | ||
23 | With slram it's only possible to map a contigous memory region. Therfore | ||
24 | if there's a device mapped somewhere in the region specified slram will | ||
25 | fail to load (see kernel log if modprobe fails). | ||
26 | |||
27 | - | ||
28 | |||
29 | Jochen Schaeuble <psionic@psionic.de> | ||
30 | |||
31 | ======================================================================*/ | ||
32 | |||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | #include <linux/types.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/sched.h> | ||
39 | #include <linux/ptrace.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/string.h> | ||
42 | #include <linux/timer.h> | ||
43 | #include <linux/major.h> | ||
44 | #include <linux/fs.h> | ||
45 | #include <linux/ioctl.h> | ||
46 | #include <linux/init.h> | ||
47 | #include <asm/io.h> | ||
48 | #include <asm/system.h> | ||
49 | |||
50 | #include <linux/mtd/mtd.h> | ||
51 | |||
52 | #define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ | ||
53 | |||
54 | #define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) | ||
55 | #define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) | ||
56 | |||
57 | typedef struct slram_priv { | ||
58 | u_char *start; | ||
59 | u_char *end; | ||
60 | } slram_priv_t; | ||
61 | |||
62 | typedef struct slram_mtd_list { | ||
63 | struct mtd_info *mtdinfo; | ||
64 | struct slram_mtd_list *next; | ||
65 | } slram_mtd_list_t; | ||
66 | |||
67 | #ifdef MODULE | ||
68 | static char *map[SLRAM_MAX_DEVICES_PARAMS]; | ||
69 | |||
70 | module_param_array(map, charp, NULL, 0); | ||
71 | MODULE_PARM_DESC(map, "List of memory regions to map. \"map=<name>, <start>, <length / end>\""); | ||
72 | #else | ||
73 | static char *map; | ||
74 | #endif | ||
75 | |||
76 | static slram_mtd_list_t *slram_mtdlist = NULL; | ||
77 | |||
78 | static int slram_erase(struct mtd_info *, struct erase_info *); | ||
79 | static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); | ||
80 | static void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); | ||
81 | static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); | ||
82 | static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); | ||
83 | |||
84 | static int slram_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
85 | { | ||
86 | slram_priv_t *priv = mtd->priv; | ||
87 | |||
88 | if (instr->addr + instr->len > mtd->size) { | ||
89 | return(-EINVAL); | ||
90 | } | ||
91 | |||
92 | memset(priv->start + instr->addr, 0xff, instr->len); | ||
93 | |||
94 | /* This'll catch a few races. Free the thing before returning :) | ||
95 | * I don't feel at all ashamed. This kind of thing is possible anyway | ||
96 | * with flash, but unlikely. | ||
97 | */ | ||
98 | |||
99 | instr->state = MTD_ERASE_DONE; | ||
100 | |||
101 | mtd_erase_callback(instr); | ||
102 | |||
103 | return(0); | ||
104 | } | ||
105 | |||
106 | static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, | ||
107 | size_t *retlen, u_char **mtdbuf) | ||
108 | { | ||
109 | slram_priv_t *priv = mtd->priv; | ||
110 | |||
111 | *mtdbuf = priv->start + from; | ||
112 | *retlen = len; | ||
113 | return(0); | ||
114 | } | ||
115 | |||
116 | static void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) | ||
117 | { | ||
118 | } | ||
119 | |||
120 | static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
121 | size_t *retlen, u_char *buf) | ||
122 | { | ||
123 | slram_priv_t *priv = mtd->priv; | ||
124 | |||
125 | memcpy(buf, priv->start + from, len); | ||
126 | |||
127 | *retlen = len; | ||
128 | return(0); | ||
129 | } | ||
130 | |||
131 | static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
132 | size_t *retlen, const u_char *buf) | ||
133 | { | ||
134 | slram_priv_t *priv = mtd->priv; | ||
135 | |||
136 | memcpy(priv->start + to, buf, len); | ||
137 | |||
138 | *retlen = len; | ||
139 | return(0); | ||
140 | } | ||
141 | |||
142 | /*====================================================================*/ | ||
143 | |||
144 | static int register_device(char *name, unsigned long start, unsigned long length) | ||
145 | { | ||
146 | slram_mtd_list_t **curmtd; | ||
147 | |||
148 | curmtd = &slram_mtdlist; | ||
149 | while (*curmtd) { | ||
150 | curmtd = &(*curmtd)->next; | ||
151 | } | ||
152 | |||
153 | *curmtd = kmalloc(sizeof(slram_mtd_list_t), GFP_KERNEL); | ||
154 | if (!(*curmtd)) { | ||
155 | E("slram: Cannot allocate new MTD device.\n"); | ||
156 | return(-ENOMEM); | ||
157 | } | ||
158 | (*curmtd)->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); | ||
159 | (*curmtd)->next = NULL; | ||
160 | |||
161 | if ((*curmtd)->mtdinfo) { | ||
162 | memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); | ||
163 | (*curmtd)->mtdinfo->priv = | ||
164 | kmalloc(sizeof(slram_priv_t), GFP_KERNEL); | ||
165 | |||
166 | if (!(*curmtd)->mtdinfo->priv) { | ||
167 | kfree((*curmtd)->mtdinfo); | ||
168 | (*curmtd)->mtdinfo = NULL; | ||
169 | } else { | ||
170 | memset((*curmtd)->mtdinfo->priv,0,sizeof(slram_priv_t)); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | if (!(*curmtd)->mtdinfo) { | ||
175 | E("slram: Cannot allocate new MTD device.\n"); | ||
176 | return(-ENOMEM); | ||
177 | } | ||
178 | |||
179 | if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = | ||
180 | ioremap(start, length))) { | ||
181 | E("slram: ioremap failed\n"); | ||
182 | return -EIO; | ||
183 | } | ||
184 | ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = | ||
185 | ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; | ||
186 | |||
187 | |||
188 | (*curmtd)->mtdinfo->name = name; | ||
189 | (*curmtd)->mtdinfo->size = length; | ||
190 | (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | | ||
191 | MTD_WRITEB_WRITEABLE | MTD_VOLATILE; | ||
192 | (*curmtd)->mtdinfo->erase = slram_erase; | ||
193 | (*curmtd)->mtdinfo->point = slram_point; | ||
194 | (*curmtd)->mtdinfo->unpoint = slram_unpoint; | ||
195 | (*curmtd)->mtdinfo->read = slram_read; | ||
196 | (*curmtd)->mtdinfo->write = slram_write; | ||
197 | (*curmtd)->mtdinfo->owner = THIS_MODULE; | ||
198 | (*curmtd)->mtdinfo->type = MTD_RAM; | ||
199 | (*curmtd)->mtdinfo->erasesize = 0x0; | ||
200 | |||
201 | if (add_mtd_device((*curmtd)->mtdinfo)) { | ||
202 | E("slram: Failed to register new device\n"); | ||
203 | iounmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); | ||
204 | kfree((*curmtd)->mtdinfo->priv); | ||
205 | kfree((*curmtd)->mtdinfo); | ||
206 | return(-EAGAIN); | ||
207 | } | ||
208 | T("slram: Registered device %s from %luKiB to %luKiB\n", name, | ||
209 | (start / 1024), ((start + length) / 1024)); | ||
210 | T("slram: Mapped from 0x%p to 0x%p\n", | ||
211 | ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, | ||
212 | ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); | ||
213 | return(0); | ||
214 | } | ||
215 | |||
216 | static void unregister_devices(void) | ||
217 | { | ||
218 | slram_mtd_list_t *nextitem; | ||
219 | |||
220 | while (slram_mtdlist) { | ||
221 | nextitem = slram_mtdlist->next; | ||
222 | del_mtd_device(slram_mtdlist->mtdinfo); | ||
223 | iounmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start); | ||
224 | kfree(slram_mtdlist->mtdinfo->priv); | ||
225 | kfree(slram_mtdlist->mtdinfo); | ||
226 | kfree(slram_mtdlist); | ||
227 | slram_mtdlist = nextitem; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | static unsigned long handle_unit(unsigned long value, char *unit) | ||
232 | { | ||
233 | if ((*unit == 'M') || (*unit == 'm')) { | ||
234 | return(value * 1024 * 1024); | ||
235 | } else if ((*unit == 'K') || (*unit == 'k')) { | ||
236 | return(value * 1024); | ||
237 | } | ||
238 | return(value); | ||
239 | } | ||
240 | |||
241 | static int parse_cmdline(char *devname, char *szstart, char *szlength) | ||
242 | { | ||
243 | char *buffer; | ||
244 | unsigned long devstart; | ||
245 | unsigned long devlength; | ||
246 | |||
247 | if ((!devname) || (!szstart) || (!szlength)) { | ||
248 | unregister_devices(); | ||
249 | return(-EINVAL); | ||
250 | } | ||
251 | |||
252 | devstart = simple_strtoul(szstart, &buffer, 0); | ||
253 | devstart = handle_unit(devstart, buffer); | ||
254 | |||
255 | if (*(szlength) != '+') { | ||
256 | devlength = simple_strtoul(szlength, &buffer, 0); | ||
257 | devlength = handle_unit(devlength, buffer) - devstart; | ||
258 | } else { | ||
259 | devlength = simple_strtoul(szlength + 1, &buffer, 0); | ||
260 | devlength = handle_unit(devlength, buffer); | ||
261 | } | ||
262 | T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", | ||
263 | devname, devstart, devlength); | ||
264 | if ((devstart < 0) || (devlength < 0)) { | ||
265 | E("slram: Illegal start / length parameter.\n"); | ||
266 | return(-EINVAL); | ||
267 | } | ||
268 | |||
269 | if ((devstart = register_device(devname, devstart, devlength))){ | ||
270 | unregister_devices(); | ||
271 | return((int)devstart); | ||
272 | } | ||
273 | return(0); | ||
274 | } | ||
275 | |||
276 | #ifndef MODULE | ||
277 | |||
278 | static int __init mtd_slram_setup(char *str) | ||
279 | { | ||
280 | map = str; | ||
281 | return(1); | ||
282 | } | ||
283 | |||
284 | __setup("slram=", mtd_slram_setup); | ||
285 | |||
286 | #endif | ||
287 | |||
288 | static int init_slram(void) | ||
289 | { | ||
290 | char *devname; | ||
291 | int i; | ||
292 | |||
293 | #ifndef MODULE | ||
294 | char *devstart; | ||
295 | char *devlength; | ||
296 | |||
297 | i = 0; | ||
298 | |||
299 | if (!map) { | ||
300 | E("slram: not enough parameters.\n"); | ||
301 | return(-EINVAL); | ||
302 | } | ||
303 | while (map) { | ||
304 | devname = devstart = devlength = NULL; | ||
305 | |||
306 | if (!(devname = strsep(&map, ","))) { | ||
307 | E("slram: No devicename specified.\n"); | ||
308 | break; | ||
309 | } | ||
310 | T("slram: devname = %s\n", devname); | ||
311 | if ((!map) || (!(devstart = strsep(&map, ",")))) { | ||
312 | E("slram: No devicestart specified.\n"); | ||
313 | } | ||
314 | T("slram: devstart = %s\n", devstart); | ||
315 | if ((!map) || (!(devlength = strsep(&map, ",")))) { | ||
316 | E("slram: No devicelength / -end specified.\n"); | ||
317 | } | ||
318 | T("slram: devlength = %s\n", devlength); | ||
319 | if (parse_cmdline(devname, devstart, devlength) != 0) { | ||
320 | return(-EINVAL); | ||
321 | } | ||
322 | } | ||
323 | #else | ||
324 | int count; | ||
325 | |||
326 | for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); | ||
327 | count++) { | ||
328 | } | ||
329 | |||
330 | if ((count % 3 != 0) || (count == 0)) { | ||
331 | E("slram: not enough parameters.\n"); | ||
332 | return(-EINVAL); | ||
333 | } | ||
334 | for (i = 0; i < (count / 3); i++) { | ||
335 | devname = map[i * 3]; | ||
336 | |||
337 | if (parse_cmdline(devname, map[i * 3 + 1], map[i * 3 + 2])!=0) { | ||
338 | return(-EINVAL); | ||
339 | } | ||
340 | |||
341 | } | ||
342 | #endif /* !MODULE */ | ||
343 | |||
344 | return(0); | ||
345 | } | ||
346 | |||
347 | static void __exit cleanup_slram(void) | ||
348 | { | ||
349 | unregister_devices(); | ||
350 | } | ||
351 | |||
352 | module_init(init_slram); | ||
353 | module_exit(cleanup_slram); | ||
354 | |||
355 | MODULE_LICENSE("GPL"); | ||
356 | MODULE_AUTHOR("Jochen Schaeuble <psionic@psionic.de>"); | ||
357 | MODULE_DESCRIPTION("MTD driver for uncached system RAM"); | ||