2 * Broadcom chipcommon NAND flash interface
4 * Copyright (C) 2012, 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 359506 2012-09-28 05:19:03Z $
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))
41 flashpart_t parts
[FLASH_MAX_PARTITIONS
];
45 static int nflashidx
= 0;
48 nflash_cfe_open(cfe_devctx_t
*ctx
)
54 nflash_cfe_read(cfe_devctx_t
*ctx
, iocb_buffer_t
*buffer
)
56 flashpart_t
*part
= (flashpart_t
*) ctx
->dev_softc
;
57 struct nflash_cfe
*nflash
= (struct nflash_cfe
*) part
->fp_dev
;
58 uint64 offset
= buffer
->buf_offset
+ part
->fp_offset
;
59 uint len
= (uint
) buffer
->buf_length
;
65 uint64 blk_offset
, off
, skip_bytes
= 0;
66 uint blocksize
, pagesize
, mask
, good_bytes
= 0;
70 buffer
->buf_retlen
= 0;
72 /* Check address range */
76 pagesize
= nflash
->info
->pagesize
;
77 if ((offset
& (pagesize
- 1)) != 0) {
78 extra
= offset
& (pagesize
- 1);
84 size
= (len
+ (pagesize
- 1)) & ~(pagesize
- 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 (hndnand_checkbadb(nflash
->info
, (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 (hndnand_checkbadb(nflash
->info
, 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
= hndnand_read(nflash
->info
, off
, pagesize
, 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 uint64 offset
= buffer
->buf_offset
+ part
->fp_offset
;
179 uint len
= (uint
) buffer
->buf_length
;
180 uchar
*buf
= (uchar
*) buffer
->buf_ptr
;
184 uint64 blk_offset
, off
= 0, skip_bytes
= 0;
185 uint blocksize
, mask
, good_bytes
;
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
;
203 blk_idx
< (part
->fp_offset
+part
->fp_size
)/blocksize
;
205 if ((nflash
->map
[blk_idx
] != 0) ||
206 (hndnand_checkbadb(nflash
->info
, (blocksize
*blk_idx
)) != 0)) {
207 skip_bytes
+= blocksize
;
208 nflash
->map
[blk_idx
] = 1;
210 if (good_bytes
== blk_offset
)
212 good_bytes
+= blocksize
;
215 if (blk_idx
== (part
->fp_offset
+part
->fp_size
)/blocksize
) {
219 blk_offset
= blocksize
* blk_idx
;
220 /* Backup and erase one block at a time */
224 cur
.buf_offset
= offset
& ~mask
;
225 cur
.buf_length
= blocksize
;
228 /* Copy existing data into holding sector if necessary */
229 if (!isaligned((uint
)offset
, blocksize
) || (len
< blocksize
)) {
230 cur
.buf_offset
-= part
->fp_offset
;
231 if ((ret
= nflash_cfe_read(ctx
, &cur
)))
233 if (cur
.buf_retlen
!= cur
.buf_length
) {
237 cur
.buf_offset
+= part
->fp_offset
;
239 /* Copy input data into holding block */
240 cur
.buf_retlen
= min(len
, blocksize
- (offset
& mask
));
241 memcpy(cur
.buf_ptr
+ (offset
& mask
), buf
, cur
.buf_retlen
);
244 off
= (uint
) cur
.buf_offset
+ skip_bytes
;
246 if ((ret
= hndnand_erase(nflash
->info
, off
)) < 0) {
247 hndnand_mark_badb(nflash
->info
, off
);
248 nflash
->map
[blk_idx
] = 1;
249 skip_bytes
+= blocksize
;
253 /* Write holding sector */
254 while (cur
.buf_length
) {
255 if ((bytes
= hndnand_write(nflash
->info
,
256 cur
.buf_offset
+ skip_bytes
,
257 (uint
)cur
.buf_length
,
258 (uchar
*)cur
.buf_ptr
)) < 0) {
260 hndnand_mark_badb(nflash
->info
, off
);
261 nflash
->map
[blk_idx
] = 1;
262 skip_bytes
+= blocksize
;
266 cur
.buf_offset
+= bytes
;
267 cur
.buf_length
-= bytes
;
268 cur
.buf_ptr
+= bytes
;
272 offset
+= cur
.buf_retlen
;
273 len
-= cur
.buf_retlen
;
274 buf
+= cur
.buf_retlen
;
275 buffer
->buf_retlen
+= cur
.buf_retlen
;
278 /* Check and skip bad blocks */
280 blk_offset
+= blocksize
;
282 while (((nflash
->map
[blk_idx
] != 0) ||
283 (hndnand_checkbadb(nflash
->info
, blk_offset
) != 0)) &&
284 (blk_offset
< (part
->fp_offset
+part
->fp_size
))) {
285 skip_bytes
+= blocksize
;
286 nflash
->map
[blk_idx
] = 1;
287 blk_offset
+= blocksize
;
290 if (blk_offset
>= (part
->fp_offset
+part
->fp_size
)) {
302 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
304 nflash_cfe_erase_range(cfe_devctx_t
*ctx
, flash_range_t
*range
)
306 flashpart_t
*part
= (flashpart_t
*) ctx
->dev_softc
;
307 struct nflash_cfe
*nflash
= (struct nflash_cfe
*) part
->fp_dev
;
308 uint blocksize
= nflash
->info
->blocksize
;
312 offset
= part
->fp_offset
+ range
->range_base
;
313 len
= range
->range_length
;
315 if ((offset
< part
->fp_offset
) ||
316 ((offset
+ len
) > (part
->fp_offset
+ part
->fp_size
))) {
317 printf("!! offset 0x%llx, len=0x%llx over partition boundary: "
318 "start: 0x%x, end: 0x%x",
319 offset
, len
, part
->fp_offset
, part
->fp_offset
+ part
->fp_size
);
320 return CFE_ERR_INV_PARAM
;
322 if ((offset
& (blocksize
- 1)) != 0) {
323 printf("erase offset must aligned to %llx = 0x%x\n", offset
, blocksize
);
324 return CFE_ERR_INV_PARAM
;
328 hndnand_erase(nflash
->info
, offset
);
335 #endif /* CFE_FLASH_ERASE_FLASH_ENABLED */
338 nflash_cfe_ioctl(cfe_devctx_t
*ctx
, iocb_buffer_t
*buffer
)
340 flashpart_t
*part
= (flashpart_t
*) ctx
->dev_softc
;
341 struct nflash_cfe
*nflash
= (struct nflash_cfe
*) part
->fp_dev
;
343 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
344 flash_range_t
*range
;
347 switch (buffer
->buf_ioctlcmd
) {
348 case IOCTL_FLASH_WRITE_ALL
:
349 nflash_cfe_write(ctx
, buffer
);
351 case IOCTL_FLASH_GETINFO
:
352 info
= (flash_info_t
*) buffer
->buf_ptr
;
353 info
->flash_base
= 0;
354 /* 2GB is supported for now */
356 (nflash
->info
->size
>= (1 << 11)) ? (1 << 31) : (nflash
->info
->size
<< 20);
357 info
->flash_type
= nflash
->info
->type
;
358 info
->flash_flags
= FLASH_FLAG_NOERASE
;
360 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
361 case IOCTL_FLASH_ERASE_RANGE
:
362 range
= (flash_range_t
*) buffer
->buf_ptr
;
363 /* View the base 0 and range 0 as erase all parition */
364 if (range
->range_base
== 0 && range
->range_length
== 0) {
365 range
->range_length
= part
->fp_size
;
366 printf("Erase whole mtd partition!\n");
368 xprintf("Erase range: 0x%x - 0x%x\n",
369 range
->range_base
, range
->range_base
+ range
->range_length
);
370 nflash_cfe_erase_range(ctx
, range
);
374 return CFE_ERR_INV_COMMAND
;
381 nflash_cfe_close(cfe_devctx_t
*ctx
)
386 static const cfe_devdisp_t nflash_cfe_dispatch
= {
398 nflash_do_parts(struct nflash_cfe
*nflash
, newflash_probe_t
*probe
)
403 flashpart_t
*parts
= nflash
->parts
;
404 int hibound
= (nflash
->info
->size
>= (1 << 11)) ? (1 << 31) : (nflash
->info
->size
<< 20);
406 for (idx
= 0; idx
< probe
->flash_nparts
; idx
++) {
407 if (probe
->flash_parts
[idx
].fp_size
== 0) {
411 parts
[idx
].fp_offset
= lobound
;
412 parts
[idx
].fp_size
= probe
->flash_parts
[idx
].fp_size
;
413 lobound
+= probe
->flash_parts
[idx
].fp_size
;
416 if (idx
!= probe
->flash_nparts
) {
417 for (idx
= probe
->flash_nparts
- 1; idx
> middlepart
; idx
--) {
418 parts
[idx
].fp_size
= probe
->flash_parts
[idx
].fp_size
;
419 hibound
-= probe
->flash_parts
[idx
].fp_size
;
420 parts
[idx
].fp_offset
= hibound
;
424 if (middlepart
!= -1) {
425 parts
[middlepart
].fp_offset
= lobound
;
426 parts
[middlepart
].fp_size
= hibound
- lobound
;
431 nflash_cfe_probe(cfe_driver_t
*drv
,
432 unsigned long probe_a
, unsigned long probe_b
,
435 newflash_probe_t
*probe
= (newflash_probe_t
*) probe_ptr
;
436 struct nflash_cfe
*nflash
;
437 char type
[80], descr
[80], name
[80];
441 if (!(nflash
= (struct nflash_cfe
*)KMALLOC(sizeof(struct nflash_cfe
), 0)))
443 memset(nflash
, 0, sizeof(struct nflash_cfe
));
444 /* Attach to the backplane and map to chipc */
445 sih
= si_kattach(SI_OSH
);
447 /* Initialize serial flash access */
448 if (!(nflash
->info
= hndnand_init(sih
))) {
449 xprintf("nflash: found no supported devices\n");
453 /* Set description */
454 switch (nflash
->info
->type
) {
456 sprintf(type
, "AMD");
458 case NFL_VENDOR_NUMONYX
:
459 sprintf(type
, "Numonyx");
461 case NFL_VENDOR_MICRON
:
462 sprintf(type
, "Micron");
464 case NFL_VENDOR_TOSHIBA
:
465 sprintf(type
, "Toshiba");
467 case NFL_VENDOR_HYNIX
:
468 sprintf(type
, "Hynix");
470 case NFL_VENDOR_SAMSUNG
:
471 sprintf(type
, "Samsung");
474 sprintf(type
, "Unknown type %d", nflash
->info
->type
);
477 nflash
->map
= (unsigned char *)KMALLOC(nflash
->info
->numblocks
, 0);
479 memset(nflash
->map
, 0, nflash
->info
->numblocks
);
480 if (probe
->flash_nparts
== 0) {
481 /* Just instantiate one device */
482 nflash
->parts
[0].fp_dev
= (flashdev_t
*)nflash
;
483 nflash
->parts
[0].fp_offset
= 0;
484 /* At most 2GB for one partition */
485 nflash
->parts
[0].fp_size
=
486 (nflash
->info
->size
>= (1 << 11)) ? (1 << 31) : (nflash
->info
->size
<< 20);
487 sprintf(descr
, "%s %s size %uKB",
488 type
, drv
->drv_description
,
489 nflash
->info
->size
<< 10);
490 cfe_attach(drv
, &nflash
->parts
[0], NULL
, descr
);
492 /* Partition flash into chunks */
493 nflash_do_parts(nflash
, probe
);
494 /* Instantiate devices for each piece */
495 for (idx
= 0; idx
< probe
->flash_nparts
; idx
++) {
496 sprintf(descr
, "%s %s offset %08X size %uKB",
497 type
, drv
->drv_description
,
498 nflash
->parts
[idx
].fp_offset
,
499 (nflash
->parts
[idx
].fp_size
+ 1023) / 1024);
500 nflash
->parts
[idx
].fp_dev
= (flashdev_t
*) nflash
;
501 if (probe
->flash_parts
[idx
].fp_name
)
502 strcpy(name
, probe
->flash_parts
[idx
].fp_name
);
504 sprintf(name
, "%d", idx
);
505 cfe_attach_idx(drv
, nflashidx
, &nflash
->parts
[idx
], name
, descr
);
511 const cfe_driver_t nflashdrv
= {
515 &nflash_cfe_dispatch
,