RT-AC56 3.0.0.4.374.37 core
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / drivers / mtd / 47xxnand / bcm_nflash.c
blob72dbd62f082959ac16b0e104b30488bdb5c71a69
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2012, 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/version.h>
23 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
24 #include <linux/config.h>
25 #endif
27 #include <linux/reciprocal_div.h>
28 #include <linux/module.h>
29 #include <linux/slab.h>
30 #include <linux/ioport.h>
31 #include <linux/mtd/mtd.h>
32 #include <linux/mtd/partitions.h>
34 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
35 #include <linux/mtd/compatmac.h>
36 #else
37 /* #include <linux/mtd/nand.h> */
38 #endif
40 #include <linux/errno.h>
41 #include <linux/pci.h>
42 #include <linux/delay.h>
43 #include <asm/io.h>
45 #include <typedefs.h>
46 #include <osl.h>
47 #include <bcmutils.h>
48 #include <bcmdevs.h>
49 #include <bcmnvram.h>
50 #include <siutils.h>
51 #include <hndpci.h>
52 #include <pcicfg.h>
53 #include <hndsoc.h>
54 #include <sbchipc.h>
55 #include <hndnand.h>
57 #ifdef CONFIG_MTD_PARTITIONS
58 extern struct mtd_partition *
59 init_nflash_mtd_partitions(hndnand_t *nfl, struct mtd_info *mtd, size_t size);
61 struct mtd_partition *nflash_parts;
62 #endif
64 /* Mutexing is version-dependent */
65 extern void *partitions_lock_init(void);
66 #define NFLASH_LOCK(lock) if (lock) spin_lock(lock)
67 #define NFLASH_UNLOCK(lock) if (lock) spin_unlock(lock)
69 struct nflash_mtd {
70 si_t *sih;
71 hndnand_t *nfl;
72 struct mtd_info mtd;
73 struct mtd_erase_region_info region;
74 unsigned char *map;
77 /* Private global state */
78 static struct nflash_mtd nflash;
80 static int
81 _nflash_mtd_read(struct mtd_info *mtd, struct mtd_partition *part,
82 loff_t from, size_t len, size_t *retlen, u_char *buf)
84 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
85 int bytes, ret = 0;
86 uint extra = 0;
87 uchar *tmpbuf = NULL;
88 int size;
89 uint offset, blocksize, mask, blk_offset, off;
90 uint skip_bytes = 0, good_bytes = 0;
91 int blk_idx, i;
92 int need_copy = 0;
93 uchar *ptr = NULL;
95 /* Locate the part */
96 if (!part) {
97 for (i = 0; nflash_parts[i].name; i++) {
98 if (from >= nflash_parts[i].offset &&
99 ((nflash_parts[i+1].name == NULL) || (from < nflash_parts[i+1].offset))) {
100 part = &nflash_parts[i];
101 break;
104 if (!part)
105 return -EINVAL;
107 /* Check address range */
108 if (!len)
109 return 0;
110 if ((from + len) > mtd->size)
111 return -EINVAL;
112 offset = from;
113 if ((offset & (NFL_SECTOR_SIZE - 1)) != 0) {
114 extra = offset & (NFL_SECTOR_SIZE - 1);
115 offset -= extra;
116 len += extra;
117 need_copy = 1;
119 size = (len + (NFL_SECTOR_SIZE - 1)) & ~(NFL_SECTOR_SIZE - 1);
120 if (size != len)
121 need_copy = 1;
122 if (!need_copy) {
123 ptr = buf;
124 } else {
125 tmpbuf = (uchar *)kmalloc(size, GFP_KERNEL);
126 ptr = tmpbuf;
129 blocksize = mtd->erasesize;
130 mask = blocksize - 1;
131 blk_offset = offset & ~mask;
132 good_bytes = part->offset & ~mask;
133 /* Check and skip bad blocks */
134 for (blk_idx = good_bytes/blocksize; blk_idx < mtd->eraseregions->numblocks; blk_idx++) {
135 if (nflash->map[blk_idx] != 0) {
136 skip_bytes += blocksize;
137 } else {
138 if (good_bytes == blk_offset)
139 break;
140 good_bytes += blocksize;
143 if (blk_idx == mtd->eraseregions->numblocks) {
144 ret = -EINVAL;
145 goto done;
147 blk_offset = blocksize * blk_idx;
148 *retlen = 0;
149 while (len > 0) {
150 off = offset + skip_bytes;
152 /* Check and skip bad blocks */
153 if (off >= (blk_offset + blocksize)) {
154 blk_offset += blocksize;
155 blk_idx++;
156 while ((nflash->map[blk_idx] != 0) &&
157 (blk_offset < mtd->size)) {
158 skip_bytes += blocksize;
159 blk_offset += blocksize;
160 blk_idx++;
162 if (blk_offset >= mtd->size) {
163 ret = -EINVAL;
164 goto done;
166 off = offset + skip_bytes;
169 if ((bytes = hndnand_read(nflash->nfl,
170 off, NFL_SECTOR_SIZE, ptr)) < 0) {
171 ret = bytes;
172 goto done;
174 if (bytes > len)
175 bytes = len;
176 offset += bytes;
177 len -= bytes;
178 ptr += bytes;
179 *retlen += bytes;
182 done:
183 if (tmpbuf) {
184 *retlen -= extra;
185 memcpy(buf, tmpbuf+extra, *retlen);
186 kfree(tmpbuf);
188 return ret;
191 static int
192 nflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
194 int ret;
196 NFLASH_LOCK(mtd->mlock);
197 ret = _nflash_mtd_read(mtd, NULL, from, len, retlen, buf);
198 NFLASH_UNLOCK(mtd->mlock);
200 return ret;
203 static int
204 nflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
206 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
207 int bytes, ret = 0;
208 struct mtd_partition *part = NULL;
209 u_char *block = NULL;
210 u_char *ptr = (u_char *)buf;
211 uint offset, blocksize, mask, blk_offset, off;
212 uint skip_bytes = 0, good_bytes = 0;
213 int blk_idx, i;
214 int read_len, write_len, copy_len = 0;
215 loff_t from = to;
216 u_char *write_ptr;
217 int docopy = 1;
218 uint r_blocksize, part_blk_start, part_blk_end;
220 /* Locate the part */
221 for (i = 0; nflash_parts[i].name; i++) {
222 if (to >= nflash_parts[i].offset &&
223 ((nflash_parts[i+1].name == NULL) ||
224 (to < (nflash_parts[i].offset + nflash_parts[i].size)))) {
225 part = &nflash_parts[i];
226 break;
229 if (!part)
230 return -EINVAL;
231 /* Check address range */
232 if (!len)
233 return 0;
234 if ((to + len) > (part->offset + part->size))
235 return -EINVAL;
236 offset = to;
237 blocksize = mtd->erasesize;
238 r_blocksize = reciprocal_value(blocksize);
240 if (!(block = kmalloc(blocksize, GFP_KERNEL)))
241 return -ENOMEM;
243 NFLASH_LOCK(mtd->mlock);
245 mask = blocksize - 1;
246 /* Check and skip bad blocks */
247 blk_offset = offset & ~mask;
248 good_bytes = part->offset & ~mask;
249 part_blk_start = reciprocal_divide(good_bytes, r_blocksize);
250 part_blk_end = reciprocal_divide(part->offset + part->size, r_blocksize);
252 for (blk_idx = part_blk_start; blk_idx < part_blk_end; blk_idx++) {
253 if (nflash->map[blk_idx] != 0) {
254 skip_bytes += blocksize;
255 } else {
256 if (good_bytes == blk_offset)
257 break;
258 good_bytes += blocksize;
261 if (blk_idx == part_blk_end) {
262 ret = -EINVAL;
263 goto done;
265 blk_offset = blocksize * blk_idx;
266 /* Backup and erase one block at a time */
267 *retlen = 0;
268 while (len) {
269 if (docopy) {
270 /* Align offset */
271 from = offset & ~mask;
272 /* Copy existing data into holding block if necessary */
273 if (((offset & (blocksize-1)) != 0) || (len < blocksize)) {
274 ret = _nflash_mtd_read(mtd, part, from, blocksize,
275 &read_len, block);
276 if (ret)
277 goto done;
278 if (read_len != blocksize) {
279 ret = -EINVAL;
280 goto done;
283 /* Copy input data into holding block */
284 copy_len = min(len, blocksize - (offset & mask));
285 memcpy(block + (offset & mask), ptr, copy_len);
287 off = (uint) from + skip_bytes;
288 /* Erase block */
289 if ((ret = hndnand_erase(nflash->nfl, off)) < 0) {
290 hndnand_mark_badb(nflash->nfl, off);
291 nflash->map[blk_idx] = 1;
292 skip_bytes += blocksize;
293 docopy = 0;
295 else {
296 /* Write holding block */
297 write_ptr = block;
298 write_len = blocksize;
299 while (write_len) {
300 if ((bytes = hndnand_write(nflash->nfl,
301 (uint) from + skip_bytes, (uint) write_len,
302 (uchar *) write_ptr)) < 0) {
303 hndnand_mark_badb(nflash->nfl, off);
304 nflash->map[blk_idx] = 1;
305 skip_bytes += blocksize;
306 docopy = 0;
307 break;
309 from += bytes;
310 write_len -= bytes;
311 write_ptr += bytes;
312 docopy = 1;
314 if (docopy) {
315 offset += copy_len;
316 len -= copy_len;
317 ptr += copy_len;
318 *retlen += copy_len;
321 /* Check and skip bad blocks */
322 if (len) {
323 blk_offset += blocksize;
324 blk_idx++;
325 while ((nflash->map[blk_idx] != 0) &&
326 (blk_offset < (part->offset+part->size))) {
327 skip_bytes += blocksize;
328 blk_offset += blocksize;
329 blk_idx++;
331 if (blk_offset >= (part->offset+part->size)) {
332 ret = -EINVAL;
333 goto done;
337 done:
338 NFLASH_UNLOCK(mtd->mlock);
340 if (block)
341 kfree(block);
342 return ret;
345 static int
346 nflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
348 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
349 struct mtd_partition *part = NULL;
350 int i, ret = 0;
351 uint addr, len, blocksize;
352 uint part_start_blk, part_end_blk;
353 uint blknum, new_addr, erase_blknum;
354 uint reciprocal_blocksize;
356 addr = erase->addr;
357 len = erase->len;
359 blocksize = mtd->erasesize;
360 reciprocal_blocksize = reciprocal_value(blocksize);
362 /* Check address range */
363 if (!len)
364 return 0;
366 if ((addr + len) > mtd->size)
367 return -EINVAL;
369 if (addr & (blocksize - 1))
370 return -EINVAL;
372 /* Locate the part */
373 for (i = 0; nflash_parts[i].name; i++) {
374 if (addr >= nflash_parts[i].offset &&
375 ((addr + len) <= (nflash_parts[i].offset + nflash_parts[i].size))) {
376 part = &nflash_parts[i];
377 break;
381 if (!part)
382 return -EINVAL;
384 NFLASH_LOCK(mtd->mlock);
386 /* Find the effective start block address to erase */
387 part_start_blk = reciprocal_divide(part->offset & ~(blocksize-1),
388 reciprocal_blocksize);
389 part_end_blk = reciprocal_divide(((part->offset + part->size) + (blocksize-1)),
390 reciprocal_blocksize);
392 new_addr = part_start_blk * blocksize;
393 /* The block number to be skipped relative to the start address of
394 * the MTD partition
396 blknum = reciprocal_divide(addr - new_addr, reciprocal_blocksize);
398 for (i = part_start_blk; (i < part_end_blk) && (blknum > 0); i++) {
399 if (nflash->map[i] != 0) {
400 new_addr += blocksize;
401 } else {
402 new_addr += blocksize;
403 blknum--;
407 /* Erase the blocks from the new block address */
408 erase_blknum = reciprocal_divide(len + (blocksize-1), reciprocal_blocksize);
410 if ((new_addr + (erase_blknum * blocksize)) > (part->offset + part->size)) {
411 ret = -EINVAL;
412 goto done;
415 for (i = new_addr; erase_blknum; i += blocksize) {
416 /* Skip bad block erase */
417 uint j = reciprocal_divide(i, reciprocal_blocksize);
418 if (nflash->map[j] != 0) {
419 continue;
422 if ((ret = hndnand_erase(nflash->nfl, i)) < 0) {
423 hndnand_mark_badb(nflash->nfl, i);
424 nflash->map[i / blocksize] = 1;
425 } else {
426 erase_blknum--;
430 done:
431 /* Set erase status */
432 if (ret)
433 erase->state = MTD_ERASE_FAILED;
434 else
435 erase->state = MTD_ERASE_DONE;
437 NFLASH_UNLOCK(mtd->mlock);
439 /* Call erase callback */
440 if (erase->callback)
441 erase->callback(erase);
443 return ret;
446 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
447 #define nflash_mtd_init init_module
448 #define nflash_mtd_exit cleanup_module
449 #endif
451 static int __init
452 nflash_mtd_init(void)
454 int ret = 0;
455 hndnand_t *info;
456 #ifdef CONFIG_MTD_PARTITIONS
457 struct mtd_partition *parts;
458 int i;
459 #endif
461 memset(&nflash, 0, sizeof(struct nflash_mtd));
463 /* attach to the backplane */
464 if (!(nflash.sih = si_kattach(SI_OSH))) {
465 printk(KERN_ERR "nflash: error attaching to backplane\n");
466 ret = -EIO;
467 goto fail;
470 /* Initialize serial flash access */
471 if (!(info = hndnand_init(nflash.sih))) {
472 printk(KERN_ERR "nflash: found no supported devices\n");
473 ret = -ENODEV;
474 goto fail;
476 nflash.nfl = info;
478 /* Setup region info */
479 nflash.region.offset = 0;
480 nflash.region.erasesize = info->blocksize;
481 nflash.region.numblocks = info->numblocks;
482 if (nflash.region.erasesize > nflash.mtd.erasesize)
483 nflash.mtd.erasesize = nflash.region.erasesize;
484 /* At most 2GB is supported */
485 nflash.mtd.size = (info->size >= (1 << 11)) ? (1 << 31) : (info->size << 20);
486 nflash.mtd.numeraseregions = 1;
487 nflash.map = (unsigned char *)kmalloc(info->numblocks, GFP_KERNEL);
488 if (nflash.map)
489 memset(nflash.map, 0, info->numblocks);
491 /* Register with MTD */
492 nflash.mtd.name = "nflash";
493 nflash.mtd.type = MTD_NANDFLASH;
494 nflash.mtd.flags = MTD_CAP_NANDFLASH;
495 nflash.mtd.eraseregions = &nflash.region;
496 nflash.mtd.erase = nflash_mtd_erase;
497 nflash.mtd.read = nflash_mtd_read;
498 nflash.mtd.write = nflash_mtd_write;
499 nflash.mtd.writesize = NFL_SECTOR_SIZE;
500 nflash.mtd.priv = &nflash;
501 nflash.mtd.owner = THIS_MODULE;
502 nflash.mtd.mlock = partitions_lock_init();
503 if (!nflash.mtd.mlock)
504 return -ENOMEM;
506 /* Scan bad block */
507 NFLASH_LOCK(nflash.mtd.mlock);
508 for (i = 0; i < info->numblocks; i++) {
509 if (hndnand_checkbadb(nflash.nfl, (i * info->blocksize)) != 0) {
510 nflash.map[i] = 1;
513 NFLASH_UNLOCK(nflash.mtd.mlock);
515 #ifdef CONFIG_MTD_PARTITIONS
516 parts = init_nflash_mtd_partitions(info, &nflash.mtd, nflash.mtd.size);
517 if (!parts)
518 goto fail;
520 for (i = 0; parts[i].name; i++)
523 ret = add_mtd_partitions(&nflash.mtd, parts, i);
524 if (ret) {
525 printk(KERN_ERR "nflash: add_mtd failed\n");
526 goto fail;
528 nflash_parts = parts;
529 #endif
530 return 0;
532 fail:
533 return ret;
536 static void __exit
537 nflash_mtd_exit(void)
539 #ifdef CONFIG_MTD_PARTITIONS
540 del_mtd_partitions(&nflash.mtd);
541 #else
542 del_mtd_device(&nflash.mtd);
543 #endif
546 module_init(nflash_mtd_init);
547 module_exit(nflash_mtd_exit);