hw/nand: Support large NAND devices

Add support for NAND devices of over 1Gb.

Signed-off-by: Juha Riihimäki <juha.riihimaki@nokia.com>
[Riku Voipio: Fixes and restructuring patchset]
Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
[Peter Maydell: More fixes and cleanups for upstream submission]
Signed-off-by:  Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrzej Zaborowski <andrew.zaborowski@intel.com>
This commit is contained in:
Juha Riihimäki 2011-07-29 16:35:20 +01:00 committed by Andrzej Zaborowski
parent 522f253ca8
commit d5f2fd586f
1 changed files with 27 additions and 21 deletions

View File

@ -6,6 +6,10 @@
* Copyright (c) 2006 Openedhand Ltd. * Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org> * Written by Andrzej Zaborowski <balrog@zabor.org>
* *
* Support for additional features based on "MT29F2G16ABCWP 2Gx16"
* datasheet from Micron Technology and "NAND02G-B2C" datasheet
* from ST Microelectronics.
*
* This code is licensed under the GNU GPL v2. * This code is licensed under the GNU GPL v2.
*/ */
@ -57,14 +61,15 @@ struct NANDFlashState {
uint8_t *ioaddr; uint8_t *ioaddr;
int iolen; int iolen;
uint32_t cmd, addr; uint32_t cmd;
uint64_t addr;
int addrlen; int addrlen;
int status; int status;
int offset; int offset;
void (*blk_write)(NANDFlashState *s); void (*blk_write)(NANDFlashState *s);
void (*blk_erase)(NANDFlashState *s); void (*blk_erase)(NANDFlashState *s);
void (*blk_load)(NANDFlashState *s, uint32_t addr, int offset); void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset);
uint32_t ioaddr_vmstate; uint32_t ioaddr_vmstate;
}; };
@ -318,7 +323,7 @@ static const VMStateDescription vmstate_nand = {
VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState),
VMSTATE_INT32(iolen, NANDFlashState), VMSTATE_INT32(iolen, NANDFlashState),
VMSTATE_UINT32(cmd, NANDFlashState), VMSTATE_UINT32(cmd, NANDFlashState),
VMSTATE_UINT32(addr, NANDFlashState), VMSTATE_UINT64(addr, NANDFlashState),
VMSTATE_INT32(addrlen, NANDFlashState), VMSTATE_INT32(addrlen, NANDFlashState),
VMSTATE_INT32(status, NANDFlashState), VMSTATE_INT32(status, NANDFlashState),
VMSTATE_INT32(offset, NANDFlashState), VMSTATE_INT32(offset, NANDFlashState),
@ -432,7 +437,7 @@ uint8_t nand_getio(NANDFlashState *s)
/* Allow sequential reading */ /* Allow sequential reading */
if (!s->iolen && s->cmd == NAND_CMD_READ0) { if (!s->iolen && s->cmd == NAND_CMD_READ0) {
offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
s->offset = 0; s->offset = 0;
s->blk_load(s, s->addr, offset); s->blk_load(s, s->addr, offset);
@ -526,7 +531,7 @@ void nand_done(NANDFlashState *s)
/* Program a single page */ /* Program a single page */
static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
{ {
uint32_t off, page, sector, soff; uint64_t off, page, sector, soff;
uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
if (PAGE(s->addr) >= s->pages) if (PAGE(s->addr) >= s->pages)
return; return;
@ -539,7 +544,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
off = (s->addr & PAGE_MASK) + s->offset; off = (s->addr & PAGE_MASK) + s->offset;
soff = SECTOR_OFFSET(s->addr); soff = SECTOR_OFFSET(s->addr);
if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) { if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) {
printf("%s: read error in sector %i\n", __FUNCTION__, sector); printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
return; return;
} }
@ -551,20 +556,20 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
} }
if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1)
printf("%s: write error in sector %i\n", __FUNCTION__, sector); printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
} else { } else {
off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
sector = off >> 9; sector = off >> 9;
soff = off & 0x1ff; soff = off & 0x1ff;
if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) { if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) {
printf("%s: read error in sector %i\n", __FUNCTION__, sector); printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
return; return;
} }
memcpy(iobuf + soff, s->io, s->iolen); memcpy(iobuf + soff, s->io, s->iolen);
if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1)
printf("%s: write error in sector %i\n", __FUNCTION__, sector); printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
} }
s->offset = 0; s->offset = 0;
} }
@ -572,7 +577,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
/* Erase a single block */ /* Erase a single block */
static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
{ {
uint32_t i, page, addr; uint64_t i, page, addr;
uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);
@ -589,34 +594,35 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
for (; i < page; i ++) for (; i < page; i ++)
if (bdrv_write(s->bdrv, i, iobuf, 1) == -1) if (bdrv_write(s->bdrv, i, iobuf, 1) == -1)
printf("%s: write error in sector %i\n", __FUNCTION__, i); printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
} else { } else {
addr = PAGE_START(addr); addr = PAGE_START(addr);
page = addr >> 9; page = addr >> 9;
if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
printf("%s: read error in sector %i\n", __FUNCTION__, page); printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
printf("%s: write error in sector %i\n", __FUNCTION__, page); printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
memset(iobuf, 0xff, 0x200); memset(iobuf, 0xff, 0x200);
i = (addr & ~0x1ff) + 0x200; i = (addr & ~0x1ff) + 0x200;
for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
i < addr; i += 0x200) i < addr; i += 0x200)
if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1) if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1)
printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9); printf("%s: write error in sector %" PRIu64 "\n",
__func__, i >> 9);
page = i >> 9; page = i >> 9;
if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
printf("%s: read error in sector %i\n", __FUNCTION__, page); printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
printf("%s: write error in sector %i\n", __FUNCTION__, page); printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
} }
} }
static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
uint32_t addr, int offset) uint64_t addr, int offset)
{ {
if (PAGE(addr) >= s->pages) if (PAGE(addr) >= s->pages)
return; return;
@ -624,8 +630,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
if (s->bdrv) { if (s->bdrv) {
if (s->mem_oob) { if (s->mem_oob) {
if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1) if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1)
printf("%s: read error in sector %i\n", printf("%s: read error in sector %" PRIu64 "\n",
__FUNCTION__, SECTOR(addr)); __func__, SECTOR(addr));
memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
s->storage + (PAGE(s->addr) << OOB_SHIFT), s->storage + (PAGE(s->addr) << OOB_SHIFT),
OOB_SIZE); OOB_SIZE);
@ -633,8 +639,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
} else { } else {
if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9, if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
s->io, (PAGE_SECTORS + 2)) == -1) s->io, (PAGE_SECTORS + 2)) == -1)
printf("%s: read error in sector %i\n", printf("%s: read error in sector %" PRIu64 "\n",
__FUNCTION__, PAGE_START(addr) >> 9); __func__, PAGE_START(addr) >> 9);
s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
} }
} else { } else {