Blame view

hw/nand.c 19.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
 * Flash NAND memory emulation.  Based on "16M x 8 Bit NAND Flash
 * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from
 * Samsung Electronic.
 *
 * Copyright (c) 2006 Openedhand Ltd.
 * Written by Andrzej Zaborowski <balrog@zabor.org>
 *
 * This code is licensed under the GNU GPL v2.
 */

#ifndef NAND_IO
pbrook authored
14
15
16
17
18
# include "hw.h"
# include "flash.h"
# include "block.h"
/* FIXME: Pass block device as an argument.  */
# include "sysemu.h"
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# define NAND_CMD_READ0		0x00
# define NAND_CMD_READ1		0x01
# define NAND_CMD_READ2		0x50
# define NAND_CMD_LPREAD2	0x30
# define NAND_CMD_NOSERIALREAD2	0x35
# define NAND_CMD_RANDOMREAD1	0x05
# define NAND_CMD_RANDOMREAD2	0xe0
# define NAND_CMD_READID	0x90
# define NAND_CMD_RESET		0xff
# define NAND_CMD_PAGEPROGRAM1	0x80
# define NAND_CMD_PAGEPROGRAM2	0x10
# define NAND_CMD_CACHEPROGRAM2	0x15
# define NAND_CMD_BLOCKERASE1	0x60
# define NAND_CMD_BLOCKERASE2	0xd0
# define NAND_CMD_READSTATUS	0x70
# define NAND_CMD_COPYBACKPRG1	0x85

# define NAND_IOSTATUS_ERROR	(1 << 0)
# define NAND_IOSTATUS_PLANE0	(1 << 1)
# define NAND_IOSTATUS_PLANE1	(1 << 2)
# define NAND_IOSTATUS_PLANE2	(1 << 3)
# define NAND_IOSTATUS_PLANE3	(1 << 4)
# define NAND_IOSTATUS_BUSY	(1 << 6)
# define NAND_IOSTATUS_UNPROTCT	(1 << 7)

# define MAX_PAGE		0x800
# define MAX_OOB		0x40
Paul Brook authored
48
struct NANDFlashState {
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    uint8_t manf_id, chip_id;
    int size, pages;
    int page_shift, oob_shift, erase_shift, addr_shift;
    uint8_t *storage;
    BlockDriverState *bdrv;
    int mem_oob;

    int cle, ale, ce, wp, gnd;

    uint8_t io[MAX_PAGE + MAX_OOB + 0x400];
    uint8_t *ioaddr;
    int iolen;

    uint32_t cmd, addr;
    int addrlen;
    int status;
    int offset;
Paul Brook authored
67
68
69
    void (*blk_write)(NANDFlashState *s);
    void (*blk_erase)(NANDFlashState *s);
    void (*blk_load)(NANDFlashState *s, uint32_t addr, int offset);
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
};

# define NAND_NO_AUTOINCR	0x00000001
# define NAND_BUSWIDTH_16	0x00000002
# define NAND_NO_PADDING	0x00000004
# define NAND_CACHEPRG		0x00000008
# define NAND_COPYBACK		0x00000010
# define NAND_IS_AND		0x00000020
# define NAND_4PAGE_ARRAY	0x00000040
# define NAND_NO_READRDY	0x00000100
# define NAND_SAMSUNG_LP	(NAND_NO_PADDING | NAND_COPYBACK)

# define NAND_IO

# define PAGE(addr)		((addr) >> ADDR_SHIFT)
# define PAGE_START(page)	(PAGE(page) * (PAGE_SIZE + OOB_SIZE))
# define PAGE_MASK		((1 << ADDR_SHIFT) - 1)
# define OOB_SHIFT		(PAGE_SHIFT - 5)
# define OOB_SIZE		(1 << OOB_SHIFT)
# define SECTOR(addr)		((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT))
# define SECTOR_OFFSET(addr)	((addr) & ((511 >> PAGE_SHIFT) << 8))

# define PAGE_SIZE		256
# define PAGE_SHIFT		8
# define PAGE_SECTORS		1
# define ADDR_SHIFT		8
# include "nand.c"
# define PAGE_SIZE		512
# define PAGE_SHIFT		9
# define PAGE_SECTORS		1
# define ADDR_SHIFT		8
# include "nand.c"
# define PAGE_SIZE		2048
# define PAGE_SHIFT		11
# define PAGE_SECTORS		4
# define ADDR_SHIFT		16
# include "nand.c"

/* Information based on Linux drivers/mtd/nand/nand_ids.c */
Paul Brook authored
109
static const struct {
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
    int size;
    int width;
    int page_shift;
    int erase_shift;
    uint32_t options;
} nand_flash_ids[0x100] = {
    [0 ... 0xff] = { 0 },

    [0x6e] = { 1,	8,	8, 4, 0 },
    [0x64] = { 2,	8,	8, 4, 0 },
    [0x6b] = { 4,	8,	9, 4, 0 },
    [0xe8] = { 1,	8,	8, 4, 0 },
    [0xec] = { 1,	8,	8, 4, 0 },
    [0xea] = { 2,	8,	8, 4, 0 },
    [0xd5] = { 4,	8,	9, 4, 0 },
    [0xe3] = { 4,	8,	9, 4, 0 },
    [0xe5] = { 4,	8,	9, 4, 0 },
    [0xd6] = { 8,	8,	9, 4, 0 },

    [0x39] = { 8,	8,	9, 4, 0 },
    [0xe6] = { 8,	8,	9, 4, 0 },
    [0x49] = { 8,	16,	9, 4, NAND_BUSWIDTH_16 },
    [0x59] = { 8,	16,	9, 4, NAND_BUSWIDTH_16 },

    [0x33] = { 16,	8,	9, 5, 0 },
    [0x73] = { 16,	8,	9, 5, 0 },
    [0x43] = { 16,	16,	9, 5, NAND_BUSWIDTH_16 },
    [0x53] = { 16,	16,	9, 5, NAND_BUSWIDTH_16 },

    [0x35] = { 32,	8,	9, 5, 0 },
    [0x75] = { 32,	8,	9, 5, 0 },
    [0x45] = { 32,	16,	9, 5, NAND_BUSWIDTH_16 },
    [0x55] = { 32,	16,	9, 5, NAND_BUSWIDTH_16 },

    [0x36] = { 64,	8,	9, 5, 0 },
    [0x76] = { 64,	8,	9, 5, 0 },
    [0x46] = { 64,	16,	9, 5, NAND_BUSWIDTH_16 },
    [0x56] = { 64,	16,	9, 5, NAND_BUSWIDTH_16 },

    [0x78] = { 128,	8,	9, 5, 0 },
    [0x39] = { 128,	8,	9, 5, 0 },
    [0x79] = { 128,	8,	9, 5, 0 },
    [0x72] = { 128,	16,	9, 5, NAND_BUSWIDTH_16 },
    [0x49] = { 128,	16,	9, 5, NAND_BUSWIDTH_16 },
    [0x74] = { 128,	16,	9, 5, NAND_BUSWIDTH_16 },
    [0x59] = { 128,	16,	9, 5, NAND_BUSWIDTH_16 },

    [0x71] = { 256,	8,	9, 5, 0 },

    /*
     * These are the new chips with large page size. The pagesize and the
     * erasesize is determined from the extended id bytes
     */
# define LP_OPTIONS	(NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR)
# define LP_OPTIONS16	(LP_OPTIONS | NAND_BUSWIDTH_16)

    /* 512 Megabit */
    [0xa2] = { 64,	8,	0, 0, LP_OPTIONS },
    [0xf2] = { 64,	8,	0, 0, LP_OPTIONS },
    [0xb2] = { 64,	16,	0, 0, LP_OPTIONS16 },
    [0xc2] = { 64,	16,	0, 0, LP_OPTIONS16 },

    /* 1 Gigabit */
    [0xa1] = { 128,	8,	0, 0, LP_OPTIONS },
    [0xf1] = { 128,	8,	0, 0, LP_OPTIONS },
    [0xb1] = { 128,	16,	0, 0, LP_OPTIONS16 },
    [0xc1] = { 128,	16,	0, 0, LP_OPTIONS16 },

    /* 2 Gigabit */
    [0xaa] = { 256,	8,	0, 0, LP_OPTIONS },
    [0xda] = { 256,	8,	0, 0, LP_OPTIONS },
    [0xba] = { 256,	16,	0, 0, LP_OPTIONS16 },
    [0xca] = { 256,	16,	0, 0, LP_OPTIONS16 },

    /* 4 Gigabit */
    [0xac] = { 512,	8,	0, 0, LP_OPTIONS },
    [0xdc] = { 512,	8,	0, 0, LP_OPTIONS },
    [0xbc] = { 512,	16,	0, 0, LP_OPTIONS16 },
    [0xcc] = { 512,	16,	0, 0, LP_OPTIONS16 },

    /* 8 Gigabit */
    [0xa3] = { 1024,	8,	0, 0, LP_OPTIONS },
    [0xd3] = { 1024,	8,	0, 0, LP_OPTIONS },
    [0xb3] = { 1024,	16,	0, 0, LP_OPTIONS16 },
    [0xc3] = { 1024,	16,	0, 0, LP_OPTIONS16 },

    /* 16 Gigabit */
    [0xa5] = { 2048,	8,	0, 0, LP_OPTIONS },
    [0xd5] = { 2048,	8,	0, 0, LP_OPTIONS },
    [0xb5] = { 2048,	16,	0, 0, LP_OPTIONS16 },
    [0xc5] = { 2048,	16,	0, 0, LP_OPTIONS16 },
};
Paul Brook authored
203
static void nand_reset(NANDFlashState *s)
204
205
206
207
208
209
210
211
212
{
    s->cmd = NAND_CMD_READ0;
    s->addr = 0;
    s->addrlen = 0;
    s->iolen = 0;
    s->offset = 0;
    s->status &= NAND_IOSTATUS_UNPROTCT;
}
Paul Brook authored
213
static void nand_command(NANDFlashState *s)
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
{
    switch (s->cmd) {
    case NAND_CMD_READ0:
        s->iolen = 0;
        break;

    case NAND_CMD_READID:
        s->io[0] = s->manf_id;
        s->io[1] = s->chip_id;
        s->io[2] = 'Q';		/* Don't-care byte (often 0xa5) */
        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
            s->io[3] = 0x15;	/* Page Size, Block Size, Spare Size.. */
        else
            s->io[3] = 0xc0;	/* Multi-plane */
        s->ioaddr = s->io;
        s->iolen = 4;
        break;

    case NAND_CMD_RANDOMREAD2:
    case NAND_CMD_NOSERIALREAD2:
        if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP))
            break;

        s->blk_load(s, s->addr, s->addr & ((1 << s->addr_shift) - 1));
        break;

    case NAND_CMD_RESET:
        nand_reset(s);
        break;

    case NAND_CMD_PAGEPROGRAM1:
        s->ioaddr = s->io;
        s->iolen = 0;
        break;

    case NAND_CMD_PAGEPROGRAM2:
        if (s->wp) {
            s->blk_write(s);
        }
        break;

    case NAND_CMD_BLOCKERASE1:
        break;

    case NAND_CMD_BLOCKERASE2:
        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
            s->addr <<= 16;
        else
            s->addr <<= 8;

        if (s->wp) {
            s->blk_erase(s);
        }
        break;

    case NAND_CMD_READSTATUS:
        s->io[0] = s->status;
        s->ioaddr = s->io;
        s->iolen = 1;
        break;

    default:
        printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd);
    }
}
280
281
static void nand_save(QEMUFile *f, void *opaque)
{
Paul Brook authored
282
    NANDFlashState *s = (NANDFlashState *) opaque;
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
    qemu_put_byte(f, s->cle);
    qemu_put_byte(f, s->ale);
    qemu_put_byte(f, s->ce);
    qemu_put_byte(f, s->wp);
    qemu_put_byte(f, s->gnd);
    qemu_put_buffer(f, s->io, sizeof(s->io));
    qemu_put_be32(f, s->ioaddr - s->io);
    qemu_put_be32(f, s->iolen);

    qemu_put_be32s(f, &s->cmd);
    qemu_put_be32s(f, &s->addr);
    qemu_put_be32(f, s->addrlen);
    qemu_put_be32(f, s->status);
    qemu_put_be32(f, s->offset);
    /* XXX: do we want to save s->storage too? */
}

static int nand_load(QEMUFile *f, void *opaque, int version_id)
{
Paul Brook authored
302
    NANDFlashState *s = (NANDFlashState *) opaque;
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
    s->cle = qemu_get_byte(f);
    s->ale = qemu_get_byte(f);
    s->ce = qemu_get_byte(f);
    s->wp = qemu_get_byte(f);
    s->gnd = qemu_get_byte(f);
    qemu_get_buffer(f, s->io, sizeof(s->io));
    s->ioaddr = s->io + qemu_get_be32(f);
    s->iolen = qemu_get_be32(f);
    if (s->ioaddr >= s->io + sizeof(s->io) || s->ioaddr < s->io)
        return -EINVAL;

    qemu_get_be32s(f, &s->cmd);
    qemu_get_be32s(f, &s->addr);
    s->addrlen = qemu_get_be32(f);
    s->status = qemu_get_be32(f);
    s->offset = qemu_get_be32(f);
    return 0;
}
322
323
324
325
326
327
/*
 * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins.  Chip
 * outputs are R/B and eight I/O pins.
 *
 * CE, WP and R/B are active low.
 */
Paul Brook authored
328
void nand_setpins(NANDFlashState *s,
329
330
331
332
333
334
335
336
337
338
339
340
341
                int cle, int ale, int ce, int wp, int gnd)
{
    s->cle = cle;
    s->ale = ale;
    s->ce = ce;
    s->wp = wp;
    s->gnd = gnd;
    if (wp)
        s->status |= NAND_IOSTATUS_UNPROTCT;
    else
        s->status &= ~NAND_IOSTATUS_UNPROTCT;
}
Paul Brook authored
342
void nand_getpins(NANDFlashState *s, int *rb)
343
344
345
346
{
    *rb = 1;
}
Paul Brook authored
347
void nand_setio(NANDFlashState *s, uint8_t value)
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
{
    if (!s->ce && s->cle) {
        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
            if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2)
                return;
            if (value == NAND_CMD_RANDOMREAD1) {
                s->addr &= ~((1 << s->addr_shift) - 1);
                s->addrlen = 0;
                return;
            }
        }
        if (value == NAND_CMD_READ0)
            s->offset = 0;
	else if (value == NAND_CMD_READ1) {
            s->offset = 0x100;
            value = NAND_CMD_READ0;
        }
	else if (value == NAND_CMD_READ2) {
            s->offset = 1 << s->page_shift;
            value = NAND_CMD_READ0;
        }

        s->cmd = value;

        if (s->cmd == NAND_CMD_READSTATUS ||
                s->cmd == NAND_CMD_PAGEPROGRAM2 ||
                s->cmd == NAND_CMD_BLOCKERASE1 ||
                s->cmd == NAND_CMD_BLOCKERASE2 ||
                s->cmd == NAND_CMD_NOSERIALREAD2 ||
                s->cmd == NAND_CMD_RANDOMREAD2 ||
                s->cmd == NAND_CMD_RESET)
            nand_command(s);

        if (s->cmd != NAND_CMD_RANDOMREAD2) {
            s->addrlen = 0;
            s->addr = 0;
        }
    }

    if (s->ale) {
        s->addr |= value << (s->addrlen * 8);
        s->addrlen ++;

        if (s->addrlen == 1 && s->cmd == NAND_CMD_READID)
            nand_command(s);

        if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
                s->addrlen == 3 && (
                    s->cmd == NAND_CMD_READ0 ||
                    s->cmd == NAND_CMD_PAGEPROGRAM1))
            nand_command(s);
        if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
               s->addrlen == 4 && (
                    s->cmd == NAND_CMD_READ0 ||
                    s->cmd == NAND_CMD_PAGEPROGRAM1))
            nand_command(s);
    }

    if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) {
        if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift))
            s->io[s->iolen ++] = value;
    } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) {
        if ((s->addr & ((1 << s->addr_shift) - 1)) <
                (1 << s->page_shift) + (1 << s->oob_shift)) {
            s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value;
            s->addr ++;
        }
    }
}
Paul Brook authored
418
uint8_t nand_getio(NANDFlashState *s)
419
420
{
    int offset;
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
    /* Allow sequential reading */
    if (!s->iolen && s->cmd == NAND_CMD_READ0) {
        offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
        s->offset = 0;

        s->blk_load(s, s->addr, offset);
        if (s->gnd)
            s->iolen = (1 << s->page_shift) - offset;
        else
            s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
    }

    if (s->ce || s->iolen <= 0)
        return 0;

    s->iolen --;
    return *(s->ioaddr ++);
}
Paul Brook authored
441
NANDFlashState *nand_init(int manf_id, int chip_id)
442
443
{
    int pagesize;
Paul Brook authored
444
    NANDFlashState *s;
445
    int index;
446
447

    if (nand_flash_ids[chip_id].size == 0) {
448
        hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__);
449
450
    }
Paul Brook authored
451
    s = (NANDFlashState *) qemu_mallocz(sizeof(NANDFlashState));
452
453
454
    index = drive_get_index(IF_MTD, 0, 0);
    if (index != -1)
        s->bdrv = drives_table[index].bdrv;
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
    s->manf_id = manf_id;
    s->chip_id = chip_id;
    s->size = nand_flash_ids[s->chip_id].size << 20;
    if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
        s->page_shift = 11;
        s->erase_shift = 6;
    } else {
        s->page_shift = nand_flash_ids[s->chip_id].page_shift;
        s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
    }

    switch (1 << s->page_shift) {
    case 256:
        nand_init_256(s);
        break;
    case 512:
        nand_init_512(s);
        break;
    case 2048:
        nand_init_2048(s);
        break;
    default:
477
        hw_error("%s: Unsupported NAND block size.\n", __FUNCTION__);
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
    }

    pagesize = 1 << s->oob_shift;
    s->mem_oob = 1;
    if (s->bdrv && bdrv_getlength(s->bdrv) >=
                    (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
        pagesize = 0;
        s->mem_oob = 0;
    }

    if (!s->bdrv)
        pagesize += 1 << s->page_shift;
    if (pagesize)
        s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize),
                        0xff, s->pages * pagesize);
pbrook authored
493
494
495
    /* Give s->ioaddr a sane value in case we save state before it
       is used.  */
    s->ioaddr = s->io;
496
497
    register_savevm("nand", -1, 0, nand_save, nand_load, s);
498
499
500
501
    return s;
}
Paul Brook authored
502
void nand_done(NANDFlashState *s)
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
{
    if (s->bdrv) {
        bdrv_close(s->bdrv);
        bdrv_delete(s->bdrv);
    }

    if (!s->bdrv || s->mem_oob)
        free(s->storage);

    free(s);
}

#else

/* Program a single page */
Paul Brook authored
518
static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
{
    uint32_t off, page, sector, soff;
    uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
    if (PAGE(s->addr) >= s->pages)
        return;

    if (!s->bdrv) {
        memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
                        s->offset, s->io, s->iolen);
    } else if (s->mem_oob) {
        sector = SECTOR(s->addr);
        off = (s->addr & PAGE_MASK) + s->offset;
        soff = SECTOR_OFFSET(s->addr);
        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) {
            printf("%s: read error in sector %i\n", __FUNCTION__, sector);
            return;
        }

        memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off));
        if (off + s->iolen > PAGE_SIZE) {
            page = PAGE(s->addr);
            memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off,
                            MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
        }

        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1)
            printf("%s: write error in sector %i\n", __FUNCTION__, sector);
    } else {
        off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
        sector = off >> 9;
        soff = off & 0x1ff;
        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) {
            printf("%s: read error in sector %i\n", __FUNCTION__, sector);
            return;
        }

        memcpy(iobuf + soff, s->io, s->iolen);

        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1)
            printf("%s: write error in sector %i\n", __FUNCTION__, sector);
    }
    s->offset = 0;
}

/* Erase a single block */
Paul Brook authored
564
static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
{
    uint32_t i, page, addr;
    uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
    addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);

    if (PAGE(addr) >= s->pages)
        return;

    if (!s->bdrv) {
        memset(s->storage + PAGE_START(addr),
                        0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift);
    } else if (s->mem_oob) {
        memset(s->storage + (PAGE(addr) << OOB_SHIFT),
                        0xff, OOB_SIZE << s->erase_shift);
        i = SECTOR(addr);
        page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
        for (; i < page; i ++)
            if (bdrv_write(s->bdrv, i, iobuf, 1) == -1)
                printf("%s: write error in sector %i\n", __FUNCTION__, i);
    } else {
        addr = PAGE_START(addr);
        page = addr >> 9;
        if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
            printf("%s: read error in sector %i\n", __FUNCTION__, page);
        memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
        if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
            printf("%s: write error in sector %i\n", __FUNCTION__, page);

        memset(iobuf, 0xff, 0x200);
        i = (addr & ~0x1ff) + 0x200;
        for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
                        i < addr; i += 0x200)
            if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1)
                printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9);

        page = i >> 9;
        if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
            printf("%s: read error in sector %i\n", __FUNCTION__, page);
603
        memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
604
605
606
607
608
        if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
            printf("%s: write error in sector %i\n", __FUNCTION__, page);
    }
}
Paul Brook authored
609
static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
                uint32_t addr, int offset)
{
    if (PAGE(addr) >= s->pages)
        return;

    if (s->bdrv) {
        if (s->mem_oob) {
            if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1)
                printf("%s: read error in sector %i\n",
                                __FUNCTION__, SECTOR(addr));
            memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
                            s->storage + (PAGE(s->addr) << OOB_SHIFT),
                            OOB_SIZE);
            s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
        } else {
            if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
                                    s->io, (PAGE_SECTORS + 2)) == -1)
                printf("%s: read error in sector %i\n",
                                __FUNCTION__, PAGE_START(addr) >> 9);
            s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
        }
    } else {
        memcpy(s->io, s->storage + PAGE_START(s->addr) +
                        offset, PAGE_SIZE + OOB_SIZE - offset);
        s->ioaddr = s->io;
    }

    s->addr &= PAGE_SIZE - 1;
    s->addr += PAGE_SIZE;
}
Paul Brook authored
641
static void glue(nand_init_, PAGE_SIZE)(NANDFlashState *s)
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
{
    s->oob_shift = PAGE_SHIFT - 5;
    s->pages = s->size >> PAGE_SHIFT;
    s->addr_shift = ADDR_SHIFT;

    s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE);
    s->blk_write = glue(nand_blk_write_, PAGE_SIZE);
    s->blk_load = glue(nand_blk_load_, PAGE_SIZE);
}

# undef PAGE_SIZE
# undef PAGE_SHIFT
# undef PAGE_SECTORS
# undef ADDR_SHIFT
#endif	/* NAND_IO */