]>
Commit | Line | Data |
---|---|---|
008ff9d7 JM |
1 | /* |
2 | * QEMU PowerPC 4xx embedded processors shared devices emulation | |
3 | * | |
4 | * Copyright (c) 2007 Jocelyn Mayer | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
87ecb68b PB |
24 | #include "hw.h" |
25 | #include "ppc.h" | |
008ff9d7 | 26 | #include "ppc4xx.h" |
87ecb68b | 27 | #include "sysemu.h" |
3b3fb322 | 28 | #include "qemu-log.h" |
008ff9d7 JM |
29 | |
30 | //#define DEBUG_MMIO | |
aae9366a | 31 | //#define DEBUG_UNASSIGNED |
008ff9d7 JM |
32 | #define DEBUG_UIC |
33 | ||
34 | /*****************************************************************************/ | |
35 | /* Generic PowerPC 4xx processor instanciation */ | |
b55266b5 | 36 | CPUState *ppc4xx_init (const char *cpu_model, |
008ff9d7 JM |
37 | clk_setup_t *cpu_clk, clk_setup_t *tb_clk, |
38 | uint32_t sysclk) | |
39 | { | |
40 | CPUState *env; | |
008ff9d7 JM |
41 | |
42 | /* init CPUs */ | |
aaed909a FB |
43 | env = cpu_init(cpu_model); |
44 | if (!env) { | |
45 | fprintf(stderr, "Unable to find PowerPC %s CPU definition\n", | |
46 | cpu_model); | |
47 | exit(1); | |
008ff9d7 | 48 | } |
008ff9d7 JM |
49 | cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ |
50 | cpu_clk->opaque = env; | |
51 | /* Set time-base frequency to sysclk */ | |
52 | tb_clk->cb = ppc_emb_timers_init(env, sysclk); | |
53 | tb_clk->opaque = env; | |
54 | ppc_dcr_init(env, NULL, NULL); | |
55 | /* Register qemu callbacks */ | |
56 | qemu_register_reset(&cpu_ppc_reset, env); | |
008ff9d7 JM |
57 | |
58 | return env; | |
59 | } | |
60 | ||
61 | /*****************************************************************************/ | |
62 | /* Fake device used to map multiple devices in a single memory page */ | |
63 | #define MMIO_AREA_BITS 8 | |
64 | #define MMIO_AREA_LEN (1 << MMIO_AREA_BITS) | |
65 | #define MMIO_AREA_NB (1 << (TARGET_PAGE_BITS - MMIO_AREA_BITS)) | |
66 | #define MMIO_IDX(addr) (((addr) >> MMIO_AREA_BITS) & (MMIO_AREA_NB - 1)) | |
67 | struct ppc4xx_mmio_t { | |
68 | target_phys_addr_t base; | |
69 | CPUReadMemoryFunc **mem_read[MMIO_AREA_NB]; | |
70 | CPUWriteMemoryFunc **mem_write[MMIO_AREA_NB]; | |
71 | void *opaque[MMIO_AREA_NB]; | |
72 | }; | |
73 | ||
74 | static uint32_t unassigned_mmio_readb (void *opaque, target_phys_addr_t addr) | |
75 | { | |
76 | #ifdef DEBUG_UNASSIGNED | |
77 | ppc4xx_mmio_t *mmio; | |
78 | ||
79 | mmio = opaque; | |
80 | printf("Unassigned mmio read 0x" PADDRX " base " PADDRX "\n", | |
81 | addr, mmio->base); | |
82 | #endif | |
83 | ||
84 | return 0; | |
85 | } | |
86 | ||
87 | static void unassigned_mmio_writeb (void *opaque, | |
88 | target_phys_addr_t addr, uint32_t val) | |
89 | { | |
90 | #ifdef DEBUG_UNASSIGNED | |
91 | ppc4xx_mmio_t *mmio; | |
92 | ||
93 | mmio = opaque; | |
94 | printf("Unassigned mmio write 0x" PADDRX " = 0x%x base " PADDRX "\n", | |
95 | addr, val, mmio->base); | |
96 | #endif | |
97 | } | |
98 | ||
99 | static CPUReadMemoryFunc *unassigned_mmio_read[3] = { | |
100 | unassigned_mmio_readb, | |
101 | unassigned_mmio_readb, | |
102 | unassigned_mmio_readb, | |
103 | }; | |
104 | ||
105 | static CPUWriteMemoryFunc *unassigned_mmio_write[3] = { | |
106 | unassigned_mmio_writeb, | |
107 | unassigned_mmio_writeb, | |
108 | unassigned_mmio_writeb, | |
109 | }; | |
110 | ||
111 | static uint32_t mmio_readlen (ppc4xx_mmio_t *mmio, | |
112 | target_phys_addr_t addr, int len) | |
113 | { | |
114 | CPUReadMemoryFunc **mem_read; | |
115 | uint32_t ret; | |
116 | int idx; | |
117 | ||
8da3ff18 | 118 | idx = MMIO_IDX(addr); |
008ff9d7 JM |
119 | #if defined(DEBUG_MMIO) |
120 | printf("%s: mmio %p len %d addr " PADDRX " idx %d\n", __func__, | |
121 | mmio, len, addr, idx); | |
122 | #endif | |
123 | mem_read = mmio->mem_read[idx]; | |
8da3ff18 | 124 | ret = (*mem_read[len])(mmio->opaque[idx], addr); |
008ff9d7 JM |
125 | |
126 | return ret; | |
127 | } | |
128 | ||
129 | static void mmio_writelen (ppc4xx_mmio_t *mmio, | |
130 | target_phys_addr_t addr, uint32_t value, int len) | |
131 | { | |
132 | CPUWriteMemoryFunc **mem_write; | |
133 | int idx; | |
134 | ||
8da3ff18 | 135 | idx = MMIO_IDX(addr); |
008ff9d7 | 136 | #if defined(DEBUG_MMIO) |
aae9366a JM |
137 | printf("%s: mmio %p len %d addr " PADDRX " idx %d value %08" PRIx32 "\n", |
138 | __func__, mmio, len, addr, idx, value); | |
008ff9d7 JM |
139 | #endif |
140 | mem_write = mmio->mem_write[idx]; | |
8da3ff18 | 141 | (*mem_write[len])(mmio->opaque[idx], addr, value); |
008ff9d7 JM |
142 | } |
143 | ||
144 | static uint32_t mmio_readb (void *opaque, target_phys_addr_t addr) | |
145 | { | |
146 | #if defined(DEBUG_MMIO) | |
147 | printf("%s: addr " PADDRX "\n", __func__, addr); | |
148 | #endif | |
149 | ||
150 | return mmio_readlen(opaque, addr, 0); | |
151 | } | |
152 | ||
153 | static void mmio_writeb (void *opaque, | |
154 | target_phys_addr_t addr, uint32_t value) | |
155 | { | |
156 | #if defined(DEBUG_MMIO) | |
aae9366a | 157 | printf("%s: addr " PADDRX " val %08" PRIx32 "\n", __func__, addr, value); |
008ff9d7 JM |
158 | #endif |
159 | mmio_writelen(opaque, addr, value, 0); | |
160 | } | |
161 | ||
162 | static uint32_t mmio_readw (void *opaque, target_phys_addr_t addr) | |
163 | { | |
164 | #if defined(DEBUG_MMIO) | |
165 | printf("%s: addr " PADDRX "\n", __func__, addr); | |
166 | #endif | |
167 | ||
168 | return mmio_readlen(opaque, addr, 1); | |
169 | } | |
170 | ||
171 | static void mmio_writew (void *opaque, | |
172 | target_phys_addr_t addr, uint32_t value) | |
173 | { | |
174 | #if defined(DEBUG_MMIO) | |
aae9366a | 175 | printf("%s: addr " PADDRX " val %08" PRIx32 "\n", __func__, addr, value); |
008ff9d7 JM |
176 | #endif |
177 | mmio_writelen(opaque, addr, value, 1); | |
178 | } | |
179 | ||
180 | static uint32_t mmio_readl (void *opaque, target_phys_addr_t addr) | |
181 | { | |
182 | #if defined(DEBUG_MMIO) | |
183 | printf("%s: addr " PADDRX "\n", __func__, addr); | |
184 | #endif | |
185 | ||
186 | return mmio_readlen(opaque, addr, 2); | |
187 | } | |
188 | ||
189 | static void mmio_writel (void *opaque, | |
190 | target_phys_addr_t addr, uint32_t value) | |
191 | { | |
192 | #if defined(DEBUG_MMIO) | |
aae9366a | 193 | printf("%s: addr " PADDRX " val %08" PRIx32 "\n", __func__, addr, value); |
008ff9d7 JM |
194 | #endif |
195 | mmio_writelen(opaque, addr, value, 2); | |
196 | } | |
197 | ||
198 | static CPUReadMemoryFunc *mmio_read[] = { | |
199 | &mmio_readb, | |
200 | &mmio_readw, | |
201 | &mmio_readl, | |
202 | }; | |
203 | ||
204 | static CPUWriteMemoryFunc *mmio_write[] = { | |
205 | &mmio_writeb, | |
206 | &mmio_writew, | |
207 | &mmio_writel, | |
208 | }; | |
209 | ||
210 | int ppc4xx_mmio_register (CPUState *env, ppc4xx_mmio_t *mmio, | |
211 | target_phys_addr_t offset, uint32_t len, | |
212 | CPUReadMemoryFunc **mem_read, | |
213 | CPUWriteMemoryFunc **mem_write, void *opaque) | |
214 | { | |
aae9366a | 215 | target_phys_addr_t end; |
008ff9d7 JM |
216 | int idx, eidx; |
217 | ||
218 | if ((offset + len) > TARGET_PAGE_SIZE) | |
219 | return -1; | |
220 | idx = MMIO_IDX(offset); | |
221 | end = offset + len - 1; | |
222 | eidx = MMIO_IDX(end); | |
223 | #if defined(DEBUG_MMIO) | |
aae9366a JM |
224 | printf("%s: offset " PADDRX " len %08" PRIx32 " " PADDRX " %d %d\n", |
225 | __func__, offset, len, end, idx, eidx); | |
008ff9d7 JM |
226 | #endif |
227 | for (; idx <= eidx; idx++) { | |
228 | mmio->mem_read[idx] = mem_read; | |
229 | mmio->mem_write[idx] = mem_write; | |
230 | mmio->opaque[idx] = opaque; | |
231 | } | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | ppc4xx_mmio_t *ppc4xx_mmio_init (CPUState *env, target_phys_addr_t base) | |
237 | { | |
238 | ppc4xx_mmio_t *mmio; | |
239 | int mmio_memory; | |
240 | ||
241 | mmio = qemu_mallocz(sizeof(ppc4xx_mmio_t)); | |
242 | if (mmio != NULL) { | |
243 | mmio->base = base; | |
244 | mmio_memory = cpu_register_io_memory(0, mmio_read, mmio_write, mmio); | |
245 | #if defined(DEBUG_MMIO) | |
aae9366a JM |
246 | printf("%s: base " PADDRX " len %08x %d\n", __func__, |
247 | base, TARGET_PAGE_SIZE, mmio_memory); | |
008ff9d7 JM |
248 | #endif |
249 | cpu_register_physical_memory(base, TARGET_PAGE_SIZE, mmio_memory); | |
250 | ppc4xx_mmio_register(env, mmio, 0, TARGET_PAGE_SIZE, | |
251 | unassigned_mmio_read, unassigned_mmio_write, | |
252 | mmio); | |
253 | } | |
254 | ||
255 | return mmio; | |
256 | } | |
257 | ||
258 | /*****************************************************************************/ | |
259 | /* "Universal" Interrupt controller */ | |
260 | enum { | |
261 | DCR_UICSR = 0x000, | |
262 | DCR_UICSRS = 0x001, | |
263 | DCR_UICER = 0x002, | |
264 | DCR_UICCR = 0x003, | |
265 | DCR_UICPR = 0x004, | |
266 | DCR_UICTR = 0x005, | |
267 | DCR_UICMSR = 0x006, | |
268 | DCR_UICVR = 0x007, | |
269 | DCR_UICVCR = 0x008, | |
270 | DCR_UICMAX = 0x009, | |
271 | }; | |
272 | ||
273 | #define UIC_MAX_IRQ 32 | |
274 | typedef struct ppcuic_t ppcuic_t; | |
275 | struct ppcuic_t { | |
276 | uint32_t dcr_base; | |
277 | int use_vectors; | |
4c54e875 | 278 | uint32_t level; /* Remembers the state of level-triggered interrupts. */ |
008ff9d7 JM |
279 | uint32_t uicsr; /* Status register */ |
280 | uint32_t uicer; /* Enable register */ | |
281 | uint32_t uiccr; /* Critical register */ | |
282 | uint32_t uicpr; /* Polarity register */ | |
283 | uint32_t uictr; /* Triggering register */ | |
284 | uint32_t uicvcr; /* Vector configuration register */ | |
285 | uint32_t uicvr; | |
286 | qemu_irq *irqs; | |
287 | }; | |
288 | ||
289 | static void ppcuic_trigger_irq (ppcuic_t *uic) | |
290 | { | |
291 | uint32_t ir, cr; | |
292 | int start, end, inc, i; | |
293 | ||
294 | /* Trigger interrupt if any is pending */ | |
295 | ir = uic->uicsr & uic->uicer & (~uic->uiccr); | |
296 | cr = uic->uicsr & uic->uicer & uic->uiccr; | |
297 | #ifdef DEBUG_UIC | |
298 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
299 | fprintf(logfile, "%s: uicsr %08" PRIx32 " uicer %08" PRIx32 |
300 | " uiccr %08" PRIx32 "\n" | |
301 | " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", | |
302 | __func__, uic->uicsr, uic->uicer, uic->uiccr, | |
008ff9d7 JM |
303 | uic->uicsr & uic->uicer, ir, cr); |
304 | } | |
305 | #endif | |
306 | if (ir != 0x0000000) { | |
307 | #ifdef DEBUG_UIC | |
308 | if (loglevel & CPU_LOG_INT) { | |
309 | fprintf(logfile, "Raise UIC interrupt\n"); | |
310 | } | |
311 | #endif | |
312 | qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]); | |
313 | } else { | |
314 | #ifdef DEBUG_UIC | |
315 | if (loglevel & CPU_LOG_INT) { | |
316 | fprintf(logfile, "Lower UIC interrupt\n"); | |
317 | } | |
318 | #endif | |
319 | qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]); | |
320 | } | |
321 | /* Trigger critical interrupt if any is pending and update vector */ | |
322 | if (cr != 0x0000000) { | |
323 | qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]); | |
324 | if (uic->use_vectors) { | |
325 | /* Compute critical IRQ vector */ | |
326 | if (uic->uicvcr & 1) { | |
327 | start = 31; | |
328 | end = 0; | |
329 | inc = -1; | |
330 | } else { | |
331 | start = 0; | |
332 | end = 31; | |
333 | inc = 1; | |
334 | } | |
335 | uic->uicvr = uic->uicvcr & 0xFFFFFFFC; | |
336 | for (i = start; i <= end; i += inc) { | |
337 | if (cr & (1 << i)) { | |
338 | uic->uicvr += (i - start) * 512 * inc; | |
339 | break; | |
340 | } | |
341 | } | |
342 | } | |
343 | #ifdef DEBUG_UIC | |
344 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
345 | fprintf(logfile, "Raise UIC critical interrupt - " |
346 | "vector %08" PRIx32 "\n", uic->uicvr); | |
008ff9d7 JM |
347 | } |
348 | #endif | |
349 | } else { | |
350 | #ifdef DEBUG_UIC | |
351 | if (loglevel & CPU_LOG_INT) { | |
352 | fprintf(logfile, "Lower UIC critical interrupt\n"); | |
353 | } | |
354 | #endif | |
355 | qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]); | |
356 | uic->uicvr = 0x00000000; | |
357 | } | |
358 | } | |
359 | ||
360 | static void ppcuic_set_irq (void *opaque, int irq_num, int level) | |
361 | { | |
362 | ppcuic_t *uic; | |
363 | uint32_t mask, sr; | |
364 | ||
365 | uic = opaque; | |
923e5e33 | 366 | mask = 1 << (31-irq_num); |
008ff9d7 JM |
367 | #ifdef DEBUG_UIC |
368 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
369 | fprintf(logfile, "%s: irq %d level %d uicsr %08" PRIx32 |
370 | " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", | |
371 | __func__, irq_num, level, | |
008ff9d7 JM |
372 | uic->uicsr, mask, uic->uicsr & mask, level << irq_num); |
373 | } | |
374 | #endif | |
375 | if (irq_num < 0 || irq_num > 31) | |
376 | return; | |
377 | sr = uic->uicsr; | |
50bf72b3 | 378 | |
008ff9d7 JM |
379 | /* Update status register */ |
380 | if (uic->uictr & mask) { | |
381 | /* Edge sensitive interrupt */ | |
382 | if (level == 1) | |
383 | uic->uicsr |= mask; | |
384 | } else { | |
385 | /* Level sensitive interrupt */ | |
4c54e875 | 386 | if (level == 1) { |
008ff9d7 | 387 | uic->uicsr |= mask; |
4c54e875 AJ |
388 | uic->level |= mask; |
389 | } else { | |
008ff9d7 | 390 | uic->uicsr &= ~mask; |
4c54e875 AJ |
391 | uic->level &= ~mask; |
392 | } | |
008ff9d7 JM |
393 | } |
394 | #ifdef DEBUG_UIC | |
395 | if (loglevel & CPU_LOG_INT) { | |
aae9366a JM |
396 | fprintf(logfile, "%s: irq %d level %d sr %" PRIx32 " => " |
397 | "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); | |
008ff9d7 JM |
398 | } |
399 | #endif | |
400 | if (sr != uic->uicsr) | |
401 | ppcuic_trigger_irq(uic); | |
402 | } | |
403 | ||
404 | static target_ulong dcr_read_uic (void *opaque, int dcrn) | |
405 | { | |
406 | ppcuic_t *uic; | |
407 | target_ulong ret; | |
408 | ||
409 | uic = opaque; | |
410 | dcrn -= uic->dcr_base; | |
411 | switch (dcrn) { | |
412 | case DCR_UICSR: | |
413 | case DCR_UICSRS: | |
414 | ret = uic->uicsr; | |
415 | break; | |
416 | case DCR_UICER: | |
417 | ret = uic->uicer; | |
418 | break; | |
419 | case DCR_UICCR: | |
420 | ret = uic->uiccr; | |
421 | break; | |
422 | case DCR_UICPR: | |
423 | ret = uic->uicpr; | |
424 | break; | |
425 | case DCR_UICTR: | |
426 | ret = uic->uictr; | |
427 | break; | |
428 | case DCR_UICMSR: | |
429 | ret = uic->uicsr & uic->uicer; | |
430 | break; | |
431 | case DCR_UICVR: | |
432 | if (!uic->use_vectors) | |
433 | goto no_read; | |
434 | ret = uic->uicvr; | |
435 | break; | |
436 | case DCR_UICVCR: | |
437 | if (!uic->use_vectors) | |
438 | goto no_read; | |
439 | ret = uic->uicvcr; | |
440 | break; | |
441 | default: | |
442 | no_read: | |
443 | ret = 0x00000000; | |
444 | break; | |
445 | } | |
446 | ||
447 | return ret; | |
448 | } | |
449 | ||
450 | static void dcr_write_uic (void *opaque, int dcrn, target_ulong val) | |
451 | { | |
452 | ppcuic_t *uic; | |
453 | ||
454 | uic = opaque; | |
455 | dcrn -= uic->dcr_base; | |
456 | #ifdef DEBUG_UIC | |
457 | if (loglevel & CPU_LOG_INT) { | |
458 | fprintf(logfile, "%s: dcr %d val " ADDRX "\n", __func__, dcrn, val); | |
459 | } | |
460 | #endif | |
461 | switch (dcrn) { | |
462 | case DCR_UICSR: | |
463 | uic->uicsr &= ~val; | |
4c54e875 | 464 | uic->uicsr |= uic->level; |
008ff9d7 JM |
465 | ppcuic_trigger_irq(uic); |
466 | break; | |
467 | case DCR_UICSRS: | |
468 | uic->uicsr |= val; | |
469 | ppcuic_trigger_irq(uic); | |
470 | break; | |
471 | case DCR_UICER: | |
472 | uic->uicer = val; | |
473 | ppcuic_trigger_irq(uic); | |
474 | break; | |
475 | case DCR_UICCR: | |
476 | uic->uiccr = val; | |
477 | ppcuic_trigger_irq(uic); | |
478 | break; | |
479 | case DCR_UICPR: | |
480 | uic->uicpr = val; | |
008ff9d7 JM |
481 | break; |
482 | case DCR_UICTR: | |
483 | uic->uictr = val; | |
484 | ppcuic_trigger_irq(uic); | |
485 | break; | |
486 | case DCR_UICMSR: | |
487 | break; | |
488 | case DCR_UICVR: | |
489 | break; | |
490 | case DCR_UICVCR: | |
491 | uic->uicvcr = val & 0xFFFFFFFD; | |
492 | ppcuic_trigger_irq(uic); | |
493 | break; | |
494 | } | |
495 | } | |
496 | ||
497 | static void ppcuic_reset (void *opaque) | |
498 | { | |
499 | ppcuic_t *uic; | |
500 | ||
501 | uic = opaque; | |
502 | uic->uiccr = 0x00000000; | |
503 | uic->uicer = 0x00000000; | |
504 | uic->uicpr = 0x00000000; | |
505 | uic->uicsr = 0x00000000; | |
506 | uic->uictr = 0x00000000; | |
507 | if (uic->use_vectors) { | |
508 | uic->uicvcr = 0x00000000; | |
509 | uic->uicvr = 0x0000000; | |
510 | } | |
511 | } | |
512 | ||
513 | qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs, | |
514 | uint32_t dcr_base, int has_ssr, int has_vr) | |
515 | { | |
516 | ppcuic_t *uic; | |
517 | int i; | |
518 | ||
519 | uic = qemu_mallocz(sizeof(ppcuic_t)); | |
520 | if (uic != NULL) { | |
521 | uic->dcr_base = dcr_base; | |
522 | uic->irqs = irqs; | |
523 | if (has_vr) | |
524 | uic->use_vectors = 1; | |
525 | for (i = 0; i < DCR_UICMAX; i++) { | |
526 | ppc_dcr_register(env, dcr_base + i, uic, | |
527 | &dcr_read_uic, &dcr_write_uic); | |
528 | } | |
529 | qemu_register_reset(ppcuic_reset, uic); | |
530 | ppcuic_reset(uic); | |
531 | } | |
532 | ||
533 | return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ); | |
534 | } | |
61b24405 AJ |
535 | |
536 | /*****************************************************************************/ | |
537 | /* SDRAM controller */ | |
538 | typedef struct ppc4xx_sdram_t ppc4xx_sdram_t; | |
539 | struct ppc4xx_sdram_t { | |
540 | uint32_t addr; | |
541 | int nbanks; | |
542 | target_phys_addr_t ram_bases[4]; | |
543 | target_phys_addr_t ram_sizes[4]; | |
544 | uint32_t besr0; | |
545 | uint32_t besr1; | |
546 | uint32_t bear; | |
547 | uint32_t cfg; | |
548 | uint32_t status; | |
549 | uint32_t rtr; | |
550 | uint32_t pmit; | |
551 | uint32_t bcr[4]; | |
552 | uint32_t tr; | |
553 | uint32_t ecccfg; | |
554 | uint32_t eccesr; | |
555 | qemu_irq irq; | |
556 | }; | |
557 | ||
558 | enum { | |
559 | SDRAM0_CFGADDR = 0x010, | |
560 | SDRAM0_CFGDATA = 0x011, | |
561 | }; | |
562 | ||
563 | /* XXX: TOFIX: some patches have made this code become inconsistent: | |
564 | * there are type inconsistencies, mixing target_phys_addr_t, target_ulong | |
565 | * and uint32_t | |
566 | */ | |
567 | static uint32_t sdram_bcr (target_phys_addr_t ram_base, | |
568 | target_phys_addr_t ram_size) | |
569 | { | |
570 | uint32_t bcr; | |
571 | ||
572 | switch (ram_size) { | |
573 | case (4 * 1024 * 1024): | |
574 | bcr = 0x00000000; | |
575 | break; | |
576 | case (8 * 1024 * 1024): | |
577 | bcr = 0x00020000; | |
578 | break; | |
579 | case (16 * 1024 * 1024): | |
580 | bcr = 0x00040000; | |
581 | break; | |
582 | case (32 * 1024 * 1024): | |
583 | bcr = 0x00060000; | |
584 | break; | |
585 | case (64 * 1024 * 1024): | |
586 | bcr = 0x00080000; | |
587 | break; | |
588 | case (128 * 1024 * 1024): | |
589 | bcr = 0x000A0000; | |
590 | break; | |
591 | case (256 * 1024 * 1024): | |
592 | bcr = 0x000C0000; | |
593 | break; | |
594 | default: | |
595 | printf("%s: invalid RAM size " PADDRX "\n", __func__, ram_size); | |
596 | return 0x00000000; | |
597 | } | |
598 | bcr |= ram_base & 0xFF800000; | |
599 | bcr |= 1; | |
600 | ||
601 | return bcr; | |
602 | } | |
603 | ||
604 | static always_inline target_phys_addr_t sdram_base (uint32_t bcr) | |
605 | { | |
606 | return bcr & 0xFF800000; | |
607 | } | |
608 | ||
609 | static target_ulong sdram_size (uint32_t bcr) | |
610 | { | |
611 | target_ulong size; | |
612 | int sh; | |
613 | ||
614 | sh = (bcr >> 17) & 0x7; | |
615 | if (sh == 7) | |
616 | size = -1; | |
617 | else | |
618 | size = (4 * 1024 * 1024) << sh; | |
619 | ||
620 | return size; | |
621 | } | |
622 | ||
623 | static void sdram_set_bcr (uint32_t *bcrp, uint32_t bcr, int enabled) | |
624 | { | |
625 | if (*bcrp & 0x00000001) { | |
626 | /* Unmap RAM */ | |
627 | #ifdef DEBUG_SDRAM | |
628 | printf("%s: unmap RAM area " PADDRX " " ADDRX "\n", | |
629 | __func__, sdram_base(*bcrp), sdram_size(*bcrp)); | |
630 | #endif | |
631 | cpu_register_physical_memory(sdram_base(*bcrp), sdram_size(*bcrp), | |
632 | IO_MEM_UNASSIGNED); | |
633 | } | |
634 | *bcrp = bcr & 0xFFDEE001; | |
635 | if (enabled && (bcr & 0x00000001)) { | |
636 | #ifdef DEBUG_SDRAM | |
637 | printf("%s: Map RAM area " PADDRX " " ADDRX "\n", | |
638 | __func__, sdram_base(bcr), sdram_size(bcr)); | |
639 | #endif | |
640 | cpu_register_physical_memory(sdram_base(bcr), sdram_size(bcr), | |
641 | sdram_base(bcr) | IO_MEM_RAM); | |
642 | } | |
643 | } | |
644 | ||
645 | static void sdram_map_bcr (ppc4xx_sdram_t *sdram) | |
646 | { | |
647 | int i; | |
648 | ||
649 | for (i = 0; i < sdram->nbanks; i++) { | |
650 | if (sdram->ram_sizes[i] != 0) { | |
651 | sdram_set_bcr(&sdram->bcr[i], | |
652 | sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]), | |
653 | 1); | |
654 | } else { | |
655 | sdram_set_bcr(&sdram->bcr[i], 0x00000000, 0); | |
656 | } | |
657 | } | |
658 | } | |
659 | ||
660 | static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram) | |
661 | { | |
662 | int i; | |
663 | ||
664 | for (i = 0; i < sdram->nbanks; i++) { | |
665 | #ifdef DEBUG_SDRAM | |
666 | printf("%s: Unmap RAM area " PADDRX " " ADDRX "\n", | |
667 | __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i])); | |
668 | #endif | |
669 | cpu_register_physical_memory(sdram_base(sdram->bcr[i]), | |
670 | sdram_size(sdram->bcr[i]), | |
671 | IO_MEM_UNASSIGNED); | |
672 | } | |
673 | } | |
674 | ||
675 | static target_ulong dcr_read_sdram (void *opaque, int dcrn) | |
676 | { | |
677 | ppc4xx_sdram_t *sdram; | |
678 | target_ulong ret; | |
679 | ||
680 | sdram = opaque; | |
681 | switch (dcrn) { | |
682 | case SDRAM0_CFGADDR: | |
683 | ret = sdram->addr; | |
684 | break; | |
685 | case SDRAM0_CFGDATA: | |
686 | switch (sdram->addr) { | |
687 | case 0x00: /* SDRAM_BESR0 */ | |
688 | ret = sdram->besr0; | |
689 | break; | |
690 | case 0x08: /* SDRAM_BESR1 */ | |
691 | ret = sdram->besr1; | |
692 | break; | |
693 | case 0x10: /* SDRAM_BEAR */ | |
694 | ret = sdram->bear; | |
695 | break; | |
696 | case 0x20: /* SDRAM_CFG */ | |
697 | ret = sdram->cfg; | |
698 | break; | |
699 | case 0x24: /* SDRAM_STATUS */ | |
700 | ret = sdram->status; | |
701 | break; | |
702 | case 0x30: /* SDRAM_RTR */ | |
703 | ret = sdram->rtr; | |
704 | break; | |
705 | case 0x34: /* SDRAM_PMIT */ | |
706 | ret = sdram->pmit; | |
707 | break; | |
708 | case 0x40: /* SDRAM_B0CR */ | |
709 | ret = sdram->bcr[0]; | |
710 | break; | |
711 | case 0x44: /* SDRAM_B1CR */ | |
712 | ret = sdram->bcr[1]; | |
713 | break; | |
714 | case 0x48: /* SDRAM_B2CR */ | |
715 | ret = sdram->bcr[2]; | |
716 | break; | |
717 | case 0x4C: /* SDRAM_B3CR */ | |
718 | ret = sdram->bcr[3]; | |
719 | break; | |
720 | case 0x80: /* SDRAM_TR */ | |
721 | ret = -1; /* ? */ | |
722 | break; | |
723 | case 0x94: /* SDRAM_ECCCFG */ | |
724 | ret = sdram->ecccfg; | |
725 | break; | |
726 | case 0x98: /* SDRAM_ECCESR */ | |
727 | ret = sdram->eccesr; | |
728 | break; | |
729 | default: /* Error */ | |
730 | ret = -1; | |
731 | break; | |
732 | } | |
733 | break; | |
734 | default: | |
735 | /* Avoid gcc warning */ | |
736 | ret = 0x00000000; | |
737 | break; | |
738 | } | |
739 | ||
740 | return ret; | |
741 | } | |
742 | ||
743 | static void dcr_write_sdram (void *opaque, int dcrn, target_ulong val) | |
744 | { | |
745 | ppc4xx_sdram_t *sdram; | |
746 | ||
747 | sdram = opaque; | |
748 | switch (dcrn) { | |
749 | case SDRAM0_CFGADDR: | |
750 | sdram->addr = val; | |
751 | break; | |
752 | case SDRAM0_CFGDATA: | |
753 | switch (sdram->addr) { | |
754 | case 0x00: /* SDRAM_BESR0 */ | |
755 | sdram->besr0 &= ~val; | |
756 | break; | |
757 | case 0x08: /* SDRAM_BESR1 */ | |
758 | sdram->besr1 &= ~val; | |
759 | break; | |
760 | case 0x10: /* SDRAM_BEAR */ | |
761 | sdram->bear = val; | |
762 | break; | |
763 | case 0x20: /* SDRAM_CFG */ | |
764 | val &= 0xFFE00000; | |
765 | if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) { | |
766 | #ifdef DEBUG_SDRAM | |
767 | printf("%s: enable SDRAM controller\n", __func__); | |
768 | #endif | |
769 | /* validate all RAM mappings */ | |
770 | sdram_map_bcr(sdram); | |
771 | sdram->status &= ~0x80000000; | |
772 | } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) { | |
773 | #ifdef DEBUG_SDRAM | |
774 | printf("%s: disable SDRAM controller\n", __func__); | |
775 | #endif | |
776 | /* invalidate all RAM mappings */ | |
777 | sdram_unmap_bcr(sdram); | |
778 | sdram->status |= 0x80000000; | |
779 | } | |
780 | if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) | |
781 | sdram->status |= 0x40000000; | |
782 | else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) | |
783 | sdram->status &= ~0x40000000; | |
784 | sdram->cfg = val; | |
785 | break; | |
786 | case 0x24: /* SDRAM_STATUS */ | |
787 | /* Read-only register */ | |
788 | break; | |
789 | case 0x30: /* SDRAM_RTR */ | |
790 | sdram->rtr = val & 0x3FF80000; | |
791 | break; | |
792 | case 0x34: /* SDRAM_PMIT */ | |
793 | sdram->pmit = (val & 0xF8000000) | 0x07C00000; | |
794 | break; | |
795 | case 0x40: /* SDRAM_B0CR */ | |
796 | sdram_set_bcr(&sdram->bcr[0], val, sdram->cfg & 0x80000000); | |
797 | break; | |
798 | case 0x44: /* SDRAM_B1CR */ | |
799 | sdram_set_bcr(&sdram->bcr[1], val, sdram->cfg & 0x80000000); | |
800 | break; | |
801 | case 0x48: /* SDRAM_B2CR */ | |
802 | sdram_set_bcr(&sdram->bcr[2], val, sdram->cfg & 0x80000000); | |
803 | break; | |
804 | case 0x4C: /* SDRAM_B3CR */ | |
805 | sdram_set_bcr(&sdram->bcr[3], val, sdram->cfg & 0x80000000); | |
806 | break; | |
807 | case 0x80: /* SDRAM_TR */ | |
808 | sdram->tr = val & 0x018FC01F; | |
809 | break; | |
810 | case 0x94: /* SDRAM_ECCCFG */ | |
811 | sdram->ecccfg = val & 0x00F00000; | |
812 | break; | |
813 | case 0x98: /* SDRAM_ECCESR */ | |
814 | val &= 0xFFF0F000; | |
815 | if (sdram->eccesr == 0 && val != 0) | |
816 | qemu_irq_raise(sdram->irq); | |
817 | else if (sdram->eccesr != 0 && val == 0) | |
818 | qemu_irq_lower(sdram->irq); | |
819 | sdram->eccesr = val; | |
820 | break; | |
821 | default: /* Error */ | |
822 | break; | |
823 | } | |
824 | break; | |
825 | } | |
826 | } | |
827 | ||
828 | static void sdram_reset (void *opaque) | |
829 | { | |
830 | ppc4xx_sdram_t *sdram; | |
831 | ||
832 | sdram = opaque; | |
833 | sdram->addr = 0x00000000; | |
834 | sdram->bear = 0x00000000; | |
835 | sdram->besr0 = 0x00000000; /* No error */ | |
836 | sdram->besr1 = 0x00000000; /* No error */ | |
837 | sdram->cfg = 0x00000000; | |
838 | sdram->ecccfg = 0x00000000; /* No ECC */ | |
839 | sdram->eccesr = 0x00000000; /* No error */ | |
840 | sdram->pmit = 0x07C00000; | |
841 | sdram->rtr = 0x05F00000; | |
842 | sdram->tr = 0x00854009; | |
843 | /* We pre-initialize RAM banks */ | |
844 | sdram->status = 0x00000000; | |
845 | sdram->cfg = 0x00800000; | |
846 | sdram_unmap_bcr(sdram); | |
847 | } | |
848 | ||
80e8bd2b | 849 | void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks, |
61b24405 AJ |
850 | target_phys_addr_t *ram_bases, |
851 | target_phys_addr_t *ram_sizes, | |
852 | int do_init) | |
853 | { | |
854 | ppc4xx_sdram_t *sdram; | |
855 | ||
856 | sdram = qemu_mallocz(sizeof(ppc4xx_sdram_t)); | |
857 | if (sdram != NULL) { | |
858 | sdram->irq = irq; | |
859 | sdram->nbanks = nbanks; | |
860 | memset(sdram->ram_bases, 0, 4 * sizeof(target_phys_addr_t)); | |
861 | memcpy(sdram->ram_bases, ram_bases, | |
862 | nbanks * sizeof(target_phys_addr_t)); | |
863 | memset(sdram->ram_sizes, 0, 4 * sizeof(target_phys_addr_t)); | |
864 | memcpy(sdram->ram_sizes, ram_sizes, | |
865 | nbanks * sizeof(target_phys_addr_t)); | |
866 | sdram_reset(sdram); | |
867 | qemu_register_reset(&sdram_reset, sdram); | |
868 | ppc_dcr_register(env, SDRAM0_CFGADDR, | |
869 | sdram, &dcr_read_sdram, &dcr_write_sdram); | |
870 | ppc_dcr_register(env, SDRAM0_CFGDATA, | |
871 | sdram, &dcr_read_sdram, &dcr_write_sdram); | |
872 | if (do_init) | |
873 | sdram_map_bcr(sdram); | |
874 | } | |
875 | } | |
b7da58fd AJ |
876 | |
877 | /* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory. | |
878 | * | |
879 | * sdram_bank_sizes[] must be 0-terminated. | |
880 | * | |
881 | * The 4xx SDRAM controller supports a small number of banks, and each bank | |
882 | * must be one of a small set of sizes. The number of banks and the supported | |
883 | * sizes varies by SoC. */ | |
884 | ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, | |
885 | target_phys_addr_t ram_bases[], | |
886 | target_phys_addr_t ram_sizes[], | |
887 | const unsigned int sdram_bank_sizes[]) | |
888 | { | |
889 | ram_addr_t ram_end = 0; | |
890 | int i; | |
891 | int j; | |
892 | ||
893 | for (i = 0; i < nr_banks; i++) { | |
894 | for (j = 0; sdram_bank_sizes[j] != 0; j++) { | |
895 | unsigned int bank_size = sdram_bank_sizes[j]; | |
896 | ||
897 | if (bank_size <= ram_size) { | |
898 | ram_bases[i] = ram_end; | |
899 | ram_sizes[i] = bank_size; | |
900 | ram_end += bank_size; | |
901 | ram_size -= bank_size; | |
902 | break; | |
903 | } | |
904 | } | |
905 | ||
906 | if (!ram_size) { | |
907 | /* No need to use the remaining banks. */ | |
908 | break; | |
909 | } | |
910 | } | |
911 | ||
912 | if (ram_size) | |
913 | printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n", | |
914 | (int)(ram_end >> 20)); | |
915 | ||
916 | return ram_end; | |
917 | } |