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 | 140 | BlockDriverState *bs_table[MAX_DISKS + 1], *fd_table[MAX_FD]; |
| 141 | 141 | BlockDriverState *pflash_table[MAX_PFLASH]; |
| 142 | 142 | BlockDriverState *sd_bdrv; |
| 143 | +BlockDriverState *mtd_bdrv; | |
| 143 | 144 | /* point to the block driver where the snapshots are managed */ |
| 144 | 145 | BlockDriverState *bs_snapshots; |
| 145 | 146 | int vga_ram_size; |
| ... | ... | @@ -6419,6 +6420,7 @@ void help(void) |
| 6419 | 6420 | "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n" |
| 6420 | 6421 | "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n" |
| 6421 | 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 | 6424 | "-sd file use 'file' as SecureDigital card image\n" |
| 6423 | 6425 | "-pflash file use 'file' as a parallel flash image\n" |
| 6424 | 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 | 6561 | QEMU_OPTION_hdc, |
| 6560 | 6562 | QEMU_OPTION_hdd, |
| 6561 | 6563 | QEMU_OPTION_cdrom, |
| 6564 | + QEMU_OPTION_mtdblock, | |
| 6562 | 6565 | QEMU_OPTION_sd, |
| 6563 | 6566 | QEMU_OPTION_pflash, |
| 6564 | 6567 | QEMU_OPTION_boot, |
| ... | ... | @@ -6640,6 +6643,7 @@ const QEMUOption qemu_options[] = { |
| 6640 | 6643 | { "hdc", HAS_ARG, QEMU_OPTION_hdc }, |
| 6641 | 6644 | { "hdd", HAS_ARG, QEMU_OPTION_hdd }, |
| 6642 | 6645 | { "cdrom", HAS_ARG, QEMU_OPTION_cdrom }, |
| 6646 | + { "mtdblock", HAS_ARG, QEMU_OPTION_mtdblock }, | |
| 6643 | 6647 | { "sd", HAS_ARG, QEMU_OPTION_sd }, |
| 6644 | 6648 | { "pflash", HAS_ARG, QEMU_OPTION_pflash }, |
| 6645 | 6649 | { "boot", HAS_ARG, QEMU_OPTION_boot }, |
| ... | ... | @@ -6944,6 +6948,7 @@ int main(int argc, char **argv) |
| 6944 | 6948 | const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; |
| 6945 | 6949 | const char *pflash_filename[MAX_PFLASH]; |
| 6946 | 6950 | const char *sd_filename; |
| 6951 | + const char *mtd_filename; | |
| 6947 | 6952 | const char *kernel_filename, *kernel_cmdline; |
| 6948 | 6953 | DisplayState *ds = &display_state; |
| 6949 | 6954 | int cyls, heads, secs, translation; |
| ... | ... | @@ -7008,6 +7013,7 @@ int main(int argc, char **argv) |
| 7008 | 7013 | pflash_filename[i] = NULL; |
| 7009 | 7014 | pflash_index = 0; |
| 7010 | 7015 | sd_filename = NULL; |
| 7016 | + mtd_filename = NULL; | |
| 7011 | 7017 | ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; |
| 7012 | 7018 | vga_ram_size = VGA_RAM_SIZE; |
| 7013 | 7019 | #ifdef CONFIG_GDBSTUB |
| ... | ... | @@ -7126,6 +7132,9 @@ int main(int argc, char **argv) |
| 7126 | 7132 | cdrom_index = -1; |
| 7127 | 7133 | } |
| 7128 | 7134 | break; |
| 7135 | + case QEMU_OPTION_mtdblock: | |
| 7136 | + mtd_filename = optarg; | |
| 7137 | + break; | |
| 7129 | 7138 | case QEMU_OPTION_sd: |
| 7130 | 7139 | sd_filename = optarg; |
| 7131 | 7140 | break; |
| ... | ... | @@ -7678,6 +7687,18 @@ int main(int argc, char **argv) |
| 7678 | 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 | 7702 | register_savevm("timer", 0, 2, timer_save, timer_load, NULL); |
| 7682 | 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 | 967 | |
| 968 | 968 | extern BlockDriverState *bs_table[MAX_DISKS + 1]; |
| 969 | 969 | extern BlockDriverState *sd_bdrv; |
| 970 | +extern BlockDriverState *mtd_bdrv; | |
| 970 | 971 | |
| 971 | 972 | void isa_ide_init(int iobase, int iobase2, qemu_irq irq, |
| 972 | 973 | BlockDriverState *hd0, BlockDriverState *hd1); |
| ... | ... | @@ -1478,6 +1479,27 @@ pflash_t *pflash_register (target_ulong base, ram_addr_t off, |
| 1478 | 1479 | uint16_t id0, uint16_t id1, |
| 1479 | 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 | 1503 | /* PCMCIA/Cardbus */ |
| 1482 | 1504 | |
| 1483 | 1505 | struct pcmcia_socket_s { | ... | ... |