BCM WL 6.30.102.9 (r366174)
[tomato.git] / release / src-rt / linux / linux-2.6 / drivers / mtd / devices / bcm_nflash.c
blobd44c52cd9ca79021021651c2852c7f32539146b9
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2011, 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 skip_bytes += blocksize;
121 } else {
122 if (good_bytes == blk_offset)
123 break;
124 good_bytes += blocksize;
127 if (blk_idx == mtd->eraseregions->numblocks) {
128 ret = -EINVAL;
129 goto done;
131 blk_offset = blocksize * blk_idx;
132 *retlen = 0;
133 while (len > 0) {
134 off = offset + skip_bytes;
136 /* Check and skip bad blocks */
137 if (off >= (blk_offset + blocksize)) {
138 blk_offset += blocksize;
139 blk_idx++;
140 while ((nflash->map[blk_idx] != 0) &&
141 (blk_offset < mtd->size)) {
142 skip_bytes += blocksize;
143 blk_offset += blocksize;
144 blk_idx++;
146 if (blk_offset >= mtd->size) {
147 ret = -EINVAL;
148 goto done;
150 off = offset + skip_bytes;
153 if ((bytes = nflash_read(nflash->sih, nflash->cc, off, NFL_SECTOR_SIZE, ptr)) < 0) {
154 ret = bytes;
155 goto done;
157 if (bytes > len)
158 bytes = len;
159 offset += bytes;
160 len -= bytes;
161 ptr += bytes;
162 *retlen += bytes;
165 done:
166 if (tmpbuf) {
167 *retlen -= extra;
168 memcpy(buf, tmpbuf+extra, *retlen);
169 kfree(tmpbuf);
171 return ret;
174 static int
175 nflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
177 int ret;
179 mutex_lock(mtd->mutex);
180 ret = _nflash_mtd_read(mtd, NULL, from, len, retlen, buf);
181 mutex_unlock(mtd->mutex);
183 return ret;
186 static int
187 nflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
189 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
190 int bytes, ret = 0;
191 struct mtd_partition *part = NULL;
192 u_char *block = NULL;
193 u_char *ptr = (u_char *)buf;
194 uint offset, blocksize, mask, blk_offset, off;
195 uint skip_bytes = 0, good_bytes = 0;
196 int blk_idx, i;
197 int read_len, write_len, copy_len = 0;
198 loff_t from = to;
199 u_char *write_ptr;
200 int docopy = 1;
202 /* Locate the part */
203 for (i = 0; nflash_parts[i].name; i++) {
204 if (to >= nflash_parts[i].offset &&
205 ((nflash_parts[i+1].name == NULL) ||
206 (to < (nflash_parts[i].offset + nflash_parts[i].size)))) {
207 part = &nflash_parts[i];
208 break;
211 if (!part)
212 return -EINVAL;
213 /* Check address range */
214 if (!len)
215 return 0;
216 if ((to + len) > (part->offset + part->size))
217 return -EINVAL;
218 offset = to;
219 blocksize = mtd->erasesize;
220 if (!(block = kmalloc(blocksize, GFP_KERNEL)))
221 return -ENOMEM;
223 mutex_lock(mtd->mutex);
225 mask = blocksize - 1;
226 /* Check and skip bad blocks */
227 blk_offset = offset & ~mask;
228 good_bytes = part->offset & ~mask;
229 for (blk_idx = good_bytes/blocksize; blk_idx < (part->offset+part->size)/blocksize;
230 blk_idx++) {
231 if (nflash->map[blk_idx] != 0) {
232 skip_bytes += blocksize;
233 } else {
234 if (good_bytes == blk_offset)
235 break;
236 good_bytes += blocksize;
239 if (blk_idx == (part->offset+part->size)/blocksize) {
240 ret = -EINVAL;
241 goto done;
243 blk_offset = blocksize * blk_idx;
244 /* Backup and erase one block at a time */
245 *retlen = 0;
246 while (len) {
247 if (docopy) {
248 /* Align offset */
249 from = offset & ~mask;
250 /* Copy existing data into holding block if necessary */
251 if (((offset & (blocksize-1)) != 0) || (len < blocksize)) {
252 ret = _nflash_mtd_read(mtd, part, from, blocksize,
253 &read_len, block);
254 if (ret)
255 goto done;
256 if (read_len != blocksize) {
257 ret = -EINVAL;
258 goto done;
261 /* Copy input data into holding block */
262 copy_len = min(len, blocksize - (offset & mask));
263 memcpy(block + (offset & mask), ptr, copy_len);
265 off = (uint) from + skip_bytes;
266 /* Erase block */
267 if ((ret = nflash_erase(nflash->sih, nflash->cc, off)) < 0) {
268 nflash_mark_badb(nflash->sih, nflash->cc, off);
269 nflash->map[blk_idx] = 1;
270 skip_bytes += blocksize;
271 docopy = 0;
273 else {
274 /* Write holding block */
275 write_ptr = block;
276 write_len = blocksize;
277 while (write_len) {
278 if ((bytes = nflash_write(nflash->sih, nflash->cc,
279 (uint) from + skip_bytes, (uint) write_len,
280 (uchar *) write_ptr)) < 0) {
281 nflash_mark_badb(nflash->sih, nflash->cc, off);
282 nflash->map[blk_idx] = 1;
283 skip_bytes += blocksize;
284 docopy = 0;
285 break;
287 from += bytes;
288 write_len -= bytes;
289 write_ptr += bytes;
290 docopy = 1;
292 if (docopy) {
293 offset += copy_len;
294 len -= copy_len;
295 ptr += copy_len;
296 *retlen += copy_len;
299 /* Check and skip bad blocks */
300 if (len) {
301 blk_offset += blocksize;
302 blk_idx++;
303 while ((nflash->map[blk_idx] != 0) &&
304 (blk_offset < (part->offset+part->size))) {
305 skip_bytes += blocksize;
306 blk_offset += blocksize;
307 blk_idx++;
309 if (blk_offset >= (part->offset+part->size)) {
310 ret = -EINVAL;
311 goto done;
315 done:
316 mutex_unlock(mtd->mutex);
318 if (block)
319 kfree(block);
320 return ret;
323 static int
324 nflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
326 struct nflash_mtd *nflash = (struct nflash_mtd *) mtd->priv;
327 struct mtd_partition *part = NULL;
328 int i, ret = 0;
329 uint addr, len, blocksize;
330 uint part_start_blk, part_end_blk;
331 uint blknum, new_addr, erase_blknum;
333 addr = erase->addr;
334 len = erase->len;
336 blocksize = mtd->erasesize;
338 /* Check address range */
339 if (!len)
340 return 0;
342 if ((addr + len) > mtd->size)
343 return -EINVAL;
345 if (addr & (blocksize - 1))
346 return -EINVAL;
348 /* Locate the part */
349 for (i = 0; nflash_parts[i].name; i++) {
350 if (addr >= nflash_parts[i].offset &&
351 ((addr + len) <= (nflash_parts[i].offset + nflash_parts[i].size))) {
352 part = &nflash_parts[i];
353 break;
357 if (!part)
358 return -EINVAL;
360 mutex_lock(mtd->mutex);
362 /* Find the effective start block address to erase */
363 part_start_blk = (part->offset & ~(blocksize - 1)) / blocksize;
364 part_end_blk = ROUNDUP((part->offset + part->size), blocksize) / blocksize;
366 new_addr = part_start_blk * blocksize;
367 /* The block number to be skipped relative to the start address of
368 * the MTD partition
370 blknum = (addr - new_addr) / blocksize;
372 for (i = part_start_blk; (i < part_end_blk) && (blknum > 0); i++) {
373 if (nflash->map[i] != 0) {
374 new_addr += blocksize;
375 } else {
376 new_addr += blocksize;
377 blknum--;
381 /* Erase the blocks from the new block address */
382 erase_blknum = ROUNDUP(len, blocksize) / blocksize;
384 if ((new_addr + (erase_blknum * blocksize)) > (part->offset + part->size)) {
385 ret = -EINVAL;
386 goto done;
389 for (i = new_addr; erase_blknum; i += blocksize) {
390 /* Skip bad block erase */
391 if (nflash->map[i / blocksize] != 0) {
392 continue;
395 if ((ret = nflash_erase(nflash->sih, nflash->cc, i)) < 0) {
396 nflash_mark_badb(nflash->sih, nflash->cc, i);
397 nflash->map[i / blocksize] = 1;
398 } else {
399 erase_blknum--;
403 done:
404 /* Set erase status */
405 if (ret)
406 erase->state = MTD_ERASE_FAILED;
407 else
408 erase->state = MTD_ERASE_DONE;
410 mutex_unlock(mtd->mutex);
412 /* Call erase callback */
413 if (erase->callback)
414 erase->callback(erase);
416 return ret;
419 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
420 #define nflash_mtd_init init_module
421 #define nflash_mtd_exit cleanup_module
422 #endif
424 static int __init
425 nflash_mtd_init(void)
427 int ret = 0;
428 struct nflash *info;
429 struct pci_dev *dev = NULL;
430 #ifdef CONFIG_MTD_PARTITIONS
431 struct mtd_partition *parts;
432 int i;
433 #endif
435 list_for_each_entry(dev, &((pci_find_bus(0, 0))->devices), bus_list) {
436 if ((dev != NULL) && (dev->device == CC_CORE_ID))
437 break;
440 if (dev == NULL) {
441 printk(KERN_ERR "nflash: chipcommon not found\n");
442 return -ENODEV;
445 memset(&nflash, 0, sizeof(struct nflash_mtd));
447 /* attach to the backplane */
448 if (!(nflash.sih = si_kattach(SI_OSH))) {
449 printk(KERN_ERR "nflash: error attaching to backplane\n");
450 ret = -EIO;
451 goto fail;
454 /* Map registers and flash base */
455 if (!(nflash.cc = ioremap_nocache(
456 pci_resource_start(dev, 0),
457 pci_resource_len(dev, 0)))) {
458 printk(KERN_ERR "nflash: error mapping registers\n");
459 ret = -EIO;
460 goto fail;
463 /* Initialize serial flash access */
464 if (!(info = nflash_init(nflash.sih, nflash.cc))) {
465 printk(KERN_ERR "nflash: found no supported devices\n");
466 ret = -ENODEV;
467 goto fail;
470 /* Setup region info */
471 nflash.region.offset = 0;
472 nflash.region.erasesize = info->blocksize;
473 nflash.region.numblocks = info->numblocks;
474 if (nflash.region.erasesize > nflash.mtd.erasesize)
475 nflash.mtd.erasesize = nflash.region.erasesize;
476 /* At most 2GB is supported */
477 nflash.mtd.size = (info->size >= (1 << 11)) ? (1 << 31) : (info->size << 20);
478 nflash.mtd.numeraseregions = 1;
479 nflash.map = (unsigned char *)kmalloc(info->numblocks, GFP_KERNEL);
480 if (nflash.map)
481 memset(nflash.map, 0, info->numblocks);
483 /* Register with MTD */
484 nflash.mtd.name = "nflash";
485 nflash.mtd.type = MTD_NANDFLASH;
486 nflash.mtd.flags = MTD_CAP_NANDFLASH;
487 nflash.mtd.eraseregions = &nflash.region;
488 nflash.mtd.erase = nflash_mtd_erase;
489 nflash.mtd.read = nflash_mtd_read;
490 nflash.mtd.write = nflash_mtd_write;
491 nflash.mtd.writesize = NFL_SECTOR_SIZE;
492 nflash.mtd.priv = &nflash;
493 nflash.mtd.owner = THIS_MODULE;
494 nflash.mtd.mutex = partitions_mutex_init();
495 if (!nflash.mtd.mutex)
496 return -ENOMEM;
498 /* Scan bad block */
499 mutex_lock(nflash.mtd.mutex);
500 for (i = 0; i < info->numblocks; i++) {
501 if (nflash_checkbadb(nflash.sih, nflash.cc, (i * info->blocksize)) != 0) {
502 nflash.map[i] = 1;
505 mutex_unlock(nflash.mtd.mutex);
507 #ifdef CONFIG_MTD_PARTITIONS
508 parts = init_nflash_mtd_partitions(&nflash.mtd, nflash.mtd.size);
509 if (!parts)
510 goto fail;
511 for (i = 0; parts[i].name; i++);
512 ret = add_mtd_partitions(&nflash.mtd, parts, i);
513 if (ret) {
514 printk(KERN_ERR "nflash: add_mtd failed\n");
515 goto fail;
517 nflash_parts = parts;
518 #endif
519 return 0;
521 fail:
522 if (nflash.cc)
523 iounmap((void *) nflash.cc);
524 if (nflash.sih)
525 si_detach(nflash.sih);
526 return ret;
529 static void __exit
530 nflash_mtd_exit(void)
532 #ifdef CONFIG_MTD_PARTITIONS
533 del_mtd_partitions(&nflash.mtd);
534 #else
535 del_mtd_device(&nflash.mtd);
536 #endif
537 iounmap((void *) nflash.cc);
538 si_detach(nflash.sih);
541 module_init(nflash_mtd_init);
542 module_exit(nflash_mtd_exit);