|
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 "hw.h" |
|
15 # include "flash.h" |
|
16 # include "block.h" |
|
17 /* FIXME: Pass block device as an argument. */ |
|
18 # include "sysemu.h" |
|
19 |
|
20 # define NAND_CMD_READ0 0x00 |
|
21 # define NAND_CMD_READ1 0x01 |
|
22 # define NAND_CMD_READ2 0x50 |
|
23 # define NAND_CMD_LPREAD2 0x30 |
|
24 # define NAND_CMD_NOSERIALREAD2 0x35 |
|
25 # define NAND_CMD_RANDOMREAD1 0x05 |
|
26 # define NAND_CMD_RANDOMREAD2 0xe0 |
|
27 # define NAND_CMD_READID 0x90 |
|
28 # define NAND_CMD_RESET 0xff |
|
29 # define NAND_CMD_PAGEPROGRAM1 0x80 |
|
30 # define NAND_CMD_PAGEPROGRAM2 0x10 |
|
31 # define NAND_CMD_CACHEPROGRAM2 0x15 |
|
32 # define NAND_CMD_BLOCKERASE1 0x60 |
|
33 # define NAND_CMD_BLOCKERASE2 0xd0 |
|
34 # define NAND_CMD_READSTATUS 0x70 |
|
35 # define NAND_CMD_COPYBACKPRG1 0x85 |
|
36 |
|
37 # define NAND_IOSTATUS_ERROR (1 << 0) |
|
38 # define NAND_IOSTATUS_PLANE0 (1 << 1) |
|
39 # define NAND_IOSTATUS_PLANE1 (1 << 2) |
|
40 # define NAND_IOSTATUS_PLANE2 (1 << 3) |
|
41 # define NAND_IOSTATUS_PLANE3 (1 << 4) |
|
42 # define NAND_IOSTATUS_BUSY (1 << 6) |
|
43 # define NAND_IOSTATUS_UNPROTCT (1 << 7) |
|
44 |
|
45 # define MAX_PAGE 0x800 |
|
46 # define MAX_OOB 0x40 |
|
47 |
|
48 struct nand_flash_s { |
|
49 uint8_t manf_id, chip_id; |
|
50 int size, pages; |
|
51 int page_shift, oob_shift, erase_shift, addr_shift; |
|
52 uint8_t *storage; |
|
53 BlockDriverState *bdrv; |
|
54 int mem_oob; |
|
55 |
|
56 int cle, ale, ce, wp, gnd; |
|
57 |
|
58 uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; |
|
59 uint8_t *ioaddr; |
|
60 int iolen; |
|
61 |
|
62 uint32_t cmd, addr; |
|
63 int addrlen; |
|
64 int status; |
|
65 int offset; |
|
66 |
|
67 void (*blk_write)(struct nand_flash_s *s); |
|
68 void (*blk_erase)(struct nand_flash_s *s); |
|
69 void (*blk_load)(struct nand_flash_s *s, uint32_t addr, int offset); |
|
70 }; |
|
71 |
|
72 # define NAND_NO_AUTOINCR 0x00000001 |
|
73 # define NAND_BUSWIDTH_16 0x00000002 |
|
74 # define NAND_NO_PADDING 0x00000004 |
|
75 # define NAND_CACHEPRG 0x00000008 |
|
76 # define NAND_COPYBACK 0x00000010 |
|
77 # define NAND_IS_AND 0x00000020 |
|
78 # define NAND_4PAGE_ARRAY 0x00000040 |
|
79 # define NAND_NO_READRDY 0x00000100 |
|
80 # define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) |
|
81 |
|
82 # define NAND_IO |
|
83 |
|
84 # define PAGE(addr) ((addr) >> ADDR_SHIFT) |
|
85 # define PAGE_START(page) (PAGE(page) * (PAGE_SIZE + OOB_SIZE)) |
|
86 # define PAGE_MASK ((1 << ADDR_SHIFT) - 1) |
|
87 # define OOB_SHIFT (PAGE_SHIFT - 5) |
|
88 # define OOB_SIZE (1 << OOB_SHIFT) |
|
89 # define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) |
|
90 # define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) |
|
91 |
|
92 # define PAGE_SIZE 256 |
|
93 # define PAGE_SHIFT 8 |
|
94 # define PAGE_SECTORS 1 |
|
95 # define ADDR_SHIFT 8 |
|
96 # include "nand.c" |
|
97 # define PAGE_SIZE 512 |
|
98 # define PAGE_SHIFT 9 |
|
99 # define PAGE_SECTORS 1 |
|
100 # define ADDR_SHIFT 8 |
|
101 # include "nand.c" |
|
102 # define PAGE_SIZE 2048 |
|
103 # define PAGE_SHIFT 11 |
|
104 # define PAGE_SECTORS 4 |
|
105 # define ADDR_SHIFT 16 |
|
106 # include "nand.c" |
|
107 |
|
108 /* Information based on Linux drivers/mtd/nand/nand_ids.c */ |
|
109 static const struct nand_info_s { |
|
110 int size; |
|
111 int width; |
|
112 int page_shift; |
|
113 int erase_shift; |
|
114 uint32_t options; |
|
115 } nand_flash_ids[0x100] = { |
|
116 [0 ... 0xff] = { 0 }, |
|
117 |
|
118 [0x6e] = { 1, 8, 8, 4, 0 }, |
|
119 [0x64] = { 2, 8, 8, 4, 0 }, |
|
120 [0x6b] = { 4, 8, 9, 4, 0 }, |
|
121 [0xe8] = { 1, 8, 8, 4, 0 }, |
|
122 [0xec] = { 1, 8, 8, 4, 0 }, |
|
123 [0xea] = { 2, 8, 8, 4, 0 }, |
|
124 [0xd5] = { 4, 8, 9, 4, 0 }, |
|
125 [0xe3] = { 4, 8, 9, 4, 0 }, |
|
126 [0xe5] = { 4, 8, 9, 4, 0 }, |
|
127 [0xd6] = { 8, 8, 9, 4, 0 }, |
|
128 |
|
129 [0x39] = { 8, 8, 9, 4, 0 }, |
|
130 [0xe6] = { 8, 8, 9, 4, 0 }, |
|
131 [0x49] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, |
|
132 [0x59] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, |
|
133 |
|
134 [0x33] = { 16, 8, 9, 5, 0 }, |
|
135 [0x73] = { 16, 8, 9, 5, 0 }, |
|
136 [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
137 [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
138 |
|
139 [0x35] = { 32, 8, 9, 5, 0 }, |
|
140 [0x75] = { 32, 8, 9, 5, 0 }, |
|
141 [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
142 [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
143 |
|
144 [0x36] = { 64, 8, 9, 5, 0 }, |
|
145 [0x76] = { 64, 8, 9, 5, 0 }, |
|
146 [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
147 [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
148 |
|
149 [0x78] = { 128, 8, 9, 5, 0 }, |
|
150 [0x39] = { 128, 8, 9, 5, 0 }, |
|
151 [0x79] = { 128, 8, 9, 5, 0 }, |
|
152 [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
153 [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
154 [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
155 [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, |
|
156 |
|
157 [0x71] = { 256, 8, 9, 5, 0 }, |
|
158 |
|
159 /* |
|
160 * These are the new chips with large page size. The pagesize and the |
|
161 * erasesize is determined from the extended id bytes |
|
162 */ |
|
163 # define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) |
|
164 # define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) |
|
165 |
|
166 /* 512 Megabit */ |
|
167 [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, |
|
168 [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, |
|
169 [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, |
|
170 [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, |
|
171 |
|
172 /* 1 Gigabit */ |
|
173 [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, |
|
174 [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, |
|
175 [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, |
|
176 [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, |
|
177 |
|
178 /* 2 Gigabit */ |
|
179 [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, |
|
180 [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, |
|
181 [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, |
|
182 [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, |
|
183 |
|
184 /* 4 Gigabit */ |
|
185 [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, |
|
186 [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, |
|
187 [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, |
|
188 [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, |
|
189 |
|
190 /* 8 Gigabit */ |
|
191 [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, |
|
192 [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, |
|
193 [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, |
|
194 [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, |
|
195 |
|
196 /* 16 Gigabit */ |
|
197 [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, |
|
198 [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, |
|
199 [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, |
|
200 [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, |
|
201 }; |
|
202 |
|
203 static void nand_reset(struct nand_flash_s *s) |
|
204 { |
|
205 s->cmd = NAND_CMD_READ0; |
|
206 s->addr = 0; |
|
207 s->addrlen = 0; |
|
208 s->iolen = 0; |
|
209 s->offset = 0; |
|
210 s->status &= NAND_IOSTATUS_UNPROTCT; |
|
211 } |
|
212 |
|
213 static void nand_command(struct nand_flash_s *s) |
|
214 { |
|
215 switch (s->cmd) { |
|
216 case NAND_CMD_READ0: |
|
217 s->iolen = 0; |
|
218 break; |
|
219 |
|
220 case NAND_CMD_READID: |
|
221 s->io[0] = s->manf_id; |
|
222 s->io[1] = s->chip_id; |
|
223 s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */ |
|
224 if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) |
|
225 s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */ |
|
226 else |
|
227 s->io[3] = 0xc0; /* Multi-plane */ |
|
228 s->ioaddr = s->io; |
|
229 s->iolen = 4; |
|
230 break; |
|
231 |
|
232 case NAND_CMD_RANDOMREAD2: |
|
233 case NAND_CMD_NOSERIALREAD2: |
|
234 if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) |
|
235 break; |
|
236 |
|
237 s->blk_load(s, s->addr, s->addr & ((1 << s->addr_shift) - 1)); |
|
238 break; |
|
239 |
|
240 case NAND_CMD_RESET: |
|
241 nand_reset(s); |
|
242 break; |
|
243 |
|
244 case NAND_CMD_PAGEPROGRAM1: |
|
245 s->ioaddr = s->io; |
|
246 s->iolen = 0; |
|
247 break; |
|
248 |
|
249 case NAND_CMD_PAGEPROGRAM2: |
|
250 if (s->wp) { |
|
251 s->blk_write(s); |
|
252 } |
|
253 break; |
|
254 |
|
255 case NAND_CMD_BLOCKERASE1: |
|
256 break; |
|
257 |
|
258 case NAND_CMD_BLOCKERASE2: |
|
259 if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) |
|
260 s->addr <<= 16; |
|
261 else |
|
262 s->addr <<= 8; |
|
263 |
|
264 if (s->wp) { |
|
265 s->blk_erase(s); |
|
266 } |
|
267 break; |
|
268 |
|
269 case NAND_CMD_READSTATUS: |
|
270 s->io[0] = s->status; |
|
271 s->ioaddr = s->io; |
|
272 s->iolen = 1; |
|
273 break; |
|
274 |
|
275 default: |
|
276 printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd); |
|
277 } |
|
278 } |
|
279 |
|
280 static void nand_save(QEMUFile *f, void *opaque) |
|
281 { |
|
282 struct nand_flash_s *s = (struct nand_flash_s *) opaque; |
|
283 qemu_put_byte(f, s->cle); |
|
284 qemu_put_byte(f, s->ale); |
|
285 qemu_put_byte(f, s->ce); |
|
286 qemu_put_byte(f, s->wp); |
|
287 qemu_put_byte(f, s->gnd); |
|
288 qemu_put_buffer(f, s->io, sizeof(s->io)); |
|
289 qemu_put_be32(f, s->ioaddr - s->io); |
|
290 qemu_put_be32(f, s->iolen); |
|
291 |
|
292 qemu_put_be32s(f, &s->cmd); |
|
293 qemu_put_be32s(f, &s->addr); |
|
294 qemu_put_be32(f, s->addrlen); |
|
295 qemu_put_be32(f, s->status); |
|
296 qemu_put_be32(f, s->offset); |
|
297 /* XXX: do we want to save s->storage too? */ |
|
298 } |
|
299 |
|
300 static int nand_load(QEMUFile *f, void *opaque, int version_id) |
|
301 { |
|
302 struct nand_flash_s *s = (struct nand_flash_s *) opaque; |
|
303 s->cle = qemu_get_byte(f); |
|
304 s->ale = qemu_get_byte(f); |
|
305 s->ce = qemu_get_byte(f); |
|
306 s->wp = qemu_get_byte(f); |
|
307 s->gnd = qemu_get_byte(f); |
|
308 qemu_get_buffer(f, s->io, sizeof(s->io)); |
|
309 s->ioaddr = s->io + qemu_get_be32(f); |
|
310 s->iolen = qemu_get_be32(f); |
|
311 if (s->ioaddr >= s->io + sizeof(s->io) || s->ioaddr < s->io) |
|
312 return -EINVAL; |
|
313 |
|
314 qemu_get_be32s(f, &s->cmd); |
|
315 qemu_get_be32s(f, &s->addr); |
|
316 s->addrlen = qemu_get_be32(f); |
|
317 s->status = qemu_get_be32(f); |
|
318 s->offset = qemu_get_be32(f); |
|
319 return 0; |
|
320 } |
|
321 |
|
322 /* |
|
323 * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip |
|
324 * outputs are R/B and eight I/O pins. |
|
325 * |
|
326 * CE, WP and R/B are active low. |
|
327 */ |
|
328 void nand_setpins(struct nand_flash_s *s, |
|
329 int cle, int ale, int ce, int wp, int gnd) |
|
330 { |
|
331 s->cle = cle; |
|
332 s->ale = ale; |
|
333 s->ce = ce; |
|
334 s->wp = wp; |
|
335 s->gnd = gnd; |
|
336 if (wp) |
|
337 s->status |= NAND_IOSTATUS_UNPROTCT; |
|
338 else |
|
339 s->status &= ~NAND_IOSTATUS_UNPROTCT; |
|
340 } |
|
341 |
|
342 void nand_getpins(struct nand_flash_s *s, int *rb) |
|
343 { |
|
344 *rb = 1; |
|
345 } |
|
346 |
|
347 void nand_setio(struct nand_flash_s *s, uint8_t value) |
|
348 { |
|
349 if (!s->ce && s->cle) { |
|
350 if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { |
|
351 if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) |
|
352 return; |
|
353 if (value == NAND_CMD_RANDOMREAD1) { |
|
354 s->addr &= ~((1 << s->addr_shift) - 1); |
|
355 s->addrlen = 0; |
|
356 return; |
|
357 } |
|
358 } |
|
359 if (value == NAND_CMD_READ0) |
|
360 s->offset = 0; |
|
361 else if (value == NAND_CMD_READ1) { |
|
362 s->offset = 0x100; |
|
363 value = NAND_CMD_READ0; |
|
364 } |
|
365 else if (value == NAND_CMD_READ2) { |
|
366 s->offset = 1 << s->page_shift; |
|
367 value = NAND_CMD_READ0; |
|
368 } |
|
369 |
|
370 s->cmd = value; |
|
371 |
|
372 if (s->cmd == NAND_CMD_READSTATUS || |
|
373 s->cmd == NAND_CMD_PAGEPROGRAM2 || |
|
374 s->cmd == NAND_CMD_BLOCKERASE1 || |
|
375 s->cmd == NAND_CMD_BLOCKERASE2 || |
|
376 s->cmd == NAND_CMD_NOSERIALREAD2 || |
|
377 s->cmd == NAND_CMD_RANDOMREAD2 || |
|
378 s->cmd == NAND_CMD_RESET) |
|
379 nand_command(s); |
|
380 |
|
381 if (s->cmd != NAND_CMD_RANDOMREAD2) { |
|
382 s->addrlen = 0; |
|
383 s->addr = 0; |
|
384 } |
|
385 } |
|
386 |
|
387 if (s->ale) { |
|
388 s->addr |= value << (s->addrlen * 8); |
|
389 s->addrlen ++; |
|
390 |
|
391 if (s->addrlen == 1 && s->cmd == NAND_CMD_READID) |
|
392 nand_command(s); |
|
393 |
|
394 if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && |
|
395 s->addrlen == 3 && ( |
|
396 s->cmd == NAND_CMD_READ0 || |
|
397 s->cmd == NAND_CMD_PAGEPROGRAM1)) |
|
398 nand_command(s); |
|
399 if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && |
|
400 s->addrlen == 4 && ( |
|
401 s->cmd == NAND_CMD_READ0 || |
|
402 s->cmd == NAND_CMD_PAGEPROGRAM1)) |
|
403 nand_command(s); |
|
404 } |
|
405 |
|
406 if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { |
|
407 if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) |
|
408 s->io[s->iolen ++] = value; |
|
409 } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { |
|
410 if ((s->addr & ((1 << s->addr_shift) - 1)) < |
|
411 (1 << s->page_shift) + (1 << s->oob_shift)) { |
|
412 s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value; |
|
413 s->addr ++; |
|
414 } |
|
415 } |
|
416 } |
|
417 |
|
418 uint8_t nand_getio(struct nand_flash_s *s) |
|
419 { |
|
420 int offset; |
|
421 |
|
422 /* Allow sequential reading */ |
|
423 if (!s->iolen && s->cmd == NAND_CMD_READ0) { |
|
424 offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; |
|
425 s->offset = 0; |
|
426 |
|
427 s->blk_load(s, s->addr, offset); |
|
428 if (s->gnd) |
|
429 s->iolen = (1 << s->page_shift) - offset; |
|
430 else |
|
431 s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; |
|
432 } |
|
433 |
|
434 if (s->ce || s->iolen <= 0) |
|
435 return 0; |
|
436 |
|
437 s->iolen --; |
|
438 return *(s->ioaddr ++); |
|
439 } |
|
440 |
|
441 struct nand_flash_s *nand_init(int manf_id, int chip_id) |
|
442 { |
|
443 int pagesize; |
|
444 struct nand_flash_s *s; |
|
445 int index; |
|
446 |
|
447 if (nand_flash_ids[chip_id].size == 0) { |
|
448 cpu_abort(cpu_single_env, "%s: Unsupported NAND chip ID.\n", |
|
449 __FUNCTION__); |
|
450 } |
|
451 |
|
452 s = (struct nand_flash_s *) qemu_mallocz(sizeof(struct nand_flash_s)); |
|
453 index = drive_get_index(IF_MTD, 0, 0); |
|
454 if (index != -1) |
|
455 s->bdrv = drives_table[index].bdrv; |
|
456 s->manf_id = manf_id; |
|
457 s->chip_id = chip_id; |
|
458 s->size = nand_flash_ids[s->chip_id].size << 20; |
|
459 if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { |
|
460 s->page_shift = 11; |
|
461 s->erase_shift = 6; |
|
462 } else { |
|
463 s->page_shift = nand_flash_ids[s->chip_id].page_shift; |
|
464 s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; |
|
465 } |
|
466 |
|
467 switch (1 << s->page_shift) { |
|
468 case 256: |
|
469 nand_init_256(s); |
|
470 break; |
|
471 case 512: |
|
472 nand_init_512(s); |
|
473 break; |
|
474 case 2048: |
|
475 nand_init_2048(s); |
|
476 break; |
|
477 default: |
|
478 cpu_abort(cpu_single_env, "%s: Unsupported NAND block size.\n", |
|
479 __FUNCTION__); |
|
480 } |
|
481 |
|
482 pagesize = 1 << s->oob_shift; |
|
483 s->mem_oob = 1; |
|
484 if (s->bdrv && bdrv_getlength(s->bdrv) >= |
|
485 (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { |
|
486 pagesize = 0; |
|
487 s->mem_oob = 0; |
|
488 } |
|
489 |
|
490 if (!s->bdrv) |
|
491 pagesize += 1 << s->page_shift; |
|
492 if (pagesize) |
|
493 s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize), |
|
494 0xff, s->pages * pagesize); |
|
495 /* Give s->ioaddr a sane value in case we save state before it |
|
496 is used. */ |
|
497 s->ioaddr = s->io; |
|
498 |
|
499 register_savevm("nand", -1, 0, nand_save, nand_load, s); |
|
500 |
|
501 return s; |
|
502 } |
|
503 |
|
504 void nand_done(struct nand_flash_s *s) |
|
505 { |
|
506 if (s->bdrv) { |
|
507 bdrv_close(s->bdrv); |
|
508 bdrv_delete(s->bdrv); |
|
509 } |
|
510 |
|
511 if (!s->bdrv || s->mem_oob) |
|
512 free(s->storage); |
|
513 |
|
514 free(s); |
|
515 } |
|
516 |
|
517 #else |
|
518 |
|
519 /* Program a single page */ |
|
520 static void glue(nand_blk_write_, PAGE_SIZE)(struct nand_flash_s *s) |
|
521 { |
|
522 uint32_t off, page, sector, soff; |
|
523 uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; |
|
524 if (PAGE(s->addr) >= s->pages) |
|
525 return; |
|
526 |
|
527 if (!s->bdrv) { |
|
528 memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + |
|
529 s->offset, s->io, s->iolen); |
|
530 } else if (s->mem_oob) { |
|
531 sector = SECTOR(s->addr); |
|
532 off = (s->addr & PAGE_MASK) + s->offset; |
|
533 soff = SECTOR_OFFSET(s->addr); |
|
534 if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) { |
|
535 printf("%s: read error in sector %i\n", __FUNCTION__, sector); |
|
536 return; |
|
537 } |
|
538 |
|
539 memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); |
|
540 if (off + s->iolen > PAGE_SIZE) { |
|
541 page = PAGE(s->addr); |
|
542 memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, |
|
543 MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); |
|
544 } |
|
545 |
|
546 if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) |
|
547 printf("%s: write error in sector %i\n", __FUNCTION__, sector); |
|
548 } else { |
|
549 off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; |
|
550 sector = off >> 9; |
|
551 soff = off & 0x1ff; |
|
552 if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) { |
|
553 printf("%s: read error in sector %i\n", __FUNCTION__, sector); |
|
554 return; |
|
555 } |
|
556 |
|
557 memcpy(iobuf + soff, s->io, s->iolen); |
|
558 |
|
559 if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) |
|
560 printf("%s: write error in sector %i\n", __FUNCTION__, sector); |
|
561 } |
|
562 s->offset = 0; |
|
563 } |
|
564 |
|
565 /* Erase a single block */ |
|
566 static void glue(nand_blk_erase_, PAGE_SIZE)(struct nand_flash_s *s) |
|
567 { |
|
568 uint32_t i, page, addr; |
|
569 uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; |
|
570 addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); |
|
571 |
|
572 if (PAGE(addr) >= s->pages) |
|
573 return; |
|
574 |
|
575 if (!s->bdrv) { |
|
576 memset(s->storage + PAGE_START(addr), |
|
577 0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift); |
|
578 } else if (s->mem_oob) { |
|
579 memset(s->storage + (PAGE(addr) << OOB_SHIFT), |
|
580 0xff, OOB_SIZE << s->erase_shift); |
|
581 i = SECTOR(addr); |
|
582 page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); |
|
583 for (; i < page; i ++) |
|
584 if (bdrv_write(s->bdrv, i, iobuf, 1) == -1) |
|
585 printf("%s: write error in sector %i\n", __FUNCTION__, i); |
|
586 } else { |
|
587 addr = PAGE_START(addr); |
|
588 page = addr >> 9; |
|
589 if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) |
|
590 printf("%s: read error in sector %i\n", __FUNCTION__, page); |
|
591 memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); |
|
592 if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) |
|
593 printf("%s: write error in sector %i\n", __FUNCTION__, page); |
|
594 |
|
595 memset(iobuf, 0xff, 0x200); |
|
596 i = (addr & ~0x1ff) + 0x200; |
|
597 for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; |
|
598 i < addr; i += 0x200) |
|
599 if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1) |
|
600 printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9); |
|
601 |
|
602 page = i >> 9; |
|
603 if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) |
|
604 printf("%s: read error in sector %i\n", __FUNCTION__, page); |
|
605 memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); |
|
606 if (bdrv_write(s->bdrv, page, iobuf, 1) == -1) |
|
607 printf("%s: write error in sector %i\n", __FUNCTION__, page); |
|
608 } |
|
609 } |
|
610 |
|
611 static void glue(nand_blk_load_, PAGE_SIZE)(struct nand_flash_s *s, |
|
612 uint32_t addr, int offset) |
|
613 { |
|
614 if (PAGE(addr) >= s->pages) |
|
615 return; |
|
616 |
|
617 if (s->bdrv) { |
|
618 if (s->mem_oob) { |
|
619 if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1) |
|
620 printf("%s: read error in sector %i\n", |
|
621 __FUNCTION__, SECTOR(addr)); |
|
622 memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, |
|
623 s->storage + (PAGE(s->addr) << OOB_SHIFT), |
|
624 OOB_SIZE); |
|
625 s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; |
|
626 } else { |
|
627 if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9, |
|
628 s->io, (PAGE_SECTORS + 2)) == -1) |
|
629 printf("%s: read error in sector %i\n", |
|
630 __FUNCTION__, PAGE_START(addr) >> 9); |
|
631 s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; |
|
632 } |
|
633 } else { |
|
634 memcpy(s->io, s->storage + PAGE_START(s->addr) + |
|
635 offset, PAGE_SIZE + OOB_SIZE - offset); |
|
636 s->ioaddr = s->io; |
|
637 } |
|
638 |
|
639 s->addr &= PAGE_SIZE - 1; |
|
640 s->addr += PAGE_SIZE; |
|
641 } |
|
642 |
|
643 static void glue(nand_init_, PAGE_SIZE)(struct nand_flash_s *s) |
|
644 { |
|
645 s->oob_shift = PAGE_SHIFT - 5; |
|
646 s->pages = s->size >> PAGE_SHIFT; |
|
647 s->addr_shift = ADDR_SHIFT; |
|
648 |
|
649 s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE); |
|
650 s->blk_write = glue(nand_blk_write_, PAGE_SIZE); |
|
651 s->blk_load = glue(nand_blk_load_, PAGE_SIZE); |
|
652 } |
|
653 |
|
654 # undef PAGE_SIZE |
|
655 # undef PAGE_SHIFT |
|
656 # undef PAGE_SECTORS |
|
657 # undef ADDR_SHIFT |
|
658 #endif /* NAND_IO */ |