2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2011, Broadcom Corporation. All Rights Reserved.
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.
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.
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>
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
;
51 extern struct mutex
*partitions_mutex_init(void);
57 struct mtd_erase_region_info region
;
61 /* Private global state */
62 static struct nflash_mtd nflash
;
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
;
73 uint offset
, blocksize
, mask
, blk_offset
, off
;
74 uint skip_bytes
= 0, good_bytes
= 0;
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
];
91 /* Check address range */
94 if ((from
+ len
) > mtd
->size
)
97 if ((offset
& (NFL_SECTOR_SIZE
- 1)) != 0) {
98 extra
= offset
& (NFL_SECTOR_SIZE
- 1);
103 size
= (len
+ (NFL_SECTOR_SIZE
- 1)) & ~(NFL_SECTOR_SIZE
- 1);
109 tmpbuf
= (uchar
*)kmalloc(size
, GFP_KERNEL
);
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
;
122 if (good_bytes
== blk_offset
)
124 good_bytes
+= blocksize
;
127 if (blk_idx
== mtd
->eraseregions
->numblocks
) {
131 blk_offset
= blocksize
* blk_idx
;
134 off
= offset
+ skip_bytes
;
136 /* Check and skip bad blocks */
137 if (off
>= (blk_offset
+ blocksize
)) {
138 blk_offset
+= blocksize
;
140 while ((nflash
->map
[blk_idx
] != 0) &&
141 (blk_offset
< mtd
->size
)) {
142 skip_bytes
+= blocksize
;
143 blk_offset
+= blocksize
;
146 if (blk_offset
>= mtd
->size
) {
150 off
= offset
+ skip_bytes
;
153 if ((bytes
= nflash_read(nflash
->sih
, nflash
->cc
, off
, NFL_SECTOR_SIZE
, ptr
)) < 0) {
168 memcpy(buf
, tmpbuf
+extra
, *retlen
);
175 nflash_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
, size_t *retlen
, u_char
*buf
)
179 mutex_lock(mtd
->mutex
);
180 ret
= _nflash_mtd_read(mtd
, NULL
, from
, len
, retlen
, buf
);
181 mutex_unlock(mtd
->mutex
);
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
;
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;
197 int read_len
, write_len
, copy_len
= 0;
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
];
213 /* Check address range */
216 if ((to
+ len
) > (part
->offset
+ part
->size
))
219 blocksize
= mtd
->erasesize
;
220 if (!(block
= kmalloc(blocksize
, GFP_KERNEL
)))
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
;
231 if (nflash
->map
[blk_idx
] != 0) {
232 skip_bytes
+= blocksize
;
234 if (good_bytes
== blk_offset
)
236 good_bytes
+= blocksize
;
239 if (blk_idx
== (part
->offset
+part
->size
)/blocksize
) {
243 blk_offset
= blocksize
* blk_idx
;
244 /* Backup and erase one block at a time */
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
,
256 if (read_len
!= blocksize
) {
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
;
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
;
274 /* Write holding block */
276 write_len
= blocksize
;
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
;
299 /* Check and skip bad blocks */
301 blk_offset
+= blocksize
;
303 while ((nflash
->map
[blk_idx
] != 0) &&
304 (blk_offset
< (part
->offset
+part
->size
))) {
305 skip_bytes
+= blocksize
;
306 blk_offset
+= blocksize
;
309 if (blk_offset
>= (part
->offset
+part
->size
)) {
316 mutex_unlock(mtd
->mutex
);
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
;
329 uint addr
, len
, blocksize
;
330 uint part_start_blk
, part_end_blk
;
331 uint blknum
, new_addr
, erase_blknum
;
336 blocksize
= mtd
->erasesize
;
338 /* Check address range */
342 if ((addr
+ len
) > mtd
->size
)
345 if (addr
& (blocksize
- 1))
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
];
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
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
;
376 new_addr
+= blocksize
;
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
)) {
389 for (i
= new_addr
; erase_blknum
; i
+= blocksize
) {
390 /* Skip bad block erase */
391 if (nflash
->map
[i
/ blocksize
] != 0) {
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;
404 /* Set erase status */
406 erase
->state
= MTD_ERASE_FAILED
;
408 erase
->state
= MTD_ERASE_DONE
;
410 mutex_unlock(mtd
->mutex
);
412 /* Call erase callback */
414 erase
->callback(erase
);
419 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
420 #define nflash_mtd_init init_module
421 #define nflash_mtd_exit cleanup_module
425 nflash_mtd_init(void)
429 struct pci_dev
*dev
= NULL
;
430 #ifdef CONFIG_MTD_PARTITIONS
431 struct mtd_partition
*parts
;
435 list_for_each_entry(dev
, &((pci_find_bus(0, 0))->devices
), bus_list
) {
436 if ((dev
!= NULL
) && (dev
->device
== CC_CORE_ID
))
441 printk(KERN_ERR
"nflash: chipcommon not found\n");
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");
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");
463 /* Initialize serial flash access */
464 if (!(info
= nflash_init(nflash
.sih
, nflash
.cc
))) {
465 printk(KERN_ERR
"nflash: found no supported devices\n");
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
);
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
)
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) {
505 mutex_unlock(nflash
.mtd
.mutex
);
507 #ifdef CONFIG_MTD_PARTITIONS
508 parts
= init_nflash_mtd_partitions(&nflash
.mtd
, nflash
.mtd
.size
);
511 for (i
= 0; parts
[i
].name
; i
++);
512 ret
= add_mtd_partitions(&nflash
.mtd
, parts
, i
);
514 printk(KERN_ERR
"nflash: add_mtd failed\n");
517 nflash_parts
= parts
;
523 iounmap((void *) nflash
.cc
);
525 si_detach(nflash
.sih
);
530 nflash_mtd_exit(void)
532 #ifdef CONFIG_MTD_PARTITIONS
533 del_mtd_partitions(&nflash
.mtd
);
535 del_mtd_device(&nflash
.mtd
);
537 iounmap((void *) nflash
.cc
);
538 si_detach(nflash
.sih
);
541 module_init(nflash_mtd_init
);
542 module_exit(nflash_mtd_exit
);