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