diff options
author | Dmitry Torokhov <dtor_core@ameritech.net> | 2006-04-29 01:11:23 -0400 |
---|---|---|
committer | Dmitry Torokhov <dtor_core@ameritech.net> | 2006-04-29 01:11:23 -0400 |
commit | 7b7e394185014e0f3bd8989cac937003f20ef9ce (patch) | |
tree | 3beda5f979bba0aa9822534e239cf1b45f3be69c /drivers/mtd | |
parent | ddc5d3414593e4d7ad7fbd33e7f7517fcc234544 (diff) | |
parent | 693f7d362055261882659475d2ef022e32edbff1 (diff) |
Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/chips/Kconfig | 21 | ||||
-rw-r--r-- | drivers/mtd/devices/Kconfig | 13 | ||||
-rw-r--r-- | drivers/mtd/devices/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/devices/blkmtd.c | 819 | ||||
-rw-r--r-- | drivers/mtd/maps/pcmciamtd.c | 115 |
5 files changed, 50 insertions, 919 deletions
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 0f6bb2e625d8..a7ec5954caf5 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig | |||
@@ -200,27 +200,6 @@ config MTD_CFI_AMDSTD | |||
200 | provides support for one of those command sets, used on chips | 200 | provides support for one of those command sets, used on chips |
201 | including the AMD Am29LV320. | 201 | including the AMD Am29LV320. |
202 | 202 | ||
203 | config MTD_CFI_AMDSTD_RETRY | ||
204 | int "Retry failed commands (erase/program)" | ||
205 | depends on MTD_CFI_AMDSTD | ||
206 | default "0" | ||
207 | help | ||
208 | Some chips, when attached to a shared bus, don't properly filter | ||
209 | bus traffic that is destined to other devices. This broken | ||
210 | behavior causes erase and program sequences to be aborted when | ||
211 | the sequences are mixed with traffic for other devices. | ||
212 | |||
213 | SST49LF040 (and related) chips are know to be broken. | ||
214 | |||
215 | config MTD_CFI_AMDSTD_RETRY_MAX | ||
216 | int "Max retries of failed commands (erase/program)" | ||
217 | depends on MTD_CFI_AMDSTD_RETRY | ||
218 | default "0" | ||
219 | help | ||
220 | If you have an SST49LF040 (or related chip) then this value should | ||
221 | be set to at least 1. This can also be adjusted at driver load | ||
222 | time with the retry_cmd_max module parameter. | ||
223 | |||
224 | config MTD_CFI_STAA | 203 | config MTD_CFI_STAA |
225 | tristate "Support for ST (Advanced Architecture) flash chips" | 204 | tristate "Support for ST (Advanced Architecture) flash chips" |
226 | depends on MTD_GEN_PROBE | 205 | depends on MTD_GEN_PROBE |
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index dd628cb51e31..7fac438b5c32 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -129,8 +129,8 @@ config MTDRAM_ABS_POS | |||
129 | allocating space from Linux's available memory. Otherwise, leave | 129 | allocating space from Linux's available memory. Otherwise, leave |
130 | this set to zero. Most people will want to leave this as zero. | 130 | this set to zero. Most people will want to leave this as zero. |
131 | 131 | ||
132 | config MTD_BLKMTD | 132 | config MTD_BLOCK2MTD |
133 | tristate "MTD emulation using block device" | 133 | tristate "MTD using block device" |
134 | depends on MTD | 134 | depends on MTD |
135 | help | 135 | help |
136 | This driver allows a block device to appear as an MTD. It would | 136 | This driver allows a block device to appear as an MTD. It would |
@@ -141,15 +141,6 @@ config MTD_BLKMTD | |||
141 | Testing MTD users (eg JFFS2) on large media and media that might | 141 | Testing MTD users (eg JFFS2) on large media and media that might |
142 | be removed during a write (using the floppy drive). | 142 | be removed during a write (using the floppy drive). |
143 | 143 | ||
144 | config MTD_BLOCK2MTD | ||
145 | tristate "MTD using block device (rewrite)" | ||
146 | depends on MTD && EXPERIMENTAL | ||
147 | help | ||
148 | This driver is basically the same at MTD_BLKMTD above, but | ||
149 | experienced some interface changes plus serious speedups. In | ||
150 | the long term, it should replace MTD_BLKMTD. Right now, you | ||
151 | shouldn't entrust important data to it yet. | ||
152 | |||
153 | comment "Disk-On-Chip Device Drivers" | 144 | comment "Disk-On-Chip Device Drivers" |
154 | 145 | ||
155 | config MTD_DOC2000 | 146 | config MTD_DOC2000 |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 7c5ed2178380..b6573670316f 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
@@ -21,7 +21,6 @@ obj-$(CONFIG_MTD_PMC551) += pmc551.o | |||
21 | obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o | 21 | obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o |
22 | obj-$(CONFIG_MTD_MTDRAM) += mtdram.o | 22 | obj-$(CONFIG_MTD_MTDRAM) += mtdram.o |
23 | obj-$(CONFIG_MTD_LART) += lart.o | 23 | obj-$(CONFIG_MTD_LART) += lart.o |
24 | obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o | ||
25 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 24 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
26 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 25 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
27 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 26 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
diff --git a/drivers/mtd/devices/blkmtd.c b/drivers/mtd/devices/blkmtd.c deleted file mode 100644 index 79f2e1f23ebd..000000000000 --- a/drivers/mtd/devices/blkmtd.c +++ /dev/null | |||
@@ -1,819 +0,0 @@ | |||
1 | /* | ||
2 | * $Id: blkmtd.c,v 1.27 2005/11/07 11:14:24 gleixner Exp $ | ||
3 | * | ||
4 | * blkmtd.c - use a block device as a fake MTD | ||
5 | * | ||
6 | * Author: Simon Evans <spse@secret.org.uk> | ||
7 | * | ||
8 | * Copyright (C) 2001,2002 Simon Evans | ||
9 | * | ||
10 | * Licence: GPL | ||
11 | * | ||
12 | * How it works: | ||
13 | * The driver uses raw/io to read/write the device and the page | ||
14 | * cache to cache access. Writes update the page cache with the | ||
15 | * new data and mark it dirty and add the page into a BIO which | ||
16 | * is then written out. | ||
17 | * | ||
18 | * It can be loaded Read-Only to prevent erases and writes to the | ||
19 | * medium. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/blkdev.h> | ||
27 | #include <linux/bio.h> | ||
28 | #include <linux/pagemap.h> | ||
29 | #include <linux/list.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/mount.h> | ||
32 | #include <linux/mtd/mtd.h> | ||
33 | #include <linux/mutex.h> | ||
34 | |||
35 | #define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) | ||
36 | #define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) | ||
37 | #define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg) | ||
38 | #define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) | ||
39 | |||
40 | |||
41 | /* Default erase size in K, always make it a multiple of PAGE_SIZE */ | ||
42 | #define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ | ||
43 | #define VERSION "$Revision: 1.27 $" | ||
44 | |||
45 | /* Info for the block device */ | ||
46 | struct blkmtd_dev { | ||
47 | struct list_head list; | ||
48 | struct block_device *blkdev; | ||
49 | struct mtd_info mtd_info; | ||
50 | struct mutex wrbuf_mutex; | ||
51 | }; | ||
52 | |||
53 | |||
54 | /* Static info about the MTD, used in cleanup_module */ | ||
55 | static LIST_HEAD(blkmtd_device_list); | ||
56 | |||
57 | |||
58 | static void blkmtd_sync(struct mtd_info *mtd); | ||
59 | |||
60 | #define MAX_DEVICES 4 | ||
61 | |||
62 | /* Module parameters passed by insmod/modprobe */ | ||
63 | static char *device[MAX_DEVICES]; /* the block device to use */ | ||
64 | static int erasesz[MAX_DEVICES]; /* optional default erase size */ | ||
65 | static int ro[MAX_DEVICES]; /* optional read only flag */ | ||
66 | static int sync; | ||
67 | |||
68 | |||
69 | MODULE_LICENSE("GPL"); | ||
70 | MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>"); | ||
71 | MODULE_DESCRIPTION("Emulate an MTD using a block device"); | ||
72 | module_param_array(device, charp, NULL, 0); | ||
73 | MODULE_PARM_DESC(device, "block device to use"); | ||
74 | module_param_array(erasesz, int, NULL, 0); | ||
75 | MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); | ||
76 | module_param_array(ro, bool, NULL, 0); | ||
77 | MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); | ||
78 | module_param(sync, bool, 0); | ||
79 | MODULE_PARM_DESC(sync, "1=Synchronous writes"); | ||
80 | |||
81 | |||
82 | /* completion handler for BIO reads */ | ||
83 | static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error) | ||
84 | { | ||
85 | if (bio->bi_size) | ||
86 | return 1; | ||
87 | |||
88 | complete((struct completion*)bio->bi_private); | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | |||
93 | /* completion handler for BIO writes */ | ||
94 | static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error) | ||
95 | { | ||
96 | const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); | ||
97 | struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; | ||
98 | |||
99 | if (bio->bi_size) | ||
100 | return 1; | ||
101 | |||
102 | if(!uptodate) | ||
103 | err("bi_write_complete: not uptodate\n"); | ||
104 | |||
105 | do { | ||
106 | struct page *page = bvec->bv_page; | ||
107 | DEBUG(3, "Cleaning up page %ld\n", page->index); | ||
108 | if (--bvec >= bio->bi_io_vec) | ||
109 | prefetchw(&bvec->bv_page->flags); | ||
110 | |||
111 | if (uptodate) { | ||
112 | SetPageUptodate(page); | ||
113 | } else { | ||
114 | ClearPageUptodate(page); | ||
115 | SetPageError(page); | ||
116 | } | ||
117 | clear_page_dirty(page); | ||
118 | unlock_page(page); | ||
119 | page_cache_release(page); | ||
120 | } while (bvec >= bio->bi_io_vec); | ||
121 | |||
122 | complete((struct completion*)bio->bi_private); | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | |||
127 | /* read one page from the block device */ | ||
128 | static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page) | ||
129 | { | ||
130 | struct bio *bio; | ||
131 | struct completion event; | ||
132 | int err = -ENOMEM; | ||
133 | |||
134 | if(PageUptodate(page)) { | ||
135 | DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index); | ||
136 | unlock_page(page); | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | ClearPageUptodate(page); | ||
141 | ClearPageError(page); | ||
142 | |||
143 | bio = bio_alloc(GFP_KERNEL, 1); | ||
144 | if(bio) { | ||
145 | init_completion(&event); | ||
146 | bio->bi_bdev = dev->blkdev; | ||
147 | bio->bi_sector = page->index << (PAGE_SHIFT-9); | ||
148 | bio->bi_private = &event; | ||
149 | bio->bi_end_io = bi_read_complete; | ||
150 | if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) { | ||
151 | submit_bio(READ_SYNC, bio); | ||
152 | wait_for_completion(&event); | ||
153 | err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; | ||
154 | bio_put(bio); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | if(err) | ||
159 | SetPageError(page); | ||
160 | else | ||
161 | SetPageUptodate(page); | ||
162 | flush_dcache_page(page); | ||
163 | unlock_page(page); | ||
164 | return err; | ||
165 | } | ||
166 | |||
167 | |||
168 | /* write out the current BIO and wait for it to finish */ | ||
169 | static int blkmtd_write_out(struct bio *bio) | ||
170 | { | ||
171 | struct completion event; | ||
172 | int err; | ||
173 | |||
174 | if(!bio->bi_vcnt) { | ||
175 | bio_put(bio); | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | init_completion(&event); | ||
180 | bio->bi_private = &event; | ||
181 | bio->bi_end_io = bi_write_complete; | ||
182 | submit_bio(WRITE_SYNC, bio); | ||
183 | wait_for_completion(&event); | ||
184 | DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt); | ||
185 | err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; | ||
186 | bio_put(bio); | ||
187 | return err; | ||
188 | } | ||
189 | |||
190 | |||
191 | /** | ||
192 | * blkmtd_add_page - add a page to the current BIO | ||
193 | * @bio: bio to add to (NULL to alloc initial bio) | ||
194 | * @blkdev: block device | ||
195 | * @page: page to add | ||
196 | * @pagecnt: pages left to add | ||
197 | * | ||
198 | * Adds a page to the current bio, allocating it if necessary. If it cannot be | ||
199 | * added, the current bio is written out and a new one is allocated. Returns | ||
200 | * the new bio to add or NULL on error | ||
201 | */ | ||
202 | static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev, | ||
203 | struct page *page, int pagecnt) | ||
204 | { | ||
205 | |||
206 | retry: | ||
207 | if(!bio) { | ||
208 | bio = bio_alloc(GFP_KERNEL, pagecnt); | ||
209 | if(!bio) | ||
210 | return NULL; | ||
211 | bio->bi_sector = page->index << (PAGE_SHIFT-9); | ||
212 | bio->bi_bdev = blkdev; | ||
213 | } | ||
214 | |||
215 | if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) { | ||
216 | blkmtd_write_out(bio); | ||
217 | bio = NULL; | ||
218 | goto retry; | ||
219 | } | ||
220 | return bio; | ||
221 | } | ||
222 | |||
223 | |||
224 | /** | ||
225 | * write_pages - write block of data to device via the page cache | ||
226 | * @dev: device to write to | ||
227 | * @buf: data source or NULL if erase (output is set to 0xff) | ||
228 | * @to: offset into output device | ||
229 | * @len: amount to data to write | ||
230 | * @retlen: amount of data written | ||
231 | * | ||
232 | * Grab pages from the page cache and fill them with the source data. | ||
233 | * Non page aligned start and end result in a readin of the page and | ||
234 | * part of the page being modified. Pages are added to the bio and then written | ||
235 | * out. | ||
236 | */ | ||
237 | static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, | ||
238 | size_t len, size_t *retlen) | ||
239 | { | ||
240 | int pagenr, offset; | ||
241 | size_t start_len = 0, end_len; | ||
242 | int pagecnt = 0; | ||
243 | int err = 0; | ||
244 | struct bio *bio = NULL; | ||
245 | size_t thislen = 0; | ||
246 | |||
247 | pagenr = to >> PAGE_SHIFT; | ||
248 | offset = to & ~PAGE_MASK; | ||
249 | |||
250 | DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n", | ||
251 | buf, (long)to, len, pagenr, offset); | ||
252 | |||
253 | /* see if we have to do a partial write at the start */ | ||
254 | if(offset) { | ||
255 | start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; | ||
256 | len -= start_len; | ||
257 | } | ||
258 | |||
259 | /* calculate the length of the other two regions */ | ||
260 | end_len = len & ~PAGE_MASK; | ||
261 | len -= end_len; | ||
262 | |||
263 | if(start_len) | ||
264 | pagecnt++; | ||
265 | |||
266 | if(len) | ||
267 | pagecnt += len >> PAGE_SHIFT; | ||
268 | |||
269 | if(end_len) | ||
270 | pagecnt++; | ||
271 | |||
272 | mutex_lock(&dev->wrbuf_mutex); | ||
273 | |||
274 | DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n", | ||
275 | start_len, len, end_len, pagecnt); | ||
276 | |||
277 | if(start_len) { | ||
278 | /* do partial start region */ | ||
279 | struct page *page; | ||
280 | |||
281 | DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n", | ||
282 | pagenr, start_len, offset); | ||
283 | |||
284 | BUG_ON(!buf); | ||
285 | page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); | ||
286 | lock_page(page); | ||
287 | if(PageDirty(page)) { | ||
288 | err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", | ||
289 | to, start_len, len, end_len, pagenr); | ||
290 | BUG(); | ||
291 | } | ||
292 | memcpy(page_address(page)+offset, buf, start_len); | ||
293 | set_page_dirty(page); | ||
294 | SetPageUptodate(page); | ||
295 | buf += start_len; | ||
296 | thislen = start_len; | ||
297 | bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); | ||
298 | if(!bio) { | ||
299 | err = -ENOMEM; | ||
300 | err("bio_add_page failed\n"); | ||
301 | goto write_err; | ||
302 | } | ||
303 | pagecnt--; | ||
304 | pagenr++; | ||
305 | } | ||
306 | |||
307 | /* Now do the main loop to a page aligned, n page sized output */ | ||
308 | if(len) { | ||
309 | int pagesc = len >> PAGE_SHIFT; | ||
310 | DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n", | ||
311 | pagenr, pagesc); | ||
312 | while(pagesc) { | ||
313 | struct page *page; | ||
314 | |||
315 | /* see if page is in the page cache */ | ||
316 | DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); | ||
317 | page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr); | ||
318 | if(PageDirty(page)) { | ||
319 | BUG(); | ||
320 | } | ||
321 | if(!page) { | ||
322 | warn("write: cannot grab cache page %d", pagenr); | ||
323 | err = -ENOMEM; | ||
324 | goto write_err; | ||
325 | } | ||
326 | if(!buf) { | ||
327 | memset(page_address(page), 0xff, PAGE_SIZE); | ||
328 | } else { | ||
329 | memcpy(page_address(page), buf, PAGE_SIZE); | ||
330 | buf += PAGE_SIZE; | ||
331 | } | ||
332 | bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); | ||
333 | if(!bio) { | ||
334 | err = -ENOMEM; | ||
335 | err("bio_add_page failed\n"); | ||
336 | goto write_err; | ||
337 | } | ||
338 | pagenr++; | ||
339 | pagecnt--; | ||
340 | set_page_dirty(page); | ||
341 | SetPageUptodate(page); | ||
342 | pagesc--; | ||
343 | thislen += PAGE_SIZE; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | if(end_len) { | ||
348 | /* do the third region */ | ||
349 | struct page *page; | ||
350 | DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n", | ||
351 | pagenr, end_len); | ||
352 | BUG_ON(!buf); | ||
353 | page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); | ||
354 | lock_page(page); | ||
355 | if(PageDirty(page)) { | ||
356 | err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", | ||
357 | to, start_len, len, end_len, pagenr); | ||
358 | BUG(); | ||
359 | } | ||
360 | memcpy(page_address(page), buf, end_len); | ||
361 | set_page_dirty(page); | ||
362 | SetPageUptodate(page); | ||
363 | DEBUG(3, "blkmtd: write: writing out partial end\n"); | ||
364 | thislen += end_len; | ||
365 | bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); | ||
366 | if(!bio) { | ||
367 | err = -ENOMEM; | ||
368 | err("bio_add_page failed\n"); | ||
369 | goto write_err; | ||
370 | } | ||
371 | pagenr++; | ||
372 | } | ||
373 | |||
374 | DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt); | ||
375 | write_err: | ||
376 | if(bio) | ||
377 | blkmtd_write_out(bio); | ||
378 | |||
379 | DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err); | ||
380 | mutex_unlock(&dev->wrbuf_mutex); | ||
381 | |||
382 | if(retlen) | ||
383 | *retlen = thislen; | ||
384 | return err; | ||
385 | } | ||
386 | |||
387 | |||
388 | /* erase a specified part of the device */ | ||
389 | static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
390 | { | ||
391 | struct blkmtd_dev *dev = mtd->priv; | ||
392 | struct mtd_erase_region_info *einfo = mtd->eraseregions; | ||
393 | int numregions = mtd->numeraseregions; | ||
394 | size_t from; | ||
395 | u_long len; | ||
396 | int err = -EIO; | ||
397 | size_t retlen; | ||
398 | |||
399 | instr->state = MTD_ERASING; | ||
400 | from = instr->addr; | ||
401 | len = instr->len; | ||
402 | |||
403 | /* check erase region has valid start and length */ | ||
404 | DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n", | ||
405 | mtd->name+9, from, len); | ||
406 | while(numregions) { | ||
407 | DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", | ||
408 | einfo->offset, einfo->erasesize, einfo->numblocks); | ||
409 | if(from >= einfo->offset | ||
410 | && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { | ||
411 | if(len == einfo->erasesize | ||
412 | && ( (from - einfo->offset) % einfo->erasesize == 0)) | ||
413 | break; | ||
414 | } | ||
415 | numregions--; | ||
416 | einfo++; | ||
417 | } | ||
418 | |||
419 | if(!numregions) { | ||
420 | /* Not a valid erase block */ | ||
421 | err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); | ||
422 | instr->state = MTD_ERASE_FAILED; | ||
423 | err = -EIO; | ||
424 | } | ||
425 | |||
426 | if(instr->state != MTD_ERASE_FAILED) { | ||
427 | /* do the erase */ | ||
428 | DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); | ||
429 | err = write_pages(dev, NULL, from, len, &retlen); | ||
430 | if(err || retlen != len) { | ||
431 | err("erase failed err = %d", err); | ||
432 | instr->state = MTD_ERASE_FAILED; | ||
433 | } else { | ||
434 | instr->state = MTD_ERASE_DONE; | ||
435 | } | ||
436 | } | ||
437 | |||
438 | DEBUG(3, "blkmtd: erase: checking callback\n"); | ||
439 | mtd_erase_callback(instr); | ||
440 | DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); | ||
441 | return err; | ||
442 | } | ||
443 | |||
444 | |||
445 | /* read a range of the data via the page cache */ | ||
446 | static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
447 | size_t *retlen, u_char *buf) | ||
448 | { | ||
449 | struct blkmtd_dev *dev = mtd->priv; | ||
450 | int err = 0; | ||
451 | int offset; | ||
452 | int pagenr, pages; | ||
453 | size_t thislen = 0; | ||
454 | |||
455 | DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", | ||
456 | mtd->name+9, from, len, buf); | ||
457 | |||
458 | if(from > mtd->size) | ||
459 | return -EINVAL; | ||
460 | if(from + len > mtd->size) | ||
461 | len = mtd->size - from; | ||
462 | |||
463 | pagenr = from >> PAGE_SHIFT; | ||
464 | offset = from - (pagenr << PAGE_SHIFT); | ||
465 | |||
466 | pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; | ||
467 | DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", | ||
468 | pagenr, offset, pages); | ||
469 | |||
470 | while(pages) { | ||
471 | struct page *page; | ||
472 | int cpylen; | ||
473 | |||
474 | DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); | ||
475 | page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); | ||
476 | if(IS_ERR(page)) { | ||
477 | err = -EIO; | ||
478 | goto readerr; | ||
479 | } | ||
480 | |||
481 | cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; | ||
482 | if(offset+cpylen > PAGE_SIZE) | ||
483 | cpylen = PAGE_SIZE-offset; | ||
484 | |||
485 | memcpy(buf + thislen, page_address(page) + offset, cpylen); | ||
486 | offset = 0; | ||
487 | len -= cpylen; | ||
488 | thislen += cpylen; | ||
489 | pagenr++; | ||
490 | pages--; | ||
491 | if(!PageDirty(page)) | ||
492 | page_cache_release(page); | ||
493 | } | ||
494 | |||
495 | readerr: | ||
496 | if(retlen) | ||
497 | *retlen = thislen; | ||
498 | DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", thislen, err); | ||
499 | return err; | ||
500 | } | ||
501 | |||
502 | |||
503 | /* write data to the underlying device */ | ||
504 | static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
505 | size_t *retlen, const u_char *buf) | ||
506 | { | ||
507 | struct blkmtd_dev *dev = mtd->priv; | ||
508 | int err; | ||
509 | |||
510 | if(!len) | ||
511 | return 0; | ||
512 | |||
513 | DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", | ||
514 | mtd->name+9, to, len, buf); | ||
515 | |||
516 | if(to >= mtd->size) { | ||
517 | return -ENOSPC; | ||
518 | } | ||
519 | |||
520 | if(to + len > mtd->size) { | ||
521 | len = mtd->size - to; | ||
522 | } | ||
523 | |||
524 | err = write_pages(dev, buf, to, len, retlen); | ||
525 | if(err > 0) | ||
526 | err = 0; | ||
527 | DEBUG(2, "blkmtd: write: end, err = %d\n", err); | ||
528 | return err; | ||
529 | } | ||
530 | |||
531 | |||
532 | /* sync the device - wait until the write queue is empty */ | ||
533 | static void blkmtd_sync(struct mtd_info *mtd) | ||
534 | { | ||
535 | /* Currently all writes are synchronous */ | ||
536 | } | ||
537 | |||
538 | |||
539 | static void free_device(struct blkmtd_dev *dev) | ||
540 | { | ||
541 | DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); | ||
542 | if(dev) { | ||
543 | kfree(dev->mtd_info.eraseregions); | ||
544 | kfree(dev->mtd_info.name); | ||
545 | if(dev->blkdev) { | ||
546 | invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); | ||
547 | close_bdev_excl(dev->blkdev); | ||
548 | } | ||
549 | kfree(dev); | ||
550 | } | ||
551 | } | ||
552 | |||
553 | |||
554 | /* For a given size and initial erase size, calculate the number | ||
555 | * and size of each erase region. Goes round the loop twice, | ||
556 | * once to find out how many regions, then allocates space, | ||
557 | * then round the loop again to fill it in. | ||
558 | */ | ||
559 | static struct mtd_erase_region_info *calc_erase_regions( | ||
560 | size_t erase_size, size_t total_size, int *regions) | ||
561 | { | ||
562 | struct mtd_erase_region_info *info = NULL; | ||
563 | |||
564 | DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", | ||
565 | erase_size, total_size, *regions); | ||
566 | /* Make any user specified erasesize be a power of 2 | ||
567 | and at least PAGE_SIZE */ | ||
568 | if(erase_size) { | ||
569 | int es = erase_size; | ||
570 | erase_size = 1; | ||
571 | while(es != 1) { | ||
572 | es >>= 1; | ||
573 | erase_size <<= 1; | ||
574 | } | ||
575 | if(erase_size < PAGE_SIZE) | ||
576 | erase_size = PAGE_SIZE; | ||
577 | } else { | ||
578 | erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; | ||
579 | } | ||
580 | |||
581 | *regions = 0; | ||
582 | |||
583 | do { | ||
584 | int tot_size = total_size; | ||
585 | int er_size = erase_size; | ||
586 | int count = 0, offset = 0, regcnt = 0; | ||
587 | |||
588 | while(tot_size) { | ||
589 | count = tot_size / er_size; | ||
590 | if(count) { | ||
591 | tot_size = tot_size % er_size; | ||
592 | if(info) { | ||
593 | DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", | ||
594 | offset, er_size, count); | ||
595 | (info+regcnt)->offset = offset; | ||
596 | (info+regcnt)->erasesize = er_size; | ||
597 | (info+regcnt)->numblocks = count; | ||
598 | (*regions)++; | ||
599 | } | ||
600 | regcnt++; | ||
601 | offset += (count * er_size); | ||
602 | } | ||
603 | while(er_size > tot_size) | ||
604 | er_size >>= 1; | ||
605 | } | ||
606 | if(info == NULL) { | ||
607 | info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); | ||
608 | if(!info) | ||
609 | break; | ||
610 | } | ||
611 | } while(!(*regions)); | ||
612 | DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", | ||
613 | erase_size, total_size, *regions); | ||
614 | return info; | ||
615 | } | ||
616 | |||
617 | |||
618 | static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) | ||
619 | { | ||
620 | struct block_device *bdev; | ||
621 | int mode; | ||
622 | struct blkmtd_dev *dev; | ||
623 | |||
624 | if(!devname) | ||
625 | return NULL; | ||
626 | |||
627 | /* Get a handle on the device */ | ||
628 | |||
629 | |||
630 | #ifdef MODULE | ||
631 | mode = (readonly) ? O_RDONLY : O_RDWR; | ||
632 | bdev = open_bdev_excl(devname, mode, NULL); | ||
633 | #else | ||
634 | mode = (readonly) ? FMODE_READ : FMODE_WRITE; | ||
635 | bdev = open_by_devnum(name_to_dev_t(devname), mode); | ||
636 | #endif | ||
637 | if(IS_ERR(bdev)) { | ||
638 | err("error: cannot open device %s", devname); | ||
639 | DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev)); | ||
640 | return NULL; | ||
641 | } | ||
642 | |||
643 | DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", | ||
644 | MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); | ||
645 | |||
646 | if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { | ||
647 | err("attempting to use an MTD device as a block device"); | ||
648 | blkdev_put(bdev); | ||
649 | return NULL; | ||
650 | } | ||
651 | |||
652 | dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); | ||
653 | if(dev == NULL) { | ||
654 | blkdev_put(bdev); | ||
655 | return NULL; | ||
656 | } | ||
657 | |||
658 | memset(dev, 0, sizeof(struct blkmtd_dev)); | ||
659 | dev->blkdev = bdev; | ||
660 | if(!readonly) { | ||
661 | mutex_init(&dev->wrbuf_mutex); | ||
662 | } | ||
663 | |||
664 | dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; | ||
665 | |||
666 | /* Setup the MTD structure */ | ||
667 | /* make the name contain the block device in */ | ||
668 | dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); | ||
669 | if(dev->mtd_info.name == NULL) | ||
670 | goto devinit_err; | ||
671 | |||
672 | sprintf(dev->mtd_info.name, "blkmtd: %s", devname); | ||
673 | dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, | ||
674 | &dev->mtd_info.numeraseregions); | ||
675 | if(dev->mtd_info.eraseregions == NULL) | ||
676 | goto devinit_err; | ||
677 | |||
678 | dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; | ||
679 | DEBUG(1, "blkmtd: init: found %d erase regions\n", | ||
680 | dev->mtd_info.numeraseregions); | ||
681 | |||
682 | if(readonly) { | ||
683 | dev->mtd_info.type = MTD_ROM; | ||
684 | dev->mtd_info.flags = MTD_CAP_ROM; | ||
685 | } else { | ||
686 | dev->mtd_info.type = MTD_RAM; | ||
687 | dev->mtd_info.flags = MTD_CAP_RAM; | ||
688 | dev->mtd_info.erase = blkmtd_erase; | ||
689 | dev->mtd_info.write = blkmtd_write; | ||
690 | dev->mtd_info.writev = default_mtd_writev; | ||
691 | dev->mtd_info.sync = blkmtd_sync; | ||
692 | } | ||
693 | dev->mtd_info.read = blkmtd_read; | ||
694 | dev->mtd_info.readv = default_mtd_readv; | ||
695 | dev->mtd_info.priv = dev; | ||
696 | dev->mtd_info.owner = THIS_MODULE; | ||
697 | |||
698 | list_add(&dev->list, &blkmtd_device_list); | ||
699 | if (add_mtd_device(&dev->mtd_info)) { | ||
700 | /* Device didnt get added, so free the entry */ | ||
701 | list_del(&dev->list); | ||
702 | goto devinit_err; | ||
703 | } else { | ||
704 | info("mtd%d: [%s] erase_size = %dKiB %s", | ||
705 | dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), | ||
706 | dev->mtd_info.erasesize >> 10, | ||
707 | readonly ? "(read-only)" : ""); | ||
708 | } | ||
709 | |||
710 | return dev; | ||
711 | |||
712 | devinit_err: | ||
713 | free_device(dev); | ||
714 | return NULL; | ||
715 | } | ||
716 | |||
717 | |||
718 | /* Cleanup and exit - sync the device and kill of the kernel thread */ | ||
719 | static void __devexit cleanup_blkmtd(void) | ||
720 | { | ||
721 | struct list_head *temp1, *temp2; | ||
722 | |||
723 | /* Remove the MTD devices */ | ||
724 | list_for_each_safe(temp1, temp2, &blkmtd_device_list) { | ||
725 | struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, | ||
726 | list); | ||
727 | blkmtd_sync(&dev->mtd_info); | ||
728 | del_mtd_device(&dev->mtd_info); | ||
729 | info("mtd%d: [%s] removed", dev->mtd_info.index, | ||
730 | dev->mtd_info.name + strlen("blkmtd: ")); | ||
731 | list_del(&dev->list); | ||
732 | free_device(dev); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | #ifndef MODULE | ||
737 | |||
738 | /* Handle kernel boot params */ | ||
739 | |||
740 | |||
741 | static int __init param_blkmtd_device(char *str) | ||
742 | { | ||
743 | int i; | ||
744 | |||
745 | for(i = 0; i < MAX_DEVICES; i++) { | ||
746 | device[i] = str; | ||
747 | DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); | ||
748 | strsep(&str, ","); | ||
749 | } | ||
750 | return 1; | ||
751 | } | ||
752 | |||
753 | |||
754 | static int __init param_blkmtd_erasesz(char *str) | ||
755 | { | ||
756 | int i; | ||
757 | for(i = 0; i < MAX_DEVICES; i++) { | ||
758 | char *val = strsep(&str, ","); | ||
759 | if(val) | ||
760 | erasesz[i] = simple_strtoul(val, NULL, 0); | ||
761 | DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); | ||
762 | } | ||
763 | |||
764 | return 1; | ||
765 | } | ||
766 | |||
767 | |||
768 | static int __init param_blkmtd_ro(char *str) | ||
769 | { | ||
770 | int i; | ||
771 | for(i = 0; i < MAX_DEVICES; i++) { | ||
772 | char *val = strsep(&str, ","); | ||
773 | if(val) | ||
774 | ro[i] = simple_strtoul(val, NULL, 0); | ||
775 | DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); | ||
776 | } | ||
777 | |||
778 | return 1; | ||
779 | } | ||
780 | |||
781 | |||
782 | static int __init param_blkmtd_sync(char *str) | ||
783 | { | ||
784 | if(str[0] == '1') | ||
785 | sync = 1; | ||
786 | return 1; | ||
787 | } | ||
788 | |||
789 | __setup("blkmtd_device=", param_blkmtd_device); | ||
790 | __setup("blkmtd_erasesz=", param_blkmtd_erasesz); | ||
791 | __setup("blkmtd_ro=", param_blkmtd_ro); | ||
792 | __setup("blkmtd_sync=", param_blkmtd_sync); | ||
793 | |||
794 | #endif | ||
795 | |||
796 | |||
797 | /* Startup */ | ||
798 | static int __init init_blkmtd(void) | ||
799 | { | ||
800 | int i; | ||
801 | |||
802 | info("version " VERSION); | ||
803 | /* Check args - device[0] is the bare minimum*/ | ||
804 | if(!device[0]) { | ||
805 | err("error: missing `device' name\n"); | ||
806 | return -EINVAL; | ||
807 | } | ||
808 | |||
809 | for(i = 0; i < MAX_DEVICES; i++) | ||
810 | add_device(device[i], ro[i], erasesz[i] << 10); | ||
811 | |||
812 | if(list_empty(&blkmtd_device_list)) | ||
813 | return -EINVAL; | ||
814 | |||
815 | return 0; | ||
816 | } | ||
817 | |||
818 | module_init(init_blkmtd); | ||
819 | module_exit(cleanup_blkmtd); | ||
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 8bbc751a6021..d27f4129afd3 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c | |||
@@ -54,7 +54,7 @@ static const int debug = 0; | |||
54 | #define MAX_PCMCIA_ADDR 0x4000000 | 54 | #define MAX_PCMCIA_ADDR 0x4000000 |
55 | 55 | ||
56 | struct pcmciamtd_dev { | 56 | struct pcmciamtd_dev { |
57 | dev_link_t link; /* PCMCIA link */ | 57 | struct pcmcia_device *p_dev; |
58 | dev_node_t node; /* device node */ | 58 | dev_node_t node; /* device node */ |
59 | caddr_t win_base; /* ioremapped address of PCMCIA window */ | 59 | caddr_t win_base; /* ioremapped address of PCMCIA window */ |
60 | unsigned int win_size; /* size of window */ | 60 | unsigned int win_size; /* size of window */ |
@@ -111,8 +111,8 @@ static caddr_t remap_window(struct map_info *map, unsigned long to) | |||
111 | memreq_t mrq; | 111 | memreq_t mrq; |
112 | int ret; | 112 | int ret; |
113 | 113 | ||
114 | if(!(dev->link.state & DEV_PRESENT)) { | 114 | if (!pcmcia_dev_present(dev->p_dev)) { |
115 | DEBUG(1, "device removed state = 0x%4.4X", dev->link.state); | 115 | DEBUG(1, "device removed"); |
116 | return 0; | 116 | return 0; |
117 | } | 117 | } |
118 | 118 | ||
@@ -122,7 +122,7 @@ static caddr_t remap_window(struct map_info *map, unsigned long to) | |||
122 | dev->offset, mrq.CardOffset); | 122 | dev->offset, mrq.CardOffset); |
123 | mrq.Page = 0; | 123 | mrq.Page = 0; |
124 | if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) { | 124 | if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) { |
125 | cs_error(dev->link.handle, MapMemPage, ret); | 125 | cs_error(dev->p_dev, MapMemPage, ret); |
126 | return NULL; | 126 | return NULL; |
127 | } | 127 | } |
128 | dev->offset = mrq.CardOffset; | 128 | dev->offset = mrq.CardOffset; |
@@ -238,7 +238,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v | |||
238 | 238 | ||
239 | /* read/write{8,16} copy_{from,to} routines with direct access */ | 239 | /* read/write{8,16} copy_{from,to} routines with direct access */ |
240 | 240 | ||
241 | #define DEV_REMOVED(x) (!(*(u_int *)x->map_priv_1 & DEV_PRESENT)) | 241 | #define DEV_REMOVED(x) (!(pcmcia_dev_present(((struct pcmciamtd_dev *)map->map_priv_1)->p_dev))) |
242 | 242 | ||
243 | static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) | 243 | static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) |
244 | { | 244 | { |
@@ -319,7 +319,7 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f | |||
319 | static void pcmciamtd_set_vpp(struct map_info *map, int on) | 319 | static void pcmciamtd_set_vpp(struct map_info *map, int on) |
320 | { | 320 | { |
321 | struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; | 321 | struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; |
322 | dev_link_t *link = &dev->link; | 322 | struct pcmcia_device *link = dev->p_dev; |
323 | modconf_t mod; | 323 | modconf_t mod; |
324 | int ret; | 324 | int ret; |
325 | 325 | ||
@@ -328,9 +328,9 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on) | |||
328 | mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0; | 328 | mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0; |
329 | 329 | ||
330 | DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp); | 330 | DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp); |
331 | ret = pcmcia_modify_configuration(link->handle, &mod); | 331 | ret = pcmcia_modify_configuration(link, &mod); |
332 | if(ret != CS_SUCCESS) { | 332 | if(ret != CS_SUCCESS) { |
333 | cs_error(link->handle, ModifyConfiguration, ret); | 333 | cs_error(link, ModifyConfiguration, ret); |
334 | } | 334 | } |
335 | } | 335 | } |
336 | 336 | ||
@@ -340,7 +340,7 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on) | |||
340 | * still open, this will be postponed until it is closed. | 340 | * still open, this will be postponed until it is closed. |
341 | */ | 341 | */ |
342 | 342 | ||
343 | static void pcmciamtd_release(dev_link_t *link) | 343 | static void pcmciamtd_release(struct pcmcia_device *link) |
344 | { | 344 | { |
345 | struct pcmciamtd_dev *dev = link->priv; | 345 | struct pcmciamtd_dev *dev = link->priv; |
346 | 346 | ||
@@ -353,12 +353,11 @@ static void pcmciamtd_release(dev_link_t *link) | |||
353 | } | 353 | } |
354 | pcmcia_release_window(link->win); | 354 | pcmcia_release_window(link->win); |
355 | } | 355 | } |
356 | pcmcia_release_configuration(link->handle); | 356 | pcmcia_disable_device(link); |
357 | link->state &= ~DEV_CONFIG; | ||
358 | } | 357 | } |
359 | 358 | ||
360 | 359 | ||
361 | static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_name) | 360 | static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name) |
362 | { | 361 | { |
363 | int rc; | 362 | int rc; |
364 | tuple_t tuple; | 363 | tuple_t tuple; |
@@ -371,16 +370,16 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ | |||
371 | tuple.TupleOffset = 0; | 370 | tuple.TupleOffset = 0; |
372 | tuple.DesiredTuple = RETURN_FIRST_TUPLE; | 371 | tuple.DesiredTuple = RETURN_FIRST_TUPLE; |
373 | 372 | ||
374 | rc = pcmcia_get_first_tuple(link->handle, &tuple); | 373 | rc = pcmcia_get_first_tuple(link, &tuple); |
375 | while(rc == CS_SUCCESS) { | 374 | while(rc == CS_SUCCESS) { |
376 | rc = pcmcia_get_tuple_data(link->handle, &tuple); | 375 | rc = pcmcia_get_tuple_data(link, &tuple); |
377 | if(rc != CS_SUCCESS) { | 376 | if(rc != CS_SUCCESS) { |
378 | cs_error(link->handle, GetTupleData, rc); | 377 | cs_error(link, GetTupleData, rc); |
379 | break; | 378 | break; |
380 | } | 379 | } |
381 | rc = pcmcia_parse_tuple(link->handle, &tuple, &parse); | 380 | rc = pcmcia_parse_tuple(link, &tuple, &parse); |
382 | if(rc != CS_SUCCESS) { | 381 | if(rc != CS_SUCCESS) { |
383 | cs_error(link->handle, ParseTuple, rc); | 382 | cs_error(link, ParseTuple, rc); |
384 | break; | 383 | break; |
385 | } | 384 | } |
386 | 385 | ||
@@ -451,7 +450,7 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ | |||
451 | DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); | 450 | DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); |
452 | } | 451 | } |
453 | 452 | ||
454 | rc = pcmcia_get_next_tuple(link->handle, &tuple); | 453 | rc = pcmcia_get_next_tuple(link, &tuple); |
455 | } | 454 | } |
456 | if(!dev->pcmcia_map.size) | 455 | if(!dev->pcmcia_map.size) |
457 | dev->pcmcia_map.size = MAX_PCMCIA_ADDR; | 456 | dev->pcmcia_map.size = MAX_PCMCIA_ADDR; |
@@ -488,7 +487,7 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ | |||
488 | #define CS_CHECK(fn, ret) \ | 487 | #define CS_CHECK(fn, ret) \ |
489 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) | 488 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) |
490 | 489 | ||
491 | static void pcmciamtd_config(dev_link_t *link) | 490 | static int pcmciamtd_config(struct pcmcia_device *link) |
492 | { | 491 | { |
493 | struct pcmciamtd_dev *dev = link->priv; | 492 | struct pcmciamtd_dev *dev = link->priv; |
494 | struct mtd_info *mtd = NULL; | 493 | struct mtd_info *mtd = NULL; |
@@ -504,13 +503,10 @@ static void pcmciamtd_config(dev_link_t *link) | |||
504 | 503 | ||
505 | DEBUG(3, "link=0x%p", link); | 504 | DEBUG(3, "link=0x%p", link); |
506 | 505 | ||
507 | /* Configure card */ | ||
508 | link->state |= DEV_CONFIG; | ||
509 | |||
510 | DEBUG(2, "Validating CIS"); | 506 | DEBUG(2, "Validating CIS"); |
511 | ret = pcmcia_validate_cis(link->handle, &cisinfo); | 507 | ret = pcmcia_validate_cis(link, &cisinfo); |
512 | if(ret != CS_SUCCESS) { | 508 | if(ret != CS_SUCCESS) { |
513 | cs_error(link->handle, GetTupleData, ret); | 509 | cs_error(link, GetTupleData, ret); |
514 | } else { | 510 | } else { |
515 | DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains); | 511 | DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains); |
516 | } | 512 | } |
@@ -538,7 +534,7 @@ static void pcmciamtd_config(dev_link_t *link) | |||
538 | req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; | 534 | req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; |
539 | req.Base = 0; | 535 | req.Base = 0; |
540 | req.AccessSpeed = mem_speed; | 536 | req.AccessSpeed = mem_speed; |
541 | link->win = (window_handle_t)link->handle; | 537 | link->win = (window_handle_t)link; |
542 | req.Size = (force_size) ? force_size << 20 : MAX_PCMCIA_ADDR; | 538 | req.Size = (force_size) ? force_size << 20 : MAX_PCMCIA_ADDR; |
543 | dev->win_size = 0; | 539 | dev->win_size = 0; |
544 | 540 | ||
@@ -546,7 +542,7 @@ static void pcmciamtd_config(dev_link_t *link) | |||
546 | int ret; | 542 | int ret; |
547 | DEBUG(2, "requesting window with size = %dKiB memspeed = %d", | 543 | DEBUG(2, "requesting window with size = %dKiB memspeed = %d", |
548 | req.Size >> 10, req.AccessSpeed); | 544 | req.Size >> 10, req.AccessSpeed); |
549 | ret = pcmcia_request_window(&link->handle, &req, &link->win); | 545 | ret = pcmcia_request_window(&link, &req, &link->win); |
550 | DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); | 546 | DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); |
551 | if(ret) { | 547 | if(ret) { |
552 | req.Size >>= 1; | 548 | req.Size >>= 1; |
@@ -562,19 +558,19 @@ static void pcmciamtd_config(dev_link_t *link) | |||
562 | if(!dev->win_size) { | 558 | if(!dev->win_size) { |
563 | err("Cant allocate memory window"); | 559 | err("Cant allocate memory window"); |
564 | pcmciamtd_release(link); | 560 | pcmciamtd_release(link); |
565 | return; | 561 | return -ENODEV; |
566 | } | 562 | } |
567 | DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); | 563 | DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); |
568 | 564 | ||
569 | /* Get write protect status */ | 565 | /* Get write protect status */ |
570 | CS_CHECK(GetStatus, pcmcia_get_status(link->handle, &status)); | 566 | CS_CHECK(GetStatus, pcmcia_get_status(link, &status)); |
571 | DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", | 567 | DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", |
572 | status.CardState, (unsigned long)link->win); | 568 | status.CardState, (unsigned long)link->win); |
573 | dev->win_base = ioremap(req.Base, req.Size); | 569 | dev->win_base = ioremap(req.Base, req.Size); |
574 | if(!dev->win_base) { | 570 | if(!dev->win_base) { |
575 | err("ioremap(%lu, %u) failed", req.Base, req.Size); | 571 | err("ioremap(%lu, %u) failed", req.Base, req.Size); |
576 | pcmciamtd_release(link); | 572 | pcmciamtd_release(link); |
577 | return; | 573 | return -ENODEV; |
578 | } | 574 | } |
579 | DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", | 575 | DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", |
580 | dev, req.Base, dev->win_base, req.Size); | 576 | dev, req.Base, dev->win_base, req.Size); |
@@ -584,17 +580,14 @@ static void pcmciamtd_config(dev_link_t *link) | |||
584 | dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; | 580 | dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; |
585 | 581 | ||
586 | DEBUG(2, "Getting configuration"); | 582 | DEBUG(2, "Getting configuration"); |
587 | CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link->handle, &t)); | 583 | CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &t)); |
588 | DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2); | 584 | DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2); |
589 | dev->vpp = (vpp) ? vpp : t.Vpp1; | 585 | dev->vpp = (vpp) ? vpp : t.Vpp1; |
590 | link->conf.Attributes = 0; | 586 | link->conf.Attributes = 0; |
591 | link->conf.Vcc = t.Vcc; | ||
592 | if(setvpp == 2) { | 587 | if(setvpp == 2) { |
593 | link->conf.Vpp1 = dev->vpp; | 588 | link->conf.Vpp = dev->vpp; |
594 | link->conf.Vpp2 = dev->vpp; | ||
595 | } else { | 589 | } else { |
596 | link->conf.Vpp1 = 0; | 590 | link->conf.Vpp = 0; |
597 | link->conf.Vpp2 = 0; | ||
598 | } | 591 | } |
599 | 592 | ||
600 | link->conf.IntType = INT_MEMORY; | 593 | link->conf.IntType = INT_MEMORY; |
@@ -606,9 +599,10 @@ static void pcmciamtd_config(dev_link_t *link) | |||
606 | link->conf.ConfigIndex = 0; | 599 | link->conf.ConfigIndex = 0; |
607 | link->conf.Present = t.Present; | 600 | link->conf.Present = t.Present; |
608 | DEBUG(2, "Setting Configuration"); | 601 | DEBUG(2, "Setting Configuration"); |
609 | ret = pcmcia_request_configuration(link->handle, &link->conf); | 602 | ret = pcmcia_request_configuration(link, &link->conf); |
610 | if(ret != CS_SUCCESS) { | 603 | if(ret != CS_SUCCESS) { |
611 | cs_error(link->handle, RequestConfiguration, ret); | 604 | cs_error(link, RequestConfiguration, ret); |
605 | return -ENODEV; | ||
612 | } | 606 | } |
613 | 607 | ||
614 | if(mem_type == 1) { | 608 | if(mem_type == 1) { |
@@ -629,7 +623,7 @@ static void pcmciamtd_config(dev_link_t *link) | |||
629 | if(!mtd) { | 623 | if(!mtd) { |
630 | DEBUG(1, "Cant find an MTD"); | 624 | DEBUG(1, "Cant find an MTD"); |
631 | pcmciamtd_release(link); | 625 | pcmciamtd_release(link); |
632 | return; | 626 | return -ENODEV; |
633 | } | 627 | } |
634 | 628 | ||
635 | dev->mtd_info = mtd; | 629 | dev->mtd_info = mtd; |
@@ -654,7 +648,6 @@ static void pcmciamtd_config(dev_link_t *link) | |||
654 | use the faster non-remapping read/write functions */ | 648 | use the faster non-remapping read/write functions */ |
655 | if(mtd->size <= dev->win_size) { | 649 | if(mtd->size <= dev->win_size) { |
656 | DEBUG(1, "Using non remapping memory functions"); | 650 | DEBUG(1, "Using non remapping memory functions"); |
657 | dev->pcmcia_map.map_priv_1 = (unsigned long)&(dev->link.state); | ||
658 | dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; | 651 | dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; |
659 | if (dev->pcmcia_map.bankwidth == 1) { | 652 | if (dev->pcmcia_map.bankwidth == 1) { |
660 | dev->pcmcia_map.read = pcmcia_read8; | 653 | dev->pcmcia_map.read = pcmcia_read8; |
@@ -672,19 +665,18 @@ static void pcmciamtd_config(dev_link_t *link) | |||
672 | dev->mtd_info = NULL; | 665 | dev->mtd_info = NULL; |
673 | err("Couldnt register MTD device"); | 666 | err("Couldnt register MTD device"); |
674 | pcmciamtd_release(link); | 667 | pcmciamtd_release(link); |
675 | return; | 668 | return -ENODEV; |
676 | } | 669 | } |
677 | snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); | 670 | snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); |
678 | info("mtd%d: %s", mtd->index, mtd->name); | 671 | info("mtd%d: %s", mtd->index, mtd->name); |
679 | link->state &= ~DEV_CONFIG_PENDING; | 672 | link->dev_node = &dev->node; |
680 | link->dev = &dev->node; | 673 | return 0; |
681 | return; | ||
682 | 674 | ||
683 | cs_failed: | 675 | cs_failed: |
684 | cs_error(link->handle, last_fn, last_ret); | 676 | cs_error(link, last_fn, last_ret); |
685 | err("CS Error, exiting"); | 677 | err("CS Error, exiting"); |
686 | pcmciamtd_release(link); | 678 | pcmciamtd_release(link); |
687 | return; | 679 | return -ENODEV; |
688 | } | 680 | } |
689 | 681 | ||
690 | 682 | ||
@@ -713,21 +705,18 @@ static int pcmciamtd_resume(struct pcmcia_device *dev) | |||
713 | * when the device is released. | 705 | * when the device is released. |
714 | */ | 706 | */ |
715 | 707 | ||
716 | static void pcmciamtd_detach(struct pcmcia_device *p_dev) | 708 | static void pcmciamtd_detach(struct pcmcia_device *link) |
717 | { | 709 | { |
718 | dev_link_t *link = dev_to_instance(p_dev); | 710 | struct pcmciamtd_dev *dev = link->priv; |
719 | 711 | ||
720 | DEBUG(3, "link=0x%p", link); | 712 | DEBUG(3, "link=0x%p", link); |
721 | 713 | ||
722 | if(link->state & DEV_CONFIG) { | 714 | if(dev->mtd_info) { |
723 | struct pcmciamtd_dev *dev = link->priv; | 715 | del_mtd_device(dev->mtd_info); |
724 | if(dev->mtd_info) { | 716 | info("mtd%d: Removed", dev->mtd_info->index); |
725 | del_mtd_device(dev->mtd_info); | ||
726 | info("mtd%d: Removed", dev->mtd_info->index); | ||
727 | } | ||
728 | |||
729 | pcmciamtd_release(link); | ||
730 | } | 717 | } |
718 | |||
719 | pcmciamtd_release(link); | ||
731 | } | 720 | } |
732 | 721 | ||
733 | 722 | ||
@@ -736,10 +725,9 @@ static void pcmciamtd_detach(struct pcmcia_device *p_dev) | |||
736 | * with Card Services. | 725 | * with Card Services. |
737 | */ | 726 | */ |
738 | 727 | ||
739 | static int pcmciamtd_attach(struct pcmcia_device *p_dev) | 728 | static int pcmciamtd_probe(struct pcmcia_device *link) |
740 | { | 729 | { |
741 | struct pcmciamtd_dev *dev; | 730 | struct pcmciamtd_dev *dev; |
742 | dev_link_t *link; | ||
743 | 731 | ||
744 | /* Create new memory card device */ | 732 | /* Create new memory card device */ |
745 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); | 733 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); |
@@ -747,20 +735,13 @@ static int pcmciamtd_attach(struct pcmcia_device *p_dev) | |||
747 | DEBUG(1, "dev=0x%p", dev); | 735 | DEBUG(1, "dev=0x%p", dev); |
748 | 736 | ||
749 | memset(dev, 0, sizeof(*dev)); | 737 | memset(dev, 0, sizeof(*dev)); |
750 | link = &dev->link; | 738 | dev->p_dev = link; |
751 | link->priv = dev; | 739 | link->priv = dev; |
752 | 740 | ||
753 | link->conf.Attributes = 0; | 741 | link->conf.Attributes = 0; |
754 | link->conf.IntType = INT_MEMORY; | 742 | link->conf.IntType = INT_MEMORY; |
755 | 743 | ||
756 | link->next = NULL; | 744 | return pcmciamtd_config(link); |
757 | link->handle = p_dev; | ||
758 | p_dev->instance = link; | ||
759 | |||
760 | link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; | ||
761 | pcmciamtd_config(link); | ||
762 | |||
763 | return 0; | ||
764 | } | 745 | } |
765 | 746 | ||
766 | static struct pcmcia_device_id pcmciamtd_ids[] = { | 747 | static struct pcmcia_device_id pcmciamtd_ids[] = { |
@@ -794,7 +775,7 @@ static struct pcmcia_driver pcmciamtd_driver = { | |||
794 | .drv = { | 775 | .drv = { |
795 | .name = "pcmciamtd" | 776 | .name = "pcmciamtd" |
796 | }, | 777 | }, |
797 | .probe = pcmciamtd_attach, | 778 | .probe = pcmciamtd_probe, |
798 | .remove = pcmciamtd_detach, | 779 | .remove = pcmciamtd_detach, |
799 | .owner = THIS_MODULE, | 780 | .owner = THIS_MODULE, |
800 | .id_table = pcmciamtd_ids, | 781 | .id_table = pcmciamtd_ids, |