|
1 /* |
|
2 * OneNAND flash memories emulation. |
|
3 * |
|
4 * Copyright (C) 2008 Nokia Corporation |
|
5 * Written by Andrzej Zaborowski <andrew@openedhand.com> |
|
6 * |
|
7 * This program is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU General Public License as |
|
9 * published by the Free Software Foundation; either version 2 or |
|
10 * (at your option) version 3 of the License. |
|
11 * |
|
12 * This program is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
15 * GNU General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License |
|
18 * along with this program; if not, write to the Free Software |
|
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
|
20 * MA 02111-1307 USA |
|
21 */ |
|
22 |
|
23 #include "qemu-common.h" |
|
24 #include "flash.h" |
|
25 #include "irq.h" |
|
26 #include "sysemu.h" |
|
27 #include "block.h" |
|
28 |
|
29 /* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ |
|
30 #define PAGE_SHIFT 11 |
|
31 |
|
32 /* Fixed */ |
|
33 #define BLOCK_SHIFT (PAGE_SHIFT + 6) |
|
34 |
|
35 struct onenand_s { |
|
36 uint32_t id; |
|
37 int shift; |
|
38 target_phys_addr_t base; |
|
39 qemu_irq intr; |
|
40 qemu_irq rdy; |
|
41 BlockDriverState *bdrv; |
|
42 BlockDriverState *bdrv_cur; |
|
43 uint8_t *image; |
|
44 uint8_t *otp; |
|
45 uint8_t *current; |
|
46 ram_addr_t ram; |
|
47 uint8_t *boot[2]; |
|
48 uint8_t *data[2][2]; |
|
49 int iomemtype; |
|
50 int cycle; |
|
51 int otpmode; |
|
52 |
|
53 uint16_t addr[8]; |
|
54 uint16_t unladdr[8]; |
|
55 int bufaddr; |
|
56 int count; |
|
57 uint16_t command; |
|
58 uint16_t config[2]; |
|
59 uint16_t status; |
|
60 uint16_t intstatus; |
|
61 uint16_t wpstatus; |
|
62 |
|
63 struct ecc_state_s ecc; |
|
64 |
|
65 int density_mask; |
|
66 int secs; |
|
67 int secs_cur; |
|
68 int blocks; |
|
69 uint8_t *blockwp; |
|
70 }; |
|
71 |
|
72 enum { |
|
73 ONEN_BUF_BLOCK = 0, |
|
74 ONEN_BUF_BLOCK2 = 1, |
|
75 ONEN_BUF_DEST_BLOCK = 2, |
|
76 ONEN_BUF_DEST_PAGE = 3, |
|
77 ONEN_BUF_PAGE = 7, |
|
78 }; |
|
79 |
|
80 enum { |
|
81 ONEN_ERR_CMD = 1 << 10, |
|
82 ONEN_ERR_ERASE = 1 << 11, |
|
83 ONEN_ERR_PROG = 1 << 12, |
|
84 ONEN_ERR_LOAD = 1 << 13, |
|
85 }; |
|
86 |
|
87 enum { |
|
88 ONEN_INT_RESET = 1 << 4, |
|
89 ONEN_INT_ERASE = 1 << 5, |
|
90 ONEN_INT_PROG = 1 << 6, |
|
91 ONEN_INT_LOAD = 1 << 7, |
|
92 ONEN_INT = 1 << 15, |
|
93 }; |
|
94 |
|
95 enum { |
|
96 ONEN_LOCK_LOCKTIGHTEN = 1 << 0, |
|
97 ONEN_LOCK_LOCKED = 1 << 1, |
|
98 ONEN_LOCK_UNLOCKED = 1 << 2, |
|
99 }; |
|
100 |
|
101 void onenand_base_update(void *opaque, target_phys_addr_t new) |
|
102 { |
|
103 struct onenand_s *s = (struct onenand_s *) opaque; |
|
104 |
|
105 s->base = new; |
|
106 |
|
107 /* XXX: We should use IO_MEM_ROMD but we broke it earlier... |
|
108 * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to |
|
109 * write boot commands. Also take note of the BWPS bit. */ |
|
110 cpu_register_physical_memory(s->base + (0x0000 << s->shift), |
|
111 0x0200 << s->shift, s->iomemtype); |
|
112 cpu_register_physical_memory(s->base + (0x0200 << s->shift), |
|
113 0xbe00 << s->shift, |
|
114 (s->ram +(0x0200 << s->shift)) | IO_MEM_RAM); |
|
115 if (s->iomemtype) |
|
116 cpu_register_physical_memory_offset(s->base + (0xc000 << s->shift), |
|
117 0x4000 << s->shift, s->iomemtype, (0xc000 << s->shift)); |
|
118 } |
|
119 |
|
120 void onenand_base_unmap(void *opaque) |
|
121 { |
|
122 struct onenand_s *s = (struct onenand_s *) opaque; |
|
123 |
|
124 cpu_register_physical_memory(s->base, |
|
125 0x10000 << s->shift, IO_MEM_UNASSIGNED); |
|
126 } |
|
127 |
|
128 static void onenand_intr_update(struct onenand_s *s) |
|
129 { |
|
130 qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1); |
|
131 } |
|
132 |
|
133 /* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */ |
|
134 static void onenand_reset(struct onenand_s *s, int cold) |
|
135 { |
|
136 memset(&s->addr, 0, sizeof(s->addr)); |
|
137 s->command = 0; |
|
138 s->count = 1; |
|
139 s->bufaddr = 0; |
|
140 s->config[0] = 0x40c0; |
|
141 s->config[1] = 0x0000; |
|
142 onenand_intr_update(s); |
|
143 qemu_irq_raise(s->rdy); |
|
144 s->status = 0x0000; |
|
145 s->intstatus = cold ? 0x8080 : 0x8010; |
|
146 s->unladdr[0] = 0; |
|
147 s->unladdr[1] = 0; |
|
148 s->wpstatus = 0x0002; |
|
149 s->cycle = 0; |
|
150 s->otpmode = 0; |
|
151 s->bdrv_cur = s->bdrv; |
|
152 s->current = s->image; |
|
153 s->secs_cur = s->secs; |
|
154 |
|
155 if (cold) { |
|
156 /* Lock the whole flash */ |
|
157 memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); |
|
158 |
|
159 if (s->bdrv && bdrv_read(s->bdrv, 0, s->boot[0], 8) < 0) |
|
160 cpu_abort(cpu_single_env, "%s: Loading the BootRAM failed.\n", |
|
161 __FUNCTION__); |
|
162 } |
|
163 } |
|
164 |
|
165 static inline int onenand_load_main(struct onenand_s *s, int sec, int secn, |
|
166 void *dest) |
|
167 { |
|
168 if (s->bdrv_cur) |
|
169 return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0; |
|
170 else if (sec + secn > s->secs_cur) |
|
171 return 1; |
|
172 |
|
173 memcpy(dest, s->current + (sec << 9), secn << 9); |
|
174 |
|
175 return 0; |
|
176 } |
|
177 |
|
178 static inline int onenand_prog_main(struct onenand_s *s, int sec, int secn, |
|
179 void *src) |
|
180 { |
|
181 if (s->bdrv_cur) |
|
182 return bdrv_write(s->bdrv_cur, sec, src, secn) < 0; |
|
183 else if (sec + secn > s->secs_cur) |
|
184 return 1; |
|
185 |
|
186 memcpy(s->current + (sec << 9), src, secn << 9); |
|
187 |
|
188 return 0; |
|
189 } |
|
190 |
|
191 static inline int onenand_load_spare(struct onenand_s *s, int sec, int secn, |
|
192 void *dest) |
|
193 { |
|
194 uint8_t buf[512]; |
|
195 |
|
196 if (s->bdrv_cur) { |
|
197 if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) |
|
198 return 1; |
|
199 memcpy(dest, buf + ((sec & 31) << 4), secn << 4); |
|
200 } else if (sec + secn > s->secs_cur) |
|
201 return 1; |
|
202 else |
|
203 memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4); |
|
204 |
|
205 return 0; |
|
206 } |
|
207 |
|
208 static inline int onenand_prog_spare(struct onenand_s *s, int sec, int secn, |
|
209 void *src) |
|
210 { |
|
211 uint8_t buf[512]; |
|
212 |
|
213 if (s->bdrv_cur) { |
|
214 if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) |
|
215 return 1; |
|
216 memcpy(buf + ((sec & 31) << 4), src, secn << 4); |
|
217 return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0; |
|
218 } else if (sec + secn > s->secs_cur) |
|
219 return 1; |
|
220 |
|
221 memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4); |
|
222 |
|
223 return 0; |
|
224 } |
|
225 |
|
226 static inline int onenand_erase(struct onenand_s *s, int sec, int num) |
|
227 { |
|
228 /* TODO: optimise */ |
|
229 uint8_t buf[512]; |
|
230 |
|
231 memset(buf, 0xff, sizeof(buf)); |
|
232 for (; num > 0; num --, sec ++) { |
|
233 if (onenand_prog_main(s, sec, 1, buf)) |
|
234 return 1; |
|
235 if (onenand_prog_spare(s, sec, 1, buf)) |
|
236 return 1; |
|
237 } |
|
238 |
|
239 return 0; |
|
240 } |
|
241 |
|
242 static void onenand_command(struct onenand_s *s, int cmd) |
|
243 { |
|
244 int b; |
|
245 int sec; |
|
246 void *buf; |
|
247 #define SETADDR(block, page) \ |
|
248 sec = (s->addr[page] & 3) + \ |
|
249 ((((s->addr[page] >> 2) & 0x3f) + \ |
|
250 (((s->addr[block] & 0xfff) | \ |
|
251 (s->addr[block] >> 15 ? \ |
|
252 s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9)); |
|
253 #define SETBUF_M() \ |
|
254 buf = (s->bufaddr & 8) ? \ |
|
255 s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \ |
|
256 buf += (s->bufaddr & 3) << 9; |
|
257 #define SETBUF_S() \ |
|
258 buf = (s->bufaddr & 8) ? \ |
|
259 s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ |
|
260 buf += (s->bufaddr & 3) << 4; |
|
261 |
|
262 switch (cmd) { |
|
263 case 0x00: /* Load single/multiple sector data unit into buffer */ |
|
264 SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) |
|
265 |
|
266 SETBUF_M() |
|
267 if (onenand_load_main(s, sec, s->count, buf)) |
|
268 s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; |
|
269 |
|
270 #if 0 |
|
271 SETBUF_S() |
|
272 if (onenand_load_spare(s, sec, s->count, buf)) |
|
273 s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; |
|
274 #endif |
|
275 |
|
276 /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) |
|
277 * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) |
|
278 * then we need two split the read/write into two chunks. |
|
279 */ |
|
280 s->intstatus |= ONEN_INT | ONEN_INT_LOAD; |
|
281 break; |
|
282 case 0x13: /* Load single/multiple spare sector into buffer */ |
|
283 SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) |
|
284 |
|
285 SETBUF_S() |
|
286 if (onenand_load_spare(s, sec, s->count, buf)) |
|
287 s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; |
|
288 |
|
289 /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) |
|
290 * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) |
|
291 * then we need two split the read/write into two chunks. |
|
292 */ |
|
293 s->intstatus |= ONEN_INT | ONEN_INT_LOAD; |
|
294 break; |
|
295 case 0x80: /* Program single/multiple sector data unit from buffer */ |
|
296 SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) |
|
297 |
|
298 SETBUF_M() |
|
299 if (onenand_prog_main(s, sec, s->count, buf)) |
|
300 s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; |
|
301 |
|
302 #if 0 |
|
303 SETBUF_S() |
|
304 if (onenand_prog_spare(s, sec, s->count, buf)) |
|
305 s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; |
|
306 #endif |
|
307 |
|
308 /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) |
|
309 * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) |
|
310 * then we need two split the read/write into two chunks. |
|
311 */ |
|
312 s->intstatus |= ONEN_INT | ONEN_INT_PROG; |
|
313 break; |
|
314 case 0x1a: /* Program single/multiple spare area sector from buffer */ |
|
315 SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) |
|
316 |
|
317 SETBUF_S() |
|
318 if (onenand_prog_spare(s, sec, s->count, buf)) |
|
319 s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; |
|
320 |
|
321 /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) |
|
322 * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) |
|
323 * then we need two split the read/write into two chunks. |
|
324 */ |
|
325 s->intstatus |= ONEN_INT | ONEN_INT_PROG; |
|
326 break; |
|
327 case 0x1b: /* Copy-back program */ |
|
328 SETBUF_S() |
|
329 |
|
330 SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) |
|
331 if (onenand_load_main(s, sec, s->count, buf)) |
|
332 s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; |
|
333 |
|
334 SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE) |
|
335 if (onenand_prog_main(s, sec, s->count, buf)) |
|
336 s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; |
|
337 |
|
338 /* TODO: spare areas */ |
|
339 |
|
340 s->intstatus |= ONEN_INT | ONEN_INT_PROG; |
|
341 break; |
|
342 |
|
343 case 0x23: /* Unlock NAND array block(s) */ |
|
344 s->intstatus |= ONEN_INT; |
|
345 |
|
346 /* XXX the previous (?) area should be locked automatically */ |
|
347 for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { |
|
348 if (b >= s->blocks) { |
|
349 s->status |= ONEN_ERR_CMD; |
|
350 break; |
|
351 } |
|
352 if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) |
|
353 break; |
|
354 |
|
355 s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; |
|
356 } |
|
357 break; |
|
358 case 0x27: /* Unlock All NAND array blocks */ |
|
359 s->intstatus |= ONEN_INT; |
|
360 |
|
361 for (b = 0; b < s->blocks; b ++) { |
|
362 if (b >= s->blocks) { |
|
363 s->status |= ONEN_ERR_CMD; |
|
364 break; |
|
365 } |
|
366 if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) |
|
367 break; |
|
368 |
|
369 s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; |
|
370 } |
|
371 break; |
|
372 |
|
373 case 0x2a: /* Lock NAND array block(s) */ |
|
374 s->intstatus |= ONEN_INT; |
|
375 |
|
376 for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { |
|
377 if (b >= s->blocks) { |
|
378 s->status |= ONEN_ERR_CMD; |
|
379 break; |
|
380 } |
|
381 if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) |
|
382 break; |
|
383 |
|
384 s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED; |
|
385 } |
|
386 break; |
|
387 case 0x2c: /* Lock-tight NAND array block(s) */ |
|
388 s->intstatus |= ONEN_INT; |
|
389 |
|
390 for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { |
|
391 if (b >= s->blocks) { |
|
392 s->status |= ONEN_ERR_CMD; |
|
393 break; |
|
394 } |
|
395 if (s->blockwp[b] == ONEN_LOCK_UNLOCKED) |
|
396 continue; |
|
397 |
|
398 s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN; |
|
399 } |
|
400 break; |
|
401 |
|
402 case 0x71: /* Erase-Verify-Read */ |
|
403 s->intstatus |= ONEN_INT; |
|
404 break; |
|
405 case 0x95: /* Multi-block erase */ |
|
406 qemu_irq_pulse(s->intr); |
|
407 /* Fall through. */ |
|
408 case 0x94: /* Block erase */ |
|
409 sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) | |
|
410 (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0)) |
|
411 << (BLOCK_SHIFT - 9); |
|
412 if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9))) |
|
413 s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE; |
|
414 |
|
415 s->intstatus |= ONEN_INT | ONEN_INT_ERASE; |
|
416 break; |
|
417 case 0xb0: /* Erase suspend */ |
|
418 break; |
|
419 case 0x30: /* Erase resume */ |
|
420 s->intstatus |= ONEN_INT | ONEN_INT_ERASE; |
|
421 break; |
|
422 |
|
423 case 0xf0: /* Reset NAND Flash core */ |
|
424 onenand_reset(s, 0); |
|
425 break; |
|
426 case 0xf3: /* Reset OneNAND */ |
|
427 onenand_reset(s, 0); |
|
428 break; |
|
429 |
|
430 case 0x65: /* OTP Access */ |
|
431 s->intstatus |= ONEN_INT; |
|
432 s->bdrv_cur = 0; |
|
433 s->current = s->otp; |
|
434 s->secs_cur = 1 << (BLOCK_SHIFT - 9); |
|
435 s->addr[ONEN_BUF_BLOCK] = 0; |
|
436 s->otpmode = 1; |
|
437 break; |
|
438 |
|
439 default: |
|
440 s->status |= ONEN_ERR_CMD; |
|
441 s->intstatus |= ONEN_INT; |
|
442 fprintf(stderr, "%s: unknown OneNAND command %x\n", |
|
443 __FUNCTION__, cmd); |
|
444 } |
|
445 |
|
446 onenand_intr_update(s); |
|
447 } |
|
448 |
|
449 static uint32_t onenand_read(void *opaque, target_phys_addr_t addr) |
|
450 { |
|
451 struct onenand_s *s = (struct onenand_s *) opaque; |
|
452 int offset = addr >> s->shift; |
|
453 |
|
454 switch (offset) { |
|
455 case 0x0000 ... 0xc000: |
|
456 return lduw_le_p(s->boot[0] + addr); |
|
457 |
|
458 case 0xf000: /* Manufacturer ID */ |
|
459 return (s->id >> 16) & 0xff; |
|
460 case 0xf001: /* Device ID */ |
|
461 return (s->id >> 8) & 0xff; |
|
462 /* TODO: get the following values from a real chip! */ |
|
463 case 0xf002: /* Version ID */ |
|
464 return (s->id >> 0) & 0xff; |
|
465 case 0xf003: /* Data Buffer size */ |
|
466 return 1 << PAGE_SHIFT; |
|
467 case 0xf004: /* Boot Buffer size */ |
|
468 return 0x200; |
|
469 case 0xf005: /* Amount of buffers */ |
|
470 return 1 | (2 << 8); |
|
471 case 0xf006: /* Technology */ |
|
472 return 0; |
|
473 |
|
474 case 0xf100 ... 0xf107: /* Start addresses */ |
|
475 return s->addr[offset - 0xf100]; |
|
476 |
|
477 case 0xf200: /* Start buffer */ |
|
478 return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10))); |
|
479 |
|
480 case 0xf220: /* Command */ |
|
481 return s->command; |
|
482 case 0xf221: /* System Configuration 1 */ |
|
483 return s->config[0] & 0xffe0; |
|
484 case 0xf222: /* System Configuration 2 */ |
|
485 return s->config[1]; |
|
486 |
|
487 case 0xf240: /* Controller Status */ |
|
488 return s->status; |
|
489 case 0xf241: /* Interrupt */ |
|
490 return s->intstatus; |
|
491 case 0xf24c: /* Unlock Start Block Address */ |
|
492 return s->unladdr[0]; |
|
493 case 0xf24d: /* Unlock End Block Address */ |
|
494 return s->unladdr[1]; |
|
495 case 0xf24e: /* Write Protection Status */ |
|
496 return s->wpstatus; |
|
497 |
|
498 case 0xff00: /* ECC Status */ |
|
499 return 0x00; |
|
500 case 0xff01: /* ECC Result of main area data */ |
|
501 case 0xff02: /* ECC Result of spare area data */ |
|
502 case 0xff03: /* ECC Result of main area data */ |
|
503 case 0xff04: /* ECC Result of spare area data */ |
|
504 cpu_abort(cpu_single_env, "%s: imeplement ECC\n", __FUNCTION__); |
|
505 return 0x0000; |
|
506 } |
|
507 |
|
508 fprintf(stderr, "%s: unknown OneNAND register %x\n", |
|
509 __FUNCTION__, offset); |
|
510 return 0; |
|
511 } |
|
512 |
|
513 static void onenand_write(void *opaque, target_phys_addr_t addr, |
|
514 uint32_t value) |
|
515 { |
|
516 struct onenand_s *s = (struct onenand_s *) opaque; |
|
517 int offset = addr >> s->shift; |
|
518 int sec; |
|
519 |
|
520 switch (offset) { |
|
521 case 0x0000 ... 0x01ff: |
|
522 case 0x8000 ... 0x800f: |
|
523 if (s->cycle) { |
|
524 s->cycle = 0; |
|
525 |
|
526 if (value == 0x0000) { |
|
527 SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) |
|
528 onenand_load_main(s, sec, |
|
529 1 << (PAGE_SHIFT - 9), s->data[0][0]); |
|
530 s->addr[ONEN_BUF_PAGE] += 4; |
|
531 s->addr[ONEN_BUF_PAGE] &= 0xff; |
|
532 } |
|
533 break; |
|
534 } |
|
535 |
|
536 switch (value) { |
|
537 case 0x00f0: /* Reset OneNAND */ |
|
538 onenand_reset(s, 0); |
|
539 break; |
|
540 |
|
541 case 0x00e0: /* Load Data into Buffer */ |
|
542 s->cycle = 1; |
|
543 break; |
|
544 |
|
545 case 0x0090: /* Read Identification Data */ |
|
546 memset(s->boot[0], 0, 3 << s->shift); |
|
547 s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff; |
|
548 s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff; |
|
549 s->boot[0][2 << s->shift] = s->wpstatus & 0xff; |
|
550 break; |
|
551 |
|
552 default: |
|
553 fprintf(stderr, "%s: unknown OneNAND boot command %x\n", |
|
554 __FUNCTION__, value); |
|
555 } |
|
556 break; |
|
557 |
|
558 case 0xf100 ... 0xf107: /* Start addresses */ |
|
559 s->addr[offset - 0xf100] = value; |
|
560 break; |
|
561 |
|
562 case 0xf200: /* Start buffer */ |
|
563 s->bufaddr = (value >> 8) & 0xf; |
|
564 if (PAGE_SHIFT == 11) |
|
565 s->count = (value & 3) ?: 4; |
|
566 else if (PAGE_SHIFT == 10) |
|
567 s->count = (value & 1) ?: 2; |
|
568 break; |
|
569 |
|
570 case 0xf220: /* Command */ |
|
571 if (s->intstatus & (1 << 15)) |
|
572 break; |
|
573 s->command = value; |
|
574 onenand_command(s, s->command); |
|
575 break; |
|
576 case 0xf221: /* System Configuration 1 */ |
|
577 s->config[0] = value; |
|
578 onenand_intr_update(s); |
|
579 qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1); |
|
580 break; |
|
581 case 0xf222: /* System Configuration 2 */ |
|
582 s->config[1] = value; |
|
583 break; |
|
584 |
|
585 case 0xf241: /* Interrupt */ |
|
586 s->intstatus &= value; |
|
587 if ((1 << 15) & ~s->intstatus) |
|
588 s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE | |
|
589 ONEN_ERR_PROG | ONEN_ERR_LOAD); |
|
590 onenand_intr_update(s); |
|
591 break; |
|
592 case 0xf24c: /* Unlock Start Block Address */ |
|
593 s->unladdr[0] = value & (s->blocks - 1); |
|
594 /* For some reason we have to set the end address to by default |
|
595 * be same as start because the software forgets to write anything |
|
596 * in there. */ |
|
597 s->unladdr[1] = value & (s->blocks - 1); |
|
598 break; |
|
599 case 0xf24d: /* Unlock End Block Address */ |
|
600 s->unladdr[1] = value & (s->blocks - 1); |
|
601 break; |
|
602 |
|
603 default: |
|
604 fprintf(stderr, "%s: unknown OneNAND register %x\n", |
|
605 __FUNCTION__, offset); |
|
606 } |
|
607 } |
|
608 |
|
609 static CPUReadMemoryFunc *onenand_readfn[] = { |
|
610 onenand_read, /* TODO */ |
|
611 onenand_read, |
|
612 onenand_read, |
|
613 }; |
|
614 |
|
615 static CPUWriteMemoryFunc *onenand_writefn[] = { |
|
616 onenand_write, /* TODO */ |
|
617 onenand_write, |
|
618 onenand_write, |
|
619 }; |
|
620 |
|
621 void *onenand_init(uint32_t id, int regshift, qemu_irq irq) |
|
622 { |
|
623 struct onenand_s *s = (struct onenand_s *) qemu_mallocz(sizeof(*s)); |
|
624 int bdrv_index = drive_get_index(IF_MTD, 0, 0); |
|
625 uint32_t size = 1 << (24 + ((id >> 12) & 7)); |
|
626 void *ram; |
|
627 |
|
628 s->shift = regshift; |
|
629 s->intr = irq; |
|
630 s->rdy = 0; |
|
631 s->id = id; |
|
632 s->blocks = size >> BLOCK_SHIFT; |
|
633 s->secs = size >> 9; |
|
634 s->blockwp = qemu_malloc(s->blocks); |
|
635 s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0; |
|
636 s->iomemtype = cpu_register_io_memory(0, onenand_readfn, |
|
637 onenand_writefn, s); |
|
638 if (bdrv_index == -1) |
|
639 s->image = memset(qemu_malloc(size + (size >> 5)), |
|
640 0xff, size + (size >> 5)); |
|
641 else |
|
642 s->bdrv = drives_table[bdrv_index].bdrv; |
|
643 s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT), |
|
644 0xff, (64 + 2) << PAGE_SHIFT); |
|
645 s->ram = qemu_ram_alloc(0xc000 << s->shift); |
|
646 /* FIXME: This is broken if it spans multiple RAM regions. */ |
|
647 ram = host_ram_addr(s->ram); |
|
648 s->boot[0] = ram + (0x0000 << s->shift); |
|
649 s->boot[1] = ram + (0x8000 << s->shift); |
|
650 s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift); |
|
651 s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift); |
|
652 s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift); |
|
653 s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift); |
|
654 |
|
655 onenand_reset(s, 1); |
|
656 |
|
657 return s; |
|
658 } |
|
659 |
|
660 void *onenand_raw_otp(void *opaque) |
|
661 { |
|
662 struct onenand_s *s = (struct onenand_s *) opaque; |
|
663 |
|
664 return s->otp; |
|
665 } |