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