xref: /openbmc/qemu/hw/block/nand.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * Flash NAND memory emulation.  Based on "16M x 8 Bit NAND Flash
349ab747fSPaolo Bonzini  * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from
449ab747fSPaolo Bonzini  * Samsung Electronic.
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Copyright (c) 2006 Openedhand Ltd.
749ab747fSPaolo Bonzini  * Written by Andrzej Zaborowski <balrog@zabor.org>
849ab747fSPaolo Bonzini  *
949ab747fSPaolo Bonzini  * Support for additional features based on "MT29F2G16ABCWP 2Gx16"
1049ab747fSPaolo Bonzini  * datasheet from Micron Technology and "NAND02G-B2C" datasheet
1149ab747fSPaolo Bonzini  * from ST Microelectronics.
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * This code is licensed under the GNU GPL v2.
1449ab747fSPaolo Bonzini  *
1549ab747fSPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
1649ab747fSPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
1749ab747fSPaolo Bonzini  */
1849ab747fSPaolo Bonzini 
1949ab747fSPaolo Bonzini #ifndef NAND_IO
2049ab747fSPaolo Bonzini 
2174c0e474SPeter Maydell #include "qemu/osdep.h"
2249ab747fSPaolo Bonzini #include "hw/hw.h"
23a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
24ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
2549ab747fSPaolo Bonzini #include "hw/block/flash.h"
264be74634SMarkus Armbruster #include "sysemu/block-backend.h"
27d6454270SMarkus Armbruster #include "migration/vmstate.h"
28da34e65cSMarkus Armbruster #include "qapi/error.h"
2949ab747fSPaolo Bonzini #include "qemu/error-report.h"
300b8fa32fSMarkus Armbruster #include "qemu/module.h"
31db1015e9SEduardo Habkost #include "qom/object.h"
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini # define NAND_CMD_READ0         0x00
3449ab747fSPaolo Bonzini # define NAND_CMD_READ1         0x01
3549ab747fSPaolo Bonzini # define NAND_CMD_READ2         0x50
3649ab747fSPaolo Bonzini # define NAND_CMD_LPREAD2       0x30
3749ab747fSPaolo Bonzini # define NAND_CMD_NOSERIALREAD2 0x35
3849ab747fSPaolo Bonzini # define NAND_CMD_RANDOMREAD1   0x05
3949ab747fSPaolo Bonzini # define NAND_CMD_RANDOMREAD2   0xe0
4049ab747fSPaolo Bonzini # define NAND_CMD_READID        0x90
4149ab747fSPaolo Bonzini # define NAND_CMD_RESET         0xff
4249ab747fSPaolo Bonzini # define NAND_CMD_PAGEPROGRAM1  0x80
4349ab747fSPaolo Bonzini # define NAND_CMD_PAGEPROGRAM2  0x10
4449ab747fSPaolo Bonzini # define NAND_CMD_CACHEPROGRAM2 0x15
4549ab747fSPaolo Bonzini # define NAND_CMD_BLOCKERASE1   0x60
4649ab747fSPaolo Bonzini # define NAND_CMD_BLOCKERASE2   0xd0
4749ab747fSPaolo Bonzini # define NAND_CMD_READSTATUS    0x70
4849ab747fSPaolo Bonzini # define NAND_CMD_COPYBACKPRG1  0x85
4949ab747fSPaolo Bonzini 
5049ab747fSPaolo Bonzini # define NAND_IOSTATUS_ERROR    (1 << 0)
5149ab747fSPaolo Bonzini # define NAND_IOSTATUS_PLANE0   (1 << 1)
5249ab747fSPaolo Bonzini # define NAND_IOSTATUS_PLANE1   (1 << 2)
5349ab747fSPaolo Bonzini # define NAND_IOSTATUS_PLANE2   (1 << 3)
5449ab747fSPaolo Bonzini # define NAND_IOSTATUS_PLANE3   (1 << 4)
5549ab747fSPaolo Bonzini # define NAND_IOSTATUS_READY    (1 << 6)
5649ab747fSPaolo Bonzini # define NAND_IOSTATUS_UNPROTCT (1 << 7)
5749ab747fSPaolo Bonzini 
5849ab747fSPaolo Bonzini # define MAX_PAGE       0x800
5949ab747fSPaolo Bonzini # define MAX_OOB        0x40
6049ab747fSPaolo Bonzini 
6149ab747fSPaolo Bonzini typedef struct NANDFlashState NANDFlashState;
6249ab747fSPaolo Bonzini struct NANDFlashState {
637426aa72SPeter Crosthwaite     DeviceState parent_obj;
647426aa72SPeter Crosthwaite 
6549ab747fSPaolo Bonzini     uint8_t manf_id, chip_id;
6649ab747fSPaolo Bonzini     uint8_t buswidth; /* in BYTES */
6749ab747fSPaolo Bonzini     int size, pages;
6849ab747fSPaolo Bonzini     int page_shift, oob_shift, erase_shift, addr_shift;
6949ab747fSPaolo Bonzini     uint8_t *storage;
704be74634SMarkus Armbruster     BlockBackend *blk;
7149ab747fSPaolo Bonzini     int mem_oob;
7249ab747fSPaolo Bonzini 
7349ab747fSPaolo Bonzini     uint8_t cle, ale, ce, wp, gnd;
7449ab747fSPaolo Bonzini 
7549ab747fSPaolo Bonzini     uint8_t io[MAX_PAGE + MAX_OOB + 0x400];
7649ab747fSPaolo Bonzini     uint8_t *ioaddr;
7749ab747fSPaolo Bonzini     int iolen;
7849ab747fSPaolo Bonzini 
7949ab747fSPaolo Bonzini     uint32_t cmd;
8049ab747fSPaolo Bonzini     uint64_t addr;
8149ab747fSPaolo Bonzini     int addrlen;
8249ab747fSPaolo Bonzini     int status;
8349ab747fSPaolo Bonzini     int offset;
8449ab747fSPaolo Bonzini 
8549ab747fSPaolo Bonzini     void (*blk_write)(NANDFlashState *s);
8649ab747fSPaolo Bonzini     void (*blk_erase)(NANDFlashState *s);
872e3e09b3SPhilippe Mathieu-Daudé     /*
882e3e09b3SPhilippe Mathieu-Daudé      * Returns %true when block containing (@addr + @offset) is
892e3e09b3SPhilippe Mathieu-Daudé      * successfully loaded, otherwise %false.
902e3e09b3SPhilippe Mathieu-Daudé      */
912e3e09b3SPhilippe Mathieu-Daudé     bool (*blk_load)(NANDFlashState *s, uint64_t addr, unsigned offset);
9249ab747fSPaolo Bonzini 
9349ab747fSPaolo Bonzini     uint32_t ioaddr_vmstate;
9449ab747fSPaolo Bonzini };
9549ab747fSPaolo Bonzini 
96e12078ccSPeter Crosthwaite #define TYPE_NAND "nand"
97e12078ccSPeter Crosthwaite 
OBJECT_DECLARE_SIMPLE_TYPE(NANDFlashState,NAND)988063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(NANDFlashState, NAND)
99e12078ccSPeter Crosthwaite 
10049ab747fSPaolo Bonzini static void mem_and(uint8_t *dest, const uint8_t *src, size_t n)
10149ab747fSPaolo Bonzini {
10249ab747fSPaolo Bonzini     /* Like memcpy() but we logical-AND the data into the destination */
10349ab747fSPaolo Bonzini     int i;
10449ab747fSPaolo Bonzini     for (i = 0; i < n; i++) {
10549ab747fSPaolo Bonzini         dest[i] &= src[i];
10649ab747fSPaolo Bonzini     }
10749ab747fSPaolo Bonzini }
10849ab747fSPaolo Bonzini 
10949ab747fSPaolo Bonzini # define NAND_NO_AUTOINCR   0x00000001
11049ab747fSPaolo Bonzini # define NAND_BUSWIDTH_16   0x00000002
11149ab747fSPaolo Bonzini # define NAND_NO_PADDING    0x00000004
11249ab747fSPaolo Bonzini # define NAND_CACHEPRG      0x00000008
11349ab747fSPaolo Bonzini # define NAND_COPYBACK      0x00000010
11449ab747fSPaolo Bonzini # define NAND_IS_AND        0x00000020
11549ab747fSPaolo Bonzini # define NAND_4PAGE_ARRAY   0x00000040
11649ab747fSPaolo Bonzini # define NAND_NO_READRDY    0x00000100
11749ab747fSPaolo Bonzini # define NAND_SAMSUNG_LP    (NAND_NO_PADDING | NAND_COPYBACK)
11849ab747fSPaolo Bonzini 
11949ab747fSPaolo Bonzini # define NAND_IO
12049ab747fSPaolo Bonzini 
12149ab747fSPaolo Bonzini # define PAGE(addr)          ((addr) >> ADDR_SHIFT)
1229c572725SJiaxun Yang # define PAGE_START(page)    (PAGE(page) * (NAND_PAGE_SIZE + OOB_SIZE))
12349ab747fSPaolo Bonzini # define PAGE_MASK           ((1 << ADDR_SHIFT) - 1)
12449ab747fSPaolo Bonzini # define OOB_SHIFT           (PAGE_SHIFT - 5)
12549ab747fSPaolo Bonzini # define OOB_SIZE            (1 << OOB_SHIFT)
12649ab747fSPaolo Bonzini # define SECTOR(addr)        ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT))
12749ab747fSPaolo Bonzini # define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8))
12849ab747fSPaolo Bonzini 
1299c572725SJiaxun Yang # define NAND_PAGE_SIZE     256
13049ab747fSPaolo Bonzini # define PAGE_SHIFT         8
13149ab747fSPaolo Bonzini # define PAGE_SECTORS       1
13249ab747fSPaolo Bonzini # define ADDR_SHIFT         8
13349ab747fSPaolo Bonzini # include "nand.c"
1349c572725SJiaxun Yang # define NAND_PAGE_SIZE     512
13549ab747fSPaolo Bonzini # define PAGE_SHIFT         9
13649ab747fSPaolo Bonzini # define PAGE_SECTORS       1
13749ab747fSPaolo Bonzini # define ADDR_SHIFT         8
13849ab747fSPaolo Bonzini # include "nand.c"
1399c572725SJiaxun Yang # define NAND_PAGE_SIZE     2048
14049ab747fSPaolo Bonzini # define PAGE_SHIFT         11
14149ab747fSPaolo Bonzini # define PAGE_SECTORS       4
14249ab747fSPaolo Bonzini # define ADDR_SHIFT         16
14349ab747fSPaolo Bonzini # include "nand.c"
14449ab747fSPaolo Bonzini 
1459d1cc1d0SPhilippe Mathieu-Daudé /* Information based on Linux drivers/mtd/nand/raw/nand_ids.c */
14649ab747fSPaolo Bonzini static const struct {
14749ab747fSPaolo Bonzini     int size;
14849ab747fSPaolo Bonzini     int width;
14949ab747fSPaolo Bonzini     int page_shift;
15049ab747fSPaolo Bonzini     int erase_shift;
15149ab747fSPaolo Bonzini     uint32_t options;
15249ab747fSPaolo Bonzini } nand_flash_ids[0x100] = {
15349ab747fSPaolo Bonzini     [0 ... 0xff] = { 0 },
15449ab747fSPaolo Bonzini 
15549ab747fSPaolo Bonzini     [0x6b] = { 4,   8,  9, 4, 0 },
15649ab747fSPaolo Bonzini     [0xe3] = { 4,   8,  9, 4, 0 },
15749ab747fSPaolo Bonzini     [0xe5] = { 4,   8,  9, 4, 0 },
15849ab747fSPaolo Bonzini     [0xd6] = { 8,   8,  9, 4, 0 },
15949ab747fSPaolo Bonzini     [0xe6] = { 8,   8,  9, 4, 0 },
16049ab747fSPaolo Bonzini 
16149ab747fSPaolo Bonzini     [0x33] = { 16,  8,  9, 5, 0 },
16249ab747fSPaolo Bonzini     [0x73] = { 16,  8,  9, 5, 0 },
16349ab747fSPaolo Bonzini     [0x43] = { 16,  16, 9, 5, NAND_BUSWIDTH_16 },
16449ab747fSPaolo Bonzini     [0x53] = { 16,  16, 9, 5, NAND_BUSWIDTH_16 },
16549ab747fSPaolo Bonzini 
16649ab747fSPaolo Bonzini     [0x35] = { 32,  8,  9, 5, 0 },
16749ab747fSPaolo Bonzini     [0x75] = { 32,  8,  9, 5, 0 },
16849ab747fSPaolo Bonzini     [0x45] = { 32,  16, 9, 5, NAND_BUSWIDTH_16 },
16949ab747fSPaolo Bonzini     [0x55] = { 32,  16, 9, 5, NAND_BUSWIDTH_16 },
17049ab747fSPaolo Bonzini 
17149ab747fSPaolo Bonzini     [0x36] = { 64,  8,  9, 5, 0 },
17249ab747fSPaolo Bonzini     [0x76] = { 64,  8,  9, 5, 0 },
17349ab747fSPaolo Bonzini     [0x46] = { 64,  16, 9, 5, NAND_BUSWIDTH_16 },
17449ab747fSPaolo Bonzini     [0x56] = { 64,  16, 9, 5, NAND_BUSWIDTH_16 },
17549ab747fSPaolo Bonzini 
17649ab747fSPaolo Bonzini     [0x78] = { 128, 8,  9, 5, 0 },
17749ab747fSPaolo Bonzini     [0x39] = { 128, 8,  9, 5, 0 },
17849ab747fSPaolo Bonzini     [0x79] = { 128, 8,  9, 5, 0 },
17949ab747fSPaolo Bonzini     [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
18049ab747fSPaolo Bonzini     [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
18149ab747fSPaolo Bonzini     [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
18249ab747fSPaolo Bonzini     [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
18349ab747fSPaolo Bonzini 
18449ab747fSPaolo Bonzini     [0x71] = { 256, 8,  9, 5, 0 },
18549ab747fSPaolo Bonzini 
18649ab747fSPaolo Bonzini     /*
18749ab747fSPaolo Bonzini      * These are the new chips with large page size. The pagesize and the
18849ab747fSPaolo Bonzini      * erasesize is determined from the extended id bytes
18949ab747fSPaolo Bonzini      */
19049ab747fSPaolo Bonzini # define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR)
19149ab747fSPaolo Bonzini # define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
19249ab747fSPaolo Bonzini 
19349ab747fSPaolo Bonzini     /* 512 Megabit */
19449ab747fSPaolo Bonzini     [0xa2] = { 64,   8,  0, 0, LP_OPTIONS },
19549ab747fSPaolo Bonzini     [0xf2] = { 64,   8,  0, 0, LP_OPTIONS },
19649ab747fSPaolo Bonzini     [0xb2] = { 64,   16, 0, 0, LP_OPTIONS16 },
19749ab747fSPaolo Bonzini     [0xc2] = { 64,   16, 0, 0, LP_OPTIONS16 },
19849ab747fSPaolo Bonzini 
19949ab747fSPaolo Bonzini     /* 1 Gigabit */
20049ab747fSPaolo Bonzini     [0xa1] = { 128,  8,  0, 0, LP_OPTIONS },
20149ab747fSPaolo Bonzini     [0xf1] = { 128,  8,  0, 0, LP_OPTIONS },
20249ab747fSPaolo Bonzini     [0xb1] = { 128,  16, 0, 0, LP_OPTIONS16 },
20349ab747fSPaolo Bonzini     [0xc1] = { 128,  16, 0, 0, LP_OPTIONS16 },
20449ab747fSPaolo Bonzini 
20549ab747fSPaolo Bonzini     /* 2 Gigabit */
20649ab747fSPaolo Bonzini     [0xaa] = { 256,  8,  0, 0, LP_OPTIONS },
20749ab747fSPaolo Bonzini     [0xda] = { 256,  8,  0, 0, LP_OPTIONS },
20849ab747fSPaolo Bonzini     [0xba] = { 256,  16, 0, 0, LP_OPTIONS16 },
20949ab747fSPaolo Bonzini     [0xca] = { 256,  16, 0, 0, LP_OPTIONS16 },
21049ab747fSPaolo Bonzini 
21149ab747fSPaolo Bonzini     /* 4 Gigabit */
21249ab747fSPaolo Bonzini     [0xac] = { 512,  8,  0, 0, LP_OPTIONS },
21349ab747fSPaolo Bonzini     [0xdc] = { 512,  8,  0, 0, LP_OPTIONS },
21449ab747fSPaolo Bonzini     [0xbc] = { 512,  16, 0, 0, LP_OPTIONS16 },
21549ab747fSPaolo Bonzini     [0xcc] = { 512,  16, 0, 0, LP_OPTIONS16 },
21649ab747fSPaolo Bonzini 
21749ab747fSPaolo Bonzini     /* 8 Gigabit */
21849ab747fSPaolo Bonzini     [0xa3] = { 1024, 8,  0, 0, LP_OPTIONS },
21949ab747fSPaolo Bonzini     [0xd3] = { 1024, 8,  0, 0, LP_OPTIONS },
22049ab747fSPaolo Bonzini     [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 },
22149ab747fSPaolo Bonzini     [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 },
22249ab747fSPaolo Bonzini 
22349ab747fSPaolo Bonzini     /* 16 Gigabit */
22449ab747fSPaolo Bonzini     [0xa5] = { 2048, 8,  0, 0, LP_OPTIONS },
22549ab747fSPaolo Bonzini     [0xd5] = { 2048, 8,  0, 0, LP_OPTIONS },
22649ab747fSPaolo Bonzini     [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 },
22749ab747fSPaolo Bonzini     [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 },
22849ab747fSPaolo Bonzini };
22949ab747fSPaolo Bonzini 
nand_reset(DeviceState * dev)23049ab747fSPaolo Bonzini static void nand_reset(DeviceState *dev)
23149ab747fSPaolo Bonzini {
232e12078ccSPeter Crosthwaite     NANDFlashState *s = NAND(dev);
23349ab747fSPaolo Bonzini     s->cmd = NAND_CMD_READ0;
23449ab747fSPaolo Bonzini     s->addr = 0;
23549ab747fSPaolo Bonzini     s->addrlen = 0;
23649ab747fSPaolo Bonzini     s->iolen = 0;
23749ab747fSPaolo Bonzini     s->offset = 0;
23849ab747fSPaolo Bonzini     s->status &= NAND_IOSTATUS_UNPROTCT;
23949ab747fSPaolo Bonzini     s->status |= NAND_IOSTATUS_READY;
24049ab747fSPaolo Bonzini }
24149ab747fSPaolo Bonzini 
nand_pushio_byte(NANDFlashState * s,uint8_t value)24249ab747fSPaolo Bonzini static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value)
24349ab747fSPaolo Bonzini {
24449ab747fSPaolo Bonzini     s->ioaddr[s->iolen++] = value;
24549ab747fSPaolo Bonzini     for (value = s->buswidth; --value;) {
24649ab747fSPaolo Bonzini         s->ioaddr[s->iolen++] = 0;
24749ab747fSPaolo Bonzini     }
24849ab747fSPaolo Bonzini }
24949ab747fSPaolo Bonzini 
2507a86544fSPhilippe Mathieu-Daudé /*
2517a86544fSPhilippe Mathieu-Daudé  * nand_load_block: Load block containing (s->addr + @offset).
2527a86544fSPhilippe Mathieu-Daudé  * Returns length of data available at @offset in this block.
2537a86544fSPhilippe Mathieu-Daudé  */
nand_load_block(NANDFlashState * s,unsigned offset)2547a86544fSPhilippe Mathieu-Daudé static unsigned nand_load_block(NANDFlashState *s, unsigned offset)
2557a86544fSPhilippe Mathieu-Daudé {
2567a86544fSPhilippe Mathieu-Daudé     unsigned iolen;
2577a86544fSPhilippe Mathieu-Daudé 
258d39fdfffSPhilippe Mathieu-Daudé     if (!s->blk_load(s, s->addr, offset)) {
259d39fdfffSPhilippe Mathieu-Daudé         return 0;
260d39fdfffSPhilippe Mathieu-Daudé     }
2617a86544fSPhilippe Mathieu-Daudé 
2627a86544fSPhilippe Mathieu-Daudé     iolen = (1 << s->page_shift);
2637a86544fSPhilippe Mathieu-Daudé     if (s->gnd) {
2647a86544fSPhilippe Mathieu-Daudé         iolen += 1 << s->oob_shift;
2657a86544fSPhilippe Mathieu-Daudé     }
2667a86544fSPhilippe Mathieu-Daudé     assert(offset <= iolen);
2677a86544fSPhilippe Mathieu-Daudé     iolen -= offset;
2687a86544fSPhilippe Mathieu-Daudé 
2697a86544fSPhilippe Mathieu-Daudé     return iolen;
2707a86544fSPhilippe Mathieu-Daudé }
2717a86544fSPhilippe Mathieu-Daudé 
nand_command(NANDFlashState * s)27249ab747fSPaolo Bonzini static void nand_command(NANDFlashState *s)
27349ab747fSPaolo Bonzini {
27449ab747fSPaolo Bonzini     switch (s->cmd) {
27549ab747fSPaolo Bonzini     case NAND_CMD_READ0:
27649ab747fSPaolo Bonzini         s->iolen = 0;
27749ab747fSPaolo Bonzini         break;
27849ab747fSPaolo Bonzini 
27949ab747fSPaolo Bonzini     case NAND_CMD_READID:
28049ab747fSPaolo Bonzini         s->ioaddr = s->io;
28149ab747fSPaolo Bonzini         s->iolen = 0;
28249ab747fSPaolo Bonzini         nand_pushio_byte(s, s->manf_id);
28349ab747fSPaolo Bonzini         nand_pushio_byte(s, s->chip_id);
28449ab747fSPaolo Bonzini         nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */
28549ab747fSPaolo Bonzini         if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
28649ab747fSPaolo Bonzini             /* Page Size, Block Size, Spare Size; bit 6 indicates
28749ab747fSPaolo Bonzini              * 8 vs 16 bit width NAND.
28849ab747fSPaolo Bonzini              */
28949ab747fSPaolo Bonzini             nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15);
29049ab747fSPaolo Bonzini         } else {
29149ab747fSPaolo Bonzini             nand_pushio_byte(s, 0xc0); /* Multi-plane */
29249ab747fSPaolo Bonzini         }
29349ab747fSPaolo Bonzini         break;
29449ab747fSPaolo Bonzini 
29549ab747fSPaolo Bonzini     case NAND_CMD_RANDOMREAD2:
29649ab747fSPaolo Bonzini     case NAND_CMD_NOSERIALREAD2:
29749ab747fSPaolo Bonzini         if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP))
29849ab747fSPaolo Bonzini             break;
2997a86544fSPhilippe Mathieu-Daudé         s->iolen = nand_load_block(s, s->addr & ((1 << s->addr_shift) - 1));
30049ab747fSPaolo Bonzini         break;
30149ab747fSPaolo Bonzini 
30249ab747fSPaolo Bonzini     case NAND_CMD_RESET:
303e12078ccSPeter Crosthwaite         nand_reset(DEVICE(s));
30449ab747fSPaolo Bonzini         break;
30549ab747fSPaolo Bonzini 
30649ab747fSPaolo Bonzini     case NAND_CMD_PAGEPROGRAM1:
30749ab747fSPaolo Bonzini         s->ioaddr = s->io;
30849ab747fSPaolo Bonzini         s->iolen = 0;
30949ab747fSPaolo Bonzini         break;
31049ab747fSPaolo Bonzini 
31149ab747fSPaolo Bonzini     case NAND_CMD_PAGEPROGRAM2:
31249ab747fSPaolo Bonzini         if (s->wp) {
31349ab747fSPaolo Bonzini             s->blk_write(s);
31449ab747fSPaolo Bonzini         }
31549ab747fSPaolo Bonzini         break;
31649ab747fSPaolo Bonzini 
31749ab747fSPaolo Bonzini     case NAND_CMD_BLOCKERASE1:
31849ab747fSPaolo Bonzini         break;
31949ab747fSPaolo Bonzini 
32049ab747fSPaolo Bonzini     case NAND_CMD_BLOCKERASE2:
32149ab747fSPaolo Bonzini         s->addr &= (1ull << s->addrlen * 8) - 1;
3221984745eSPeter Crosthwaite         s->addr <<= nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP ?
3231984745eSPeter Crosthwaite                                                                     16 : 8;
32449ab747fSPaolo Bonzini 
32549ab747fSPaolo Bonzini         if (s->wp) {
32649ab747fSPaolo Bonzini             s->blk_erase(s);
32749ab747fSPaolo Bonzini         }
32849ab747fSPaolo Bonzini         break;
32949ab747fSPaolo Bonzini 
33049ab747fSPaolo Bonzini     case NAND_CMD_READSTATUS:
33149ab747fSPaolo Bonzini         s->ioaddr = s->io;
33249ab747fSPaolo Bonzini         s->iolen = 0;
33349ab747fSPaolo Bonzini         nand_pushio_byte(s, s->status);
33449ab747fSPaolo Bonzini         break;
33549ab747fSPaolo Bonzini 
33649ab747fSPaolo Bonzini     default:
337a89f364aSAlistair Francis         printf("%s: Unknown NAND command 0x%02x\n", __func__, s->cmd);
33849ab747fSPaolo Bonzini     }
33949ab747fSPaolo Bonzini }
34049ab747fSPaolo Bonzini 
nand_pre_save(void * opaque)34144b1ff31SDr. David Alan Gilbert static int nand_pre_save(void *opaque)
34249ab747fSPaolo Bonzini {
343e12078ccSPeter Crosthwaite     NANDFlashState *s = NAND(opaque);
34449ab747fSPaolo Bonzini 
34549ab747fSPaolo Bonzini     s->ioaddr_vmstate = s->ioaddr - s->io;
34644b1ff31SDr. David Alan Gilbert 
34744b1ff31SDr. David Alan Gilbert     return 0;
34849ab747fSPaolo Bonzini }
34949ab747fSPaolo Bonzini 
nand_post_load(void * opaque,int version_id)35049ab747fSPaolo Bonzini static int nand_post_load(void *opaque, int version_id)
35149ab747fSPaolo Bonzini {
352e12078ccSPeter Crosthwaite     NANDFlashState *s = NAND(opaque);
35349ab747fSPaolo Bonzini 
35449ab747fSPaolo Bonzini     if (s->ioaddr_vmstate > sizeof(s->io)) {
35549ab747fSPaolo Bonzini         return -EINVAL;
35649ab747fSPaolo Bonzini     }
35749ab747fSPaolo Bonzini     s->ioaddr = s->io + s->ioaddr_vmstate;
35849ab747fSPaolo Bonzini 
35949ab747fSPaolo Bonzini     return 0;
36049ab747fSPaolo Bonzini }
36149ab747fSPaolo Bonzini 
36249ab747fSPaolo Bonzini static const VMStateDescription vmstate_nand = {
36349ab747fSPaolo Bonzini     .name = "nand",
36449ab747fSPaolo Bonzini     .version_id = 1,
36549ab747fSPaolo Bonzini     .minimum_version_id = 1,
36649ab747fSPaolo Bonzini     .pre_save = nand_pre_save,
36749ab747fSPaolo Bonzini     .post_load = nand_post_load,
3687d5dc0a3SRichard Henderson     .fields = (const VMStateField[]) {
36949ab747fSPaolo Bonzini         VMSTATE_UINT8(cle, NANDFlashState),
37049ab747fSPaolo Bonzini         VMSTATE_UINT8(ale, NANDFlashState),
37149ab747fSPaolo Bonzini         VMSTATE_UINT8(ce, NANDFlashState),
37249ab747fSPaolo Bonzini         VMSTATE_UINT8(wp, NANDFlashState),
37349ab747fSPaolo Bonzini         VMSTATE_UINT8(gnd, NANDFlashState),
37449ab747fSPaolo Bonzini         VMSTATE_BUFFER(io, NANDFlashState),
37549ab747fSPaolo Bonzini         VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState),
37649ab747fSPaolo Bonzini         VMSTATE_INT32(iolen, NANDFlashState),
37749ab747fSPaolo Bonzini         VMSTATE_UINT32(cmd, NANDFlashState),
37849ab747fSPaolo Bonzini         VMSTATE_UINT64(addr, NANDFlashState),
37949ab747fSPaolo Bonzini         VMSTATE_INT32(addrlen, NANDFlashState),
38049ab747fSPaolo Bonzini         VMSTATE_INT32(status, NANDFlashState),
38149ab747fSPaolo Bonzini         VMSTATE_INT32(offset, NANDFlashState),
38249ab747fSPaolo Bonzini         /* XXX: do we want to save s->storage too? */
38349ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
38449ab747fSPaolo Bonzini     }
38549ab747fSPaolo Bonzini };
38649ab747fSPaolo Bonzini 
nand_realize(DeviceState * dev,Error ** errp)387d47a5d9bSPeter Crosthwaite static void nand_realize(DeviceState *dev, Error **errp)
38849ab747fSPaolo Bonzini {
38949ab747fSPaolo Bonzini     int pagesize;
390e12078ccSPeter Crosthwaite     NANDFlashState *s = NAND(dev);
391a17c17a2SKevin Wolf     int ret;
392a17c17a2SKevin Wolf 
39349ab747fSPaolo Bonzini 
39449ab747fSPaolo Bonzini     s->buswidth = nand_flash_ids[s->chip_id].width >> 3;
39549ab747fSPaolo Bonzini     s->size = nand_flash_ids[s->chip_id].size << 20;
39649ab747fSPaolo Bonzini     if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
39749ab747fSPaolo Bonzini         s->page_shift = 11;
39849ab747fSPaolo Bonzini         s->erase_shift = 6;
39949ab747fSPaolo Bonzini     } else {
40049ab747fSPaolo Bonzini         s->page_shift = nand_flash_ids[s->chip_id].page_shift;
40149ab747fSPaolo Bonzini         s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
40249ab747fSPaolo Bonzini     }
40349ab747fSPaolo Bonzini 
40449ab747fSPaolo Bonzini     switch (1 << s->page_shift) {
40549ab747fSPaolo Bonzini     case 256:
40649ab747fSPaolo Bonzini         nand_init_256(s);
40749ab747fSPaolo Bonzini         break;
40849ab747fSPaolo Bonzini     case 512:
40949ab747fSPaolo Bonzini         nand_init_512(s);
41049ab747fSPaolo Bonzini         break;
41149ab747fSPaolo Bonzini     case 2048:
41249ab747fSPaolo Bonzini         nand_init_2048(s);
41349ab747fSPaolo Bonzini         break;
41449ab747fSPaolo Bonzini     default:
415eec5eb42SGonglei         error_setg(errp, "Unsupported NAND block size %#x",
416d47a5d9bSPeter Crosthwaite                    1 << s->page_shift);
417d47a5d9bSPeter Crosthwaite         return;
41849ab747fSPaolo Bonzini     }
41949ab747fSPaolo Bonzini 
42049ab747fSPaolo Bonzini     pagesize = 1 << s->oob_shift;
42149ab747fSPaolo Bonzini     s->mem_oob = 1;
4224be74634SMarkus Armbruster     if (s->blk) {
42386b1cf32SKevin Wolf         if (!blk_supports_write_perm(s->blk)) {
424d47a5d9bSPeter Crosthwaite             error_setg(errp, "Can't use a read-only drive");
425d47a5d9bSPeter Crosthwaite             return;
42649ab747fSPaolo Bonzini         }
427a17c17a2SKevin Wolf         ret = blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
428a17c17a2SKevin Wolf                            BLK_PERM_ALL, errp);
429a17c17a2SKevin Wolf         if (ret < 0) {
430a17c17a2SKevin Wolf             return;
431a17c17a2SKevin Wolf         }
4324be74634SMarkus Armbruster         if (blk_getlength(s->blk) >=
43349ab747fSPaolo Bonzini                 (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
43449ab747fSPaolo Bonzini             pagesize = 0;
43549ab747fSPaolo Bonzini             s->mem_oob = 0;
43649ab747fSPaolo Bonzini         }
43749ab747fSPaolo Bonzini     } else {
43849ab747fSPaolo Bonzini         pagesize += 1 << s->page_shift;
43949ab747fSPaolo Bonzini     }
44049ab747fSPaolo Bonzini     if (pagesize) {
44149ab747fSPaolo Bonzini         s->storage = (uint8_t *) memset(g_malloc(s->pages * pagesize),
44249ab747fSPaolo Bonzini                         0xff, s->pages * pagesize);
44349ab747fSPaolo Bonzini     }
44449ab747fSPaolo Bonzini     /* Give s->ioaddr a sane value in case we save state before it is used. */
44549ab747fSPaolo Bonzini     s->ioaddr = s->io;
44649ab747fSPaolo Bonzini }
44749ab747fSPaolo Bonzini 
44849ab747fSPaolo Bonzini static Property nand_properties[] = {
44949ab747fSPaolo Bonzini     DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0),
45049ab747fSPaolo Bonzini     DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0),
4514be74634SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", NANDFlashState, blk),
45249ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
45349ab747fSPaolo Bonzini };
45449ab747fSPaolo Bonzini 
nand_class_init(ObjectClass * klass,void * data)45549ab747fSPaolo Bonzini static void nand_class_init(ObjectClass *klass, void *data)
45649ab747fSPaolo Bonzini {
45749ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
45849ab747fSPaolo Bonzini 
459d47a5d9bSPeter Crosthwaite     dc->realize = nand_realize;
460*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, nand_reset);
46149ab747fSPaolo Bonzini     dc->vmsd = &vmstate_nand;
4624f67d30bSMarc-André Lureau     device_class_set_props(dc, nand_properties);
46384aab60cSGan Qixin     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
46449ab747fSPaolo Bonzini }
46549ab747fSPaolo Bonzini 
46649ab747fSPaolo Bonzini static const TypeInfo nand_info = {
467e12078ccSPeter Crosthwaite     .name          = TYPE_NAND,
4687426aa72SPeter Crosthwaite     .parent        = TYPE_DEVICE,
46949ab747fSPaolo Bonzini     .instance_size = sizeof(NANDFlashState),
47049ab747fSPaolo Bonzini     .class_init    = nand_class_init,
47149ab747fSPaolo Bonzini };
47249ab747fSPaolo Bonzini 
nand_register_types(void)47349ab747fSPaolo Bonzini static void nand_register_types(void)
47449ab747fSPaolo Bonzini {
47549ab747fSPaolo Bonzini     type_register_static(&nand_info);
47649ab747fSPaolo Bonzini }
47749ab747fSPaolo Bonzini 
47849ab747fSPaolo Bonzini /*
47949ab747fSPaolo Bonzini  * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins.  Chip
48049ab747fSPaolo Bonzini  * outputs are R/B and eight I/O pins.
48149ab747fSPaolo Bonzini  *
48249ab747fSPaolo Bonzini  * CE, WP and R/B are active low.
48349ab747fSPaolo Bonzini  */
nand_setpins(DeviceState * dev,uint8_t cle,uint8_t ale,uint8_t ce,uint8_t wp,uint8_t gnd)48449ab747fSPaolo Bonzini void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale,
48549ab747fSPaolo Bonzini                   uint8_t ce, uint8_t wp, uint8_t gnd)
48649ab747fSPaolo Bonzini {
487e12078ccSPeter Crosthwaite     NANDFlashState *s = NAND(dev);
488e12078ccSPeter Crosthwaite 
48949ab747fSPaolo Bonzini     s->cle = cle;
49049ab747fSPaolo Bonzini     s->ale = ale;
49149ab747fSPaolo Bonzini     s->ce = ce;
49249ab747fSPaolo Bonzini     s->wp = wp;
49349ab747fSPaolo Bonzini     s->gnd = gnd;
4941984745eSPeter Crosthwaite     if (wp) {
49549ab747fSPaolo Bonzini         s->status |= NAND_IOSTATUS_UNPROTCT;
4961984745eSPeter Crosthwaite     } else {
49749ab747fSPaolo Bonzini         s->status &= ~NAND_IOSTATUS_UNPROTCT;
49849ab747fSPaolo Bonzini     }
4991984745eSPeter Crosthwaite }
50049ab747fSPaolo Bonzini 
nand_getpins(DeviceState * dev,int * rb)50149ab747fSPaolo Bonzini void nand_getpins(DeviceState *dev, int *rb)
50249ab747fSPaolo Bonzini {
50349ab747fSPaolo Bonzini     *rb = 1;
50449ab747fSPaolo Bonzini }
50549ab747fSPaolo Bonzini 
nand_setio(DeviceState * dev,uint32_t value)50649ab747fSPaolo Bonzini void nand_setio(DeviceState *dev, uint32_t value)
50749ab747fSPaolo Bonzini {
50849ab747fSPaolo Bonzini     int i;
509e12078ccSPeter Crosthwaite     NANDFlashState *s = NAND(dev);
510e12078ccSPeter Crosthwaite 
51149ab747fSPaolo Bonzini     if (!s->ce && s->cle) {
51249ab747fSPaolo Bonzini         if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
51349ab747fSPaolo Bonzini             if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2)
51449ab747fSPaolo Bonzini                 return;
51549ab747fSPaolo Bonzini             if (value == NAND_CMD_RANDOMREAD1) {
51649ab747fSPaolo Bonzini                 s->addr &= ~((1 << s->addr_shift) - 1);
51749ab747fSPaolo Bonzini                 s->addrlen = 0;
51849ab747fSPaolo Bonzini                 return;
51949ab747fSPaolo Bonzini             }
52049ab747fSPaolo Bonzini         }
5211984745eSPeter Crosthwaite         if (value == NAND_CMD_READ0) {
52249ab747fSPaolo Bonzini             s->offset = 0;
5231984745eSPeter Crosthwaite         } else if (value == NAND_CMD_READ1) {
52449ab747fSPaolo Bonzini             s->offset = 0x100;
52549ab747fSPaolo Bonzini             value = NAND_CMD_READ0;
5261984745eSPeter Crosthwaite         } else if (value == NAND_CMD_READ2) {
52749ab747fSPaolo Bonzini             s->offset = 1 << s->page_shift;
52849ab747fSPaolo Bonzini             value = NAND_CMD_READ0;
52949ab747fSPaolo Bonzini         }
53049ab747fSPaolo Bonzini 
53149ab747fSPaolo Bonzini         s->cmd = value;
53249ab747fSPaolo Bonzini 
53349ab747fSPaolo Bonzini         if (s->cmd == NAND_CMD_READSTATUS ||
53449ab747fSPaolo Bonzini                 s->cmd == NAND_CMD_PAGEPROGRAM2 ||
53549ab747fSPaolo Bonzini                 s->cmd == NAND_CMD_BLOCKERASE1 ||
53649ab747fSPaolo Bonzini                 s->cmd == NAND_CMD_BLOCKERASE2 ||
53749ab747fSPaolo Bonzini                 s->cmd == NAND_CMD_NOSERIALREAD2 ||
53849ab747fSPaolo Bonzini                 s->cmd == NAND_CMD_RANDOMREAD2 ||
5391984745eSPeter Crosthwaite                 s->cmd == NAND_CMD_RESET) {
54049ab747fSPaolo Bonzini             nand_command(s);
5411984745eSPeter Crosthwaite         }
54249ab747fSPaolo Bonzini 
54349ab747fSPaolo Bonzini         if (s->cmd != NAND_CMD_RANDOMREAD2) {
54449ab747fSPaolo Bonzini             s->addrlen = 0;
54549ab747fSPaolo Bonzini         }
54649ab747fSPaolo Bonzini     }
54749ab747fSPaolo Bonzini 
54849ab747fSPaolo Bonzini     if (s->ale) {
54949ab747fSPaolo Bonzini         unsigned int shift = s->addrlen * 8;
550a184e74fSRabin Vincent         uint64_t mask = ~(0xffull << shift);
551a184e74fSRabin Vincent         uint64_t v = (uint64_t)value << shift;
55249ab747fSPaolo Bonzini 
55349ab747fSPaolo Bonzini         s->addr = (s->addr & mask) | v;
55449ab747fSPaolo Bonzini         s->addrlen ++;
55549ab747fSPaolo Bonzini 
55649ab747fSPaolo Bonzini         switch (s->addrlen) {
55749ab747fSPaolo Bonzini         case 1:
55849ab747fSPaolo Bonzini             if (s->cmd == NAND_CMD_READID) {
55949ab747fSPaolo Bonzini                 nand_command(s);
56049ab747fSPaolo Bonzini             }
56149ab747fSPaolo Bonzini             break;
56249ab747fSPaolo Bonzini         case 2: /* fix cache address as a byte address */
56349ab747fSPaolo Bonzini             s->addr <<= (s->buswidth - 1);
56449ab747fSPaolo Bonzini             break;
56549ab747fSPaolo Bonzini         case 3:
56649ab747fSPaolo Bonzini             if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
56749ab747fSPaolo Bonzini                     (s->cmd == NAND_CMD_READ0 ||
56849ab747fSPaolo Bonzini                      s->cmd == NAND_CMD_PAGEPROGRAM1)) {
56949ab747fSPaolo Bonzini                 nand_command(s);
57049ab747fSPaolo Bonzini             }
57149ab747fSPaolo Bonzini             break;
57249ab747fSPaolo Bonzini         case 4:
57349ab747fSPaolo Bonzini             if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
57449ab747fSPaolo Bonzini                     nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */
57549ab747fSPaolo Bonzini                     (s->cmd == NAND_CMD_READ0 ||
57649ab747fSPaolo Bonzini                      s->cmd == NAND_CMD_PAGEPROGRAM1)) {
57749ab747fSPaolo Bonzini                 nand_command(s);
57849ab747fSPaolo Bonzini             }
57949ab747fSPaolo Bonzini             break;
58049ab747fSPaolo Bonzini         case 5:
58149ab747fSPaolo Bonzini             if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
58249ab747fSPaolo Bonzini                     nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */
58349ab747fSPaolo Bonzini                     (s->cmd == NAND_CMD_READ0 ||
58449ab747fSPaolo Bonzini                      s->cmd == NAND_CMD_PAGEPROGRAM1)) {
58549ab747fSPaolo Bonzini                 nand_command(s);
58649ab747fSPaolo Bonzini             }
58749ab747fSPaolo Bonzini             break;
58849ab747fSPaolo Bonzini         default:
58949ab747fSPaolo Bonzini             break;
59049ab747fSPaolo Bonzini         }
59149ab747fSPaolo Bonzini     }
59249ab747fSPaolo Bonzini 
59349ab747fSPaolo Bonzini     if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) {
59449ab747fSPaolo Bonzini         if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) {
59549ab747fSPaolo Bonzini             for (i = s->buswidth; i--; value >>= 8) {
59649ab747fSPaolo Bonzini                 s->io[s->iolen ++] = (uint8_t) (value & 0xff);
59749ab747fSPaolo Bonzini             }
59849ab747fSPaolo Bonzini         }
59949ab747fSPaolo Bonzini     } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) {
60049ab747fSPaolo Bonzini         if ((s->addr & ((1 << s->addr_shift) - 1)) <
60149ab747fSPaolo Bonzini                 (1 << s->page_shift) + (1 << s->oob_shift)) {
60249ab747fSPaolo Bonzini             for (i = s->buswidth; i--; s->addr++, value >>= 8) {
60349ab747fSPaolo Bonzini                 s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] =
60449ab747fSPaolo Bonzini                     (uint8_t) (value & 0xff);
60549ab747fSPaolo Bonzini             }
60649ab747fSPaolo Bonzini         }
60749ab747fSPaolo Bonzini     }
60849ab747fSPaolo Bonzini }
60949ab747fSPaolo Bonzini 
nand_getio(DeviceState * dev)61049ab747fSPaolo Bonzini uint32_t nand_getio(DeviceState *dev)
61149ab747fSPaolo Bonzini {
61249ab747fSPaolo Bonzini     int offset;
61349ab747fSPaolo Bonzini     uint32_t x = 0;
614e12078ccSPeter Crosthwaite     NANDFlashState *s = NAND(dev);
61549ab747fSPaolo Bonzini 
61649ab747fSPaolo Bonzini     /* Allow sequential reading */
61749ab747fSPaolo Bonzini     if (!s->iolen && s->cmd == NAND_CMD_READ0) {
61849ab747fSPaolo Bonzini         offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
61949ab747fSPaolo Bonzini         s->offset = 0;
6207a86544fSPhilippe Mathieu-Daudé         s->iolen = nand_load_block(s, offset);
62149ab747fSPaolo Bonzini     }
62249ab747fSPaolo Bonzini 
6231984745eSPeter Crosthwaite     if (s->ce || s->iolen <= 0) {
62449ab747fSPaolo Bonzini         return 0;
6251984745eSPeter Crosthwaite     }
62649ab747fSPaolo Bonzini 
62749ab747fSPaolo Bonzini     for (offset = s->buswidth; offset--;) {
62849ab747fSPaolo Bonzini         x |= s->ioaddr[offset] << (offset << 3);
62949ab747fSPaolo Bonzini     }
63049ab747fSPaolo Bonzini     /* after receiving READ STATUS command all subsequent reads will
63149ab747fSPaolo Bonzini      * return the status register value until another command is issued
63249ab747fSPaolo Bonzini      */
63349ab747fSPaolo Bonzini     if (s->cmd != NAND_CMD_READSTATUS) {
63449ab747fSPaolo Bonzini         s->addr   += s->buswidth;
63549ab747fSPaolo Bonzini         s->ioaddr += s->buswidth;
63649ab747fSPaolo Bonzini         s->iolen  -= s->buswidth;
63749ab747fSPaolo Bonzini     }
63849ab747fSPaolo Bonzini     return x;
63949ab747fSPaolo Bonzini }
64049ab747fSPaolo Bonzini 
nand_getbuswidth(DeviceState * dev)64149ab747fSPaolo Bonzini uint32_t nand_getbuswidth(DeviceState *dev)
64249ab747fSPaolo Bonzini {
64349ab747fSPaolo Bonzini     NANDFlashState *s = (NANDFlashState *) dev;
64449ab747fSPaolo Bonzini     return s->buswidth << 3;
64549ab747fSPaolo Bonzini }
64649ab747fSPaolo Bonzini 
nand_init(BlockBackend * blk,int manf_id,int chip_id)6474be74634SMarkus Armbruster DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id)
64849ab747fSPaolo Bonzini {
64949ab747fSPaolo Bonzini     DeviceState *dev;
65049ab747fSPaolo Bonzini 
65149ab747fSPaolo Bonzini     if (nand_flash_ids[chip_id].size == 0) {
652a89f364aSAlistair Francis         hw_error("%s: Unsupported NAND chip ID.\n", __func__);
65349ab747fSPaolo Bonzini     }
6544e3a6778SMarkus Armbruster     dev = qdev_new(TYPE_NAND);
65549ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "manufacturer_id", manf_id);
65649ab747fSPaolo Bonzini     qdev_prop_set_uint8(dev, "chip_id", chip_id);
6574be74634SMarkus Armbruster     if (blk) {
658934df912SMarkus Armbruster         qdev_prop_set_drive_err(dev, "drive", blk, &error_fatal);
65949ab747fSPaolo Bonzini     }
66049ab747fSPaolo Bonzini 
661ce189ab2SMarkus Armbruster     qdev_realize(dev, NULL, &error_fatal);
66249ab747fSPaolo Bonzini     return dev;
66349ab747fSPaolo Bonzini }
66449ab747fSPaolo Bonzini 
66549ab747fSPaolo Bonzini type_init(nand_register_types)
66649ab747fSPaolo Bonzini 
66749ab747fSPaolo Bonzini #else
66849ab747fSPaolo Bonzini 
66949ab747fSPaolo Bonzini /* Program a single page */
6709c572725SJiaxun Yang static void glue(nand_blk_write_, NAND_PAGE_SIZE)(NANDFlashState *s)
67149ab747fSPaolo Bonzini {
67249ab747fSPaolo Bonzini     uint64_t off, page, sector, soff;
67349ab747fSPaolo Bonzini     uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
67449ab747fSPaolo Bonzini     if (PAGE(s->addr) >= s->pages)
67549ab747fSPaolo Bonzini         return;
67649ab747fSPaolo Bonzini 
6774be74634SMarkus Armbruster     if (!s->blk) {
67849ab747fSPaolo Bonzini         mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
67949ab747fSPaolo Bonzini                         s->offset, s->io, s->iolen);
68049ab747fSPaolo Bonzini     } else if (s->mem_oob) {
68149ab747fSPaolo Bonzini         sector = SECTOR(s->addr);
68249ab747fSPaolo Bonzini         off = (s->addr & PAGE_MASK) + s->offset;
68349ab747fSPaolo Bonzini         soff = SECTOR_OFFSET(s->addr);
684a9262f55SAlberto Faria         if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS,
685a9262f55SAlberto Faria                       PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) {
68649ab747fSPaolo Bonzini             printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
68749ab747fSPaolo Bonzini             return;
68849ab747fSPaolo Bonzini         }
68949ab747fSPaolo Bonzini 
6909c572725SJiaxun Yang         mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, NAND_PAGE_SIZE - off));
6919c572725SJiaxun Yang         if (off + s->iolen > NAND_PAGE_SIZE) {
69249ab747fSPaolo Bonzini             page = PAGE(s->addr);
6939c572725SJiaxun Yang             mem_and(s->storage + (page << OOB_SHIFT), s->io + NAND_PAGE_SIZE - off,
6949c572725SJiaxun Yang                             MIN(OOB_SIZE, off + s->iolen - NAND_PAGE_SIZE));
69549ab747fSPaolo Bonzini         }
69649ab747fSPaolo Bonzini 
697a9262f55SAlberto Faria         if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS,
698a9262f55SAlberto Faria                        PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) {
69949ab747fSPaolo Bonzini             printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
70049ab747fSPaolo Bonzini         }
70149ab747fSPaolo Bonzini     } else {
70249ab747fSPaolo Bonzini         off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
70349ab747fSPaolo Bonzini         sector = off >> 9;
70449ab747fSPaolo Bonzini         soff = off & 0x1ff;
705a9262f55SAlberto Faria         if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS,
706a9262f55SAlberto Faria                       (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) {
70749ab747fSPaolo Bonzini             printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
70849ab747fSPaolo Bonzini             return;
70949ab747fSPaolo Bonzini         }
71049ab747fSPaolo Bonzini 
71149ab747fSPaolo Bonzini         mem_and(iobuf + soff, s->io, s->iolen);
71249ab747fSPaolo Bonzini 
713a9262f55SAlberto Faria         if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS,
714a9262f55SAlberto Faria                        (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) {
71549ab747fSPaolo Bonzini             printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
71649ab747fSPaolo Bonzini         }
71749ab747fSPaolo Bonzini     }
71849ab747fSPaolo Bonzini     s->offset = 0;
71949ab747fSPaolo Bonzini }
72049ab747fSPaolo Bonzini 
72149ab747fSPaolo Bonzini /* Erase a single block */
7229c572725SJiaxun Yang static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s)
72349ab747fSPaolo Bonzini {
72449ab747fSPaolo Bonzini     uint64_t i, page, addr;
72549ab747fSPaolo Bonzini     uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
72649ab747fSPaolo Bonzini     addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);
72749ab747fSPaolo Bonzini 
7281984745eSPeter Crosthwaite     if (PAGE(addr) >= s->pages) {
72949ab747fSPaolo Bonzini         return;
7301984745eSPeter Crosthwaite     }
73149ab747fSPaolo Bonzini 
7324be74634SMarkus Armbruster     if (!s->blk) {
73349ab747fSPaolo Bonzini         memset(s->storage + PAGE_START(addr),
7349c572725SJiaxun Yang                         0xff, (NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift);
73549ab747fSPaolo Bonzini     } else if (s->mem_oob) {
73649ab747fSPaolo Bonzini         memset(s->storage + (PAGE(addr) << OOB_SHIFT),
73749ab747fSPaolo Bonzini                         0xff, OOB_SIZE << s->erase_shift);
73849ab747fSPaolo Bonzini         i = SECTOR(addr);
7398e37ca6dSRicard Wanderlof         page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift)));
74049ab747fSPaolo Bonzini         for (; i < page; i ++)
741a9262f55SAlberto Faria             if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS,
742a9262f55SAlberto Faria                            BDRV_SECTOR_SIZE, iobuf, 0) < 0) {
74349ab747fSPaolo Bonzini                 printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
74449ab747fSPaolo Bonzini             }
74549ab747fSPaolo Bonzini     } else {
74649ab747fSPaolo Bonzini         addr = PAGE_START(addr);
74749ab747fSPaolo Bonzini         page = addr >> 9;
748a9262f55SAlberto Faria         if (blk_pread(s->blk, page << BDRV_SECTOR_BITS,
749a9262f55SAlberto Faria                       BDRV_SECTOR_SIZE, iobuf, 0) < 0) {
75049ab747fSPaolo Bonzini             printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
75149ab747fSPaolo Bonzini         }
75249ab747fSPaolo Bonzini         memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
753a9262f55SAlberto Faria         if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS,
754a9262f55SAlberto Faria                        BDRV_SECTOR_SIZE, iobuf, 0) < 0) {
75549ab747fSPaolo Bonzini             printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
75649ab747fSPaolo Bonzini         }
75749ab747fSPaolo Bonzini 
75849ab747fSPaolo Bonzini         memset(iobuf, 0xff, 0x200);
75949ab747fSPaolo Bonzini         i = (addr & ~0x1ff) + 0x200;
7609c572725SJiaxun Yang         for (addr += ((NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
7611984745eSPeter Crosthwaite                         i < addr; i += 0x200) {
762a9262f55SAlberto Faria             if (blk_pwrite(s->blk, i, BDRV_SECTOR_SIZE, iobuf, 0) < 0) {
76349ab747fSPaolo Bonzini                 printf("%s: write error in sector %" PRIu64 "\n",
76449ab747fSPaolo Bonzini                        __func__, i >> 9);
76549ab747fSPaolo Bonzini             }
7661984745eSPeter Crosthwaite         }
76749ab747fSPaolo Bonzini 
76849ab747fSPaolo Bonzini         page = i >> 9;
769a9262f55SAlberto Faria         if (blk_pread(s->blk, page << BDRV_SECTOR_BITS,
770a9262f55SAlberto Faria                       BDRV_SECTOR_SIZE, iobuf, 0) < 0) {
77149ab747fSPaolo Bonzini             printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
77249ab747fSPaolo Bonzini         }
77349ab747fSPaolo Bonzini         memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
774a9262f55SAlberto Faria         if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS,
775a9262f55SAlberto Faria                        BDRV_SECTOR_SIZE, iobuf, 0) < 0) {
77649ab747fSPaolo Bonzini             printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
77749ab747fSPaolo Bonzini         }
77849ab747fSPaolo Bonzini     }
77949ab747fSPaolo Bonzini }
78049ab747fSPaolo Bonzini 
7812e3e09b3SPhilippe Mathieu-Daudé static bool glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s,
7822e3e09b3SPhilippe Mathieu-Daudé                                                  uint64_t addr, unsigned offset)
78349ab747fSPaolo Bonzini {
7841984745eSPeter Crosthwaite     if (PAGE(addr) >= s->pages) {
7852e3e09b3SPhilippe Mathieu-Daudé         return false;
7861984745eSPeter Crosthwaite     }
78749ab747fSPaolo Bonzini 
788d39fdfffSPhilippe Mathieu-Daudé     if (offset > NAND_PAGE_SIZE + OOB_SIZE) {
789d39fdfffSPhilippe Mathieu-Daudé         return false;
790d39fdfffSPhilippe Mathieu-Daudé     }
791d39fdfffSPhilippe Mathieu-Daudé 
7924be74634SMarkus Armbruster     if (s->blk) {
79349ab747fSPaolo Bonzini         if (s->mem_oob) {
794a9262f55SAlberto Faria             if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS,
795a9262f55SAlberto Faria                           PAGE_SECTORS << BDRV_SECTOR_BITS, s->io, 0) < 0) {
79649ab747fSPaolo Bonzini                 printf("%s: read error in sector %" PRIu64 "\n",
79749ab747fSPaolo Bonzini                                 __func__, SECTOR(addr));
79849ab747fSPaolo Bonzini             }
7999c572725SJiaxun Yang             memcpy(s->io + SECTOR_OFFSET(s->addr) + NAND_PAGE_SIZE,
80049ab747fSPaolo Bonzini                             s->storage + (PAGE(s->addr) << OOB_SHIFT),
80149ab747fSPaolo Bonzini                             OOB_SIZE);
80249ab747fSPaolo Bonzini             s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
80349ab747fSPaolo Bonzini         } else {
804a9262f55SAlberto Faria             if (blk_pread(s->blk, PAGE_START(addr),
805a9262f55SAlberto Faria                           (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, s->io, 0)
806a9262f55SAlberto Faria                 < 0) {
80749ab747fSPaolo Bonzini                 printf("%s: read error in sector %" PRIu64 "\n",
80849ab747fSPaolo Bonzini                                 __func__, PAGE_START(addr) >> 9);
80949ab747fSPaolo Bonzini             }
81049ab747fSPaolo Bonzini             s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
81149ab747fSPaolo Bonzini         }
81249ab747fSPaolo Bonzini     } else {
81349ab747fSPaolo Bonzini         memcpy(s->io, s->storage + PAGE_START(s->addr) +
8149c572725SJiaxun Yang                         offset, NAND_PAGE_SIZE + OOB_SIZE - offset);
81549ab747fSPaolo Bonzini         s->ioaddr = s->io;
81649ab747fSPaolo Bonzini     }
8172e3e09b3SPhilippe Mathieu-Daudé 
8182e3e09b3SPhilippe Mathieu-Daudé     return true;
81949ab747fSPaolo Bonzini }
82049ab747fSPaolo Bonzini 
8219c572725SJiaxun Yang static void glue(nand_init_, NAND_PAGE_SIZE)(NANDFlashState *s)
82249ab747fSPaolo Bonzini {
82349ab747fSPaolo Bonzini     s->oob_shift = PAGE_SHIFT - 5;
82449ab747fSPaolo Bonzini     s->pages = s->size >> PAGE_SHIFT;
82549ab747fSPaolo Bonzini     s->addr_shift = ADDR_SHIFT;
82649ab747fSPaolo Bonzini 
8279c572725SJiaxun Yang     s->blk_erase = glue(nand_blk_erase_, NAND_PAGE_SIZE);
8289c572725SJiaxun Yang     s->blk_write = glue(nand_blk_write_, NAND_PAGE_SIZE);
8299c572725SJiaxun Yang     s->blk_load = glue(nand_blk_load_, NAND_PAGE_SIZE);
83049ab747fSPaolo Bonzini }
83149ab747fSPaolo Bonzini 
8329c572725SJiaxun Yang # undef NAND_PAGE_SIZE
83349ab747fSPaolo Bonzini # undef PAGE_SHIFT
83449ab747fSPaolo Bonzini # undef PAGE_SECTORS
83549ab747fSPaolo Bonzini # undef ADDR_SHIFT
83649ab747fSPaolo Bonzini #endif /* NAND_IO */
837