2 * Broadcom chipcommon NAND flash interface
4 * Copyright (C) 2011, Broadcom Corporation
7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
8 * the contents of this file may not be disclosed to third parties, copied
9 * or duplicated in any form, in whole or in part, without the prior
10 * written permission of Broadcom Corporation.
12 * $Id: dev_nflash.c 304950 2011-12-23 09:17:46Z $
15 #include "lib_types.h"
16 #include "lib_malloc.h"
17 #include "lib_printf.h"
18 #include "lib_string.h"
19 #include "addrspace.h"
21 #include "cfe_device.h"
22 #include "cfe_ioctl.h"
23 #include "cfe_error.h"
24 #include "dev_newflash.h"
26 #include "bsp_config.h"
35 #define isaligned(x, y) (((x) % (y)) == 0)
36 #define min(a, b) ((a) < (b) ? (a) : (b))
37 #define max(a, b) ((a) > (b) ? (a) : (b))
43 flashpart_t parts
[FLASH_MAX_PARTITIONS
];
47 static int nflashidx
= 0;
50 nflash_cfe_open(cfe_devctx_t
*ctx
)
56 nflash_cfe_read(cfe_devctx_t
*ctx
, iocb_buffer_t
*buffer
)
58 flashpart_t
*part
= (flashpart_t
*) ctx
->dev_softc
;
59 struct nflash_cfe
*nflash
= (struct nflash_cfe
*) part
->fp_dev
;
60 uint offset
= (uint
) buffer
->buf_offset
+ part
->fp_offset
;
61 uint len
= (uint
) buffer
->buf_length
;
67 uint blocksize
, mask
, blk_offset
, off
;
68 uint skip_bytes
= 0, good_bytes
= 0;
72 buffer
->buf_retlen
= 0;
74 /* Check address range */
77 if ((offset
& (NFL_SECTOR_SIZE
- 1)) != 0) {
78 extra
= offset
& (NFL_SECTOR_SIZE
- 1);
84 size
= (len
+ (NFL_SECTOR_SIZE
- 1)) & ~(NFL_SECTOR_SIZE
- 1);
88 buf
= buffer
->buf_ptr
;
90 tmpbuf
= (uchar
*)KMALLOC(size
, 0);
94 if ((((offset
+ len
) >> 20) > nflash
->info
->size
) ||
95 ((((offset
+ len
) >> 20) == nflash
->info
->size
) &&
96 (((offset
+ len
) & ((1 << 20) - 1)) != 0))) {
101 blocksize
= nflash
->info
->blocksize
;
102 mask
= blocksize
- 1;
103 blk_offset
= offset
& ~mask
;
104 good_bytes
= part
->fp_offset
& ~mask
;
105 /* Check and skip bad blocks */
106 for (blk_idx
= good_bytes
/blocksize
; blk_idx
< nflash
->info
->numblocks
; blk_idx
++) {
107 if ((nflash
->map
[blk_idx
] != 0) ||
108 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, (blocksize
*blk_idx
)) != 0)) {
109 skip_bytes
+= blocksize
;
110 nflash
->map
[blk_idx
] = 1;
112 if (good_bytes
== blk_offset
)
114 good_bytes
+= blocksize
;
117 if (blk_idx
== nflash
->info
->numblocks
) {
121 blk_offset
= blocksize
* blk_idx
;
123 off
= offset
+ skip_bytes
;
125 /* Check and skip bad blocks */
126 if (off
>= (blk_offset
+ blocksize
)) {
127 blk_offset
+= blocksize
;
129 while (((nflash
->map
[blk_idx
] != 0) ||
130 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, blk_offset
) != 0)) &&
131 ((blk_offset
>> 20) < nflash
->info
->size
)) {
132 skip_bytes
+= blocksize
;
133 nflash
->map
[blk_idx
] = 1;
134 blk_offset
+= blocksize
;
137 if ((blk_offset
>> 20) >= nflash
->info
->size
) {
141 off
= offset
+ skip_bytes
;
143 if ((bytes
= nflash_read(nflash
->sih
, nflash
->cc
, off
, NFL_SECTOR_SIZE
, buf
)) < 0) {
152 buffer
->buf_retlen
+= bytes
;
158 buf
= (uchar
*) buffer
->buf_ptr
;
159 buffer
->buf_retlen
-= extra
;
160 memcpy(buf
, tmpbuf
+extra
, buffer
->buf_retlen
);
167 nflash_cfe_inpstat(cfe_devctx_t
*ctx
, iocb_inpstat_t
*inpstat
)
169 inpstat
->inp_status
= 1;
174 nflash_cfe_write(cfe_devctx_t
*ctx
, iocb_buffer_t
*buffer
)
176 flashpart_t
*part
= (flashpart_t
*) ctx
->dev_softc
;
177 struct nflash_cfe
*nflash
= (struct nflash_cfe
*) part
->fp_dev
;
178 uint offset
= (uint
) buffer
->buf_offset
+ part
->fp_offset
;
179 uint len
= (uint
) buffer
->buf_length
;
180 uchar
*buf
= (uchar
*) buffer
->buf_ptr
;
184 uint blocksize
, mask
, blk_offset
, off
= 0;
185 uint skip_bytes
= 0, good_bytes
= 0;
189 buffer
->buf_retlen
= 0;
190 /* Check address range */
193 if ((offset
+ len
) > (part
->fp_offset
+ part
->fp_size
))
194 return CFE_ERR_IOERR
;
195 blocksize
= nflash
->info
->blocksize
;
196 if (!(block
= KMALLOC(blocksize
, 0)))
197 return CFE_ERR_NOMEM
;
198 mask
= blocksize
- 1;
199 /* Check and skip bad blocks */
200 blk_offset
= offset
& ~mask
;
201 good_bytes
= part
->fp_offset
& ~mask
;
202 for (blk_idx
= good_bytes
/blocksize
; blk_idx
< (part
->fp_offset
+part
->fp_size
)/blocksize
; blk_idx
++) {
203 if ((nflash
->map
[blk_idx
] != 0) ||
204 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, (blocksize
*blk_idx
)) != 0)) {
205 skip_bytes
+= blocksize
;
206 nflash
->map
[blk_idx
] = 1;
208 if (good_bytes
== blk_offset
)
210 good_bytes
+= blocksize
;
213 if (blk_idx
== (part
->fp_offset
+part
->fp_size
)/blocksize
) {
217 blk_offset
= blocksize
* blk_idx
;
218 /* Backup and erase one block at a time */
222 cur
.buf_offset
= offset
& ~mask
;
223 cur
.buf_length
= blocksize
;
226 /* Copy existing data into holding sector if necessary */
227 if (!isaligned(offset
, blocksize
) || (len
< blocksize
)) {
228 cur
.buf_offset
-= part
->fp_offset
;
229 if ((ret
= nflash_cfe_read(ctx
, &cur
)))
231 if (cur
.buf_retlen
!= cur
.buf_length
) {
235 cur
.buf_offset
+= part
->fp_offset
;
237 /* Copy input data into holding block */
238 cur
.buf_retlen
= min(len
, blocksize
- (offset
& mask
));
239 memcpy(cur
.buf_ptr
+ (offset
& mask
), buf
, cur
.buf_retlen
);
242 off
= (uint
) cur
.buf_offset
+ skip_bytes
;
244 if ((ret
= nflash_erase(nflash
->sih
, nflash
->cc
, off
)) < 0) {
245 nflash_mark_badb(nflash
->sih
, nflash
->cc
, off
);
246 nflash
->map
[blk_idx
] = 1;
247 skip_bytes
+= blocksize
;
251 /* Write holding sector */
252 while (cur
.buf_length
) {
253 if ((bytes
= nflash_write(nflash
->sih
, nflash
->cc
,
254 (uint
) cur
.buf_offset
+ skip_bytes
,
255 (uint
) cur
.buf_length
,
256 (uchar
*) cur
.buf_ptr
)) < 0) {
257 nflash_mark_badb(nflash
->sih
, nflash
->cc
, off
);
258 nflash
->map
[blk_idx
] = 1;
259 skip_bytes
+= blocksize
;
264 cur
.buf_offset
+= bytes
;
265 cur
.buf_length
-= bytes
;
266 cur
.buf_ptr
+= bytes
;
270 offset
+= cur
.buf_retlen
;
271 len
-= cur
.buf_retlen
;
272 buf
+= cur
.buf_retlen
;
273 buffer
->buf_retlen
+= cur
.buf_retlen
;
276 /* Check and skip bad blocks */
278 blk_offset
+= blocksize
;
280 while (((nflash
->map
[blk_idx
] != 0) ||
281 (nflash_checkbadb(nflash
->sih
, nflash
->cc
, blk_offset
) != 0)) &&
282 (blk_offset
< (part
->fp_offset
+part
->fp_size
))) {
283 skip_bytes
+= blocksize
;
284 nflash
->map
[blk_idx
] = 1;
285 blk_offset
+= blocksize
;
288 if (blk_offset
>= (part
->fp_offset
+part
->fp_size
)) {
300 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
302 nflash_cfe_erase_range(cfe_devctx_t
*ctx
, flash_range_t
*range
)
304 flashpart_t
*part
= (flashpart_t
*) ctx
->dev_softc
;
305 struct nflash_cfe
*nflash
= (struct nflash_cfe
*) part
->fp_dev
;
306 uint blocksize
= nflash
->info
->blocksize
;
310 offset
= part
->fp_offset
+ range
->range_base
;
311 len
= range
->range_length
;
313 if ((offset
< part
->fp_offset
) ||
314 ((offset
+ len
) > (part
->fp_offset
+ part
->fp_size
))) {
315 printf("!! offset 0x%x, len=0x%x over partition boundary: start: 0x%x, end: 0x%x",
316 offset
, len
, part
->fp_offset
, part
->fp_offset
+ part
->fp_size
);
317 return CFE_ERR_INV_PARAM
;
319 if ((offset
& (blocksize
- 1)) != 0) {
320 printf("ersae offset must aligned to %x = 0x%x\n", offset
, blocksize
);
321 return CFE_ERR_INV_PARAM
;
325 nflash_erase(nflash
->sih
, nflash
->cc
, offset
);
335 nflash_cfe_ioctl(cfe_devctx_t
*ctx
, iocb_buffer_t
*buffer
)
337 flashpart_t
*part
= (flashpart_t
*) ctx
->dev_softc
;
338 struct nflash_cfe
*nflash
= (struct nflash_cfe
*) part
->fp_dev
;
340 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
341 flash_range_t
*range
;
344 switch (buffer
->buf_ioctlcmd
) {
345 case IOCTL_FLASH_WRITE_ALL
:
346 nflash_cfe_write(ctx
, buffer
);
348 case IOCTL_FLASH_GETINFO
:
349 info
= (flash_info_t
*) buffer
->buf_ptr
;
350 info
->flash_base
= 0;
351 /* 2GB is supported for now */
352 info
->flash_size
= (nflash
->info
->size
>= (1 << 11)) ? (1 << 31) : (nflash
->info
->size
<< 20);
353 info
->flash_type
= nflash
->info
->type
;
354 info
->flash_flags
= FLASH_FLAG_NOERASE
;
356 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
357 case IOCTL_FLASH_ERASE_RANGE
:
358 range
= (flash_range_t
*) buffer
->buf_ptr
;
359 /* View the base 0 and range 0 as erase all parition */
360 if(range
->range_base
== 0 && range
->range_length
== 0) {
361 range
->range_length
= part
->fp_size
;
362 printf("Erase whole mtd partition!\n");
364 xprintf("Erase range: 0x%x - 0x%x\n", range
->range_base
, range
->range_base
+ range
->range_length
);
365 nflash_cfe_erase_range(ctx
, range
);
369 return CFE_ERR_INV_COMMAND
;
376 nflash_cfe_close(cfe_devctx_t
*ctx
)
381 static const cfe_devdisp_t nflash_cfe_dispatch
= {
393 nflash_do_parts(struct nflash_cfe
*nflash
, newflash_probe_t
*probe
)
398 flashpart_t
*parts
= nflash
->parts
;
399 int hibound
= (nflash
->info
->size
>= (1 << 11)) ? (1 << 31) : (nflash
->info
->size
<< 20);
401 for (idx
= 0; idx
< probe
->flash_nparts
; idx
++) {
402 if (probe
->flash_parts
[idx
].fp_size
== 0) {
406 parts
[idx
].fp_offset
= lobound
;
407 parts
[idx
].fp_size
= probe
->flash_parts
[idx
].fp_size
;
408 lobound
+= probe
->flash_parts
[idx
].fp_size
;
411 if (idx
!= probe
->flash_nparts
) {
412 for (idx
= probe
->flash_nparts
- 1; idx
> middlepart
; idx
--) {
413 parts
[idx
].fp_size
= probe
->flash_parts
[idx
].fp_size
;
414 hibound
-= probe
->flash_parts
[idx
].fp_size
;
415 parts
[idx
].fp_offset
= hibound
;
419 if (middlepart
!= -1) {
420 parts
[middlepart
].fp_offset
= lobound
;
421 parts
[middlepart
].fp_size
= hibound
- lobound
;
426 nflash_cfe_probe(cfe_driver_t
*drv
,
427 unsigned long probe_a
, unsigned long probe_b
,
430 newflash_probe_t
*probe
= (newflash_probe_t
*) probe_ptr
;
431 struct nflash_cfe
*nflash
;
432 char type
[80], descr
[80], name
[80];
435 if (!(nflash
= (struct nflash_cfe
*) KMALLOC(sizeof(struct nflash_cfe
), 0)))
437 memset(nflash
, 0, sizeof(struct nflash_cfe
));
438 /* Attach to the backplane and map to chipc */
439 nflash
->sih
= si_kattach(SI_OSH
);
440 nflash
->cc
= (chipcregs_t
*)probe
->flash_cmdset
;
441 /* Initialize serial flash access */
442 if (!(nflash
->info
= nflash_init(nflash
->sih
, nflash
->cc
))) {
443 xprintf("nflash: found no supported devices\n");
447 /* Set description */
448 switch (nflash
->info
->type
) {
450 sprintf(type
, "AMD");
452 case NFL_VENDOR_NUMONYX
:
453 sprintf(type
, "Numonyx");
455 case NFL_VENDOR_MICRON
:
456 sprintf(type
, "Micron");
458 case NFL_VENDOR_TOSHIBA
:
459 sprintf(type
, "Toshiba");
461 case NFL_VENDOR_HYNIX
:
462 sprintf(type
, "Hynix");
464 case NFL_VENDOR_SAMSUNG
:
465 sprintf(type
, "Samsung");
468 sprintf(type
, "Unknown type %d", nflash
->info
->type
);
471 nflash
->map
= (unsigned char *)KMALLOC(nflash
->info
->numblocks
, 0);
473 memset(nflash
->map
, 0, nflash
->info
->numblocks
);
474 if (probe
->flash_nparts
== 0) {
475 /* Just instantiate one device */
476 nflash
->parts
[0].fp_dev
= (flashdev_t
*) nflash
;
477 nflash
->parts
[0].fp_offset
= 0;
478 /* At most 2GB for one partition */
479 nflash
->parts
[0].fp_size
= (nflash
->info
->size
>= (1 << 11)) ? (1 << 31) : (nflash
->info
->size
<< 20);
480 sprintf(descr
, "%s %s size %uKB",
481 type
, drv
->drv_description
,
482 nflash
->info
->size
<< 10);
483 cfe_attach(drv
, &nflash
->parts
[0], NULL
, descr
);
485 /* Partition flash into chunks */
486 nflash_do_parts(nflash
, probe
);
487 /* Instantiate devices for each piece */
488 for (idx
= 0; idx
< probe
->flash_nparts
; idx
++) {
489 sprintf(descr
, "%s %s offset %08X size %uKB",
490 type
, drv
->drv_description
,
491 nflash
->parts
[idx
].fp_offset
,
492 (nflash
->parts
[idx
].fp_size
+ 1023) / 1024);
493 nflash
->parts
[idx
].fp_dev
= (flashdev_t
*) nflash
;
494 if (probe
->flash_parts
[idx
].fp_name
)
495 strcpy(name
, probe
->flash_parts
[idx
].fp_name
);
497 sprintf(name
, "%d", idx
);
498 cfe_attach_idx(drv
, nflashidx
, &nflash
->parts
[idx
], name
, descr
);
504 const cfe_driver_t nflashdrv
= {
508 &nflash_cfe_dispatch
,