GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / cfe / cfe / dev / dev_nflash.c
blob567bd279d275eca39df033be0b3b7d5dc5b16974
1 /*
2 * Broadcom chipcommon NAND flash interface
4 * Copyright (C) 2012, Broadcom Corporation
5 * All Rights Reserved.
6 *
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"
20 #include "cfe_iocb.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"
28 #include <typedefs.h>
29 #include <osl.h>
30 #include <bcmutils.h>
31 #include <siutils.h>
32 #include <hndnand.h>
33 #include <hndsoc.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))
39 struct nflash_cfe {
40 hndnand_t *info;
41 flashpart_t parts[FLASH_MAX_PARTITIONS];
42 unsigned char *map;
45 static int nflashidx = 0;
47 static int
48 nflash_cfe_open(cfe_devctx_t *ctx)
50 return 0;
53 static int
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;
60 uchar *buf = NULL;
61 int bytes, ret = 0;
62 uint extra = 0;
63 uchar *tmpbuf = NULL;
64 int size;
65 uint64 blk_offset, off, skip_bytes = 0;
66 uint blocksize, pagesize, mask, good_bytes = 0;
67 int blk_idx;
68 int need_copy = 0;
70 buffer->buf_retlen = 0;
72 /* Check address range */
73 if (!len)
74 return 0;
76 pagesize = nflash->info->pagesize;
77 if ((offset & (pagesize - 1)) != 0) {
78 extra = offset & (pagesize - 1);
79 offset -= extra;
80 len += extra;
81 need_copy = 1;
84 size = (len + (pagesize - 1)) & ~(pagesize - 1);
85 if (size != len)
86 need_copy = 1;
87 if (!need_copy) {
88 buf = buffer->buf_ptr;
89 } else {
90 tmpbuf = (uchar *)KMALLOC(size, 0);
91 buf = tmpbuf;
94 if ((((offset + len) >> 20) > nflash->info->size) ||
95 ((((offset + len) >> 20) == nflash->info->size) &&
96 (((offset + len) & ((1 << 20) - 1)) != 0))) {
97 ret = CFE_ERR_IOERR;
98 goto done;
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;
111 } else {
112 if (good_bytes == blk_offset)
113 break;
114 good_bytes += blocksize;
117 if (blk_idx == nflash->info->numblocks) {
118 ret = CFE_ERR_IOERR;
119 goto done;
121 blk_offset = blocksize * blk_idx;
122 while (len > 0) {
123 off = offset + skip_bytes;
125 /* Check and skip bad blocks */
126 if (off >= (blk_offset + blocksize)) {
127 blk_offset += blocksize;
128 blk_idx++;
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;
135 blk_idx++;
137 if ((blk_offset >> 20) >= nflash->info->size) {
138 ret = CFE_ERR_IOERR;
139 goto done;
141 off = offset + skip_bytes;
143 if ((bytes = hndnand_read(nflash->info, off, pagesize, buf)) < 0) {
144 ret = bytes;
145 goto done;
147 if (bytes > len)
148 bytes = len;
149 offset += bytes;
150 len -= bytes;
151 buf += bytes;
152 buffer->buf_retlen += bytes;
156 done:
157 if (tmpbuf) {
158 buf = (uchar *) buffer->buf_ptr;
159 buffer->buf_retlen -= extra;
160 memcpy(buf, tmpbuf+extra, buffer->buf_retlen);
161 KFREE(tmpbuf);
163 return ret;
166 static int
167 nflash_cfe_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
169 inpstat->inp_status = 1;
170 return 0;
173 static int
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;
181 uchar *block = NULL;
182 iocb_buffer_t cur;
183 int bytes, ret = 0;
184 uint64 blk_offset, off = 0, skip_bytes = 0;
185 uint blocksize, mask, good_bytes;
186 int blk_idx;
187 int docopy = 1;
189 buffer->buf_retlen = 0;
190 /* Check address range */
191 if (!len)
192 return 0;
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;
204 blk_idx++) {
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;
209 } else {
210 if (good_bytes == blk_offset)
211 break;
212 good_bytes += blocksize;
215 if (blk_idx == (part->fp_offset+part->fp_size)/blocksize) {
216 ret = CFE_ERR_IOERR;
217 goto done;
219 blk_offset = blocksize * blk_idx;
220 /* Backup and erase one block at a time */
221 while (len) {
222 if (docopy) {
223 /* Align offset */
224 cur.buf_offset = offset & ~mask;
225 cur.buf_length = blocksize;
226 cur.buf_ptr = block;
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)))
232 goto done;
233 if (cur.buf_retlen != cur.buf_length) {
234 ret = CFE_ERR_IOERR;
235 goto done;
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;
245 /* Erase block */
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;
250 docopy = 0;
252 else {
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) {
259 /* Mark bad block */
260 hndnand_mark_badb(nflash->info, off);
261 nflash->map[blk_idx] = 1;
262 skip_bytes += blocksize;
263 docopy = 0;
264 break;
266 cur.buf_offset += bytes;
267 cur.buf_length -= bytes;
268 cur.buf_ptr += bytes;
269 docopy = 1;
271 if (docopy) {
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 */
279 if (len) {
280 blk_offset += blocksize;
281 blk_idx++;
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;
288 blk_idx++;
290 if (blk_offset >= (part->fp_offset+part->fp_size)) {
291 ret = CFE_ERR_IOERR;
292 goto done;
296 done:
297 if (block)
298 KFREE(block);
299 return ret;
302 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
303 static int
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;
310 uint64 offset, len;
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;
327 while (len > 0) {
328 hndnand_erase(nflash->info, offset);
329 offset += blocksize;
330 len -= blocksize;
333 return 0;
335 #endif /* CFE_FLASH_ERASE_FLASH_ENABLED */
337 static int
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;
342 flash_info_t *info;
343 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
344 flash_range_t *range;
345 #endif
347 switch (buffer->buf_ioctlcmd) {
348 case IOCTL_FLASH_WRITE_ALL:
349 nflash_cfe_write(ctx, buffer);
350 break;
351 case IOCTL_FLASH_GETINFO:
352 info = (flash_info_t *) buffer->buf_ptr;
353 info->flash_base = 0;
354 /* 2GB is supported for now */
355 info->flash_size =
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;
359 break;
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);
371 break;
372 #endif
373 default:
374 return CFE_ERR_INV_COMMAND;
377 return 0;
380 static int
381 nflash_cfe_close(cfe_devctx_t *ctx)
383 return 0;
386 static const cfe_devdisp_t nflash_cfe_dispatch = {
387 nflash_cfe_open,
388 nflash_cfe_read,
389 nflash_cfe_inpstat,
390 nflash_cfe_write,
391 nflash_cfe_ioctl,
392 nflash_cfe_close,
393 NULL,
394 NULL
397 static void
398 nflash_do_parts(struct nflash_cfe *nflash, newflash_probe_t *probe)
400 int idx;
401 int middlepart = -1;
402 int lobound = 0;
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) {
408 middlepart = idx;
409 break;
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;
430 static void
431 nflash_cfe_probe(cfe_driver_t *drv,
432 unsigned long probe_a, unsigned long probe_b,
433 void *probe_ptr)
435 newflash_probe_t *probe = (newflash_probe_t *) probe_ptr;
436 struct nflash_cfe *nflash;
437 char type[80], descr[80], name[80];
438 int idx;
439 si_t *sih;
441 if (!(nflash = (struct nflash_cfe *)KMALLOC(sizeof(struct nflash_cfe), 0)))
442 return;
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");
450 KFREE(nflash);
451 return;
453 /* Set description */
454 switch (nflash->info->type) {
455 case NFL_VENDOR_AMD:
456 sprintf(type, "AMD");
457 break;
458 case NFL_VENDOR_NUMONYX:
459 sprintf(type, "Numonyx");
460 break;
461 case NFL_VENDOR_MICRON:
462 sprintf(type, "Micron");
463 break;
464 case NFL_VENDOR_TOSHIBA:
465 sprintf(type, "Toshiba");
466 break;
467 case NFL_VENDOR_HYNIX:
468 sprintf(type, "Hynix");
469 break;
470 case NFL_VENDOR_SAMSUNG:
471 sprintf(type, "Samsung");
472 break;
473 default:
474 sprintf(type, "Unknown type %d", nflash->info->type);
475 break;
477 nflash->map = (unsigned char *)KMALLOC(nflash->info->numblocks, 0);
478 if (nflash->map)
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);
491 } else {
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);
503 else
504 sprintf(name, "%d", idx);
505 cfe_attach_idx(drv, nflashidx, &nflash->parts[idx], name, descr);
507 nflashidx++;
511 const cfe_driver_t nflashdrv = {
512 "NAND flash",
513 "nflash",
514 CFE_DEV_FLASH,
515 &nflash_cfe_dispatch,
516 nflash_cfe_probe