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,6 +17,7 @@ | ||
| 17 | - MIPS mipssim pequdo machine (Thiemo Seufer) | 17 | - MIPS mipssim pequdo machine (Thiemo Seufer) |
| 18 | - Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh) | 18 | - Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh) |
| 19 | - OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski) | 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 | version 0.9.0: | 22 | version 0.9.0: |
| 22 | 23 |
Makefile.target
| @@ -493,7 +493,9 @@ ifeq ($(TARGET_BASE_ARCH), arm) | @@ -493,7 +493,9 @@ ifeq ($(TARGET_BASE_ARCH), arm) | ||
| 493 | VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o | 493 | VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o |
| 494 | VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o | 494 | VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o |
| 495 | VL_OBJS+= versatile_pci.o sd.o ptimer.o | 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 | VL_OBJS+= arm-semi.o | 499 | VL_OBJS+= arm-semi.o |
| 498 | VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o | 500 | VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o |
| 499 | VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o | 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,6 +173,7 @@ static inline TranslationBlock *tb_find_fast(void) | ||
| 173 | flags |= (1 << 6); | 173 | flags |= (1 << 6); |
| 174 | if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) | 174 | if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) |
| 175 | flags |= (1 << 7); | 175 | flags |= (1 << 7); |
| 176 | + flags |= (env->condexec_bits << 8); | ||
| 176 | cs_base = 0; | 177 | cs_base = 0; |
| 177 | pc = env->regs[15]; | 178 | pc = env->regs[15]; |
| 178 | #elif defined(TARGET_SPARC) | 179 | #elif defined(TARGET_SPARC) |
| @@ -511,8 +512,18 @@ int cpu_exec(CPUState *env1) | @@ -511,8 +512,18 @@ int cpu_exec(CPUState *env1) | ||
| 511 | env->exception_index = EXCP_FIQ; | 512 | env->exception_index = EXCP_FIQ; |
| 512 | do_interrupt(env); | 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 | if (interrupt_request & CPU_INTERRUPT_HARD | 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 | env->exception_index = EXCP_IRQ; | 527 | env->exception_index = EXCP_IRQ; |
| 517 | do_interrupt(env); | 528 | do_interrupt(env); |
| 518 | } | 529 | } |
fpu/softfloat-native.h
| @@ -224,6 +224,11 @@ INLINE float32 float32_chs(float32 a) | @@ -224,6 +224,11 @@ INLINE float32 float32_chs(float32 a) | ||
| 224 | return -a; | 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 | | Software IEC/IEEE double-precision conversion routines. | 233 | | Software IEC/IEEE double-precision conversion routines. |
| 229 | *----------------------------------------------------------------------------*/ | 234 | *----------------------------------------------------------------------------*/ |
| @@ -311,6 +316,11 @@ INLINE float64 float64_chs(float64 a) | @@ -311,6 +316,11 @@ INLINE float64 float64_chs(float64 a) | ||
| 311 | return -a; | 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 | #ifdef FLOATX80 | 324 | #ifdef FLOATX80 |
| 315 | 325 | ||
| 316 | /*---------------------------------------------------------------------------- | 326 | /*---------------------------------------------------------------------------- |
| @@ -391,4 +401,10 @@ INLINE floatx80 floatx80_chs(floatx80 a) | @@ -391,4 +401,10 @@ INLINE floatx80 floatx80_chs(floatx80 a) | ||
| 391 | { | 401 | { |
| 392 | return -a; | 402 | return -a; |
| 393 | } | 403 | } |
| 404 | + | ||
| 405 | +INLINE floatx80 floatx80_scalbn(floatx80 a, int n) | ||
| 406 | +{ | ||
| 407 | + return scalbnl(a, n); | ||
| 408 | +} | ||
| 409 | + | ||
| 394 | #endif | 410 | #endif |
fpu/softfloat.c
| @@ -5377,3 +5377,78 @@ int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \ | @@ -5377,3 +5377,78 @@ int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \ | ||
| 5377 | 5377 | ||
| 5378 | COMPARE(32, 0xff) | 5378 | COMPARE(32, 0xff) |
| 5379 | COMPARE(64, 0x7ff) | 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,6 +244,7 @@ int float32_compare( float32, float32 STATUS_PARAM ); | ||
| 244 | int float32_compare_quiet( float32, float32 STATUS_PARAM ); | 244 | int float32_compare_quiet( float32, float32 STATUS_PARAM ); |
| 245 | int float32_is_nan( float32 ); | 245 | int float32_is_nan( float32 ); |
| 246 | int float32_is_signaling_nan( float32 ); | 246 | int float32_is_signaling_nan( float32 ); |
| 247 | +float32 float32_scalbn( float32, int STATUS_PARAM ); | ||
| 247 | 248 | ||
| 248 | INLINE float32 float32_abs(float32 a) | 249 | INLINE float32 float32_abs(float32 a) |
| 249 | { | 250 | { |
| @@ -295,6 +296,7 @@ int float64_compare( float64, float64 STATUS_PARAM ); | @@ -295,6 +296,7 @@ int float64_compare( float64, float64 STATUS_PARAM ); | ||
| 295 | int float64_compare_quiet( float64, float64 STATUS_PARAM ); | 296 | int float64_compare_quiet( float64, float64 STATUS_PARAM ); |
| 296 | int float64_is_nan( float64 a ); | 297 | int float64_is_nan( float64 a ); |
| 297 | int float64_is_signaling_nan( float64 ); | 298 | int float64_is_signaling_nan( float64 ); |
| 299 | +float64 float64_scalbn( float64, int STATUS_PARAM ); | ||
| 298 | 300 | ||
| 299 | INLINE float64 float64_abs(float64 a) | 301 | INLINE float64 float64_abs(float64 a) |
| 300 | { | 302 | { |
| @@ -339,6 +341,7 @@ int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); | @@ -339,6 +341,7 @@ int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); | ||
| 339 | int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); | 341 | int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); |
| 340 | int floatx80_is_nan( floatx80 ); | 342 | int floatx80_is_nan( floatx80 ); |
| 341 | int floatx80_is_signaling_nan( floatx80 ); | 343 | int floatx80_is_signaling_nan( floatx80 ); |
| 344 | +floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM ); | ||
| 342 | 345 | ||
| 343 | INLINE floatx80 floatx80_abs(floatx80 a) | 346 | INLINE floatx80 floatx80_abs(floatx80 a) |
| 344 | { | 347 | { |
| @@ -387,6 +390,7 @@ int float128_le_quiet( float128, float128 STATUS_PARAM ); | @@ -387,6 +390,7 @@ int float128_le_quiet( float128, float128 STATUS_PARAM ); | ||
| 387 | int float128_lt_quiet( float128, float128 STATUS_PARAM ); | 390 | int float128_lt_quiet( float128, float128 STATUS_PARAM ); |
| 388 | int float128_is_nan( float128 ); | 391 | int float128_is_nan( float128 ); |
| 389 | int float128_is_signaling_nan( float128 ); | 392 | int float128_is_signaling_nan( float128 ); |
| 393 | +float128 float128_scalbn( float128, int STATUS_PARAM ); | ||
| 390 | 394 | ||
| 391 | INLINE float128 float128_abs(float128 a) | 395 | INLINE float128 float128_abs(float128 a) |
| 392 | { | 396 | { |
hw/arm_boot.c
| 1 | /* | 1 | /* |
| 2 | * ARM kernel loader. | 2 | * ARM kernel loader. |
| 3 | * | 3 | * |
| 4 | - * Copyright (c) 2006 CodeSourcery. | 4 | + * Copyright (c) 2006-2007 CodeSourcery. |
| 5 | * Written by Paul Brook | 5 | * Written by Paul Brook |
| 6 | * | 6 | * |
| 7 | * This code is licenced under the GPL. | 7 | * This code is licenced under the GPL. |
| @@ -24,6 +24,22 @@ static uint32_t bootloader[] = { | @@ -24,6 +24,22 @@ static uint32_t bootloader[] = { | ||
| 24 | 0 /* Kernel entry point. Set by integratorcp_init. */ | 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 | static void main_cpu_reset(void *opaque) | 43 | static void main_cpu_reset(void *opaque) |
| 28 | { | 44 | { |
| 29 | CPUState *env = opaque; | 45 | CPUState *env = opaque; |
| @@ -33,6 +49,8 @@ static void main_cpu_reset(void *opaque) | @@ -33,6 +49,8 @@ static void main_cpu_reset(void *opaque) | ||
| 33 | arm_load_kernel(env, env->ram_size, env->kernel_filename, | 49 | arm_load_kernel(env, env->ram_size, env->kernel_filename, |
| 34 | env->kernel_cmdline, env->initrd_filename, | 50 | env->kernel_cmdline, env->initrd_filename, |
| 35 | env->board_id, env->loader_start); | 51 | env->board_id, env->loader_start); |
| 52 | + | ||
| 53 | + /* TODO: Reset secondary CPUs. */ | ||
| 36 | } | 54 | } |
| 37 | 55 | ||
| 38 | static void set_kernel_args(uint32_t ram_size, int initrd_size, | 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,6 +229,8 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename, | ||
| 211 | bootloader[6] = entry; | 229 | bootloader[6] = entry; |
| 212 | for (n = 0; n < sizeof(bootloader) / 4; n++) | 230 | for (n = 0; n < sizeof(bootloader) / 4; n++) |
| 213 | stl_raw(phys_ram_base + (n * 4), bootloader[n]); | 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 | if (old_param) | 234 | if (old_param) |
| 215 | set_kernel_args_old(ram_size, initrd_size, | 235 | set_kernel_args_old(ram_size, initrd_size, |
| 216 | kernel_cmdline, loader_start); | 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 | * Written by Paul Brook | 5 | * Written by Paul Brook |
| 6 | * | 6 | * |
| 7 | * This code is licenced under the GPL. | 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 | //#define DEBUG_GIC | 14 | //#define DEBUG_GIC |
| 17 | 15 | ||
| @@ -22,58 +20,84 @@ do { printf("arm_gic: " fmt , ##args); } while (0) | @@ -22,58 +20,84 @@ do { printf("arm_gic: " fmt , ##args); } while (0) | ||
| 22 | #define DPRINTF(fmt, args...) do {} while(0) | 20 | #define DPRINTF(fmt, args...) do {} while(0) |
| 23 | #endif | 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 | static const uint8_t gic_id[] = | 31 | static const uint8_t gic_id[] = |
| 28 | { 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | 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 | typedef struct gic_irq_state | 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 | unsigned enabled:1; | 41 | unsigned enabled:1; |
| 35 | - unsigned pending:1; | ||
| 36 | - unsigned active:1; | 42 | + unsigned pending:NCPU; |
| 43 | + unsigned active:NCPU; | ||
| 37 | unsigned level:1; | 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 | unsigned trigger:1; /* nonzero = edge triggered. */ | 46 | unsigned trigger:1; /* nonzero = edge triggered. */ |
| 40 | } gic_irq_state; | 47 | } gic_irq_state; |
| 41 | 48 | ||
| 49 | +#define ALL_CPU_MASK ((1 << NCPU) - 1) | ||
| 50 | + | ||
| 42 | #define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1 | 51 | #define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1 |
| 43 | #define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0 | 52 | #define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0 |
| 44 | #define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled | 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 | #define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 | 60 | #define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 |
| 52 | #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 | 61 | #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 |
| 53 | #define GIC_TEST_MODEL(irq) s->irq_state[irq].model | 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 | #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 | 66 | #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 |
| 58 | #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 | 67 | #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 |
| 59 | #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger | 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 | typedef struct gic_state | 77 | typedef struct gic_state |
| 62 | { | 78 | { |
| 63 | uint32_t base; | 79 | uint32_t base; |
| 64 | - qemu_irq parent_irq; | 80 | + qemu_irq parent_irq[NCPU]; |
| 65 | int enabled; | 81 | int enabled; |
| 66 | - int cpu_enabled; | 82 | + int cpu_enabled[NCPU]; |
| 67 | 83 | ||
| 68 | gic_irq_state irq_state[GIC_NIRQ]; | 84 | gic_irq_state irq_state[GIC_NIRQ]; |
| 85 | +#ifndef NVIC | ||
| 69 | int irq_target[GIC_NIRQ]; | 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 | } gic_state; | 101 | } gic_state; |
| 78 | 102 | ||
| 79 | /* TODO: Many places that call this routine could be optimized. */ | 103 | /* TODO: Many places that call this routine could be optimized. */ |
| @@ -83,112 +107,136 @@ static void gic_update(gic_state *s) | @@ -83,112 +107,136 @@ static void gic_update(gic_state *s) | ||
| 83 | int best_irq; | 107 | int best_irq; |
| 84 | int best_prio; | 108 | int best_prio; |
| 85 | int irq; | 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 | static void gic_set_irq(void *opaque, int irq, int level) | 157 | static void gic_set_irq(void *opaque, int irq, int level) |
| 114 | { | 158 | { |
| 115 | gic_state *s = (gic_state *)opaque; | 159 | gic_state *s = (gic_state *)opaque; |
| 116 | /* The first external input line is internal interrupt 32. */ | 160 | /* The first external input line is internal interrupt 32. */ |
| 117 | irq += 32; | 161 | irq += 32; |
| 118 | - if (level == GIC_TEST_LEVEL(irq)) | 162 | + if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK)) |
| 119 | return; | 163 | return; |
| 120 | 164 | ||
| 121 | if (level) { | 165 | if (level) { |
| 122 | - GIC_SET_LEVEL(irq); | 166 | + GIC_SET_LEVEL(irq, ALL_CPU_MASK); |
| 123 | if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) { | 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 | } else { | 171 | } else { |
| 128 | - GIC_CLEAR_LEVEL(irq); | 172 | + GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK); |
| 129 | } | 173 | } |
| 130 | gic_update(s); | 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 | gic_update(s); | 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 | int new_irq; | 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 | DPRINTF("ACK no pending IRQ\n"); | 195 | DPRINTF("ACK no pending IRQ\n"); |
| 149 | return 1023; | 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 | DPRINTF("ACK %d\n", new_irq); | 203 | DPRINTF("ACK %d\n", new_irq); |
| 158 | return new_irq; | 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 | int update = 0; | 209 | int update = 0; |
| 210 | + int cm = 1 << cpu; | ||
| 164 | DPRINTF("EOI %d\n", irq); | 211 | DPRINTF("EOI %d\n", irq); |
| 165 | - if (s->running_irq == 1023) | 212 | + if (s->running_irq[cpu] == 1023) |
| 166 | return; /* No active IRQ. */ | 213 | return; /* No active IRQ. */ |
| 167 | if (irq != 1023) { | 214 | if (irq != 1023) { |
| 168 | /* Mark level triggered interrupts as pending if they are still | 215 | /* Mark level triggered interrupts as pending if they are still |
| 169 | raised. */ | 216 | raised. */ |
| 170 | if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq) | 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 | update = 1; | 221 | update = 1; |
| 174 | } | 222 | } |
| 175 | } | 223 | } |
| 176 | - if (irq != s->running_irq) { | 224 | + if (irq != s->running_irq[cpu]) { |
| 177 | /* Complete an IRQ that is not currently running. */ | 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 | break; | 230 | break; |
| 183 | } | 231 | } |
| 184 | - tmp = s->last_active[tmp]; | 232 | + tmp = s->last_active[tmp][cpu]; |
| 185 | } | 233 | } |
| 186 | if (update) { | 234 | if (update) { |
| 187 | gic_update(s); | 235 | gic_update(s); |
| 188 | } | 236 | } |
| 189 | } else { | 237 | } else { |
| 190 | /* Complete the current running IRQ. */ | 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,15 +246,22 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | ||
| 198 | uint32_t res; | 246 | uint32_t res; |
| 199 | int irq; | 247 | int irq; |
| 200 | int i; | 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 | if (offset < 0x100) { | 256 | if (offset < 0x100) { |
| 257 | +#ifndef NVIC | ||
| 204 | if (offset == 0) | 258 | if (offset == 0) |
| 205 | return s->enabled; | 259 | return s->enabled; |
| 206 | if (offset == 4) | 260 | if (offset == 4) |
| 207 | - return (GIC_NIRQ / 32) - 1; | 261 | + return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5); |
| 208 | if (offset < 0x08) | 262 | if (offset < 0x08) |
| 209 | return 0; | 263 | return 0; |
| 264 | +#endif | ||
| 210 | goto bad_reg; | 265 | goto bad_reg; |
| 211 | } else if (offset < 0x200) { | 266 | } else if (offset < 0x200) { |
| 212 | /* Interrupt Set/Clear Enable. */ | 267 | /* Interrupt Set/Clear Enable. */ |
| @@ -214,6 +269,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | @@ -214,6 +269,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | ||
| 214 | irq = (offset - 0x100) * 8; | 269 | irq = (offset - 0x100) * 8; |
| 215 | else | 270 | else |
| 216 | irq = (offset - 0x180) * 8; | 271 | irq = (offset - 0x180) * 8; |
| 272 | + irq += GIC_BASE_IRQ; | ||
| 217 | if (irq >= GIC_NIRQ) | 273 | if (irq >= GIC_NIRQ) |
| 218 | goto bad_reg; | 274 | goto bad_reg; |
| 219 | res = 0; | 275 | res = 0; |
| @@ -228,40 +284,48 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | @@ -228,40 +284,48 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | ||
| 228 | irq = (offset - 0x200) * 8; | 284 | irq = (offset - 0x200) * 8; |
| 229 | else | 285 | else |
| 230 | irq = (offset - 0x280) * 8; | 286 | irq = (offset - 0x280) * 8; |
| 287 | + irq += GIC_BASE_IRQ; | ||
| 231 | if (irq >= GIC_NIRQ) | 288 | if (irq >= GIC_NIRQ) |
| 232 | goto bad_reg; | 289 | goto bad_reg; |
| 233 | res = 0; | 290 | res = 0; |
| 291 | + mask = (irq < 32) ? cm : ALL_CPU_MASK; | ||
| 234 | for (i = 0; i < 8; i++) { | 292 | for (i = 0; i < 8; i++) { |
| 235 | - if (GIC_TEST_PENDING(irq + i)) { | 293 | + if (GIC_TEST_PENDING(irq + i, mask)) { |
| 236 | res |= (1 << i); | 294 | res |= (1 << i); |
| 237 | } | 295 | } |
| 238 | } | 296 | } |
| 239 | } else if (offset < 0x400) { | 297 | } else if (offset < 0x400) { |
| 240 | /* Interrupt Active. */ | 298 | /* Interrupt Active. */ |
| 241 | - irq = (offset - 0x300) * 8; | 299 | + irq = (offset - 0x300) * 8 + GIC_BASE_IRQ; |
| 242 | if (irq >= GIC_NIRQ) | 300 | if (irq >= GIC_NIRQ) |
| 243 | goto bad_reg; | 301 | goto bad_reg; |
| 244 | res = 0; | 302 | res = 0; |
| 303 | + mask = (irq < 32) ? cm : ALL_CPU_MASK; | ||
| 245 | for (i = 0; i < 8; i++) { | 304 | for (i = 0; i < 8; i++) { |
| 246 | - if (GIC_TEST_ACTIVE(irq + i)) { | 305 | + if (GIC_TEST_ACTIVE(irq + i, mask)) { |
| 247 | res |= (1 << i); | 306 | res |= (1 << i); |
| 248 | } | 307 | } |
| 249 | } | 308 | } |
| 250 | } else if (offset < 0x800) { | 309 | } else if (offset < 0x800) { |
| 251 | /* Interrupt Priority. */ | 310 | /* Interrupt Priority. */ |
| 252 | - irq = offset - 0x400; | 311 | + irq = (offset - 0x400) + GIC_BASE_IRQ; |
| 253 | if (irq >= GIC_NIRQ) | 312 | if (irq >= GIC_NIRQ) |
| 254 | goto bad_reg; | 313 | goto bad_reg; |
| 255 | - res = s->priority[irq]; | 314 | + res = GIC_GET_PRIORITY(irq, cpu); |
| 315 | +#ifndef NVIC | ||
| 256 | } else if (offset < 0xc00) { | 316 | } else if (offset < 0xc00) { |
| 257 | /* Interrupt CPU Target. */ | 317 | /* Interrupt CPU Target. */ |
| 258 | - irq = offset - 0x800; | 318 | + irq = (offset - 0x800) + GIC_BASE_IRQ; |
| 259 | if (irq >= GIC_NIRQ) | 319 | if (irq >= GIC_NIRQ) |
| 260 | goto bad_reg; | 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 | } else if (offset < 0xf00) { | 326 | } else if (offset < 0xf00) { |
| 263 | /* Interrupt Configuration. */ | 327 | /* Interrupt Configuration. */ |
| 264 | - irq = (offset - 0xc00) * 2; | 328 | + irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ; |
| 265 | if (irq >= GIC_NIRQ) | 329 | if (irq >= GIC_NIRQ) |
| 266 | goto bad_reg; | 330 | goto bad_reg; |
| 267 | res = 0; | 331 | res = 0; |
| @@ -271,6 +335,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | @@ -271,6 +335,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | ||
| 271 | if (GIC_TEST_TRIGGER(irq + i)) | 335 | if (GIC_TEST_TRIGGER(irq + i)) |
| 272 | res |= (2 << (i * 2)); | 336 | res |= (2 << (i * 2)); |
| 273 | } | 337 | } |
| 338 | +#endif | ||
| 274 | } else if (offset < 0xfe0) { | 339 | } else if (offset < 0xfe0) { |
| 275 | goto bad_reg; | 340 | goto bad_reg; |
| 276 | } else /* offset >= 0xfe0 */ { | 341 | } else /* offset >= 0xfe0 */ { |
| @@ -282,7 +347,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | @@ -282,7 +347,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | ||
| 282 | } | 347 | } |
| 283 | return res; | 348 | return res; |
| 284 | bad_reg: | 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 | return 0; | 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,6 +362,13 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) | ||
| 297 | static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) | 362 | static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) |
| 298 | { | 363 | { |
| 299 | uint32_t val; | 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 | val = gic_dist_readw(opaque, offset); | 372 | val = gic_dist_readw(opaque, offset); |
| 301 | val |= gic_dist_readw(opaque, offset + 2) << 16; | 373 | val |= gic_dist_readw(opaque, offset + 2) << 16; |
| 302 | return val; | 374 | return val; |
| @@ -308,9 +380,14 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | @@ -308,9 +380,14 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | ||
| 308 | gic_state *s = (gic_state *)opaque; | 380 | gic_state *s = (gic_state *)opaque; |
| 309 | int irq; | 381 | int irq; |
| 310 | int i; | 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 | if (offset < 0x100) { | 387 | if (offset < 0x100) { |
| 388 | +#ifdef NVIC | ||
| 389 | + goto bad_reg; | ||
| 390 | +#else | ||
| 314 | if (offset == 0) { | 391 | if (offset == 0) { |
| 315 | s->enabled = (value & 1); | 392 | s->enabled = (value & 1); |
| 316 | DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); | 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,27 +396,36 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | ||
| 319 | } else { | 396 | } else { |
| 320 | goto bad_reg; | 397 | goto bad_reg; |
| 321 | } | 398 | } |
| 399 | +#endif | ||
| 322 | } else if (offset < 0x180) { | 400 | } else if (offset < 0x180) { |
| 323 | /* Interrupt Set Enable. */ | 401 | /* Interrupt Set Enable. */ |
| 324 | - irq = (offset - 0x100) * 8; | 402 | + irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; |
| 325 | if (irq >= GIC_NIRQ) | 403 | if (irq >= GIC_NIRQ) |
| 326 | goto bad_reg; | 404 | goto bad_reg; |
| 405 | + if (irq < 16) | ||
| 406 | + value = 0xff; | ||
| 327 | for (i = 0; i < 8; i++) { | 407 | for (i = 0; i < 8; i++) { |
| 328 | if (value & (1 << i)) { | 408 | if (value & (1 << i)) { |
| 409 | + int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq); | ||
| 329 | if (!GIC_TEST_ENABLED(irq + i)) | 410 | if (!GIC_TEST_ENABLED(irq + i)) |
| 330 | DPRINTF("Enabled IRQ %d\n", irq + i); | 411 | DPRINTF("Enabled IRQ %d\n", irq + i); |
| 331 | GIC_SET_ENABLED(irq + i); | 412 | GIC_SET_ENABLED(irq + i); |
| 332 | /* If a raised level triggered IRQ enabled then mark | 413 | /* If a raised level triggered IRQ enabled then mark |
| 333 | is as pending. */ | 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 | } else if (offset < 0x200) { | 422 | } else if (offset < 0x200) { |
| 339 | /* Interrupt Clear Enable. */ | 423 | /* Interrupt Clear Enable. */ |
| 340 | - irq = (offset - 0x180) * 8; | 424 | + irq = (offset - 0x180) * 8 + GIC_BASE_IRQ; |
| 341 | if (irq >= GIC_NIRQ) | 425 | if (irq >= GIC_NIRQ) |
| 342 | goto bad_reg; | 426 | goto bad_reg; |
| 427 | + if (irq < 16) | ||
| 428 | + value = 0; | ||
| 343 | for (i = 0; i < 8; i++) { | 429 | for (i = 0; i < 8; i++) { |
| 344 | if (value & (1 << i)) { | 430 | if (value & (1 << i)) { |
| 345 | if (GIC_TEST_ENABLED(irq + i)) | 431 | if (GIC_TEST_ENABLED(irq + i)) |
| @@ -349,22 +435,28 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | @@ -349,22 +435,28 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | ||
| 349 | } | 435 | } |
| 350 | } else if (offset < 0x280) { | 436 | } else if (offset < 0x280) { |
| 351 | /* Interrupt Set Pending. */ | 437 | /* Interrupt Set Pending. */ |
| 352 | - irq = (offset - 0x200) * 8; | 438 | + irq = (offset - 0x200) * 8 + GIC_BASE_IRQ; |
| 353 | if (irq >= GIC_NIRQ) | 439 | if (irq >= GIC_NIRQ) |
| 354 | goto bad_reg; | 440 | goto bad_reg; |
| 441 | + if (irq < 16) | ||
| 442 | + irq = 0; | ||
| 443 | + | ||
| 355 | for (i = 0; i < 8; i++) { | 444 | for (i = 0; i < 8; i++) { |
| 356 | if (value & (1 << i)) { | 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 | } else if (offset < 0x300) { | 449 | } else if (offset < 0x300) { |
| 361 | /* Interrupt Clear Pending. */ | 450 | /* Interrupt Clear Pending. */ |
| 362 | - irq = (offset - 0x280) * 8; | 451 | + irq = (offset - 0x280) * 8 + GIC_BASE_IRQ; |
| 363 | if (irq >= GIC_NIRQ) | 452 | if (irq >= GIC_NIRQ) |
| 364 | goto bad_reg; | 453 | goto bad_reg; |
| 365 | for (i = 0; i < 8; i++) { | 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 | if (value & (1 << i)) { | 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 | } else if (offset < 0x400) { | 462 | } else if (offset < 0x400) { |
| @@ -372,21 +464,32 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | @@ -372,21 +464,32 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | ||
| 372 | goto bad_reg; | 464 | goto bad_reg; |
| 373 | } else if (offset < 0x800) { | 465 | } else if (offset < 0x800) { |
| 374 | /* Interrupt Priority. */ | 466 | /* Interrupt Priority. */ |
| 375 | - irq = offset - 0x400; | 467 | + irq = (offset - 0x400) + GIC_BASE_IRQ; |
| 376 | if (irq >= GIC_NIRQ) | 468 | if (irq >= GIC_NIRQ) |
| 377 | goto bad_reg; | 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 | } else if (offset < 0xc00) { | 476 | } else if (offset < 0xc00) { |
| 380 | /* Interrupt CPU Target. */ | 477 | /* Interrupt CPU Target. */ |
| 381 | - irq = offset - 0x800; | 478 | + irq = (offset - 0x800) + GIC_BASE_IRQ; |
| 382 | if (irq >= GIC_NIRQ) | 479 | if (irq >= GIC_NIRQ) |
| 383 | goto bad_reg; | 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 | } else if (offset < 0xf00) { | 486 | } else if (offset < 0xf00) { |
| 386 | /* Interrupt Configuration. */ | 487 | /* Interrupt Configuration. */ |
| 387 | - irq = (offset - 0xc00) * 4; | 488 | + irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; |
| 388 | if (irq >= GIC_NIRQ) | 489 | if (irq >= GIC_NIRQ) |
| 389 | goto bad_reg; | 490 | goto bad_reg; |
| 491 | + if (irq < 32) | ||
| 492 | + value |= 0xaa; | ||
| 390 | for (i = 0; i < 4; i++) { | 493 | for (i = 0; i < 4; i++) { |
| 391 | if (value & (1 << (i * 2))) { | 494 | if (value & (1 << (i * 2))) { |
| 392 | GIC_SET_MODEL(irq + i); | 495 | GIC_SET_MODEL(irq + i); |
| @@ -399,25 +502,20 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | @@ -399,25 +502,20 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | ||
| 399 | GIC_CLEAR_TRIGGER(irq + i); | 502 | GIC_CLEAR_TRIGGER(irq + i); |
| 400 | } | 503 | } |
| 401 | } | 504 | } |
| 505 | +#endif | ||
| 402 | } else { | 506 | } else { |
| 403 | - /* 0xf00 is only handled for word writes. */ | 507 | + /* 0xf00 is only handled for 32-bit writes. */ |
| 404 | goto bad_reg; | 508 | goto bad_reg; |
| 405 | } | 509 | } |
| 406 | gic_update(s); | 510 | gic_update(s); |
| 407 | return; | 511 | return; |
| 408 | bad_reg: | 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 | static void gic_dist_writew(void *opaque, target_phys_addr_t offset, | 516 | static void gic_dist_writew(void *opaque, target_phys_addr_t offset, |
| 413 | uint32_t value) | 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 | gic_dist_writeb(opaque, offset, value & 0xff); | 519 | gic_dist_writeb(opaque, offset, value & 0xff); |
| 422 | gic_dist_writeb(opaque, offset + 1, value >> 8); | 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,6 +523,41 @@ static void gic_dist_writew(void *opaque, target_phys_addr_t offset, | ||
| 425 | static void gic_dist_writel(void *opaque, target_phys_addr_t offset, | 523 | static void gic_dist_writel(void *opaque, target_phys_addr_t offset, |
| 426 | uint32_t value) | 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 | gic_dist_writew(opaque, offset, value & 0xffff); | 561 | gic_dist_writew(opaque, offset, value & 0xffff); |
| 429 | gic_dist_writew(opaque, offset + 2, value >> 16); | 562 | gic_dist_writew(opaque, offset + 2, value >> 16); |
| 430 | } | 563 | } |
| @@ -441,105 +574,100 @@ static CPUWriteMemoryFunc *gic_dist_writefn[] = { | @@ -441,105 +574,100 @@ static CPUWriteMemoryFunc *gic_dist_writefn[] = { | ||
| 441 | gic_dist_writel | 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 | switch (offset) { | 580 | switch (offset) { |
| 449 | case 0x00: /* Control */ | 581 | case 0x00: /* Control */ |
| 450 | - return s->cpu_enabled; | 582 | + return s->cpu_enabled[cpu]; |
| 451 | case 0x04: /* Priority mask */ | 583 | case 0x04: /* Priority mask */ |
| 452 | - return s->priority_mask; | 584 | + return s->priority_mask[cpu]; |
| 453 | case 0x08: /* Binary Point */ | 585 | case 0x08: /* Binary Point */ |
| 454 | /* ??? Not implemented. */ | 586 | /* ??? Not implemented. */ |
| 455 | return 0; | 587 | return 0; |
| 456 | case 0x0c: /* Acknowledge */ | 588 | case 0x0c: /* Acknowledge */ |
| 457 | - return gic_acknowledge_irq(s); | 589 | + return gic_acknowledge_irq(s, cpu); |
| 458 | case 0x14: /* Runing Priority */ | 590 | case 0x14: /* Runing Priority */ |
| 459 | - return s->running_priority; | 591 | + return s->running_priority[cpu]; |
| 460 | case 0x18: /* Highest Pending Interrupt */ | 592 | case 0x18: /* Highest Pending Interrupt */ |
| 461 | - return s->current_pending; | 593 | + return s->current_pending[cpu]; |
| 462 | default: | 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 | return 0; | 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 | switch (offset) { | 603 | switch (offset) { |
| 474 | case 0x00: /* Control */ | 604 | case 0x00: /* Control */ |
| 475 | - s->cpu_enabled = (value & 1); | 605 | + s->cpu_enabled[cpu] = (value & 1); |
| 476 | DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); | 606 | DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); |
| 477 | break; | 607 | break; |
| 478 | case 0x04: /* Priority mask */ | 608 | case 0x04: /* Priority mask */ |
| 479 | - s->priority_mask = (value & 0x3ff); | 609 | + s->priority_mask[cpu] = (value & 0xff); |
| 480 | break; | 610 | break; |
| 481 | case 0x08: /* Binary Point */ | 611 | case 0x08: /* Binary Point */ |
| 482 | /* ??? Not implemented. */ | 612 | /* ??? Not implemented. */ |
| 483 | break; | 613 | break; |
| 484 | case 0x10: /* End Of Interrupt */ | 614 | case 0x10: /* End Of Interrupt */ |
| 485 | - return gic_complete_irq(s, value & 0x3ff); | 615 | + return gic_complete_irq(s, cpu, value & 0x3ff); |
| 486 | default: | 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 | return; | 619 | return; |
| 489 | } | 620 | } |
| 490 | gic_update(s); | 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 | static void gic_reset(gic_state *s) | 625 | static void gic_reset(gic_state *s) |
| 506 | { | 626 | { |
| 507 | int i; | 627 | int i; |
| 508 | memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state)); | 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 | for (i = 0; i < 15; i++) { | 641 | for (i = 0; i < 15; i++) { |
| 514 | GIC_SET_ENABLED(i); | 642 | GIC_SET_ENABLED(i); |
| 515 | GIC_SET_TRIGGER(i); | 643 | GIC_SET_TRIGGER(i); |
| 516 | } | 644 | } |
| 645 | +#ifdef NVIC | ||
| 646 | + /* The NVIC is always enabled. */ | ||
| 647 | + s->enabled = 1; | ||
| 648 | +#else | ||
| 517 | s->enabled = 0; | 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 | gic_state *s; | 655 | gic_state *s; |
| 524 | - qemu_irq *qi; | ||
| 525 | int iomemtype; | 656 | int iomemtype; |
| 657 | + int i; | ||
| 526 | 658 | ||
| 527 | s = (gic_state *)qemu_mallocz(sizeof(gic_state)); | 659 | s = (gic_state *)qemu_mallocz(sizeof(gic_state)); |
| 528 | if (!s) | 660 | if (!s) |
| 529 | return NULL; | 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 | gic_reset(s); | 671 | gic_reset(s); |
| 544 | - return qi; | 672 | + return s; |
| 545 | } | 673 | } |
hw/arm_sysctl.c
| 1 | /* | 1 | /* |
| 2 | * Status and system control registers for ARM RealView/Versatile boards. | 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 | * Written by Paul Brook | 5 | * Written by Paul Brook |
| 6 | * | 6 | * |
| 7 | * This code is licenced under the GPL. | 7 | * This code is licenced under the GPL. |
| @@ -200,6 +200,9 @@ void arm_sysctl_init(uint32_t base, uint32_t sys_id) | @@ -200,6 +200,9 @@ void arm_sysctl_init(uint32_t base, uint32_t sys_id) | ||
| 200 | return; | 200 | return; |
| 201 | s->base = base; | 201 | s->base = base; |
| 202 | s->sys_id = sys_id; | 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 | iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn, | 206 | iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn, |
| 204 | arm_sysctl_writefn, s); | 207 | arm_sysctl_writefn, s); |
| 205 | cpu_register_physical_memory(base, 0x00001000, iomemtype); | 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,8 +497,8 @@ static void integratorcp_init(int ram_size, int vga_ram_size, | ||
| 497 | icp_pic_init(0xca000000, pic[26], NULL); | 497 | icp_pic_init(0xca000000, pic[26], NULL); |
| 498 | icp_pit_init(0x13000000, pic, 5); | 498 | icp_pit_init(0x13000000, pic, 5); |
| 499 | pl031_init(0x15000000, pic[8]); | 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 | icp_control_init(0xcb000000); | 502 | icp_control_init(0xcb000000); |
| 503 | pl050_init(0x18000000, pic[3], 0); | 503 | pl050_init(0x18000000, pic[3], 0); |
| 504 | pl050_init(0x19000000, pic[4], 1); | 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,6 +28,7 @@ typedef struct { | ||
| 28 | int read_trigger; | 28 | int read_trigger; |
| 29 | CharDriverState *chr; | 29 | CharDriverState *chr; |
| 30 | qemu_irq irq; | 30 | qemu_irq irq; |
| 31 | + enum pl011_type type; | ||
| 31 | } pl011_state; | 32 | } pl011_state; |
| 32 | 33 | ||
| 33 | #define PL011_INT_TX 0x20 | 34 | #define PL011_INT_TX 0x20 |
| @@ -38,8 +39,10 @@ typedef struct { | @@ -38,8 +39,10 @@ typedef struct { | ||
| 38 | #define PL011_FLAG_TXFF 0x20 | 39 | #define PL011_FLAG_TXFF 0x20 |
| 39 | #define PL011_FLAG_RXFE 0x10 | 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 | static void pl011_update(pl011_state *s) | 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,7 +59,7 @@ static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) | ||
| 56 | 59 | ||
| 57 | offset -= s->base; | 60 | offset -= s->base; |
| 58 | if (offset >= 0xfe0 && offset < 0x1000) { | 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 | switch (offset >> 2) { | 64 | switch (offset >> 2) { |
| 62 | case 0: /* UARTDR */ | 65 | case 0: /* UARTDR */ |
| @@ -137,6 +140,9 @@ static void pl011_write(void *opaque, target_phys_addr_t offset, | @@ -137,6 +140,9 @@ static void pl011_write(void *opaque, target_phys_addr_t offset, | ||
| 137 | case 1: /* UARTCR */ | 140 | case 1: /* UARTCR */ |
| 138 | s->cr = value; | 141 | s->cr = value; |
| 139 | break; | 142 | break; |
| 143 | + case 6: /* UARTFR */ | ||
| 144 | + /* Writes to Flag register are ignored. */ | ||
| 145 | + break; | ||
| 140 | case 8: /* UARTUARTILPR */ | 146 | case 8: /* UARTUARTILPR */ |
| 141 | s->ilpr = value; | 147 | s->ilpr = value; |
| 142 | break; | 148 | break; |
| @@ -224,7 +230,7 @@ static CPUWriteMemoryFunc *pl011_writefn[] = { | @@ -224,7 +230,7 @@ static CPUWriteMemoryFunc *pl011_writefn[] = { | ||
| 224 | }; | 230 | }; |
| 225 | 231 | ||
| 226 | void pl011_init(uint32_t base, qemu_irq irq, | 232 | void pl011_init(uint32_t base, qemu_irq irq, |
| 227 | - CharDriverState *chr) | 233 | + CharDriverState *chr, enum pl011_type type) |
| 228 | { | 234 | { |
| 229 | int iomemtype; | 235 | int iomemtype; |
| 230 | pl011_state *s; | 236 | pl011_state *s; |
| @@ -235,6 +241,7 @@ void pl011_init(uint32_t base, qemu_irq irq, | @@ -235,6 +241,7 @@ void pl011_init(uint32_t base, qemu_irq irq, | ||
| 235 | cpu_register_physical_memory(base, 0x00001000, iomemtype); | 241 | cpu_register_physical_memory(base, 0x00001000, iomemtype); |
| 236 | s->base = base; | 242 | s->base = base; |
| 237 | s->irq = irq; | 243 | s->irq = irq; |
| 244 | + s->type = type; | ||
| 238 | s->chr = chr; | 245 | s->chr = chr; |
| 239 | s->read_trigger = 1; | 246 | s->read_trigger = 1; |
| 240 | s->ifl = 0x12; | 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,7 +297,7 @@ static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm, | ||
| 297 | ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; | 297 | ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; |
| 298 | s->env->cp15.c1_sys = 0; | 298 | s->env->cp15.c1_sys = 0; |
| 299 | s->env->cp15.c1_coproc = 0; | 299 | s->env->cp15.c1_coproc = 0; |
| 300 | - s->env->cp15.c2_base = 0; | 300 | + s->env->cp15.c2_base0 = 0; |
| 301 | s->env->cp15.c3 = 0; | 301 | s->env->cp15.c3 = 0; |
| 302 | s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ | 302 | s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ |
| 303 | s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ | 303 | s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ |
| @@ -2031,7 +2031,8 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size, | @@ -2031,7 +2031,8 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size, | ||
| 2031 | fprintf(stderr, "Unable to find CPU definition\n"); | 2031 | fprintf(stderr, "Unable to find CPU definition\n"); |
| 2032 | exit(1); | 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 | /* SDRAM & Internal Memory Storage */ | 2037 | /* SDRAM & Internal Memory Storage */ |
| 2037 | cpu_register_physical_memory(PXA2XX_SDRAM_BASE, | 2038 | cpu_register_physical_memory(PXA2XX_SDRAM_BASE, |
| @@ -2145,7 +2146,8 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size, | @@ -2145,7 +2146,8 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size, | ||
| 2145 | fprintf(stderr, "Unable to find CPU definition\n"); | 2146 | fprintf(stderr, "Unable to find CPU definition\n"); |
| 2146 | exit(1); | 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 | /* SDRAM & Internal Memory Storage */ | 2152 | /* SDRAM & Internal Memory Storage */ |
| 2151 | cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size, | 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,13 +25,32 @@ static void realview_init(int ram_size, int vga_ram_size, | ||
| 25 | NICInfo *nd; | 25 | NICInfo *nd; |
| 26 | int n; | 26 | int n; |
| 27 | int done_smc = 0; | 27 | int done_smc = 0; |
| 28 | + qemu_irq cpu_irq[4]; | ||
| 29 | + int ncpu; | ||
| 28 | 30 | ||
| 29 | if (!cpu_model) | 31 | if (!cpu_model) |
| 30 | cpu_model = "arm926"; | 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 | /* ??? RAM shoud repeat to fill physical memory space. */ | 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,18 +58,23 @@ static void realview_init(int ram_size, int vga_ram_size, | ||
| 39 | cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); | 58 | cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); |
| 40 | 59 | ||
| 41 | arm_sysctl_init(0x10000000, 0xc1400400); | 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 | pl050_init(0x10006000, pic[20], 0); | 71 | pl050_init(0x10006000, pic[20], 0); |
| 48 | pl050_init(0x10007000, pic[21], 1); | 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 | /* DMA controller is optional, apparently. */ | 79 | /* DMA controller is optional, apparently. */ |
| 56 | pl080_init(0x10030000, pic[24], 2); | 80 | pl080_init(0x10030000, pic[24], 2); |
| @@ -114,10 +138,10 @@ static void realview_init(int ram_size, int vga_ram_size, | @@ -114,10 +138,10 @@ static void realview_init(int ram_size, int vga_ram_size, | ||
| 114 | /* 0x10019000 PCI controller config. */ | 138 | /* 0x10019000 PCI controller config. */ |
| 115 | /* 0x10020000 CLCD. */ | 139 | /* 0x10020000 CLCD. */ |
| 116 | /* 0x10030000 DMA Controller. */ | 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 | /* 0x10080000 SMC. */ | 145 | /* 0x10080000 SMC. */ |
| 122 | /* 0x40000000 NOR flash. */ | 146 | /* 0x40000000 NOR flash. */ |
| 123 | /* 0x44000000 DoC flash. */ | 147 | /* 0x44000000 DoC flash. */ |
| @@ -137,8 +161,14 @@ static void realview_init(int ram_size, int vga_ram_size, | @@ -137,8 +161,14 @@ static void realview_init(int ram_size, int vga_ram_size, | ||
| 137 | /* 0x68000000 PCI mem 1. */ | 161 | /* 0x68000000 PCI mem 1. */ |
| 138 | /* 0x6c000000 PCI mem 2. */ | 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 | initrd_filename, 0x33b, 0x0); | 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 | QEMUMachine realview_machine = { | 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,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 | pl080_init(0x10130000, pic[17], 8); | 216 | pl080_init(0x10130000, pic[17], 8); |
| 217 | sp804_init(0x101e2000, pic[4]); | 217 | sp804_init(0x101e2000, pic[4]); |
qemu-doc.texi
| @@ -77,10 +77,12 @@ For system emulation, the following hardware targets are supported: | @@ -77,10 +77,12 @@ For system emulation, the following hardware targets are supported: | ||
| 77 | @item Sun4m (32-bit Sparc processor) | 77 | @item Sun4m (32-bit Sparc processor) |
| 78 | @item Sun4u (64-bit Sparc processor, in progress) | 78 | @item Sun4u (64-bit Sparc processor, in progress) |
| 79 | @item Malta board (32-bit MIPS processor) | 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 | @item Spitz, Akita, Borzoi and Terrier PDAs (PXA270 processor) | 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 | @item Freescale MCF5208EVB (ColdFire V2). | 86 | @item Freescale MCF5208EVB (ColdFire V2). |
| 85 | @item Arnewsh MCF5206 evaluation board (ColdFire V2). | 87 | @item Arnewsh MCF5206 evaluation board (ColdFire V2). |
| 86 | @item Palm Tungsten|E PDA (OMAP310 processor) | 88 | @item Palm Tungsten|E PDA (OMAP310 processor) |
| @@ -2117,7 +2119,7 @@ devices: | @@ -2117,7 +2119,7 @@ devices: | ||
| 2117 | 2119 | ||
| 2118 | @itemize @minus | 2120 | @itemize @minus |
| 2119 | @item | 2121 | @item |
| 2120 | -ARM926E, ARM1026E or ARM946E CPU | 2122 | +ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU |
| 2121 | @item | 2123 | @item |
| 2122 | Two PL011 UARTs | 2124 | Two PL011 UARTs |
| 2123 | @item | 2125 | @item |
| @@ -2134,7 +2136,7 @@ The ARM Versatile baseboard is emulated with the following devices: | @@ -2134,7 +2136,7 @@ The ARM Versatile baseboard is emulated with the following devices: | ||
| 2134 | 2136 | ||
| 2135 | @itemize @minus | 2137 | @itemize @minus |
| 2136 | @item | 2138 | @item |
| 2137 | -ARM926E CPU | 2139 | +ARM926E, ARM1136 or Cortex-A8 CPU |
| 2138 | @item | 2140 | @item |
| 2139 | PL190 Vectored Interrupt Controller | 2141 | PL190 Vectored Interrupt Controller |
| 2140 | @item | 2142 | @item |
| @@ -2163,7 +2165,7 @@ The ARM RealView Emulation baseboard is emulated with the following devices: | @@ -2163,7 +2165,7 @@ The ARM RealView Emulation baseboard is emulated with the following devices: | ||
| 2163 | 2165 | ||
| 2164 | @itemize @minus | 2166 | @itemize @minus |
| 2165 | @item | 2167 | @item |
| 2166 | -ARM926E CPU | 2168 | +ARM926E, ARM1136, ARM11MPCORE(x4) or Cortex-A8 CPU |
| 2167 | @item | 2169 | @item |
| 2168 | ARM AMBA Generic/Distributed Interrupt Controller | 2170 | ARM AMBA Generic/Distributed Interrupt Controller |
| 2169 | @item | 2171 | @item |
| @@ -2237,6 +2239,34 @@ Secure Digital card connected to OMAP MMC/SD host | @@ -2237,6 +2239,34 @@ Secure Digital card connected to OMAP MMC/SD host | ||
| 2237 | Three on-chip UARTs | 2239 | Three on-chip UARTs |
| 2238 | @end itemize | 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 | A Linux 2.6 test image is available on the QEMU web site. More | 2270 | A Linux 2.6 test image is available on the QEMU web site. More |
| 2241 | information is available in the QEMU mailing-list archive. | 2271 | information is available in the QEMU mailing-list archive. |
| 2242 | 2272 |
target-arm/cpu.h
| @@ -37,6 +37,18 @@ | @@ -37,6 +37,18 @@ | ||
| 37 | #define EXCP_IRQ 5 | 37 | #define EXCP_IRQ 5 |
| 38 | #define EXCP_FIQ 6 | 38 | #define EXCP_FIQ 6 |
| 39 | #define EXCP_BKPT 7 | 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 | typedef void ARMWriteCPFunc(void *opaque, int cp_info, | 53 | typedef void ARMWriteCPFunc(void *opaque, int cp_info, |
| 42 | int srcreg, int operand, uint32_t value); | 54 | int srcreg, int operand, uint32_t value); |
| @@ -76,17 +88,22 @@ typedef struct CPUARMState { | @@ -76,17 +88,22 @@ typedef struct CPUARMState { | ||
| 76 | uint32_t VF; /* V is the bit 31. All other bits are undefined */ | 88 | uint32_t VF; /* V is the bit 31. All other bits are undefined */ |
| 77 | uint32_t NZF; /* N is bit 31. Z is computed from NZF */ | 89 | uint32_t NZF; /* N is bit 31. Z is computed from NZF */ |
| 78 | uint32_t QF; /* 0 or 1 */ | 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 | /* System control coprocessor (cp15) */ | 95 | /* System control coprocessor (cp15) */ |
| 83 | struct { | 96 | struct { |
| 84 | uint32_t c0_cpuid; | 97 | uint32_t c0_cpuid; |
| 85 | uint32_t c0_cachetype; | 98 | uint32_t c0_cachetype; |
| 99 | + uint32_t c0_c1[8]; /* Feature registers. */ | ||
| 100 | + uint32_t c0_c2[8]; /* Instruction set registers. */ | ||
| 86 | uint32_t c1_sys; /* System control register. */ | 101 | uint32_t c1_sys; /* System control register. */ |
| 87 | uint32_t c1_coproc; /* Coprocessor access register. */ | 102 | uint32_t c1_coproc; /* Coprocessor access register. */ |
| 88 | uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ | 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 | uint32_t c2_data; /* MPU data cachable bits. */ | 107 | uint32_t c2_data; /* MPU data cachable bits. */ |
| 91 | uint32_t c2_insn; /* MPU instruction cachable bits. */ | 108 | uint32_t c2_insn; /* MPU instruction cachable bits. */ |
| 92 | uint32_t c3; /* MMU domain access control register | 109 | uint32_t c3; /* MMU domain access control register |
| @@ -100,6 +117,9 @@ typedef struct CPUARMState { | @@ -100,6 +117,9 @@ typedef struct CPUARMState { | ||
| 100 | uint32_t c9_data; | 117 | uint32_t c9_data; |
| 101 | uint32_t c13_fcse; /* FCSE PID. */ | 118 | uint32_t c13_fcse; /* FCSE PID. */ |
| 102 | uint32_t c13_context; /* Context ID. */ | 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 | uint32_t c15_cpar; /* XScale Coprocessor Access Register */ | 123 | uint32_t c15_cpar; /* XScale Coprocessor Access Register */ |
| 104 | uint32_t c15_ticonfig; /* TI925T configuration byte. */ | 124 | uint32_t c15_ticonfig; /* TI925T configuration byte. */ |
| 105 | uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ | 125 | uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ |
| @@ -107,6 +127,17 @@ typedef struct CPUARMState { | @@ -107,6 +127,17 @@ typedef struct CPUARMState { | ||
| 107 | uint32_t c15_threadid; /* TI debugger thread-ID. */ | 127 | uint32_t c15_threadid; /* TI debugger thread-ID. */ |
| 108 | } cp15; | 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 | /* Coprocessor IO used by peripherals */ | 141 | /* Coprocessor IO used by peripherals */ |
| 111 | struct { | 142 | struct { |
| 112 | ARMReadCPFunc *cp_read; | 143 | ARMReadCPFunc *cp_read; |
| @@ -117,6 +148,10 @@ typedef struct CPUARMState { | @@ -117,6 +148,10 @@ typedef struct CPUARMState { | ||
| 117 | /* Internal CPU feature flags. */ | 148 | /* Internal CPU feature flags. */ |
| 118 | uint32_t features; | 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 | /* exception/interrupt handling */ | 155 | /* exception/interrupt handling */ |
| 121 | jmp_buf jmp_env; | 156 | jmp_buf jmp_env; |
| 122 | int exception_index; | 157 | int exception_index; |
| @@ -126,7 +161,7 @@ typedef struct CPUARMState { | @@ -126,7 +161,7 @@ typedef struct CPUARMState { | ||
| 126 | 161 | ||
| 127 | /* VFP coprocessor state. */ | 162 | /* VFP coprocessor state. */ |
| 128 | struct { | 163 | struct { |
| 129 | - float64 regs[16]; | 164 | + float64 regs[32]; |
| 130 | 165 | ||
| 131 | uint32_t xregs[16]; | 166 | uint32_t xregs[16]; |
| 132 | /* We store these fpcsr fields separately for convenience. */ | 167 | /* We store these fpcsr fields separately for convenience. */ |
| @@ -136,9 +171,16 @@ typedef struct CPUARMState { | @@ -136,9 +171,16 @@ typedef struct CPUARMState { | ||
| 136 | /* Temporary variables if we don't have spare fp regs. */ | 171 | /* Temporary variables if we don't have spare fp regs. */ |
| 137 | float32 tmp0s, tmp1s; | 172 | float32 tmp0s, tmp1s; |
| 138 | float64 tmp0d, tmp1d; | 173 | float64 tmp0d, tmp1d; |
| 174 | + /* scratch space when Tn are not sufficient. */ | ||
| 175 | + uint32_t scratch[8]; | ||
| 139 | 176 | ||
| 140 | float_status fp_status; | 177 | float_status fp_status; |
| 141 | } vfp; | 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 | /* iwMMXt coprocessor state. */ | 185 | /* iwMMXt coprocessor state. */ |
| 144 | struct { | 186 | struct { |
| @@ -169,6 +211,7 @@ int cpu_arm_exec(CPUARMState *s); | @@ -169,6 +211,7 @@ int cpu_arm_exec(CPUARMState *s); | ||
| 169 | void cpu_arm_close(CPUARMState *s); | 211 | void cpu_arm_close(CPUARMState *s); |
| 170 | void do_interrupt(CPUARMState *); | 212 | void do_interrupt(CPUARMState *); |
| 171 | void switch_mode(CPUARMState *, int); | 213 | void switch_mode(CPUARMState *, int); |
| 214 | +uint32_t do_arm_semihosting(CPUARMState *env); | ||
| 172 | 215 | ||
| 173 | /* you can call this signal handler from your SIGBUS and SIGSEGV | 216 | /* you can call this signal handler from your SIGBUS and SIGSEGV |
| 174 | signal handlers to inform the virtual CPU of exceptions. non zero | 217 | signal handlers to inform the virtual CPU of exceptions. non zero |
| @@ -176,6 +219,9 @@ void switch_mode(CPUARMState *, int); | @@ -176,6 +219,9 @@ void switch_mode(CPUARMState *, int); | ||
| 176 | int cpu_arm_signal_handler(int host_signum, void *pinfo, | 219 | int cpu_arm_signal_handler(int host_signum, void *pinfo, |
| 177 | void *puc); | 220 | void *puc); |
| 178 | 221 | ||
| 222 | +void cpu_lock(void); | ||
| 223 | +void cpu_unlock(void); | ||
| 224 | + | ||
| 179 | #define CPSR_M (0x1f) | 225 | #define CPSR_M (0x1f) |
| 180 | #define CPSR_T (1 << 5) | 226 | #define CPSR_T (1 << 5) |
| 181 | #define CPSR_F (1 << 6) | 227 | #define CPSR_F (1 << 6) |
| @@ -183,13 +229,24 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo, | @@ -183,13 +229,24 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo, | ||
| 183 | #define CPSR_A (1 << 8) | 229 | #define CPSR_A (1 << 8) |
| 184 | #define CPSR_E (1 << 9) | 230 | #define CPSR_E (1 << 9) |
| 185 | #define CPSR_IT_2_7 (0xfc00) | 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 | #define CPSR_J (1 << 24) | 234 | #define CPSR_J (1 << 24) |
| 188 | #define CPSR_IT_0_1 (3 << 25) | 235 | #define CPSR_IT_0_1 (3 << 25) |
| 189 | #define CPSR_Q (1 << 27) | 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 | /* Return the current CPSR value. */ | 250 | /* Return the current CPSR value. */ |
| 194 | static inline uint32_t cpsr_read(CPUARMState *env) | 251 | static inline uint32_t cpsr_read(CPUARMState *env) |
| 195 | { | 252 | { |
| @@ -197,7 +254,21 @@ static inline uint32_t cpsr_read(CPUARMState *env) | @@ -197,7 +254,21 @@ static inline uint32_t cpsr_read(CPUARMState *env) | ||
| 197 | ZF = (env->NZF == 0); | 254 | ZF = (env->NZF == 0); |
| 198 | return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) | | 255 | return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) | |
| 199 | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27) | 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 | /* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */ | 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,6 +284,17 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) | ||
| 213 | env->QF = ((val & CPSR_Q) != 0); | 284 | env->QF = ((val & CPSR_Q) != 0); |
| 214 | if (mask & CPSR_T) | 285 | if (mask & CPSR_T) |
| 215 | env->thumb = ((val & CPSR_T) != 0); | 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 | if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { | 299 | if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { |
| 218 | switch_mode(env, val & CPSR_M); | 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,6 +303,32 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) | ||
| 221 | env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask); | 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 | enum arm_cpu_mode { | 332 | enum arm_cpu_mode { |
| 225 | ARM_CPU_MODE_USR = 0x10, | 333 | ARM_CPU_MODE_USR = 0x10, |
| 226 | ARM_CPU_MODE_FIQ = 0x11, | 334 | ARM_CPU_MODE_FIQ = 0x11, |
| @@ -234,6 +342,8 @@ enum arm_cpu_mode { | @@ -234,6 +342,8 @@ enum arm_cpu_mode { | ||
| 234 | /* VFP system registers. */ | 342 | /* VFP system registers. */ |
| 235 | #define ARM_VFP_FPSID 0 | 343 | #define ARM_VFP_FPSID 0 |
| 236 | #define ARM_VFP_FPSCR 1 | 344 | #define ARM_VFP_FPSCR 1 |
| 345 | +#define ARM_VFP_MVFR1 6 | ||
| 346 | +#define ARM_VFP_MVFR0 7 | ||
| 237 | #define ARM_VFP_FPEXC 8 | 347 | #define ARM_VFP_FPEXC 8 |
| 238 | #define ARM_VFP_FPINST 9 | 348 | #define ARM_VFP_FPINST 9 |
| 239 | #define ARM_VFP_FPINST2 10 | 349 | #define ARM_VFP_FPINST2 10 |
| @@ -253,7 +363,15 @@ enum arm_features { | @@ -253,7 +363,15 @@ enum arm_features { | ||
| 253 | ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ | 363 | ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ |
| 254 | ARM_FEATURE_XSCALE, /* Intel XScale extensions. */ | 364 | ARM_FEATURE_XSCALE, /* Intel XScale extensions. */ |
| 255 | ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ | 365 | ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ |
| 366 | + ARM_FEATURE_V6, | ||
| 367 | + ARM_FEATURE_V6K, | ||
| 368 | + ARM_FEATURE_V7, | ||
| 369 | + ARM_FEATURE_THUMB2, | ||
| 256 | ARM_FEATURE_MPU, /* Only has Memory Protection Unit, not full MMU. */ | 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 | ARM_FEATURE_OMAPCP /* OMAP specific CP15 ops handling. */ | 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,27 +382,44 @@ static inline int arm_feature(CPUARMState *env, int feature) | ||
| 264 | 382 | ||
| 265 | void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); | 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 | void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, | 390 | void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, |
| 268 | ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, | 391 | ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, |
| 269 | void *opaque); | 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 | #if defined(CONFIG_USER_ONLY) | 424 | #if defined(CONFIG_USER_ONLY) |
| 290 | #define TARGET_PAGE_BITS 12 | 425 | #define TARGET_PAGE_BITS 12 |
| @@ -302,6 +437,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, | @@ -302,6 +437,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, | ||
| 302 | #define cpu_signal_handler cpu_arm_signal_handler | 437 | #define cpu_signal_handler cpu_arm_signal_handler |
| 303 | #define cpu_list arm_cpu_list | 438 | #define cpu_list arm_cpu_list |
| 304 | 439 | ||
| 440 | +#define ARM_CPU_SAVE_VERSION 1 | ||
| 441 | + | ||
| 305 | /* MMU modes definitions */ | 442 | /* MMU modes definitions */ |
| 306 | #define MMU_MODE0_SUFFIX _kernel | 443 | #define MMU_MODE0_SUFFIX _kernel |
| 307 | #define MMU_MODE1_SUFFIX _user | 444 | #define MMU_MODE1_SUFFIX _user |
target-arm/exec.h
| @@ -68,12 +68,18 @@ static inline int cpu_halted(CPUState *env) { | @@ -68,12 +68,18 @@ static inline int cpu_halted(CPUState *env) { | ||
| 68 | 68 | ||
| 69 | /* In op_helper.c */ | 69 | /* In op_helper.c */ |
| 70 | 70 | ||
| 71 | -void cpu_lock(void); | ||
| 72 | -void cpu_unlock(void); | ||
| 73 | void helper_set_cp(CPUState *, uint32_t, uint32_t); | 71 | void helper_set_cp(CPUState *, uint32_t, uint32_t); |
| 74 | uint32_t helper_get_cp(CPUState *, uint32_t); | 72 | uint32_t helper_get_cp(CPUState *, uint32_t); |
| 75 | void helper_set_cp15(CPUState *, uint32_t, uint32_t); | 73 | void helper_set_cp15(CPUState *, uint32_t, uint32_t); |
| 76 | uint32_t helper_get_cp15(CPUState *, uint32_t); | 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 | void cpu_loop_exit(void); | 84 | void cpu_loop_exit(void); |
| 79 | 85 | ||
| @@ -91,4 +97,11 @@ void do_vfp_cmpes(void); | @@ -91,4 +97,11 @@ void do_vfp_cmpes(void); | ||
| 91 | void do_vfp_cmped(void); | 97 | void do_vfp_cmped(void); |
| 92 | void do_vfp_set_fpscr(void); | 98 | void do_vfp_set_fpscr(void); |
| 93 | void do_vfp_get_fpscr(void); | 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); |