RT-AC66 3.0.0.4.374.130 core
[tomato.git] / release / src-rt-6.x / cfe / cfe / dev / dev_nflash.c
blob1f21117a63819e19b631957cd02645940feee064
1 /*
2 * Broadcom chipcommon NAND flash interface
4 * Copyright (C) 2011, 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 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"
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 <nflash.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 si_t *sih;
41 chipcregs_t *cc;
42 struct nflash *info;
43 flashpart_t parts[FLASH_MAX_PARTITIONS];
44 unsigned char *map;
47 static int nflashidx = 0;
49 static int
50 nflash_cfe_open(cfe_devctx_t *ctx)
52 return 0;
55 static int
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;
62 uchar *buf = NULL;
63 int bytes, ret = 0;
64 uint extra = 0;
65 uchar *tmpbuf = NULL;
66 int size;
67 uint blocksize, mask, blk_offset, off;
68 uint skip_bytes = 0, good_bytes = 0;
69 int blk_idx;
70 int need_copy = 0;
72 buffer->buf_retlen = 0;
74 /* Check address range */
75 if (!len)
76 return 0;
77 if ((offset & (NFL_SECTOR_SIZE - 1)) != 0) {
78 extra = offset & (NFL_SECTOR_SIZE - 1);
79 offset -= extra;
80 len += extra;
81 need_copy = 1;
84 size = (len + (NFL_SECTOR_SIZE - 1)) & ~(NFL_SECTOR_SIZE - 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 (nflash_checkbadb(nflash->sih, nflash->cc, (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 (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;
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 = nflash_read(nflash->sih, nflash->cc, off, NFL_SECTOR_SIZE, 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 uint offset = (uint) 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 uint blocksize, mask, blk_offset, off = 0;
185 uint skip_bytes = 0, good_bytes = 0;
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; 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;
207 } else {
208 if (good_bytes == blk_offset)
209 break;
210 good_bytes += blocksize;
213 if (blk_idx == (part->fp_offset+part->fp_size)/blocksize) {
214 ret = CFE_ERR_IOERR;
215 goto done;
217 blk_offset = blocksize * blk_idx;
218 /* Backup and erase one block at a time */
219 while (len) {
220 if (docopy) {
221 /* Align offset */
222 cur.buf_offset = offset & ~mask;
223 cur.buf_length = blocksize;
224 cur.buf_ptr = block;
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)))
230 goto done;
231 if (cur.buf_retlen != cur.buf_length) {
232 ret = CFE_ERR_IOERR;
233 goto done;
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;
243 /* Erase block */
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;
248 docopy = 0;
250 else {
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;
260 docopy = 0;
261 break;
264 cur.buf_offset += bytes;
265 cur.buf_length -= bytes;
266 cur.buf_ptr += bytes;
267 docopy = 1;
269 if (docopy) {
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 */
277 if (len) {
278 blk_offset += blocksize;
279 blk_idx++;
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;
286 blk_idx++;
288 if (blk_offset >= (part->fp_offset+part->fp_size)) {
289 ret = CFE_ERR_IOERR;
290 goto done;
294 done:
295 if (block)
296 KFREE(block);
297 return ret;
300 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
301 static int
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;
308 int len, offset;
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;
324 while (len > 0) {
325 nflash_erase(nflash->sih, nflash->cc, offset);
326 offset += blocksize;
327 len -= blocksize;
330 return 0;
332 #endif
334 static int
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;
339 flash_info_t *info;
340 #ifdef CFE_FLASH_ERASE_FLASH_ENABLED
341 flash_range_t *range;
342 #endif
344 switch (buffer->buf_ioctlcmd) {
345 case IOCTL_FLASH_WRITE_ALL:
346 nflash_cfe_write(ctx, buffer);
347 break;
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;
355 break;
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);
366 break;
367 #endif
368 default:
369 return CFE_ERR_INV_COMMAND;
372 return 0;
375 static int
376 nflash_cfe_close(cfe_devctx_t *ctx)
378 return 0;
381 static const cfe_devdisp_t nflash_cfe_dispatch = {
382 nflash_cfe_open,
383 nflash_cfe_read,
384 nflash_cfe_inpstat,
385 nflash_cfe_write,
386 nflash_cfe_ioctl,
387 nflash_cfe_close,
388 NULL,
389 NULL
392 static void
393 nflash_do_parts(struct nflash_cfe *nflash, newflash_probe_t *probe)
395 int idx;
396 int middlepart = -1;
397 int lobound = 0;
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) {
403 middlepart = idx;
404 break;
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;
425 static void
426 nflash_cfe_probe(cfe_driver_t *drv,
427 unsigned long probe_a, unsigned long probe_b,
428 void *probe_ptr)
430 newflash_probe_t *probe = (newflash_probe_t *) probe_ptr;
431 struct nflash_cfe *nflash;
432 char type[80], descr[80], name[80];
433 int idx;
435 if (!(nflash = (struct nflash_cfe *) KMALLOC(sizeof(struct nflash_cfe), 0)))
436 return;
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");
444 KFREE(nflash);
445 return;
447 /* Set description */
448 switch (nflash->info->type) {
449 case NFL_VENDOR_AMD:
450 sprintf(type, "AMD");
451 break;
452 case NFL_VENDOR_NUMONYX:
453 sprintf(type, "Numonyx");
454 break;
455 case NFL_VENDOR_MICRON:
456 sprintf(type, "Micron");
457 break;
458 case NFL_VENDOR_TOSHIBA:
459 sprintf(type, "Toshiba");
460 break;
461 case NFL_VENDOR_HYNIX:
462 sprintf(type, "Hynix");
463 break;
464 case NFL_VENDOR_SAMSUNG:
465 sprintf(type, "Samsung");
466 break;
467 default:
468 sprintf(type, "Unknown type %d", nflash->info->type);
469 break;
471 nflash->map = (unsigned char *)KMALLOC(nflash->info->numblocks, 0);
472 if (nflash->map)
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);
484 } else {
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);
496 else
497 sprintf(name, "%d", idx);
498 cfe_attach_idx(drv, nflashidx, &nflash->parts[idx], name, descr);
500 nflashidx++;
504 const cfe_driver_t nflashdrv = {
505 "NAND flash",
506 "nflash",
507 CFE_DEV_FLASH,
508 &nflash_cfe_dispatch,
509 nflash_cfe_probe