JFFS for WNR3500Lv2
[tomato.git] / release / src-rt / linux / linux-2.6 / drivers / mtd / devices / bcm_nflash.c
blob72b71ab958862af12c599f04f0a9942da44dbf2d
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * $Id $
21 #include <linux/config.h>
22 #include <linux/module.h>
23 #include <linux/slab.h>
24 #include <linux/ioport.h>
25 #include <linux/mtd/compatmac.h>
26 #include <linux/mtd/mtd.h>
27 #include <linux/mtd/partitions.h>
28 #include <linux/errno.h>
29 #include <linux/pci.h>
30 #include <linux/delay.h>
31 #include <asm/io.h>
33 #include <typedefs.h>
34 #include <osl.h>
35 #include <bcmutils.h>
36 #include <bcmdevs.h>
37 #include <bcmnvram.h>
38 #include <siutils.h>
39 #include <hndpci.h>
40 #include <pcicfg.h>
41 #include <hndsoc.h>
42 #include <sbchipc.h>
43 #include <nflash.h>
45 #ifdef CONFIG_MTD_PARTITIONS
46 extern struct mtd_partition * init_nflash_mtd_partitions(struct mtd_info *mtd, size_t size);
48 struct mtd_partition *nflash_parts;
49 #endif
51 extern struct mutex *partitions_mutex_init(void);
53 struct nflash_mtd {
54 si_t *sih;
55 chipcregs_t *cc;
56 struct mtd_info mtd;
57 struct mtd_erase_region_info region;
58 unsigned char *map;
61 /* Private global state */
62 static struct nflash_mtd nflash;
64 static int
65 _nflash_mtd_read(struct mtd_info *mtd, struct mtd_partition *part,
66 loff_t from, size_t len, size_t *retlen, u_char *buf)
68 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
69 int bytes, ret = 0;
70 uint extra = 0;
71 uchar *tmpbuf = NULL;
72 int size;
73 uint offset, blocksize, mask, blk_offset, off;
74 uint skip_bytes = 0, good_bytes = 0;
75 int blk_idx, i;
76 int need_copy = 0;
77 uchar *ptr = NULL;
79 /* Locate the part */
80 if (!part) {
81 for (i = 0; nflash_parts[i].name; i++) {
82 if (from >= nflash_parts[i].offset &&
83 ((nflash_parts[i+1].name == NULL) || (from < nflash_parts[i+1].offset))) {
84 part = &nflash_parts[i];
85 break;
88 if (!part)
89 return -EINVAL;
91 /* Check address range */
92 if (!len)
93 return 0;
94 if ((from + len) > mtd->size)
95 return -EINVAL;
96 offset = from;
97 if ((offset & (NFL_SECTOR_SIZE - 1)) != 0) {
98 extra = offset & (NFL_SECTOR_SIZE - 1);
99 offset -= extra;
100 len += extra;
101 need_copy = 1;
103 size = (len + (NFL_SECTOR_SIZE - 1)) & ~(NFL_SECTOR_SIZE - 1);
104 if (size != len)
105 need_copy = 1;
106 if (!need_copy) {
107 ptr = buf;
108 } else {
109 tmpbuf = (uchar *)kmalloc(size, GFP_KERNEL);
110 ptr = tmpbuf;
113 blocksize = mtd->erasesize;
114 mask = blocksize - 1;
115 blk_offset = offset & ~mask;
116 good_bytes = part->offset & ~mask;
117 /* Check and skip bad blocks */
118 for (blk_idx = good_bytes/blocksize; blk_idx < mtd->eraseregions->numblocks; blk_idx++) {
119 if ((nflash->map[blk_idx] != 0) ||
120 (nflash_checkbadb(nflash->sih, nflash->cc, (blocksize*blk_idx)) != 0)) {
121 skip_bytes += blocksize;
122 nflash->map[blk_idx] = 1;
123 } else {
124 if (good_bytes == blk_offset)
125 break;
126 good_bytes += blocksize;
129 if (blk_idx == mtd->eraseregions->numblocks) {
130 ret = -EINVAL;
131 goto done;
133 blk_offset = blocksize * blk_idx;
134 *retlen = 0;
135 while (len > 0) {
136 off = offset + skip_bytes;
138 /* Check and skip bad blocks */
139 if (off >= (blk_offset + blocksize)) {
140 blk_offset += blocksize;
141 blk_idx++;
142 while (((nflash->map[blk_idx] != 0) ||
143 (nflash_checkbadb(nflash->sih, nflash->cc, blk_offset) != 0)) &&
144 (blk_offset < mtd->size)) {
145 skip_bytes += blocksize;
146 nflash->map[blk_idx] = 1;
147 blk_offset += blocksize;
148 blk_idx++;
150 if (blk_offset >= mtd->size) {
151 ret = -EINVAL;
152 goto done;
154 off = offset + skip_bytes;
157 if ((bytes = nflash_read(nflash->sih, nflash->cc, off, NFL_SECTOR_SIZE, ptr)) < 0) {
158 ret = bytes;
159 goto done;
161 if (bytes > len)
162 bytes = len;
163 offset += bytes;
164 len -= bytes;
165 ptr += bytes;
166 *retlen += bytes;
169 done:
170 if (tmpbuf) {
171 *retlen -= extra;
172 memcpy(buf, tmpbuf+extra, *retlen);
173 kfree(tmpbuf);
175 return ret;
178 static int
179 nflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
181 int ret;
183 mutex_lock(mtd->mutex);
184 ret = _nflash_mtd_read(mtd, NULL, from, len, retlen, buf);
185 mutex_unlock(mtd->mutex);
187 return ret;
190 static int
191 nflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
193 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
194 int bytes, ret = 0;
195 struct mtd_partition *part = NULL;
196 u_char *block = NULL;
197 u_char *ptr = (u_char *)buf;
198 uint offset, blocksize, mask, blk_offset, off;
199 uint skip_bytes = 0, good_bytes = 0;
200 int blk_idx, i;
201 int read_len, write_len, copy_len = 0;
202 loff_t from = to;
203 u_char *write_ptr;
204 int docopy = 1;
206 /* Locate the part */
207 for (i = 0; nflash_parts[i].name; i++) {
208 if (to >= nflash_parts[i].offset &&
209 ((nflash_parts[i+1].name == NULL) ||
210 (to < (nflash_parts[i].offset + nflash_parts[i].size)))) {
211 part = &nflash_parts[i];
212 break;
215 if (!part)
216 return -EINVAL;
217 /* Check address range */
218 if (!len)
219 return 0;
220 if ((to + len) > (part->offset + part->size))
221 return -EINVAL;
222 offset = to;
223 blocksize = mtd->erasesize;
224 if (!(block = kmalloc(blocksize, GFP_KERNEL)))
225 return -ENOMEM;
227 mutex_lock(mtd->mutex);
229 mask = blocksize - 1;
230 /* Check and skip bad blocks */
231 blk_offset = offset & ~mask;
232 good_bytes = part->offset & ~mask;
233 for (blk_idx = good_bytes/blocksize; blk_idx < (part->offset+part->size)/blocksize;
234 blk_idx++) {
235 if ((nflash->map[blk_idx] != 0) ||
236 (nflash_checkbadb(nflash->sih, nflash->cc, (blocksize*blk_idx)) != 0)) {
237 skip_bytes += blocksize;
238 nflash->map[blk_idx] = 1;
239 } else {
240 if (good_bytes == blk_offset)
241 break;
242 good_bytes += blocksize;
245 if (blk_idx == (part->offset+part->size)/blocksize) {
246 ret = -EINVAL;
247 goto done;
249 blk_offset = blocksize * blk_idx;
250 /* Backup and erase one block at a time */
251 *retlen = 0;
252 while (len) {
253 if (docopy) {
254 /* Align offset */
255 from = offset & ~mask;
256 /* Copy existing data into holding block if necessary */
257 if (((offset & (blocksize-1)) != 0) || (len < blocksize)) {
258 ret = _nflash_mtd_read(mtd, part, from, blocksize,
259 &read_len, block);
260 if (ret)
261 goto done;
262 if (read_len != blocksize) {
263 ret = -EINVAL;
264 goto done;
267 /* Copy input data into holding block */
268 copy_len = min(len, blocksize - (offset & mask));
269 memcpy(block + (offset & mask), ptr, copy_len);
271 off = (uint) from + skip_bytes;
272 /* Erase block */
273 if ((ret = nflash_erase(nflash->sih, nflash->cc, off)) < 0) {
274 nflash_mark_badb(nflash->sih, nflash->cc, off);
275 nflash->map[blk_idx] = 1;
276 skip_bytes += blocksize;
277 docopy = 0;
279 else {
280 /* Write holding block */
281 write_ptr = block;
282 write_len = blocksize;
283 while (write_len) {
284 if ((bytes = nflash_write(nflash->sih, nflash->cc,
285 (uint) from + skip_bytes, (uint) write_len,
286 (uchar *) write_ptr)) < 0) {
287 nflash_mark_badb(nflash->sih, nflash->cc, off);
288 nflash->map[blk_idx] = 1;
289 skip_bytes += blocksize;
290 docopy = 0;
291 break;
293 from += bytes;
294 write_len -= bytes;
295 write_ptr += bytes;
296 docopy = 1;
298 if (docopy) {
299 offset += copy_len;
300 len -= copy_len;
301 ptr += copy_len;
302 *retlen += copy_len;
305 /* Check and skip bad blocks */
306 if (len) {
307 blk_offset += blocksize;
308 blk_idx++;
309 while (((nflash->map[blk_idx] != 0) ||
310 (nflash_checkbadb(nflash->sih, nflash->cc, blk_offset) != 0)) &&
311 (blk_offset < (part->offset+part->size))) {
312 skip_bytes += blocksize;
313 nflash->map[blk_idx] = 1;
314 blk_offset += blocksize;
315 blk_idx++;
317 if (blk_offset >= (part->offset+part->size)) {
318 ret = -EINVAL;
319 goto done;
323 done:
324 mutex_unlock(mtd->mutex);
326 if (block)
327 kfree(block);
328 return ret;
331 static int
332 nflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
334 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
335 struct mtd_partition *part = NULL;
336 int i, ret = 0;
337 uint addr, len, blocksize;
338 uint part_start_blk, part_end_blk;
339 uint blknum, new_addr, erase_blknum;
341 addr = erase->addr;
342 len = erase->len;
344 blocksize = mtd->erasesize;
346 /* Check address range */
347 if (!len)
348 return 0;
350 if ((addr + len) > mtd->size)
351 return -EINVAL;
353 if (addr & (blocksize - 1))
354 return -EINVAL;
356 /* Locate the part */
357 for (i = 0; nflash_parts[i].name; i++) {
358 if (addr >= nflash_parts[i].offset &&
359 ((addr + len) <= (nflash_parts[i].offset + nflash_parts[i].size))) {
360 part = &nflash_parts[i];
361 break;
365 if (!part)
366 return -EINVAL;
368 mutex_lock(mtd->mutex);
370 /* Find the effective start block address to erase */
371 part_start_blk = (part->offset & ~(blocksize - 1)) / blocksize;
372 part_end_blk = ROUNDUP((part->offset + part->size), blocksize) / blocksize;
374 new_addr = part_start_blk * blocksize;
375 blknum = (addr / blocksize) - part_start_blk;
377 for (i = part_start_blk; i < part_end_blk; i++) {
378 if ((nflash->map[i] != 0) ||
379 (nflash_checkbadb(nflash->sih, nflash->cc, i * blocksize) != 0)) {
380 nflash->map[i] = 1;
381 new_addr += blocksize;
382 continue;
383 } else {
384 if (blknum) {
385 new_addr += blocksize;
386 blknum--;
390 if (blknum == 0)
391 break;
394 /* Erase the blocks from the new block address */
395 erase_blknum = ROUNDUP(len, blocksize) / blocksize;
397 #if 0
398 if ((new_addr + (erase_blknum * blocksize)) > (part->offset + part->size)) {
399 ret = -EINVAL;
400 goto done;
402 #endif
404 for (i = new_addr; erase_blknum; i += blocksize) {
405 if (i >= (part->offset + part->size)) {
406 /* It indicates bad block, but do NOT treat it as fail */
407 printk(KERN_WARNING "%s(%d):end of part %s (0x%x)!\n",
408 __FUNCTION__, __LINE__, part->name, i);
409 ret = 0;
410 goto done;
413 /* Skip bad block erase */
414 if ((nflash->map[i / blocksize] != 0) ||
415 (nflash_checkbadb(nflash->sih, nflash->cc, i) != 0)) {
416 nflash->map[i / blocksize] = 1;
417 continue;
420 if ((ret = nflash_erase(nflash->sih, nflash->cc, i)) < 0) {
421 nflash_mark_badb(nflash->sih, nflash->cc, i);
422 nflash->map[i / blocksize] = 1;
423 } else {
424 erase_blknum--;
428 done:
429 /* Set erase status */
430 if (ret)
431 erase->state = MTD_ERASE_FAILED;
432 else
433 erase->state = MTD_ERASE_DONE;
435 mutex_unlock(mtd->mutex);
437 /* Call erase callback */
438 if (erase->callback)
439 erase->callback(erase);
441 return ret;
444 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
445 #define nflash_mtd_init init_module
446 #define nflash_mtd_exit cleanup_module
447 #endif
449 static int __init
450 nflash_mtd_init(void)
452 int ret = 0;
453 struct nflash *info;
454 struct pci_dev *dev = NULL;
455 #ifdef CONFIG_MTD_PARTITIONS
456 struct mtd_partition *parts;
457 int i;
458 #endif
460 list_for_each_entry(dev, &((pci_find_bus(0, 0))->devices), bus_list) {
461 if ((dev != NULL) && (dev->device == CC_CORE_ID))
462 break;
465 if (dev == NULL) {
466 printk(KERN_ERR "nflash: chipcommon not found\n");
467 return -ENODEV;
470 memset(&nflash, 0, sizeof(struct nflash_mtd));
472 /* attach to the backplane */
473 if (!(nflash.sih = si_kattach(SI_OSH))) {
474 printk(KERN_ERR "nflash: error attaching to backplane\n");
475 ret = -EIO;
476 goto fail;
479 /* Map registers and flash base */
480 if (!(nflash.cc = ioremap_nocache(
481 pci_resource_start(dev, 0),
482 pci_resource_len(dev, 0)))) {
483 printk(KERN_ERR "nflash: error mapping registers\n");
484 ret = -EIO;
485 goto fail;
488 /* Initialize serial flash access */
489 if (!(info = nflash_init(nflash.sih, nflash.cc))) {
490 printk(KERN_ERR "nflash: found no supported devices\n");
491 ret = -ENODEV;
492 goto fail;
495 /* Setup region info */
496 nflash.region.offset = 0;
497 nflash.region.erasesize = info->blocksize;
498 nflash.region.numblocks = info->numblocks;
499 if (nflash.region.erasesize > nflash.mtd.erasesize)
500 nflash.mtd.erasesize = nflash.region.erasesize;
501 /* At most 2GB is supported */
502 nflash.mtd.size = (info->size >= (1 << 11)) ? (1 << 31) : (info->size << 20);
503 nflash.mtd.numeraseregions = 1;
504 nflash.map = (unsigned char *)kmalloc(info->numblocks, GFP_KERNEL);
505 if (nflash.map)
506 memset(nflash.map, 0, info->numblocks);
508 /* Register with MTD */
509 nflash.mtd.name = "nflash";
510 nflash.mtd.type = MTD_NANDFLASH;
511 nflash.mtd.flags = MTD_CAP_NANDFLASH;
512 nflash.mtd.eraseregions = &nflash.region;
513 nflash.mtd.erase = nflash_mtd_erase;
514 nflash.mtd.read = nflash_mtd_read;
515 nflash.mtd.write = nflash_mtd_write;
516 nflash.mtd.writesize = NFL_SECTOR_SIZE;
517 nflash.mtd.priv = &nflash;
518 nflash.mtd.owner = THIS_MODULE;
519 nflash.mtd.mutex = partitions_mutex_init();
520 if (!nflash.mtd.mutex)
521 return -ENOMEM;
523 /* Scan bad block */
524 mutex_lock(nflash.mtd.mutex);
525 for (i = 0; i < info->numblocks; i++) {
526 if (nflash_checkbadb(nflash.sih, nflash.cc, (i * info->blocksize)) != 0) {
527 nflash.map[i] = 1;
530 mutex_unlock(nflash.mtd.mutex);
532 #ifdef CONFIG_MTD_PARTITIONS
533 parts = init_nflash_mtd_partitions(&nflash.mtd, nflash.mtd.size);
534 if (!parts)
535 goto fail;
536 for (i = 0; parts[i].name; i++);
537 ret = add_mtd_partitions(&nflash.mtd, parts, i);
538 if (ret) {
539 printk(KERN_ERR "nflash: add_mtd failed\n");
540 goto fail;
542 nflash_parts = parts;
543 #endif
544 return 0;
546 fail:
547 if (nflash.cc)
548 iounmap((void *) nflash.cc);
549 if (nflash.sih)
550 si_detach(nflash.sih);
551 return ret;
554 static void __exit
555 nflash_mtd_exit(void)
557 #ifdef CONFIG_MTD_PARTITIONS
558 del_mtd_partitions(&nflash.mtd);
559 #else
560 del_mtd_device(&nflash.mtd);
561 #endif
562 iounmap((void *) nflash.cc);
563 si_detach(nflash.sih);
566 module_init(nflash_mtd_init);
567 module_exit(nflash_mtd_exit);