Commit 9ee6e8bb853bdea7ef6c645a1a07aa55fd206aba
1 parent
ee4e83ed
ARMv7 support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3572 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
26 changed files
with
3847 additions
and
235 deletions
Too many changes to show.
To preserve performance only 26 of 35 files are displayed.
Changelog
... | ... | @@ -17,6 +17,7 @@ |
17 | 17 | - MIPS mipssim pequdo machine (Thiemo Seufer) |
18 | 18 | - Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh) |
19 | 19 | - OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski) |
20 | + - ARM v6, v7, NEON SIMD and SMP emulation (Paul Brook/CodeSourcery) | |
20 | 21 | |
21 | 22 | version 0.9.0: |
22 | 23 | ... | ... |
Makefile.target
... | ... | @@ -493,7 +493,9 @@ ifeq ($(TARGET_BASE_ARCH), arm) |
493 | 493 | VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o |
494 | 494 | VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o |
495 | 495 | VL_OBJS+= versatile_pci.o sd.o ptimer.o |
496 | -VL_OBJS+= arm_gic.o realview.o arm_sysctl.o | |
496 | +VL_OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o | |
497 | +VL_OBJS+= armv7m.o armv7m_nvic.o stellaris.o i2c.o ssd0303.o pl022.o | |
498 | +VL_OBJS+= ssd0323.o pl061.o | |
497 | 499 | VL_OBJS+= arm-semi.o |
498 | 500 | VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o |
499 | 501 | VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o | ... | ... |
cpu-exec.c
... | ... | @@ -173,6 +173,7 @@ static inline TranslationBlock *tb_find_fast(void) |
173 | 173 | flags |= (1 << 6); |
174 | 174 | if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) |
175 | 175 | flags |= (1 << 7); |
176 | + flags |= (env->condexec_bits << 8); | |
176 | 177 | cs_base = 0; |
177 | 178 | pc = env->regs[15]; |
178 | 179 | #elif defined(TARGET_SPARC) |
... | ... | @@ -511,8 +512,18 @@ int cpu_exec(CPUState *env1) |
511 | 512 | env->exception_index = EXCP_FIQ; |
512 | 513 | do_interrupt(env); |
513 | 514 | } |
515 | + /* ARMv7-M interrupt return works by loading a magic value | |
516 | + into the PC. On real hardware the load causes the | |
517 | + return to occur. The qemu implementation performs the | |
518 | + jump normally, then does the exception return when the | |
519 | + CPU tries to execute code at the magic address. | |
520 | + This will cause the magic PC value to be pushed to | |
521 | + the stack if an interrupt occured at the wrong time. | |
522 | + We avoid this by disabling interrupts when | |
523 | + pc contains a magic address. */ | |
514 | 524 | if (interrupt_request & CPU_INTERRUPT_HARD |
515 | - && !(env->uncached_cpsr & CPSR_I)) { | |
525 | + && ((IS_M(env) && env->regs[15] < 0xfffffff0) | |
526 | + || !(env->uncached_cpsr & CPSR_I))) { | |
516 | 527 | env->exception_index = EXCP_IRQ; |
517 | 528 | do_interrupt(env); |
518 | 529 | } | ... | ... |
fpu/softfloat-native.h
... | ... | @@ -224,6 +224,11 @@ INLINE float32 float32_chs(float32 a) |
224 | 224 | return -a; |
225 | 225 | } |
226 | 226 | |
227 | +INLINE float32 float32_scalbn(float32 a, int n) | |
228 | +{ | |
229 | + return scalbnf(a, n); | |
230 | +} | |
231 | + | |
227 | 232 | /*---------------------------------------------------------------------------- |
228 | 233 | | Software IEC/IEEE double-precision conversion routines. |
229 | 234 | *----------------------------------------------------------------------------*/ |
... | ... | @@ -311,6 +316,11 @@ INLINE float64 float64_chs(float64 a) |
311 | 316 | return -a; |
312 | 317 | } |
313 | 318 | |
319 | +INLINE float64 float64_scalbn(float64 a, int n) | |
320 | +{ | |
321 | + return scalbn(a, n); | |
322 | +} | |
323 | + | |
314 | 324 | #ifdef FLOATX80 |
315 | 325 | |
316 | 326 | /*---------------------------------------------------------------------------- |
... | ... | @@ -391,4 +401,10 @@ INLINE floatx80 floatx80_chs(floatx80 a) |
391 | 401 | { |
392 | 402 | return -a; |
393 | 403 | } |
404 | + | |
405 | +INLINE floatx80 floatx80_scalbn(floatx80 a, int n) | |
406 | +{ | |
407 | + return scalbnl(a, n); | |
408 | +} | |
409 | + | |
394 | 410 | #endif | ... | ... |
fpu/softfloat.c
... | ... | @@ -5377,3 +5377,78 @@ int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \ |
5377 | 5377 | |
5378 | 5378 | COMPARE(32, 0xff) |
5379 | 5379 | COMPARE(64, 0x7ff) |
5380 | + | |
5381 | +/* Multiply A by 2 raised to the power N. */ | |
5382 | +float32 float32_scalbn( float32 a, int n STATUS_PARAM ) | |
5383 | +{ | |
5384 | + flag aSign; | |
5385 | + int16 aExp; | |
5386 | + bits32 aSig; | |
5387 | + | |
5388 | + aSig = extractFloat32Frac( a ); | |
5389 | + aExp = extractFloat32Exp( a ); | |
5390 | + aSign = extractFloat32Sign( a ); | |
5391 | + | |
5392 | + if ( aExp == 0xFF ) { | |
5393 | + return a; | |
5394 | + } | |
5395 | + aExp += n; | |
5396 | + return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR ); | |
5397 | +} | |
5398 | + | |
5399 | +float64 float64_scalbn( float64 a, int n STATUS_PARAM ) | |
5400 | +{ | |
5401 | + flag aSign; | |
5402 | + int16 aExp; | |
5403 | + bits64 aSig; | |
5404 | + | |
5405 | + aSig = extractFloat64Frac( a ); | |
5406 | + aExp = extractFloat64Exp( a ); | |
5407 | + aSign = extractFloat64Sign( a ); | |
5408 | + | |
5409 | + if ( aExp == 0x7FF ) { | |
5410 | + return a; | |
5411 | + } | |
5412 | + aExp += n; | |
5413 | + return roundAndPackFloat64( aSign, aExp, aSig STATUS_VAR ); | |
5414 | +} | |
5415 | + | |
5416 | +#ifdef FLOATX80 | |
5417 | +floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM ) | |
5418 | +{ | |
5419 | + flag aSign; | |
5420 | + int16 aExp; | |
5421 | + bits64 aSig; | |
5422 | + | |
5423 | + aSig = extractFloatx80Frac( a ); | |
5424 | + aExp = extractFloatx80Exp( a ); | |
5425 | + aSign = extractFloatx80Sign( a ); | |
5426 | + | |
5427 | + if ( aExp == 0x7FF ) { | |
5428 | + return a; | |
5429 | + } | |
5430 | + aExp += n; | |
5431 | + return roundAndPackFloatx80( STATUS(floatx80_rounding_precision), | |
5432 | + aSign, aExp, aSig, 0 STATUS_VAR ); | |
5433 | +} | |
5434 | +#endif | |
5435 | + | |
5436 | +#ifdef FLOAT128 | |
5437 | +float128 float128_scalbn( float128 a, int n STATUS_PARAM ) | |
5438 | +{ | |
5439 | + flag aSign; | |
5440 | + int32 aExp; | |
5441 | + bits64 aSig0, aSig1; | |
5442 | + | |
5443 | + aSig1 = extractFloat128Frac1( a ); | |
5444 | + aSig0 = extractFloat128Frac0( a ); | |
5445 | + aExp = extractFloat128Exp( a ); | |
5446 | + aSign = extractFloat128Sign( a ); | |
5447 | + if ( aExp == 0x7FFF ) { | |
5448 | + return a; | |
5449 | + } | |
5450 | + aExp += n; | |
5451 | + return roundAndPackFloat128( aSign, aExp, aSig0, aSig1, 0 STATUS_VAR ); | |
5452 | + | |
5453 | +} | |
5454 | +#endif | ... | ... |
fpu/softfloat.h
... | ... | @@ -244,6 +244,7 @@ int float32_compare( float32, float32 STATUS_PARAM ); |
244 | 244 | int float32_compare_quiet( float32, float32 STATUS_PARAM ); |
245 | 245 | int float32_is_nan( float32 ); |
246 | 246 | int float32_is_signaling_nan( float32 ); |
247 | +float32 float32_scalbn( float32, int STATUS_PARAM ); | |
247 | 248 | |
248 | 249 | INLINE float32 float32_abs(float32 a) |
249 | 250 | { |
... | ... | @@ -295,6 +296,7 @@ int float64_compare( float64, float64 STATUS_PARAM ); |
295 | 296 | int float64_compare_quiet( float64, float64 STATUS_PARAM ); |
296 | 297 | int float64_is_nan( float64 a ); |
297 | 298 | int float64_is_signaling_nan( float64 ); |
299 | +float64 float64_scalbn( float64, int STATUS_PARAM ); | |
298 | 300 | |
299 | 301 | INLINE float64 float64_abs(float64 a) |
300 | 302 | { |
... | ... | @@ -339,6 +341,7 @@ int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); |
339 | 341 | int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); |
340 | 342 | int floatx80_is_nan( floatx80 ); |
341 | 343 | int floatx80_is_signaling_nan( floatx80 ); |
344 | +floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM ); | |
342 | 345 | |
343 | 346 | INLINE floatx80 floatx80_abs(floatx80 a) |
344 | 347 | { |
... | ... | @@ -387,6 +390,7 @@ int float128_le_quiet( float128, float128 STATUS_PARAM ); |
387 | 390 | int float128_lt_quiet( float128, float128 STATUS_PARAM ); |
388 | 391 | int float128_is_nan( float128 ); |
389 | 392 | int float128_is_signaling_nan( float128 ); |
393 | +float128 float128_scalbn( float128, int STATUS_PARAM ); | |
390 | 394 | |
391 | 395 | INLINE float128 float128_abs(float128 a) |
392 | 396 | { | ... | ... |
hw/arm_boot.c
1 | 1 | /* |
2 | 2 | * ARM kernel loader. |
3 | 3 | * |
4 | - * Copyright (c) 2006 CodeSourcery. | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | 5 | * Written by Paul Brook |
6 | 6 | * |
7 | 7 | * This code is licenced under the GPL. |
... | ... | @@ -24,6 +24,22 @@ static uint32_t bootloader[] = { |
24 | 24 | 0 /* Kernel entry point. Set by integratorcp_init. */ |
25 | 25 | }; |
26 | 26 | |
27 | +/* Entry point for secondary CPUs. Enable interrupt controller and | |
28 | + Issue WFI until start address is written to system controller. */ | |
29 | +static uint32_t smpboot[] = { | |
30 | + 0xe3a00201, /* mov r0, #0x10000000 */ | |
31 | + 0xe3800601, /* orr r0, r0, #0x001000000 */ | |
32 | + 0xe3a01001, /* mov r1, #1 */ | |
33 | + 0xe5801100, /* str r1, [r0, #0x100] */ | |
34 | + 0xe3a00201, /* mov r0, #0x10000000 */ | |
35 | + 0xe3800030, /* orr r0, #0x30 */ | |
36 | + 0xe320f003, /* wfi */ | |
37 | + 0xe5901000, /* ldr r1, [r0] */ | |
38 | + 0xe3110003, /* tst r1, #3 */ | |
39 | + 0x1afffffb, /* bne <wfi> */ | |
40 | + 0xe12fff11 /* bx r1 */ | |
41 | +}; | |
42 | + | |
27 | 43 | static void main_cpu_reset(void *opaque) |
28 | 44 | { |
29 | 45 | CPUState *env = opaque; |
... | ... | @@ -33,6 +49,8 @@ static void main_cpu_reset(void *opaque) |
33 | 49 | arm_load_kernel(env, env->ram_size, env->kernel_filename, |
34 | 50 | env->kernel_cmdline, env->initrd_filename, |
35 | 51 | env->board_id, env->loader_start); |
52 | + | |
53 | + /* TODO: Reset secondary CPUs. */ | |
36 | 54 | } |
37 | 55 | |
38 | 56 | static void set_kernel_args(uint32_t ram_size, int initrd_size, |
... | ... | @@ -211,6 +229,8 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename, |
211 | 229 | bootloader[6] = entry; |
212 | 230 | for (n = 0; n < sizeof(bootloader) / 4; n++) |
213 | 231 | stl_raw(phys_ram_base + (n * 4), bootloader[n]); |
232 | + for (n = 0; n < sizeof(smpboot) / 4; n++) | |
233 | + stl_raw(phys_ram_base + ram_size + (n * 4), smpboot[n]); | |
214 | 234 | if (old_param) |
215 | 235 | set_kernel_args_old(ram_size, initrd_size, |
216 | 236 | kernel_cmdline, loader_start); | ... | ... |
hw/arm_gic.c
1 | 1 | /* |
2 | - * ARM AMBA Generic/Distributed Interrupt Controller | |
2 | + * ARM Generic/Distributed Interrupt Controller | |
3 | 3 | * |
4 | - * Copyright (c) 2006 CodeSourcery. | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | 5 | * Written by Paul Brook |
6 | 6 | * |
7 | 7 | * This code is licenced under the GPL. |
8 | 8 | */ |
9 | 9 | |
10 | -/* TODO: Some variants of this controller can handle multiple CPUs. | |
11 | - Currently only single CPU operation is implemented. */ | |
12 | - | |
13 | -#include "vl.h" | |
14 | -#include "arm_pic.h" | |
10 | +/* This file contains implementation code for the RealView EB interrupt | |
11 | + controller, MPCore distributed interrupt controller and ARMv7-M | |
12 | + Nested Vectored Interrupt Controller. */ | |
15 | 13 | |
16 | 14 | //#define DEBUG_GIC |
17 | 15 | |
... | ... | @@ -22,58 +20,84 @@ do { printf("arm_gic: " fmt , ##args); } while (0) |
22 | 20 | #define DPRINTF(fmt, args...) do {} while(0) |
23 | 21 | #endif |
24 | 22 | |
25 | -/* Distributed interrupt controller. */ | |
26 | - | |
23 | +#ifdef NVIC | |
24 | +static const uint8_t gic_id[] = | |
25 | +{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 }; | |
26 | +#define GIC_DIST_OFFSET 0 | |
27 | +/* The NVIC has 16 internal vectors. However these are not exposed | |
28 | + through the normal GIC interface. */ | |
29 | +#define GIC_BASE_IRQ 32 | |
30 | +#else | |
27 | 31 | static const uint8_t gic_id[] = |
28 | 32 | { 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; |
29 | - | |
30 | -#define GIC_NIRQ 96 | |
33 | +#define GIC_DIST_OFFSET 0x1000 | |
34 | +#define GIC_BASE_IRQ 0 | |
35 | +#endif | |
31 | 36 | |
32 | 37 | typedef struct gic_irq_state |
33 | 38 | { |
39 | + /* ??? The documentation seems to imply the enable bits are global, even | |
40 | + for per-cpu interrupts. This seems strange. */ | |
34 | 41 | unsigned enabled:1; |
35 | - unsigned pending:1; | |
36 | - unsigned active:1; | |
42 | + unsigned pending:NCPU; | |
43 | + unsigned active:NCPU; | |
37 | 44 | unsigned level:1; |
38 | - unsigned model:1; /* 0 = 1:N, 1 = N:N */ | |
45 | + unsigned model:1; /* 0 = N:N, 1 = 1:N */ | |
39 | 46 | unsigned trigger:1; /* nonzero = edge triggered. */ |
40 | 47 | } gic_irq_state; |
41 | 48 | |
49 | +#define ALL_CPU_MASK ((1 << NCPU) - 1) | |
50 | + | |
42 | 51 | #define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1 |
43 | 52 | #define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0 |
44 | 53 | #define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled |
45 | -#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1 | |
46 | -#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0 | |
47 | -#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending | |
48 | -#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1 | |
49 | -#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0 | |
50 | -#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active | |
54 | +#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) | |
55 | +#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) | |
56 | +#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) | |
57 | +#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) | |
58 | +#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) | |
59 | +#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) | |
51 | 60 | #define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 |
52 | 61 | #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 |
53 | 62 | #define GIC_TEST_MODEL(irq) s->irq_state[irq].model |
54 | -#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1 | |
55 | -#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0 | |
56 | -#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level | |
63 | +#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) | |
64 | +#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) | |
65 | +#define GIC_TEST_LEVEL(irq, cm) (s->irq_state[irq].level & (cm)) != 0 | |
57 | 66 | #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 |
58 | 67 | #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 |
59 | 68 | #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger |
69 | +#define GIC_GET_PRIORITY(irq, cpu) \ | |
70 | + (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32]) | |
71 | +#ifdef NVIC | |
72 | +#define GIC_TARGET(irq) 1 | |
73 | +#else | |
74 | +#define GIC_TARGET(irq) s->irq_target[irq] | |
75 | +#endif | |
60 | 76 | |
61 | 77 | typedef struct gic_state |
62 | 78 | { |
63 | 79 | uint32_t base; |
64 | - qemu_irq parent_irq; | |
80 | + qemu_irq parent_irq[NCPU]; | |
65 | 81 | int enabled; |
66 | - int cpu_enabled; | |
82 | + int cpu_enabled[NCPU]; | |
67 | 83 | |
68 | 84 | gic_irq_state irq_state[GIC_NIRQ]; |
85 | +#ifndef NVIC | |
69 | 86 | int irq_target[GIC_NIRQ]; |
70 | - int priority[GIC_NIRQ]; | |
71 | - int last_active[GIC_NIRQ]; | |
72 | - | |
73 | - int priority_mask; | |
74 | - int running_irq; | |
75 | - int running_priority; | |
76 | - int current_pending; | |
87 | +#endif | |
88 | + int priority1[32][NCPU]; | |
89 | + int priority2[GIC_NIRQ - 32]; | |
90 | + int last_active[GIC_NIRQ][NCPU]; | |
91 | + | |
92 | + int priority_mask[NCPU]; | |
93 | + int running_irq[NCPU]; | |
94 | + int running_priority[NCPU]; | |
95 | + int current_pending[NCPU]; | |
96 | + | |
97 | + qemu_irq *in; | |
98 | +#ifdef NVIC | |
99 | + void *nvic; | |
100 | +#endif | |
77 | 101 | } gic_state; |
78 | 102 | |
79 | 103 | /* TODO: Many places that call this routine could be optimized. */ |
... | ... | @@ -83,112 +107,136 @@ static void gic_update(gic_state *s) |
83 | 107 | int best_irq; |
84 | 108 | int best_prio; |
85 | 109 | int irq; |
86 | - | |
87 | - s->current_pending = 1023; | |
88 | - if (!s->enabled || !s->cpu_enabled) { | |
89 | - qemu_irq_lower(s->parent_irq); | |
90 | - return; | |
91 | - } | |
92 | - best_prio = 0x100; | |
93 | - best_irq = 1023; | |
94 | - for (irq = 0; irq < 96; irq++) { | |
95 | - if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) { | |
96 | - if (s->priority[irq] < best_prio) { | |
97 | - best_prio = s->priority[irq]; | |
98 | - best_irq = irq; | |
110 | + int level; | |
111 | + int cpu; | |
112 | + int cm; | |
113 | + | |
114 | + for (cpu = 0; cpu < NCPU; cpu++) { | |
115 | + cm = 1 << cpu; | |
116 | + s->current_pending[cpu] = 1023; | |
117 | + if (!s->enabled || !s->cpu_enabled[cpu]) { | |
118 | + qemu_irq_lower(s->parent_irq[cpu]); | |
119 | + return; | |
120 | + } | |
121 | + best_prio = 0x100; | |
122 | + best_irq = 1023; | |
123 | + for (irq = 0; irq < GIC_NIRQ; irq++) { | |
124 | + if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) { | |
125 | + if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { | |
126 | + best_prio = GIC_GET_PRIORITY(irq, cpu); | |
127 | + best_irq = irq; | |
128 | + } | |
99 | 129 | } |
100 | 130 | } |
101 | - } | |
102 | - if (best_prio > s->priority_mask) { | |
103 | - qemu_irq_lower(s->parent_irq); | |
104 | - } else { | |
105 | - s->current_pending = best_irq; | |
106 | - if (best_prio < s->running_priority) { | |
107 | - DPRINTF("Raised pending IRQ %d\n", best_irq); | |
108 | - qemu_irq_raise(s->parent_irq); | |
131 | + level = 0; | |
132 | + if (best_prio <= s->priority_mask[cpu]) { | |
133 | + s->current_pending[cpu] = best_irq; | |
134 | + if (best_prio < s->running_priority[cpu]) { | |
135 | + DPRINTF("Raised pending IRQ %d\n", best_irq); | |
136 | + level = 1; | |
137 | + } | |
109 | 138 | } |
139 | + qemu_set_irq(s->parent_irq[cpu], level); | |
110 | 140 | } |
111 | 141 | } |
112 | 142 | |
143 | +static void __attribute__((unused)) | |
144 | +gic_set_pending_private(gic_state *s, int cpu, int irq) | |
145 | +{ | |
146 | + int cm = 1 << cpu; | |
147 | + | |
148 | + if (GIC_TEST_PENDING(irq, cm)) | |
149 | + return; | |
150 | + | |
151 | + DPRINTF("Set %d pending cpu %d\n", irq, cpu); | |
152 | + GIC_SET_PENDING(irq, cm); | |
153 | + gic_update(s); | |
154 | +} | |
155 | + | |
156 | +/* Process a change in an external IRQ input. */ | |
113 | 157 | static void gic_set_irq(void *opaque, int irq, int level) |
114 | 158 | { |
115 | 159 | gic_state *s = (gic_state *)opaque; |
116 | 160 | /* The first external input line is internal interrupt 32. */ |
117 | 161 | irq += 32; |
118 | - if (level == GIC_TEST_LEVEL(irq)) | |
162 | + if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK)) | |
119 | 163 | return; |
120 | 164 | |
121 | 165 | if (level) { |
122 | - GIC_SET_LEVEL(irq); | |
166 | + GIC_SET_LEVEL(irq, ALL_CPU_MASK); | |
123 | 167 | if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) { |
124 | - DPRINTF("Set %d pending\n", irq); | |
125 | - GIC_SET_PENDING(irq); | |
168 | + DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq)); | |
169 | + GIC_SET_PENDING(irq, GIC_TARGET(irq)); | |
126 | 170 | } |
127 | 171 | } else { |
128 | - GIC_CLEAR_LEVEL(irq); | |
172 | + GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK); | |
129 | 173 | } |
130 | 174 | gic_update(s); |
131 | 175 | } |
132 | 176 | |
133 | -static void gic_set_running_irq(gic_state *s, int irq) | |
177 | +static void gic_set_running_irq(gic_state *s, int cpu, int irq) | |
134 | 178 | { |
135 | - s->running_irq = irq; | |
136 | - if (irq == 1023) | |
137 | - s->running_priority = 0x100; | |
138 | - else | |
139 | - s->running_priority = s->priority[irq]; | |
179 | + s->running_irq[cpu] = irq; | |
180 | + if (irq == 1023) { | |
181 | + s->running_priority[cpu] = 0x100; | |
182 | + } else { | |
183 | + s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu); | |
184 | + } | |
140 | 185 | gic_update(s); |
141 | 186 | } |
142 | 187 | |
143 | -static uint32_t gic_acknowledge_irq(gic_state *s) | |
188 | +static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) | |
144 | 189 | { |
145 | 190 | int new_irq; |
146 | - new_irq = s->current_pending; | |
147 | - if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) { | |
191 | + int cm = 1 << cpu; | |
192 | + new_irq = s->current_pending[cpu]; | |
193 | + if (new_irq == 1023 | |
194 | + || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) { | |
148 | 195 | DPRINTF("ACK no pending IRQ\n"); |
149 | 196 | return 1023; |
150 | 197 | } |
151 | - qemu_irq_lower(s->parent_irq); | |
152 | - s->last_active[new_irq] = s->running_irq; | |
153 | - /* For level triggered interrupts we clear the pending bit while | |
154 | - the interrupt is active. */ | |
155 | - GIC_CLEAR_PENDING(new_irq); | |
156 | - gic_set_running_irq(s, new_irq); | |
198 | + s->last_active[new_irq][cpu] = s->running_irq[cpu]; | |
199 | + /* Clear pending flags for both level and edge triggered interrupts. | |
200 | + Level triggered IRQs will be reasserted once they become inactive. */ | |
201 | + GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm); | |
202 | + gic_set_running_irq(s, cpu, new_irq); | |
157 | 203 | DPRINTF("ACK %d\n", new_irq); |
158 | 204 | return new_irq; |
159 | 205 | } |
160 | 206 | |
161 | -static void gic_complete_irq(gic_state * s, int irq) | |
207 | +static void gic_complete_irq(gic_state * s, int cpu, int irq) | |
162 | 208 | { |
163 | 209 | int update = 0; |
210 | + int cm = 1 << cpu; | |
164 | 211 | DPRINTF("EOI %d\n", irq); |
165 | - if (s->running_irq == 1023) | |
212 | + if (s->running_irq[cpu] == 1023) | |
166 | 213 | return; /* No active IRQ. */ |
167 | 214 | if (irq != 1023) { |
168 | 215 | /* Mark level triggered interrupts as pending if they are still |
169 | 216 | raised. */ |
170 | 217 | if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq) |
171 | - && GIC_TEST_LEVEL(irq)) { | |
172 | - GIC_SET_PENDING(irq); | |
218 | + && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { | |
219 | + DPRINTF("Set %d pending mask %x\n", irq, cm); | |
220 | + GIC_SET_PENDING(irq, cm); | |
173 | 221 | update = 1; |
174 | 222 | } |
175 | 223 | } |
176 | - if (irq != s->running_irq) { | |
224 | + if (irq != s->running_irq[cpu]) { | |
177 | 225 | /* Complete an IRQ that is not currently running. */ |
178 | - int tmp = s->running_irq; | |
179 | - while (s->last_active[tmp] != 1023) { | |
180 | - if (s->last_active[tmp] == irq) { | |
181 | - s->last_active[tmp] = s->last_active[irq]; | |
226 | + int tmp = s->running_irq[cpu]; | |
227 | + while (s->last_active[tmp][cpu] != 1023) { | |
228 | + if (s->last_active[tmp][cpu] == irq) { | |
229 | + s->last_active[tmp][cpu] = s->last_active[irq][cpu]; | |
182 | 230 | break; |
183 | 231 | } |
184 | - tmp = s->last_active[tmp]; | |
232 | + tmp = s->last_active[tmp][cpu]; | |
185 | 233 | } |
186 | 234 | if (update) { |
187 | 235 | gic_update(s); |
188 | 236 | } |
189 | 237 | } else { |
190 | 238 | /* Complete the current running IRQ. */ |
191 | - gic_set_running_irq(s, s->last_active[s->running_irq]); | |
239 | + gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]); | |
192 | 240 | } |
193 | 241 | } |
194 | 242 | |
... | ... | @@ -198,15 +246,22 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) |
198 | 246 | uint32_t res; |
199 | 247 | int irq; |
200 | 248 | int i; |
249 | + int cpu; | |
250 | + int cm; | |
251 | + int mask; | |
201 | 252 | |
202 | - offset -= s->base + 0x1000; | |
253 | + cpu = gic_get_current_cpu(); | |
254 | + cm = 1 << cpu; | |
255 | + offset -= s->base + GIC_DIST_OFFSET; | |
203 | 256 | if (offset < 0x100) { |
257 | +#ifndef NVIC | |
204 | 258 | if (offset == 0) |
205 | 259 | return s->enabled; |
206 | 260 | if (offset == 4) |
207 | - return (GIC_NIRQ / 32) - 1; | |
261 | + return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5); | |
208 | 262 | if (offset < 0x08) |
209 | 263 | return 0; |
264 | +#endif | |
210 | 265 | goto bad_reg; |
211 | 266 | } else if (offset < 0x200) { |
212 | 267 | /* Interrupt Set/Clear Enable. */ |
... | ... | @@ -214,6 +269,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) |
214 | 269 | irq = (offset - 0x100) * 8; |
215 | 270 | else |
216 | 271 | irq = (offset - 0x180) * 8; |
272 | + irq += GIC_BASE_IRQ; | |
217 | 273 | if (irq >= GIC_NIRQ) |
218 | 274 | goto bad_reg; |
219 | 275 | res = 0; |
... | ... | @@ -228,40 +284,48 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) |
228 | 284 | irq = (offset - 0x200) * 8; |
229 | 285 | else |
230 | 286 | irq = (offset - 0x280) * 8; |
287 | + irq += GIC_BASE_IRQ; | |
231 | 288 | if (irq >= GIC_NIRQ) |
232 | 289 | goto bad_reg; |
233 | 290 | res = 0; |
291 | + mask = (irq < 32) ? cm : ALL_CPU_MASK; | |
234 | 292 | for (i = 0; i < 8; i++) { |
235 | - if (GIC_TEST_PENDING(irq + i)) { | |
293 | + if (GIC_TEST_PENDING(irq + i, mask)) { | |
236 | 294 | res |= (1 << i); |
237 | 295 | } |
238 | 296 | } |
239 | 297 | } else if (offset < 0x400) { |
240 | 298 | /* Interrupt Active. */ |
241 | - irq = (offset - 0x300) * 8; | |
299 | + irq = (offset - 0x300) * 8 + GIC_BASE_IRQ; | |
242 | 300 | if (irq >= GIC_NIRQ) |
243 | 301 | goto bad_reg; |
244 | 302 | res = 0; |
303 | + mask = (irq < 32) ? cm : ALL_CPU_MASK; | |
245 | 304 | for (i = 0; i < 8; i++) { |
246 | - if (GIC_TEST_ACTIVE(irq + i)) { | |
305 | + if (GIC_TEST_ACTIVE(irq + i, mask)) { | |
247 | 306 | res |= (1 << i); |
248 | 307 | } |
249 | 308 | } |
250 | 309 | } else if (offset < 0x800) { |
251 | 310 | /* Interrupt Priority. */ |
252 | - irq = offset - 0x400; | |
311 | + irq = (offset - 0x400) + GIC_BASE_IRQ; | |
253 | 312 | if (irq >= GIC_NIRQ) |
254 | 313 | goto bad_reg; |
255 | - res = s->priority[irq]; | |
314 | + res = GIC_GET_PRIORITY(irq, cpu); | |
315 | +#ifndef NVIC | |
256 | 316 | } else if (offset < 0xc00) { |
257 | 317 | /* Interrupt CPU Target. */ |
258 | - irq = offset - 0x800; | |
318 | + irq = (offset - 0x800) + GIC_BASE_IRQ; | |
259 | 319 | if (irq >= GIC_NIRQ) |
260 | 320 | goto bad_reg; |
261 | - res = s->irq_target[irq]; | |
321 | + if (irq >= 29 && irq <= 31) { | |
322 | + res = cm; | |
323 | + } else { | |
324 | + res = GIC_TARGET(irq); | |
325 | + } | |
262 | 326 | } else if (offset < 0xf00) { |
263 | 327 | /* Interrupt Configuration. */ |
264 | - irq = (offset - 0xc00) * 2; | |
328 | + irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ; | |
265 | 329 | if (irq >= GIC_NIRQ) |
266 | 330 | goto bad_reg; |
267 | 331 | res = 0; |
... | ... | @@ -271,6 +335,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) |
271 | 335 | if (GIC_TEST_TRIGGER(irq + i)) |
272 | 336 | res |= (2 << (i * 2)); |
273 | 337 | } |
338 | +#endif | |
274 | 339 | } else if (offset < 0xfe0) { |
275 | 340 | goto bad_reg; |
276 | 341 | } else /* offset >= 0xfe0 */ { |
... | ... | @@ -282,7 +347,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) |
282 | 347 | } |
283 | 348 | return res; |
284 | 349 | bad_reg: |
285 | - cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset); | |
350 | + cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset); | |
286 | 351 | return 0; |
287 | 352 | } |
288 | 353 | |
... | ... | @@ -297,6 +362,13 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) |
297 | 362 | static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) |
298 | 363 | { |
299 | 364 | uint32_t val; |
365 | +#ifdef NVIC | |
366 | + gic_state *s = (gic_state *)opaque; | |
367 | + uint32_t addr; | |
368 | + addr = offset - s->base; | |
369 | + if (addr < 0x100 || addr > 0xd00) | |
370 | + return nvic_readl(s->nvic, addr); | |
371 | +#endif | |
300 | 372 | val = gic_dist_readw(opaque, offset); |
301 | 373 | val |= gic_dist_readw(opaque, offset + 2) << 16; |
302 | 374 | return val; |
... | ... | @@ -308,9 +380,14 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, |
308 | 380 | gic_state *s = (gic_state *)opaque; |
309 | 381 | int irq; |
310 | 382 | int i; |
383 | + int cpu; | |
311 | 384 | |
312 | - offset -= s->base + 0x1000; | |
385 | + cpu = gic_get_current_cpu(); | |
386 | + offset -= s->base + GIC_DIST_OFFSET; | |
313 | 387 | if (offset < 0x100) { |
388 | +#ifdef NVIC | |
389 | + goto bad_reg; | |
390 | +#else | |
314 | 391 | if (offset == 0) { |
315 | 392 | s->enabled = (value & 1); |
316 | 393 | DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); |
... | ... | @@ -319,27 +396,36 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, |
319 | 396 | } else { |
320 | 397 | goto bad_reg; |
321 | 398 | } |
399 | +#endif | |
322 | 400 | } else if (offset < 0x180) { |
323 | 401 | /* Interrupt Set Enable. */ |
324 | - irq = (offset - 0x100) * 8; | |
402 | + irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; | |
325 | 403 | if (irq >= GIC_NIRQ) |
326 | 404 | goto bad_reg; |
405 | + if (irq < 16) | |
406 | + value = 0xff; | |
327 | 407 | for (i = 0; i < 8; i++) { |
328 | 408 | if (value & (1 << i)) { |
409 | + int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq); | |
329 | 410 | if (!GIC_TEST_ENABLED(irq + i)) |
330 | 411 | DPRINTF("Enabled IRQ %d\n", irq + i); |
331 | 412 | GIC_SET_ENABLED(irq + i); |
332 | 413 | /* If a raised level triggered IRQ enabled then mark |
333 | 414 | is as pending. */ |
334 | - if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i)) | |
335 | - GIC_SET_PENDING(irq + i); | |
415 | + if (GIC_TEST_LEVEL(irq + i, mask) | |
416 | + && !GIC_TEST_TRIGGER(irq + i)) { | |
417 | + DPRINTF("Set %d pending mask %x\n", irq + i, mask); | |
418 | + GIC_SET_PENDING(irq + i, mask); | |
419 | + } | |
336 | 420 | } |
337 | 421 | } |
338 | 422 | } else if (offset < 0x200) { |
339 | 423 | /* Interrupt Clear Enable. */ |
340 | - irq = (offset - 0x180) * 8; | |
424 | + irq = (offset - 0x180) * 8 + GIC_BASE_IRQ; | |
341 | 425 | if (irq >= GIC_NIRQ) |
342 | 426 | goto bad_reg; |
427 | + if (irq < 16) | |
428 | + value = 0; | |
343 | 429 | for (i = 0; i < 8; i++) { |
344 | 430 | if (value & (1 << i)) { |
345 | 431 | if (GIC_TEST_ENABLED(irq + i)) |
... | ... | @@ -349,22 +435,28 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, |
349 | 435 | } |
350 | 436 | } else if (offset < 0x280) { |
351 | 437 | /* Interrupt Set Pending. */ |
352 | - irq = (offset - 0x200) * 8; | |
438 | + irq = (offset - 0x200) * 8 + GIC_BASE_IRQ; | |
353 | 439 | if (irq >= GIC_NIRQ) |
354 | 440 | goto bad_reg; |
441 | + if (irq < 16) | |
442 | + irq = 0; | |
443 | + | |
355 | 444 | for (i = 0; i < 8; i++) { |
356 | 445 | if (value & (1 << i)) { |
357 | - GIC_SET_PENDING(irq + i); | |
446 | + GIC_SET_PENDING(irq + i, GIC_TARGET(irq)); | |
358 | 447 | } |
359 | 448 | } |
360 | 449 | } else if (offset < 0x300) { |
361 | 450 | /* Interrupt Clear Pending. */ |
362 | - irq = (offset - 0x280) * 8; | |
451 | + irq = (offset - 0x280) * 8 + GIC_BASE_IRQ; | |
363 | 452 | if (irq >= GIC_NIRQ) |
364 | 453 | goto bad_reg; |
365 | 454 | for (i = 0; i < 8; i++) { |
455 | + /* ??? This currently clears the pending bit for all CPUs, even | |
456 | + for per-CPU interrupts. It's unclear whether this is the | |
457 | + corect behavior. */ | |
366 | 458 | if (value & (1 << i)) { |
367 | - GIC_CLEAR_PENDING(irq + i); | |
459 | + GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK); | |
368 | 460 | } |
369 | 461 | } |
370 | 462 | } else if (offset < 0x400) { |
... | ... | @@ -372,21 +464,32 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, |
372 | 464 | goto bad_reg; |
373 | 465 | } else if (offset < 0x800) { |
374 | 466 | /* Interrupt Priority. */ |
375 | - irq = offset - 0x400; | |
467 | + irq = (offset - 0x400) + GIC_BASE_IRQ; | |
376 | 468 | if (irq >= GIC_NIRQ) |
377 | 469 | goto bad_reg; |
378 | - s->priority[irq] = value; | |
470 | + if (irq < 32) { | |
471 | + s->priority1[irq][cpu] = value; | |
472 | + } else { | |
473 | + s->priority2[irq - 32] = value; | |
474 | + } | |
475 | +#ifndef NVIC | |
379 | 476 | } else if (offset < 0xc00) { |
380 | 477 | /* Interrupt CPU Target. */ |
381 | - irq = offset - 0x800; | |
478 | + irq = (offset - 0x800) + GIC_BASE_IRQ; | |
382 | 479 | if (irq >= GIC_NIRQ) |
383 | 480 | goto bad_reg; |
384 | - s->irq_target[irq] = value; | |
481 | + if (irq < 29) | |
482 | + value = 0; | |
483 | + else if (irq < 32) | |
484 | + value = ALL_CPU_MASK; | |
485 | + s->irq_target[irq] = value & ALL_CPU_MASK; | |
385 | 486 | } else if (offset < 0xf00) { |
386 | 487 | /* Interrupt Configuration. */ |
387 | - irq = (offset - 0xc00) * 4; | |
488 | + irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; | |
388 | 489 | if (irq >= GIC_NIRQ) |
389 | 490 | goto bad_reg; |
491 | + if (irq < 32) | |
492 | + value |= 0xaa; | |
390 | 493 | for (i = 0; i < 4; i++) { |
391 | 494 | if (value & (1 << (i * 2))) { |
392 | 495 | GIC_SET_MODEL(irq + i); |
... | ... | @@ -399,25 +502,20 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, |
399 | 502 | GIC_CLEAR_TRIGGER(irq + i); |
400 | 503 | } |
401 | 504 | } |
505 | +#endif | |
402 | 506 | } else { |
403 | - /* 0xf00 is only handled for word writes. */ | |
507 | + /* 0xf00 is only handled for 32-bit writes. */ | |
404 | 508 | goto bad_reg; |
405 | 509 | } |
406 | 510 | gic_update(s); |
407 | 511 | return; |
408 | 512 | bad_reg: |
409 | - cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset); | |
513 | + cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset); | |
410 | 514 | } |
411 | 515 | |
412 | 516 | static void gic_dist_writew(void *opaque, target_phys_addr_t offset, |
413 | 517 | uint32_t value) |
414 | 518 | { |
415 | - gic_state *s = (gic_state *)opaque; | |
416 | - if (offset - s->base == 0xf00) { | |
417 | - GIC_SET_PENDING(value & 0x3ff); | |
418 | - gic_update(s); | |
419 | - return; | |
420 | - } | |
421 | 519 | gic_dist_writeb(opaque, offset, value & 0xff); |
422 | 520 | gic_dist_writeb(opaque, offset + 1, value >> 8); |
423 | 521 | } |
... | ... | @@ -425,6 +523,41 @@ static void gic_dist_writew(void *opaque, target_phys_addr_t offset, |
425 | 523 | static void gic_dist_writel(void *opaque, target_phys_addr_t offset, |
426 | 524 | uint32_t value) |
427 | 525 | { |
526 | + gic_state *s = (gic_state *)opaque; | |
527 | +#ifdef NVIC | |
528 | + uint32_t addr; | |
529 | + addr = offset - s->base; | |
530 | + if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) { | |
531 | + nvic_writel(s->nvic, addr, value); | |
532 | + return; | |
533 | + } | |
534 | +#endif | |
535 | + if (offset - s->base == GIC_DIST_OFFSET + 0xf00) { | |
536 | + int cpu; | |
537 | + int irq; | |
538 | + int mask; | |
539 | + | |
540 | + cpu = gic_get_current_cpu(); | |
541 | + irq = value & 0x3ff; | |
542 | + switch ((value >> 24) & 3) { | |
543 | + case 0: | |
544 | + mask = (value >> 16) & ALL_CPU_MASK; | |
545 | + break; | |
546 | + case 1: | |
547 | + mask = 1 << cpu; | |
548 | + break; | |
549 | + case 2: | |
550 | + mask = ALL_CPU_MASK ^ (1 << cpu); | |
551 | + break; | |
552 | + default: | |
553 | + DPRINTF("Bad Soft Int target filter\n"); | |
554 | + mask = ALL_CPU_MASK; | |
555 | + break; | |
556 | + } | |
557 | + GIC_SET_PENDING(irq, mask); | |
558 | + gic_update(s); | |
559 | + return; | |
560 | + } | |
428 | 561 | gic_dist_writew(opaque, offset, value & 0xffff); |
429 | 562 | gic_dist_writew(opaque, offset + 2, value >> 16); |
430 | 563 | } |
... | ... | @@ -441,105 +574,100 @@ static CPUWriteMemoryFunc *gic_dist_writefn[] = { |
441 | 574 | gic_dist_writel |
442 | 575 | }; |
443 | 576 | |
444 | -static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset) | |
577 | +#ifndef NVIC | |
578 | +static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset) | |
445 | 579 | { |
446 | - gic_state *s = (gic_state *)opaque; | |
447 | - offset -= s->base; | |
448 | 580 | switch (offset) { |
449 | 581 | case 0x00: /* Control */ |
450 | - return s->cpu_enabled; | |
582 | + return s->cpu_enabled[cpu]; | |
451 | 583 | case 0x04: /* Priority mask */ |
452 | - return s->priority_mask; | |
584 | + return s->priority_mask[cpu]; | |
453 | 585 | case 0x08: /* Binary Point */ |
454 | 586 | /* ??? Not implemented. */ |
455 | 587 | return 0; |
456 | 588 | case 0x0c: /* Acknowledge */ |
457 | - return gic_acknowledge_irq(s); | |
589 | + return gic_acknowledge_irq(s, cpu); | |
458 | 590 | case 0x14: /* Runing Priority */ |
459 | - return s->running_priority; | |
591 | + return s->running_priority[cpu]; | |
460 | 592 | case 0x18: /* Highest Pending Interrupt */ |
461 | - return s->current_pending; | |
593 | + return s->current_pending[cpu]; | |
462 | 594 | default: |
463 | - cpu_abort (cpu_single_env, "gic_cpu_read: Bad offset %x\n", offset); | |
595 | + cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n", | |
596 | + (int)offset); | |
464 | 597 | return 0; |
465 | 598 | } |
466 | 599 | } |
467 | 600 | |
468 | -static void gic_cpu_write(void *opaque, target_phys_addr_t offset, | |
469 | - uint32_t value) | |
601 | +static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value) | |
470 | 602 | { |
471 | - gic_state *s = (gic_state *)opaque; | |
472 | - offset -= s->base; | |
473 | 603 | switch (offset) { |
474 | 604 | case 0x00: /* Control */ |
475 | - s->cpu_enabled = (value & 1); | |
605 | + s->cpu_enabled[cpu] = (value & 1); | |
476 | 606 | DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); |
477 | 607 | break; |
478 | 608 | case 0x04: /* Priority mask */ |
479 | - s->priority_mask = (value & 0x3ff); | |
609 | + s->priority_mask[cpu] = (value & 0xff); | |
480 | 610 | break; |
481 | 611 | case 0x08: /* Binary Point */ |
482 | 612 | /* ??? Not implemented. */ |
483 | 613 | break; |
484 | 614 | case 0x10: /* End Of Interrupt */ |
485 | - return gic_complete_irq(s, value & 0x3ff); | |
615 | + return gic_complete_irq(s, cpu, value & 0x3ff); | |
486 | 616 | default: |
487 | - cpu_abort (cpu_single_env, "gic_cpu_write: Bad offset %x\n", offset); | |
617 | + cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n", | |
618 | + (int)offset); | |
488 | 619 | return; |
489 | 620 | } |
490 | 621 | gic_update(s); |
491 | 622 | } |
492 | - | |
493 | -static CPUReadMemoryFunc *gic_cpu_readfn[] = { | |
494 | - gic_cpu_read, | |
495 | - gic_cpu_read, | |
496 | - gic_cpu_read | |
497 | -}; | |
498 | - | |
499 | -static CPUWriteMemoryFunc *gic_cpu_writefn[] = { | |
500 | - gic_cpu_write, | |
501 | - gic_cpu_write, | |
502 | - gic_cpu_write | |
503 | -}; | |
623 | +#endif | |
504 | 624 | |
505 | 625 | static void gic_reset(gic_state *s) |
506 | 626 | { |
507 | 627 | int i; |
508 | 628 | memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state)); |
509 | - s->priority_mask = 0xf0; | |
510 | - s->current_pending = 1023; | |
511 | - s->running_irq = 1023; | |
512 | - s->running_priority = 0x100; | |
629 | + for (i = 0 ; i < NCPU; i++) { | |
630 | + s->priority_mask[i] = 0xf0; | |
631 | + s->current_pending[i] = 1023; | |
632 | + s->running_irq[i] = 1023; | |
633 | + s->running_priority[i] = 0x100; | |
634 | +#ifdef NVIC | |
635 | + /* The NVIC doesn't have per-cpu interfaces, so enable by default. */ | |
636 | + s->cpu_enabled[i] = 1; | |
637 | +#else | |
638 | + s->cpu_enabled[i] = 0; | |
639 | +#endif | |
640 | + } | |
513 | 641 | for (i = 0; i < 15; i++) { |
514 | 642 | GIC_SET_ENABLED(i); |
515 | 643 | GIC_SET_TRIGGER(i); |
516 | 644 | } |
645 | +#ifdef NVIC | |
646 | + /* The NVIC is always enabled. */ | |
647 | + s->enabled = 1; | |
648 | +#else | |
517 | 649 | s->enabled = 0; |
518 | - s->cpu_enabled = 0; | |
650 | +#endif | |
519 | 651 | } |
520 | 652 | |
521 | -qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq) | |
653 | +static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq) | |
522 | 654 | { |
523 | 655 | gic_state *s; |
524 | - qemu_irq *qi; | |
525 | 656 | int iomemtype; |
657 | + int i; | |
526 | 658 | |
527 | 659 | s = (gic_state *)qemu_mallocz(sizeof(gic_state)); |
528 | 660 | if (!s) |
529 | 661 | return NULL; |
530 | - qi = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ); | |
531 | - s->parent_irq = parent_irq; | |
532 | - if (base != 0xffffffff) { | |
533 | - iomemtype = cpu_register_io_memory(0, gic_cpu_readfn, | |
534 | - gic_cpu_writefn, s); | |
535 | - cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
536 | - iomemtype = cpu_register_io_memory(0, gic_dist_readfn, | |
537 | - gic_dist_writefn, s); | |
538 | - cpu_register_physical_memory(base + 0x1000, 0x00001000, iomemtype); | |
539 | - s->base = base; | |
540 | - } else { | |
541 | - s->base = 0; | |
662 | + s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ); | |
663 | + for (i = 0; i < NCPU; i++) { | |
664 | + s->parent_irq[i] = parent_irq[i]; | |
542 | 665 | } |
666 | + iomemtype = cpu_register_io_memory(0, gic_dist_readfn, | |
667 | + gic_dist_writefn, s); | |
668 | + cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000, | |
669 | + iomemtype); | |
670 | + s->base = base; | |
543 | 671 | gic_reset(s); |
544 | - return qi; | |
672 | + return s; | |
545 | 673 | } | ... | ... |
hw/arm_sysctl.c
1 | 1 | /* |
2 | 2 | * Status and system control registers for ARM RealView/Versatile boards. |
3 | 3 | * |
4 | - * Copyright (c) 2006 CodeSourcery. | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | 5 | * Written by Paul Brook |
6 | 6 | * |
7 | 7 | * This code is licenced under the GPL. |
... | ... | @@ -200,6 +200,9 @@ void arm_sysctl_init(uint32_t base, uint32_t sys_id) |
200 | 200 | return; |
201 | 201 | s->base = base; |
202 | 202 | s->sys_id = sys_id; |
203 | + /* The MPcore bootloader uses these flags to start secondary CPUs. | |
204 | + We don't use a bootloader, so do this here. */ | |
205 | + s->flags = 3; | |
203 | 206 | iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn, |
204 | 207 | arm_sysctl_writefn, s); |
205 | 208 | cpu_register_physical_memory(base, 0x00001000, iomemtype); | ... | ... |
hw/armv7m.c
0 โ 100644
1 | +/* | |
2 | + * ARMV7M System emulation. | |
3 | + * | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | + | |
12 | +/* Bitbanded IO. Each word corresponds to a single bit. */ | |
13 | + | |
14 | +/* Get the byte address of the real memory for a bitband acess. */ | |
15 | +static inline uint32_t bitband_addr(uint32_t addr) | |
16 | +{ | |
17 | + uint32_t res; | |
18 | + | |
19 | + res = addr & 0xe0000000; | |
20 | + res |= (addr & 0x1ffffff) >> 5; | |
21 | + return res; | |
22 | + | |
23 | +} | |
24 | + | |
25 | +static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset) | |
26 | +{ | |
27 | + uint8_t v; | |
28 | + cpu_physical_memory_read(bitband_addr(offset), &v, 1); | |
29 | + return (v & (1 << ((offset >> 2) & 7))) != 0; | |
30 | +} | |
31 | + | |
32 | +static void bitband_writeb(void *opaque, target_phys_addr_t offset, | |
33 | + uint32_t value) | |
34 | +{ | |
35 | + uint32_t addr; | |
36 | + uint8_t mask; | |
37 | + uint8_t v; | |
38 | + addr = bitband_addr(offset); | |
39 | + mask = (1 << ((offset >> 2) & 7)); | |
40 | + cpu_physical_memory_read(addr, &v, 1); | |
41 | + if (value & 1) | |
42 | + v |= mask; | |
43 | + else | |
44 | + v &= ~mask; | |
45 | + cpu_physical_memory_write(addr, &v, 1); | |
46 | +} | |
47 | + | |
48 | +static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset) | |
49 | +{ | |
50 | + uint32_t addr; | |
51 | + uint16_t mask; | |
52 | + uint16_t v; | |
53 | + addr = bitband_addr(offset) & ~1; | |
54 | + mask = (1 << ((offset >> 2) & 15)); | |
55 | + mask = tswap16(mask); | |
56 | + cpu_physical_memory_read(addr, (uint8_t *)&v, 2); | |
57 | + return (v & mask) != 0; | |
58 | +} | |
59 | + | |
60 | +static void bitband_writew(void *opaque, target_phys_addr_t offset, | |
61 | + uint32_t value) | |
62 | +{ | |
63 | + uint32_t addr; | |
64 | + uint16_t mask; | |
65 | + uint16_t v; | |
66 | + addr = bitband_addr(offset) & ~1; | |
67 | + mask = (1 << ((offset >> 2) & 15)); | |
68 | + mask = tswap16(mask); | |
69 | + cpu_physical_memory_read(addr, (uint8_t *)&v, 2); | |
70 | + if (value & 1) | |
71 | + v |= mask; | |
72 | + else | |
73 | + v &= ~mask; | |
74 | + cpu_physical_memory_write(addr, (uint8_t *)&v, 2); | |
75 | +} | |
76 | + | |
77 | +static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset) | |
78 | +{ | |
79 | + uint32_t addr; | |
80 | + uint32_t mask; | |
81 | + uint32_t v; | |
82 | + addr = bitband_addr(offset) & ~3; | |
83 | + mask = (1 << ((offset >> 2) & 31)); | |
84 | + mask = tswap32(mask); | |
85 | + cpu_physical_memory_read(addr, (uint8_t *)&v, 4); | |
86 | + return (v & mask) != 0; | |
87 | +} | |
88 | + | |
89 | +static void bitband_writel(void *opaque, target_phys_addr_t offset, | |
90 | + uint32_t value) | |
91 | +{ | |
92 | + uint32_t addr; | |
93 | + uint32_t mask; | |
94 | + uint32_t v; | |
95 | + addr = bitband_addr(offset) & ~3; | |
96 | + mask = (1 << ((offset >> 2) & 31)); | |
97 | + mask = tswap32(mask); | |
98 | + cpu_physical_memory_read(addr, (uint8_t *)&v, 4); | |
99 | + if (value & 1) | |
100 | + v |= mask; | |
101 | + else | |
102 | + v &= ~mask; | |
103 | + cpu_physical_memory_write(addr, (uint8_t *)&v, 4); | |
104 | +} | |
105 | + | |
106 | +static CPUReadMemoryFunc *bitband_readfn[] = { | |
107 | + bitband_readb, | |
108 | + bitband_readw, | |
109 | + bitband_readl | |
110 | +}; | |
111 | + | |
112 | +static CPUWriteMemoryFunc *bitband_writefn[] = { | |
113 | + bitband_writeb, | |
114 | + bitband_writew, | |
115 | + bitband_writel | |
116 | +}; | |
117 | + | |
118 | +static void armv7m_bitband_init(void) | |
119 | +{ | |
120 | + int iomemtype; | |
121 | + | |
122 | + iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn, | |
123 | + NULL); | |
124 | + cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype); | |
125 | + cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype); | |
126 | +} | |
127 | + | |
128 | +/* Board init. */ | |
129 | +/* Init CPU and memory for a v7-M based board. | |
130 | + flash_size and sram_size are in kb. | |
131 | + Returns the NVIC array. */ | |
132 | + | |
133 | +qemu_irq *armv7m_init(int flash_size, int sram_size, | |
134 | + const char *kernel_filename, const char *cpu_model) | |
135 | +{ | |
136 | + CPUState *env; | |
137 | + qemu_irq *pic; | |
138 | + uint32_t pc; | |
139 | + int image_size; | |
140 | + uint64_t entry; | |
141 | + uint64_t lowaddr; | |
142 | + | |
143 | + flash_size *= 1024; | |
144 | + sram_size *= 1024; | |
145 | + | |
146 | + if (!cpu_model) | |
147 | + cpu_model = "cortex-m3"; | |
148 | + env = cpu_init(cpu_model); | |
149 | + if (!env) { | |
150 | + fprintf(stderr, "Unable to find CPU definition\n"); | |
151 | + exit(1); | |
152 | + } | |
153 | + | |
154 | +#if 0 | |
155 | + /* > 32Mb SRAM gets complicated because it overlaps the bitband area. | |
156 | + We don't have proper commandline options, so allocate half of memory | |
157 | + as SRAM, up to a maximum of 32Mb, and the rest as code. */ | |
158 | + if (ram_size > (512 + 32) * 1024 * 1024) | |
159 | + ram_size = (512 + 32) * 1024 * 1024; | |
160 | + sram_size = (ram_size / 2) & TARGET_PAGE_MASK; | |
161 | + if (sram_size > 32 * 1024 * 1024) | |
162 | + sram_size = 32 * 1024 * 1024; | |
163 | + code_size = ram_size - sram_size; | |
164 | +#endif | |
165 | + | |
166 | + /* Flash programming is done via the SCU, so pretend it is ROM. */ | |
167 | + cpu_register_physical_memory(0, flash_size, IO_MEM_ROM); | |
168 | + cpu_register_physical_memory(0x20000000, sram_size, | |
169 | + flash_size + IO_MEM_RAM); | |
170 | + armv7m_bitband_init(); | |
171 | + | |
172 | + pic = armv7m_nvic_init(env); | |
173 | + | |
174 | + image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL); | |
175 | + if (image_size < 0) { | |
176 | + image_size = load_image(kernel_filename, phys_ram_base); | |
177 | + lowaddr = 0; | |
178 | + } | |
179 | + if (image_size < 0) { | |
180 | + fprintf(stderr, "qemu: could not load kernel '%s'\n", | |
181 | + kernel_filename); | |
182 | + exit(1); | |
183 | + } | |
184 | + | |
185 | + /* If the image was loaded at address zero then assume it is a | |
186 | + regular ROM image and perform the normal CPU reset sequence. | |
187 | + Otherwise jump directly to the entry point. */ | |
188 | + if (lowaddr == 0) { | |
189 | + env->regs[13] = tswap32(*(uint32_t *)phys_ram_base); | |
190 | + pc = tswap32(*(uint32_t *)(phys_ram_base + 4)); | |
191 | + } else { | |
192 | + pc = entry; | |
193 | + } | |
194 | + env->thumb = pc & 1; | |
195 | + env->regs[15] = pc & ~1; | |
196 | + | |
197 | + /* Hack to map an additional page of ram at the top of the address | |
198 | + space. This stops qemu complaining about executing code outside RAM | |
199 | + when returning from an exception. */ | |
200 | + cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size); | |
201 | + | |
202 | + return pic; | |
203 | +} | |
204 | + | ... | ... |
hw/armv7m_nvic.c
0 โ 100644
1 | +/* | |
2 | + * ARM Nested Vectored Interrupt Controller | |
3 | + * | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + * | |
9 | + * The ARMv7M System controller is fairly tightly tied in with the | |
10 | + * NVIC. Much of that is also implemented here. | |
11 | + */ | |
12 | + | |
13 | +#include "vl.h" | |
14 | +#include "arm_pic.h" | |
15 | + | |
16 | +#define GIC_NIRQ 64 | |
17 | +#define NCPU 1 | |
18 | +#define NVIC 1 | |
19 | + | |
20 | +/* Only a single "CPU" interface is present. */ | |
21 | +static inline int | |
22 | +gic_get_current_cpu(void) | |
23 | +{ | |
24 | + return 0; | |
25 | +} | |
26 | + | |
27 | +static uint32_t nvic_readl(void *opaque, uint32_t offset); | |
28 | +static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); | |
29 | + | |
30 | +#include "arm_gic.c" | |
31 | + | |
32 | +typedef struct { | |
33 | + struct { | |
34 | + uint32_t control; | |
35 | + uint32_t reload; | |
36 | + int64_t tick; | |
37 | + QEMUTimer *timer; | |
38 | + } systick; | |
39 | + gic_state *gic; | |
40 | +} nvic_state; | |
41 | + | |
42 | +/* qemu timers run at 1GHz. We want something closer to 1MHz. */ | |
43 | +#define SYSTICK_SCALE 1000ULL | |
44 | + | |
45 | +#define SYSTICK_ENABLE (1 << 0) | |
46 | +#define SYSTICK_TICKINT (1 << 1) | |
47 | +#define SYSTICK_CLKSOURCE (1 << 2) | |
48 | +#define SYSTICK_COUNTFLAG (1 << 16) | |
49 | + | |
50 | +/* Conversion factor from qemu timer to SysTick frequencies. | |
51 | + QEMU uses a base of 1GHz, so these give 20MHz and 1MHz for core and | |
52 | + reference frequencies. */ | |
53 | + | |
54 | +static inline int64_t systick_scale(nvic_state *s) | |
55 | +{ | |
56 | + if (s->systick.control & SYSTICK_CLKSOURCE) | |
57 | + return 50; | |
58 | + else | |
59 | + return 1000; | |
60 | +} | |
61 | + | |
62 | +static void systick_reload(nvic_state *s, int reset) | |
63 | +{ | |
64 | + if (reset) | |
65 | + s->systick.tick = qemu_get_clock(vm_clock); | |
66 | + s->systick.tick += (s->systick.reload + 1) * systick_scale(s); | |
67 | + qemu_mod_timer(s->systick.timer, s->systick.tick); | |
68 | +} | |
69 | + | |
70 | +static void systick_timer_tick(void * opaque) | |
71 | +{ | |
72 | + nvic_state *s = (nvic_state *)opaque; | |
73 | + s->systick.control |= SYSTICK_COUNTFLAG; | |
74 | + if (s->systick.control & SYSTICK_TICKINT) { | |
75 | + /* Trigger the interrupt. */ | |
76 | + armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); | |
77 | + } | |
78 | + if (s->systick.reload == 0) { | |
79 | + s->systick.control &= ~SYSTICK_ENABLE; | |
80 | + } else { | |
81 | + systick_reload(s, 0); | |
82 | + } | |
83 | +} | |
84 | + | |
85 | +/* The external routines use the hardware vector numbering, ie. the first | |
86 | + IRQ is #16. The internal GIC routines use #32 as the first IRQ. */ | |
87 | +void armv7m_nvic_set_pending(void *opaque, int irq) | |
88 | +{ | |
89 | + nvic_state *s = (nvic_state *)opaque; | |
90 | + if (irq >= 16) | |
91 | + irq += 16; | |
92 | + gic_set_pending_private(s->gic, 0, irq); | |
93 | +} | |
94 | + | |
95 | +/* Make pending IRQ active. */ | |
96 | +int armv7m_nvic_acknowledge_irq(void *opaque) | |
97 | +{ | |
98 | + nvic_state *s = (nvic_state *)opaque; | |
99 | + uint32_t irq; | |
100 | + | |
101 | + irq = gic_acknowledge_irq(s->gic, 0); | |
102 | + if (irq == 1023) | |
103 | + cpu_abort(cpu_single_env, "Interrupt but no vector\n"); | |
104 | + if (irq >= 32) | |
105 | + irq -= 16; | |
106 | + return irq; | |
107 | +} | |
108 | + | |
109 | +void armv7m_nvic_complete_irq(void *opaque, int irq) | |
110 | +{ | |
111 | + nvic_state *s = (nvic_state *)opaque; | |
112 | + if (irq >= 16) | |
113 | + irq += 16; | |
114 | + gic_complete_irq(s->gic, 0, irq); | |
115 | +} | |
116 | + | |
117 | +static uint32_t nvic_readl(void *opaque, uint32_t offset) | |
118 | +{ | |
119 | + nvic_state *s = (nvic_state *)opaque; | |
120 | + uint32_t val; | |
121 | + int irq; | |
122 | + | |
123 | + switch (offset) { | |
124 | + case 4: /* Interrupt Control Type. */ | |
125 | + return (GIC_NIRQ / 32) - 1; | |
126 | + case 0x10: /* SysTick Control and Status. */ | |
127 | + val = s->systick.control; | |
128 | + s->systick.control &= ~SYSTICK_COUNTFLAG; | |
129 | + return val; | |
130 | + case 0x14: /* SysTick Reload Value. */ | |
131 | + return s->systick.reload; | |
132 | + case 0x18: /* SysTick Current Value. */ | |
133 | + { | |
134 | + int64_t t; | |
135 | + if ((s->systick.control & SYSTICK_ENABLE) == 0) | |
136 | + return 0; | |
137 | + t = qemu_get_clock(vm_clock); | |
138 | + if (t >= s->systick.tick) | |
139 | + return 0; | |
140 | + val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1; | |
141 | + /* The interrupt in triggered when the timer reaches zero. | |
142 | + However the counter is not reloaded until the next clock | |
143 | + tick. This is a hack to return zero during the first tick. */ | |
144 | + if (val > s->systick.reload) | |
145 | + val = 0; | |
146 | + return val; | |
147 | + } | |
148 | + case 0x1c: /* SysTick Calibration Value. */ | |
149 | + return 10000; | |
150 | + case 0xd00: /* CPUID Base. */ | |
151 | + return cpu_single_env->cp15.c0_cpuid; | |
152 | + case 0xd04: /* Interrypt Control State. */ | |
153 | + /* VECTACTIVE */ | |
154 | + val = s->gic->running_irq[0]; | |
155 | + if (val == 1023) { | |
156 | + val = 0; | |
157 | + } else if (val >= 32) { | |
158 | + val -= 16; | |
159 | + } | |
160 | + /* RETTOBASE */ | |
161 | + if (s->gic->running_irq[0] == 1023 | |
162 | + || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) { | |
163 | + val |= (1 << 11); | |
164 | + } | |
165 | + /* VECTPENDING */ | |
166 | + if (s->gic->current_pending[0] != 1023) | |
167 | + val |= (s->gic->current_pending[0] << 12); | |
168 | + /* ISRPENDING */ | |
169 | + for (irq = 32; irq < GIC_NIRQ; irq++) { | |
170 | + if (s->gic->irq_state[irq].pending) { | |
171 | + val |= (1 << 22); | |
172 | + break; | |
173 | + } | |
174 | + } | |
175 | + /* PENDSTSET */ | |
176 | + if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending) | |
177 | + val |= (1 << 26); | |
178 | + /* PENDSVSET */ | |
179 | + if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending) | |
180 | + val |= (1 << 28); | |
181 | + /* NMIPENDSET */ | |
182 | + if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending) | |
183 | + val |= (1 << 31); | |
184 | + return val; | |
185 | + case 0xd08: /* Vector Table Offset. */ | |
186 | + return cpu_single_env->v7m.vecbase; | |
187 | + case 0xd0c: /* Application Interrupt/Reset Control. */ | |
188 | + return 0xfa05000; | |
189 | + case 0xd10: /* System Control. */ | |
190 | + /* TODO: Implement SLEEPONEXIT. */ | |
191 | + return 0; | |
192 | + case 0xd14: /* Configuration Control. */ | |
193 | + /* TODO: Implement Configuration Control bits. */ | |
194 | + return 0; | |
195 | + case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */ | |
196 | + irq = offset - 0xd14; | |
197 | + val = 0; | |
198 | + val = s->gic->priority1[irq++][0]; | |
199 | + val = s->gic->priority1[irq++][0] << 8; | |
200 | + val = s->gic->priority1[irq++][0] << 16; | |
201 | + val = s->gic->priority1[irq][0] << 24; | |
202 | + return val; | |
203 | + case 0xd24: /* System Handler Status. */ | |
204 | + val = 0; | |
205 | + if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0); | |
206 | + if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1); | |
207 | + if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3); | |
208 | + if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7); | |
209 | + if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8); | |
210 | + if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10); | |
211 | + if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11); | |
212 | + if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12); | |
213 | + if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13); | |
214 | + if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14); | |
215 | + if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15); | |
216 | + if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16); | |
217 | + if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17); | |
218 | + if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18); | |
219 | + return val; | |
220 | + case 0xd28: /* Configurable Fault Status. */ | |
221 | + /* TODO: Implement Fault Status. */ | |
222 | + cpu_abort(cpu_single_env, | |
223 | + "Not implemented: Configurable Fault Status."); | |
224 | + return 0; | |
225 | + case 0xd2c: /* Hard Fault Status. */ | |
226 | + case 0xd30: /* Debug Fault Status. */ | |
227 | + case 0xd34: /* Mem Manage Address. */ | |
228 | + case 0xd38: /* Bus Fault Address. */ | |
229 | + case 0xd3c: /* Aux Fault Status. */ | |
230 | + /* TODO: Implement fault status registers. */ | |
231 | + goto bad_reg; | |
232 | + case 0xd40: /* PFR0. */ | |
233 | + return 0x00000030; | |
234 | + case 0xd44: /* PRF1. */ | |
235 | + return 0x00000200; | |
236 | + case 0xd48: /* DFR0. */ | |
237 | + return 0x00100000; | |
238 | + case 0xd4c: /* AFR0. */ | |
239 | + return 0x00000000; | |
240 | + case 0xd50: /* MMFR0. */ | |
241 | + return 0x00000030; | |
242 | + case 0xd54: /* MMFR1. */ | |
243 | + return 0x00000000; | |
244 | + case 0xd58: /* MMFR2. */ | |
245 | + return 0x00000000; | |
246 | + case 0xd5c: /* MMFR3. */ | |
247 | + return 0x00000000; | |
248 | + case 0xd60: /* ISAR0. */ | |
249 | + return 0x01141110; | |
250 | + case 0xd64: /* ISAR1. */ | |
251 | + return 0x02111000; | |
252 | + case 0xd68: /* ISAR2. */ | |
253 | + return 0x21112231; | |
254 | + case 0xd6c: /* ISAR3. */ | |
255 | + return 0x01111110; | |
256 | + case 0xd70: /* ISAR4. */ | |
257 | + return 0x01310102; | |
258 | + /* TODO: Implement debug registers. */ | |
259 | + default: | |
260 | + bad_reg: | |
261 | + cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset); | |
262 | + } | |
263 | +} | |
264 | + | |
265 | +static void nvic_writel(void *opaque, uint32_t offset, uint32_t value) | |
266 | +{ | |
267 | + nvic_state *s = (nvic_state *)opaque; | |
268 | + uint32_t oldval; | |
269 | + switch (offset) { | |
270 | + case 0x10: /* SysTick Control and Status. */ | |
271 | + oldval = s->systick.control; | |
272 | + s->systick.control &= 0xfffffff8; | |
273 | + s->systick.control |= value & 7; | |
274 | + if ((oldval ^ value) & SYSTICK_ENABLE) { | |
275 | + int64_t now = qemu_get_clock(vm_clock); | |
276 | + if (value & SYSTICK_ENABLE) { | |
277 | + if (s->systick.tick) { | |
278 | + s->systick.tick += now; | |
279 | + qemu_mod_timer(s->systick.timer, s->systick.tick); | |
280 | + } else { | |
281 | + systick_reload(s, 1); | |
282 | + } | |
283 | + } else { | |
284 | + qemu_del_timer(s->systick.timer); | |
285 | + s->systick.tick -= now; | |
286 | + if (s->systick.tick < 0) | |
287 | + s->systick.tick = 0; | |
288 | + } | |
289 | + } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { | |
290 | + /* This is a hack. Force the timer to be reloaded | |
291 | + when the reference clock is changed. */ | |
292 | + systick_reload(s, 1); | |
293 | + } | |
294 | + break; | |
295 | + case 0x14: /* SysTick Reload Value. */ | |
296 | + s->systick.reload = value; | |
297 | + break; | |
298 | + case 0x18: /* SysTick Current Value. Writes reload the timer. */ | |
299 | + systick_reload(s, 1); | |
300 | + s->systick.control &= ~SYSTICK_COUNTFLAG; | |
301 | + break; | |
302 | + case 0xd04: /* Interrupt Control State. */ | |
303 | + if (value & (1 << 31)) { | |
304 | + armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI); | |
305 | + } | |
306 | + if (value & (1 << 28)) { | |
307 | + armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV); | |
308 | + } else if (value & (1 << 27)) { | |
309 | + s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0; | |
310 | + gic_update(s->gic); | |
311 | + } | |
312 | + if (value & (1 << 26)) { | |
313 | + armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); | |
314 | + } else if (value & (1 << 25)) { | |
315 | + s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0; | |
316 | + gic_update(s->gic); | |
317 | + } | |
318 | + break; | |
319 | + case 0xd08: /* Vector Table Offset. */ | |
320 | + cpu_single_env->v7m.vecbase = value & 0xffffff80; | |
321 | + break; | |
322 | + case 0xd0c: /* Application Interrupt/Reset Control. */ | |
323 | + if ((value >> 16) == 0x05fa) { | |
324 | + if (value & 2) { | |
325 | + cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented"); | |
326 | + } | |
327 | + if (value & 5) { | |
328 | + cpu_abort(cpu_single_env, "System reset"); | |
329 | + } | |
330 | + } | |
331 | + break; | |
332 | + case 0xd10: /* System Control. */ | |
333 | + case 0xd14: /* Configuration Control. */ | |
334 | + /* TODO: Implement control registers. */ | |
335 | + goto bad_reg; | |
336 | + case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */ | |
337 | + { | |
338 | + int irq; | |
339 | + irq = offset - 0xd14; | |
340 | + s->gic->priority1[irq++][0] = value & 0xff; | |
341 | + s->gic->priority1[irq++][0] = (value >> 8) & 0xff; | |
342 | + s->gic->priority1[irq++][0] = (value >> 16) & 0xff; | |
343 | + s->gic->priority1[irq][0] = (value >> 24) & 0xff; | |
344 | + gic_update(s->gic); | |
345 | + } | |
346 | + break; | |
347 | + case 0xd24: /* System Handler Control. */ | |
348 | + /* TODO: Real hardware allows you to set/clear the active bits | |
349 | + under some circumstances. We don't implement this. */ | |
350 | + s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0; | |
351 | + s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0; | |
352 | + s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; | |
353 | + break; | |
354 | + case 0xd28: /* Configurable Fault Status. */ | |
355 | + case 0xd2c: /* Hard Fault Status. */ | |
356 | + case 0xd30: /* Debug Fault Status. */ | |
357 | + case 0xd34: /* Mem Manage Address. */ | |
358 | + case 0xd38: /* Bus Fault Address. */ | |
359 | + case 0xd3c: /* Aux Fault Status. */ | |
360 | + goto bad_reg; | |
361 | + default: | |
362 | + bad_reg: | |
363 | + cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset); | |
364 | + } | |
365 | +} | |
366 | + | |
367 | +qemu_irq *armv7m_nvic_init(CPUState *env) | |
368 | +{ | |
369 | + nvic_state *s; | |
370 | + qemu_irq *parent; | |
371 | + | |
372 | + parent = arm_pic_init_cpu(env); | |
373 | + s = (nvic_state *)qemu_mallocz(sizeof(nvic_state)); | |
374 | + s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]); | |
375 | + s->gic->nvic = s; | |
376 | + s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s); | |
377 | + if (env->v7m.nvic) | |
378 | + cpu_abort(env, "CPU can only have one NVIC\n"); | |
379 | + env->v7m.nvic = s; | |
380 | + return s->gic->in; | |
381 | +} | ... | ... |
hw/integratorcp.c
... | ... | @@ -497,8 +497,8 @@ static void integratorcp_init(int ram_size, int vga_ram_size, |
497 | 497 | icp_pic_init(0xca000000, pic[26], NULL); |
498 | 498 | icp_pit_init(0x13000000, pic, 5); |
499 | 499 | pl031_init(0x15000000, pic[8]); |
500 | - pl011_init(0x16000000, pic[1], serial_hds[0]); | |
501 | - pl011_init(0x17000000, pic[2], serial_hds[1]); | |
500 | + pl011_init(0x16000000, pic[1], serial_hds[0], PL011_ARM); | |
501 | + pl011_init(0x17000000, pic[2], serial_hds[1], PL011_ARM); | |
502 | 502 | icp_control_init(0xcb000000); |
503 | 503 | pl050_init(0x18000000, pic[3], 0); |
504 | 504 | pl050_init(0x19000000, pic[4], 1); | ... | ... |
hw/mpcore.c
0 โ 100644
1 | +/* | |
2 | + * ARM MPCore internal peripheral emulation. | |
3 | + * | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | + | |
12 | +#define MPCORE_PRIV_BASE 0x10100000 | |
13 | +#define NCPU 4 | |
14 | +/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines | |
15 | + (+ 32 internal). However my test chip only exposes/reports 32. | |
16 | + More importantly Linux falls over if more than 32 are present! */ | |
17 | +#define GIC_NIRQ 64 | |
18 | + | |
19 | +static inline int | |
20 | +gic_get_current_cpu(void) | |
21 | +{ | |
22 | + return cpu_single_env->cpu_index; | |
23 | +} | |
24 | + | |
25 | +#include "arm_gic.c" | |
26 | + | |
27 | +/* MPCore private memory region. */ | |
28 | + | |
29 | +typedef struct { | |
30 | + uint32_t count; | |
31 | + uint32_t load; | |
32 | + uint32_t control; | |
33 | + uint32_t status; | |
34 | + uint32_t old_status; | |
35 | + int64_t tick; | |
36 | + QEMUTimer *timer; | |
37 | + struct mpcore_priv_state *mpcore; | |
38 | + int id; /* Encodes both timer/watchdog and CPU. */ | |
39 | +} mpcore_timer_state; | |
40 | + | |
41 | +typedef struct mpcore_priv_state { | |
42 | + gic_state *gic; | |
43 | + uint32_t scu_control; | |
44 | + mpcore_timer_state timer[8]; | |
45 | +} mpcore_priv_state; | |
46 | + | |
47 | +/* Per-CPU Timers. */ | |
48 | + | |
49 | +static inline void mpcore_timer_update_irq(mpcore_timer_state *s) | |
50 | +{ | |
51 | + if (s->status & ~s->old_status) { | |
52 | + gic_set_pending_private(s->mpcore->gic, s->id >> 1, 29 + (s->id & 1)); | |
53 | + } | |
54 | + s->old_status = s->status; | |
55 | +} | |
56 | + | |
57 | +/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ | |
58 | +static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s) | |
59 | +{ | |
60 | + return (((s->control >> 8) & 0xff) + 1) * 10; | |
61 | +} | |
62 | + | |
63 | +static void mpcore_timer_reload(mpcore_timer_state *s, int restart) | |
64 | +{ | |
65 | + if (s->count == 0) | |
66 | + return; | |
67 | + if (restart) | |
68 | + s->tick = qemu_get_clock(vm_clock); | |
69 | + s->tick += (int64_t)s->count * mpcore_timer_scale(s); | |
70 | + qemu_mod_timer(s->timer, s->tick); | |
71 | +} | |
72 | + | |
73 | +static void mpcore_timer_tick(void *opaque) | |
74 | +{ | |
75 | + mpcore_timer_state *s = (mpcore_timer_state *)opaque; | |
76 | + s->status = 1; | |
77 | + if (s->control & 2) { | |
78 | + s->count = s->load; | |
79 | + mpcore_timer_reload(s, 0); | |
80 | + } else { | |
81 | + s->count = 0; | |
82 | + } | |
83 | + mpcore_timer_update_irq(s); | |
84 | +} | |
85 | + | |
86 | +static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset) | |
87 | +{ | |
88 | + int64_t val; | |
89 | + switch (offset) { | |
90 | + case 0: /* Load */ | |
91 | + return s->load; | |
92 | + /* Fall through. */ | |
93 | + case 4: /* Counter. */ | |
94 | + if (((s->control & 1) == 0) || (s->count == 0)) | |
95 | + return 0; | |
96 | + /* Slow and ugly, but hopefully won't happen too often. */ | |
97 | + val = s->tick - qemu_get_clock(vm_clock); | |
98 | + val /= mpcore_timer_scale(s); | |
99 | + if (val < 0) | |
100 | + val = 0; | |
101 | + return val; | |
102 | + case 8: /* Control. */ | |
103 | + return s->control; | |
104 | + case 12: /* Interrupt status. */ | |
105 | + return s->status; | |
106 | + } | |
107 | +} | |
108 | + | |
109 | +static void mpcore_timer_write(mpcore_timer_state *s, int offset, | |
110 | + uint32_t value) | |
111 | +{ | |
112 | + int64_t old; | |
113 | + switch (offset) { | |
114 | + case 0: /* Load */ | |
115 | + s->load = value; | |
116 | + /* Fall through. */ | |
117 | + case 4: /* Counter. */ | |
118 | + if ((s->control & 1) && s->count) { | |
119 | + /* Cancel the previous timer. */ | |
120 | + qemu_del_timer(s->timer); | |
121 | + } | |
122 | + s->count = value; | |
123 | + if (s->control & 1) { | |
124 | + mpcore_timer_reload(s, 1); | |
125 | + } | |
126 | + break; | |
127 | + case 8: /* Control. */ | |
128 | + old = s->control; | |
129 | + s->control = value; | |
130 | + if (((old & 1) == 0) && (value & 1)) { | |
131 | + if (s->count == 0 && (s->control & 2)) | |
132 | + s->count = s->load; | |
133 | + mpcore_timer_reload(s, 1); | |
134 | + } | |
135 | + break; | |
136 | + case 12: /* Interrupt status. */ | |
137 | + s->status &= ~value; | |
138 | + mpcore_timer_update_irq(s); | |
139 | + break; | |
140 | + } | |
141 | +} | |
142 | + | |
143 | +static void mpcore_timer_init(mpcore_priv_state *mpcore, | |
144 | + mpcore_timer_state *s, int id) | |
145 | +{ | |
146 | + s->id = id; | |
147 | + s->mpcore = mpcore; | |
148 | + s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s); | |
149 | +} | |
150 | + | |
151 | + | |
152 | +/* Per-CPU private memory mapped IO. */ | |
153 | + | |
154 | +static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset) | |
155 | +{ | |
156 | + mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
157 | + int id; | |
158 | + offset &= 0xfff; | |
159 | + if (offset < 0x100) { | |
160 | + /* SCU */ | |
161 | + switch (offset) { | |
162 | + case 0x00: /* Control. */ | |
163 | + return s->scu_control; | |
164 | + case 0x04: /* Configuration. */ | |
165 | + return 0xf3; | |
166 | + case 0x08: /* CPU status. */ | |
167 | + return 0; | |
168 | + case 0x0c: /* Invalidate all. */ | |
169 | + return 0; | |
170 | + default: | |
171 | + goto bad_reg; | |
172 | + } | |
173 | + } else if (offset < 0x600) { | |
174 | + /* Interrupt controller. */ | |
175 | + if (offset < 0x200) { | |
176 | + id = gic_get_current_cpu(); | |
177 | + } else { | |
178 | + id = (offset - 0x200) >> 8; | |
179 | + } | |
180 | + return gic_cpu_read(s->gic, id, offset & 0xff); | |
181 | + } else if (offset < 0xb00) { | |
182 | + /* Timers. */ | |
183 | + if (offset < 0x700) { | |
184 | + id = gic_get_current_cpu(); | |
185 | + } else { | |
186 | + id = (offset - 0x700) >> 8; | |
187 | + } | |
188 | + id <<= 1; | |
189 | + if (offset & 0x20) | |
190 | + id++; | |
191 | + return mpcore_timer_read(&s->timer[id], offset & 0xf); | |
192 | + } | |
193 | +bad_reg: | |
194 | + cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n", | |
195 | + (int)offset); | |
196 | + return 0; | |
197 | +} | |
198 | + | |
199 | +static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, | |
200 | + uint32_t value) | |
201 | +{ | |
202 | + mpcore_priv_state *s = (mpcore_priv_state *)opaque; | |
203 | + int id; | |
204 | + offset &= 0xfff; | |
205 | + if (offset < 0x100) { | |
206 | + /* SCU */ | |
207 | + switch (offset) { | |
208 | + case 0: /* Control register. */ | |
209 | + s->scu_control = value & 1; | |
210 | + break; | |
211 | + case 0x0c: /* Invalidate all. */ | |
212 | + /* This is a no-op as cache is not emulated. */ | |
213 | + break; | |
214 | + default: | |
215 | + goto bad_reg; | |
216 | + } | |
217 | + } else if (offset < 0x600) { | |
218 | + /* Interrupt controller. */ | |
219 | + if (offset < 0x200) { | |
220 | + id = gic_get_current_cpu(); | |
221 | + } else { | |
222 | + id = (offset - 0x200) >> 8; | |
223 | + } | |
224 | + gic_cpu_write(s->gic, id, offset & 0xff, value); | |
225 | + } else if (offset < 0xb00) { | |
226 | + /* Timers. */ | |
227 | + if (offset < 0x700) { | |
228 | + id = gic_get_current_cpu(); | |
229 | + } else { | |
230 | + id = (offset - 0x700) >> 8; | |
231 | + } | |
232 | + id <<= 1; | |
233 | + if (offset & 0x20) | |
234 | + id++; | |
235 | + mpcore_timer_write(&s->timer[id], offset & 0xf, value); | |
236 | + return; | |
237 | + } | |
238 | + return; | |
239 | +bad_reg: | |
240 | + cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n", | |
241 | + (int)offset); | |
242 | +} | |
243 | + | |
244 | +static CPUReadMemoryFunc *mpcore_priv_readfn[] = { | |
245 | + mpcore_priv_read, | |
246 | + mpcore_priv_read, | |
247 | + mpcore_priv_read | |
248 | +}; | |
249 | + | |
250 | +static CPUWriteMemoryFunc *mpcore_priv_writefn[] = { | |
251 | + mpcore_priv_write, | |
252 | + mpcore_priv_write, | |
253 | + mpcore_priv_write | |
254 | +}; | |
255 | + | |
256 | + | |
257 | +static qemu_irq *mpcore_priv_init(uint32_t base, qemu_irq *pic_irq) | |
258 | +{ | |
259 | + mpcore_priv_state *s; | |
260 | + int iomemtype; | |
261 | + int i; | |
262 | + | |
263 | + s = (mpcore_priv_state *)qemu_mallocz(sizeof(mpcore_priv_state)); | |
264 | + if (!s) | |
265 | + return NULL; | |
266 | + s->gic = gic_init(base, pic_irq); | |
267 | + if (!s->gic) | |
268 | + return NULL; | |
269 | + iomemtype = cpu_register_io_memory(0, mpcore_priv_readfn, | |
270 | + mpcore_priv_writefn, s); | |
271 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
272 | + for (i = 0; i < 8; i++) { | |
273 | + mpcore_timer_init(s, &s->timer[i], i); | |
274 | + } | |
275 | + return s->gic->in; | |
276 | +} | |
277 | + | |
278 | +/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ | |
279 | + controllers. The output of these, plus some of the raw input lines | |
280 | + are fed into a single SMP-aware interrupt controller on the CPU. */ | |
281 | +typedef struct { | |
282 | + qemu_irq *cpuic; | |
283 | + qemu_irq *rvic[4]; | |
284 | +} mpcore_rirq_state; | |
285 | + | |
286 | +/* Map baseboard IRQs onto CPU IRQ lines. */ | |
287 | +static const int mpcore_irq_map[32] = { | |
288 | + -1, -1, -1, -1, 1, 2, -1, -1, | |
289 | + -1, -1, 6, -1, 4, 5, -1, -1, | |
290 | + -1, 14, 15, 0, 7, 8, -1, -1, | |
291 | + -1, -1, -1, -1, 9, 3, -1, -1, | |
292 | +}; | |
293 | + | |
294 | +static void mpcore_rirq_set_irq(void *opaque, int irq, int level) | |
295 | +{ | |
296 | + mpcore_rirq_state *s = (mpcore_rirq_state *)opaque; | |
297 | + int i; | |
298 | + | |
299 | + for (i = 0; i < 4; i++) { | |
300 | + qemu_set_irq(s->rvic[i][irq], level); | |
301 | + } | |
302 | + if (irq < 32) { | |
303 | + irq = mpcore_irq_map[irq]; | |
304 | + if (irq >= 0) { | |
305 | + qemu_set_irq(s->cpuic[irq], level); | |
306 | + } | |
307 | + } | |
308 | +} | |
309 | + | |
310 | +qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq) | |
311 | +{ | |
312 | + mpcore_rirq_state *s; | |
313 | + int n; | |
314 | + | |
315 | + /* ??? IRQ routing is hardcoded to "normal" mode. */ | |
316 | + s = qemu_mallocz(sizeof(mpcore_rirq_state)); | |
317 | + s->cpuic = mpcore_priv_init(MPCORE_PRIV_BASE, cpu_irq); | |
318 | + for (n = 0; n < 4; n++) { | |
319 | + s->rvic[n] = realview_gic_init(0x10040000 + n * 0x10000, | |
320 | + s->cpuic[10 + n]); | |
321 | + } | |
322 | + return qemu_allocate_irqs(mpcore_rirq_set_irq, s, 64); | |
323 | +} | ... | ... |
hw/pl011.c
... | ... | @@ -28,6 +28,7 @@ typedef struct { |
28 | 28 | int read_trigger; |
29 | 29 | CharDriverState *chr; |
30 | 30 | qemu_irq irq; |
31 | + enum pl011_type type; | |
31 | 32 | } pl011_state; |
32 | 33 | |
33 | 34 | #define PL011_INT_TX 0x20 |
... | ... | @@ -38,8 +39,10 @@ typedef struct { |
38 | 39 | #define PL011_FLAG_TXFF 0x20 |
39 | 40 | #define PL011_FLAG_RXFE 0x10 |
40 | 41 | |
41 | -static const unsigned char pl011_id[] = | |
42 | -{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | |
42 | +static const unsigned char pl011_id[2][8] = { | |
43 | + { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */ | |
44 | + { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */ | |
45 | +}; | |
43 | 46 | |
44 | 47 | static void pl011_update(pl011_state *s) |
45 | 48 | { |
... | ... | @@ -56,7 +59,7 @@ static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) |
56 | 59 | |
57 | 60 | offset -= s->base; |
58 | 61 | if (offset >= 0xfe0 && offset < 0x1000) { |
59 | - return pl011_id[(offset - 0xfe0) >> 2]; | |
62 | + return pl011_id[s->type][(offset - 0xfe0) >> 2]; | |
60 | 63 | } |
61 | 64 | switch (offset >> 2) { |
62 | 65 | case 0: /* UARTDR */ |
... | ... | @@ -137,6 +140,9 @@ static void pl011_write(void *opaque, target_phys_addr_t offset, |
137 | 140 | case 1: /* UARTCR */ |
138 | 141 | s->cr = value; |
139 | 142 | break; |
143 | + case 6: /* UARTFR */ | |
144 | + /* Writes to Flag register are ignored. */ | |
145 | + break; | |
140 | 146 | case 8: /* UARTUARTILPR */ |
141 | 147 | s->ilpr = value; |
142 | 148 | break; |
... | ... | @@ -224,7 +230,7 @@ static CPUWriteMemoryFunc *pl011_writefn[] = { |
224 | 230 | }; |
225 | 231 | |
226 | 232 | void pl011_init(uint32_t base, qemu_irq irq, |
227 | - CharDriverState *chr) | |
233 | + CharDriverState *chr, enum pl011_type type) | |
228 | 234 | { |
229 | 235 | int iomemtype; |
230 | 236 | pl011_state *s; |
... | ... | @@ -235,6 +241,7 @@ void pl011_init(uint32_t base, qemu_irq irq, |
235 | 241 | cpu_register_physical_memory(base, 0x00001000, iomemtype); |
236 | 242 | s->base = base; |
237 | 243 | s->irq = irq; |
244 | + s->type = type; | |
238 | 245 | s->chr = chr; |
239 | 246 | s->read_trigger = 1; |
240 | 247 | s->ifl = 0x12; | ... | ... |
hw/pl022.c
0 โ 100644
1 | +/* | |
2 | + * Arm PrimeCell PL022 Synchronous Serial Port | |
3 | + * | |
4 | + * Copyright (c) 2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | + | |
12 | +//#define DEBUG_PL022 1 | |
13 | + | |
14 | +#ifdef DEBUG_PL022 | |
15 | +#define DPRINTF(fmt, args...) \ | |
16 | +do { printf("pl022: " fmt , ##args); } while (0) | |
17 | +#define BADF(fmt, args...) \ | |
18 | +do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0) | |
19 | +#else | |
20 | +#define DPRINTF(fmt, args...) do {} while(0) | |
21 | +#define BADF(fmt, args...) \ | |
22 | +do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0) | |
23 | +#endif | |
24 | + | |
25 | +#define PL022_CR1_LBM 0x01 | |
26 | +#define PL022_CR1_SSE 0x02 | |
27 | +#define PL022_CR1_MS 0x04 | |
28 | +#define PL022_CR1_SDO 0x08 | |
29 | + | |
30 | +#define PL022_SR_TFE 0x01 | |
31 | +#define PL022_SR_TNF 0x02 | |
32 | +#define PL022_SR_RNE 0x04 | |
33 | +#define PL022_SR_RFF 0x08 | |
34 | +#define PL022_SR_BSY 0x10 | |
35 | + | |
36 | +#define PL022_INT_ROR 0x01 | |
37 | +#define PL022_INT_RT 0x04 | |
38 | +#define PL022_INT_RX 0x04 | |
39 | +#define PL022_INT_TX 0x08 | |
40 | + | |
41 | +typedef struct { | |
42 | + uint32_t base; | |
43 | + uint32_t cr0; | |
44 | + uint32_t cr1; | |
45 | + uint32_t bitmask; | |
46 | + uint32_t sr; | |
47 | + uint32_t cpsr; | |
48 | + uint32_t is; | |
49 | + uint32_t im; | |
50 | + /* The FIFO head points to the next empty entry. */ | |
51 | + int tx_fifo_head; | |
52 | + int rx_fifo_head; | |
53 | + int tx_fifo_len; | |
54 | + int rx_fifo_len; | |
55 | + uint16_t tx_fifo[8]; | |
56 | + uint16_t rx_fifo[8]; | |
57 | + qemu_irq irq; | |
58 | + int (*xfer_cb)(void *, int); | |
59 | + void *opaque; | |
60 | +} pl022_state; | |
61 | + | |
62 | +static const unsigned char pl022_id[8] = | |
63 | + { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | |
64 | + | |
65 | +static void pl022_update(pl022_state *s) | |
66 | +{ | |
67 | + s->sr = 0; | |
68 | + if (s->tx_fifo_len == 0) | |
69 | + s->sr |= PL022_SR_TFE; | |
70 | + if (s->tx_fifo_len != 8) | |
71 | + s->sr |= PL022_SR_TNF; | |
72 | + if (s->rx_fifo_len != 0) | |
73 | + s->sr |= PL022_SR_RNE; | |
74 | + if (s->rx_fifo_len == 8) | |
75 | + s->sr |= PL022_SR_RFF; | |
76 | + if (s->tx_fifo_len) | |
77 | + s->sr |= PL022_SR_BSY; | |
78 | + s->is = 0; | |
79 | + if (s->rx_fifo_len >= 4) | |
80 | + s->is |= PL022_INT_RX; | |
81 | + if (s->tx_fifo_len <= 4) | |
82 | + s->is |= PL022_INT_TX; | |
83 | + | |
84 | + qemu_set_irq(s->irq, (s->is & s->im) != 0); | |
85 | +} | |
86 | + | |
87 | +static void pl022_xfer(pl022_state *s) | |
88 | +{ | |
89 | + int i; | |
90 | + int o; | |
91 | + int val; | |
92 | + | |
93 | + if ((s->cr1 & PL022_CR1_SSE) == 0) { | |
94 | + pl022_update(s); | |
95 | + DPRINTF("Disabled\n"); | |
96 | + return; | |
97 | + } | |
98 | + | |
99 | + DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len); | |
100 | + i = (s->tx_fifo_head - s->tx_fifo_len) & 7; | |
101 | + o = s->rx_fifo_head; | |
102 | + /* ??? We do not emulate the line speed. | |
103 | + This may break some applications. The are two problematic cases: | |
104 | + (a) A driver feeds data into the TX FIFO until it is full, | |
105 | + and only then drains the RX FIFO. On real hardware the CPU can | |
106 | + feed data fast enough that the RX fifo never gets chance to overflow. | |
107 | + (b) A driver transmits data, deliberately allowing the RX FIFO to | |
108 | + overflow because it ignores the RX data anyway. | |
109 | + | |
110 | + We choose to support (a) by stalling the transmit engine if it would | |
111 | + cause the RX FIFO to overflow. In practice much transmit-only code | |
112 | + falls into (a) because it flushes the RX FIFO to determine when | |
113 | + the transfer has completed. */ | |
114 | + while (s->tx_fifo_len && s->rx_fifo_len < 8) { | |
115 | + DPRINTF("xfer\n"); | |
116 | + val = s->tx_fifo[i]; | |
117 | + if (s->cr1 & PL022_CR1_LBM) { | |
118 | + /* Loopback mode. */ | |
119 | + } else if (s->xfer_cb) { | |
120 | + val = s->xfer_cb(s->opaque, val); | |
121 | + } else { | |
122 | + val = 0; | |
123 | + } | |
124 | + s->rx_fifo[o] = val & s->bitmask; | |
125 | + i = (i + 1) & 7; | |
126 | + o = (o + 1) & 7; | |
127 | + s->tx_fifo_len--; | |
128 | + s->rx_fifo_len++; | |
129 | + } | |
130 | + s->rx_fifo_head = o; | |
131 | + pl022_update(s); | |
132 | +} | |
133 | + | |
134 | +static uint32_t pl022_read(void *opaque, target_phys_addr_t offset) | |
135 | +{ | |
136 | + pl022_state *s = (pl022_state *)opaque; | |
137 | + int val; | |
138 | + | |
139 | + offset -= s->base; | |
140 | + if (offset >= 0xfe0 && offset < 0x1000) { | |
141 | + return pl022_id[(offset - 0xfe0) >> 2]; | |
142 | + } | |
143 | + switch (offset) { | |
144 | + case 0x00: /* CR0 */ | |
145 | + return s->cr0; | |
146 | + case 0x04: /* CR1 */ | |
147 | + return s->cr1; | |
148 | + case 0x08: /* DR */ | |
149 | + if (s->rx_fifo_len) { | |
150 | + val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7]; | |
151 | + DPRINTF("RX %02x\n", val); | |
152 | + s->rx_fifo_len--; | |
153 | + pl022_xfer(s); | |
154 | + } else { | |
155 | + val = 0; | |
156 | + } | |
157 | + return val; | |
158 | + case 0x0c: /* SR */ | |
159 | + return s->sr; | |
160 | + case 0x10: /* CPSR */ | |
161 | + return s->cpsr; | |
162 | + case 0x14: /* IMSC */ | |
163 | + return s->im; | |
164 | + case 0x18: /* RIS */ | |
165 | + return s->is; | |
166 | + case 0x1c: /* MIS */ | |
167 | + return s->im & s->is; | |
168 | + case 0x20: /* DMACR */ | |
169 | + /* Not implemented. */ | |
170 | + return 0; | |
171 | + default: | |
172 | + cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n", | |
173 | + (int)offset); | |
174 | + return 0; | |
175 | + } | |
176 | +} | |
177 | + | |
178 | +static void pl022_write(void *opaque, target_phys_addr_t offset, | |
179 | + uint32_t value) | |
180 | +{ | |
181 | + pl022_state *s = (pl022_state *)opaque; | |
182 | + | |
183 | + offset -= s->base; | |
184 | + switch (offset) { | |
185 | + case 0x00: /* CR0 */ | |
186 | + s->cr0 = value; | |
187 | + /* Clock rate and format are ignored. */ | |
188 | + s->bitmask = (1 << ((value & 15) + 1)) - 1; | |
189 | + break; | |
190 | + case 0x04: /* CR1 */ | |
191 | + s->cr1 = value; | |
192 | + if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE)) | |
193 | + == (PL022_CR1_MS | PL022_CR1_SSE)) { | |
194 | + BADF("SPI slave mode not implemented\n"); | |
195 | + } | |
196 | + pl022_xfer(s); | |
197 | + break; | |
198 | + case 0x08: /* DR */ | |
199 | + if (s->tx_fifo_len < 8) { | |
200 | + DPRINTF("TX %02x\n", value); | |
201 | + s->tx_fifo[s->tx_fifo_head] = value & s->bitmask; | |
202 | + s->tx_fifo_head = (s->tx_fifo_head + 1) & 7; | |
203 | + s->tx_fifo_len++; | |
204 | + pl022_xfer(s); | |
205 | + } | |
206 | + break; | |
207 | + case 0x10: /* CPSR */ | |
208 | + /* Prescaler. Ignored. */ | |
209 | + s->cpsr = value & 0xff; | |
210 | + break; | |
211 | + case 0x14: /* IMSC */ | |
212 | + s->im = value; | |
213 | + pl022_update(s); | |
214 | + break; | |
215 | + case 0x20: /* DMACR */ | |
216 | + if (value) | |
217 | + cpu_abort (cpu_single_env, "pl022: DMA not implemented\n"); | |
218 | + break; | |
219 | + default: | |
220 | + cpu_abort (cpu_single_env, "pl022_write: Bad offset %x\n", | |
221 | + (int)offset); | |
222 | + } | |
223 | +} | |
224 | + | |
225 | +static void pl022_reset(pl022_state *s) | |
226 | +{ | |
227 | + s->rx_fifo_len = 0; | |
228 | + s->tx_fifo_len = 0; | |
229 | + s->im = 0; | |
230 | + s->is = PL022_INT_TX; | |
231 | + s->sr = PL022_SR_TFE | PL022_SR_TNF; | |
232 | +} | |
233 | + | |
234 | +static CPUReadMemoryFunc *pl022_readfn[] = { | |
235 | + pl022_read, | |
236 | + pl022_read, | |
237 | + pl022_read | |
238 | +}; | |
239 | + | |
240 | +static CPUWriteMemoryFunc *pl022_writefn[] = { | |
241 | + pl022_write, | |
242 | + pl022_write, | |
243 | + pl022_write | |
244 | +}; | |
245 | + | |
246 | +void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int), | |
247 | + void * opaque) | |
248 | +{ | |
249 | + int iomemtype; | |
250 | + pl022_state *s; | |
251 | + | |
252 | + s = (pl022_state *)qemu_mallocz(sizeof(pl022_state)); | |
253 | + iomemtype = cpu_register_io_memory(0, pl022_readfn, | |
254 | + pl022_writefn, s); | |
255 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
256 | + s->base = base; | |
257 | + s->irq = irq; | |
258 | + s->xfer_cb = xfer_cb; | |
259 | + s->opaque = opaque; | |
260 | + pl022_reset(s); | |
261 | + /* ??? Save/restore. */ | |
262 | +} | |
263 | + | |
264 | + | ... | ... |
hw/pl061.c
0 โ 100644
1 | +/* | |
2 | + * Arm PrimeCell PL061 General Purpose IO with additional | |
3 | + * Luminary Micro Stellaris bits. | |
4 | + * | |
5 | + * Copyright (c) 2007 CodeSourcery. | |
6 | + * Written by Paul Brook | |
7 | + * | |
8 | + * This code is licenced under the GPL. | |
9 | + */ | |
10 | + | |
11 | +#include "vl.h" | |
12 | + | |
13 | +//#define DEBUG_PL061 1 | |
14 | + | |
15 | +#ifdef DEBUG_PL061 | |
16 | +#define DPRINTF(fmt, args...) \ | |
17 | +do { printf("pl061: " fmt , ##args); } while (0) | |
18 | +#define BADF(fmt, args...) \ | |
19 | +do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0) | |
20 | +#else | |
21 | +#define DPRINTF(fmt, args...) do {} while(0) | |
22 | +#define BADF(fmt, args...) \ | |
23 | +do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0) | |
24 | +#endif | |
25 | + | |
26 | +static const uint8_t pl061_id[12] = | |
27 | + { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; | |
28 | + | |
29 | +typedef struct { | |
30 | + uint32_t base; | |
31 | + int locked; | |
32 | + uint8_t data; | |
33 | + uint8_t old_data; | |
34 | + uint8_t dir; | |
35 | + uint8_t isense; | |
36 | + uint8_t ibe; | |
37 | + uint8_t iev; | |
38 | + uint8_t im; | |
39 | + uint8_t istate; | |
40 | + uint8_t afsel; | |
41 | + uint8_t dr2r; | |
42 | + uint8_t dr4r; | |
43 | + uint8_t dr8r; | |
44 | + uint8_t odr; | |
45 | + uint8_t pur; | |
46 | + uint8_t pdr; | |
47 | + uint8_t slr; | |
48 | + uint8_t den; | |
49 | + uint8_t cr; | |
50 | + qemu_irq irq; | |
51 | + qemu_irq out[8]; | |
52 | +} pl061_state; | |
53 | + | |
54 | +static void pl061_update(pl061_state *s) | |
55 | +{ | |
56 | + uint8_t changed; | |
57 | + uint8_t mask; | |
58 | + int i; | |
59 | + | |
60 | + changed = s->old_data ^ s->data; | |
61 | + if (!changed) | |
62 | + return; | |
63 | + | |
64 | + s->old_data = s->data; | |
65 | + for (i = 0; i < 8; i++) { | |
66 | + mask = 1 << i; | |
67 | + if ((changed & mask & s->dir) && s->out) { | |
68 | + DPRINTF("Set output %d = %d\n", i, (s->data & mask) != 0); | |
69 | + qemu_set_irq(s->out[i], (s->data & mask) != 0); | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + /* FIXME: Implement input interrupts. */ | |
74 | +} | |
75 | + | |
76 | +static uint32_t pl061_read(void *opaque, target_phys_addr_t offset) | |
77 | +{ | |
78 | + pl061_state *s = (pl061_state *)opaque; | |
79 | + | |
80 | + offset -= s->base; | |
81 | + if (offset >= 0xfd0 && offset < 0x1000) { | |
82 | + return pl061_id[(offset - 0xfd0) >> 2]; | |
83 | + } | |
84 | + if (offset < 0x400) { | |
85 | + return s->data & (offset >> 2); | |
86 | + } | |
87 | + switch (offset) { | |
88 | + case 0x400: /* Direction */ | |
89 | + return s->dir; | |
90 | + case 0x404: /* Interrupt sense */ | |
91 | + return s->isense; | |
92 | + case 0x408: /* Interrupt both edges */ | |
93 | + return s->ibe; | |
94 | + case 0x40c: /* Interupt event */ | |
95 | + return s->iev; | |
96 | + case 0x410: /* Interrupt mask */ | |
97 | + return s->im; | |
98 | + case 0x414: /* Raw interrupt status */ | |
99 | + return s->istate; | |
100 | + case 0x418: /* Masked interrupt status */ | |
101 | + return s->istate | s->im; | |
102 | + case 0x420: /* Alternate function select */ | |
103 | + return s->afsel; | |
104 | + case 0x500: /* 2mA drive */ | |
105 | + return s->dr2r; | |
106 | + case 0x504: /* 4mA drive */ | |
107 | + return s->dr4r; | |
108 | + case 0x508: /* 8mA drive */ | |
109 | + return s->dr8r; | |
110 | + case 0x50c: /* Open drain */ | |
111 | + return s->odr; | |
112 | + case 0x510: /* Pull-up */ | |
113 | + return s->pur; | |
114 | + case 0x514: /* Pull-down */ | |
115 | + return s->pdr; | |
116 | + case 0x518: /* Slew rate control */ | |
117 | + return s->slr; | |
118 | + case 0x51c: /* Digital enable */ | |
119 | + return s->den; | |
120 | + case 0x520: /* Lock */ | |
121 | + return s->locked; | |
122 | + case 0x524: /* Commit */ | |
123 | + return s->cr; | |
124 | + default: | |
125 | + cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n", | |
126 | + (int)offset); | |
127 | + return 0; | |
128 | + } | |
129 | +} | |
130 | + | |
131 | +static void pl061_write(void *opaque, target_phys_addr_t offset, | |
132 | + uint32_t value) | |
133 | +{ | |
134 | + pl061_state *s = (pl061_state *)opaque; | |
135 | + uint8_t mask; | |
136 | + | |
137 | + offset -= s->base; | |
138 | + if (offset < 0x400) { | |
139 | + mask = (offset >> 2) & s->dir; | |
140 | + s->data = (s->data & ~mask) | (value & mask); | |
141 | + pl061_update(s); | |
142 | + return; | |
143 | + } | |
144 | + switch (offset) { | |
145 | + case 0x400: /* Direction */ | |
146 | + s->dir = value; | |
147 | + break; | |
148 | + case 0x404: /* Interrupt sense */ | |
149 | + s->isense = value; | |
150 | + break; | |
151 | + case 0x408: /* Interrupt both edges */ | |
152 | + s->ibe = value; | |
153 | + break; | |
154 | + case 0x40c: /* Interupt event */ | |
155 | + s->iev = value; | |
156 | + break; | |
157 | + case 0x410: /* Interrupt mask */ | |
158 | + s->im = value; | |
159 | + break; | |
160 | + case 0x41c: /* Interrupt clear */ | |
161 | + s->istate &= ~value; | |
162 | + break; | |
163 | + case 0x420: /* Alternate function select */ | |
164 | + mask = s->cr; | |
165 | + s->afsel = (s->afsel & ~mask) | (value & mask); | |
166 | + break; | |
167 | + case 0x500: /* 2mA drive */ | |
168 | + s->dr2r = value; | |
169 | + break; | |
170 | + case 0x504: /* 4mA drive */ | |
171 | + s->dr4r = value; | |
172 | + break; | |
173 | + case 0x508: /* 8mA drive */ | |
174 | + s->dr8r = value; | |
175 | + break; | |
176 | + case 0x50c: /* Open drain */ | |
177 | + s->odr = value; | |
178 | + break; | |
179 | + case 0x510: /* Pull-up */ | |
180 | + s->pur = value; | |
181 | + break; | |
182 | + case 0x514: /* Pull-down */ | |
183 | + s->pdr = value; | |
184 | + break; | |
185 | + case 0x518: /* Slew rate control */ | |
186 | + s->slr = value; | |
187 | + break; | |
188 | + case 0x51c: /* Digital enable */ | |
189 | + s->den = value; | |
190 | + break; | |
191 | + case 0x520: /* Lock */ | |
192 | + s->locked = (value != 0xacce551); | |
193 | + break; | |
194 | + case 0x524: /* Commit */ | |
195 | + if (!s->locked) | |
196 | + s->cr = value; | |
197 | + break; | |
198 | + default: | |
199 | + cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n", | |
200 | + (int)offset); | |
201 | + } | |
202 | + pl061_update(s); | |
203 | +} | |
204 | + | |
205 | +static void pl061_reset(pl061_state *s) | |
206 | +{ | |
207 | + s->locked = 1; | |
208 | + s->cr = 0xff; | |
209 | +} | |
210 | + | |
211 | +void pl061_set_irq(void * opaque, int irq, int level) | |
212 | +{ | |
213 | + pl061_state *s = (pl061_state *)opaque; | |
214 | + uint8_t mask; | |
215 | + | |
216 | + mask = 1 << irq; | |
217 | + if ((s->dir & mask) == 0) { | |
218 | + s->data &= ~mask; | |
219 | + if (level) | |
220 | + s->data |= mask; | |
221 | + pl061_update(s); | |
222 | + } | |
223 | +} | |
224 | + | |
225 | +static CPUReadMemoryFunc *pl061_readfn[] = { | |
226 | + pl061_read, | |
227 | + pl061_read, | |
228 | + pl061_read | |
229 | +}; | |
230 | + | |
231 | +static CPUWriteMemoryFunc *pl061_writefn[] = { | |
232 | + pl061_write, | |
233 | + pl061_write, | |
234 | + pl061_write | |
235 | +}; | |
236 | + | |
237 | +/* Returns an array of inputs. */ | |
238 | +qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out) | |
239 | +{ | |
240 | + int iomemtype; | |
241 | + pl061_state *s; | |
242 | + | |
243 | + s = (pl061_state *)qemu_mallocz(sizeof(pl061_state)); | |
244 | + iomemtype = cpu_register_io_memory(0, pl061_readfn, | |
245 | + pl061_writefn, s); | |
246 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
247 | + s->base = base; | |
248 | + s->irq = irq; | |
249 | + pl061_reset(s); | |
250 | + if (out) | |
251 | + *out = s->out; | |
252 | + | |
253 | + /* ??? Save/restore. */ | |
254 | + return qemu_allocate_irqs(pl061_set_irq, s, 8); | |
255 | +} | |
256 | + | ... | ... |
hw/pxa2xx.c
... | ... | @@ -297,7 +297,7 @@ static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm, |
297 | 297 | ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; |
298 | 298 | s->env->cp15.c1_sys = 0; |
299 | 299 | s->env->cp15.c1_coproc = 0; |
300 | - s->env->cp15.c2_base = 0; | |
300 | + s->env->cp15.c2_base0 = 0; | |
301 | 301 | s->env->cp15.c3 = 0; |
302 | 302 | s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ |
303 | 303 | s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ |
... | ... | @@ -2031,7 +2031,8 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size, |
2031 | 2031 | fprintf(stderr, "Unable to find CPU definition\n"); |
2032 | 2032 | exit(1); |
2033 | 2033 | } |
2034 | - register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env); | |
2034 | + register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load, | |
2035 | + s->env); | |
2035 | 2036 | |
2036 | 2037 | /* SDRAM & Internal Memory Storage */ |
2037 | 2038 | cpu_register_physical_memory(PXA2XX_SDRAM_BASE, |
... | ... | @@ -2145,7 +2146,8 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size, |
2145 | 2146 | fprintf(stderr, "Unable to find CPU definition\n"); |
2146 | 2147 | exit(1); |
2147 | 2148 | } |
2148 | - register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env); | |
2149 | + register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load, | |
2150 | + s->env); | |
2149 | 2151 | |
2150 | 2152 | /* SDRAM & Internal Memory Storage */ |
2151 | 2153 | cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size, | ... | ... |
hw/realview.c
... | ... | @@ -25,13 +25,32 @@ static void realview_init(int ram_size, int vga_ram_size, |
25 | 25 | NICInfo *nd; |
26 | 26 | int n; |
27 | 27 | int done_smc = 0; |
28 | + qemu_irq cpu_irq[4]; | |
29 | + int ncpu; | |
28 | 30 | |
29 | 31 | if (!cpu_model) |
30 | 32 | cpu_model = "arm926"; |
31 | - env = cpu_init(cpu_model); | |
32 | - if (!env) { | |
33 | - fprintf(stderr, "Unable to find CPU definition\n"); | |
34 | - exit(1); | |
33 | + /* FIXME: obey smp_cpus. */ | |
34 | + if (strcmp(cpu_model, "arm11mpcore") == 0) { | |
35 | + ncpu = 4; | |
36 | + } else { | |
37 | + ncpu = 1; | |
38 | + } | |
39 | + | |
40 | + for (n = 0; n < ncpu; n++) { | |
41 | + env = cpu_init(cpu_model); | |
42 | + if (!env) { | |
43 | + fprintf(stderr, "Unable to find CPU definition\n"); | |
44 | + exit(1); | |
45 | + } | |
46 | + pic = arm_pic_init_cpu(env); | |
47 | + cpu_irq[n] = pic[ARM_PIC_CPU_IRQ]; | |
48 | + if (n > 0) { | |
49 | + /* Set entry point for secondary CPUs. This assumes we're using | |
50 | + the init code from arm_boot.c. Real hardware resets all CPUs | |
51 | + the same. */ | |
52 | + env->regs[15] = 0x80000000; | |
53 | + } | |
35 | 54 | } |
36 | 55 | |
37 | 56 | /* ??? RAM shoud repeat to fill physical memory space. */ |
... | ... | @@ -39,18 +58,23 @@ static void realview_init(int ram_size, int vga_ram_size, |
39 | 58 | cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); |
40 | 59 | |
41 | 60 | arm_sysctl_init(0x10000000, 0xc1400400); |
42 | - pic = arm_pic_init_cpu(env); | |
43 | - /* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3 | |
44 | - is nIRQ (there are inconsistencies). However Linux 2.6.17 expects | |
45 | - GIC1 to be nIRQ and ignores all the others, so do that for now. */ | |
46 | - pic = arm_gic_init(0x10040000, pic[ARM_PIC_CPU_IRQ]); | |
61 | + | |
62 | + if (ncpu == 1) { | |
63 | + /* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3 | |
64 | + is nIRQ (there are inconsistencies). However Linux 2.6.17 expects | |
65 | + GIC1 to be nIRQ and ignores all the others, so do that for now. */ | |
66 | + pic = realview_gic_init(0x10040000, cpu_irq[0]); | |
67 | + } else { | |
68 | + pic = mpcore_irq_init(cpu_irq); | |
69 | + } | |
70 | + | |
47 | 71 | pl050_init(0x10006000, pic[20], 0); |
48 | 72 | pl050_init(0x10007000, pic[21], 1); |
49 | 73 | |
50 | - pl011_init(0x10009000, pic[12], serial_hds[0]); | |
51 | - pl011_init(0x1000a000, pic[13], serial_hds[1]); | |
52 | - pl011_init(0x1000b000, pic[14], serial_hds[2]); | |
53 | - pl011_init(0x1000c000, pic[15], serial_hds[3]); | |
74 | + pl011_init(0x10009000, pic[12], serial_hds[0], PL011_ARM); | |
75 | + pl011_init(0x1000a000, pic[13], serial_hds[1], PL011_ARM); | |
76 | + pl011_init(0x1000b000, pic[14], serial_hds[2], PL011_ARM); | |
77 | + pl011_init(0x1000c000, pic[15], serial_hds[3], PL011_ARM); | |
54 | 78 | |
55 | 79 | /* DMA controller is optional, apparently. */ |
56 | 80 | pl080_init(0x10030000, pic[24], 2); |
... | ... | @@ -114,10 +138,10 @@ static void realview_init(int ram_size, int vga_ram_size, |
114 | 138 | /* 0x10019000 PCI controller config. */ |
115 | 139 | /* 0x10020000 CLCD. */ |
116 | 140 | /* 0x10030000 DMA Controller. */ |
117 | - /* 0x10040000 GIC1 (FIQ1). */ | |
118 | - /* 0x10050000 GIC2 (IRQ1). */ | |
119 | - /* 0x10060000 GIC3 (FIQ2). */ | |
120 | - /* 0x10070000 GIC4 (IRQ2). */ | |
141 | + /* 0x10040000 GIC1. */ | |
142 | + /* 0x10050000 GIC2. */ | |
143 | + /* 0x10060000 GIC3. */ | |
144 | + /* 0x10070000 GIC4. */ | |
121 | 145 | /* 0x10080000 SMC. */ |
122 | 146 | /* 0x40000000 NOR flash. */ |
123 | 147 | /* 0x44000000 DoC flash. */ |
... | ... | @@ -137,8 +161,14 @@ static void realview_init(int ram_size, int vga_ram_size, |
137 | 161 | /* 0x68000000 PCI mem 1. */ |
138 | 162 | /* 0x6c000000 PCI mem 2. */ |
139 | 163 | |
140 | - arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline, | |
164 | + arm_load_kernel(first_cpu, ram_size, kernel_filename, kernel_cmdline, | |
141 | 165 | initrd_filename, 0x33b, 0x0); |
166 | + | |
167 | + /* ??? Hack to map an additional page of ram for the secondary CPU | |
168 | + startup code. I guess this works on real hardware because the | |
169 | + BootROM happens to be in ROM/flash or in memory that isn't clobbered | |
170 | + until after Linux boots the secondary CPUs. */ | |
171 | + cpu_register_physical_memory(0x80000000, 0x1000, IO_MEM_RAM + ram_size); | |
142 | 172 | } |
143 | 173 | |
144 | 174 | QEMUMachine realview_machine = { | ... | ... |
hw/realview_gic.c
0 โ 100644
1 | +/* | |
2 | + * ARM RealView Emulation Baseboard Interrupt Controller | |
3 | + * | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | +#include "arm_pic.h" | |
12 | + | |
13 | +#define GIC_NIRQ 96 | |
14 | +#define NCPU 1 | |
15 | + | |
16 | +/* Only a single "CPU" interface is present. */ | |
17 | +static inline int | |
18 | +gic_get_current_cpu(void) | |
19 | +{ | |
20 | + return 0; | |
21 | +} | |
22 | + | |
23 | +#include "arm_gic.c" | |
24 | + | |
25 | +static uint32_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset) | |
26 | +{ | |
27 | + gic_state *s = (gic_state *)opaque; | |
28 | + offset -= s->base; | |
29 | + return gic_cpu_read(s, gic_get_current_cpu(), offset); | |
30 | +} | |
31 | + | |
32 | +static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset, | |
33 | + uint32_t value) | |
34 | +{ | |
35 | + gic_state *s = (gic_state *)opaque; | |
36 | + offset -= s->base; | |
37 | + gic_cpu_write(s, gic_get_current_cpu(), offset, value); | |
38 | +} | |
39 | + | |
40 | +static CPUReadMemoryFunc *realview_gic_cpu_readfn[] = { | |
41 | + realview_gic_cpu_read, | |
42 | + realview_gic_cpu_read, | |
43 | + realview_gic_cpu_read | |
44 | +}; | |
45 | + | |
46 | +static CPUWriteMemoryFunc *realview_gic_cpu_writefn[] = { | |
47 | + realview_gic_cpu_write, | |
48 | + realview_gic_cpu_write, | |
49 | + realview_gic_cpu_write | |
50 | +}; | |
51 | + | |
52 | +qemu_irq *realview_gic_init(uint32_t base, qemu_irq parent_irq) | |
53 | +{ | |
54 | + gic_state *s; | |
55 | + int iomemtype; | |
56 | + | |
57 | + s = gic_init(base, &parent_irq); | |
58 | + if (!s) | |
59 | + return NULL; | |
60 | + iomemtype = cpu_register_io_memory(0, realview_gic_cpu_readfn, | |
61 | + realview_gic_cpu_writefn, s); | |
62 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
63 | + return s->in; | |
64 | +} | ... | ... |
hw/ssd0303.c
0 โ 100644
1 | +/* | |
2 | + * SSD0303 OLED controller with OSRAM Pictiva 96x16 display. | |
3 | + * | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + */ | |
9 | + | |
10 | +/* The controller can support a variety of different displays, but we only | |
11 | + implement one. Most of the commends relating to brightness and geometry | |
12 | + setup are ignored. */ | |
13 | +#include "vl.h" | |
14 | + | |
15 | +//#define DEBUG_SSD0303 1 | |
16 | + | |
17 | +#ifdef DEBUG_SSD0303 | |
18 | +#define DPRINTF(fmt, args...) \ | |
19 | +do { printf("ssd0303: " fmt , ##args); } while (0) | |
20 | +#define BADF(fmt, args...) \ | |
21 | +do { fprintf(stderr, "ssd0303: error: " fmt , ##args); exit(1);} while (0) | |
22 | +#else | |
23 | +#define DPRINTF(fmt, args...) do {} while(0) | |
24 | +#define BADF(fmt, args...) \ | |
25 | +do { fprintf(stderr, "ssd0303: error: " fmt , ##args);} while (0) | |
26 | +#endif | |
27 | + | |
28 | +/* Scaling factor for pixels. */ | |
29 | +#define MAGNIFY 4 | |
30 | + | |
31 | +enum ssd0303_mode | |
32 | +{ | |
33 | + SSD0303_IDLE, | |
34 | + SSD0303_DATA, | |
35 | + SSD0303_CMD | |
36 | +}; | |
37 | + | |
38 | +enum ssd0303_cmd { | |
39 | + SSD0303_CMD_NONE, | |
40 | + SSD0303_CMD_SKIP1 | |
41 | +}; | |
42 | + | |
43 | +typedef struct { | |
44 | + i2c_slave i2c; | |
45 | + DisplayState *ds; | |
46 | + int row; | |
47 | + int col; | |
48 | + int start_line; | |
49 | + int mirror; | |
50 | + int flash; | |
51 | + int enabled; | |
52 | + int inverse; | |
53 | + int redraw; | |
54 | + enum ssd0303_mode mode; | |
55 | + enum ssd0303_cmd cmd_state; | |
56 | + uint8_t framebuffer[132*8]; | |
57 | +} ssd0303_state; | |
58 | + | |
59 | +static int ssd0303_recv(i2c_slave *i2c) | |
60 | +{ | |
61 | + BADF("Reads not implemented\n"); | |
62 | + return -1; | |
63 | +} | |
64 | + | |
65 | +static int ssd0303_send(i2c_slave *i2c, uint8_t data) | |
66 | +{ | |
67 | + ssd0303_state *s = (ssd0303_state *)i2c; | |
68 | + enum ssd0303_cmd old_cmd_state; | |
69 | + switch (s->mode) { | |
70 | + case SSD0303_IDLE: | |
71 | + DPRINTF("byte 0x%02x\n", data); | |
72 | + if (data == 0x80) | |
73 | + s->mode = SSD0303_CMD; | |
74 | + else if (data == 0x40) | |
75 | + s->mode = SSD0303_DATA; | |
76 | + else | |
77 | + BADF("Unexpected byte 0x%x\n", data); | |
78 | + break; | |
79 | + case SSD0303_DATA: | |
80 | + DPRINTF("data 0x%02x\n", data); | |
81 | + if (s->col < 132) { | |
82 | + s->framebuffer[s->col + s->row * 132] = data; | |
83 | + s->col++; | |
84 | + s->redraw = 1; | |
85 | + } | |
86 | + break; | |
87 | + case SSD0303_CMD: | |
88 | + old_cmd_state = s->cmd_state; | |
89 | + s->cmd_state = SSD0303_CMD_NONE; | |
90 | + switch (old_cmd_state) { | |
91 | + case SSD0303_CMD_NONE: | |
92 | + DPRINTF("cmd 0x%02x\n", data); | |
93 | + s->mode = SSD0303_IDLE; | |
94 | + switch (data) { | |
95 | + case 0x00 ... 0x0f: /* Set lower colum address. */ | |
96 | + s->col = (s->col & 0xf0) | (data & 0xf); | |
97 | + break; | |
98 | + case 0x10 ... 0x20: /* Set higher column address. */ | |
99 | + s->col = (s->col & 0x0f) | ((data & 0xf) << 4); | |
100 | + break; | |
101 | + case 0x40 ... 0x7f: /* Set start line. */ | |
102 | + s->start_line = 0; | |
103 | + break; | |
104 | + case 0x81: /* Set contrast (Ignored). */ | |
105 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
106 | + break; | |
107 | + case 0xa0: /* Mirror off. */ | |
108 | + s->mirror = 0; | |
109 | + break; | |
110 | + case 0xa1: /* Mirror off. */ | |
111 | + s->mirror = 1; | |
112 | + break; | |
113 | + case 0xa4: /* Entire display off. */ | |
114 | + s->flash = 0; | |
115 | + break; | |
116 | + case 0xa5: /* Entire display on. */ | |
117 | + s->flash = 1; | |
118 | + break; | |
119 | + case 0xa6: /* Inverse off. */ | |
120 | + s->inverse = 0; | |
121 | + break; | |
122 | + case 0xa7: /* Inverse on. */ | |
123 | + s->inverse = 1; | |
124 | + break; | |
125 | + case 0xa8: /* Set multipled ratio (Ignored). */ | |
126 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
127 | + break; | |
128 | + case 0xad: /* DC-DC power control. */ | |
129 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
130 | + break; | |
131 | + case 0xae: /* Display off. */ | |
132 | + s->enabled = 0; | |
133 | + break; | |
134 | + case 0xaf: /* Display on. */ | |
135 | + s->enabled = 1; | |
136 | + break; | |
137 | + case 0xb0 ... 0xbf: /* Set Page address. */ | |
138 | + s->row = data & 7; | |
139 | + break; | |
140 | + case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */ | |
141 | + break; | |
142 | + case 0xd3: /* Set display offset (Ignored). */ | |
143 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
144 | + break; | |
145 | + case 0xd5: /* Set display clock (Ignored). */ | |
146 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
147 | + break; | |
148 | + case 0xd8: /* Set color and power mode (Ignored). */ | |
149 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
150 | + break; | |
151 | + case 0xd9: /* Set pre-charge period (Ignored). */ | |
152 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
153 | + break; | |
154 | + case 0xda: /* Set COM pin configuration (Ignored). */ | |
155 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
156 | + break; | |
157 | + case 0xdb: /* Set VCOM dselect level (Ignored). */ | |
158 | + s->cmd_state = SSD0303_CMD_SKIP1; | |
159 | + break; | |
160 | + case 0xe3: /* no-op. */ | |
161 | + break; | |
162 | + default: | |
163 | + BADF("Unknown command: 0x%x\n", data); | |
164 | + } | |
165 | + break; | |
166 | + case SSD0303_CMD_SKIP1: | |
167 | + DPRINTF("skip 0x%02x\n", data); | |
168 | + break; | |
169 | + } | |
170 | + break; | |
171 | + } | |
172 | + return 0; | |
173 | +} | |
174 | + | |
175 | +static void ssd0303_event(i2c_slave *i2c, enum i2c_event event) | |
176 | +{ | |
177 | + ssd0303_state *s = (ssd0303_state *)i2c; | |
178 | + switch (event) { | |
179 | + case I2C_FINISH: | |
180 | + s->mode = SSD0303_IDLE; | |
181 | + break; | |
182 | + case I2C_START_RECV: | |
183 | + case I2C_START_SEND: | |
184 | + case I2C_NACK: | |
185 | + /* Nothing to do. */ | |
186 | + break; | |
187 | + } | |
188 | +} | |
189 | + | |
190 | +static void ssd0303_update_display(void *opaque) | |
191 | +{ | |
192 | + ssd0303_state *s = (ssd0303_state *)opaque; | |
193 | + uint8_t *dest; | |
194 | + uint8_t *src; | |
195 | + int x; | |
196 | + int y; | |
197 | + int line; | |
198 | + char *colors[2]; | |
199 | + char colortab[MAGNIFY * 8]; | |
200 | + int dest_width; | |
201 | + uint8_t mask; | |
202 | + | |
203 | + if (s->redraw) { | |
204 | + switch (s->ds->depth) { | |
205 | + case 0: | |
206 | + return; | |
207 | + case 15: | |
208 | + dest_width = 2; | |
209 | + break; | |
210 | + case 16: | |
211 | + dest_width = 2; | |
212 | + break; | |
213 | + case 24: | |
214 | + dest_width = 3; | |
215 | + break; | |
216 | + case 32: | |
217 | + dest_width = 4; | |
218 | + break; | |
219 | + default: | |
220 | + BADF("Bad color depth\n"); | |
221 | + return; | |
222 | + } | |
223 | + dest_width *= MAGNIFY; | |
224 | + memset(colortab, 0xff, dest_width); | |
225 | + memset(colortab + dest_width, 0, dest_width); | |
226 | + if (s->flash) { | |
227 | + colors[0] = colortab; | |
228 | + colors[1] = colortab; | |
229 | + } else if (s->inverse) { | |
230 | + colors[0] = colortab; | |
231 | + colors[1] = colortab + dest_width; | |
232 | + } else { | |
233 | + colors[0] = colortab + dest_width; | |
234 | + colors[1] = colortab; | |
235 | + } | |
236 | + dest = s->ds->data; | |
237 | + for (y = 0; y < 16; y++) { | |
238 | + line = (y + s->start_line) & 63; | |
239 | + src = s->framebuffer + 132 * (line >> 3) + 36; | |
240 | + mask = 1 << (line & 7); | |
241 | + for (x = 0; x < 96; x++) { | |
242 | + memcpy(dest, colors[(*src & mask) != 0], dest_width); | |
243 | + dest += dest_width; | |
244 | + src++; | |
245 | + } | |
246 | + for (x = 1; x < MAGNIFY; x++) { | |
247 | + memcpy(dest, dest - dest_width * 96, dest_width * 96); | |
248 | + dest += dest_width * 96; | |
249 | + } | |
250 | + } | |
251 | + } | |
252 | + dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY); | |
253 | +} | |
254 | + | |
255 | +static void ssd0303_invalidate_display(void * opaque) | |
256 | +{ | |
257 | + ssd0303_state *s = (ssd0303_state *)opaque; | |
258 | + s->redraw = 1; | |
259 | +} | |
260 | + | |
261 | +void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address) | |
262 | +{ | |
263 | + ssd0303_state *s; | |
264 | + | |
265 | + s = (ssd0303_state *)i2c_slave_init(bus, address, sizeof(ssd0303_state)); | |
266 | + s->ds = ds; | |
267 | + s->i2c.event = ssd0303_event; | |
268 | + s->i2c.recv = ssd0303_recv; | |
269 | + s->i2c.send = ssd0303_send; | |
270 | + graphic_console_init(ds, ssd0303_update_display, ssd0303_invalidate_display, | |
271 | + NULL, s); | |
272 | + dpy_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY); | |
273 | +} | ... | ... |
hw/ssd0323.c
0 โ 100644
1 | +/* | |
2 | + * SSD0323 OLED controller with OSRAM Pictiva 128x64 display. | |
3 | + * | |
4 | + * Copyright (c) 2006-2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + */ | |
9 | + | |
10 | +/* The controller can support a variety of different displays, but we only | |
11 | + implement one. Most of the commends relating to brightness and geometry | |
12 | + setup are ignored. */ | |
13 | +#include "vl.h" | |
14 | + | |
15 | +//#define DEBUG_SSD0323 1 | |
16 | + | |
17 | +#ifdef DEBUG_SSD0323 | |
18 | +#define DPRINTF(fmt, args...) \ | |
19 | +do { printf("ssd0323: " fmt , ##args); } while (0) | |
20 | +#define BADF(fmt, args...) \ | |
21 | +do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0) | |
22 | +#else | |
23 | +#define DPRINTF(fmt, args...) do {} while(0) | |
24 | +#define BADF(fmt, args...) \ | |
25 | +do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0) | |
26 | +#endif | |
27 | + | |
28 | +/* Scaling factor for pixels. */ | |
29 | +#define MAGNIFY 4 | |
30 | + | |
31 | +enum ssd0323_mode | |
32 | +{ | |
33 | + SSD0323_CMD, | |
34 | + SSD0323_DATA | |
35 | +}; | |
36 | + | |
37 | +typedef struct { | |
38 | + DisplayState *ds; | |
39 | + | |
40 | + int cmd_len; | |
41 | + int cmd; | |
42 | + int cmd_data[8]; | |
43 | + int row; | |
44 | + int row_start; | |
45 | + int row_end; | |
46 | + int col; | |
47 | + int col_start; | |
48 | + int col_end; | |
49 | + int redraw; | |
50 | + enum ssd0323_mode mode; | |
51 | + uint8_t framebuffer[128 * 80 / 2]; | |
52 | +} ssd0323_state; | |
53 | + | |
54 | +int ssd0323_xfer_ssi(void *opaque, int data) | |
55 | +{ | |
56 | + ssd0323_state *s = (ssd0323_state *)opaque; | |
57 | + switch (s->mode) { | |
58 | + case SSD0323_DATA: | |
59 | + DPRINTF("data 0x%02x\n", data); | |
60 | + s->framebuffer[s->col + s->row * 64] = data; | |
61 | + s->col++; | |
62 | + if (s->col > s->col_end) { | |
63 | + s->row++; | |
64 | + s->col = s->col_start; | |
65 | + } | |
66 | + if (s->row > s->row_end) { | |
67 | + s->row = s->row_start; | |
68 | + } | |
69 | + s->redraw = 1; | |
70 | + break; | |
71 | + case SSD0323_CMD: | |
72 | + DPRINTF("cmd 0x%02x\n", data); | |
73 | + if (s->cmd_len == 0) { | |
74 | + s->cmd = data; | |
75 | + } else { | |
76 | + s->cmd_data[s->cmd_len - 1] = data; | |
77 | + } | |
78 | + s->cmd_len++; | |
79 | + switch (s->cmd) { | |
80 | +#define DATA(x) if (s->cmd_len <= (x)) return 0 | |
81 | + case 0x15: /* Set column. */ | |
82 | + DATA(2); | |
83 | + s->col_start = s->cmd_data[0] % 64; | |
84 | + s->col_end = s->cmd_data[1] % 64; | |
85 | + break; | |
86 | + case 0x75: /* Set row. */ | |
87 | + DATA(2); | |
88 | + s->row_start = s->cmd_data[0] % 80; | |
89 | + s->row_end = s->cmd_data[1] % 80; | |
90 | + break; | |
91 | + case 0x81: /* Set contrast */ | |
92 | + DATA(1); | |
93 | + break; | |
94 | + case 0x84: case 0x85: case 0x86: /* Max current. */ | |
95 | + DATA(0); | |
96 | + break; | |
97 | + case 0xa0: /* Set remapping. */ | |
98 | + /* FIXME: Implement this. */ | |
99 | + DATA(1); | |
100 | + break; | |
101 | + case 0xa1: /* Set display start line. */ | |
102 | + case 0xa2: /* Set display offset. */ | |
103 | + /* FIXME: Implement these. */ | |
104 | + DATA(1); | |
105 | + break; | |
106 | + case 0xa4: /* Normal mode. */ | |
107 | + case 0xa5: /* All on. */ | |
108 | + case 0xa6: /* All off. */ | |
109 | + case 0xa7: /* Inverse. */ | |
110 | + /* FIXME: Implement these. */ | |
111 | + DATA(0); | |
112 | + break; | |
113 | + case 0xa8: /* Set multiplex ratio. */ | |
114 | + case 0xad: /* Set DC-DC converter. */ | |
115 | + DATA(1); | |
116 | + /* Ignored. Don't care. */ | |
117 | + break; | |
118 | + case 0xae: /* Display off. */ | |
119 | + case 0xaf: /* Display on. */ | |
120 | + DATA(0); | |
121 | + /* TODO: Implement power control. */ | |
122 | + break; | |
123 | + case 0xb1: /* Set phase length. */ | |
124 | + case 0xb2: /* Set row period. */ | |
125 | + case 0xb3: /* Set clock rate. */ | |
126 | + case 0xbc: /* Set precharge. */ | |
127 | + case 0xbe: /* Set VCOMH. */ | |
128 | + case 0xbf: /* Set segment low. */ | |
129 | + DATA(1); | |
130 | + /* Ignored. Don't care. */ | |
131 | + break; | |
132 | + case 0xb8: /* Set grey scale table. */ | |
133 | + /* FIXME: Implement this. */ | |
134 | + DATA(8); | |
135 | + break; | |
136 | + case 0xe3: /* NOP. */ | |
137 | + DATA(0); | |
138 | + break; | |
139 | + default: | |
140 | + BADF("Unknown command: 0x%x\n", data); | |
141 | + } | |
142 | + s->cmd_len = 0; | |
143 | + return 0; | |
144 | + } | |
145 | + return 0; | |
146 | +} | |
147 | + | |
148 | +static void ssd0323_update_display(void *opaque) | |
149 | +{ | |
150 | + ssd0323_state *s = (ssd0323_state *)opaque; | |
151 | + uint8_t *dest; | |
152 | + uint8_t *src; | |
153 | + int x; | |
154 | + int y; | |
155 | + int i; | |
156 | + int line; | |
157 | + char *colors[16]; | |
158 | + char colortab[MAGNIFY * 64]; | |
159 | + char *p; | |
160 | + int dest_width; | |
161 | + | |
162 | + if (s->redraw) { | |
163 | + switch (s->ds->depth) { | |
164 | + case 0: | |
165 | + return; | |
166 | + case 15: | |
167 | + dest_width = 2; | |
168 | + break; | |
169 | + case 16: | |
170 | + dest_width = 2; | |
171 | + break; | |
172 | + case 24: | |
173 | + dest_width = 3; | |
174 | + break; | |
175 | + case 32: | |
176 | + dest_width = 4; | |
177 | + break; | |
178 | + default: | |
179 | + BADF("Bad color depth\n"); | |
180 | + return; | |
181 | + } | |
182 | + p = colortab; | |
183 | + for (i = 0; i < 16; i++) { | |
184 | + int n; | |
185 | + colors[i] = p; | |
186 | + switch (s->ds->depth) { | |
187 | + case 15: | |
188 | + n = i * 2 + (i >> 3); | |
189 | + p[0] = n | (n << 5); | |
190 | + p[1] = (n << 2) | (n >> 3); | |
191 | + break; | |
192 | + case 16: | |
193 | + n = i * 2 + (i >> 3); | |
194 | + p[0] = n | (n << 6) | ((n << 1) & 0x20); | |
195 | + p[1] = (n << 3) | (n >> 2); | |
196 | + break; | |
197 | + case 24: | |
198 | + case 32: | |
199 | + n = (i << 4) | i; | |
200 | + p[0] = p[1] = p[2] = n; | |
201 | + break; | |
202 | + default: | |
203 | + BADF("Bad color depth\n"); | |
204 | + return; | |
205 | + } | |
206 | + p += dest_width; | |
207 | + } | |
208 | + dest = s->ds->data; | |
209 | + for (y = 0; y < 64; y++) { | |
210 | + line = y; | |
211 | + src = s->framebuffer + 64 * line; | |
212 | + for (x = 0; x < 64; x++) { | |
213 | + int val; | |
214 | + val = *src >> 4; | |
215 | + for (i = 0; i < MAGNIFY; i++) { | |
216 | + memcpy(dest, colors[val], dest_width); | |
217 | + dest += dest_width; | |
218 | + } | |
219 | + val = *src & 0xf; | |
220 | + for (i = 0; i < MAGNIFY; i++) { | |
221 | + memcpy(dest, colors[val], dest_width); | |
222 | + dest += dest_width; | |
223 | + } | |
224 | + src++; | |
225 | + } | |
226 | + for (i = 1; i < MAGNIFY; i++) { | |
227 | + memcpy(dest, dest - dest_width * MAGNIFY * 128, | |
228 | + dest_width * 128 * MAGNIFY); | |
229 | + dest += dest_width * 128 * MAGNIFY; | |
230 | + } | |
231 | + } | |
232 | + } | |
233 | + dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY); | |
234 | +} | |
235 | + | |
236 | +static void ssd0323_invalidate_display(void * opaque) | |
237 | +{ | |
238 | + ssd0323_state *s = (ssd0323_state *)opaque; | |
239 | + s->redraw = 1; | |
240 | +} | |
241 | + | |
242 | +/* Command/data input. */ | |
243 | +static void ssd0323_cd(void *opaque, int n, int level) | |
244 | +{ | |
245 | + ssd0323_state *s = (ssd0323_state *)opaque; | |
246 | + DPRINTF("%s mode\n", level ? "Data" : "Command"); | |
247 | + s->mode = level ? SSD0323_DATA : SSD0323_CMD; | |
248 | +} | |
249 | + | |
250 | +void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p) | |
251 | +{ | |
252 | + ssd0323_state *s; | |
253 | + qemu_irq *cmd; | |
254 | + | |
255 | + s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state)); | |
256 | + s->ds = ds; | |
257 | + graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display, | |
258 | + NULL, s); | |
259 | + dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY); | |
260 | + s->col_end = 63; | |
261 | + s->row_end = 79; | |
262 | + | |
263 | + cmd = qemu_allocate_irqs(ssd0323_cd, s, 1); | |
264 | + *cmd_p = *cmd; | |
265 | + | |
266 | + return s; | |
267 | +} | ... | ... |
hw/stellaris.c
0 โ 100644
1 | +/* | |
2 | + * Luminary Micro Stellaris preipherals | |
3 | + * | |
4 | + * Copyright (c) 2006 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the GPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | +#include "arm_pic.h" | |
12 | + | |
13 | +typedef const struct { | |
14 | + const char *name; | |
15 | + uint32_t did0; | |
16 | + uint32_t did1; | |
17 | + uint32_t dc0; | |
18 | + uint32_t dc1; | |
19 | + uint32_t dc2; | |
20 | + uint32_t dc3; | |
21 | + uint32_t dc4; | |
22 | + enum {OLED_I2C, OLED_SSI} oled; | |
23 | +} stellaris_board_info; | |
24 | + | |
25 | +/* General purpose timer module. */ | |
26 | + | |
27 | +/* Multiplication factor to convert from GPTM timer ticks to qemu timer | |
28 | + ticks. */ | |
29 | +static int stellaris_clock_scale; | |
30 | + | |
31 | +typedef struct gptm_state { | |
32 | + uint32_t config; | |
33 | + uint32_t mode[2]; | |
34 | + uint32_t control; | |
35 | + uint32_t state; | |
36 | + uint32_t mask; | |
37 | + uint32_t load[2]; | |
38 | + uint32_t match[2]; | |
39 | + uint32_t prescale[2]; | |
40 | + uint32_t match_prescale[2]; | |
41 | + uint32_t rtc; | |
42 | + int64_t tick[2]; | |
43 | + struct gptm_state *opaque[2]; | |
44 | + uint32_t base; | |
45 | + QEMUTimer *timer[2]; | |
46 | + /* The timers have an alternate output used to trigger the ADC. */ | |
47 | + qemu_irq trigger; | |
48 | + qemu_irq irq; | |
49 | +} gptm_state; | |
50 | + | |
51 | +static void gptm_update_irq(gptm_state *s) | |
52 | +{ | |
53 | + int level; | |
54 | + level = (s->state & s->mask) != 0; | |
55 | + qemu_set_irq(s->irq, level); | |
56 | +} | |
57 | + | |
58 | +static void gptm_stop(gptm_state *s, int n) | |
59 | +{ | |
60 | + qemu_del_timer(s->timer[n]); | |
61 | +} | |
62 | + | |
63 | +static void gptm_reload(gptm_state *s, int n, int reset) | |
64 | +{ | |
65 | + int64_t tick; | |
66 | + if (reset) | |
67 | + tick = qemu_get_clock(vm_clock); | |
68 | + else | |
69 | + tick = s->tick[n]; | |
70 | + | |
71 | + if (s->config == 0) { | |
72 | + /* 32-bit CountDown. */ | |
73 | + uint32_t count; | |
74 | + count = s->load[0] | (s->load[1] << 16); | |
75 | + tick += (int64_t)count * stellaris_clock_scale; | |
76 | + } else if (s->config == 1) { | |
77 | + /* 32-bit RTC. 1Hz tick. */ | |
78 | + tick += ticks_per_sec; | |
79 | + } else if (s->mode[n] == 0xa) { | |
80 | + /* PWM mode. Not implemented. */ | |
81 | + } else { | |
82 | + cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n", | |
83 | + s->mode[n]); | |
84 | + } | |
85 | + s->tick[n] = tick; | |
86 | + qemu_mod_timer(s->timer[n], tick); | |
87 | +} | |
88 | + | |
89 | +static void gptm_tick(void *opaque) | |
90 | +{ | |
91 | + gptm_state **p = (gptm_state **)opaque; | |
92 | + gptm_state *s; | |
93 | + int n; | |
94 | + | |
95 | + s = *p; | |
96 | + n = p - s->opaque; | |
97 | + if (s->config == 0) { | |
98 | + s->state |= 1; | |
99 | + if ((s->control & 0x20)) { | |
100 | + /* Output trigger. */ | |
101 | + qemu_irq_raise(s->trigger); | |
102 | + qemu_irq_lower(s->trigger); | |
103 | + } | |
104 | + if (s->mode[0] & 1) { | |
105 | + /* One-shot. */ | |
106 | + s->control &= ~1; | |
107 | + } else { | |
108 | + /* Periodic. */ | |
109 | + gptm_reload(s, 0, 0); | |
110 | + } | |
111 | + } else if (s->config == 1) { | |
112 | + /* RTC. */ | |
113 | + uint32_t match; | |
114 | + s->rtc++; | |
115 | + match = s->match[0] | (s->match[1] << 16); | |
116 | + if (s->rtc > match) | |
117 | + s->rtc = 0; | |
118 | + if (s->rtc == 0) { | |
119 | + s->state |= 8; | |
120 | + } | |
121 | + gptm_reload(s, 0, 0); | |
122 | + } else if (s->mode[n] == 0xa) { | |
123 | + /* PWM mode. Not implemented. */ | |
124 | + } else { | |
125 | + cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n", | |
126 | + s->mode[n]); | |
127 | + } | |
128 | + gptm_update_irq(s); | |
129 | +} | |
130 | + | |
131 | +static uint32_t gptm_read(void *opaque, target_phys_addr_t offset) | |
132 | +{ | |
133 | + gptm_state *s = (gptm_state *)opaque; | |
134 | + | |
135 | + offset -= s->base; | |
136 | + switch (offset) { | |
137 | + case 0x00: /* CFG */ | |
138 | + return s->config; | |
139 | + case 0x04: /* TAMR */ | |
140 | + return s->mode[0]; | |
141 | + case 0x08: /* TBMR */ | |
142 | + return s->mode[1]; | |
143 | + case 0x0c: /* CTL */ | |
144 | + return s->control; | |
145 | + case 0x18: /* IMR */ | |
146 | + return s->mask; | |
147 | + case 0x1c: /* RIS */ | |
148 | + return s->state; | |
149 | + case 0x20: /* MIS */ | |
150 | + return s->state & s->mask; | |
151 | + case 0x24: /* CR */ | |
152 | + return 0; | |
153 | + case 0x28: /* TAILR */ | |
154 | + return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0); | |
155 | + case 0x2c: /* TBILR */ | |
156 | + return s->load[1]; | |
157 | + case 0x30: /* TAMARCHR */ | |
158 | + return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0); | |
159 | + case 0x34: /* TBMATCHR */ | |
160 | + return s->match[1]; | |
161 | + case 0x38: /* TAPR */ | |
162 | + return s->prescale[0]; | |
163 | + case 0x3c: /* TBPR */ | |
164 | + return s->prescale[1]; | |
165 | + case 0x40: /* TAPMR */ | |
166 | + return s->match_prescale[0]; | |
167 | + case 0x44: /* TBPMR */ | |
168 | + return s->match_prescale[1]; | |
169 | + case 0x48: /* TAR */ | |
170 | + if (s->control == 1) | |
171 | + return s->rtc; | |
172 | + case 0x4c: /* TBR */ | |
173 | + cpu_abort(cpu_single_env, "TODO: Timer value read\n"); | |
174 | + default: | |
175 | + cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset); | |
176 | + return 0; | |
177 | + } | |
178 | +} | |
179 | + | |
180 | +static void gptm_write(void *opaque, target_phys_addr_t offset, uint32_t value) | |
181 | +{ | |
182 | + gptm_state *s = (gptm_state *)opaque; | |
183 | + uint32_t oldval; | |
184 | + | |
185 | + offset -= s->base; | |
186 | + /* The timers should be disabled before changing the configuration. | |
187 | + We take advantage of this and defer everything until the timer | |
188 | + is enabled. */ | |
189 | + switch (offset) { | |
190 | + case 0x00: /* CFG */ | |
191 | + s->config = value; | |
192 | + break; | |
193 | + case 0x04: /* TAMR */ | |
194 | + s->mode[0] = value; | |
195 | + break; | |
196 | + case 0x08: /* TBMR */ | |
197 | + s->mode[1] = value; | |
198 | + break; | |
199 | + case 0x0c: /* CTL */ | |
200 | + oldval = s->control; | |
201 | + s->control = value; | |
202 | + /* TODO: Implement pause. */ | |
203 | + if ((oldval ^ value) & 1) { | |
204 | + if (value & 1) { | |
205 | + gptm_reload(s, 0, 1); | |
206 | + } else { | |
207 | + gptm_stop(s, 0); | |
208 | + } | |
209 | + } | |
210 | + if (((oldval ^ value) & 0x100) && s->config >= 4) { | |
211 | + if (value & 0x100) { | |
212 | + gptm_reload(s, 1, 1); | |
213 | + } else { | |
214 | + gptm_stop(s, 1); | |
215 | + } | |
216 | + } | |
217 | + break; | |
218 | + case 0x18: /* IMR */ | |
219 | + s->mask = value & 0x77; | |
220 | + gptm_update_irq(s); | |
221 | + break; | |
222 | + case 0x24: /* CR */ | |
223 | + s->state &= ~value; | |
224 | + break; | |
225 | + case 0x28: /* TAILR */ | |
226 | + s->load[0] = value & 0xffff; | |
227 | + if (s->config < 4) { | |
228 | + s->load[1] = value >> 16; | |
229 | + } | |
230 | + break; | |
231 | + case 0x2c: /* TBILR */ | |
232 | + s->load[1] = value & 0xffff; | |
233 | + break; | |
234 | + case 0x30: /* TAMARCHR */ | |
235 | + s->match[0] = value & 0xffff; | |
236 | + if (s->config < 4) { | |
237 | + s->match[1] = value >> 16; | |
238 | + } | |
239 | + break; | |
240 | + case 0x34: /* TBMATCHR */ | |
241 | + s->match[1] = value >> 16; | |
242 | + break; | |
243 | + case 0x38: /* TAPR */ | |
244 | + s->prescale[0] = value; | |
245 | + break; | |
246 | + case 0x3c: /* TBPR */ | |
247 | + s->prescale[1] = value; | |
248 | + break; | |
249 | + case 0x40: /* TAPMR */ | |
250 | + s->match_prescale[0] = value; | |
251 | + break; | |
252 | + case 0x44: /* TBPMR */ | |
253 | + s->match_prescale[0] = value; | |
254 | + break; | |
255 | + default: | |
256 | + cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset); | |
257 | + } | |
258 | + gptm_update_irq(s); | |
259 | +} | |
260 | + | |
261 | +static CPUReadMemoryFunc *gptm_readfn[] = { | |
262 | + gptm_read, | |
263 | + gptm_read, | |
264 | + gptm_read | |
265 | +}; | |
266 | + | |
267 | +static CPUWriteMemoryFunc *gptm_writefn[] = { | |
268 | + gptm_write, | |
269 | + gptm_write, | |
270 | + gptm_write | |
271 | +}; | |
272 | + | |
273 | +static void stellaris_gptm_init(uint32_t base, qemu_irq irq, qemu_irq trigger) | |
274 | +{ | |
275 | + int iomemtype; | |
276 | + gptm_state *s; | |
277 | + | |
278 | + s = (gptm_state *)qemu_mallocz(sizeof(gptm_state)); | |
279 | + s->base = base; | |
280 | + s->irq = irq; | |
281 | + s->trigger = trigger; | |
282 | + s->opaque[0] = s->opaque[1] = s; | |
283 | + | |
284 | + iomemtype = cpu_register_io_memory(0, gptm_readfn, | |
285 | + gptm_writefn, s); | |
286 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
287 | + s->timer[0] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[0]); | |
288 | + s->timer[1] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[1]); | |
289 | + /* ??? Save/restore. */ | |
290 | +} | |
291 | + | |
292 | + | |
293 | +/* System controller. */ | |
294 | + | |
295 | +typedef struct { | |
296 | + uint32_t base; | |
297 | + uint32_t pborctl; | |
298 | + uint32_t ldopctl; | |
299 | + uint32_t int_status; | |
300 | + uint32_t int_mask; | |
301 | + uint32_t resc; | |
302 | + uint32_t rcc; | |
303 | + uint32_t rcgc[3]; | |
304 | + uint32_t scgc[3]; | |
305 | + uint32_t dcgc[3]; | |
306 | + uint32_t clkvclr; | |
307 | + uint32_t ldoarst; | |
308 | + qemu_irq irq; | |
309 | + stellaris_board_info *board; | |
310 | +} ssys_state; | |
311 | + | |
312 | +static void ssys_update(ssys_state *s) | |
313 | +{ | |
314 | + qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0); | |
315 | +} | |
316 | + | |
317 | +static uint32_t pllcfg_sandstorm[16] = { | |
318 | + 0x31c0, /* 1 Mhz */ | |
319 | + 0x1ae0, /* 1.8432 Mhz */ | |
320 | + 0x18c0, /* 2 Mhz */ | |
321 | + 0xd573, /* 2.4576 Mhz */ | |
322 | + 0x37a6, /* 3.57954 Mhz */ | |
323 | + 0x1ae2, /* 3.6864 Mhz */ | |
324 | + 0x0c40, /* 4 Mhz */ | |
325 | + 0x98bc, /* 4.906 Mhz */ | |
326 | + 0x935b, /* 4.9152 Mhz */ | |
327 | + 0x09c0, /* 5 Mhz */ | |
328 | + 0x4dee, /* 5.12 Mhz */ | |
329 | + 0x0c41, /* 6 Mhz */ | |
330 | + 0x75db, /* 6.144 Mhz */ | |
331 | + 0x1ae6, /* 7.3728 Mhz */ | |
332 | + 0x0600, /* 8 Mhz */ | |
333 | + 0x585b /* 8.192 Mhz */ | |
334 | +}; | |
335 | + | |
336 | +static uint32_t pllcfg_fury[16] = { | |
337 | + 0x3200, /* 1 Mhz */ | |
338 | + 0x1b20, /* 1.8432 Mhz */ | |
339 | + 0x1900, /* 2 Mhz */ | |
340 | + 0xf42b, /* 2.4576 Mhz */ | |
341 | + 0x37e3, /* 3.57954 Mhz */ | |
342 | + 0x1b21, /* 3.6864 Mhz */ | |
343 | + 0x0c80, /* 4 Mhz */ | |
344 | + 0x98ee, /* 4.906 Mhz */ | |
345 | + 0xd5b4, /* 4.9152 Mhz */ | |
346 | + 0x0a00, /* 5 Mhz */ | |
347 | + 0x4e27, /* 5.12 Mhz */ | |
348 | + 0x1902, /* 6 Mhz */ | |
349 | + 0xec1c, /* 6.144 Mhz */ | |
350 | + 0x1b23, /* 7.3728 Mhz */ | |
351 | + 0x0640, /* 8 Mhz */ | |
352 | + 0xb11c /* 8.192 Mhz */ | |
353 | +}; | |
354 | + | |
355 | +static uint32_t ssys_read(void *opaque, target_phys_addr_t offset) | |
356 | +{ | |
357 | + ssys_state *s = (ssys_state *)opaque; | |
358 | + | |
359 | + offset -= s->base; | |
360 | + switch (offset) { | |
361 | + case 0x000: /* DID0 */ | |
362 | + return s->board->did0; | |
363 | + case 0x004: /* DID1 */ | |
364 | + return s->board->did1; | |
365 | + case 0x008: /* DC0 */ | |
366 | + return s->board->dc0; | |
367 | + case 0x010: /* DC1 */ | |
368 | + return s->board->dc1; | |
369 | + case 0x014: /* DC2 */ | |
370 | + return s->board->dc2; | |
371 | + case 0x018: /* DC3 */ | |
372 | + return s->board->dc3; | |
373 | + case 0x01c: /* DC4 */ | |
374 | + return s->board->dc4; | |
375 | + case 0x030: /* PBORCTL */ | |
376 | + return s->pborctl; | |
377 | + case 0x034: /* LDOPCTL */ | |
378 | + return s->ldopctl; | |
379 | + case 0x040: /* SRCR0 */ | |
380 | + return 0; | |
381 | + case 0x044: /* SRCR1 */ | |
382 | + return 0; | |
383 | + case 0x048: /* SRCR2 */ | |
384 | + return 0; | |
385 | + case 0x050: /* RIS */ | |
386 | + return s->int_status; | |
387 | + case 0x054: /* IMC */ | |
388 | + return s->int_mask; | |
389 | + case 0x058: /* MISC */ | |
390 | + return s->int_status & s->int_mask; | |
391 | + case 0x05c: /* RESC */ | |
392 | + return s->resc; | |
393 | + case 0x060: /* RCC */ | |
394 | + return s->rcc; | |
395 | + case 0x064: /* PLLCFG */ | |
396 | + { | |
397 | + int xtal; | |
398 | + xtal = (s->rcc >> 6) & 0xf; | |
399 | + if (s->board->did0 & (1 << 16)) { | |
400 | + return pllcfg_fury[xtal]; | |
401 | + } else { | |
402 | + return pllcfg_sandstorm[xtal]; | |
403 | + } | |
404 | + } | |
405 | + case 0x100: /* RCGC0 */ | |
406 | + return s->rcgc[0]; | |
407 | + case 0x104: /* RCGC1 */ | |
408 | + return s->rcgc[1]; | |
409 | + case 0x108: /* RCGC2 */ | |
410 | + return s->rcgc[2]; | |
411 | + case 0x110: /* SCGC0 */ | |
412 | + return s->scgc[0]; | |
413 | + case 0x114: /* SCGC1 */ | |
414 | + return s->scgc[1]; | |
415 | + case 0x118: /* SCGC2 */ | |
416 | + return s->scgc[2]; | |
417 | + case 0x120: /* DCGC0 */ | |
418 | + return s->dcgc[0]; | |
419 | + case 0x124: /* DCGC1 */ | |
420 | + return s->dcgc[1]; | |
421 | + case 0x128: /* DCGC2 */ | |
422 | + return s->dcgc[2]; | |
423 | + case 0x150: /* CLKVCLR */ | |
424 | + return s->clkvclr; | |
425 | + case 0x160: /* LDOARST */ | |
426 | + return s->ldoarst; | |
427 | + default: | |
428 | + cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset); | |
429 | + return 0; | |
430 | + } | |
431 | +} | |
432 | + | |
433 | +static void ssys_write(void *opaque, target_phys_addr_t offset, uint32_t value) | |
434 | +{ | |
435 | + ssys_state *s = (ssys_state *)opaque; | |
436 | + | |
437 | + offset -= s->base; | |
438 | + switch (offset) { | |
439 | + case 0x030: /* PBORCTL */ | |
440 | + s->pborctl = value & 0xffff; | |
441 | + break; | |
442 | + case 0x034: /* LDOPCTL */ | |
443 | + s->ldopctl = value & 0x1f; | |
444 | + break; | |
445 | + case 0x040: /* SRCR0 */ | |
446 | + case 0x044: /* SRCR1 */ | |
447 | + case 0x048: /* SRCR2 */ | |
448 | + fprintf(stderr, "Peripheral reset not implemented\n"); | |
449 | + break; | |
450 | + case 0x054: /* IMC */ | |
451 | + s->int_mask = value & 0x7f; | |
452 | + break; | |
453 | + case 0x058: /* MISC */ | |
454 | + s->int_status &= ~value; | |
455 | + break; | |
456 | + case 0x05c: /* RESC */ | |
457 | + s->resc = value & 0x3f; | |
458 | + break; | |
459 | + case 0x060: /* RCC */ | |
460 | + if ((s->rcc & (1 << 13)) != 0 && (value & (1 << 13)) == 0) { | |
461 | + /* PLL enable. */ | |
462 | + s->int_status |= (1 << 6); | |
463 | + } | |
464 | + s->rcc = value; | |
465 | + stellaris_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1); | |
466 | + break; | |
467 | + case 0x100: /* RCGC0 */ | |
468 | + s->rcgc[0] = value; | |
469 | + break; | |
470 | + case 0x104: /* RCGC1 */ | |
471 | + s->rcgc[1] = value; | |
472 | + break; | |
473 | + case 0x108: /* RCGC2 */ | |
474 | + s->rcgc[2] = value; | |
475 | + break; | |
476 | + case 0x110: /* SCGC0 */ | |
477 | + s->scgc[0] = value; | |
478 | + break; | |
479 | + case 0x114: /* SCGC1 */ | |
480 | + s->scgc[1] = value; | |
481 | + break; | |
482 | + case 0x118: /* SCGC2 */ | |
483 | + s->scgc[2] = value; | |
484 | + break; | |
485 | + case 0x120: /* DCGC0 */ | |
486 | + s->dcgc[0] = value; | |
487 | + break; | |
488 | + case 0x124: /* DCGC1 */ | |
489 | + s->dcgc[1] = value; | |
490 | + break; | |
491 | + case 0x128: /* DCGC2 */ | |
492 | + s->dcgc[2] = value; | |
493 | + break; | |
494 | + case 0x150: /* CLKVCLR */ | |
495 | + s->clkvclr = value; | |
496 | + break; | |
497 | + case 0x160: /* LDOARST */ | |
498 | + s->ldoarst = value; | |
499 | + break; | |
500 | + default: | |
501 | + cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset); | |
502 | + } | |
503 | + ssys_update(s); | |
504 | +} | |
505 | + | |
506 | +static CPUReadMemoryFunc *ssys_readfn[] = { | |
507 | + ssys_read, | |
508 | + ssys_read, | |
509 | + ssys_read | |
510 | +}; | |
511 | + | |
512 | +static CPUWriteMemoryFunc *ssys_writefn[] = { | |
513 | + ssys_write, | |
514 | + ssys_write, | |
515 | + ssys_write | |
516 | +}; | |
517 | + | |
518 | +void ssys_reset(void *opaque) | |
519 | +{ | |
520 | + ssys_state *s = (ssys_state *)opaque; | |
521 | + | |
522 | + s->pborctl = 0x7ffd; | |
523 | + s->rcc = 0x078e3ac0; | |
524 | + s->rcgc[0] = 1; | |
525 | + s->scgc[0] = 1; | |
526 | + s->dcgc[0] = 1; | |
527 | +} | |
528 | + | |
529 | +static void stellaris_sys_init(uint32_t base, qemu_irq irq, | |
530 | + stellaris_board_info * board) | |
531 | +{ | |
532 | + int iomemtype; | |
533 | + ssys_state *s; | |
534 | + | |
535 | + s = (ssys_state *)qemu_mallocz(sizeof(ssys_state)); | |
536 | + s->base = base; | |
537 | + s->irq = irq; | |
538 | + s->board = board; | |
539 | + | |
540 | + iomemtype = cpu_register_io_memory(0, ssys_readfn, | |
541 | + ssys_writefn, s); | |
542 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
543 | + ssys_reset(s); | |
544 | + /* ??? Save/restore. */ | |
545 | +} | |
546 | + | |
547 | + | |
548 | +/* I2C controller. */ | |
549 | + | |
550 | +typedef struct { | |
551 | + i2c_bus *bus; | |
552 | + qemu_irq irq; | |
553 | + uint32_t base; | |
554 | + uint32_t msa; | |
555 | + uint32_t mcs; | |
556 | + uint32_t mdr; | |
557 | + uint32_t mtpr; | |
558 | + uint32_t mimr; | |
559 | + uint32_t mris; | |
560 | + uint32_t mcr; | |
561 | +} stellaris_i2c_state; | |
562 | + | |
563 | +#define STELLARIS_I2C_MCS_BUSY 0x01 | |
564 | +#define STELLARIS_I2C_MCS_ERROR 0x02 | |
565 | +#define STELLARIS_I2C_MCS_ADRACK 0x04 | |
566 | +#define STELLARIS_I2C_MCS_DATACK 0x08 | |
567 | +#define STELLARIS_I2C_MCS_ARBLST 0x10 | |
568 | +#define STELLARIS_I2C_MCS_IDLE 0x20 | |
569 | +#define STELLARIS_I2C_MCS_BUSBSY 0x40 | |
570 | + | |
571 | +static uint32_t stellaris_i2c_read(void *opaque, target_phys_addr_t offset) | |
572 | +{ | |
573 | + stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; | |
574 | + | |
575 | + offset -= s->base; | |
576 | + switch (offset) { | |
577 | + case 0x00: /* MSA */ | |
578 | + return s->msa; | |
579 | + case 0x04: /* MCS */ | |
580 | + /* We don't emulate timing, so the controller is never busy. */ | |
581 | + return s->mcs | STELLARIS_I2C_MCS_IDLE; | |
582 | + case 0x08: /* MDR */ | |
583 | + return s->mdr; | |
584 | + case 0x0c: /* MTPR */ | |
585 | + return s->mtpr; | |
586 | + case 0x10: /* MIMR */ | |
587 | + return s->mimr; | |
588 | + case 0x14: /* MRIS */ | |
589 | + return s->mris; | |
590 | + case 0x18: /* MMIS */ | |
591 | + return s->mris & s->mimr; | |
592 | + case 0x20: /* MCR */ | |
593 | + return s->mcr; | |
594 | + default: | |
595 | + cpu_abort(cpu_single_env, "strllaris_i2c_read: Bad offset 0x%x\n", | |
596 | + (int)offset); | |
597 | + return 0; | |
598 | + } | |
599 | +} | |
600 | + | |
601 | +static void stellaris_i2c_update(stellaris_i2c_state *s) | |
602 | +{ | |
603 | + int level; | |
604 | + | |
605 | + level = (s->mris & s->mimr) != 0; | |
606 | + qemu_set_irq(s->irq, level); | |
607 | +} | |
608 | + | |
609 | +static void stellaris_i2c_write(void *opaque, target_phys_addr_t offset, | |
610 | + uint32_t value) | |
611 | +{ | |
612 | + stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; | |
613 | + | |
614 | + offset -= s->base; | |
615 | + switch (offset) { | |
616 | + case 0x00: /* MSA */ | |
617 | + s->msa = value & 0xff; | |
618 | + break; | |
619 | + case 0x04: /* MCS */ | |
620 | + if ((s->mcr & 0x10) == 0) { | |
621 | + /* Disabled. Do nothing. */ | |
622 | + break; | |
623 | + } | |
624 | + /* Grab the bus if this is starting a transfer. */ | |
625 | + if ((value & 2) && (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) { | |
626 | + if (i2c_start_transfer(s->bus, s->msa >> 1, s->msa & 1)) { | |
627 | + s->mcs |= STELLARIS_I2C_MCS_ARBLST; | |
628 | + } else { | |
629 | + s->mcs &= ~STELLARIS_I2C_MCS_ARBLST; | |
630 | + s->mcs |= STELLARIS_I2C_MCS_BUSBSY; | |
631 | + } | |
632 | + } | |
633 | + /* If we don't have the bus then indicate an error. */ | |
634 | + if (!i2c_bus_busy(s->bus) | |
635 | + || (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) { | |
636 | + s->mcs |= STELLARIS_I2C_MCS_ERROR; | |
637 | + break; | |
638 | + } | |
639 | + s->mcs &= ~STELLARIS_I2C_MCS_ERROR; | |
640 | + if (value & 1) { | |
641 | + /* Transfer a byte. */ | |
642 | + /* TODO: Handle errors. */ | |
643 | + if (s->msa & 1) { | |
644 | + /* Recv */ | |
645 | + s->mdr = i2c_recv(s->bus) & 0xff; | |
646 | + } else { | |
647 | + /* Send */ | |
648 | + i2c_send(s->bus, s->mdr); | |
649 | + } | |
650 | + /* Raise an interrupt. */ | |
651 | + s->mris |= 1; | |
652 | + } | |
653 | + if (value & 4) { | |
654 | + /* Finish transfer. */ | |
655 | + i2c_end_transfer(s->bus); | |
656 | + s->mcs &= ~STELLARIS_I2C_MCS_BUSBSY; | |
657 | + } | |
658 | + break; | |
659 | + case 0x08: /* MDR */ | |
660 | + s->mdr = value & 0xff; | |
661 | + break; | |
662 | + case 0x0c: /* MTPR */ | |
663 | + s->mtpr = value & 0xff; | |
664 | + break; | |
665 | + case 0x10: /* MIMR */ | |
666 | + s->mimr = 1; | |
667 | + break; | |
668 | + case 0x1c: /* MICR */ | |
669 | + s->mris &= ~value; | |
670 | + break; | |
671 | + case 0x20: /* MCR */ | |
672 | + if (value & 1) | |
673 | + cpu_abort(cpu_single_env, | |
674 | + "stellaris_i2c_write: Loopback not implemented\n"); | |
675 | + if (value & 0x20) | |
676 | + cpu_abort(cpu_single_env, | |
677 | + "stellaris_i2c_write: Slave mode not implemented\n"); | |
678 | + s->mcr = value & 0x31; | |
679 | + break; | |
680 | + default: | |
681 | + cpu_abort(cpu_single_env, "stellaris_i2c_write: Bad offset 0x%x\n", | |
682 | + (int)offset); | |
683 | + } | |
684 | + stellaris_i2c_update(s); | |
685 | +} | |
686 | + | |
687 | +static void stellaris_i2c_reset(stellaris_i2c_state *s) | |
688 | +{ | |
689 | + if (s->mcs & STELLARIS_I2C_MCS_BUSBSY) | |
690 | + i2c_end_transfer(s->bus); | |
691 | + | |
692 | + s->msa = 0; | |
693 | + s->mcs = 0; | |
694 | + s->mdr = 0; | |
695 | + s->mtpr = 1; | |
696 | + s->mimr = 0; | |
697 | + s->mris = 0; | |
698 | + s->mcr = 0; | |
699 | + stellaris_i2c_update(s); | |
700 | +} | |
701 | + | |
702 | +static CPUReadMemoryFunc *stellaris_i2c_readfn[] = { | |
703 | + stellaris_i2c_read, | |
704 | + stellaris_i2c_read, | |
705 | + stellaris_i2c_read | |
706 | +}; | |
707 | + | |
708 | +static CPUWriteMemoryFunc *stellaris_i2c_writefn[] = { | |
709 | + stellaris_i2c_write, | |
710 | + stellaris_i2c_write, | |
711 | + stellaris_i2c_write | |
712 | +}; | |
713 | + | |
714 | +static void stellaris_i2c_init(uint32_t base, qemu_irq irq, i2c_bus *bus) | |
715 | +{ | |
716 | + stellaris_i2c_state *s; | |
717 | + int iomemtype; | |
718 | + | |
719 | + s = (stellaris_i2c_state *)qemu_mallocz(sizeof(stellaris_i2c_state)); | |
720 | + s->base = base; | |
721 | + s->irq = irq; | |
722 | + s->bus = bus; | |
723 | + | |
724 | + iomemtype = cpu_register_io_memory(0, stellaris_i2c_readfn, | |
725 | + stellaris_i2c_writefn, s); | |
726 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
727 | + /* ??? For now we only implement the master interface. */ | |
728 | + stellaris_i2c_reset(s); | |
729 | +} | |
730 | + | |
731 | +/* Analogue to Digital Converter. This is only partially implemented, | |
732 | + enough for applications that use a combined ADC and timer tick. */ | |
733 | + | |
734 | +#define STELLARIS_ADC_EM_CONTROLLER 0 | |
735 | +#define STELLARIS_ADC_EM_COMP 1 | |
736 | +#define STELLARIS_ADC_EM_EXTERNAL 4 | |
737 | +#define STELLARIS_ADC_EM_TIMER 5 | |
738 | +#define STELLARIS_ADC_EM_PWM0 6 | |
739 | +#define STELLARIS_ADC_EM_PWM1 7 | |
740 | +#define STELLARIS_ADC_EM_PWM2 8 | |
741 | + | |
742 | +#define STELLARIS_ADC_FIFO_EMPTY 0x0100 | |
743 | +#define STELLARIS_ADC_FIFO_FULL 0x1000 | |
744 | + | |
745 | +typedef struct | |
746 | +{ | |
747 | + uint32_t base; | |
748 | + uint32_t actss; | |
749 | + uint32_t ris; | |
750 | + uint32_t im; | |
751 | + uint32_t emux; | |
752 | + uint32_t ostat; | |
753 | + uint32_t ustat; | |
754 | + uint32_t sspri; | |
755 | + uint32_t sac; | |
756 | + struct { | |
757 | + uint32_t state; | |
758 | + uint32_t data[16]; | |
759 | + } fifo[4]; | |
760 | + uint32_t ssmux[4]; | |
761 | + uint32_t ssctl[4]; | |
762 | + qemu_irq irq; | |
763 | +} stellaris_adc_state; | |
764 | + | |
765 | +static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n) | |
766 | +{ | |
767 | + int tail; | |
768 | + | |
769 | + tail = s->fifo[n].state & 0xf; | |
770 | + if (s->fifo[n].state & STELLARIS_ADC_FIFO_EMPTY) { | |
771 | + s->ustat |= 1 << n; | |
772 | + } else { | |
773 | + s->fifo[n].state = (s->fifo[n].state & ~0xf) | ((tail + 1) & 0xf); | |
774 | + s->fifo[n].state &= ~STELLARIS_ADC_FIFO_FULL; | |
775 | + if (tail + 1 == ((s->fifo[n].state >> 4) & 0xf)) | |
776 | + s->fifo[n].state |= STELLARIS_ADC_FIFO_EMPTY; | |
777 | + } | |
778 | + return s->fifo[n].data[tail]; | |
779 | +} | |
780 | + | |
781 | +static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n, | |
782 | + uint32_t value) | |
783 | +{ | |
784 | + int head; | |
785 | + | |
786 | + head = (s->fifo[n].state >> 4) & 0xf; | |
787 | + if (s->fifo[n].state & STELLARIS_ADC_FIFO_FULL) { | |
788 | + s->ostat |= 1 << n; | |
789 | + return; | |
790 | + } | |
791 | + s->fifo[n].data[head] = value; | |
792 | + head = (head + 1) & 0xf; | |
793 | + s->fifo[n].state &= ~STELLARIS_ADC_FIFO_EMPTY; | |
794 | + s->fifo[n].state = (s->fifo[n].state & ~0xf0) | (head << 4); | |
795 | + if ((s->fifo[n].state & 0xf) == head) | |
796 | + s->fifo[n].state |= STELLARIS_ADC_FIFO_FULL; | |
797 | +} | |
798 | + | |
799 | +static void stellaris_adc_update(stellaris_adc_state *s) | |
800 | +{ | |
801 | + int level; | |
802 | + | |
803 | + level = (s->ris & s->im) != 0; | |
804 | + qemu_set_irq(s->irq, level); | |
805 | +} | |
806 | + | |
807 | +static void stellaris_adc_trigger(void *opaque, int irq, int level) | |
808 | +{ | |
809 | + stellaris_adc_state *s = (stellaris_adc_state *)opaque; | |
810 | + /* Some applications use the ADC as a random number source, so introduce | |
811 | + some variation into the signal. */ | |
812 | + static uint32_t noise = 0; | |
813 | + | |
814 | + if ((s->actss & 1) == 0) { | |
815 | + return; | |
816 | + } | |
817 | + | |
818 | + noise = noise * 314159 + 1; | |
819 | + /* ??? actual inputs not implemented. Return an arbitrary value. */ | |
820 | + stellaris_adc_fifo_write(s, 0, 0x200 + ((noise >> 16) & 7)); | |
821 | + s->ris |= 1; | |
822 | + stellaris_adc_update(s); | |
823 | +} | |
824 | + | |
825 | +static void stellaris_adc_reset(stellaris_adc_state *s) | |
826 | +{ | |
827 | + int n; | |
828 | + | |
829 | + for (n = 0; n < 4; n++) { | |
830 | + s->ssmux[n] = 0; | |
831 | + s->ssctl[n] = 0; | |
832 | + s->fifo[n].state = STELLARIS_ADC_FIFO_EMPTY; | |
833 | + } | |
834 | +} | |
835 | + | |
836 | +static uint32_t stellaris_adc_read(void *opaque, target_phys_addr_t offset) | |
837 | +{ | |
838 | + stellaris_adc_state *s = (stellaris_adc_state *)opaque; | |
839 | + | |
840 | + /* TODO: Implement this. */ | |
841 | + offset -= s->base; | |
842 | + if (offset >= 0x40 && offset < 0xc0) { | |
843 | + int n; | |
844 | + n = (offset - 0x40) >> 5; | |
845 | + switch (offset & 0x1f) { | |
846 | + case 0x00: /* SSMUX */ | |
847 | + return s->ssmux[n]; | |
848 | + case 0x04: /* SSCTL */ | |
849 | + return s->ssctl[n]; | |
850 | + case 0x08: /* SSFIFO */ | |
851 | + return stellaris_adc_fifo_read(s, n); | |
852 | + case 0x0c: /* SSFSTAT */ | |
853 | + return s->fifo[n].state; | |
854 | + default: | |
855 | + break; | |
856 | + } | |
857 | + } | |
858 | + switch (offset) { | |
859 | + case 0x00: /* ACTSS */ | |
860 | + return s->actss; | |
861 | + case 0x04: /* RIS */ | |
862 | + return s->ris; | |
863 | + case 0x08: /* IM */ | |
864 | + return s->im; | |
865 | + case 0x0c: /* ISC */ | |
866 | + return s->ris & s->im; | |
867 | + case 0x10: /* OSTAT */ | |
868 | + return s->ostat; | |
869 | + case 0x14: /* EMUX */ | |
870 | + return s->emux; | |
871 | + case 0x18: /* USTAT */ | |
872 | + return s->ustat; | |
873 | + case 0x20: /* SSPRI */ | |
874 | + return s->sspri; | |
875 | + case 0x30: /* SAC */ | |
876 | + return s->sac; | |
877 | + default: | |
878 | + cpu_abort(cpu_single_env, "strllaris_adc_read: Bad offset 0x%x\n", | |
879 | + (int)offset); | |
880 | + return 0; | |
881 | + } | |
882 | +} | |
883 | + | |
884 | +static void stellaris_adc_write(void *opaque, target_phys_addr_t offset, | |
885 | + uint32_t value) | |
886 | +{ | |
887 | + stellaris_adc_state *s = (stellaris_adc_state *)opaque; | |
888 | + | |
889 | + /* TODO: Implement this. */ | |
890 | + offset -= s->base; | |
891 | + if (offset >= 0x40 && offset < 0xc0) { | |
892 | + int n; | |
893 | + n = (offset - 0x40) >> 5; | |
894 | + switch (offset & 0x1f) { | |
895 | + case 0x00: /* SSMUX */ | |
896 | + s->ssmux[n] = value & 0x33333333; | |
897 | + return; | |
898 | + case 0x04: /* SSCTL */ | |
899 | + if (value != 6) { | |
900 | + cpu_abort(cpu_single_env, "ADC: Unimplemented sequence %x\n", | |
901 | + value); | |
902 | + } | |
903 | + s->ssctl[n] = value; | |
904 | + return; | |
905 | + default: | |
906 | + break; | |
907 | + } | |
908 | + } | |
909 | + switch (offset) { | |
910 | + case 0x00: /* ACTSS */ | |
911 | + s->actss = value & 0xf; | |
912 | + if (value & 0xe) { | |
913 | + cpu_abort(cpu_single_env, | |
914 | + "Not implemented: ADC sequencers 1-3\n"); | |
915 | + } | |
916 | + break; | |
917 | + case 0x08: /* IM */ | |
918 | + s->im = value; | |
919 | + break; | |
920 | + case 0x0c: /* ISC */ | |
921 | + s->ris &= ~value; | |
922 | + break; | |
923 | + case 0x10: /* OSTAT */ | |
924 | + s->ostat &= ~value; | |
925 | + break; | |
926 | + case 0x14: /* EMUX */ | |
927 | + s->emux = value; | |
928 | + break; | |
929 | + case 0x18: /* USTAT */ | |
930 | + s->ustat &= ~value; | |
931 | + break; | |
932 | + case 0x20: /* SSPRI */ | |
933 | + s->sspri = value; | |
934 | + break; | |
935 | + case 0x28: /* PSSI */ | |
936 | + cpu_abort(cpu_single_env, "Not implemented: ADC sample initiate\n"); | |
937 | + break; | |
938 | + case 0x30: /* SAC */ | |
939 | + s->sac = value; | |
940 | + break; | |
941 | + default: | |
942 | + cpu_abort(cpu_single_env, "stellaris_adc_write: Bad offset 0x%x\n", | |
943 | + (int)offset); | |
944 | + } | |
945 | + stellaris_adc_update(s); | |
946 | +} | |
947 | + | |
948 | +static CPUReadMemoryFunc *stellaris_adc_readfn[] = { | |
949 | + stellaris_adc_read, | |
950 | + stellaris_adc_read, | |
951 | + stellaris_adc_read | |
952 | +}; | |
953 | + | |
954 | +static CPUWriteMemoryFunc *stellaris_adc_writefn[] = { | |
955 | + stellaris_adc_write, | |
956 | + stellaris_adc_write, | |
957 | + stellaris_adc_write | |
958 | +}; | |
959 | + | |
960 | +static qemu_irq stellaris_adc_init(uint32_t base, qemu_irq irq) | |
961 | +{ | |
962 | + stellaris_adc_state *s; | |
963 | + int iomemtype; | |
964 | + qemu_irq *qi; | |
965 | + | |
966 | + s = (stellaris_adc_state *)qemu_mallocz(sizeof(stellaris_adc_state)); | |
967 | + s->base = base; | |
968 | + s->irq = irq; | |
969 | + | |
970 | + iomemtype = cpu_register_io_memory(0, stellaris_adc_readfn, | |
971 | + stellaris_adc_writefn, s); | |
972 | + cpu_register_physical_memory(base, 0x00001000, iomemtype); | |
973 | + stellaris_adc_reset(s); | |
974 | + qi = qemu_allocate_irqs(stellaris_adc_trigger, s, 1); | |
975 | + return qi[0]; | |
976 | +} | |
977 | + | |
978 | +/* Board init. */ | |
979 | +static stellaris_board_info stellaris_boards[] = { | |
980 | + { "LM3S811EVB", | |
981 | + 0, | |
982 | + 0x0032000e, | |
983 | + 0x001f001f, /* dc0 */ | |
984 | + 0x001132bf, | |
985 | + 0x01071013, | |
986 | + 0x3f0f01ff, | |
987 | + 0x0000001f, | |
988 | + OLED_I2C | |
989 | + }, | |
990 | + { "LM3S6965EVB", | |
991 | + 0x10010002, | |
992 | + 0x1073402e, | |
993 | + 0x00ff007f, /* dc0 */ | |
994 | + 0x001133ff, | |
995 | + 0x030f5317, | |
996 | + 0x0f0f87ff, | |
997 | + 0x5000007f, | |
998 | + OLED_SSI | |
999 | + } | |
1000 | +}; | |
1001 | + | |
1002 | +static void stellaris_init(const char *kernel_filename, const char *cpu_model, | |
1003 | + DisplayState *ds, stellaris_board_info *board) | |
1004 | +{ | |
1005 | + static const int uart_irq[] = {5, 6, 33, 34}; | |
1006 | + static const int timer_irq[] = {19, 21, 23, 35}; | |
1007 | + static const uint32_t gpio_addr[7] = | |
1008 | + { 0x40004000, 0x40005000, 0x40006000, 0x40007000, | |
1009 | + 0x40024000, 0x40025000, 0x40026000}; | |
1010 | + static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; | |
1011 | + | |
1012 | + qemu_irq *pic; | |
1013 | + qemu_irq *gpio_in[5]; | |
1014 | + qemu_irq *gpio_out[5]; | |
1015 | + qemu_irq adc; | |
1016 | + int sram_size; | |
1017 | + int flash_size; | |
1018 | + i2c_bus *i2c; | |
1019 | + int i; | |
1020 | + | |
1021 | + flash_size = ((board->dc0 & 0xffff) + 1) << 1; | |
1022 | + sram_size = (board->dc0 >> 18) + 1; | |
1023 | + pic = armv7m_init(flash_size, sram_size, kernel_filename, cpu_model); | |
1024 | + | |
1025 | + if (board->dc1 & (1 << 16)) { | |
1026 | + adc = stellaris_adc_init(0x40038000, pic[14]); | |
1027 | + } else { | |
1028 | + adc = NULL; | |
1029 | + } | |
1030 | + for (i = 0; i < 4; i++) { | |
1031 | + if (board->dc2 & (0x10000 << i)) { | |
1032 | + stellaris_gptm_init(0x40030000 + i * 0x1000, | |
1033 | + pic[timer_irq[i]], adc); | |
1034 | + } | |
1035 | + } | |
1036 | + | |
1037 | + stellaris_sys_init(0x400fe000, pic[28], board); | |
1038 | + | |
1039 | + for (i = 0; i < 7; i++) { | |
1040 | + if (board->dc4 & (1 << i)) { | |
1041 | + gpio_in[i] = pl061_init(gpio_addr[i], pic[gpio_irq[i]], | |
1042 | + &gpio_out[i]); | |
1043 | + } | |
1044 | + } | |
1045 | + | |
1046 | + if (board->dc2 & (1 << 12)) { | |
1047 | + i2c = i2c_init_bus(); | |
1048 | + stellaris_i2c_init(0x40020000, pic[8], i2c); | |
1049 | + if (board->oled == OLED_I2C) { | |
1050 | + ssd0303_init(ds, i2c, 0x3d); | |
1051 | + } | |
1052 | + } | |
1053 | + | |
1054 | + for (i = 0; i < 4; i++) { | |
1055 | + if (board->dc2 & (1 << i)) { | |
1056 | + pl011_init(0x4000c000 + i * 0x1000, pic[uart_irq[i]], | |
1057 | + serial_hds[i], PL011_LUMINARY); | |
1058 | + } | |
1059 | + } | |
1060 | + if (board->dc2 & (1 << 4)) { | |
1061 | + if (board->oled == OLED_SSI) { | |
1062 | + void * oled; | |
1063 | + /* FIXME: Implement chip select for OLED/MMC. */ | |
1064 | + oled = ssd0323_init(ds, &gpio_out[2][7]); | |
1065 | + pl022_init(0x40008000, pic[7], ssd0323_xfer_ssi, oled); | |
1066 | + } else { | |
1067 | + pl022_init(0x40008000, pic[7], NULL, NULL); | |
1068 | + } | |
1069 | + } | |
1070 | +} | |
1071 | + | |
1072 | +/* FIXME: Figure out how to generate these from stellaris_boards. */ | |
1073 | +static void lm3s811evb_init(int ram_size, int vga_ram_size, | |
1074 | + const char *boot_device, DisplayState *ds, | |
1075 | + const char **fd_filename, int snapshot, | |
1076 | + const char *kernel_filename, const char *kernel_cmdline, | |
1077 | + const char *initrd_filename, const char *cpu_model) | |
1078 | +{ | |
1079 | + stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[0]); | |
1080 | +} | |
1081 | + | |
1082 | +static void lm3s6965evb_init(int ram_size, int vga_ram_size, | |
1083 | + const char *boot_device, DisplayState *ds, | |
1084 | + const char **fd_filename, int snapshot, | |
1085 | + const char *kernel_filename, const char *kernel_cmdline, | |
1086 | + const char *initrd_filename, const char *cpu_model) | |
1087 | +{ | |
1088 | + stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[1]); | |
1089 | +} | |
1090 | + | |
1091 | +QEMUMachine lm3s811evb_machine = { | |
1092 | + "lm3s811evb", | |
1093 | + "Stellaris LM3S811EVB", | |
1094 | + lm3s811evb_init, | |
1095 | +}; | |
1096 | + | |
1097 | +QEMUMachine lm3s6965evb_machine = { | |
1098 | + "lm3s6965evb", | |
1099 | + "Stellaris LM3S6965EVB", | |
1100 | + lm3s6965evb_init, | |
1101 | +}; | ... | ... |
hw/versatilepb.c
... | ... | @@ -208,10 +208,10 @@ static void versatile_init(int ram_size, int vga_ram_size, |
208 | 208 | } |
209 | 209 | } |
210 | 210 | |
211 | - pl011_init(0x101f1000, pic[12], serial_hds[0]); | |
212 | - pl011_init(0x101f2000, pic[13], serial_hds[1]); | |
213 | - pl011_init(0x101f3000, pic[14], serial_hds[2]); | |
214 | - pl011_init(0x10009000, sic[6], serial_hds[3]); | |
211 | + pl011_init(0x101f1000, pic[12], serial_hds[0], PL011_ARM); | |
212 | + pl011_init(0x101f2000, pic[13], serial_hds[1], PL011_ARM); | |
213 | + pl011_init(0x101f3000, pic[14], serial_hds[2], PL011_ARM); | |
214 | + pl011_init(0x10009000, sic[6], serial_hds[3], PL011_ARM); | |
215 | 215 | |
216 | 216 | pl080_init(0x10130000, pic[17], 8); |
217 | 217 | sp804_init(0x101e2000, pic[4]); | ... | ... |
qemu-doc.texi
... | ... | @@ -77,10 +77,12 @@ For system emulation, the following hardware targets are supported: |
77 | 77 | @item Sun4m (32-bit Sparc processor) |
78 | 78 | @item Sun4u (64-bit Sparc processor, in progress) |
79 | 79 | @item Malta board (32-bit MIPS processor) |
80 | -@item ARM Integrator/CP (ARM926E, 1026E or 946E processor) | |
81 | -@item ARM Versatile baseboard (ARM926E) | |
82 | -@item ARM RealView Emulation baseboard (ARM926EJ-S) | |
80 | +@item ARM Integrator/CP (ARM) | |
81 | +@item ARM Versatile baseboard (ARM) | |
82 | +@item ARM RealView Emulation baseboard (ARM) | |
83 | 83 | @item Spitz, Akita, Borzoi and Terrier PDAs (PXA270 processor) |
84 | +@item Luminary Micro LM3S811EVB (ARM Cortex-M3) | |
85 | +@item Luminary Micro LM3S6965EVB (ARM Cortex-M3) | |
84 | 86 | @item Freescale MCF5208EVB (ColdFire V2). |
85 | 87 | @item Arnewsh MCF5206 evaluation board (ColdFire V2). |
86 | 88 | @item Palm Tungsten|E PDA (OMAP310 processor) |
... | ... | @@ -2117,7 +2119,7 @@ devices: |
2117 | 2119 | |
2118 | 2120 | @itemize @minus |
2119 | 2121 | @item |
2120 | -ARM926E, ARM1026E or ARM946E CPU | |
2122 | +ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU | |
2121 | 2123 | @item |
2122 | 2124 | Two PL011 UARTs |
2123 | 2125 | @item |
... | ... | @@ -2134,7 +2136,7 @@ The ARM Versatile baseboard is emulated with the following devices: |
2134 | 2136 | |
2135 | 2137 | @itemize @minus |
2136 | 2138 | @item |
2137 | -ARM926E CPU | |
2139 | +ARM926E, ARM1136 or Cortex-A8 CPU | |
2138 | 2140 | @item |
2139 | 2141 | PL190 Vectored Interrupt Controller |
2140 | 2142 | @item |
... | ... | @@ -2163,7 +2165,7 @@ The ARM RealView Emulation baseboard is emulated with the following devices: |
2163 | 2165 | |
2164 | 2166 | @itemize @minus |
2165 | 2167 | @item |
2166 | -ARM926E CPU | |
2168 | +ARM926E, ARM1136, ARM11MPCORE(x4) or Cortex-A8 CPU | |
2167 | 2169 | @item |
2168 | 2170 | ARM AMBA Generic/Distributed Interrupt Controller |
2169 | 2171 | @item |
... | ... | @@ -2237,6 +2239,34 @@ Secure Digital card connected to OMAP MMC/SD host |
2237 | 2239 | Three on-chip UARTs |
2238 | 2240 | @end itemize |
2239 | 2241 | |
2242 | +The Luminary Micro Stellaris LM3S811EVB emulation includes the following | |
2243 | +devices: | |
2244 | + | |
2245 | +@itemize @minus | |
2246 | +@item | |
2247 | +Cortex-M3 CPU core. | |
2248 | +@item | |
2249 | +64k Flash and 8k SRAM. | |
2250 | +@item | |
2251 | +Timers, UARTs, ADC and I@math{^2}C interface. | |
2252 | +@item | |
2253 | +OSRAM Pictiva 96x16 OLED with SSD0303 controller on I@math{^2}C bus. | |
2254 | +@end itemize | |
2255 | + | |
2256 | +The Luminary Micro Stellaris LM3S6965EVB emulation includes the following | |
2257 | +devices: | |
2258 | + | |
2259 | +@itemize @minus | |
2260 | +@item | |
2261 | +Cortex-M3 CPU core. | |
2262 | +@item | |
2263 | +256k Flash and 64k SRAM. | |
2264 | +@item | |
2265 | +Timers, UARTs, ADC, I@math{^2}C and SSI interfaces. | |
2266 | +@item | |
2267 | +OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via SSI. | |
2268 | +@end itemize | |
2269 | + | |
2240 | 2270 | A Linux 2.6 test image is available on the QEMU web site. More |
2241 | 2271 | information is available in the QEMU mailing-list archive. |
2242 | 2272 | ... | ... |
target-arm/cpu.h
... | ... | @@ -37,6 +37,18 @@ |
37 | 37 | #define EXCP_IRQ 5 |
38 | 38 | #define EXCP_FIQ 6 |
39 | 39 | #define EXCP_BKPT 7 |
40 | +#define EXCP_EXCEPTION_EXIT 8 /* Return from v7M exception. */ | |
41 | + | |
42 | +#define ARMV7M_EXCP_RESET 1 | |
43 | +#define ARMV7M_EXCP_NMI 2 | |
44 | +#define ARMV7M_EXCP_HARD 3 | |
45 | +#define ARMV7M_EXCP_MEM 4 | |
46 | +#define ARMV7M_EXCP_BUS 5 | |
47 | +#define ARMV7M_EXCP_USAGE 6 | |
48 | +#define ARMV7M_EXCP_SVC 11 | |
49 | +#define ARMV7M_EXCP_DEBUG 12 | |
50 | +#define ARMV7M_EXCP_PENDSV 14 | |
51 | +#define ARMV7M_EXCP_SYSTICK 15 | |
40 | 52 | |
41 | 53 | typedef void ARMWriteCPFunc(void *opaque, int cp_info, |
42 | 54 | int srcreg, int operand, uint32_t value); |
... | ... | @@ -76,17 +88,22 @@ typedef struct CPUARMState { |
76 | 88 | uint32_t VF; /* V is the bit 31. All other bits are undefined */ |
77 | 89 | uint32_t NZF; /* N is bit 31. Z is computed from NZF */ |
78 | 90 | uint32_t QF; /* 0 or 1 */ |
79 | - | |
80 | - int thumb; /* 0 = arm mode, 1 = thumb mode */ | |
91 | + uint32_t GE; /* cpsr[19:16] */ | |
92 | + int thumb; /* cprs[5]. 0 = arm mode, 1 = thumb mode. */ | |
93 | + uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ | |
81 | 94 | |
82 | 95 | /* System control coprocessor (cp15) */ |
83 | 96 | struct { |
84 | 97 | uint32_t c0_cpuid; |
85 | 98 | uint32_t c0_cachetype; |
99 | + uint32_t c0_c1[8]; /* Feature registers. */ | |
100 | + uint32_t c0_c2[8]; /* Instruction set registers. */ | |
86 | 101 | uint32_t c1_sys; /* System control register. */ |
87 | 102 | uint32_t c1_coproc; /* Coprocessor access register. */ |
88 | 103 | uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ |
89 | - uint32_t c2_base; /* MMU translation table base. */ | |
104 | + uint32_t c2_base0; /* MMU translation table base 0. */ | |
105 | + uint32_t c2_base1; /* MMU translation table base 1. */ | |
106 | + uint32_t c2_mask; /* MMU translation table base mask. */ | |
90 | 107 | uint32_t c2_data; /* MPU data cachable bits. */ |
91 | 108 | uint32_t c2_insn; /* MPU instruction cachable bits. */ |
92 | 109 | uint32_t c3; /* MMU domain access control register |
... | ... | @@ -100,6 +117,9 @@ typedef struct CPUARMState { |
100 | 117 | uint32_t c9_data; |
101 | 118 | uint32_t c13_fcse; /* FCSE PID. */ |
102 | 119 | uint32_t c13_context; /* Context ID. */ |
120 | + uint32_t c13_tls1; /* User RW Thread register. */ | |
121 | + uint32_t c13_tls2; /* User RO Thread register. */ | |
122 | + uint32_t c13_tls3; /* Privileged Thread register. */ | |
103 | 123 | uint32_t c15_cpar; /* XScale Coprocessor Access Register */ |
104 | 124 | uint32_t c15_ticonfig; /* TI925T configuration byte. */ |
105 | 125 | uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ |
... | ... | @@ -107,6 +127,17 @@ typedef struct CPUARMState { |
107 | 127 | uint32_t c15_threadid; /* TI debugger thread-ID. */ |
108 | 128 | } cp15; |
109 | 129 | |
130 | + struct { | |
131 | + uint32_t other_sp; | |
132 | + uint32_t vecbase; | |
133 | + uint32_t basepri; | |
134 | + uint32_t control; | |
135 | + int current_sp; | |
136 | + int exception; | |
137 | + int pending_exception; | |
138 | + void *nvic; | |
139 | + } v7m; | |
140 | + | |
110 | 141 | /* Coprocessor IO used by peripherals */ |
111 | 142 | struct { |
112 | 143 | ARMReadCPFunc *cp_read; |
... | ... | @@ -117,6 +148,10 @@ typedef struct CPUARMState { |
117 | 148 | /* Internal CPU feature flags. */ |
118 | 149 | uint32_t features; |
119 | 150 | |
151 | + /* Callback for vectored interrupt controller. */ | |
152 | + int (*get_irq_vector)(struct CPUARMState *); | |
153 | + void *irq_opaque; | |
154 | + | |
120 | 155 | /* exception/interrupt handling */ |
121 | 156 | jmp_buf jmp_env; |
122 | 157 | int exception_index; |
... | ... | @@ -126,7 +161,7 @@ typedef struct CPUARMState { |
126 | 161 | |
127 | 162 | /* VFP coprocessor state. */ |
128 | 163 | struct { |
129 | - float64 regs[16]; | |
164 | + float64 regs[32]; | |
130 | 165 | |
131 | 166 | uint32_t xregs[16]; |
132 | 167 | /* We store these fpcsr fields separately for convenience. */ |
... | ... | @@ -136,9 +171,16 @@ typedef struct CPUARMState { |
136 | 171 | /* Temporary variables if we don't have spare fp regs. */ |
137 | 172 | float32 tmp0s, tmp1s; |
138 | 173 | float64 tmp0d, tmp1d; |
174 | + /* scratch space when Tn are not sufficient. */ | |
175 | + uint32_t scratch[8]; | |
139 | 176 | |
140 | 177 | float_status fp_status; |
141 | 178 | } vfp; |
179 | +#if defined(CONFIG_USER_ONLY) | |
180 | + struct mmon_state *mmon_entry; | |
181 | +#else | |
182 | + uint32_t mmon_addr; | |
183 | +#endif | |
142 | 184 | |
143 | 185 | /* iwMMXt coprocessor state. */ |
144 | 186 | struct { |
... | ... | @@ -169,6 +211,7 @@ int cpu_arm_exec(CPUARMState *s); |
169 | 211 | void cpu_arm_close(CPUARMState *s); |
170 | 212 | void do_interrupt(CPUARMState *); |
171 | 213 | void switch_mode(CPUARMState *, int); |
214 | +uint32_t do_arm_semihosting(CPUARMState *env); | |
172 | 215 | |
173 | 216 | /* you can call this signal handler from your SIGBUS and SIGSEGV |
174 | 217 | signal handlers to inform the virtual CPU of exceptions. non zero |
... | ... | @@ -176,6 +219,9 @@ void switch_mode(CPUARMState *, int); |
176 | 219 | int cpu_arm_signal_handler(int host_signum, void *pinfo, |
177 | 220 | void *puc); |
178 | 221 | |
222 | +void cpu_lock(void); | |
223 | +void cpu_unlock(void); | |
224 | + | |
179 | 225 | #define CPSR_M (0x1f) |
180 | 226 | #define CPSR_T (1 << 5) |
181 | 227 | #define CPSR_F (1 << 6) |
... | ... | @@ -183,13 +229,24 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo, |
183 | 229 | #define CPSR_A (1 << 8) |
184 | 230 | #define CPSR_E (1 << 9) |
185 | 231 | #define CPSR_IT_2_7 (0xfc00) |
186 | -/* Bits 20-23 reserved. */ | |
232 | +#define CPSR_GE (0xf << 16) | |
233 | +#define CPSR_RESERVED (0xf << 20) | |
187 | 234 | #define CPSR_J (1 << 24) |
188 | 235 | #define CPSR_IT_0_1 (3 << 25) |
189 | 236 | #define CPSR_Q (1 << 27) |
190 | -#define CPSR_NZCV (0xf << 28) | |
237 | +#define CPSR_V (1 << 28) | |
238 | +#define CPSR_C (1 << 29) | |
239 | +#define CPSR_Z (1 << 30) | |
240 | +#define CPSR_N (1 << 31) | |
241 | +#define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V) | |
242 | + | |
243 | +#define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7) | |
244 | +#define CACHED_CPSR_BITS (CPSR_T | CPSR_GE | CPSR_IT | CPSR_Q | CPSR_NZCV) | |
245 | +/* Bits writable in user mode. */ | |
246 | +#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE) | |
247 | +/* Execution state bits. MRS read as zero, MSR writes ignored. */ | |
248 | +#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J) | |
191 | 249 | |
192 | -#define CACHED_CPSR_BITS (CPSR_T | CPSR_Q | CPSR_NZCV) | |
193 | 250 | /* Return the current CPSR value. */ |
194 | 251 | static inline uint32_t cpsr_read(CPUARMState *env) |
195 | 252 | { |
... | ... | @@ -197,7 +254,21 @@ static inline uint32_t cpsr_read(CPUARMState *env) |
197 | 254 | ZF = (env->NZF == 0); |
198 | 255 | return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) | |
199 | 256 | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27) |
200 | - | (env->thumb << 5); | |
257 | + | (env->thumb << 5) | ((env->condexec_bits & 3) << 25) | |
258 | + | ((env->condexec_bits & 0xfc) << 8) | |
259 | + | (env->GE << 16); | |
260 | +} | |
261 | + | |
262 | +/* Return the current xPSR value. */ | |
263 | +static inline uint32_t xpsr_read(CPUARMState *env) | |
264 | +{ | |
265 | + int ZF; | |
266 | + ZF = (env->NZF == 0); | |
267 | + return (env->NZF & 0x80000000) | (ZF << 30) | |
268 | + | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27) | |
269 | + | (env->thumb << 24) | ((env->condexec_bits & 3) << 25) | |
270 | + | ((env->condexec_bits & 0xfc) << 8) | |
271 | + | env->v7m.exception; | |
201 | 272 | } |
202 | 273 | |
203 | 274 | /* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */ |
... | ... | @@ -213,6 +284,17 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) |
213 | 284 | env->QF = ((val & CPSR_Q) != 0); |
214 | 285 | if (mask & CPSR_T) |
215 | 286 | env->thumb = ((val & CPSR_T) != 0); |
287 | + if (mask & CPSR_IT_0_1) { | |
288 | + env->condexec_bits &= ~3; | |
289 | + env->condexec_bits |= (val >> 25) & 3; | |
290 | + } | |
291 | + if (mask & CPSR_IT_2_7) { | |
292 | + env->condexec_bits &= 3; | |
293 | + env->condexec_bits |= (val >> 8) & 0xfc; | |
294 | + } | |
295 | + if (mask & CPSR_GE) { | |
296 | + env->GE = (val >> 16) & 0xf; | |
297 | + } | |
216 | 298 | |
217 | 299 | if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { |
218 | 300 | switch_mode(env, val & CPSR_M); |
... | ... | @@ -221,6 +303,32 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) |
221 | 303 | env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); |
222 | 304 | } |
223 | 305 | |
306 | +/* Set the xPSR. Note that some bits of mask must be all-set or all-clear. */ | |
307 | +static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) | |
308 | +{ | |
309 | + /* NOTE: N = 1 and Z = 1 cannot be stored currently */ | |
310 | + if (mask & CPSR_NZCV) { | |
311 | + env->NZF = (val & 0xc0000000) ^ 0x40000000; | |
312 | + env->CF = (val >> 29) & 1; | |
313 | + env->VF = (val << 3) & 0x80000000; | |
314 | + } | |
315 | + if (mask & CPSR_Q) | |
316 | + env->QF = ((val & CPSR_Q) != 0); | |
317 | + if (mask & (1 << 24)) | |
318 | + env->thumb = ((val & (1 << 24)) != 0); | |
319 | + if (mask & CPSR_IT_0_1) { | |
320 | + env->condexec_bits &= ~3; | |
321 | + env->condexec_bits |= (val >> 25) & 3; | |
322 | + } | |
323 | + if (mask & CPSR_IT_2_7) { | |
324 | + env->condexec_bits &= 3; | |
325 | + env->condexec_bits |= (val >> 8) & 0xfc; | |
326 | + } | |
327 | + if (mask & 0x1ff) { | |
328 | + env->v7m.exception = val & 0x1ff; | |
329 | + } | |
330 | +} | |
331 | + | |
224 | 332 | enum arm_cpu_mode { |
225 | 333 | ARM_CPU_MODE_USR = 0x10, |
226 | 334 | ARM_CPU_MODE_FIQ = 0x11, |
... | ... | @@ -234,6 +342,8 @@ enum arm_cpu_mode { |
234 | 342 | /* VFP system registers. */ |
235 | 343 | #define ARM_VFP_FPSID 0 |
236 | 344 | #define ARM_VFP_FPSCR 1 |
345 | +#define ARM_VFP_MVFR1 6 | |
346 | +#define ARM_VFP_MVFR0 7 | |
237 | 347 | #define ARM_VFP_FPEXC 8 |
238 | 348 | #define ARM_VFP_FPINST 9 |
239 | 349 | #define ARM_VFP_FPINST2 10 |
... | ... | @@ -253,7 +363,15 @@ enum arm_features { |
253 | 363 | ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ |
254 | 364 | ARM_FEATURE_XSCALE, /* Intel XScale extensions. */ |
255 | 365 | ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ |
366 | + ARM_FEATURE_V6, | |
367 | + ARM_FEATURE_V6K, | |
368 | + ARM_FEATURE_V7, | |
369 | + ARM_FEATURE_THUMB2, | |
256 | 370 | ARM_FEATURE_MPU, /* Only has Memory Protection Unit, not full MMU. */ |
371 | + ARM_FEATURE_VFP3, | |
372 | + ARM_FEATURE_NEON, | |
373 | + ARM_FEATURE_DIV, | |
374 | + ARM_FEATURE_M, /* Microcontroller profile. */ | |
257 | 375 | ARM_FEATURE_OMAPCP /* OMAP specific CP15 ops handling. */ |
258 | 376 | }; |
259 | 377 | |
... | ... | @@ -264,27 +382,44 @@ static inline int arm_feature(CPUARMState *env, int feature) |
264 | 382 | |
265 | 383 | void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); |
266 | 384 | |
385 | +/* Interface between CPU and Interrupt controller. */ | |
386 | +void armv7m_nvic_set_pending(void *opaque, int irq); | |
387 | +int armv7m_nvic_acknowledge_irq(void *opaque); | |
388 | +void armv7m_nvic_complete_irq(void *opaque, int irq); | |
389 | + | |
267 | 390 | void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, |
268 | 391 | ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, |
269 | 392 | void *opaque); |
270 | 393 | |
271 | -#define ARM_CPUID_ARM1026 0x4106a262 | |
272 | -#define ARM_CPUID_ARM926 0x41069265 | |
273 | -#define ARM_CPUID_ARM946 0x41059461 | |
274 | -#define ARM_CPUID_TI915T 0x54029152 | |
275 | -#define ARM_CPUID_TI925T 0x54029252 | |
276 | -#define ARM_CPUID_PXA250 0x69052100 | |
277 | -#define ARM_CPUID_PXA255 0x69052d00 | |
278 | -#define ARM_CPUID_PXA260 0x69052903 | |
279 | -#define ARM_CPUID_PXA261 0x69052d05 | |
280 | -#define ARM_CPUID_PXA262 0x69052d06 | |
281 | -#define ARM_CPUID_PXA270 0x69054110 | |
282 | -#define ARM_CPUID_PXA270_A0 0x69054110 | |
283 | -#define ARM_CPUID_PXA270_A1 0x69054111 | |
284 | -#define ARM_CPUID_PXA270_B0 0x69054112 | |
285 | -#define ARM_CPUID_PXA270_B1 0x69054113 | |
286 | -#define ARM_CPUID_PXA270_C0 0x69054114 | |
287 | -#define ARM_CPUID_PXA270_C5 0x69054117 | |
394 | +/* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3. | |
395 | + Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are | |
396 | + conventional cores (ie. Application or Realtime profile). */ | |
397 | + | |
398 | +#define IS_M(env) arm_feature(env, ARM_FEATURE_M) | |
399 | +#define ARM_CPUID(env) (env->cp15.c0_cpuid) | |
400 | + | |
401 | +#define ARM_CPUID_ARM1026 0x4106a262 | |
402 | +#define ARM_CPUID_ARM926 0x41069265 | |
403 | +#define ARM_CPUID_ARM946 0x41059461 | |
404 | +#define ARM_CPUID_TI915T 0x54029152 | |
405 | +#define ARM_CPUID_TI925T 0x54029252 | |
406 | +#define ARM_CPUID_PXA250 0x69052100 | |
407 | +#define ARM_CPUID_PXA255 0x69052d00 | |
408 | +#define ARM_CPUID_PXA260 0x69052903 | |
409 | +#define ARM_CPUID_PXA261 0x69052d05 | |
410 | +#define ARM_CPUID_PXA262 0x69052d06 | |
411 | +#define ARM_CPUID_PXA270 0x69054110 | |
412 | +#define ARM_CPUID_PXA270_A0 0x69054110 | |
413 | +#define ARM_CPUID_PXA270_A1 0x69054111 | |
414 | +#define ARM_CPUID_PXA270_B0 0x69054112 | |
415 | +#define ARM_CPUID_PXA270_B1 0x69054113 | |
416 | +#define ARM_CPUID_PXA270_C0 0x69054114 | |
417 | +#define ARM_CPUID_PXA270_C5 0x69054117 | |
418 | +#define ARM_CPUID_ARM1136 0x4117b363 | |
419 | +#define ARM_CPUID_ARM11MPCORE 0x410fb022 | |
420 | +#define ARM_CPUID_CORTEXA8 0x410fc080 | |
421 | +#define ARM_CPUID_CORTEXM3 0x410fc231 | |
422 | +#define ARM_CPUID_ANY 0xffffffff | |
288 | 423 | |
289 | 424 | #if defined(CONFIG_USER_ONLY) |
290 | 425 | #define TARGET_PAGE_BITS 12 |
... | ... | @@ -302,6 +437,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, |
302 | 437 | #define cpu_signal_handler cpu_arm_signal_handler |
303 | 438 | #define cpu_list arm_cpu_list |
304 | 439 | |
440 | +#define ARM_CPU_SAVE_VERSION 1 | |
441 | + | |
305 | 442 | /* MMU modes definitions */ |
306 | 443 | #define MMU_MODE0_SUFFIX _kernel |
307 | 444 | #define MMU_MODE1_SUFFIX _user | ... | ... |
target-arm/exec.h
... | ... | @@ -68,12 +68,18 @@ static inline int cpu_halted(CPUState *env) { |
68 | 68 | |
69 | 69 | /* In op_helper.c */ |
70 | 70 | |
71 | -void cpu_lock(void); | |
72 | -void cpu_unlock(void); | |
73 | 71 | void helper_set_cp(CPUState *, uint32_t, uint32_t); |
74 | 72 | uint32_t helper_get_cp(CPUState *, uint32_t); |
75 | 73 | void helper_set_cp15(CPUState *, uint32_t, uint32_t); |
76 | 74 | uint32_t helper_get_cp15(CPUState *, uint32_t); |
75 | +void helper_set_r13_banked(CPUState *env, int mode, uint32_t val); | |
76 | +uint32_t helper_get_r13_banked(CPUState *env, int mode); | |
77 | +uint32_t helper_v7m_mrs(CPUState *env, int reg); | |
78 | +void helper_v7m_msr(CPUState *env, int reg, uint32_t val); | |
79 | + | |
80 | +void helper_mark_exclusive(CPUARMState *, uint32_t addr); | |
81 | +int helper_test_exclusive(CPUARMState *, uint32_t addr); | |
82 | +void helper_clrex(CPUARMState *env); | |
77 | 83 | |
78 | 84 | void cpu_loop_exit(void); |
79 | 85 | |
... | ... | @@ -91,4 +97,11 @@ void do_vfp_cmpes(void); |
91 | 97 | void do_vfp_cmped(void); |
92 | 98 | void do_vfp_set_fpscr(void); |
93 | 99 | void do_vfp_get_fpscr(void); |
94 | - | |
100 | +float32 helper_recps_f32(float32, float32); | |
101 | +float32 helper_rsqrts_f32(float32, float32); | |
102 | +uint32_t helper_recpe_u32(uint32_t); | |
103 | +uint32_t helper_rsqrte_u32(uint32_t); | |
104 | +float32 helper_recpe_f32(float32); | |
105 | +float32 helper_rsqrte_f32(float32); | |
106 | +void helper_neon_tbl(int rn, int maxindex); | |
107 | +uint32_t helper_neon_mul_p8(uint32_t op1, uint32_t op2); | ... | ... |