2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2009, Broadcom Corporation
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
15 #include <linux/config.h>
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 #include <linux/ioport.h>
19 #include <linux/mtd/compatmac.h>
20 #include <linux/mtd/mtd.h>
21 #include <linux/mtd/partitions.h>
22 #include <linux/errno.h>
23 #include <linux/pci.h>
24 #include <linux/delay.h>
39 #ifdef CONFIG_MTD_PARTITIONS
40 extern struct mtd_partition
* init_nflash_mtd_partitions(struct mtd_info
*mtd
, size_t size
);
42 struct mtd_partition
*nflash_parts
;
50 struct mtd_erase_region_info region
;
54 /* Private global state */
55 static struct nflash_mtd nflash
;
58 nflash_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
, size_t *retlen
, u_char
*buf
)
60 struct nflash_mtd
*nflash
= (struct nflash_mtd
*) mtd
->priv
;
62 struct mtd_partition
*part
= NULL
;
66 uint offset
, blocksize
, mask
, blk_offset
, off
;
67 uint skip_bytes
= 0, good_bytes
= 0;
73 for (i
= 0; nflash_parts
[i
].name
; i
++) {
74 if (from
>= nflash_parts
[i
].offset
&&
75 ((nflash_parts
[i
+1].name
== NULL
) || (from
< nflash_parts
[i
+1].offset
))) {
76 part
= &nflash_parts
[i
];
82 /* Check address range */
85 if ((from
+ len
) > mtd
->size
)
88 if ((offset
& (NFL_SECTOR_SIZE
- 1)) != 0) {
89 extra
= offset
& (NFL_SECTOR_SIZE
- 1);
94 size
= (len
+ (NFL_SECTOR_SIZE
- 1)) & ~(NFL_SECTOR_SIZE
- 1);
100 tmpbuf
= (uchar
*)kmalloc(size
, GFP_KERNEL
);
104 blocksize
= mtd
->erasesize
;
105 mask
= blocksize
- 1;
106 blk_offset
= offset
& ~mask
;
107 good_bytes
= part
->offset
& ~mask
;
108 /* Check and skip bad blocks */
109 for (blk_idx
= good_bytes
/blocksize
; blk_idx
< mtd
->eraseregions
->numblocks
; blk_idx
++) {
110 if ((nflash
->map
[blk_idx
] != 0) ||
111 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, (blocksize
*blk_idx
)) != 0)) {
112 skip_bytes
+= blocksize
;
113 nflash
->map
[blk_idx
] = 1;
115 if (good_bytes
== blk_offset
)
117 good_bytes
+= blocksize
;
120 if (blk_idx
== mtd
->eraseregions
->numblocks
) {
124 blk_offset
= blocksize
* blk_idx
;
127 off
= offset
+ skip_bytes
;
129 /* Check and skip bad blocks */
130 if (off
>= (blk_offset
+ blocksize
)) {
131 blk_offset
+= blocksize
;
133 while (((nflash
->map
[blk_idx
] != 0) ||
134 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, blk_offset
) != 0)) &&
135 (blk_offset
< mtd
->size
)) {
136 skip_bytes
+= blocksize
;
137 nflash
->map
[blk_idx
] = 1;
138 blk_offset
+= blocksize
;
141 if (blk_offset
>= mtd
->size
) {
145 off
= offset
+ skip_bytes
;
148 if ((bytes
= nflash_read(nflash
->sih
, nflash
->cc
, off
, NFL_SECTOR_SIZE
, ptr
)) < 0) {
163 memcpy(buf
, tmpbuf
+extra
, *retlen
);
170 nflash_mtd_write(struct mtd_info
*mtd
, loff_t to
, size_t len
, size_t *retlen
, const u_char
*buf
)
172 struct nflash_mtd
*nflash
= (struct nflash_mtd
*) mtd
->priv
;
174 struct mtd_partition
*part
= NULL
;
175 u_char
*block
= NULL
;
177 uint offset
, blocksize
, mask
, blk_offset
, off
;
178 uint skip_bytes
= 0, good_bytes
= 0;
180 int read_len
, write_len
, copy_len
;
184 /* Locate the part */
185 for (i
= 0; nflash_parts
[i
].name
; i
++) {
186 if (to
>= nflash_parts
[i
].offset
&&
187 ((nflash_parts
[i
+1].name
== NULL
) || (to
< nflash_parts
[i
+1].offset
))) {
188 part
= &nflash_parts
[i
];
194 /* Check address range */
197 if ((to
+ len
) > (part
->offset
+ part
->size
))
200 blocksize
= mtd
->erasesize
;
201 if (!(block
= kmalloc(blocksize
, GFP_KERNEL
)))
203 mask
= blocksize
- 1;
204 /* Check and skip bad blocks */
205 blk_offset
= offset
& ~mask
;
206 good_bytes
= part
->offset
& ~mask
;
207 for (blk_idx
= good_bytes
/blocksize
; blk_idx
< (part
->offset
+part
->size
)/blocksize
; blk_idx
++) {
208 if ((nflash
->map
[blk_idx
] != 0) ||
209 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, (blocksize
*blk_idx
)) != 0)) {
210 skip_bytes
+= blocksize
;
211 nflash
->map
[blk_idx
] = 1;
213 if (good_bytes
== blk_offset
)
215 good_bytes
+= blocksize
;
218 if (blk_idx
== (part
->offset
+part
->size
)/blocksize
) {
222 blk_offset
= blocksize
* blk_idx
;
223 /* Backup and erase one block at a time */
227 from
= offset
& ~mask
;
228 /* Copy existing data into holding block if necessary */
229 if (((offset
& (blocksize
-1)) != 0) || (len
< blocksize
)) {
230 if ((ret
= nflash_mtd_read(mtd
, from
, blocksize
, &read_len
, block
)))
232 if (read_len
!= blocksize
) {
237 /* Copy input data into holding block */
238 copy_len
= min(len
, blocksize
- (offset
& mask
));
239 memcpy(block
+ (offset
& mask
), ptr
, copy_len
);
240 off
= (uint
) from
+ skip_bytes
;
242 if ((ret
= nflash_erase(nflash
->sih
, nflash
->cc
, off
)) < 0) {
245 /* Write holding block */
247 write_len
= blocksize
;
249 if ((bytes
= nflash_write(nflash
->sih
, nflash
->cc
,
250 (uint
) from
+ skip_bytes
,
252 (uchar
*) write_ptr
)) < 0) {
264 /* Check and skip bad blocks */
266 blk_offset
+= blocksize
;
268 while (((nflash
->map
[blk_idx
] != 0) ||
269 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, blk_offset
) != 0)) &&
270 (blk_offset
< (part
->offset
+part
->size
))) {
271 skip_bytes
+= blocksize
;
272 nflash
->map
[blk_idx
] = 1;
273 blk_offset
+= blocksize
;
276 if (blk_offset
>= (part
->offset
+part
->size
)) {
289 nflash_mtd_erase(struct mtd_info
*mtd
, struct erase_info
*erase
)
291 struct nflash_mtd
*nflash
= (struct nflash_mtd
*) mtd
->priv
;
293 unsigned int addr
, len
;
295 /* Check address range */
298 if ((erase
->addr
+ erase
->len
) > mtd
->size
)
304 /* Ensure that requested region is aligned */
305 for (i
= 0; i
< mtd
->numeraseregions
; i
++) {
306 for (j
= 0; j
< mtd
->eraseregions
[i
].numblocks
; j
++) {
307 if (addr
== mtd
->eraseregions
[i
].offset
+
308 mtd
->eraseregions
[i
].erasesize
* j
&&
309 len
>= mtd
->eraseregions
[i
].erasesize
) {
310 if ((ret
= nflash_erase(nflash
->sih
, nflash
->cc
, addr
)) < 0)
312 addr
+= mtd
->eraseregions
[i
].erasesize
;
313 len
-= mtd
->eraseregions
[i
].erasesize
;
320 /* Set erase status */
322 erase
->state
= MTD_ERASE_FAILED
;
324 erase
->state
= MTD_ERASE_DONE
;
326 /* Call erase callback */
328 erase
->callback(erase
);
333 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
334 #define nflash_mtd_init init_module
335 #define nflash_mtd_exit cleanup_module
339 nflash_mtd_init(void)
343 struct pci_dev
*dev
= NULL
;
344 #ifdef CONFIG_MTD_PARTITIONS
345 struct mtd_partition
*parts
;
349 list_for_each_entry(dev
, &((pci_find_bus(0, 0))->devices
), bus_list
) {
350 if ((dev
!= NULL
) && (dev
->device
== CC_CORE_ID
))
355 printk(KERN_ERR
"nflash: chipcommon not found\n");
359 memset(&nflash
, 0, sizeof(struct nflash_mtd
));
361 /* attach to the backplane */
362 if (!(nflash
.sih
= si_kattach(SI_OSH
))) {
363 printk(KERN_ERR
"nflash: error attaching to backplane\n");
368 /* Map registers and flash base */
369 if (!(nflash
.cc
= ioremap_nocache(
370 pci_resource_start(dev
, 0),
371 pci_resource_len(dev
, 0)))) {
372 printk(KERN_ERR
"nflash: error mapping registers\n");
377 /* Initialize serial flash access */
378 if (!(info
= nflash_init(nflash
.sih
, nflash
.cc
))) {
379 printk(KERN_ERR
"nflash: found no supported devices\n");
384 /* Setup region info */
385 nflash
.region
.offset
= 0;
386 nflash
.region
.erasesize
= info
->blocksize
;
387 nflash
.region
.numblocks
= info
->numblocks
;
388 if (nflash
.region
.erasesize
> nflash
.mtd
.erasesize
)
389 nflash
.mtd
.erasesize
= nflash
.region
.erasesize
;
390 /* At most 2GB is supported */
391 nflash
.mtd
.size
= (info
->size
>= (1 << 11)) ? (1 << 31) : (info
->size
<< 20);
392 nflash
.mtd
.numeraseregions
= 1;
393 nflash
.map
= (unsigned char *)kmalloc(info
->numblocks
, GFP_KERNEL
);
395 memset(nflash
.map
, 0, info
->numblocks
);
397 /* Register with MTD */
398 nflash
.mtd
.name
= "nflash";
399 nflash
.mtd
.type
= MTD_NANDFLASH
;
400 nflash
.mtd
.flags
= MTD_CAP_NANDFLASH
;
401 nflash
.mtd
.eraseregions
= &nflash
.region
;
402 nflash
.mtd
.erase
= nflash_mtd_erase
;
403 nflash
.mtd
.read
= nflash_mtd_read
;
404 nflash
.mtd
.write
= nflash_mtd_write
;
405 nflash
.mtd
.writesize
= NFL_SECTOR_SIZE
;
406 nflash
.mtd
.priv
= &nflash
;
407 nflash
.mtd
.owner
= THIS_MODULE
;
410 #ifdef CONFIG_MTD_PARTITIONS
411 parts
= init_nflash_mtd_partitions(&nflash
.mtd
, nflash
.mtd
.size
);
414 for (i
= 0; parts
[i
].name
; i
++);
415 ret
= add_mtd_partitions(&nflash
.mtd
, parts
, i
);
417 printk(KERN_ERR
"nflash: add_mtd failed\n");
420 nflash_parts
= parts
;
426 iounmap((void *) nflash
.cc
);
428 si_detach(nflash
.sih
);
433 nflash_mtd_exit(void)
435 #ifdef CONFIG_MTD_PARTITIONS
436 del_mtd_partitions(&nflash
.mtd
);
438 del_mtd_device(&nflash
.mtd
);
440 iounmap((void *) nflash
.cc
);
441 si_detach(nflash
.sih
);
444 module_init(nflash_mtd_init
);
445 module_exit(nflash_mtd_exit
);