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); | ... | ... |