Commit 3e3d5815cbc1fdbf33adbe55f63ede3acead886f
1 parent
18c9b560
NAND Flash memory emulation and ECC calculation helpers for use by NAND controllers.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2753 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
723 additions
and
0 deletions
ecc.h
0 → 100644
| 1 | +/* | ||
| 2 | + * Calculate Error-correcting Codes. Used by NAND Flash controllers | ||
| 3 | + * (not by NAND chips). | ||
| 4 | + * | ||
| 5 | + * Copyright (c) 2006 Openedhand Ltd. | ||
| 6 | + * Written by Andrzej Zaborowski <balrog@zabor.org> | ||
| 7 | + * | ||
| 8 | + * This code is licensed under the GNU GPL v2. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +struct ecc_state_s { | ||
| 12 | + uint8_t cp; /* Column parity */ | ||
| 13 | + uint16_t lp[2]; /* Line parity */ | ||
| 14 | + uint16_t count; | ||
| 15 | +}; | ||
| 16 | + | ||
| 17 | +/* | ||
| 18 | + * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux. | ||
| 19 | + */ | ||
| 20 | +static const uint8_t nand_ecc_precalc_table[] = { | ||
| 21 | + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, | ||
| 22 | + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, | ||
| 23 | + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, | ||
| 24 | + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, | ||
| 25 | + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, | ||
| 26 | + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, | ||
| 27 | + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, | ||
| 28 | + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, | ||
| 29 | + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, | ||
| 30 | + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, | ||
| 31 | + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, | ||
| 32 | + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, | ||
| 33 | + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, | ||
| 34 | + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, | ||
| 35 | + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, | ||
| 36 | + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, | ||
| 37 | + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, | ||
| 38 | + 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, | ||
| 39 | + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, | ||
| 40 | + 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, | ||
| 41 | + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, | ||
| 42 | + 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, | ||
| 43 | + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, | ||
| 44 | + 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, | ||
| 45 | + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, | ||
| 46 | + 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, | ||
| 47 | + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, | ||
| 48 | + 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, | ||
| 49 | + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, | ||
| 50 | + 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, | ||
| 51 | + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, | ||
| 52 | + 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, | ||
| 53 | +}; | ||
| 54 | + | ||
| 55 | +/* Update ECC parity count. */ | ||
| 56 | +static inline uint8_t ecc_digest(struct ecc_state_s *s, uint8_t sample) | ||
| 57 | +{ | ||
| 58 | + uint8_t idx = nand_ecc_precalc_table[sample]; | ||
| 59 | + | ||
| 60 | + s->cp ^= idx & 0x3f; | ||
| 61 | + if (idx & 0x40) { | ||
| 62 | + s->lp[0] ^= ~s->count; | ||
| 63 | + s->lp[1] ^= s->count; | ||
| 64 | + } | ||
| 65 | + s->count ++; | ||
| 66 | + | ||
| 67 | + return sample; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +/* Reinitialise the counters. */ | ||
| 71 | +static inline void ecc_reset(struct ecc_state_s *s) | ||
| 72 | +{ | ||
| 73 | + s->lp[0] = 0x0000; | ||
| 74 | + s->lp[1] = 0x0000; | ||
| 75 | + s->cp = 0x00; | ||
| 76 | + s->count = 0; | ||
| 77 | +} |
hw/nand.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash | ||
| 3 | + * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from | ||
| 4 | + * Samsung Electronic. | ||
| 5 | + * | ||
| 6 | + * Copyright (c) 2006 Openedhand Ltd. | ||
| 7 | + * Written by Andrzej Zaborowski <balrog@zabor.org> | ||
| 8 | + * | ||
| 9 | + * This code is licensed under the GNU GPL v2. | ||
| 10 | + */ | ||
| 11 | + | ||
| 12 | +#ifndef NAND_IO | ||
| 13 | + | ||
| 14 | +# include "vl.h" | ||
| 15 | + | ||
| 16 | +# define NAND_CMD_READ0 0x00 | ||
| 17 | +# define NAND_CMD_READ1 0x01 | ||
| 18 | +# define NAND_CMD_READ2 0x50 | ||
| 19 | +# define NAND_CMD_LPREAD2 0x30 | ||
| 20 | +# define NAND_CMD_NOSERIALREAD2 0x35 | ||
| 21 | +# define NAND_CMD_RANDOMREAD1 0x05 | ||
| 22 | +# define NAND_CMD_RANDOMREAD2 0xe0 | ||
| 23 | +# define NAND_CMD_READID 0x90 | ||
| 24 | +# define NAND_CMD_RESET 0xff | ||
| 25 | +# define NAND_CMD_PAGEPROGRAM1 0x80 | ||
| 26 | +# define NAND_CMD_PAGEPROGRAM2 0x10 | ||
| 27 | +# define NAND_CMD_CACHEPROGRAM2 0x15 | ||
| 28 | +# define NAND_CMD_BLOCKERASE1 0x60 | ||
| 29 | +# define NAND_CMD_BLOCKERASE2 0xd0 | ||
| 30 | +# define NAND_CMD_READSTATUS 0x70 | ||
| 31 | +# define NAND_CMD_COPYBACKPRG1 0x85 | ||
| 32 | + | ||
| 33 | +# define NAND_IOSTATUS_ERROR (1 << 0) | ||
| 34 | +# define NAND_IOSTATUS_PLANE0 (1 << 1) | ||
| 35 | +# define NAND_IOSTATUS_PLANE1 (1 << 2) | ||
| 36 | +# define NAND_IOSTATUS_PLANE2 (1 << 3) | ||
| 37 | +# define NAND_IOSTATUS_PLANE3 (1 << 4) | ||
| 38 | +# define NAND_IOSTATUS_BUSY (1 << 6) | ||
| 39 | +# define NAND_IOSTATUS_UNPROTCT (1 << 7) | ||
| 40 | + | ||
| 41 | +# define MAX_PAGE 0x800 | ||
| 42 | +# define MAX_OOB 0x40 | ||
| 43 | + | ||
| 44 | +struct nand_flash_s { | ||
| 45 | + uint8_t manf_id, chip_id; | ||
| 46 | + int size, pages; | ||
| 47 | + int page_shift, oob_shift, erase_shift, addr_shift; | ||
| 48 | + uint8_t *storage; | ||
| 49 | + BlockDriverState *bdrv; | ||
| 50 | + int mem_oob; | ||
| 51 | + | ||
| 52 | + int cle, ale, ce, wp, gnd; | ||
| 53 | + | ||
| 54 | + uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; | ||
| 55 | + uint8_t *ioaddr; | ||
| 56 | + int iolen; | ||
| 57 | + | ||
| 58 | + uint32_t cmd, addr; | ||
| 59 | + int addrlen; | ||
| 60 | + int status; | ||
| 61 | + int offset; | ||
| 62 | + | ||
| 63 | + void (*blk_write)(struct nand_flash_s *s); | ||
| 64 | + void (*blk_erase)(struct nand_flash_s *s); | ||
| 65 | + void (*blk_load)(struct nand_flash_s *s, uint32_t addr, int offset); | ||
| 66 | +}; | ||
| 67 | + | ||
| 68 | +# define NAND_NO_AUTOINCR 0x00000001 | ||
| 69 | +# define NAND_BUSWIDTH_16 0x00000002 | ||
| 70 | +# define NAND_NO_PADDING 0x00000004 | ||
| 71 | +# define NAND_CACHEPRG 0x00000008 | ||
| 72 | +# define NAND_COPYBACK 0x00000010 | ||
| 73 | +# define NAND_IS_AND 0x00000020 | ||
| 74 | +# define NAND_4PAGE_ARRAY 0x00000040 | ||
| 75 | +# define NAND_NO_READRDY 0x00000100 | ||
| 76 | +# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) | ||
| 77 | + | ||
| 78 | +# define NAND_IO | ||
| 79 | + | ||
| 80 | +# define PAGE(addr) ((addr) >> ADDR_SHIFT) | ||
| 81 | +# define PAGE_START(page) (PAGE(page) * (PAGE_SIZE + OOB_SIZE)) | ||
| 82 | +# define PAGE_MASK ((1 << ADDR_SHIFT) - 1) | ||
| 83 | +# define OOB_SHIFT (PAGE_SHIFT - 5) | ||
| 84 | +# define OOB_SIZE (1 << OOB_SHIFT) | ||
| 85 | +# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) | ||
| 86 | +# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) | ||
| 87 | + | ||
| 88 | +# define PAGE_SIZE 256 | ||
| 89 | +# define PAGE_SHIFT 8 | ||
| 90 | +# define PAGE_SECTORS 1 | ||
| 91 | +# define ADDR_SHIFT 8 | ||
| 92 | +# include "nand.c" | ||
| 93 | +# define PAGE_SIZE 512 | ||
| 94 | +# define PAGE_SHIFT 9 | ||
| 95 | +# define PAGE_SECTORS 1 | ||
| 96 | +# define ADDR_SHIFT 8 | ||
| 97 | +# include "nand.c" | ||
| 98 | +# define PAGE_SIZE 2048 | ||
| 99 | +# define PAGE_SHIFT 11 | ||
| 100 | +# define PAGE_SECTORS 4 | ||
| 101 | +# define ADDR_SHIFT 16 | ||
| 102 | +# include "nand.c" | ||
| 103 | + | ||
| 104 | +/* Information based on Linux drivers/mtd/nand/nand_ids.c */ | ||
| 105 | +struct nand_info_s { | ||
| 106 | + int size; | ||
| 107 | + int width; | ||
| 108 | + int page_shift; | ||
| 109 | + int erase_shift; | ||
| 110 | + uint32_t options; | ||
| 111 | +} nand_flash_ids[0x100] = { | ||
| 112 | + [0 ... 0xff] = { 0 }, | ||
| 113 | + | ||
| 114 | + [0x6e] = { 1, 8, 8, 4, 0 }, | ||
| 115 | + [0x64] = { 2, 8, 8, 4, 0 }, | ||
| 116 | + [0x6b] = { 4, 8, 9, 4, 0 }, | ||
| 117 | + [0xe8] = { 1, 8, 8, 4, 0 }, | ||
| 118 | + [0xec] = { 1, 8, 8, 4, 0 }, | ||
| 119 | + [0xea] = { 2, 8, 8, 4, 0 }, | ||
| 120 | + [0xd5] = { 4, 8, 9, 4, 0 }, | ||
| 121 | + [0xe3] = { 4, 8, 9, 4, 0 }, | ||
| 122 | + [0xe5] = { 4, 8, 9, 4, 0 }, | ||
| 123 | + [0xd6] = { 8, 8, 9, 4, 0 }, | ||
| 124 | + | ||
| 125 | + [0x39] = { 8, 8, 9, 4, 0 }, | ||
| 126 | + [0xe6] = { 8, 8, 9, 4, 0 }, | ||
| 127 | + [0x49] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, | ||
| 128 | + [0x59] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, | ||
| 129 | + | ||
| 130 | + [0x33] = { 16, 8, 9, 5, 0 }, | ||
| 131 | + [0x73] = { 16, 8, 9, 5, 0 }, | ||
| 132 | + [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 133 | + [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 134 | + | ||
| 135 | + [0x35] = { 32, 8, 9, 5, 0 }, | ||
| 136 | + [0x75] = { 32, 8, 9, 5, 0 }, | ||
| 137 | + [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 138 | + [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 139 | + | ||
| 140 | + [0x36] = { 64, 8, 9, 5, 0 }, | ||
| 141 | + [0x76] = { 64, 8, 9, 5, 0 }, | ||
| 142 | + [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 143 | + [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 144 | + | ||
| 145 | + [0x78] = { 128, 8, 9, 5, 0 }, | ||
| 146 | + [0x39] = { 128, 8, 9, 5, 0 }, | ||
| 147 | + [0x79] = { 128, 8, 9, 5, 0 }, | ||
| 148 | + [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 149 | + [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 150 | + [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 151 | + [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, | ||
| 152 | + | ||
| 153 | + [0x71] = { 256, 8, 9, 5, 0 }, | ||
| 154 | + | ||
| 155 | + /* | ||
| 156 | + * These are the new chips with large page size. The pagesize and the | ||
| 157 | + * erasesize is determined from the extended id bytes | ||
| 158 | + */ | ||
| 159 | +# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) | ||
| 160 | +# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) | ||
| 161 | + | ||
| 162 | + /* 512 Megabit */ | ||
| 163 | + [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, | ||
| 164 | + [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, | ||
| 165 | + [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, | ||
| 166 | + [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, | ||
| 167 | + | ||
| 168 | + /* 1 Gigabit */ | ||
| 169 | + [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, | ||
| 170 | + [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, | ||
| 171 | + [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, | ||
| 172 | + [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, | ||
| 173 | + | ||
| 174 | + /* 2 Gigabit */ | ||
| 175 | + [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, | ||
| 176 | + [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, | ||
| 177 | + [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, | ||
| 178 | + [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, | ||
| 179 | + | ||
| 180 | + /* 4 Gigabit */ | ||
| 181 | + [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, | ||
| 182 | + [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, | ||
| 183 | + [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, | ||
| 184 | + [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, | ||
| 185 | + | ||
| 186 | + /* 8 Gigabit */ | ||
| 187 | + [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, | ||
| 188 | + [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, | ||
| 189 | + [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, | ||
| 190 | + [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, | ||
| 191 | + | ||
| 192 | + /* 16 Gigabit */ | ||
| 193 | + [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, | ||
| 194 | + [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, | ||
| 195 | + [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, | ||
| 196 | + [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, | ||
| 197 | +}; | ||
| 198 | + | ||
| 199 | +static void nand_reset(struct nand_flash_s *s) | ||
| 200 | +{ | ||
| 201 | + s->cmd = NAND_CMD_READ0; | ||
| 202 | + s->addr = 0; | ||
| 203 | + s->addrlen = 0; | ||
| 204 | + s->iolen = 0; | ||
| 205 | + s->offset = 0; | ||
| 206 | + s->status &= NAND_IOSTATUS_UNPROTCT; | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +static void nand_command(struct nand_flash_s *s) | ||
| 210 | +{ | ||
| 211 | + switch (s->cmd) { | ||
| 212 | + case NAND_CMD_READ0: | ||
| 213 | + s->iolen = 0; | ||
| 214 | + break; | ||
| 215 | + | ||
| 216 | + case NAND_CMD_READID: | ||
| 217 | + s->io[0] = s->manf_id; | ||
| 218 | + s->io[1] = s->chip_id; | ||
| 219 | + s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */ | ||
| 220 | + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) | ||
| 221 | + s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */ | ||
| 222 | + else | ||
| 223 | + s->io[3] = 0xc0; /* Multi-plane */ | ||
| 224 | + s->ioaddr = s->io; | ||
| 225 | + s->iolen = 4; | ||
| 226 | + break; | ||
| 227 | + | ||
| 228 | + case NAND_CMD_RANDOMREAD2: | ||
| 229 | + case NAND_CMD_NOSERIALREAD2: | ||
| 230 | + if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) | ||
| 231 | + break; | ||
| 232 | + | ||
| 233 | + s->blk_load(s, s->addr, s->addr & ((1 << s->addr_shift) - 1)); | ||
| 234 | + break; | ||
| 235 | + | ||
| 236 | + case NAND_CMD_RESET: | ||
| 237 | + nand_reset(s); | ||
| 238 | + break; | ||
| 239 | + | ||
| 240 | + case NAND_CMD_PAGEPROGRAM1: | ||
| 241 | + s->ioaddr = s->io; | ||
| 242 | + s->iolen = 0; | ||
| 243 | + break; | ||
| 244 | + | ||
| 245 | + case NAND_CMD_PAGEPROGRAM2: | ||
| 246 | + if (s->wp) { | ||
| 247 | + s->blk_write(s); | ||
| 248 | + } | ||
| 249 | + break; | ||
| 250 | + | ||
| 251 | + case NAND_CMD_BLOCKERASE1: | ||
| 252 | + break; | ||
| 253 | + | ||
| 254 | + case NAND_CMD_BLOCKERASE2: | ||
| 255 | + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) | ||
| 256 | + s->addr <<= 16; | ||
| 257 | + else | ||
| 258 | + s->addr <<= 8; | ||
| 259 | + | ||
| 260 | + if (s->wp) { | ||
| 261 | + s->blk_erase(s); | ||
| 262 | + } | ||
| 263 | + break; | ||
| 264 | + | ||
| 265 | + case NAND_CMD_READSTATUS: | ||
| 266 | + s->io[0] = s->status; | ||
| 267 | + s->ioaddr = s->io; | ||
| 268 | + s->iolen = 1; | ||
| 269 | + break; | ||
| 270 | + | ||
| 271 | + default: | ||
| 272 | + printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd); | ||
| 273 | + } | ||
| 274 | +} | ||
| 275 | + | ||
| 276 | +/* | ||
| 277 | + * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip | ||
| 278 | + * outputs are R/B and eight I/O pins. | ||
| 279 | + * | ||
| 280 | + * CE, WP and R/B are active low. | ||
| 281 | + */ | ||
| 282 | +void nand_setpins(struct nand_flash_s *s, | ||
| 283 | + int cle, int ale, int ce, int wp, int gnd) | ||
| 284 | +{ | ||
| 285 | + s->cle = cle; | ||
| 286 | + s->ale = ale; | ||
| 287 | + s->ce = ce; | ||
| 288 | + s->wp = wp; | ||
| 289 | + s->gnd = gnd; | ||
| 290 | + if (wp) | ||
| 291 | + s->status |= NAND_IOSTATUS_UNPROTCT; | ||
| 292 | + else | ||
| 293 | + s->status &= ~NAND_IOSTATUS_UNPROTCT; | ||
| 294 | +} | ||
| 295 | + | ||
| 296 | +void nand_getpins(struct nand_flash_s *s, int *rb) | ||
| 297 | +{ | ||
| 298 | + *rb = 1; | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +void nand_setio(struct nand_flash_s *s, uint8_t value) | ||
| 302 | +{ | ||
| 303 | + if (!s->ce && s->cle) { | ||
| 304 | + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { | ||
| 305 | + if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) | ||
| 306 | + return; | ||
| 307 | + if (value == NAND_CMD_RANDOMREAD1) { | ||
| 308 | + s->addr &= ~((1 << s->addr_shift) - 1); | ||
| 309 | + s->addrlen = 0; | ||
| 310 | + return; | ||
| 311 | + } | ||
| 312 | + } | ||
| 313 | + if (value == NAND_CMD_READ0) | ||
| 314 | + s->offset = 0; | ||
| 315 | + else if (value == NAND_CMD_READ1) { | ||
| 316 | + s->offset = 0x100; | ||
| 317 | + value = NAND_CMD_READ0; | ||
| 318 | + } | ||
| 319 | + else if (value == NAND_CMD_READ2) { | ||
| 320 | + s->offset = 1 << s->page_shift; | ||
| 321 | + value = NAND_CMD_READ0; | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + s->cmd = value; | ||
| 325 | + | ||
| 326 | + if (s->cmd == NAND_CMD_READSTATUS || | ||
| 327 | + s->cmd == NAND_CMD_PAGEPROGRAM2 || | ||
| 328 | + s->cmd == NAND_CMD_BLOCKERASE1 || | ||
| 329 | + s->cmd == NAND_CMD_BLOCKERASE2 || | ||
| 330 | + s->cmd == NAND_CMD_NOSERIALREAD2 || | ||
| 331 | + s->cmd == NAND_CMD_RANDOMREAD2 || | ||
| 332 | + s->cmd == NAND_CMD_RESET) | ||
| 333 | + nand_command(s); | ||
| 334 | + | ||
| 335 | + if (s->cmd != NAND_CMD_RANDOMREAD2) { | ||
| 336 | + s->addrlen = 0; | ||
| 337 | + s->addr = 0; | ||
| 338 | + } | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + if (s->ale) { | ||
| 342 | + s->addr |= value << (s->addrlen * 8); | ||
| 343 | + s->addrlen ++; | ||
| 344 | + | ||
| 345 | + if (s->addrlen == 1 && s->cmd == NAND_CMD_READID) | ||
| 346 | + nand_command(s); | ||
| 347 | + | ||
| 348 | + if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && | ||
| 349 | + s->addrlen == 3 && ( | ||
| 350 | + s->cmd == NAND_CMD_READ0 || | ||
| 351 | + s->cmd == NAND_CMD_PAGEPROGRAM1)) | ||
| 352 | + nand_command(s); | ||
| 353 | + if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && | ||
| 354 | + s->addrlen == 4 && ( | ||
| 355 | + s->cmd == NAND_CMD_READ0 || | ||
| 356 | + s->cmd == NAND_CMD_PAGEPROGRAM1)) | ||
| 357 | + nand_command(s); | ||
| 358 | + } | ||
| 359 | + | ||
| 360 | + if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { | ||
| 361 | + if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) | ||
| 362 | + s->io[s->iolen ++] = value; | ||
| 363 | + } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { | ||
| 364 | + if ((s->addr & ((1 << s->addr_shift) - 1)) < | ||
| 365 | + (1 << s->page_shift) + (1 << s->oob_shift)) { | ||
| 366 | + s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value; | ||
| 367 | + s->addr ++; | ||
| 368 | + } | ||
| 369 | + } | ||
| 370 | +} | ||
| 371 | + | ||
| 372 | +uint8_t nand_getio(struct nand_flash_s *s) | ||
| 373 | +{ | ||
| 374 | + int offset; | ||
| 375 | + | ||
| 376 | + /* Allow sequential reading */ | ||
| 377 | + if (!s->iolen && s->cmd == NAND_CMD_READ0) { | ||
| 378 | + offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; | ||
| 379 | + s->offset = 0; | ||
| 380 | + | ||
| 381 | + s->blk_load(s, s->addr, offset); | ||
| 382 | + if (s->gnd) | ||
| 383 | + s->iolen = (1 << s->page_shift) - offset; | ||
| 384 | + else | ||
| 385 | + s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; | ||
| 386 | + } | ||
| 387 | + | ||
| 388 | + if (s->ce || s->iolen <= 0) | ||
| 389 | + return 0; | ||
| 390 | + | ||
| 391 | + s->iolen --; | ||
| 392 | + return *(s->ioaddr ++); | ||
| 393 | +} | ||
| 394 | + | ||
| 395 | +struct nand_flash_s *nand_init(int manf_id, int chip_id) | ||
| 396 | +{ | ||
| 397 | + int pagesize; | ||
| 398 | + struct nand_flash_s *s; | ||
| 399 | + | ||
| 400 | + if (nand_flash_ids[chip_id].size == 0) { | ||
| 401 | + cpu_abort(cpu_single_env, "%s: Unsupported NAND chip ID.\n", | ||
| 402 | + __FUNCTION__); | ||
| 403 | + } | ||
| 404 | + | ||
| 405 | + s = (struct nand_flash_s *) qemu_mallocz(sizeof(struct nand_flash_s)); | ||
| 406 | + s->bdrv = mtd_bdrv; | ||
| 407 | + s->manf_id = manf_id; | ||
| 408 | + s->chip_id = chip_id; | ||
| 409 | + s->size = nand_flash_ids[s->chip_id].size << 20; | ||
| 410 | + if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { | ||
| 411 | + s->page_shift = 11; | ||
| 412 | + s->erase_shift = 6; | ||
| 413 | + } else { | ||
| 414 | + s->page_shift = nand_flash_ids[s->chip_id].page_shift; | ||
| 415 | + s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + switch (1 << s->page_shift) { | ||
| 419 | + case 256: | ||
| 420 | + nand_init_256(s); | ||
| 421 | + break; | ||
| 422 | + case 512: | ||
| 423 | + nand_init_512(s); | ||
| 424 | + break; | ||
| 425 | + case 2048: | ||
| 426 | + nand_init_2048(s); | ||
| 427 | + break; | ||
| 428 | + default: | ||
| 429 | + cpu_abort(cpu_single_env, "%s: Unsupported NAND block size.\n", | ||
| 430 | + __FUNCTION__); | ||
| 431 | + } | ||
| 432 | + | ||
| 433 | + pagesize = 1 << s->oob_shift; | ||
| 434 | + s->mem_oob = 1; | ||
| 435 | + if (s->bdrv && bdrv_getlength(s->bdrv) >= | ||
| 436 | + (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { | ||
| 437 | + pagesize = 0; | ||
| 438 | + s->mem_oob = 0; | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + if (!s->bdrv) | ||
| 442 | + pagesize += 1 << s->page_shift; | ||
| 443 | + if (pagesize) | ||
| 444 | + s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize), | ||
| 445 | + 0xff, s->pages * pagesize); | ||
| 446 | + return s; | ||
| 447 | +} | ||
| 448 | + | ||
| 449 | +void nand_done(struct nand_flash_s *s) | ||
| 450 | +{ | ||
| 451 | + if (s->bdrv) { | ||
| 452 | + bdrv_close(s->bdrv); | ||
| 453 | + bdrv_delete(s->bdrv); | ||
| 454 | + } | ||
| 455 | + | ||
| 456 | + if (!s->bdrv || s->mem_oob) | ||
| 457 | + free(s->storage); | ||
| 458 | + | ||
| 459 | + free(s); | ||
| 460 | +} | ||
| 461 | + | ||
| 462 | +#else | ||
| 463 | + | ||
| 464 | +/* Program a single page */ | ||
| 465 | +static void glue(nand_blk_write_, PAGE_SIZE)(struct nand_flash_s *s) | ||
| 466 | +{ | ||
| 467 | + uint32_t off, page, sector, soff; | ||
| 468 | + uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; | ||
| 469 | + if (PAGE(s->addr) >= s->pages) | ||
| 470 | + return; | ||
| 471 | + | ||
| 472 | + if (!s->bdrv) { | ||
| 473 | + memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + | ||
| 474 | + s->offset, s->io, s->iolen); | ||
| 475 | + } else if (s->mem_oob) { | ||
| 476 | + sector = SECTOR(s->addr); | ||
| 477 | + off = (s->addr & PAGE_MASK) + s->offset; | ||
| 478 | + soff = SECTOR_OFFSET(s->addr); | ||
| 479 | + if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) { | ||
| 480 | + printf("%s: read error in sector %i\n", __FUNCTION__, sector); | ||
| 481 | + return; | ||
| 482 | + } | ||
| 483 | + | ||
| 484 | + memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); | ||
| 485 | + if (off + s->iolen > PAGE_SIZE) { | ||
| 486 | + page = PAGE(s->addr); | ||
| 487 | + memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, | ||
| 488 | + MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); | ||
| 489 | + } | ||
| 490 | + | ||
| 491 | + if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) | ||
| 492 | + printf("%s: write error in sector %i\n", __FUNCTION__, sector); | ||
| 493 | + } else { | ||
| 494 | + off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; | ||
| 495 | + sector = off >> 9; | ||
| 496 | + soff = off & 0x1ff; | ||
| 497 | + if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) { | ||
| 498 | + printf("%s: read error in sector %i\n", __FUNCTION__, sector); | ||
| 499 | + return; | ||
| 500 | + } | ||
| 501 | + | ||
| 502 | + memcpy(iobuf + soff, s->io, s->iolen); | ||
| 503 | + | ||
| 504 | + if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) | ||
| 505 | + printf("%s: write error in sector %i\n", __FUNCTION__, sector); | ||
| 506 | + } | ||
| 507 | + s->offset = 0; | ||
| 508 | +} | ||
| 509 | + | ||
| 510 | +/* Erase a single block */ | ||
| 511 | +static void glue(nand_blk_erase_, PAGE_SIZE)(struct nand_flash_s *s) | ||
| 512 | +{ | ||
| 513 | + uint32_t i, page, addr; | ||
| 514 | + uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; | ||
| 515 | + addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); | ||
| 516 | + | ||
| 517 | + if (PAGE(addr) >= s->pages) | ||
| 518 | + return; | ||
| 519 | + | ||
| 520 | + if (!s->bdrv) { | ||
| 521 | + memset(s->storage + PAGE_START(addr), | ||
| 522 | + 0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift); | ||
| 523 | + } else if (s->mem_oob) { | ||
| 524 | + memset(s->storage + (PAGE(addr) << OOB_SHIFT), | ||
| 525 | + 0xff, OOB_SIZE << s->erase_shift); | ||
| 526 | + i = SECTOR(addr); | ||
| 527 | + page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); | ||
| 528 | + for (; i < page; i ++) | ||
| 529 | + if (bdrv_write(s->bdrv, i, iobuf, 1) == -1) | ||
| 530 | + printf("%s: write error in sector %i\n", __FUNCTION__, i); | ||
| 531 | + } else { | ||
| 532 | + addr = PAGE_START(addr); | ||
| 533 | + page = addr >> 9; | ||
| 534 | + if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) | ||
| 535 | + printf("%s: read error in sector %i\n", __FUNCTION__, page); | ||
| 536 | + memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); | ||
| 537 | + if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) | ||
| 538 | + printf("%s: write error in sector %i\n", __FUNCTION__, page); | ||
| 539 | + | ||
| 540 | + memset(iobuf, 0xff, 0x200); | ||
| 541 | + i = (addr & ~0x1ff) + 0x200; | ||
| 542 | + for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; | ||
| 543 | + i < addr; i += 0x200) | ||
| 544 | + if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1) | ||
| 545 | + printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9); | ||
| 546 | + | ||
| 547 | + page = i >> 9; | ||
| 548 | + if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) | ||
| 549 | + printf("%s: read error in sector %i\n", __FUNCTION__, page); | ||
| 550 | + memset(iobuf, 0xff, addr & 0x1ff); | ||
| 551 | + if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) | ||
| 552 | + printf("%s: write error in sector %i\n", __FUNCTION__, page); | ||
| 553 | + } | ||
| 554 | +} | ||
| 555 | + | ||
| 556 | +static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s, | ||
| 557 | + uint32_t addr, int offset) | ||
| 558 | +{ | ||
| 559 | + if (PAGE(addr) >= s->pages) | ||
| 560 | + return; | ||
| 561 | + | ||
| 562 | + if (s->bdrv) { | ||
| 563 | + if (s->mem_oob) { | ||
| 564 | + if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1) | ||
| 565 | + printf("%s: read error in sector %i\n", | ||
| 566 | + __FUNCTION__, SECTOR(addr)); | ||
| 567 | + memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, | ||
| 568 | + s->storage + (PAGE(s->addr) << OOB_SHIFT), | ||
| 569 | + OOB_SIZE); | ||
| 570 | + s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; | ||
| 571 | + } else { | ||
| 572 | + if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9, | ||
| 573 | + s->io, (PAGE_SECTORS + 2)) == -1) | ||
| 574 | + printf("%s: read error in sector %i\n", | ||
| 575 | + __FUNCTION__, PAGE_START(addr) >> 9); | ||
| 576 | + s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; | ||
| 577 | + } | ||
| 578 | + } else { | ||
| 579 | + memcpy(s->io, s->storage + PAGE_START(s->addr) + | ||
| 580 | + offset, PAGE_SIZE + OOB_SIZE - offset); | ||
| 581 | + s->ioaddr = s->io; | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + s->addr &= PAGE_SIZE - 1; | ||
| 585 | + s->addr += PAGE_SIZE; | ||
| 586 | +} | ||
| 587 | + | ||
| 588 | +static void glue(nand_init_, PAGE_SIZE)(struct nand_flash_s *s) | ||
| 589 | +{ | ||
| 590 | + s->oob_shift = PAGE_SHIFT - 5; | ||
| 591 | + s->pages = s->size >> PAGE_SHIFT; | ||
| 592 | + s->addr_shift = ADDR_SHIFT; | ||
| 593 | + | ||
| 594 | + s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE); | ||
| 595 | + s->blk_write = glue(nand_blk_write_, PAGE_SIZE); | ||
| 596 | + s->blk_load = glue(nand_blk_load_, PAGE_SIZE); | ||
| 597 | +} | ||
| 598 | + | ||
| 599 | +# undef PAGE_SIZE | ||
| 600 | +# undef PAGE_SHIFT | ||
| 601 | +# undef PAGE_SECTORS | ||
| 602 | +# undef ADDR_SHIFT | ||
| 603 | +#endif /* NAND_IO */ |
vl.c
| @@ -140,6 +140,7 @@ IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; | @@ -140,6 +140,7 @@ IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; | ||
| 140 | BlockDriverState *bs_table[MAX_DISKS + 1], *fd_table[MAX_FD]; | 140 | BlockDriverState *bs_table[MAX_DISKS + 1], *fd_table[MAX_FD]; |
| 141 | BlockDriverState *pflash_table[MAX_PFLASH]; | 141 | BlockDriverState *pflash_table[MAX_PFLASH]; |
| 142 | BlockDriverState *sd_bdrv; | 142 | BlockDriverState *sd_bdrv; |
| 143 | +BlockDriverState *mtd_bdrv; | ||
| 143 | /* point to the block driver where the snapshots are managed */ | 144 | /* point to the block driver where the snapshots are managed */ |
| 144 | BlockDriverState *bs_snapshots; | 145 | BlockDriverState *bs_snapshots; |
| 145 | int vga_ram_size; | 146 | int vga_ram_size; |
| @@ -6419,6 +6420,7 @@ void help(void) | @@ -6419,6 +6420,7 @@ void help(void) | ||
| 6419 | "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n" | 6420 | "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n" |
| 6420 | "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n" | 6421 | "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n" |
| 6421 | "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n" | 6422 | "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n" |
| 6423 | + "-mtdblock file use 'file' as on-board Flash memory image\n" | ||
| 6422 | "-sd file use 'file' as SecureDigital card image\n" | 6424 | "-sd file use 'file' as SecureDigital card image\n" |
| 6423 | "-pflash file use 'file' as a parallel flash image\n" | 6425 | "-pflash file use 'file' as a parallel flash image\n" |
| 6424 | "-boot [a|c|d|n] boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)\n" | 6426 | "-boot [a|c|d|n] boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)\n" |
| @@ -6559,6 +6561,7 @@ enum { | @@ -6559,6 +6561,7 @@ enum { | ||
| 6559 | QEMU_OPTION_hdc, | 6561 | QEMU_OPTION_hdc, |
| 6560 | QEMU_OPTION_hdd, | 6562 | QEMU_OPTION_hdd, |
| 6561 | QEMU_OPTION_cdrom, | 6563 | QEMU_OPTION_cdrom, |
| 6564 | + QEMU_OPTION_mtdblock, | ||
| 6562 | QEMU_OPTION_sd, | 6565 | QEMU_OPTION_sd, |
| 6563 | QEMU_OPTION_pflash, | 6566 | QEMU_OPTION_pflash, |
| 6564 | QEMU_OPTION_boot, | 6567 | QEMU_OPTION_boot, |
| @@ -6640,6 +6643,7 @@ const QEMUOption qemu_options[] = { | @@ -6640,6 +6643,7 @@ const QEMUOption qemu_options[] = { | ||
| 6640 | { "hdc", HAS_ARG, QEMU_OPTION_hdc }, | 6643 | { "hdc", HAS_ARG, QEMU_OPTION_hdc }, |
| 6641 | { "hdd", HAS_ARG, QEMU_OPTION_hdd }, | 6644 | { "hdd", HAS_ARG, QEMU_OPTION_hdd }, |
| 6642 | { "cdrom", HAS_ARG, QEMU_OPTION_cdrom }, | 6645 | { "cdrom", HAS_ARG, QEMU_OPTION_cdrom }, |
| 6646 | + { "mtdblock", HAS_ARG, QEMU_OPTION_mtdblock }, | ||
| 6643 | { "sd", HAS_ARG, QEMU_OPTION_sd }, | 6647 | { "sd", HAS_ARG, QEMU_OPTION_sd }, |
| 6644 | { "pflash", HAS_ARG, QEMU_OPTION_pflash }, | 6648 | { "pflash", HAS_ARG, QEMU_OPTION_pflash }, |
| 6645 | { "boot", HAS_ARG, QEMU_OPTION_boot }, | 6649 | { "boot", HAS_ARG, QEMU_OPTION_boot }, |
| @@ -6944,6 +6948,7 @@ int main(int argc, char **argv) | @@ -6944,6 +6948,7 @@ int main(int argc, char **argv) | ||
| 6944 | const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; | 6948 | const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; |
| 6945 | const char *pflash_filename[MAX_PFLASH]; | 6949 | const char *pflash_filename[MAX_PFLASH]; |
| 6946 | const char *sd_filename; | 6950 | const char *sd_filename; |
| 6951 | + const char *mtd_filename; | ||
| 6947 | const char *kernel_filename, *kernel_cmdline; | 6952 | const char *kernel_filename, *kernel_cmdline; |
| 6948 | DisplayState *ds = &display_state; | 6953 | DisplayState *ds = &display_state; |
| 6949 | int cyls, heads, secs, translation; | 6954 | int cyls, heads, secs, translation; |
| @@ -7008,6 +7013,7 @@ int main(int argc, char **argv) | @@ -7008,6 +7013,7 @@ int main(int argc, char **argv) | ||
| 7008 | pflash_filename[i] = NULL; | 7013 | pflash_filename[i] = NULL; |
| 7009 | pflash_index = 0; | 7014 | pflash_index = 0; |
| 7010 | sd_filename = NULL; | 7015 | sd_filename = NULL; |
| 7016 | + mtd_filename = NULL; | ||
| 7011 | ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; | 7017 | ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; |
| 7012 | vga_ram_size = VGA_RAM_SIZE; | 7018 | vga_ram_size = VGA_RAM_SIZE; |
| 7013 | #ifdef CONFIG_GDBSTUB | 7019 | #ifdef CONFIG_GDBSTUB |
| @@ -7126,6 +7132,9 @@ int main(int argc, char **argv) | @@ -7126,6 +7132,9 @@ int main(int argc, char **argv) | ||
| 7126 | cdrom_index = -1; | 7132 | cdrom_index = -1; |
| 7127 | } | 7133 | } |
| 7128 | break; | 7134 | break; |
| 7135 | + case QEMU_OPTION_mtdblock: | ||
| 7136 | + mtd_filename = optarg; | ||
| 7137 | + break; | ||
| 7129 | case QEMU_OPTION_sd: | 7138 | case QEMU_OPTION_sd: |
| 7130 | sd_filename = optarg; | 7139 | sd_filename = optarg; |
| 7131 | break; | 7140 | break; |
| @@ -7678,6 +7687,18 @@ int main(int argc, char **argv) | @@ -7678,6 +7687,18 @@ int main(int argc, char **argv) | ||
| 7678 | qemu_key_check(sd_bdrv, sd_filename); | 7687 | qemu_key_check(sd_bdrv, sd_filename); |
| 7679 | } | 7688 | } |
| 7680 | 7689 | ||
| 7690 | + if (mtd_filename) { | ||
| 7691 | + mtd_bdrv = bdrv_new ("mtd"); | ||
| 7692 | + if (bdrv_open(mtd_bdrv, mtd_filename, | ||
| 7693 | + snapshot ? BDRV_O_SNAPSHOT : 0) < 0 || | ||
| 7694 | + qemu_key_check(mtd_bdrv, mtd_filename)) { | ||
| 7695 | + fprintf(stderr, "qemu: could not open Flash image %s\n", | ||
| 7696 | + mtd_filename); | ||
| 7697 | + bdrv_delete(mtd_bdrv); | ||
| 7698 | + mtd_bdrv = 0; | ||
| 7699 | + } | ||
| 7700 | + } | ||
| 7701 | + | ||
| 7681 | register_savevm("timer", 0, 2, timer_save, timer_load, NULL); | 7702 | register_savevm("timer", 0, 2, timer_save, timer_load, NULL); |
| 7682 | register_savevm("ram", 0, 2, ram_save, ram_load, NULL); | 7703 | register_savevm("ram", 0, 2, ram_save, ram_load, NULL); |
| 7683 | 7704 |
vl.h
| @@ -967,6 +967,7 @@ extern uint8_t _translate_keycode(const int key); | @@ -967,6 +967,7 @@ extern uint8_t _translate_keycode(const int key); | ||
| 967 | 967 | ||
| 968 | extern BlockDriverState *bs_table[MAX_DISKS + 1]; | 968 | extern BlockDriverState *bs_table[MAX_DISKS + 1]; |
| 969 | extern BlockDriverState *sd_bdrv; | 969 | extern BlockDriverState *sd_bdrv; |
| 970 | +extern BlockDriverState *mtd_bdrv; | ||
| 970 | 971 | ||
| 971 | void isa_ide_init(int iobase, int iobase2, qemu_irq irq, | 972 | void isa_ide_init(int iobase, int iobase2, qemu_irq irq, |
| 972 | BlockDriverState *hd0, BlockDriverState *hd1); | 973 | BlockDriverState *hd0, BlockDriverState *hd1); |
| @@ -1478,6 +1479,27 @@ pflash_t *pflash_register (target_ulong base, ram_addr_t off, | @@ -1478,6 +1479,27 @@ pflash_t *pflash_register (target_ulong base, ram_addr_t off, | ||
| 1478 | uint16_t id0, uint16_t id1, | 1479 | uint16_t id0, uint16_t id1, |
| 1479 | uint16_t id2, uint16_t id3); | 1480 | uint16_t id2, uint16_t id3); |
| 1480 | 1481 | ||
| 1482 | +/* nand.c */ | ||
| 1483 | +struct nand_flash_s; | ||
| 1484 | +struct nand_flash_s *nand_init(int manf_id, int chip_id); | ||
| 1485 | +void nand_done(struct nand_flash_s *s); | ||
| 1486 | +void nand_setpins(struct nand_flash_s *s, | ||
| 1487 | + int cle, int ale, int ce, int wp, int gnd); | ||
| 1488 | +void nand_getpins(struct nand_flash_s *s, int *rb); | ||
| 1489 | +void nand_setio(struct nand_flash_s *s, uint8_t value); | ||
| 1490 | +uint8_t nand_getio(struct nand_flash_s *s); | ||
| 1491 | + | ||
| 1492 | +#define NAND_MFR_TOSHIBA 0x98 | ||
| 1493 | +#define NAND_MFR_SAMSUNG 0xec | ||
| 1494 | +#define NAND_MFR_FUJITSU 0x04 | ||
| 1495 | +#define NAND_MFR_NATIONAL 0x8f | ||
| 1496 | +#define NAND_MFR_RENESAS 0x07 | ||
| 1497 | +#define NAND_MFR_STMICRO 0x20 | ||
| 1498 | +#define NAND_MFR_HYNIX 0xad | ||
| 1499 | +#define NAND_MFR_MICRON 0x2c | ||
| 1500 | + | ||
| 1501 | +#include "ecc.h" | ||
| 1502 | + | ||
| 1481 | /* PCMCIA/Cardbus */ | 1503 | /* PCMCIA/Cardbus */ |
| 1482 | 1504 | ||
| 1483 | struct pcmcia_socket_s { | 1505 | struct pcmcia_socket_s { |