* [Qemu-devel] [PATCH 0/3] StrongARM/collie support
@ 2011-04-04 13:38 Dmitry Eremin-Solenikov
2011-04-04 13:38 ` [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
` (2 more replies)
0 siblings, 3 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-04 13:38 UTC (permalink / raw)
To: qemu-devel
Please find the next iteration of the patches. Changes since previous release
are: fix for VMStateDescription of strongarm_uart, fixed RTSR reg. offset and
added emulation of strongarm SSP port. Also unused "(void) s;" statement was
dropped from hw/collie.c
^ permalink raw reply [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
2011-04-04 13:38 [Qemu-devel] [PATCH 0/3] StrongARM/collie support Dmitry Eremin-Solenikov
@ 2011-04-04 13:38 ` Dmitry Eremin-Solenikov
2011-04-06 13:56 ` Peter Maydell
` (2 more replies)
2011-04-04 13:38 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
2011-04-04 13:38 ` [Qemu-devel] [PATCH 3/3] Basic implementation of Sharp Zaurus SL-5500 collie PDA Dmitry Eremin-Solenikov
2 siblings, 3 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-04 13:38 UTC (permalink / raw)
To: qemu-devel
Currently target-arm/ assumes at least ARMv5 core. Add support for
handling also ARMv4/ARMv4T. This changes the following instructions:
BX(v4T and later)
BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
SMULxy, SMULWxy, STC2 (v5 and later)
All instructions that are "v5TE and later" are also bound to just v5, as
that's how it was before.
This patch doesn _not_ include disabling of cp15 access and base-updated
data abort model (that will be required to emulate chips based on a
ARM7TDMI), because:
* no ARM7TDMI chips are currently emulated (or planned)
* those features aren't strictly necessary for my purposes (SA-1 core
emulation).
All v5 models are handled as they are v5T. Internally we still have a
check if the model is a v5(T) or v5TE, but as all emulated cores are
v5TE, those two cases are simply aliased (for now).
Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
Sanders <vince@kyllikki.org>.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
target-arm/cpu.h | 4 ++-
target-arm/helper.c | 29 ++++++++++++++++++++++-
target-arm/translate.c | 59 +++++++++++++++++++++++++++++++++++++++--------
3 files changed, 80 insertions(+), 12 deletions(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1ae7982..e247a7a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -360,7 +360,9 @@ enum arm_features {
ARM_FEATURE_M, /* Microcontroller profile. */
ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
ARM_FEATURE_THUMB2EE,
- ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V4T,
+ ARM_FEATURE_V5,
};
static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 6788a4c..ce9a9d8 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cpuid = id;
switch (id) {
case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
env->cp15.c0_cachetype = 0x1dd20d2;
env->cp15.c1_sys = 0x00090078;
break;
case ARM_CPUID_ARM946:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_MPU);
env->cp15.c0_cachetype = 0x0f004006;
env->cp15.c1_sys = 0x00000078;
break;
case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
@@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_ARM1136_R2:
case ARM_CPUID_ARM1136:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
@@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00050078;
break;
case ARM_CPUID_ARM11MPCORE:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_VFP);
@@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0x1dd20d2;
break;
case ARM_CPUID_CORTEXA8:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXA9:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXM3:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_THUMB2);
set_feature(env, ARM_FEATURE_V7);
@@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_DIV);
break;
case ARM_CPUID_ANY: /* For userspace emulation. */
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_TI915T:
case ARM_CPUID_TI925T:
+ set_feature(env, ARM_FEATURE_V4T);
set_feature(env, ARM_FEATURE_OMAPCP);
env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
env->cp15.c0_cachetype = 0x5109149;
@@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA260:
case ARM_CPUID_PXA261:
case ARM_CPUID_PXA262:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
env->cp15.c0_cachetype = 0xd172172;
@@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA270_B1:
case ARM_CPUID_PXA270_C0:
case ARM_CPUID_PXA270_C5:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
set_feature(env, ARM_FEATURE_IWMMXT);
@@ -856,7 +879,11 @@ void do_interrupt(CPUARMState *env)
/* Switch to the new mode, and to the correct instruction set. */
env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
env->uncached_cpsr |= mask;
- env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+ /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
+ * and we should just guard the thumb mode on V4 */
+ if (arm_feature(env, ARM_FEATURE_V4T)) {
+ env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+ }
env->regs[14] = env->regs[15] + offset;
env->regs[15] = addr;
env->interrupt_request |= CPU_INTERRUPT_EXITTB;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 33417e6..05a58d4 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,6 +34,10 @@
#define GEN_HELPER 1
#include "helpers.h"
+#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
+#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
+/* currently all emulated v5 cores are also v5TE, so don't bother */
+#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5)
#define ENABLE_ARCH_5J 0
#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
@@ -750,6 +754,20 @@ static inline void store_reg_bx(CPUState *env, DisasContext *s,
}
}
+/* Variant of store_reg which uses branch&exchange logic when storing
+ * to r15 in ARM architecture v5T and above. This is used for storing
+ * the results of a LDR/LDM/POP into r15, and corresponds to the cases
+ * in the ARM ARM which use the LoadWritePC() pseudocode function. */
+static inline void store_reg_from_load(CPUState *env, DisasContext *s,
+ int reg, TCGv var)
+{
+ if (reg == 15 && ENABLE_ARCH_5) {
+ gen_bx(s, var);
+ } else {
+ store_reg(s, reg, var);
+ }
+}
+
static inline TCGv gen_ld8s(TCGv addr, int index)
{
TCGv tmp = tcg_temp_new_i32();
@@ -3443,6 +3461,10 @@ static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
/* Mask out undefined bits. */
mask &= ~CPSR_RESERVED;
+ if (!arm_feature(env, ARM_FEATURE_V4T))
+ mask &= ~CPSR_T;
+ if (!arm_feature(env, ARM_FEATURE_V5))
+ mask &= ~CPSR_Q; /* V5TE in reality*/
if (!arm_feature(env, ARM_FEATURE_V6))
mask &= ~(CPSR_E | CPSR_GE);
if (!arm_feature(env, ARM_FEATURE_THUMB2))
@@ -6145,6 +6167,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
cond = insn >> 28;
if (cond == 0xf){
+ /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
+ * choose to UNDEF. In ARMv5 and above the space is used
+ * for miscellaneous unconditional instructions.
+ */
+ ARCH(5);
+
/* Unconditional instructions. */
if (((insn >> 25) & 7) == 1) {
/* NEON Data processing. */
@@ -6173,6 +6201,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
}
/* Otherwise PLD; v5TE+ */
+ ARCH(5TE);
return;
}
if (((insn & 0x0f70f000) == 0x0450f000) ||
@@ -6309,6 +6338,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
val += (offset << 2) | ((insn >> 23) & 2) | 1;
/* pipeline offset */
val += 4;
+ /* protected by ARCH(5); above, near the start of uncond block */
gen_bx_im(s, val);
return;
} else if ((insn & 0x0e000f00) == 0x0c000100) {
@@ -6320,6 +6350,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
} else if ((insn & 0x0fe00000) == 0x0c400000) {
/* Coprocessor double register transfer. */
+ ARCH(5TE);
} else if ((insn & 0x0f000010) == 0x0e000010) {
/* Additional coprocessor register transfer. */
} else if ((insn & 0x0ff10020) == 0x01000000) {
@@ -6420,10 +6451,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
case 0x1:
if (op1 == 1) {
/* branch/exchange thumb (bx). */
+ ARCH(4T);
tmp = load_reg(s, rm);
gen_bx(s, tmp);
} else if (op1 == 3) {
/* clz */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
tmp = load_reg(s, rm);
gen_helper_clz(tmp, tmp);
@@ -6446,6 +6479,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (op1 != 1)
goto illegal_op;
+ ARCH(5);
/* branch link/exchange thumb (blx) */
tmp = load_reg(s, rm);
tmp2 = tcg_temp_new_i32();
@@ -6454,6 +6488,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx(s, tmp);
break;
case 0x5: /* saturating add/subtract */
+ ARCH(5TE);
rd = (insn >> 12) & 0xf;
rn = (insn >> 16) & 0xf;
tmp = load_reg(s, rm);
@@ -6475,12 +6510,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
}
/* bkpt */
+ ARCH(5);
gen_exception_insn(s, 4, EXCP_BKPT);
break;
case 0x8: /* signed multiply */
case 0xa:
case 0xc:
case 0xe:
+ ARCH(5TE);
rs = (insn >> 8) & 0xf;
rn = (insn >> 12) & 0xf;
rd = (insn >> 16) & 0xf;
@@ -6876,6 +6913,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
load = 1;
} else if (sh & 2) {
+ ARCH(5TE);
/* doubleword */
if (sh & 1) {
/* store */
@@ -7216,10 +7254,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
if (insn & (1 << 20)) {
/* Complete the load. */
- if (rd == 15)
- gen_bx(s, tmp);
- else
- store_reg(s, rd, tmp);
+ store_reg_from_load(env, s, rd, tmp);
}
break;
case 0x08:
@@ -7272,9 +7307,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (insn & (1 << 20)) {
/* load */
tmp = gen_ld32(addr, IS_USER(s));
- if (i == 15) {
- gen_bx(s, tmp);
- } else if (user) {
+ if (user) {
tmp2 = tcg_const_i32(i);
gen_helper_set_user_reg(tmp2, tmp);
tcg_temp_free_i32(tmp2);
@@ -7283,7 +7316,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
loaded_var = tmp;
loaded_base = 1;
} else {
- store_reg(s, i, tmp);
+ store_reg_from_load(env, s, i, tmp);
}
} else {
/* store */
@@ -7483,6 +7516,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
16-bit instructions to get correct prefetch abort behavior. */
insn = insn_hw1;
if ((insn & (1 << 12)) == 0) {
+ ARCH(5);
/* Second half of blx. */
offset = ((insn & 0x7ff) << 1);
tmp = load_reg(s, 14);
@@ -8079,6 +8113,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
} else {
/* blx */
offset &= ~(uint32_t)2;
+ /* thumb2 bx, no need to check */
gen_bx_im(s, offset);
}
} else if (((insn >> 23) & 7) == 7) {
@@ -8660,11 +8695,13 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
case 3:/* branch [and link] exchange thumb register */
tmp = load_reg(s, rm);
if (insn & (1 << 7)) {
+ ARCH(5);
val = (uint32_t)s->pc | 1;
tmp2 = tcg_temp_new_i32();
tcg_gen_movi_i32(tmp2, val);
store_reg(s, 14, tmp2);
}
+ /* already thumb, no need to check */
gen_bx(s, tmp);
break;
}
@@ -9024,8 +9061,9 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
/* write back the new stack pointer */
store_reg(s, 13, addr);
/* set the new PC value */
- if ((insn & 0x0900) == 0x0900)
- gen_bx(s, tmp);
+ if ((insn & 0x0900) == 0x0900) {
+ store_reg_from_load(env, s, 15, tmp);
+ }
break;
case 1: case 3: case 9: case 11: /* czb */
@@ -9056,6 +9094,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
break;
case 0xe: /* bkpt */
+ ARCH(5);
gen_exception_insn(s, 2, EXCP_BKPT);
break;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
2011-04-04 13:38 ` [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
@ 2011-04-06 13:56 ` Peter Maydell
2011-04-06 17:41 ` Dmitry Eremin-Solenikov
2011-04-10 11:25 ` Aurelien Jarno
2011-04-10 12:24 ` Marek Vasut
2 siblings, 1 reply; 17+ messages in thread
From: Peter Maydell @ 2011-04-06 13:56 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: qemu-devel
On 4 April 2011 14:38, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
> Currently target-arm/ assumes at least ARMv5 core. Add support for
> handling also ARMv4/ARMv4T. This changes the following instructions:
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
-- PMM
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
2011-04-06 13:56 ` Peter Maydell
@ 2011-04-06 17:41 ` Dmitry Eremin-Solenikov
2011-04-06 17:50 ` Peter Maydell
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-06 17:41 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-devel
On 4/6/11, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 4 April 2011 14:38, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
>> Currently target-arm/ assumes at least ARMv5 core. Add support for
>> handling also ARMv4/ARMv4T. This changes the following instructions:
>
>> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
>
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Please commit then, as I don't have commit access.
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
2011-04-06 17:41 ` Dmitry Eremin-Solenikov
@ 2011-04-06 17:50 ` Peter Maydell
0 siblings, 0 replies; 17+ messages in thread
From: Peter Maydell @ 2011-04-06 17:50 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: qemu-devel
On 6 April 2011 18:41, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
> On 4/6/11, Peter Maydell <peter.maydell@linaro.org> wrote:
>> On 4 April 2011 14:38, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
>>> Currently target-arm/ assumes at least ARMv5 core. Add support for
>>> handling also ARMv4/ARMv4T. This changes the following instructions:
>>
>>> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
>>
>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>
> Please commit then, as I don't have commit access.
Neither do I :-) I'm assuming Aurelien will also review this;
if all else fails it will go into my set of ARM patches for the
next pull request I make.
-- PMM
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
2011-04-04 13:38 ` [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-04-06 13:56 ` Peter Maydell
@ 2011-04-10 11:25 ` Aurelien Jarno
2011-04-10 12:24 ` Marek Vasut
2 siblings, 0 replies; 17+ messages in thread
From: Aurelien Jarno @ 2011-04-10 11:25 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: qemu-devel
On Mon, Apr 04, 2011 at 05:38:44PM +0400, Dmitry Eremin-Solenikov wrote:
> Currently target-arm/ assumes at least ARMv5 core. Add support for
> handling also ARMv4/ARMv4T. This changes the following instructions:
>
> BX(v4T and later)
>
> BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
> MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
> SMULxy, SMULWxy, STC2 (v5 and later)
>
> All instructions that are "v5TE and later" are also bound to just v5, as
> that's how it was before.
>
> This patch doesn _not_ include disabling of cp15 access and base-updated
> data abort model (that will be required to emulate chips based on a
> ARM7TDMI), because:
> * no ARM7TDMI chips are currently emulated (or planned)
> * those features aren't strictly necessary for my purposes (SA-1 core
> emulation).
>
> All v5 models are handled as they are v5T. Internally we still have a
> check if the model is a v5(T) or v5TE, but as all emulated cores are
> v5TE, those two cases are simply aliased (for now).
>
> Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
> which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
> Sanders <vince@kyllikki.org>.
>
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> ---
> target-arm/cpu.h | 4 ++-
> target-arm/helper.c | 29 ++++++++++++++++++++++-
> target-arm/translate.c | 59 +++++++++++++++++++++++++++++++++++++++--------
> 3 files changed, 80 insertions(+), 12 deletions(-)
Thanks, applied.
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 1ae7982..e247a7a 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -360,7 +360,9 @@ enum arm_features {
> ARM_FEATURE_M, /* Microcontroller profile. */
> ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
> ARM_FEATURE_THUMB2EE,
> - ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
> + ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
> + ARM_FEATURE_V4T,
> + ARM_FEATURE_V5,
> };
>
> static inline int arm_feature(CPUARMState *env, int feature)
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index 6788a4c..ce9a9d8 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> env->cp15.c0_cpuid = id;
> switch (id) {
> case ARM_CPUID_ARM926:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_VFP);
> env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
> env->cp15.c0_cachetype = 0x1dd20d2;
> env->cp15.c1_sys = 0x00090078;
> break;
> case ARM_CPUID_ARM946:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_MPU);
> env->cp15.c0_cachetype = 0x0f004006;
> env->cp15.c1_sys = 0x00000078;
> break;
> case ARM_CPUID_ARM1026:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_VFP);
> set_feature(env, ARM_FEATURE_AUXCR);
> env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
> @@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> break;
> case ARM_CPUID_ARM1136_R2:
> case ARM_CPUID_ARM1136:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_VFP);
> set_feature(env, ARM_FEATURE_AUXCR);
> @@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> env->cp15.c1_sys = 0x00050078;
> break;
> case ARM_CPUID_ARM11MPCORE:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_VFP);
> @@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> env->cp15.c0_cachetype = 0x1dd20d2;
> break;
> case ARM_CPUID_CORTEXA8:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_V7);
> @@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> env->cp15.c1_sys = 0x00c50078;
> break;
> case ARM_CPUID_CORTEXA9:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_V7);
> @@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> env->cp15.c1_sys = 0x00c50078;
> break;
> case ARM_CPUID_CORTEXM3:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_THUMB2);
> set_feature(env, ARM_FEATURE_V7);
> @@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> set_feature(env, ARM_FEATURE_DIV);
> break;
> case ARM_CPUID_ANY: /* For userspace emulation. */
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_V7);
> @@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> break;
> case ARM_CPUID_TI915T:
> case ARM_CPUID_TI925T:
> + set_feature(env, ARM_FEATURE_V4T);
> set_feature(env, ARM_FEATURE_OMAPCP);
> env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
> env->cp15.c0_cachetype = 0x5109149;
> @@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> case ARM_CPUID_PXA260:
> case ARM_CPUID_PXA261:
> case ARM_CPUID_PXA262:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_XSCALE);
> /* JTAG_ID is ((id << 28) | 0x09265013) */
> env->cp15.c0_cachetype = 0xd172172;
> @@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> case ARM_CPUID_PXA270_B1:
> case ARM_CPUID_PXA270_C0:
> case ARM_CPUID_PXA270_C5:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_XSCALE);
> /* JTAG_ID is ((id << 28) | 0x09265013) */
> set_feature(env, ARM_FEATURE_IWMMXT);
> @@ -856,7 +879,11 @@ void do_interrupt(CPUARMState *env)
> /* Switch to the new mode, and to the correct instruction set. */
> env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
> env->uncached_cpsr |= mask;
> - env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> + /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
> + * and we should just guard the thumb mode on V4 */
> + if (arm_feature(env, ARM_FEATURE_V4T)) {
> + env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> + }
> env->regs[14] = env->regs[15] + offset;
> env->regs[15] = addr;
> env->interrupt_request |= CPU_INTERRUPT_EXITTB;
> diff --git a/target-arm/translate.c b/target-arm/translate.c
> index 33417e6..05a58d4 100644
> --- a/target-arm/translate.c
> +++ b/target-arm/translate.c
> @@ -34,6 +34,10 @@
> #define GEN_HELPER 1
> #include "helpers.h"
>
> +#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
> +#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
> +/* currently all emulated v5 cores are also v5TE, so don't bother */
> +#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5)
> #define ENABLE_ARCH_5J 0
> #define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
> #define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
> @@ -750,6 +754,20 @@ static inline void store_reg_bx(CPUState *env, DisasContext *s,
> }
> }
>
> +/* Variant of store_reg which uses branch&exchange logic when storing
> + * to r15 in ARM architecture v5T and above. This is used for storing
> + * the results of a LDR/LDM/POP into r15, and corresponds to the cases
> + * in the ARM ARM which use the LoadWritePC() pseudocode function. */
> +static inline void store_reg_from_load(CPUState *env, DisasContext *s,
> + int reg, TCGv var)
> +{
> + if (reg == 15 && ENABLE_ARCH_5) {
> + gen_bx(s, var);
> + } else {
> + store_reg(s, reg, var);
> + }
> +}
> +
> static inline TCGv gen_ld8s(TCGv addr, int index)
> {
> TCGv tmp = tcg_temp_new_i32();
> @@ -3443,6 +3461,10 @@ static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
>
> /* Mask out undefined bits. */
> mask &= ~CPSR_RESERVED;
> + if (!arm_feature(env, ARM_FEATURE_V4T))
> + mask &= ~CPSR_T;
> + if (!arm_feature(env, ARM_FEATURE_V5))
> + mask &= ~CPSR_Q; /* V5TE in reality*/
> if (!arm_feature(env, ARM_FEATURE_V6))
> mask &= ~(CPSR_E | CPSR_GE);
> if (!arm_feature(env, ARM_FEATURE_THUMB2))
> @@ -6145,6 +6167,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> goto illegal_op;
> cond = insn >> 28;
> if (cond == 0xf){
> + /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
> + * choose to UNDEF. In ARMv5 and above the space is used
> + * for miscellaneous unconditional instructions.
> + */
> + ARCH(5);
> +
> /* Unconditional instructions. */
> if (((insn >> 25) & 7) == 1) {
> /* NEON Data processing. */
> @@ -6173,6 +6201,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> }
> }
> /* Otherwise PLD; v5TE+ */
> + ARCH(5TE);
> return;
> }
> if (((insn & 0x0f70f000) == 0x0450f000) ||
> @@ -6309,6 +6338,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> val += (offset << 2) | ((insn >> 23) & 2) | 1;
> /* pipeline offset */
> val += 4;
> + /* protected by ARCH(5); above, near the start of uncond block */
> gen_bx_im(s, val);
> return;
> } else if ((insn & 0x0e000f00) == 0x0c000100) {
> @@ -6320,6 +6350,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> }
> } else if ((insn & 0x0fe00000) == 0x0c400000) {
> /* Coprocessor double register transfer. */
> + ARCH(5TE);
> } else if ((insn & 0x0f000010) == 0x0e000010) {
> /* Additional coprocessor register transfer. */
> } else if ((insn & 0x0ff10020) == 0x01000000) {
> @@ -6420,10 +6451,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> case 0x1:
> if (op1 == 1) {
> /* branch/exchange thumb (bx). */
> + ARCH(4T);
> tmp = load_reg(s, rm);
> gen_bx(s, tmp);
> } else if (op1 == 3) {
> /* clz */
> + ARCH(5);
> rd = (insn >> 12) & 0xf;
> tmp = load_reg(s, rm);
> gen_helper_clz(tmp, tmp);
> @@ -6446,6 +6479,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> if (op1 != 1)
> goto illegal_op;
>
> + ARCH(5);
> /* branch link/exchange thumb (blx) */
> tmp = load_reg(s, rm);
> tmp2 = tcg_temp_new_i32();
> @@ -6454,6 +6488,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> gen_bx(s, tmp);
> break;
> case 0x5: /* saturating add/subtract */
> + ARCH(5TE);
> rd = (insn >> 12) & 0xf;
> rn = (insn >> 16) & 0xf;
> tmp = load_reg(s, rm);
> @@ -6475,12 +6510,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> goto illegal_op;
> }
> /* bkpt */
> + ARCH(5);
> gen_exception_insn(s, 4, EXCP_BKPT);
> break;
> case 0x8: /* signed multiply */
> case 0xa:
> case 0xc:
> case 0xe:
> + ARCH(5TE);
> rs = (insn >> 8) & 0xf;
> rn = (insn >> 12) & 0xf;
> rd = (insn >> 16) & 0xf;
> @@ -6876,6 +6913,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> }
> load = 1;
> } else if (sh & 2) {
> + ARCH(5TE);
> /* doubleword */
> if (sh & 1) {
> /* store */
> @@ -7216,10 +7254,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> }
> if (insn & (1 << 20)) {
> /* Complete the load. */
> - if (rd == 15)
> - gen_bx(s, tmp);
> - else
> - store_reg(s, rd, tmp);
> + store_reg_from_load(env, s, rd, tmp);
> }
> break;
> case 0x08:
> @@ -7272,9 +7307,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> if (insn & (1 << 20)) {
> /* load */
> tmp = gen_ld32(addr, IS_USER(s));
> - if (i == 15) {
> - gen_bx(s, tmp);
> - } else if (user) {
> + if (user) {
> tmp2 = tcg_const_i32(i);
> gen_helper_set_user_reg(tmp2, tmp);
> tcg_temp_free_i32(tmp2);
> @@ -7283,7 +7316,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
> loaded_var = tmp;
> loaded_base = 1;
> } else {
> - store_reg(s, i, tmp);
> + store_reg_from_load(env, s, i, tmp);
> }
> } else {
> /* store */
> @@ -7483,6 +7516,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
> 16-bit instructions to get correct prefetch abort behavior. */
> insn = insn_hw1;
> if ((insn & (1 << 12)) == 0) {
> + ARCH(5);
> /* Second half of blx. */
> offset = ((insn & 0x7ff) << 1);
> tmp = load_reg(s, 14);
> @@ -8079,6 +8113,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
> } else {
> /* blx */
> offset &= ~(uint32_t)2;
> + /* thumb2 bx, no need to check */
> gen_bx_im(s, offset);
> }
> } else if (((insn >> 23) & 7) == 7) {
> @@ -8660,11 +8695,13 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
> case 3:/* branch [and link] exchange thumb register */
> tmp = load_reg(s, rm);
> if (insn & (1 << 7)) {
> + ARCH(5);
> val = (uint32_t)s->pc | 1;
> tmp2 = tcg_temp_new_i32();
> tcg_gen_movi_i32(tmp2, val);
> store_reg(s, 14, tmp2);
> }
> + /* already thumb, no need to check */
> gen_bx(s, tmp);
> break;
> }
> @@ -9024,8 +9061,9 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
> /* write back the new stack pointer */
> store_reg(s, 13, addr);
> /* set the new PC value */
> - if ((insn & 0x0900) == 0x0900)
> - gen_bx(s, tmp);
> + if ((insn & 0x0900) == 0x0900) {
> + store_reg_from_load(env, s, 15, tmp);
> + }
> break;
>
> case 1: case 3: case 9: case 11: /* czb */
> @@ -9056,6 +9094,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
> break;
>
> case 0xe: /* bkpt */
> + ARCH(5);
> gen_exception_insn(s, 2, EXCP_BKPT);
> break;
>
> --
> 1.7.4.1
>
>
>
--
Aurelien Jarno GPG: 1024D/F1BCDB73
aurelien@aurel32.net http://www.aurel32.net
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
2011-04-04 13:38 ` [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-04-06 13:56 ` Peter Maydell
2011-04-10 11:25 ` Aurelien Jarno
@ 2011-04-10 12:24 ` Marek Vasut
2011-04-11 15:18 ` Dmitry Eremin-Solenikov
2 siblings, 1 reply; 17+ messages in thread
From: Marek Vasut @ 2011-04-10 12:24 UTC (permalink / raw)
To: qemu-devel; +Cc: Dmitry Eremin-Solenikov
On Monday 04 April 2011 15:38:44 Dmitry Eremin-Solenikov wrote:
> Currently target-arm/ assumes at least ARMv5 core. Add support for
> handling also ARMv4/ARMv4T. This changes the following instructions:
>
> BX(v4T and later)
>
> BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
> MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
> SMULxy, SMULWxy, STC2 (v5 and later)
>
> All instructions that are "v5TE and later" are also bound to just v5, as
> that's how it was before.
>
> This patch doesn _not_ include disabling of cp15 access and base-updated
> data abort model (that will be required to emulate chips based on a
> ARM7TDMI), because:
> * no ARM7TDMI chips are currently emulated (or planned)
Hi,
this will come handy as I plan to add support for certain arm7tdmi chip (forward
port at91sam7 patchset)
Cheers
> * those features aren't strictly necessary for my purposes (SA-1 core
> emulation).
>
> All v5 models are handled as they are v5T. Internally we still have a
> check if the model is a v5(T) or v5TE, but as all emulated cores are
> v5TE, those two cases are simply aliased (for now).
>
> Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
> which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
> Sanders <vince@kyllikki.org>.
>
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> ---
> target-arm/cpu.h | 4 ++-
> target-arm/helper.c | 29 ++++++++++++++++++++++-
> target-arm/translate.c | 59
> +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 80
> insertions(+), 12 deletions(-)
>
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 1ae7982..e247a7a 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -360,7 +360,9 @@ enum arm_features {
> ARM_FEATURE_M, /* Microcontroller profile. */
> ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
> ARM_FEATURE_THUMB2EE,
> - ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
> + ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
> + ARM_FEATURE_V4T,
> + ARM_FEATURE_V5,
> };
>
> static inline int arm_feature(CPUARMState *env, int feature)
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index 6788a4c..ce9a9d8 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c0_cpuid = id;
> switch (id) {
> case ARM_CPUID_ARM926:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_VFP);
> env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
> env->cp15.c0_cachetype = 0x1dd20d2;
> env->cp15.c1_sys = 0x00090078;
> break;
> case ARM_CPUID_ARM946:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_MPU);
> env->cp15.c0_cachetype = 0x0f004006;
> env->cp15.c1_sys = 0x00000078;
> break;
> case ARM_CPUID_ARM1026:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_VFP);
> set_feature(env, ARM_FEATURE_AUXCR);
> env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
> @@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t
> id) break;
> case ARM_CPUID_ARM1136_R2:
> case ARM_CPUID_ARM1136:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_VFP);
> set_feature(env, ARM_FEATURE_AUXCR);
> @@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t
> id) env->cp15.c1_sys = 0x00050078;
> break;
> case ARM_CPUID_ARM11MPCORE:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_VFP);
> @@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c0_cachetype = 0x1dd20d2;
> break;
> case ARM_CPUID_CORTEXA8:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_V7);
> @@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c1_sys = 0x00c50078;
> break;
> case ARM_CPUID_CORTEXA9:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_V7);
> @@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c1_sys = 0x00c50078;
> break;
> case ARM_CPUID_CORTEXM3:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_THUMB2);
> set_feature(env, ARM_FEATURE_V7);
> @@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) set_feature(env, ARM_FEATURE_DIV);
> break;
> case ARM_CPUID_ANY: /* For userspace emulation. */
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_V6);
> set_feature(env, ARM_FEATURE_V6K);
> set_feature(env, ARM_FEATURE_V7);
> @@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) break;
> case ARM_CPUID_TI915T:
> case ARM_CPUID_TI925T:
> + set_feature(env, ARM_FEATURE_V4T);
> set_feature(env, ARM_FEATURE_OMAPCP);
> env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
> env->cp15.c0_cachetype = 0x5109149;
> @@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) case ARM_CPUID_PXA260:
> case ARM_CPUID_PXA261:
> case ARM_CPUID_PXA262:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_XSCALE);
> /* JTAG_ID is ((id << 28) | 0x09265013) */
> env->cp15.c0_cachetype = 0xd172172;
> @@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) case ARM_CPUID_PXA270_B1:
> case ARM_CPUID_PXA270_C0:
> case ARM_CPUID_PXA270_C5:
> + set_feature(env, ARM_FEATURE_V4T);
> + set_feature(env, ARM_FEATURE_V5);
> set_feature(env, ARM_FEATURE_XSCALE);
> /* JTAG_ID is ((id << 28) | 0x09265013) */
> set_feature(env, ARM_FEATURE_IWMMXT);
> @@ -856,7 +879,11 @@ void do_interrupt(CPUARMState *env)
> /* Switch to the new mode, and to the correct instruction set. */
> env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
> env->uncached_cpsr |= mask;
> - env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> + /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
> + * and we should just guard the thumb mode on V4 */
> + if (arm_feature(env, ARM_FEATURE_V4T)) {
> + env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> + }
> env->regs[14] = env->regs[15] + offset;
> env->regs[15] = addr;
> env->interrupt_request |= CPU_INTERRUPT_EXITTB;
> diff --git a/target-arm/translate.c b/target-arm/translate.c
> index 33417e6..05a58d4 100644
> --- a/target-arm/translate.c
> +++ b/target-arm/translate.c
> @@ -34,6 +34,10 @@
> #define GEN_HELPER 1
> #include "helpers.h"
>
> +#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
> +#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
> +/* currently all emulated v5 cores are also v5TE, so don't bother */
> +#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5)
> #define ENABLE_ARCH_5J 0
> #define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
> #define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
> @@ -750,6 +754,20 @@ static inline void store_reg_bx(CPUState *env,
> DisasContext *s, }
> }
>
> +/* Variant of store_reg which uses branch&exchange logic when storing
> + * to r15 in ARM architecture v5T and above. This is used for storing
> + * the results of a LDR/LDM/POP into r15, and corresponds to the cases
> + * in the ARM ARM which use the LoadWritePC() pseudocode function. */
> +static inline void store_reg_from_load(CPUState *env, DisasContext *s,
> + int reg, TCGv var)
> +{
> + if (reg == 15 && ENABLE_ARCH_5) {
> + gen_bx(s, var);
> + } else {
> + store_reg(s, reg, var);
> + }
> +}
> +
> static inline TCGv gen_ld8s(TCGv addr, int index)
> {
> TCGv tmp = tcg_temp_new_i32();
> @@ -3443,6 +3461,10 @@ static uint32_t msr_mask(CPUState *env, DisasContext
> *s, int flags, int spsr) {
>
> /* Mask out undefined bits. */
> mask &= ~CPSR_RESERVED;
> + if (!arm_feature(env, ARM_FEATURE_V4T))
> + mask &= ~CPSR_T;
> + if (!arm_feature(env, ARM_FEATURE_V5))
> + mask &= ~CPSR_Q; /* V5TE in reality*/
> if (!arm_feature(env, ARM_FEATURE_V6))
> mask &= ~(CPSR_E | CPSR_GE);
> if (!arm_feature(env, ARM_FEATURE_THUMB2))
> @@ -6145,6 +6167,12 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) goto illegal_op;
> cond = insn >> 28;
> if (cond == 0xf){
> + /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
> + * choose to UNDEF. In ARMv5 and above the space is used
> + * for miscellaneous unconditional instructions.
> + */
> + ARCH(5);
> +
> /* Unconditional instructions. */
> if (((insn >> 25) & 7) == 1) {
> /* NEON Data processing. */
> @@ -6173,6 +6201,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
> }
> /* Otherwise PLD; v5TE+ */
> + ARCH(5TE);
> return;
> }
> if (((insn & 0x0f70f000) == 0x0450f000) ||
> @@ -6309,6 +6338,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) val += (offset << 2) | ((insn >> 23) & 2) | 1;
> /* pipeline offset */
> val += 4;
> + /* protected by ARCH(5); above, near the start of uncond block
> */ gen_bx_im(s, val);
> return;
> } else if ((insn & 0x0e000f00) == 0x0c000100) {
> @@ -6320,6 +6350,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
> } else if ((insn & 0x0fe00000) == 0x0c400000) {
> /* Coprocessor double register transfer. */
> + ARCH(5TE);
> } else if ((insn & 0x0f000010) == 0x0e000010) {
> /* Additional coprocessor register transfer. */
> } else if ((insn & 0x0ff10020) == 0x01000000) {
> @@ -6420,10 +6451,12 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) case 0x1:
> if (op1 == 1) {
> /* branch/exchange thumb (bx). */
> + ARCH(4T);
> tmp = load_reg(s, rm);
> gen_bx(s, tmp);
> } else if (op1 == 3) {
> /* clz */
> + ARCH(5);
> rd = (insn >> 12) & 0xf;
> tmp = load_reg(s, rm);
> gen_helper_clz(tmp, tmp);
> @@ -6446,6 +6479,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) if (op1 != 1)
> goto illegal_op;
>
> + ARCH(5);
> /* branch link/exchange thumb (blx) */
> tmp = load_reg(s, rm);
> tmp2 = tcg_temp_new_i32();
> @@ -6454,6 +6488,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) gen_bx(s, tmp);
> break;
> case 0x5: /* saturating add/subtract */
> + ARCH(5TE);
> rd = (insn >> 12) & 0xf;
> rn = (insn >> 16) & 0xf;
> tmp = load_reg(s, rm);
> @@ -6475,12 +6510,14 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) goto illegal_op;
> }
> /* bkpt */
> + ARCH(5);
> gen_exception_insn(s, 4, EXCP_BKPT);
> break;
> case 0x8: /* signed multiply */
> case 0xa:
> case 0xc:
> case 0xe:
> + ARCH(5TE);
> rs = (insn >> 8) & 0xf;
> rn = (insn >> 12) & 0xf;
> rd = (insn >> 16) & 0xf;
> @@ -6876,6 +6913,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
> load = 1;
> } else if (sh & 2) {
> + ARCH(5TE);
> /* doubleword */
> if (sh & 1) {
> /* store */
> @@ -7216,10 +7254,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
> if (insn & (1 << 20)) {
> /* Complete the load. */
> - if (rd == 15)
> - gen_bx(s, tmp);
> - else
> - store_reg(s, rd, tmp);
> + store_reg_from_load(env, s, rd, tmp);
> }
> break;
> case 0x08:
> @@ -7272,9 +7307,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) if (insn & (1 << 20)) {
> /* load */
> tmp = gen_ld32(addr, IS_USER(s));
> - if (i == 15) {
> - gen_bx(s, tmp);
> - } else if (user) {
> + if (user) {
> tmp2 = tcg_const_i32(i);
> gen_helper_set_user_reg(tmp2, tmp);
> tcg_temp_free_i32(tmp2);
> @@ -7283,7 +7316,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) loaded_var = tmp;
> loaded_base = 1;
> } else {
> - store_reg(s, i, tmp);
> + store_reg_from_load(env, s, i, tmp);
> }
> } else {
> /* store */
> @@ -7483,6 +7516,7 @@ static int disas_thumb2_insn(CPUState *env,
> DisasContext *s, uint16_t insn_hw1) 16-bit instructions to get correct
> prefetch abort behavior. */ insn = insn_hw1;
> if ((insn & (1 << 12)) == 0) {
> + ARCH(5);
> /* Second half of blx. */
> offset = ((insn & 0x7ff) << 1);
> tmp = load_reg(s, 14);
> @@ -8079,6 +8113,7 @@ static int disas_thumb2_insn(CPUState *env,
> DisasContext *s, uint16_t insn_hw1) } else {
> /* blx */
> offset &= ~(uint32_t)2;
> + /* thumb2 bx, no need to check */
> gen_bx_im(s, offset);
> }
> } else if (((insn >> 23) & 7) == 7) {
> @@ -8660,11 +8695,13 @@ static void disas_thumb_insn(CPUState *env,
> DisasContext *s) case 3:/* branch [and link] exchange thumb register */
> tmp = load_reg(s, rm);
> if (insn & (1 << 7)) {
> + ARCH(5);
> val = (uint32_t)s->pc | 1;
> tmp2 = tcg_temp_new_i32();
> tcg_gen_movi_i32(tmp2, val);
> store_reg(s, 14, tmp2);
> }
> + /* already thumb, no need to check */
> gen_bx(s, tmp);
> break;
> }
> @@ -9024,8 +9061,9 @@ static void disas_thumb_insn(CPUState *env,
> DisasContext *s) /* write back the new stack pointer */
> store_reg(s, 13, addr);
> /* set the new PC value */
> - if ((insn & 0x0900) == 0x0900)
> - gen_bx(s, tmp);
> + if ((insn & 0x0900) == 0x0900) {
> + store_reg_from_load(env, s, 15, tmp);
> + }
> break;
>
> case 1: case 3: case 9: case 11: /* czb */
> @@ -9056,6 +9094,7 @@ static void disas_thumb_insn(CPUState *env,
> DisasContext *s) break;
>
> case 0xe: /* bkpt */
> + ARCH(5);
> gen_exception_insn(s, 2, EXCP_BKPT);
> break;
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
2011-04-10 12:24 ` Marek Vasut
@ 2011-04-11 15:18 ` Dmitry Eremin-Solenikov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-11 15:18 UTC (permalink / raw)
To: Marek Vasut; +Cc: qemu-devel
Hi,
On 4/10/11, Marek Vasut <marek.vasut@gmail.com> wrote:
> On Monday 04 April 2011 15:38:44 Dmitry Eremin-Solenikov wrote:
>> Currently target-arm/ assumes at least ARMv5 core. Add support for
>> handling also ARMv4/ARMv4T. This changes the following instructions:
>>
>> BX(v4T and later)
>>
>> BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
>> MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
>> SMULxy, SMULWxy, STC2 (v5 and later)
>>
>> All instructions that are "v5TE and later" are also bound to just v5, as
>> that's how it was before.
>>
>> This patch doesn _not_ include disabling of cp15 access and base-updated
>> data abort model (that will be required to emulate chips based on a
>> ARM7TDMI), because:
>> * no ARM7TDMI chips are currently emulated (or planned)
>
> Hi,
>
> this will come handy as I plan to add support for certain arm7tdmi chip
> (forward
> port at91sam7 patchset)
Please be sure to check for the base-updated/base-restored part of the original
patch as I'm nearly sure that it will be required for your chips.
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
2011-04-04 13:38 [Qemu-devel] [PATCH 0/3] StrongARM/collie support Dmitry Eremin-Solenikov
2011-04-04 13:38 ` [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
@ 2011-04-04 13:38 ` Dmitry Eremin-Solenikov
2011-04-10 11:40 ` Aurelien Jarno
2011-04-04 13:38 ` [Qemu-devel] [PATCH 3/3] Basic implementation of Sharp Zaurus SL-5500 collie PDA Dmitry Eremin-Solenikov
2 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-04 13:38 UTC (permalink / raw)
To: qemu-devel
Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
Implemented:
- IRQs
- GPIO
- PPC
- RTC
- UARTs (no IrDA/etc.)
- OST reused from pxa25x
Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
hw/strongarm.c
V3:
* fix the name of UART VMSD
* fix RTSR reg offset
* add SSP support
V2:
* removed all strongarm variants except latest
* dropped unused casts
* fixed PIC vmstate
* fixed new devices created with version_id = 1
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
Makefile.target | 1 +
hw/strongarm.c | 1539 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/strongarm.h | 64 +++
target-arm/cpu.h | 3 +
target-arm/helper.c | 9 +
5 files changed, 1616 insertions(+), 0 deletions(-)
create mode 100644 hw/strongarm.c
create mode 100644 hw/strongarm.h
diff --git a/Makefile.target b/Makefile.target
index 2f76714..cd03cec 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -352,6 +352,7 @@ obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
obj-arm-y += vexpress.o
+obj-arm-y += strongarm.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/strongarm.c b/hw/strongarm.c
new file mode 100644
index 0000000..90b9ad0
--- /dev/null
+++ b/hw/strongarm.c
@@ -0,0 +1,1539 @@
+/*
+ * StrongARM SA-1100/SA-1110 emulation
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * Largely based on StrongARM emulation:
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * UART code based on QEMU 16550A UART emulation
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "strongarm.h"
+#include "qemu-error.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+#include "ssi.h"
+
+/*
+ TODO
+ - Implement cp15, c14 ?
+ - Implement cp15, c15 !!! (idle used in L)
+ - Implement idle mode handling/DIM
+ - Implement sleep mode/Wake sources
+ - Implement reset control
+ - Implement memory control regs
+ - PCMCIA handling
+ - Maybe support MBGNT/MBREQ
+ - DMA channels
+ - GPCLK
+ - IrDA
+ - MCP
+ - Enhance UART with modem signals
+ */
+
+static struct {
+ target_phys_addr_t io_base;
+ int irq;
+} sa_serial[] = {
+ { 0x80010000, SA_PIC_UART1 },
+ { 0x80030000, SA_PIC_UART2 },
+ { 0x80050000, SA_PIC_UART3 },
+ { 0, 0 }
+};
+
+/* Interrupt Controller */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ uint32_t pending;
+ uint32_t enabled;
+ uint32_t is_fiq;
+ uint32_t int_idle;
+} StrongARMPICState;
+
+#define ICIP 0x00
+#define ICMR 0x04
+#define ICLR 0x08
+#define ICFP 0x10
+#define ICPR 0x20
+#define ICCR 0x0c
+
+#define SA_PIC_SRCS 32
+
+
+static void strongarm_pic_update(void *opaque)
+{
+ StrongARMPICState *s = opaque;
+
+ /* FIXME: reflect DIM */
+ qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
+ qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
+}
+
+static void strongarm_pic_set_irq(void *opaque, int irq, int level)
+{
+ StrongARMPICState *s = opaque;
+
+ if (level) {
+ s->pending |= 1 << irq;
+ } else {
+ s->pending &= ~(1 << irq);
+ }
+
+ strongarm_pic_update(s);
+}
+
+static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICIP:
+ return s->pending & ~s->is_fiq & s->enabled;
+ case ICMR:
+ return s->enabled;
+ case ICLR:
+ return s->is_fiq;
+ case ICCR:
+ return s->int_idle == 0;
+ case ICFP:
+ return s->pending & s->is_fiq & s->enabled;
+ case ICPR:
+ return s->pending;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICMR:
+ s->enabled = value;
+ break;
+ case ICLR:
+ s->is_fiq = value;
+ break;
+ case ICCR:
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ break;
+ }
+ strongarm_pic_update(s);
+}
+
+static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+};
+
+static int strongarm_pic_initfn(SysBusDevice *dev)
+{
+ StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
+ iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
+ strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ return 0;
+}
+
+static int strongarm_pic_post_load(void *opaque, int version_id)
+{
+ strongarm_pic_update(opaque);
+ return 0;
+}
+
+static VMStateDescription vmstate_strongarm_pic_regs = {
+ .name = "strongarm_pic",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_pic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pending, StrongARMPICState),
+ VMSTATE_UINT32(enabled, StrongARMPICState),
+ VMSTATE_UINT32(is_fiq, StrongARMPICState),
+ VMSTATE_UINT32(int_idle, StrongARMPICState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_pic_info = {
+ .init = strongarm_pic_initfn,
+ .qdev.name = "strongarm_pic",
+ .qdev.desc = "StrongARM PIC",
+ .qdev.size = sizeof(StrongARMPICState),
+ .qdev.vmsd = &vmstate_strongarm_pic_regs,
+};
+
+/* Real-Time Clock */
+#define RTAR 0x00 /* RTC Alarm register */
+#define RCNR 0x04 /* RTC Counter register */
+#define RTTR 0x08 /* RTC Timer Trim register */
+#define RTSR 0x10 /* RTC Status register */
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t last_rcnr;
+ int64_t last_hz;
+ QEMUTimer *rtc_alarm;
+ QEMUTimer *rtc_hz;
+ qemu_irq rtc_irq;
+ qemu_irq rtc_hz_irq;
+} StrongARMRTCState;
+
+static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
+{
+ qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
+ qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
+}
+
+static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rt_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_hz);
+ }
+
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
+ qemu_mod_timer(s->rtc_alarm, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_alarm);
+ }
+}
+
+static inline void strongarm_rtc_alarm_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 0);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static inline void strongarm_rtc_hz_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 1);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RCNR:
+ return s->last_rcnr +
+ ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ strongarm_rtc_hzupdate(s);
+ s->rttr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RTSR:
+ if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
+ strongarm_rtc_timer_update(s, value);
+ }
+
+ s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
+ strongarm_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ strongarm_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+};
+
+static int strongarm_rtc_init(SysBusDevice *dev)
+{
+ StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
+ struct tm tm;
+ int iomemtype;
+
+ s->rttr = 0x0;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_hz = qemu_get_clock_ms(rt_clock);
+
+ s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
+ s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
+
+ sysbus_init_irq(dev, &s->rtc_irq);
+ sysbus_init_irq(dev, &s->rtc_hz_irq);
+
+ iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
+ strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+
+ return 0;
+}
+
+static void strongarm_rtc_pre_save(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_hzupdate(s);
+}
+
+static int strongarm_rtc_post_load(void *opaque, int version_id)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_timer_update(s, s->rtsr);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_rtc_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = strongarm_rtc_pre_save,
+ .post_load = strongarm_rtc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rttr, StrongARMRTCState),
+ VMSTATE_UINT32(rtsr, StrongARMRTCState),
+ VMSTATE_UINT32(rtar, StrongARMRTCState),
+ VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
+ VMSTATE_INT64(last_hz, StrongARMRTCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
+ .init = strongarm_rtc_init,
+ .qdev.name = "strongarm-rtc",
+ .qdev.desc = "StrongARM RTC Controller",
+ .qdev.size = sizeof(StrongARMRTCState),
+ .qdev.vmsd = &vmstate_strongarm_rtc_regs,
+};
+
+/* GPIO */
+#define GPLR 0x00
+#define GPDR 0x04
+#define GPSR 0x08
+#define GPCR 0x0c
+#define GRER 0x10
+#define GFER 0x14
+#define GEDR 0x18
+#define GAFR 0x1c
+
+typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
+struct StrongARMGPIOInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+ qemu_irq irqs[11];
+ qemu_irq irqX;
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t rising;
+ uint32_t falling;
+ uint32_t status;
+ uint32_t gpsr;
+ uint32_t gafr;
+
+ uint32_t prev_level;
+};
+
+
+static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
+{
+ int i;
+ for (i = 0; i < 11; i++) {
+ qemu_set_irq(s->irqs[i], s->status & (1 << i));
+ }
+
+ qemu_set_irq(s->irqX, (s->status & ~0x7ff));
+}
+
+static void strongarm_gpio_set(void *opaque, int line, int level)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+ uint32_t mask;
+
+ mask = 1 << line;
+
+ if (level) {
+ s->status |= s->rising & mask &
+ ~s->ilevel & ~s->dir;
+ s->ilevel |= mask;
+ } else {
+ s->status |= s->falling & mask &
+ s->ilevel & ~s->dir;
+ s->ilevel &= ~mask;
+ }
+
+ if (s->status & mask) {
+ strongarm_gpio_irq_update(s);
+ }
+}
+
+static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return s->gpsr; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ return s->gafr;
+
+ case GPLR: /* GPIO Pin-Level registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir);
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_gpio_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir = value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel |= value;
+ strongarm_gpio_handler_update(s);
+ s->gpsr = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel &= ~value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling = value;
+ break;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ s->gafr = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status &= ~value;
+ strongarm_gpio_irq_update(s);
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
+ strongarm_gpio_read,
+ strongarm_gpio_read,
+ strongarm_gpio_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
+ strongarm_gpio_write,
+ strongarm_gpio_write,
+ strongarm_gpio_write
+};
+
+static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
+ DeviceState *pic)
+{
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "strongarm-gpio");
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ for (i = 0; i < 12; i++)
+ sysbus_connect_irq(sysbus_from_qdev(dev), i,
+ qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
+
+ return dev;
+}
+
+static int strongarm_gpio_initfn(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMGPIOInfo *s;
+ int i;
+
+ s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 28);
+
+ iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
+ strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ for (i = 0; i < 11; i++) {
+ sysbus_init_irq(dev, &s->irqs[i]);
+ }
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_gpio_regs = {
+ .name = "strongarm-gpio",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(dir, StrongARMGPIOInfo),
+ VMSTATE_UINT32(rising, StrongARMGPIOInfo),
+ VMSTATE_UINT32(falling, StrongARMGPIOInfo),
+ VMSTATE_UINT32(status, StrongARMGPIOInfo),
+ VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_gpio_info = {
+ .init = strongarm_gpio_initfn,
+ .qdev.name = "strongarm-gpio",
+ .qdev.desc = "StrongARM GPIO controller",
+ .qdev.size = sizeof(StrongARMGPIOInfo),
+};
+
+/* Peripheral Pin Controller */
+#define PPDR 0x00
+#define PPSR 0x04
+#define PPAR 0x08
+#define PSDR 0x0c
+#define PPFR 0x10
+
+typedef struct StrongARMPPCInfo StrongARMPPCInfo;
+struct StrongARMPPCInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t ppar;
+ uint32_t psdr;
+ uint32_t ppfr;
+
+ uint32_t prev_level;
+};
+
+static void strongarm_ppc_set(void *opaque, int line, int level)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ if (level) {
+ s->ilevel |= 1 << line;
+ } else {
+ s->ilevel &= ~(1 << line);
+ }
+}
+
+static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ return s->dir | ~0x3fffff;
+
+ case PPSR: /* PPC Pin State registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir) |
+ ~0x3fffff;
+
+ case PPAR:
+ return s->ppar | ~0x41000;
+
+ case PSDR:
+ return s->psdr;
+
+ case PPFR:
+ return s->ppfr | ~0x7f001;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_ppc_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ s->dir = value & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPSR: /* PPC Pin State registers */
+ s->olevel = value & s->dir & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPAR:
+ s->ppar = value & 0x41000;
+ break;
+
+ case PSDR:
+ s->psdr = value & 0x3fffff;
+ break;
+
+ case PPFR:
+ s->ppfr = value & 0x7f001;
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
+ strongarm_ppc_read,
+ strongarm_ppc_read,
+ strongarm_ppc_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
+ strongarm_ppc_write,
+ strongarm_ppc_write,
+ strongarm_ppc_write
+};
+
+static int strongarm_ppc_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMPPCInfo *s;
+
+ s = FROM_SYSBUS(StrongARMPPCInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 22);
+
+ iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
+ strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_ppc_regs = {
+ .name = "strongarm-ppc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(olevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(dir, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppar, StrongARMPPCInfo),
+ VMSTATE_UINT32(psdr, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_ppc_info = {
+ .init = strongarm_ppc_init,
+ .qdev.name = "strongarm-ppc",
+ .qdev.desc = "StrongARM PPC controller",
+ .qdev.size = sizeof(StrongARMPPCInfo),
+};
+
+/* UART Ports */
+#define UTCR0 0x00
+#define UTCR1 0x04
+#define UTCR2 0x08
+#define UTCR3 0x0c
+#define UTDR 0x14
+#define UTSR0 0x1c
+#define UTSR1 0x20
+
+#define RX_FIFO_PRE (1 << 8)
+#define RX_FIFO_FRE (1 << 9)
+#define RX_FIFO_ROR (1 << 10)
+
+typedef struct {
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t utcr0;
+ uint16_t brd;
+ uint8_t utcr3;
+ uint8_t utsr0;
+ uint8_t utsr1;
+
+ uint8_t tx_fifo[8];
+ uint8_t tx_start;
+ uint8_t tx_len;
+ uint16_t rx_fifo[12]; /* value + error flags in high bits */
+ uint8_t rx_start;
+ uint8_t rx_len;
+
+ uint64_t char_transmit_time; /* time to transmit a char in ticks*/
+ bool wait_break_end;
+ QEMUTimer *rx_timeout_timer;
+ QEMUTimer *tx_timer;
+} StrongARMUARTState;
+
+static void strongarm_uart_update_status(StrongARMUARTState *s)
+{
+ uint16_t utsr1 = 0;
+
+ if (s->tx_len != 8) {
+ utsr1 |= (1 << 2);
+ }
+
+ if (s->rx_len != 0) {
+ uint16_t ent = s->rx_fifo[s->rx_start];
+
+ utsr1 |= (1 << 1);
+ if (ent & RX_FIFO_PRE) {
+ s->utsr1 |= (1 << 3);
+ }
+ if (ent & RX_FIFO_FRE) {
+ s->utsr1 |= (1 << 4);
+ }
+ if (ent & RX_FIFO_ROR) {
+ s->utsr1 |= (1 << 5);
+ }
+ }
+
+ s->utsr1 = utsr1;
+}
+
+static void strongarm_uart_update_int_status(StrongARMUARTState *s)
+{
+ uint16_t utsr0 = s->utsr0 & 0x1c;
+ int i;
+
+ if ((s->utcr3 & (1 << 1)) &&
+ (s->utcr3 & (1 << 4)) &&
+ s->tx_len <= 4) {
+ utsr0 |= (1 << 0);
+ }
+
+ if ((s->utcr3 & (1 << 0)) &&
+ (s->utcr3 & (1 << 3)) &&
+ s->rx_len > 4) {
+ utsr0 |= (1 << 1);
+ }
+
+ for (i = 0; i < s->rx_len && i < 4; i++)
+ if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
+ utsr0 |= (1 << 5);
+ break;
+ }
+
+ s->utsr0 = utsr0;
+ qemu_set_irq(s->irq, utsr0);
+}
+
+static void strongarm_uart_update_parameters(StrongARMUARTState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->utcr0 & (1 << 0)) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->utcr0 & (1 << 1)) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+ if (s->utcr0 & (1 << 2)) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
+ frame_size += data_bits + stop_bits;
+ speed = 3686400 / 16 / (s->brd + 1);
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ if (s->chr) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+ }
+
+#if 0
+ fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
+ speed, parity, data_bits, stop_bits);
+#endif
+}
+
+static void strongarm_uart_rx_to(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len) {
+ s->utsr0 |= (1 << 2);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
+{
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ /* rx disabled */
+ return;
+ }
+
+ if (s->wait_break_end) {
+ s->utsr0 |= (1 << 4); /* REB */
+ s->wait_break_end = false;
+ }
+
+ if (s->rx_len < 12) {
+ s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
+ s->rx_len++;
+ } else
+ s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
+}
+
+static int strongarm_uart_can_receive(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len == 12) {
+ return 0;
+ }
+ /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */
+ if (s->rx_len < 8) {
+ return 8 - s->rx_len;
+ }
+ return 1;
+}
+
+static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ StrongARMUARTState *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ strongarm_uart_rx_push(s, buf[i]);
+ }
+
+ /* call the timeout receive callback in 3 char transmit time */
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static void strongarm_uart_event(void *opaque, int event)
+{
+ StrongARMUARTState *s = opaque;
+ if (event == CHR_EVENT_BREAK) {
+ s->utcr3 |= (1 << 3);
+ strongarm_uart_rx_push(s, RX_FIFO_FRE);
+ s->wait_break_end = true;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_tx(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+ uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ if (s->utcr3 & (1 << 5)) /* loopback */ {
+ strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
+ } else if (s->chr) {
+ qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
+ }
+
+ s->tx_start = (s->tx_start + 1) % 8;
+ s->tx_len--;
+ if (s->tx_len) {
+ qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMUARTState *s = opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case UTCR0:
+ return s->utcr0;
+
+ case UTCR1:
+ return s->brd >> 8;
+
+ case UTCR2:
+ return s->brd & 0xff;
+
+ case UTCR3:
+ return s->utcr3;
+
+ case UTDR:
+ if (s->rx_len != 0) {
+ ret = s->rx_fifo[s->rx_start];
+ s->rx_start = (s->rx_start + 1) % 12;
+ s->rx_len--;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ return ret;
+ }
+ return 0;
+
+ case UTSR0:
+ return s->utsr0;
+
+ case UTSR1:
+ return s->utsr1;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMUARTState *s = opaque;
+
+ switch (addr) {
+ case UTCR0:
+ s->utcr0 = value & 0x7f;
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR1:
+ s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR2:
+ s->brd = (s->brd & 0xf00) | (value & 0xff);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR3:
+ s->utcr3 = value & 0x3f;
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ s->rx_len = 0;
+ }
+ if ((s->utcr3 & (1 << 1)) == 0) {
+ s->tx_len = 0;
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ case UTDR:
+ if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
+ s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
+ s->tx_len++;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ if (s->tx_len == 1) {
+ strongarm_uart_tx(s);
+ }
+ }
+ break;
+
+ case UTSR0:
+ s->utsr0 = s->utsr0 & ~(value & 0x1c);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
+ strongarm_uart_read,
+ strongarm_uart_read,
+ strongarm_uart_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
+ strongarm_uart_write,
+ strongarm_uart_write,
+ strongarm_uart_write,
+};
+
+static int strongarm_uart_init(SysBusDevice *dev)
+{
+ StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
+ strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s);
+ s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr,
+ strongarm_uart_can_receive,
+ strongarm_uart_receive,
+ strongarm_uart_event,
+ s);
+ }
+
+ return 0;
+}
+
+static void strongarm_uart_reset(DeviceState *dev)
+{
+ StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
+
+ s->utcr0 = 0x8; /* 8 data, no parity */
+ s->brd = 23; /* 9600 */
+ s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
+
+ s->rx_len = s->tx_len = 0;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static int strongarm_uart_post_load(void *opaque, int version_id)
+{
+ StrongARMUARTState *s = opaque;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+
+ /* tx and restart timer */
+ if (s->tx_len) {
+ strongarm_uart_tx(s);
+ }
+
+ /* restart rx timeout timer */
+ if (s->rx_len) {
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_uart_regs = {
+ .name = "strongarm-uart",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(utcr0, StrongARMUARTState),
+ VMSTATE_UINT16(brd, StrongARMUARTState),
+ VMSTATE_UINT8(utcr3, StrongARMUARTState),
+ VMSTATE_UINT8(utsr0, StrongARMUARTState),
+ VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
+ VMSTATE_UINT8(tx_start, StrongARMUARTState),
+ VMSTATE_UINT8(tx_len, StrongARMUARTState),
+ VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
+ VMSTATE_UINT8(rx_start, StrongARMUARTState),
+ VMSTATE_UINT8(rx_len, StrongARMUARTState),
+ VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_uart_info = {
+ .init = strongarm_uart_init,
+ .qdev.name = "strongarm-uart",
+ .qdev.desc = "StrongARM UART controller",
+ .qdev.size = sizeof(StrongARMUARTState),
+ .qdev.reset = strongarm_uart_reset,
+ .qdev.vmsd = &vmstate_strongarm_uart_regs,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+/* Synchronous Serial Ports */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ SSIBus *bus;
+
+ uint16_t sscr[2];
+ uint16_t sssr;
+
+ uint16_t rx_fifo[8];
+ uint8_t rx_level;
+ uint8_t rx_start;
+} StrongARMSSPState;
+
+#define SSCR0 0x60 /* SSP Control register 0 */
+#define SSCR1 0x64 /* SSP Control register 1 */
+#define SSDR 0x6c /* SSP Data register */
+#define SSSR 0x74 /* SSP Status register */
+
+/* Bitfields for above registers */
+#define SSCR0_SPI(x) (((x) & 0x30) == 0x00)
+#define SSCR0_SSP(x) (((x) & 0x30) == 0x10)
+#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20)
+#define SSCR0_PSP(x) (((x) & 0x30) == 0x30)
+#define SSCR0_SSE (1 << 7)
+#define SSCR0_DSS(x) (((x) & 0xf) + 1)
+#define SSCR1_RIE (1 << 0)
+#define SSCR1_TIE (1 << 1)
+#define SSCR1_LBM (1 << 2)
+#define SSSR_TNF (1 << 2)
+#define SSSR_RNE (1 << 3)
+#define SSSR_TFS (1 << 5)
+#define SSSR_RFS (1 << 6)
+#define SSSR_ROR (1 << 7)
+#define SSSR_RW 0x0080
+
+static void strongarm_ssp_int_update(StrongARMSSPState *s)
+{
+ int level = 0;
+
+ level |= (s->sssr & SSSR_ROR);
+ level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE);
+ level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE);
+ qemu_set_irq(s->irq, level);
+}
+
+static void strongarm_ssp_fifo_update(StrongARMSSPState *s)
+{
+ s->sssr &= ~SSSR_TFS;
+ s->sssr &= ~SSSR_TNF;
+ if (s->sscr[0] & SSCR0_SSE) {
+ if (s->rx_level >= 4)
+ s->sssr |= SSSR_RFS;
+ else
+ s->sssr &= ~SSSR_RFS;
+ if (s->rx_level)
+ s->sssr |= SSSR_RNE;
+ else
+ s->sssr &= ~SSSR_RNE;
+ /* TX FIFO is never filled, so it is always in underrun
+ condition if SSP is enabled */
+ s->sssr |= SSSR_TFS;
+ s->sssr |= SSSR_TNF;
+ }
+
+ strongarm_ssp_int_update(s);
+}
+
+static uint32_t strongarm_ssp_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMSSPState *s = (StrongARMSSPState *) opaque;
+ uint32_t retval;
+
+ switch (addr) {
+ case SSCR0:
+ return s->sscr[0];
+ case SSCR1:
+ return s->sscr[1];
+ case SSSR:
+ return s->sssr;
+ case SSDR:
+ if (~s->sscr[0] & SSCR0_SSE)
+ return 0xffffffff;
+ if (s->rx_level < 1) {
+ printf("%s: SSP Rx Underrun\n", __FUNCTION__);
+ return 0xffffffff;
+ }
+ s->rx_level --;
+ retval = s->rx_fifo[s->rx_start ++];
+ s->rx_start &= 0x7;
+ strongarm_ssp_fifo_update(s);
+ return retval;
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void strongarm_ssp_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMSSPState *s = (StrongARMSSPState *) opaque;
+
+ switch (addr) {
+ case SSCR0:
+ s->sscr[0] = value & 0xffbf;
+ if ((s->sscr[0] & SSCR0_SSE) && SSCR0_DSS(value) < 4)
+ printf("%s: Wrong data size: %i bits\n", __FUNCTION__,
+ SSCR0_DSS(value));
+ if (!(value & SSCR0_SSE)) {
+ s->sssr = 0;
+ s->rx_level = 0;
+ }
+ strongarm_ssp_fifo_update(s);
+ break;
+
+ case SSCR1:
+ s->sscr[1] = value & 0x2f;
+ if (value & SSCR1_LBM)
+ printf("%s: Attempt to use SSP LBM mode\n", __FUNCTION__);
+ strongarm_ssp_fifo_update(s);
+ break;
+
+ case SSSR:
+ s->sssr &= ~(value & SSSR_RW);
+ strongarm_ssp_int_update(s);
+ break;
+
+ case SSDR:
+ if (SSCR0_UWIRE(s->sscr[0])) {
+ value &= 0xff;
+ } else
+ /* Note how 32bits overflow does no harm here */
+ value &= (1 << SSCR0_DSS(s->sscr[0])) - 1;
+
+ /* Data goes from here to the Tx FIFO and is shifted out from
+ * there directly to the slave, no need to buffer it.
+ */
+ if (s->sscr[0] & SSCR0_SSE) {
+ uint32_t readval;
+ if (s->sscr[1] & SSCR1_LBM) {
+ readval = value;
+ } else {
+ readval = ssi_transfer(s->bus, value);
+ }
+
+ if (s->rx_level < 0x08) {
+ s->rx_fifo[(s->rx_start + s->rx_level ++) & 0x7] = readval;
+ } else {
+ s->sssr |= SSSR_ROR;
+ }
+ }
+ strongarm_ssp_fifo_update(s);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_ssp_readfn[] = {
+ strongarm_ssp_read,
+ strongarm_ssp_read,
+ strongarm_ssp_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_ssp_writefn[] = {
+ strongarm_ssp_write,
+ strongarm_ssp_write,
+ strongarm_ssp_write,
+};
+
+static int strongarm_ssp_post_load(void *opaque, int version_id)
+{
+ StrongARMSSPState *s = (StrongARMSSPState *) opaque;
+
+ strongarm_ssp_fifo_update(s);
+
+ return 0;
+}
+
+static int strongarm_ssp_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMSSPState *s = FROM_SYSBUS(StrongARMSSPState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ iomemtype = cpu_register_io_memory(strongarm_ssp_readfn,
+ strongarm_ssp_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ s->bus = ssi_create_bus(&dev->qdev, "ssi");
+ return 0;
+}
+
+static void strongarm_ssp_reset(DeviceState *dev)
+{
+ StrongARMSSPState *s = DO_UPCAST(StrongARMSSPState, busdev.qdev, dev);
+ s->sssr = 0x03; /* 3 bit data, SPI, disabled */
+ s->rx_start = 0;
+ s->rx_level = 0;
+}
+
+static const VMStateDescription vmstate_strongarm_ssp_regs = {
+ .name = "strongarm-ssp",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_ssp_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16_ARRAY(sscr, StrongARMSSPState, 2),
+ VMSTATE_UINT16(sssr, StrongARMSSPState),
+ VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMSSPState, 8),
+ VMSTATE_UINT8(rx_start, StrongARMSSPState),
+ VMSTATE_UINT8(rx_level, StrongARMSSPState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_ssp_info = {
+ .init = strongarm_ssp_init,
+ .qdev.name = "strongarm-ssp",
+ .qdev.desc = "StrongARM SSP controller",
+ .qdev.size = sizeof(StrongARMSSPState),
+ .qdev.reset = strongarm_ssp_reset,
+ .qdev.vmsd = &vmstate_strongarm_ssp_regs,
+};
+
+/* Main CPU functions */
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
+{
+ StrongARMState *s;
+ qemu_irq *pic;
+ int i;
+
+ s = qemu_mallocz(sizeof(StrongARMState));
+
+ if (!rev) {
+ rev = "sa1110-b5";
+ }
+
+ if (strncmp(rev, "sa1110", 6)) {
+ error_report("Machine requires a SA1110 processor.\n");
+ exit(1);
+ }
+
+ s->env = cpu_init(rev);
+
+ if (!s->env) {
+ error_report("Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ cpu_register_physical_memory(SA_SDCS0,
+ sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
+ sdram_size) | IO_MEM_RAM);
+
+ pic = arm_pic_init_cpu(s->env);
+ s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
+ pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
+
+ sysbus_create_varargs("pxa25x-timer", 0x90000000,
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
+ NULL);
+
+ sysbus_create_simple("strongarm-rtc", 0x90010000,
+ qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
+
+ s->gpio = strongarm_gpio_init(0x90040000, s->pic);
+
+ s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
+
+ for (i = 0; sa_serial[i].io_base; i++) {
+ DeviceState *dev = qdev_create(NULL, "strongarm-uart");
+ qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ sa_serial[i].io_base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0,
+ qdev_get_gpio_in(s->pic, sa_serial[i].irq));
+ }
+
+ s->ssp = sysbus_create_varargs("strongarm-ssp", 0x80070000,
+ qdev_get_gpio_in(s->pic, SA_PIC_SSP), NULL);
+ s->ssp_bus = (SSIBus *)qdev_get_child_bus(s->ssp, "ssi");
+
+ return s;
+}
+
+static void strongarm_register_devices(void)
+{
+ sysbus_register_withprop(&strongarm_pic_info);
+ sysbus_register_withprop(&strongarm_rtc_sysbus_info);
+ sysbus_register_withprop(&strongarm_gpio_info);
+ sysbus_register_withprop(&strongarm_ppc_info);
+ sysbus_register_withprop(&strongarm_uart_info);
+ sysbus_register_withprop(&strongarm_ssp_info);
+}
+device_init(strongarm_register_devices)
diff --git a/hw/strongarm.h b/hw/strongarm.h
new file mode 100644
index 0000000..a81b110
--- /dev/null
+++ b/hw/strongarm.h
@@ -0,0 +1,64 @@
+#ifndef _STRONGARM_H
+#define _STRONGARM_H
+
+#define SA_CS0 0x00000000
+#define SA_CS1 0x08000000
+#define SA_CS2 0x10000000
+#define SA_CS3 0x18000000
+#define SA_PCMCIA_CS0 0x20000000
+#define SA_PCMCIA_CS1 0x30000000
+#define SA_CS4 0x40000000
+#define SA_CS5 0x48000000
+/* system registers here */
+#define SA_SDCS0 0xc0000000
+#define SA_SDCS1 0xc8000000
+#define SA_SDCS2 0xd0000000
+#define SA_SDCS3 0xd8000000
+
+enum {
+ SA_PIC_GPIO0_EDGE = 0,
+ SA_PIC_GPIO1_EDGE,
+ SA_PIC_GPIO2_EDGE,
+ SA_PIC_GPIO3_EDGE,
+ SA_PIC_GPIO4_EDGE,
+ SA_PIC_GPIO5_EDGE,
+ SA_PIC_GPIO6_EDGE,
+ SA_PIC_GPIO7_EDGE,
+ SA_PIC_GPIO8_EDGE,
+ SA_PIC_GPIO9_EDGE,
+ SA_PIC_GPIO10_EDGE,
+ SA_PIC_GPIOX_EDGE,
+ SA_PIC_LCD,
+ SA_PIC_UDC,
+ SA_PIC_RSVD1,
+ SA_PIC_UART1,
+ SA_PIC_UART2,
+ SA_PIC_UART3,
+ SA_PIC_MCP,
+ SA_PIC_SSP,
+ SA_PIC_DMA_CH0,
+ SA_PIC_DMA_CH1,
+ SA_PIC_DMA_CH2,
+ SA_PIC_DMA_CH3,
+ SA_PIC_DMA_CH4,
+ SA_PIC_DMA_CH5,
+ SA_PIC_OSTC0,
+ SA_PIC_OSTC1,
+ SA_PIC_OSTC2,
+ SA_PIC_OSTC3,
+ SA_PIC_RTC_HZ,
+ SA_PIC_RTC_ALARM,
+};
+
+typedef struct {
+ CPUState *env;
+ DeviceState *pic;
+ DeviceState *gpio;
+ DeviceState *ppc;
+ DeviceState *ssp;
+ SSIBus *ssp_bus;
+} StrongARMState;
+
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
+
+#endif
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index e247a7a..d5af644 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -363,6 +363,7 @@ enum arm_features {
ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
ARM_FEATURE_V4T,
ARM_FEATURE_V5,
+ ARM_FEATURE_STRONGARM,
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
+#define ARM_CPUID_SA1100 0x4401A11B
+#define ARM_CPUID_SA1110 0x6901B119
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
diff --git a/target-arm/helper.c b/target-arm/helper.c
index ce9a9d8..5cb7b25 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -214,6 +214,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0xd172172;
env->cp15.c1_sys = 0x00000078;
break;
+ case ARM_CPUID_SA1100:
+ case ARM_CPUID_SA1110:
+ set_feature(env, ARM_FEATURE_STRONGARM);
+ env->cp15.c1_sys = 0x00000070;
+ break;
default:
cpu_abort(env, "Bad CPU ID: %x\n", id);
break;
@@ -374,6 +379,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
{ ARM_CPUID_CORTEXA9, "cortex-a9"},
{ ARM_CPUID_TI925T, "ti925t" },
{ ARM_CPUID_PXA250, "pxa250" },
+ { ARM_CPUID_SA1100, "sa1100" },
+ { ARM_CPUID_SA1110, "sa1110" },
{ ARM_CPUID_PXA255, "pxa255" },
{ ARM_CPUID_PXA260, "pxa260" },
{ ARM_CPUID_PXA261, "pxa261" },
@@ -1549,6 +1556,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 9:
if (arm_feature(env, ARM_FEATURE_OMAPCP))
break;
+ if (arm_feature(env, ARM_FEATURE_STRONGARM))
+ break; /* Ignore ReadBuffer access */
switch (crm) {
case 0: /* Cache lockdown. */
switch (op1) {
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
2011-04-04 13:38 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
@ 2011-04-10 11:40 ` Aurelien Jarno
0 siblings, 0 replies; 17+ messages in thread
From: Aurelien Jarno @ 2011-04-10 11:40 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: qemu-devel
On Mon, Apr 04, 2011 at 05:38:45PM +0400, Dmitry Eremin-Solenikov wrote:
> Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
> Implemented:
> - IRQs
> - GPIO
> - PPC
> - RTC
> - UARTs (no IrDA/etc.)
> - OST reused from pxa25x
>
> Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
> hw/strongarm.c
>
> V3:
> * fix the name of UART VMSD
> * fix RTSR reg offset
> * add SSP support
>
> V2:
> * removed all strongarm variants except latest
> * dropped unused casts
> * fixed PIC vmstate
> * fixed new devices created with version_id = 1
>
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> ---
> Makefile.target | 1 +
> hw/strongarm.c | 1539 +++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/strongarm.h | 64 +++
> target-arm/cpu.h | 3 +
> target-arm/helper.c | 9 +
> 5 files changed, 1616 insertions(+), 0 deletions(-)
> create mode 100644 hw/strongarm.c
> create mode 100644 hw/strongarm.h
>
> diff --git a/Makefile.target b/Makefile.target
> index 2f76714..cd03cec 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -352,6 +352,7 @@ obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
> obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
> obj-arm-y += syborg_virtio.o
> obj-arm-y += vexpress.o
> +obj-arm-y += strongarm.o
>
> obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
> obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
> diff --git a/hw/strongarm.c b/hw/strongarm.c
> new file mode 100644
> index 0000000..90b9ad0
> --- /dev/null
> +++ b/hw/strongarm.c
> @@ -0,0 +1,1539 @@
> +/*
> + * StrongARM SA-1100/SA-1110 emulation
> + *
> + * Copyright (C) 2011 Dmitry Eremin-Solenikov
> + *
> + * Largely based on StrongARM emulation:
> + * Copyright (c) 2006 Openedhand Ltd.
> + * Written by Andrzej Zaborowski <balrog@zabor.org>
> + *
> + * UART code based on QEMU 16550A UART emulation
> + * Copyright (c) 2003-2004 Fabrice Bellard
> + * Copyright (c) 2008 Citrix Systems, Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +#include "sysbus.h"
> +#include "strongarm.h"
> +#include "qemu-error.h"
> +#include "arm-misc.h"
> +#include "sysemu.h"
> +#include "ssi.h"
> +
> +/*
> + TODO
> + - Implement cp15, c14 ?
> + - Implement cp15, c15 !!! (idle used in L)
> + - Implement idle mode handling/DIM
> + - Implement sleep mode/Wake sources
> + - Implement reset control
> + - Implement memory control regs
> + - PCMCIA handling
> + - Maybe support MBGNT/MBREQ
> + - DMA channels
> + - GPCLK
> + - IrDA
> + - MCP
> + - Enhance UART with modem signals
> + */
> +
> +static struct {
> + target_phys_addr_t io_base;
> + int irq;
> +} sa_serial[] = {
> + { 0x80010000, SA_PIC_UART1 },
> + { 0x80030000, SA_PIC_UART2 },
> + { 0x80050000, SA_PIC_UART3 },
> + { 0, 0 }
> +};
> +
> +/* Interrupt Controller */
> +typedef struct {
> + SysBusDevice busdev;
> + qemu_irq irq;
> + qemu_irq fiq;
> +
> + uint32_t pending;
> + uint32_t enabled;
> + uint32_t is_fiq;
> + uint32_t int_idle;
> +} StrongARMPICState;
> +
> +#define ICIP 0x00
> +#define ICMR 0x04
> +#define ICLR 0x08
> +#define ICFP 0x10
> +#define ICPR 0x20
> +#define ICCR 0x0c
> +
> +#define SA_PIC_SRCS 32
> +
> +
> +static void strongarm_pic_update(void *opaque)
> +{
> + StrongARMPICState *s = opaque;
> +
> + /* FIXME: reflect DIM */
> + qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
> + qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
> +}
> +
> +static void strongarm_pic_set_irq(void *opaque, int irq, int level)
> +{
> + StrongARMPICState *s = opaque;
> +
> + if (level) {
> + s->pending |= 1 << irq;
> + } else {
> + s->pending &= ~(1 << irq);
> + }
> +
> + strongarm_pic_update(s);
> +}
> +
> +static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset)
> +{
> + StrongARMPICState *s = opaque;
> +
> + switch (offset) {
> + case ICIP:
> + return s->pending & ~s->is_fiq & s->enabled;
> + case ICMR:
> + return s->enabled;
> + case ICLR:
> + return s->is_fiq;
> + case ICCR:
> + return s->int_idle == 0;
> + case ICFP:
> + return s->pending & s->is_fiq & s->enabled;
> + case ICPR:
> + return s->pending;
> + default:
> + printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + return 0;
> + }
> +}
> +
> +static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
> + uint32_t value)
> +{
> + StrongARMPICState *s = opaque;
> +
> + switch (offset) {
> + case ICMR:
> + s->enabled = value;
> + break;
> + case ICLR:
> + s->is_fiq = value;
> + break;
> + case ICCR:
> + s->int_idle = (value & 1) ? 0 : ~0;
> + break;
> + default:
> + printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + break;
> + }
> + strongarm_pic_update(s);
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
> + strongarm_pic_mem_read,
> + strongarm_pic_mem_read,
> + strongarm_pic_mem_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
> + strongarm_pic_mem_write,
> + strongarm_pic_mem_write,
> + strongarm_pic_mem_write,
> +};
> +
> +static int strongarm_pic_initfn(SysBusDevice *dev)
> +{
> + StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
> + int iomemtype;
> +
> + qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
> + iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
> + strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> + sysbus_init_irq(dev, &s->irq);
> + sysbus_init_irq(dev, &s->fiq);
> +
> + return 0;
> +}
> +
> +static int strongarm_pic_post_load(void *opaque, int version_id)
> +{
> + strongarm_pic_update(opaque);
> + return 0;
> +}
> +
> +static VMStateDescription vmstate_strongarm_pic_regs = {
> + .name = "strongarm_pic",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .post_load = strongarm_pic_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(pending, StrongARMPICState),
> + VMSTATE_UINT32(enabled, StrongARMPICState),
> + VMSTATE_UINT32(is_fiq, StrongARMPICState),
> + VMSTATE_UINT32(int_idle, StrongARMPICState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_pic_info = {
> + .init = strongarm_pic_initfn,
> + .qdev.name = "strongarm_pic",
> + .qdev.desc = "StrongARM PIC",
> + .qdev.size = sizeof(StrongARMPICState),
> + .qdev.vmsd = &vmstate_strongarm_pic_regs,
> +};
> +
> +/* Real-Time Clock */
> +#define RTAR 0x00 /* RTC Alarm register */
> +#define RCNR 0x04 /* RTC Counter register */
> +#define RTTR 0x08 /* RTC Timer Trim register */
> +#define RTSR 0x10 /* RTC Status register */
> +
> +typedef struct {
> + SysBusDevice busdev;
> + uint32_t rttr;
> + uint32_t rtsr;
> + uint32_t rtar;
> + uint32_t last_rcnr;
> + int64_t last_hz;
> + QEMUTimer *rtc_alarm;
> + QEMUTimer *rtc_hz;
> + qemu_irq rtc_irq;
> + qemu_irq rtc_hz_irq;
> +} StrongARMRTCState;
> +
> +static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
> +{
> + qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
> + qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
> +}
> +
> +static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
> +{
> + int64_t rt = qemu_get_clock_ms(rt_clock);
> + s->last_rcnr += ((rt - s->last_hz) << 15) /
> + (1000 * ((s->rttr & 0xffff) + 1));
> + s->last_hz = rt;
> +}
> +
> +static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
> + uint32_t rtsr)
> +{
> + if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
> + qemu_mod_timer(s->rtc_hz, s->last_hz +
> + (((s->rtar - s->last_rcnr) * 1000 *
> + ((s->rttr & 0xffff) + 1)) >> 15));
This part (and other similar parts in the code) is not very clear unless
you have the documentation when reading the code. It would be better to
use #define to explain what the role of the various rtsr bits. Maybe the
same could be done for rttr with a mask.
> + } else {
> + qemu_del_timer(s->rtc_hz);
> + }
> +
> + if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
> + qemu_mod_timer(s->rtc_alarm, s->last_hz +
> + (((s->rtar - s->last_rcnr) * 1000 *
> + ((s->rttr & 0xffff) + 1)) >> 15));
> + } else {
> + qemu_del_timer(s->rtc_alarm);
> + }
> +}
> +
> +static inline void strongarm_rtc_alarm_tick(void *opaque)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
> + s->rtsr |= (1 << 0);
> + strongarm_rtc_timer_update(s, s->rtsr);
> + strongarm_rtc_int_update(s);
> +}
> +
> +static inline void strongarm_rtc_hz_tick(void *opaque)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
> + s->rtsr |= (1 << 1);
> + strongarm_rtc_timer_update(s, s->rtsr);
> + strongarm_rtc_int_update(s);
> +}
> +
> +static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
You don't need a cast there.
> +
> + switch (addr) {
> + case RTTR:
> + return s->rttr;
> + case RTSR:
> + return s->rtsr;
> + case RTAR:
> + return s->rtar;
> + case RCNR:
> + return s->last_rcnr +
> + ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
> + (1000 * ((s->rttr & 0xffff) + 1));
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + return 0;
> + }
> +}
> +
> +static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
Ditto
> +
> + switch (addr) {
> + case RTTR:
> + strongarm_rtc_hzupdate(s);
> + s->rttr = value;
> + strongarm_rtc_timer_update(s, s->rtsr);
> + break;
> +
> + case RTSR:
> + if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
> + strongarm_rtc_timer_update(s, value);
> + }
> +
> + s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
Same as previous, the code would be easier to read when knowing what is
0xc.
> + strongarm_rtc_int_update(s);
> + break;
> +
> + case RTAR:
> + s->rtar = value;
> + strongarm_rtc_timer_update(s, s->rtsr);
> + break;
> +
> + case RCNR:
> + strongarm_rtc_hzupdate(s);
> + s->last_rcnr = value;
> + strongarm_rtc_timer_update(s, s->rtsr);
> + break;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
> + strongarm_rtc_read,
> + strongarm_rtc_read,
> + strongarm_rtc_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
> + strongarm_rtc_write,
> + strongarm_rtc_write,
> + strongarm_rtc_write,
> +};
> +
> +static int strongarm_rtc_init(SysBusDevice *dev)
> +{
> + StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
> + struct tm tm;
> + int iomemtype;
> +
> + s->rttr = 0x0;
> + s->rtsr = 0;
> +
> + qemu_get_timedate(&tm, 0);
> +
> + s->last_rcnr = (uint32_t) mktimegm(&tm);
> + s->last_hz = qemu_get_clock_ms(rt_clock);
> +
> + s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
> + s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
> +
> + sysbus_init_irq(dev, &s->rtc_irq);
> + sysbus_init_irq(dev, &s->rtc_hz_irq);
> +
> + iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
> + strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x10000, iomemtype);
> +
> + return 0;
> +}
> +
> +static void strongarm_rtc_pre_save(void *opaque)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
You don't need the cast here.
> +
> + strongarm_rtc_hzupdate(s);
> +}
> +
> +static int strongarm_rtc_post_load(void *opaque, int version_id)
> +{
> + StrongARMRTCState *s = (StrongARMRTCState *) opaque;
Ditto.
> + strongarm_rtc_timer_update(s, s->rtsr);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_rtc_regs = {
> + .name = "strongarm-rtc",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .pre_save = strongarm_rtc_pre_save,
> + .post_load = strongarm_rtc_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(rttr, StrongARMRTCState),
> + VMSTATE_UINT32(rtsr, StrongARMRTCState),
> + VMSTATE_UINT32(rtar, StrongARMRTCState),
> + VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
> + VMSTATE_INT64(last_hz, StrongARMRTCState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
> + .init = strongarm_rtc_init,
> + .qdev.name = "strongarm-rtc",
> + .qdev.desc = "StrongARM RTC Controller",
> + .qdev.size = sizeof(StrongARMRTCState),
> + .qdev.vmsd = &vmstate_strongarm_rtc_regs,
> +};
> +
> +/* GPIO */
> +#define GPLR 0x00
> +#define GPDR 0x04
> +#define GPSR 0x08
> +#define GPCR 0x0c
> +#define GRER 0x10
> +#define GFER 0x14
> +#define GEDR 0x18
> +#define GAFR 0x1c
> +
> +typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
> +struct StrongARMGPIOInfo {
> + SysBusDevice busdev;
> + qemu_irq handler[28];
> + qemu_irq irqs[11];
> + qemu_irq irqX;
> +
> + uint32_t ilevel;
> + uint32_t olevel;
> + uint32_t dir;
> + uint32_t rising;
> + uint32_t falling;
> + uint32_t status;
> + uint32_t gpsr;
> + uint32_t gafr;
> +
> + uint32_t prev_level;
> +};
> +
> +
> +static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
> +{
> + int i;
> + for (i = 0; i < 11; i++) {
> + qemu_set_irq(s->irqs[i], s->status & (1 << i));
> + }
> +
> + qemu_set_irq(s->irqX, (s->status & ~0x7ff));
> +}
> +
> +static void strongarm_gpio_set(void *opaque, int line, int level)
> +{
> + StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
> + uint32_t mask;
> +
> + mask = 1 << line;
> +
> + if (level) {
> + s->status |= s->rising & mask &
> + ~s->ilevel & ~s->dir;
> + s->ilevel |= mask;
> + } else {
> + s->status |= s->falling & mask &
> + s->ilevel & ~s->dir;
> + s->ilevel &= ~mask;
> + }
> +
> + if (s->status & mask) {
> + strongarm_gpio_irq_update(s);
> + }
> +}
> +
> +static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
> +{
> + uint32_t level, diff;
> + int bit;
> +
> + level = s->olevel & s->dir;
> +
> + for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
> + bit = ffs(diff) - 1;
> + qemu_set_irq(s->handler[bit], (level >> bit) & 1);
> + }
> +
> + s->prev_level = level;
> +}
> +
> +static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
> +{
> + StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
> +
> + switch (offset) {
> + case GPDR: /* GPIO Pin-Direction registers */
> + return s->dir;
> +
> + case GPSR: /* GPIO Pin-Output Set registers */
> + printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + return s->gpsr; /* Return last written value. */
> +
> + case GPCR: /* GPIO Pin-Output Clear registers */
> + printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
> + __func__, offset);
> + return 31337; /* Specified as unpredictable in the docs. */
Do we really want all those printf() there? For debug maybe, but
probably not by default.
> +
> + case GRER: /* GPIO Rising-Edge Detect Enable registers */
> + return s->rising;
> +
> + case GFER: /* GPIO Falling-Edge Detect Enable registers */
> + return s->falling;
> +
> + case GAFR: /* GPIO Alternate Function registers */
> + return s->gafr;
> +
> + case GPLR: /* GPIO Pin-Level registers */
> + return (s->olevel & s->dir) |
> + (s->ilevel & ~s->dir);
> +
> + case GEDR: /* GPIO Edge Detect Status registers */
> + return s->status;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +
> + return 0;
> +}
> +
> +static void strongarm_gpio_write(void *opaque,
> + target_phys_addr_t offset, uint32_t value)
> +{
> + StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
> +
> + switch (offset) {
> + case GPDR: /* GPIO Pin-Direction registers */
> + s->dir = value;
> + strongarm_gpio_handler_update(s);
> + break;
> +
> + case GPSR: /* GPIO Pin-Output Set registers */
> + s->olevel |= value;
> + strongarm_gpio_handler_update(s);
> + s->gpsr = value;
> + break;
> +
> + case GPCR: /* GPIO Pin-Output Clear registers */
> + s->olevel &= ~value;
> + strongarm_gpio_handler_update(s);
> + break;
> +
> + case GRER: /* GPIO Rising-Edge Detect Enable registers */
> + s->rising = value;
> + break;
> +
> + case GFER: /* GPIO Falling-Edge Detect Enable registers */
> + s->falling = value;
> + break;
> +
> + case GAFR: /* GPIO Alternate Function registers */
> + s->gafr = value;
> + break;
> +
> + case GEDR: /* GPIO Edge Detect Status registers */
> + s->status &= ~value;
> + strongarm_gpio_irq_update(s);
> + break;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
> + strongarm_gpio_read,
> + strongarm_gpio_read,
> + strongarm_gpio_read
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
> + strongarm_gpio_write,
> + strongarm_gpio_write,
> + strongarm_gpio_write
> +};
> +
> +static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
> + DeviceState *pic)
> +{
> + DeviceState *dev;
> + int i;
> +
> + dev = qdev_create(NULL, "strongarm-gpio");
> + qdev_init_nofail(dev);
> +
> + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> + for (i = 0; i < 12; i++)
> + sysbus_connect_irq(sysbus_from_qdev(dev), i,
> + qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
> +
> + return dev;
> +}
> +
> +static int strongarm_gpio_initfn(SysBusDevice *dev)
> +{
> + int iomemtype;
> + StrongARMGPIOInfo *s;
> + int i;
> +
> + s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
> +
> + qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
> + qdev_init_gpio_out(&dev->qdev, s->handler, 28);
> +
> + iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
> + strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
> +
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> + for (i = 0; i < 11; i++) {
> + sysbus_init_irq(dev, &s->irqs[i]);
> + }
> + sysbus_init_irq(dev, &s->irqX);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_gpio_regs = {
> + .name = "strongarm-gpio",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
> + VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
> + VMSTATE_UINT32(dir, StrongARMGPIOInfo),
> + VMSTATE_UINT32(rising, StrongARMGPIOInfo),
> + VMSTATE_UINT32(falling, StrongARMGPIOInfo),
> + VMSTATE_UINT32(status, StrongARMGPIOInfo),
> + VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_gpio_info = {
> + .init = strongarm_gpio_initfn,
> + .qdev.name = "strongarm-gpio",
> + .qdev.desc = "StrongARM GPIO controller",
> + .qdev.size = sizeof(StrongARMGPIOInfo),
> +};
> +
> +/* Peripheral Pin Controller */
> +#define PPDR 0x00
> +#define PPSR 0x04
> +#define PPAR 0x08
> +#define PSDR 0x0c
> +#define PPFR 0x10
> +
> +typedef struct StrongARMPPCInfo StrongARMPPCInfo;
> +struct StrongARMPPCInfo {
> + SysBusDevice busdev;
> + qemu_irq handler[28];
> +
> + uint32_t ilevel;
> + uint32_t olevel;
> + uint32_t dir;
> + uint32_t ppar;
> + uint32_t psdr;
> + uint32_t ppfr;
> +
> + uint32_t prev_level;
> +};
> +
> +static void strongarm_ppc_set(void *opaque, int line, int level)
> +{
> + StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
> +
> + if (level) {
> + s->ilevel |= 1 << line;
> + } else {
> + s->ilevel &= ~(1 << line);
> + }
> +}
> +
> +static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
> +{
> + uint32_t level, diff;
> + int bit;
> +
> + level = s->olevel & s->dir;
> +
> + for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
> + bit = ffs(diff) - 1;
> + qemu_set_irq(s->handler[bit], (level >> bit) & 1);
> + }
> +
> + s->prev_level = level;
> +}
> +
> +static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
> +{
> + StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
> +
> + switch (offset) {
> + case PPDR: /* PPC Pin Direction registers */
> + return s->dir | ~0x3fffff;
> +
> + case PPSR: /* PPC Pin State registers */
> + return (s->olevel & s->dir) |
> + (s->ilevel & ~s->dir) |
> + ~0x3fffff;
> +
> + case PPAR:
> + return s->ppar | ~0x41000;
> +
> + case PSDR:
> + return s->psdr;
> +
> + case PPFR:
> + return s->ppfr | ~0x7f001;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +
> + return 0;
> +}
> +
> +static void strongarm_ppc_write(void *opaque,
> + target_phys_addr_t offset, uint32_t value)
> +{
> + StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
> +
> + switch (offset) {
> + case PPDR: /* PPC Pin Direction registers */
> + s->dir = value & 0x3fffff;
> + strongarm_ppc_handler_update(s);
> + break;
> +
> + case PPSR: /* PPC Pin State registers */
> + s->olevel = value & s->dir & 0x3fffff;
> + strongarm_ppc_handler_update(s);
> + break;
> +
> + case PPAR:
> + s->ppar = value & 0x41000;
> + break;
> +
> + case PSDR:
> + s->psdr = value & 0x3fffff;
> + break;
> +
> + case PPFR:
> + s->ppfr = value & 0x7f001;
> + break;
> +
> + default:
> + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
> + strongarm_ppc_read,
> + strongarm_ppc_read,
> + strongarm_ppc_read
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
> + strongarm_ppc_write,
> + strongarm_ppc_write,
> + strongarm_ppc_write
> +};
> +
> +static int strongarm_ppc_init(SysBusDevice *dev)
> +{
> + int iomemtype;
> + StrongARMPPCInfo *s;
> +
> + s = FROM_SYSBUS(StrongARMPPCInfo, dev);
> +
> + qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
> + qdev_init_gpio_out(&dev->qdev, s->handler, 22);
> +
> + iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
> + strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
> +
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_ppc_regs = {
> + .name = "strongarm-ppc",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
> + VMSTATE_UINT32(olevel, StrongARMPPCInfo),
> + VMSTATE_UINT32(dir, StrongARMPPCInfo),
> + VMSTATE_UINT32(ppar, StrongARMPPCInfo),
> + VMSTATE_UINT32(psdr, StrongARMPPCInfo),
> + VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_ppc_info = {
> + .init = strongarm_ppc_init,
> + .qdev.name = "strongarm-ppc",
> + .qdev.desc = "StrongARM PPC controller",
> + .qdev.size = sizeof(StrongARMPPCInfo),
> +};
> +
> +/* UART Ports */
> +#define UTCR0 0x00
> +#define UTCR1 0x04
> +#define UTCR2 0x08
> +#define UTCR3 0x0c
> +#define UTDR 0x14
> +#define UTSR0 0x1c
> +#define UTSR1 0x20
> +
> +#define RX_FIFO_PRE (1 << 8)
> +#define RX_FIFO_FRE (1 << 9)
> +#define RX_FIFO_ROR (1 << 10)
> +
> +typedef struct {
> + SysBusDevice busdev;
> + CharDriverState *chr;
> + qemu_irq irq;
> +
> + uint8_t utcr0;
> + uint16_t brd;
> + uint8_t utcr3;
> + uint8_t utsr0;
> + uint8_t utsr1;
> +
> + uint8_t tx_fifo[8];
> + uint8_t tx_start;
> + uint8_t tx_len;
> + uint16_t rx_fifo[12]; /* value + error flags in high bits */
> + uint8_t rx_start;
> + uint8_t rx_len;
> +
> + uint64_t char_transmit_time; /* time to transmit a char in ticks*/
> + bool wait_break_end;
> + QEMUTimer *rx_timeout_timer;
> + QEMUTimer *tx_timer;
> +} StrongARMUARTState;
> +
> +static void strongarm_uart_update_status(StrongARMUARTState *s)
> +{
> + uint16_t utsr1 = 0;
> +
> + if (s->tx_len != 8) {
> + utsr1 |= (1 << 2);
What bit is (1 << 2)? Please use #define to define the bits. Same for
most of the code below.
> + }
> +
> + if (s->rx_len != 0) {
> + uint16_t ent = s->rx_fifo[s->rx_start];
> +
> + utsr1 |= (1 << 1);
> + if (ent & RX_FIFO_PRE) {
> + s->utsr1 |= (1 << 3);
> + }
> + if (ent & RX_FIFO_FRE) {
> + s->utsr1 |= (1 << 4);
> + }
> + if (ent & RX_FIFO_ROR) {
> + s->utsr1 |= (1 << 5);
> + }
> + }
> +
> + s->utsr1 = utsr1;
> +}
> +
> +static void strongarm_uart_update_int_status(StrongARMUARTState *s)
> +{
> + uint16_t utsr0 = s->utsr0 & 0x1c;
> + int i;
> +
> + if ((s->utcr3 & (1 << 1)) &&
> + (s->utcr3 & (1 << 4)) &&
> + s->tx_len <= 4) {
> + utsr0 |= (1 << 0);
> + }
> +
> + if ((s->utcr3 & (1 << 0)) &&
> + (s->utcr3 & (1 << 3)) &&
> + s->rx_len > 4) {
> + utsr0 |= (1 << 1);
> + }
> +
> + for (i = 0; i < s->rx_len && i < 4; i++)
> + if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
> + utsr0 |= (1 << 5);
> + break;
> + }
> +
> + s->utsr0 = utsr0;
> + qemu_set_irq(s->irq, utsr0);
> +}
> +
> +static void strongarm_uart_update_parameters(StrongARMUARTState *s)
> +{
> + int speed, parity, data_bits, stop_bits, frame_size;
> + QEMUSerialSetParams ssp;
> +
> + /* Start bit. */
> + frame_size = 1;
> + if (s->utcr0 & (1 << 0)) {
> + /* Parity bit. */
> + frame_size++;
> + if (s->utcr0 & (1 << 1)) {
> + parity = 'E';
> + } else {
> + parity = 'O';
> + }
> + } else {
> + parity = 'N';
> + }
> + if (s->utcr0 & (1 << 2)) {
> + stop_bits = 2;
> + } else {
> + stop_bits = 1;
> + }
> +
> + data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
> + frame_size += data_bits + stop_bits;
> + speed = 3686400 / 16 / (s->brd + 1);
> + ssp.speed = speed;
> + ssp.parity = parity;
> + ssp.data_bits = data_bits;
> + ssp.stop_bits = stop_bits;
> + s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
> + if (s->chr) {
> + qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
> + }
> +
> +#if 0
> + fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
> + speed, parity, data_bits, stop_bits);
> +#endif
> +}
> +
> +static void strongarm_uart_rx_to(void *opaque)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + if (s->rx_len) {
> + s->utsr0 |= (1 << 2);
> + strongarm_uart_update_int_status(s);
> + }
> +}
> +
> +static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
> +{
> + if ((s->utcr3 & (1 << 0)) == 0) {
> + /* rx disabled */
> + return;
> + }
> +
> + if (s->wait_break_end) {
> + s->utsr0 |= (1 << 4); /* REB */
> + s->wait_break_end = false;
> + }
> +
> + if (s->rx_len < 12) {
> + s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
> + s->rx_len++;
> + } else
> + s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
> +}
> +
> +static int strongarm_uart_can_receive(void *opaque)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + if (s->rx_len == 12) {
> + return 0;
> + }
> + /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */
> + if (s->rx_len < 8) {
> + return 8 - s->rx_len;
> + }
> + return 1;
> +}
> +
> +static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size)
> +{
> + StrongARMUARTState *s = opaque;
> + int i;
> +
> + for (i = 0; i < size; i++) {
> + strongarm_uart_rx_push(s, buf[i]);
> + }
> +
> + /* call the timeout receive callback in 3 char transmit time */
> + qemu_mod_timer(s->rx_timeout_timer,
> + qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
> +
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +}
> +
> +static void strongarm_uart_event(void *opaque, int event)
> +{
> + StrongARMUARTState *s = opaque;
> + if (event == CHR_EVENT_BREAK) {
> + s->utcr3 |= (1 << 3);
> + strongarm_uart_rx_push(s, RX_FIFO_FRE);
> + s->wait_break_end = true;
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + }
> +}
> +
> +static void strongarm_uart_tx(void *opaque)
> +{
> + StrongARMUARTState *s = opaque;
> + uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
> +
> + if (s->utcr3 & (1 << 5)) /* loopback */ {
> + strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
> + } else if (s->chr) {
> + qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
> + }
> +
> + s->tx_start = (s->tx_start + 1) % 8;
> + s->tx_len--;
> + if (s->tx_len) {
> + qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
> + }
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +}
> +
> +static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
> +{
> + StrongARMUARTState *s = opaque;
> + uint16_t ret;
> +
> + switch (addr) {
> + case UTCR0:
> + return s->utcr0;
> +
> + case UTCR1:
> + return s->brd >> 8;
> +
> + case UTCR2:
> + return s->brd & 0xff;
> +
> + case UTCR3:
> + return s->utcr3;
> +
> + case UTDR:
> + if (s->rx_len != 0) {
> + ret = s->rx_fifo[s->rx_start];
> + s->rx_start = (s->rx_start + 1) % 12;
> + s->rx_len--;
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + return ret;
> + }
> + return 0;
> +
> + case UTSR0:
> + return s->utsr0;
> +
> + case UTSR1:
> + return s->utsr1;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + return 0;
> + }
> +}
> +
> +static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + switch (addr) {
> + case UTCR0:
> + s->utcr0 = value & 0x7f;
> + strongarm_uart_update_parameters(s);
> + break;
> +
> + case UTCR1:
> + s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
> + strongarm_uart_update_parameters(s);
> + break;
> +
> + case UTCR2:
> + s->brd = (s->brd & 0xf00) | (value & 0xff);
> + strongarm_uart_update_parameters(s);
> + break;
> +
> + case UTCR3:
> + s->utcr3 = value & 0x3f;
> + if ((s->utcr3 & (1 << 0)) == 0) {
> + s->rx_len = 0;
> + }
> + if ((s->utcr3 & (1 << 1)) == 0) {
> + s->tx_len = 0;
> + }
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + break;
> +
> + case UTDR:
> + if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
> + s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
> + s->tx_len++;
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> + if (s->tx_len == 1) {
> + strongarm_uart_tx(s);
> + }
> + }
> + break;
> +
> + case UTSR0:
> + s->utsr0 = s->utsr0 & ~(value & 0x1c);
> + strongarm_uart_update_int_status(s);
> + break;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
> + strongarm_uart_read,
> + strongarm_uart_read,
> + strongarm_uart_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
> + strongarm_uart_write,
> + strongarm_uart_write,
> + strongarm_uart_write,
> +};
> +
> +static int strongarm_uart_init(SysBusDevice *dev)
> +{
> + StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
> + int iomemtype;
> +
> + iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
> + strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x10000, iomemtype);
> + sysbus_init_irq(dev, &s->irq);
> +
> + s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s);
> + s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
> +
> + if (s->chr) {
> + qemu_chr_add_handlers(s->chr,
> + strongarm_uart_can_receive,
> + strongarm_uart_receive,
> + strongarm_uart_event,
> + s);
> + }
> +
> + return 0;
> +}
> +
> +static void strongarm_uart_reset(DeviceState *dev)
> +{
> + StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
> +
> + s->utcr0 = 0x8; /* 8 data, no parity */
> + s->brd = 23; /* 9600 */
> + s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
> +
> + s->rx_len = s->tx_len = 0;
> +
> + strongarm_uart_update_parameters(s);
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +}
> +
> +static int strongarm_uart_post_load(void *opaque, int version_id)
> +{
> + StrongARMUARTState *s = opaque;
> +
> + strongarm_uart_update_parameters(s);
> + strongarm_uart_update_status(s);
> + strongarm_uart_update_int_status(s);
> +
> + /* tx and restart timer */
> + if (s->tx_len) {
> + strongarm_uart_tx(s);
> + }
> +
> + /* restart rx timeout timer */
> + if (s->rx_len) {
> + qemu_mod_timer(s->rx_timeout_timer,
> + qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
> + }
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_uart_regs = {
> + .name = "strongarm-uart",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .post_load = strongarm_uart_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT8(utcr0, StrongARMUARTState),
> + VMSTATE_UINT16(brd, StrongARMUARTState),
> + VMSTATE_UINT8(utcr3, StrongARMUARTState),
> + VMSTATE_UINT8(utsr0, StrongARMUARTState),
> + VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
> + VMSTATE_UINT8(tx_start, StrongARMUARTState),
> + VMSTATE_UINT8(tx_len, StrongARMUARTState),
> + VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
> + VMSTATE_UINT8(rx_start, StrongARMUARTState),
> + VMSTATE_UINT8(rx_len, StrongARMUARTState),
> + VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_uart_info = {
> + .init = strongarm_uart_init,
> + .qdev.name = "strongarm-uart",
> + .qdev.desc = "StrongARM UART controller",
> + .qdev.size = sizeof(StrongARMUARTState),
> + .qdev.reset = strongarm_uart_reset,
> + .qdev.vmsd = &vmstate_strongarm_uart_regs,
> + .qdev.props = (Property[]) {
> + DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
> + DEFINE_PROP_END_OF_LIST(),
> + }
> +};
> +
> +/* Synchronous Serial Ports */
> +typedef struct {
> + SysBusDevice busdev;
> + qemu_irq irq;
> + SSIBus *bus;
> +
> + uint16_t sscr[2];
> + uint16_t sssr;
> +
> + uint16_t rx_fifo[8];
> + uint8_t rx_level;
> + uint8_t rx_start;
> +} StrongARMSSPState;
> +
> +#define SSCR0 0x60 /* SSP Control register 0 */
> +#define SSCR1 0x64 /* SSP Control register 1 */
> +#define SSDR 0x6c /* SSP Data register */
> +#define SSSR 0x74 /* SSP Status register */
> +
> +/* Bitfields for above registers */
> +#define SSCR0_SPI(x) (((x) & 0x30) == 0x00)
> +#define SSCR0_SSP(x) (((x) & 0x30) == 0x10)
> +#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20)
> +#define SSCR0_PSP(x) (((x) & 0x30) == 0x30)
> +#define SSCR0_SSE (1 << 7)
> +#define SSCR0_DSS(x) (((x) & 0xf) + 1)
> +#define SSCR1_RIE (1 << 0)
> +#define SSCR1_TIE (1 << 1)
> +#define SSCR1_LBM (1 << 2)
> +#define SSSR_TNF (1 << 2)
> +#define SSSR_RNE (1 << 3)
> +#define SSSR_TFS (1 << 5)
> +#define SSSR_RFS (1 << 6)
> +#define SSSR_ROR (1 << 7)
> +#define SSSR_RW 0x0080
> +
> +static void strongarm_ssp_int_update(StrongARMSSPState *s)
> +{
> + int level = 0;
> +
> + level |= (s->sssr & SSSR_ROR);
> + level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE);
> + level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE);
> + qemu_set_irq(s->irq, level);
> +}
> +
> +static void strongarm_ssp_fifo_update(StrongARMSSPState *s)
> +{
> + s->sssr &= ~SSSR_TFS;
> + s->sssr &= ~SSSR_TNF;
> + if (s->sscr[0] & SSCR0_SSE) {
> + if (s->rx_level >= 4)
> + s->sssr |= SSSR_RFS;
> + else
> + s->sssr &= ~SSSR_RFS;
> + if (s->rx_level)
> + s->sssr |= SSSR_RNE;
> + else
> + s->sssr &= ~SSSR_RNE;
> + /* TX FIFO is never filled, so it is always in underrun
> + condition if SSP is enabled */
> + s->sssr |= SSSR_TFS;
> + s->sssr |= SSSR_TNF;
> + }
> +
> + strongarm_ssp_int_update(s);
> +}
> +
> +static uint32_t strongarm_ssp_read(void *opaque, target_phys_addr_t addr)
> +{
> + StrongARMSSPState *s = (StrongARMSSPState *) opaque;
> + uint32_t retval;
> +
> + switch (addr) {
> + case SSCR0:
> + return s->sscr[0];
> + case SSCR1:
> + return s->sscr[1];
> + case SSSR:
> + return s->sssr;
> + case SSDR:
> + if (~s->sscr[0] & SSCR0_SSE)
> + return 0xffffffff;
> + if (s->rx_level < 1) {
> + printf("%s: SSP Rx Underrun\n", __FUNCTION__);
> + return 0xffffffff;
> + }
> + s->rx_level --;
> + retval = s->rx_fifo[s->rx_start ++];
> + s->rx_start &= 0x7;
> + strongarm_ssp_fifo_update(s);
> + return retval;
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__, addr);
> + break;
> + }
> + return 0;
> +}
> +
> +static void strongarm_ssp_write(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + StrongARMSSPState *s = (StrongARMSSPState *) opaque;
> +
> + switch (addr) {
> + case SSCR0:
> + s->sscr[0] = value & 0xffbf;
> + if ((s->sscr[0] & SSCR0_SSE) && SSCR0_DSS(value) < 4)
> + printf("%s: Wrong data size: %i bits\n", __FUNCTION__,
> + SSCR0_DSS(value));
> + if (!(value & SSCR0_SSE)) {
> + s->sssr = 0;
> + s->rx_level = 0;
> + }
> + strongarm_ssp_fifo_update(s);
> + break;
> +
> + case SSCR1:
> + s->sscr[1] = value & 0x2f;
> + if (value & SSCR1_LBM)
> + printf("%s: Attempt to use SSP LBM mode\n", __FUNCTION__);
> + strongarm_ssp_fifo_update(s);
> + break;
> +
> + case SSSR:
> + s->sssr &= ~(value & SSSR_RW);
> + strongarm_ssp_int_update(s);
> + break;
> +
> + case SSDR:
> + if (SSCR0_UWIRE(s->sscr[0])) {
> + value &= 0xff;
> + } else
> + /* Note how 32bits overflow does no harm here */
> + value &= (1 << SSCR0_DSS(s->sscr[0])) - 1;
> +
> + /* Data goes from here to the Tx FIFO and is shifted out from
> + * there directly to the slave, no need to buffer it.
> + */
> + if (s->sscr[0] & SSCR0_SSE) {
> + uint32_t readval;
> + if (s->sscr[1] & SSCR1_LBM) {
> + readval = value;
> + } else {
> + readval = ssi_transfer(s->bus, value);
> + }
> +
> + if (s->rx_level < 0x08) {
> + s->rx_fifo[(s->rx_start + s->rx_level ++) & 0x7] = readval;
> + } else {
> + s->sssr |= SSSR_ROR;
> + }
> + }
> + strongarm_ssp_fifo_update(s);
> + break;
> +
> + default:
> + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__, addr);
> + break;
> + }
> +}
> +
> +static CPUReadMemoryFunc * const strongarm_ssp_readfn[] = {
> + strongarm_ssp_read,
> + strongarm_ssp_read,
> + strongarm_ssp_read,
> +};
> +
> +static CPUWriteMemoryFunc * const strongarm_ssp_writefn[] = {
> + strongarm_ssp_write,
> + strongarm_ssp_write,
> + strongarm_ssp_write,
> +};
> +
> +static int strongarm_ssp_post_load(void *opaque, int version_id)
> +{
> + StrongARMSSPState *s = (StrongARMSSPState *) opaque;
> +
> + strongarm_ssp_fifo_update(s);
> +
> + return 0;
> +}
> +
> +static int strongarm_ssp_init(SysBusDevice *dev)
> +{
> + int iomemtype;
> + StrongARMSSPState *s = FROM_SYSBUS(StrongARMSSPState, dev);
> +
> + sysbus_init_irq(dev, &s->irq);
> +
> + iomemtype = cpu_register_io_memory(strongarm_ssp_readfn,
> + strongarm_ssp_writefn, s,
> + DEVICE_NATIVE_ENDIAN);
> + sysbus_init_mmio(dev, 0x1000, iomemtype);
> +
> + s->bus = ssi_create_bus(&dev->qdev, "ssi");
> + return 0;
> +}
> +
> +static void strongarm_ssp_reset(DeviceState *dev)
> +{
> + StrongARMSSPState *s = DO_UPCAST(StrongARMSSPState, busdev.qdev, dev);
> + s->sssr = 0x03; /* 3 bit data, SPI, disabled */
> + s->rx_start = 0;
> + s->rx_level = 0;
> +}
> +
> +static const VMStateDescription vmstate_strongarm_ssp_regs = {
> + .name = "strongarm-ssp",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .minimum_version_id_old = 0,
> + .post_load = strongarm_ssp_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT16_ARRAY(sscr, StrongARMSSPState, 2),
> + VMSTATE_UINT16(sssr, StrongARMSSPState),
> + VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMSSPState, 8),
> + VMSTATE_UINT8(rx_start, StrongARMSSPState),
> + VMSTATE_UINT8(rx_level, StrongARMSSPState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static SysBusDeviceInfo strongarm_ssp_info = {
> + .init = strongarm_ssp_init,
> + .qdev.name = "strongarm-ssp",
> + .qdev.desc = "StrongARM SSP controller",
> + .qdev.size = sizeof(StrongARMSSPState),
> + .qdev.reset = strongarm_ssp_reset,
> + .qdev.vmsd = &vmstate_strongarm_ssp_regs,
> +};
> +
> +/* Main CPU functions */
> +StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
> +{
> + StrongARMState *s;
> + qemu_irq *pic;
> + int i;
> +
> + s = qemu_mallocz(sizeof(StrongARMState));
> +
> + if (!rev) {
> + rev = "sa1110-b5";
> + }
> +
> + if (strncmp(rev, "sa1110", 6)) {
> + error_report("Machine requires a SA1110 processor.\n");
> + exit(1);
> + }
> +
> + s->env = cpu_init(rev);
> +
> + if (!s->env) {
> + error_report("Unable to find CPU definition\n");
> + exit(1);
> + }
> +
> + cpu_register_physical_memory(SA_SDCS0,
> + sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
> + sdram_size) | IO_MEM_RAM);
> +
> + pic = arm_pic_init_cpu(s->env);
> + s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
> + pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
> +
> + sysbus_create_varargs("pxa25x-timer", 0x90000000,
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
> + qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
> + NULL);
> +
> + sysbus_create_simple("strongarm-rtc", 0x90010000,
> + qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
> +
> + s->gpio = strongarm_gpio_init(0x90040000, s->pic);
> +
> + s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
> +
> + for (i = 0; sa_serial[i].io_base; i++) {
> + DeviceState *dev = qdev_create(NULL, "strongarm-uart");
> + qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
> + qdev_init_nofail(dev);
> + sysbus_mmio_map(sysbus_from_qdev(dev), 0,
> + sa_serial[i].io_base);
> + sysbus_connect_irq(sysbus_from_qdev(dev), 0,
> + qdev_get_gpio_in(s->pic, sa_serial[i].irq));
> + }
> +
> + s->ssp = sysbus_create_varargs("strongarm-ssp", 0x80070000,
> + qdev_get_gpio_in(s->pic, SA_PIC_SSP), NULL);
> + s->ssp_bus = (SSIBus *)qdev_get_child_bus(s->ssp, "ssi");
> +
> + return s;
> +}
> +
> +static void strongarm_register_devices(void)
> +{
> + sysbus_register_withprop(&strongarm_pic_info);
> + sysbus_register_withprop(&strongarm_rtc_sysbus_info);
> + sysbus_register_withprop(&strongarm_gpio_info);
> + sysbus_register_withprop(&strongarm_ppc_info);
> + sysbus_register_withprop(&strongarm_uart_info);
> + sysbus_register_withprop(&strongarm_ssp_info);
> +}
> +device_init(strongarm_register_devices)
> diff --git a/hw/strongarm.h b/hw/strongarm.h
> new file mode 100644
> index 0000000..a81b110
> --- /dev/null
> +++ b/hw/strongarm.h
> @@ -0,0 +1,64 @@
> +#ifndef _STRONGARM_H
> +#define _STRONGARM_H
> +
> +#define SA_CS0 0x00000000
> +#define SA_CS1 0x08000000
> +#define SA_CS2 0x10000000
> +#define SA_CS3 0x18000000
> +#define SA_PCMCIA_CS0 0x20000000
> +#define SA_PCMCIA_CS1 0x30000000
> +#define SA_CS4 0x40000000
> +#define SA_CS5 0x48000000
> +/* system registers here */
> +#define SA_SDCS0 0xc0000000
> +#define SA_SDCS1 0xc8000000
> +#define SA_SDCS2 0xd0000000
> +#define SA_SDCS3 0xd8000000
> +
> +enum {
> + SA_PIC_GPIO0_EDGE = 0,
> + SA_PIC_GPIO1_EDGE,
> + SA_PIC_GPIO2_EDGE,
> + SA_PIC_GPIO3_EDGE,
> + SA_PIC_GPIO4_EDGE,
> + SA_PIC_GPIO5_EDGE,
> + SA_PIC_GPIO6_EDGE,
> + SA_PIC_GPIO7_EDGE,
> + SA_PIC_GPIO8_EDGE,
> + SA_PIC_GPIO9_EDGE,
> + SA_PIC_GPIO10_EDGE,
> + SA_PIC_GPIOX_EDGE,
> + SA_PIC_LCD,
> + SA_PIC_UDC,
> + SA_PIC_RSVD1,
> + SA_PIC_UART1,
> + SA_PIC_UART2,
> + SA_PIC_UART3,
> + SA_PIC_MCP,
> + SA_PIC_SSP,
> + SA_PIC_DMA_CH0,
> + SA_PIC_DMA_CH1,
> + SA_PIC_DMA_CH2,
> + SA_PIC_DMA_CH3,
> + SA_PIC_DMA_CH4,
> + SA_PIC_DMA_CH5,
> + SA_PIC_OSTC0,
> + SA_PIC_OSTC1,
> + SA_PIC_OSTC2,
> + SA_PIC_OSTC3,
> + SA_PIC_RTC_HZ,
> + SA_PIC_RTC_ALARM,
> +};
> +
> +typedef struct {
> + CPUState *env;
> + DeviceState *pic;
> + DeviceState *gpio;
> + DeviceState *ppc;
> + DeviceState *ssp;
> + SSIBus *ssp_bus;
> +} StrongARMState;
> +
> +StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
> +
> +#endif
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index e247a7a..d5af644 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -363,6 +363,7 @@ enum arm_features {
> ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
> ARM_FEATURE_V4T,
> ARM_FEATURE_V5,
> + ARM_FEATURE_STRONGARM,
> };
>
> static inline int arm_feature(CPUARMState *env, int feature)
> @@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
> #define ARM_CPUID_ARM946 0x41059461
> #define ARM_CPUID_TI915T 0x54029152
> #define ARM_CPUID_TI925T 0x54029252
> +#define ARM_CPUID_SA1100 0x4401A11B
> +#define ARM_CPUID_SA1110 0x6901B119
> #define ARM_CPUID_PXA250 0x69052100
> #define ARM_CPUID_PXA255 0x69052d00
> #define ARM_CPUID_PXA260 0x69052903
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index ce9a9d8..5cb7b25 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -214,6 +214,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
> env->cp15.c0_cachetype = 0xd172172;
> env->cp15.c1_sys = 0x00000078;
> break;
> + case ARM_CPUID_SA1100:
> + case ARM_CPUID_SA1110:
> + set_feature(env, ARM_FEATURE_STRONGARM);
> + env->cp15.c1_sys = 0x00000070;
> + break;
> default:
> cpu_abort(env, "Bad CPU ID: %x\n", id);
> break;
> @@ -374,6 +379,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
> { ARM_CPUID_CORTEXA9, "cortex-a9"},
> { ARM_CPUID_TI925T, "ti925t" },
> { ARM_CPUID_PXA250, "pxa250" },
> + { ARM_CPUID_SA1100, "sa1100" },
> + { ARM_CPUID_SA1110, "sa1110" },
> { ARM_CPUID_PXA255, "pxa255" },
> { ARM_CPUID_PXA260, "pxa260" },
> { ARM_CPUID_PXA261, "pxa261" },
> @@ -1549,6 +1556,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
> case 9:
> if (arm_feature(env, ARM_FEATURE_OMAPCP))
> break;
> + if (arm_feature(env, ARM_FEATURE_STRONGARM))
> + break; /* Ignore ReadBuffer access */
> switch (crm) {
> case 0: /* Cache lockdown. */
> switch (op1) {
> --
> 1.7.4.1
>
>
>
--
Aurelien Jarno GPG: 1024D/F1BCDB73
aurelien@aurel32.net http://www.aurel32.net
^ permalink raw reply [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 3/3] Basic implementation of Sharp Zaurus SL-5500 collie PDA
2011-04-04 13:38 [Qemu-devel] [PATCH 0/3] StrongARM/collie support Dmitry Eremin-Solenikov
2011-04-04 13:38 ` [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-04-04 13:38 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
@ 2011-04-04 13:38 ` Dmitry Eremin-Solenikov
2011-04-10 11:41 ` Aurelien Jarno
2 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-04 13:38 UTC (permalink / raw)
To: qemu-devel
Add very basic implementation of collie PDA emulation. The system lacks
LoCoMo and graphics/sound emulation. Linux kernel boots up to mounting
rootfs (theoretically it can be provided in pflash images).
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
Makefile.target | 1 +
hw/collie.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+), 0 deletions(-)
create mode 100644 hw/collie.c
diff --git a/Makefile.target b/Makefile.target
index cd03cec..47fba98 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -353,6 +353,7 @@ obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
obj-arm-y += vexpress.o
obj-arm-y += strongarm.o
+obj-arm-y += collie.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/collie.c b/hw/collie.c
new file mode 100644
index 0000000..156404d
--- /dev/null
+++ b/hw/collie.c
@@ -0,0 +1,69 @@
+/*
+ * SA-1110-based Sharp Zaurus SL-5500 platform.
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * This code is licensed under GNU GPL v2.
+ */
+#include "hw.h"
+#include "sysbus.h"
+#include "boards.h"
+#include "devices.h"
+#include "strongarm.h"
+#include "arm-misc.h"
+#include "flash.h"
+#include "blockdev.h"
+
+static struct arm_boot_info collie_binfo = {
+ .loader_start = SA_SDCS0,
+ .ram_size = 0x20000000,
+};
+
+static void collie_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ StrongARMState *s;
+ DriveInfo *dinfo;
+ ram_addr_t phys_flash;
+
+ if (!cpu_model) {
+ cpu_model = "sa1110";
+ }
+
+ s = sa1110_init(collie_binfo.ram_size, cpu_model);
+
+ phys_flash = qemu_ram_alloc(NULL, "collie.fl1", 0x02000000);
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi01_register(SA_CS0, phys_flash,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
+
+ phys_flash = qemu_ram_alloc(NULL, "collie.fl2", 0x02000000);
+ dinfo = drive_get(IF_PFLASH, 0, 1);
+ pflash_cfi01_register(SA_CS1, phys_flash,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
+
+ sysbus_create_simple("scoop", 0x40800000, NULL);
+
+ collie_binfo.kernel_filename = kernel_filename;
+ collie_binfo.kernel_cmdline = kernel_cmdline;
+ collie_binfo.initrd_filename = initrd_filename;
+ collie_binfo.board_id = 0x208;
+ arm_load_kernel(s->env, &collie_binfo);
+}
+
+static QEMUMachine collie_machine = {
+ .name = "collie",
+ .desc = "Collie PDA (SA-1110)",
+ .init = collie_init,
+};
+
+static void collie_machine_init(void)
+{
+ qemu_register_machine(&collie_machine);
+}
+
+machine_init(collie_machine_init)
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [Qemu-devel] [PATCH 3/3] Basic implementation of Sharp Zaurus SL-5500 collie PDA
2011-04-04 13:38 ` [Qemu-devel] [PATCH 3/3] Basic implementation of Sharp Zaurus SL-5500 collie PDA Dmitry Eremin-Solenikov
@ 2011-04-10 11:41 ` Aurelien Jarno
0 siblings, 0 replies; 17+ messages in thread
From: Aurelien Jarno @ 2011-04-10 11:41 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: qemu-devel
On Mon, Apr 04, 2011 at 05:38:46PM +0400, Dmitry Eremin-Solenikov wrote:
> Add very basic implementation of collie PDA emulation. The system lacks
> LoCoMo and graphics/sound emulation. Linux kernel boots up to mounting
> rootfs (theoretically it can be provided in pflash images).
>
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> ---
> Makefile.target | 1 +
> hw/collie.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 70 insertions(+), 0 deletions(-)
> create mode 100644 hw/collie.c
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
> diff --git a/Makefile.target b/Makefile.target
> index cd03cec..47fba98 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -353,6 +353,7 @@ obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
> obj-arm-y += syborg_virtio.o
> obj-arm-y += vexpress.o
> obj-arm-y += strongarm.o
> +obj-arm-y += collie.o
>
> obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
> obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
> diff --git a/hw/collie.c b/hw/collie.c
> new file mode 100644
> index 0000000..156404d
> --- /dev/null
> +++ b/hw/collie.c
> @@ -0,0 +1,69 @@
> +/*
> + * SA-1110-based Sharp Zaurus SL-5500 platform.
> + *
> + * Copyright (C) 2011 Dmitry Eremin-Solenikov
> + *
> + * This code is licensed under GNU GPL v2.
> + */
> +#include "hw.h"
> +#include "sysbus.h"
> +#include "boards.h"
> +#include "devices.h"
> +#include "strongarm.h"
> +#include "arm-misc.h"
> +#include "flash.h"
> +#include "blockdev.h"
> +
> +static struct arm_boot_info collie_binfo = {
> + .loader_start = SA_SDCS0,
> + .ram_size = 0x20000000,
> +};
> +
> +static void collie_init(ram_addr_t ram_size,
> + const char *boot_device,
> + const char *kernel_filename, const char *kernel_cmdline,
> + const char *initrd_filename, const char *cpu_model)
> +{
> + StrongARMState *s;
> + DriveInfo *dinfo;
> + ram_addr_t phys_flash;
> +
> + if (!cpu_model) {
> + cpu_model = "sa1110";
> + }
> +
> + s = sa1110_init(collie_binfo.ram_size, cpu_model);
> +
> + phys_flash = qemu_ram_alloc(NULL, "collie.fl1", 0x02000000);
> + dinfo = drive_get(IF_PFLASH, 0, 0);
> + pflash_cfi01_register(SA_CS0, phys_flash,
> + dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> + 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
> +
> + phys_flash = qemu_ram_alloc(NULL, "collie.fl2", 0x02000000);
> + dinfo = drive_get(IF_PFLASH, 0, 1);
> + pflash_cfi01_register(SA_CS1, phys_flash,
> + dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> + 512, 4, 0x00, 0x00, 0x00, 0x00, 0);
> +
> + sysbus_create_simple("scoop", 0x40800000, NULL);
> +
> + collie_binfo.kernel_filename = kernel_filename;
> + collie_binfo.kernel_cmdline = kernel_cmdline;
> + collie_binfo.initrd_filename = initrd_filename;
> + collie_binfo.board_id = 0x208;
> + arm_load_kernel(s->env, &collie_binfo);
> +}
> +
> +static QEMUMachine collie_machine = {
> + .name = "collie",
> + .desc = "Collie PDA (SA-1110)",
> + .init = collie_init,
> +};
> +
> +static void collie_machine_init(void)
> +{
> + qemu_register_machine(&collie_machine);
> +}
> +
> +machine_init(collie_machine_init)
> --
> 1.7.4.1
>
>
>
--
Aurelien Jarno GPG: 1024D/F1BCDB73
aurelien@aurel32.net http://www.aurel32.net
^ permalink raw reply [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
@ 2011-04-04 12:15 Dmitry Eremin-Solenikov
2011-04-04 12:15 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-04 12:15 UTC (permalink / raw)
To: qemu-devel
Currently target-arm/ assumes at least ARMv5 core. Add support for
handling also ARMv4/ARMv4T. This changes the following instructions:
BX(v4T and later)
BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
SMULxy, SMULWxy, STC2 (v5 and later)
All instructions that are "v5TE and later" are also bound to just v5, as
that's how it was before.
This patch doesn _not_ include disabling of cp15 access and base-updated
data abort model (that will be required to emulate chips based on a
ARM7TDMI), because:
* no ARM7TDMI chips are currently emulated (or planned)
* those features aren't strictly necessary for my purposes (SA-1 core
emulation).
All v5 models are handled as they are v5T. Internally we still have a
check if the model is a v5(T) or v5TE, but as all emulated cores are
v5TE, those two cases are simply aliased (for now).
Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
Sanders <vince@kyllikki.org>.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
target-arm/cpu.h | 4 ++-
target-arm/helper.c | 28 ++++++++++++++++++++++-
target-arm/translate.c | 59 +++++++++++++++++++++++++++++++++++++++--------
3 files changed, 79 insertions(+), 12 deletions(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1ae7982..e247a7a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -360,7 +360,9 @@ enum arm_features {
ARM_FEATURE_M, /* Microcontroller profile. */
ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
ARM_FEATURE_THUMB2EE,
- ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V4T,
+ ARM_FEATURE_V5,
};
static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 6788a4c..4c0b4a2 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cpuid = id;
switch (id) {
case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
env->cp15.c0_cachetype = 0x1dd20d2;
env->cp15.c1_sys = 0x00090078;
break;
case ARM_CPUID_ARM946:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_MPU);
env->cp15.c0_cachetype = 0x0f004006;
env->cp15.c1_sys = 0x00000078;
break;
case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
@@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_ARM1136_R2:
case ARM_CPUID_ARM1136:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
@@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00050078;
break;
case ARM_CPUID_ARM11MPCORE:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_VFP);
@@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0x1dd20d2;
break;
case ARM_CPUID_CORTEXA8:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXA9:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXM3:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_THUMB2);
set_feature(env, ARM_FEATURE_V7);
@@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_DIV);
break;
case ARM_CPUID_ANY: /* For userspace emulation. */
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_TI915T:
case ARM_CPUID_TI925T:
+ set_feature(env, ARM_FEATURE_V4T);
set_feature(env, ARM_FEATURE_OMAPCP);
env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
env->cp15.c0_cachetype = 0x5109149;
@@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA260:
case ARM_CPUID_PXA261:
case ARM_CPUID_PXA262:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
env->cp15.c0_cachetype = 0xd172172;
@@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA270_B1:
case ARM_CPUID_PXA270_C0:
case ARM_CPUID_PXA270_C5:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
set_feature(env, ARM_FEATURE_IWMMXT);
@@ -856,7 +879,11 @@ void do_interrupt(CPUARMState *env)
/* Switch to the new mode, and to the correct instruction set. */
env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
env->uncached_cpsr |= mask;
- env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+ /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
+ * and we should just guard the thumb mode on V4 */
+ if (arm_feature(env, ARM_FEATURE_V4T)) {
+ env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+ }
env->regs[14] = env->regs[15] + offset;
env->regs[15] = addr;
env->interrupt_request |= CPU_INTERRUPT_EXITTB;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 33417e6..05a58d4 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,6 +34,10 @@
#define GEN_HELPER 1
#include "helpers.h"
+#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
+#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
+/* currently all emulated v5 cores are also v5TE, so don't bother */
+#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5)
#define ENABLE_ARCH_5J 0
#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
@@ -750,6 +754,20 @@ static inline void store_reg_bx(CPUState *env, DisasContext *s,
}
}
+/* Variant of store_reg which uses branch&exchange logic when storing
+ * to r15 in ARM architecture v5T and above. This is used for storing
+ * the results of a LDR/LDM/POP into r15, and corresponds to the cases
+ * in the ARM ARM which use the LoadWritePC() pseudocode function. */
+static inline void store_reg_from_load(CPUState *env, DisasContext *s,
+ int reg, TCGv var)
+{
+ if (reg == 15 && ENABLE_ARCH_5) {
+ gen_bx(s, var);
+ } else {
+ store_reg(s, reg, var);
+ }
+}
+
static inline TCGv gen_ld8s(TCGv addr, int index)
{
TCGv tmp = tcg_temp_new_i32();
@@ -3443,6 +3461,10 @@ static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
/* Mask out undefined bits. */
mask &= ~CPSR_RESERVED;
+ if (!arm_feature(env, ARM_FEATURE_V4T))
+ mask &= ~CPSR_T;
+ if (!arm_feature(env, ARM_FEATURE_V5))
+ mask &= ~CPSR_Q; /* V5TE in reality*/
if (!arm_feature(env, ARM_FEATURE_V6))
mask &= ~(CPSR_E | CPSR_GE);
if (!arm_feature(env, ARM_FEATURE_THUMB2))
@@ -6145,6 +6167,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
cond = insn >> 28;
if (cond == 0xf){
+ /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
+ * choose to UNDEF. In ARMv5 and above the space is used
+ * for miscellaneous unconditional instructions.
+ */
+ ARCH(5);
+
/* Unconditional instructions. */
if (((insn >> 25) & 7) == 1) {
/* NEON Data processing. */
@@ -6173,6 +6201,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
}
/* Otherwise PLD; v5TE+ */
+ ARCH(5TE);
return;
}
if (((insn & 0x0f70f000) == 0x0450f000) ||
@@ -6309,6 +6338,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
val += (offset << 2) | ((insn >> 23) & 2) | 1;
/* pipeline offset */
val += 4;
+ /* protected by ARCH(5); above, near the start of uncond block */
gen_bx_im(s, val);
return;
} else if ((insn & 0x0e000f00) == 0x0c000100) {
@@ -6320,6 +6350,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
} else if ((insn & 0x0fe00000) == 0x0c400000) {
/* Coprocessor double register transfer. */
+ ARCH(5TE);
} else if ((insn & 0x0f000010) == 0x0e000010) {
/* Additional coprocessor register transfer. */
} else if ((insn & 0x0ff10020) == 0x01000000) {
@@ -6420,10 +6451,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
case 0x1:
if (op1 == 1) {
/* branch/exchange thumb (bx). */
+ ARCH(4T);
tmp = load_reg(s, rm);
gen_bx(s, tmp);
} else if (op1 == 3) {
/* clz */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
tmp = load_reg(s, rm);
gen_helper_clz(tmp, tmp);
@@ -6446,6 +6479,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (op1 != 1)
goto illegal_op;
+ ARCH(5);
/* branch link/exchange thumb (blx) */
tmp = load_reg(s, rm);
tmp2 = tcg_temp_new_i32();
@@ -6454,6 +6488,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx(s, tmp);
break;
case 0x5: /* saturating add/subtract */
+ ARCH(5TE);
rd = (insn >> 12) & 0xf;
rn = (insn >> 16) & 0xf;
tmp = load_reg(s, rm);
@@ -6475,12 +6510,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
}
/* bkpt */
+ ARCH(5);
gen_exception_insn(s, 4, EXCP_BKPT);
break;
case 0x8: /* signed multiply */
case 0xa:
case 0xc:
case 0xe:
+ ARCH(5TE);
rs = (insn >> 8) & 0xf;
rn = (insn >> 12) & 0xf;
rd = (insn >> 16) & 0xf;
@@ -6876,6 +6913,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
load = 1;
} else if (sh & 2) {
+ ARCH(5TE);
/* doubleword */
if (sh & 1) {
/* store */
@@ -7216,10 +7254,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
if (insn & (1 << 20)) {
/* Complete the load. */
- if (rd == 15)
- gen_bx(s, tmp);
- else
- store_reg(s, rd, tmp);
+ store_reg_from_load(env, s, rd, tmp);
}
break;
case 0x08:
@@ -7272,9 +7307,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (insn & (1 << 20)) {
/* load */
tmp = gen_ld32(addr, IS_USER(s));
- if (i == 15) {
- gen_bx(s, tmp);
- } else if (user) {
+ if (user) {
tmp2 = tcg_const_i32(i);
gen_helper_set_user_reg(tmp2, tmp);
tcg_temp_free_i32(tmp2);
@@ -7283,7 +7316,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
loaded_var = tmp;
loaded_base = 1;
} else {
- store_reg(s, i, tmp);
+ store_reg_from_load(env, s, i, tmp);
}
} else {
/* store */
@@ -7483,6 +7516,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
16-bit instructions to get correct prefetch abort behavior. */
insn = insn_hw1;
if ((insn & (1 << 12)) == 0) {
+ ARCH(5);
/* Second half of blx. */
offset = ((insn & 0x7ff) << 1);
tmp = load_reg(s, 14);
@@ -8079,6 +8113,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
} else {
/* blx */
offset &= ~(uint32_t)2;
+ /* thumb2 bx, no need to check */
gen_bx_im(s, offset);
}
} else if (((insn >> 23) & 7) == 7) {
@@ -8660,11 +8695,13 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
case 3:/* branch [and link] exchange thumb register */
tmp = load_reg(s, rm);
if (insn & (1 << 7)) {
+ ARCH(5);
val = (uint32_t)s->pc | 1;
tmp2 = tcg_temp_new_i32();
tcg_gen_movi_i32(tmp2, val);
store_reg(s, 14, tmp2);
}
+ /* already thumb, no need to check */
gen_bx(s, tmp);
break;
}
@@ -9024,8 +9061,9 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
/* write back the new stack pointer */
store_reg(s, 13, addr);
/* set the new PC value */
- if ((insn & 0x0900) == 0x0900)
- gen_bx(s, tmp);
+ if ((insn & 0x0900) == 0x0900) {
+ store_reg_from_load(env, s, 15, tmp);
+ }
break;
case 1: case 3: case 9: case 11: /* czb */
@@ -9056,6 +9094,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
break;
case 0xe: /* bkpt */
+ ARCH(5);
gen_exception_insn(s, 2, EXCP_BKPT);
break;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
2011-04-04 12:15 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
@ 2011-04-04 12:15 ` Dmitry Eremin-Solenikov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-04-04 12:15 UTC (permalink / raw)
To: qemu-devel
Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
Implemented:
- IRQs
- GPIO
- PPC
- RTC
- UARTs (no IrDA/etc.)
- OST reused from pxa25x
Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
hw/strongarm.c
V2:
* removed all strongarm variants except latest
* dropped unused casts
* fixed PIC vmstate
* fixed new devices created with version_id = 1
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
Makefile.target | 1 +
hw/strongarm.c | 1301 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/strongarm.h | 62 +++
target-arm/cpu.h | 3 +
target-arm/helper.c | 9 +
5 files changed, 1376 insertions(+), 0 deletions(-)
create mode 100644 hw/strongarm.c
create mode 100644 hw/strongarm.h
diff --git a/Makefile.target b/Makefile.target
index 2f76714..cd03cec 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -352,6 +352,7 @@ obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
obj-arm-y += vexpress.o
+obj-arm-y += strongarm.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/strongarm.c b/hw/strongarm.c
new file mode 100644
index 0000000..9f3df87
--- /dev/null
+++ b/hw/strongarm.c
@@ -0,0 +1,1301 @@
+/*
+ * StrongARM SA-1100/SA-1110 emulation
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * Largely based on StrongARM emulation:
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * UART code based on QEMU 16550A UART emulation
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "strongarm.h"
+#include "qemu-error.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+/*
+ TODO
+ - Implement cp15, c14 ?
+ - Implement cp15, c15 !!! (idle used in L)
+ - Implement idle mode handling/DIM
+ - Implement sleep mode/Wake sources
+ - Implement reset control
+ - Implement memory control regs
+ - PCMCIA handling
+ - Maybe support MBGNT/MBREQ
+ - DMA channels
+ - GPCLK
+ - IrDA
+ - MCP
+ - Enhance UART with modem signals
+ */
+
+static struct {
+ target_phys_addr_t io_base;
+ int irq;
+} sa_serial[] = {
+ { 0x80010000, SA_PIC_UART1 },
+ { 0x80030000, SA_PIC_UART2 },
+ { 0x80050000, SA_PIC_UART3 },
+ { 0, 0 }
+};
+
+/* Interrupt Controller */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ uint32_t pending;
+ uint32_t enabled;
+ uint32_t is_fiq;
+ uint32_t int_idle;
+} StrongARMPICState;
+
+#define ICIP 0x00
+#define ICMR 0x04
+#define ICLR 0x08
+#define ICFP 0x10
+#define ICPR 0x20
+#define ICCR 0x0c
+
+#define SA_PIC_SRCS 32
+
+
+static void strongarm_pic_update(void *opaque)
+{
+ StrongARMPICState *s = opaque;
+
+ /* FIXME: reflect DIM */
+ qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
+ qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
+}
+
+static void strongarm_pic_set_irq(void *opaque, int irq, int level)
+{
+ StrongARMPICState *s = opaque;
+
+ if (level) {
+ s->pending |= 1 << irq;
+ } else {
+ s->pending &= ~(1 << irq);
+ }
+
+ strongarm_pic_update(s);
+}
+
+static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICIP:
+ return s->pending & ~s->is_fiq & s->enabled;
+ case ICMR:
+ return s->enabled;
+ case ICLR:
+ return s->is_fiq;
+ case ICCR:
+ return s->int_idle == 0;
+ case ICFP:
+ return s->pending & s->is_fiq & s->enabled;
+ case ICPR:
+ return s->pending;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICMR:
+ s->enabled = value;
+ break;
+ case ICLR:
+ s->is_fiq = value;
+ break;
+ case ICCR:
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ break;
+ }
+ strongarm_pic_update(s);
+}
+
+static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+};
+
+static int strongarm_pic_initfn(SysBusDevice *dev)
+{
+ StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
+ iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
+ strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ return 0;
+}
+
+static int strongarm_pic_post_load(void *opaque, int version_id)
+{
+ strongarm_pic_update(opaque);
+ return 0;
+}
+
+static VMStateDescription vmstate_strongarm_pic_regs = {
+ .name = "strongarm_pic",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_pic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pending, StrongARMPICState),
+ VMSTATE_UINT32(enabled, StrongARMPICState),
+ VMSTATE_UINT32(is_fiq, StrongARMPICState),
+ VMSTATE_UINT32(int_idle, StrongARMPICState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_pic_info = {
+ .init = strongarm_pic_initfn,
+ .qdev.name = "strongarm_pic",
+ .qdev.desc = "StrongARM PIC",
+ .qdev.size = sizeof(StrongARMPICState),
+ .qdev.vmsd = &vmstate_strongarm_pic_regs,
+};
+
+/* Real-Time Clock */
+#define RTAR 0x00 /* RTC Alarm register */
+#define RCNR 0x04 /* RTC Counter register */
+#define RTTR 0x08 /* RTC Timer Trim register */
+#define RTSR 0x0c /* RTC Status register */
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t last_rcnr;
+ int64_t last_hz;
+ QEMUTimer *rtc_alarm;
+ QEMUTimer *rtc_hz;
+ qemu_irq rtc_irq;
+ qemu_irq rtc_hz_irq;
+} StrongARMRTCState;
+
+static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
+{
+ qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
+ qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
+}
+
+static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rt_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_hz);
+ }
+
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
+ qemu_mod_timer(s->rtc_alarm, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_alarm);
+ }
+}
+
+static inline void strongarm_rtc_alarm_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 0);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static inline void strongarm_rtc_hz_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 1);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RCNR:
+ return s->last_rcnr +
+ ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ strongarm_rtc_hzupdate(s);
+ s->rttr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RTSR:
+ if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
+ strongarm_rtc_timer_update(s, value);
+ }
+
+ s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
+ strongarm_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ strongarm_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+};
+
+static int strongarm_rtc_init(SysBusDevice *dev)
+{
+ StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
+ struct tm tm;
+ int iomemtype;
+
+ s->rttr = 0x0;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_hz = qemu_get_clock_ms(rt_clock);
+
+ s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
+ s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
+
+ sysbus_init_irq(dev, &s->rtc_irq);
+ sysbus_init_irq(dev, &s->rtc_hz_irq);
+
+ iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
+ strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+
+ return 0;
+}
+
+static void strongarm_rtc_pre_save(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_hzupdate(s);
+}
+
+static int strongarm_rtc_post_load(void *opaque, int version_id)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_timer_update(s, s->rtsr);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_rtc_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = strongarm_rtc_pre_save,
+ .post_load = strongarm_rtc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rttr, StrongARMRTCState),
+ VMSTATE_UINT32(rtsr, StrongARMRTCState),
+ VMSTATE_UINT32(rtar, StrongARMRTCState),
+ VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
+ VMSTATE_INT64(last_hz, StrongARMRTCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
+ .init = strongarm_rtc_init,
+ .qdev.name = "strongarm-rtc",
+ .qdev.desc = "StrongARM RTC Controller",
+ .qdev.size = sizeof(StrongARMRTCState),
+ .qdev.vmsd = &vmstate_strongarm_rtc_regs,
+};
+
+/* GPIO */
+#define GPLR 0x00
+#define GPDR 0x04
+#define GPSR 0x08
+#define GPCR 0x0c
+#define GRER 0x10
+#define GFER 0x14
+#define GEDR 0x18
+#define GAFR 0x1c
+
+typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
+struct StrongARMGPIOInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+ qemu_irq irqs[11];
+ qemu_irq irqX;
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t rising;
+ uint32_t falling;
+ uint32_t status;
+ uint32_t gpsr;
+ uint32_t gafr;
+
+ uint32_t prev_level;
+};
+
+
+static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
+{
+ int i;
+ for (i = 0; i < 11; i++) {
+ qemu_set_irq(s->irqs[i], s->status & (1 << i));
+ }
+
+ qemu_set_irq(s->irqX, (s->status & ~0x7ff));
+}
+
+static void strongarm_gpio_set(void *opaque, int line, int level)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+ uint32_t mask;
+
+ mask = 1 << line;
+
+ if (level) {
+ s->status |= s->rising & mask &
+ ~s->ilevel & ~s->dir;
+ s->ilevel |= mask;
+ } else {
+ s->status |= s->falling & mask &
+ s->ilevel & ~s->dir;
+ s->ilevel &= ~mask;
+ }
+
+ if (s->status & mask) {
+ strongarm_gpio_irq_update(s);
+ }
+}
+
+static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return s->gpsr; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ return s->gafr;
+
+ case GPLR: /* GPIO Pin-Level registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir);
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_gpio_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir = value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel |= value;
+ strongarm_gpio_handler_update(s);
+ s->gpsr = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel &= ~value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling = value;
+ break;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ s->gafr = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status &= ~value;
+ strongarm_gpio_irq_update(s);
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
+ strongarm_gpio_read,
+ strongarm_gpio_read,
+ strongarm_gpio_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
+ strongarm_gpio_write,
+ strongarm_gpio_write,
+ strongarm_gpio_write
+};
+
+static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
+ DeviceState *pic)
+{
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "strongarm-gpio");
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ for (i = 0; i < 12; i++)
+ sysbus_connect_irq(sysbus_from_qdev(dev), i,
+ qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
+
+ return dev;
+}
+
+static int strongarm_gpio_initfn(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMGPIOInfo *s;
+ int i;
+
+ s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 28);
+
+ iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
+ strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ for (i = 0; i < 11; i++) {
+ sysbus_init_irq(dev, &s->irqs[i]);
+ }
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_gpio_regs = {
+ .name = "strongarm-gpio",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(dir, StrongARMGPIOInfo),
+ VMSTATE_UINT32(rising, StrongARMGPIOInfo),
+ VMSTATE_UINT32(falling, StrongARMGPIOInfo),
+ VMSTATE_UINT32(status, StrongARMGPIOInfo),
+ VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_gpio_info = {
+ .init = strongarm_gpio_initfn,
+ .qdev.name = "strongarm-gpio",
+ .qdev.desc = "StrongARM GPIO controller",
+ .qdev.size = sizeof(StrongARMGPIOInfo),
+};
+
+/* Peripheral Pin Controller */
+#define PPDR 0x00
+#define PPSR 0x04
+#define PPAR 0x08
+#define PSDR 0x0c
+#define PPFR 0x10
+
+typedef struct StrongARMPPCInfo StrongARMPPCInfo;
+struct StrongARMPPCInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t ppar;
+ uint32_t psdr;
+ uint32_t ppfr;
+
+ uint32_t prev_level;
+};
+
+static void strongarm_ppc_set(void *opaque, int line, int level)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ if (level) {
+ s->ilevel |= 1 << line;
+ } else {
+ s->ilevel &= ~(1 << line);
+ }
+}
+
+static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ return s->dir | ~0x3fffff;
+
+ case PPSR: /* PPC Pin State registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir) |
+ ~0x3fffff;
+
+ case PPAR:
+ return s->ppar | ~0x41000;
+
+ case PSDR:
+ return s->psdr;
+
+ case PPFR:
+ return s->ppfr | ~0x7f001;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_ppc_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ s->dir = value & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPSR: /* PPC Pin State registers */
+ s->olevel = value & s->dir & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPAR:
+ s->ppar = value & 0x41000;
+ break;
+
+ case PSDR:
+ s->psdr = value & 0x3fffff;
+ break;
+
+ case PPFR:
+ s->ppfr = value & 0x7f001;
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
+ strongarm_ppc_read,
+ strongarm_ppc_read,
+ strongarm_ppc_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
+ strongarm_ppc_write,
+ strongarm_ppc_write,
+ strongarm_ppc_write
+};
+
+static int strongarm_ppc_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMPPCInfo *s;
+
+ s = FROM_SYSBUS(StrongARMPPCInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 22);
+
+ iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
+ strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_ppc_regs = {
+ .name = "strongarm-ppc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(olevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(dir, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppar, StrongARMPPCInfo),
+ VMSTATE_UINT32(psdr, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_ppc_info = {
+ .init = strongarm_ppc_init,
+ .qdev.name = "strongarm-ppc",
+ .qdev.desc = "StrongARM PPC controller",
+ .qdev.size = sizeof(StrongARMPPCInfo),
+};
+
+/* UART Ports */
+#define UTCR0 0x00
+#define UTCR1 0x04
+#define UTCR2 0x08
+#define UTCR3 0x0c
+#define UTDR 0x14
+#define UTSR0 0x1c
+#define UTSR1 0x20
+
+#define RX_FIFO_PRE (1 << 8)
+#define RX_FIFO_FRE (1 << 9)
+#define RX_FIFO_ROR (1 << 10)
+
+typedef struct {
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t utcr0;
+ uint16_t brd;
+ uint8_t utcr3;
+ uint8_t utsr0;
+ uint8_t utsr1;
+
+ uint8_t tx_fifo[8];
+ uint8_t tx_start;
+ uint8_t tx_len;
+ uint16_t rx_fifo[12]; /* value + error flags in high bits */
+ uint8_t rx_start;
+ uint8_t rx_len;
+
+ uint64_t char_transmit_time; /* time to transmit a char in ticks*/
+ bool wait_break_end;
+ QEMUTimer *rx_timeout_timer;
+ QEMUTimer *tx_timer;
+} StrongARMUARTState;
+
+static void strongarm_uart_update_status(StrongARMUARTState *s)
+{
+ uint16_t utsr1 = 0;
+
+ if (s->tx_len != 8) {
+ utsr1 |= (1 << 2);
+ }
+
+ if (s->rx_len != 0) {
+ uint16_t ent = s->rx_fifo[s->rx_start];
+
+ utsr1 |= (1 << 1);
+ if (ent & RX_FIFO_PRE) {
+ s->utsr1 |= (1 << 3);
+ }
+ if (ent & RX_FIFO_FRE) {
+ s->utsr1 |= (1 << 4);
+ }
+ if (ent & RX_FIFO_ROR) {
+ s->utsr1 |= (1 << 5);
+ }
+ }
+
+ s->utsr1 = utsr1;
+}
+
+static void strongarm_uart_update_int_status(StrongARMUARTState *s)
+{
+ uint16_t utsr0 = s->utsr0 & 0x1c;
+ int i;
+
+ if ((s->utcr3 & (1 << 1)) &&
+ (s->utcr3 & (1 << 4)) &&
+ s->tx_len <= 4) {
+ utsr0 |= (1 << 0);
+ }
+
+ if ((s->utcr3 & (1 << 0)) &&
+ (s->utcr3 & (1 << 3)) &&
+ s->rx_len > 4) {
+ utsr0 |= (1 << 1);
+ }
+
+ for (i = 0; i < s->rx_len && i < 4; i++)
+ if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
+ utsr0 |= (1 << 5);
+ break;
+ }
+
+ s->utsr0 = utsr0;
+ qemu_set_irq(s->irq, utsr0);
+}
+
+static void strongarm_uart_update_parameters(StrongARMUARTState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->utcr0 & (1 << 0)) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->utcr0 & (1 << 1)) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+ if (s->utcr0 & (1 << 2)) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
+ frame_size += data_bits + stop_bits;
+ speed = 3686400 / 16 / (s->brd + 1);
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ if (s->chr) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+ }
+
+#if 0
+ fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
+ speed, parity, data_bits, stop_bits);
+#endif
+}
+
+static void strongarm_uart_rx_to(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len) {
+ s->utsr0 |= (1 << 2);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
+{
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ /* rx disabled */
+ return;
+ }
+
+ if (s->wait_break_end) {
+ s->utsr0 |= (1 << 4); /* REB */
+ s->wait_break_end = false;
+ }
+
+ if (s->rx_len < 12) {
+ s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
+ s->rx_len++;
+ } else
+ s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
+}
+
+static int strongarm_uart_can_receive(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len == 12) {
+ return 0;
+ }
+ /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */
+ if (s->rx_len < 8) {
+ return 8 - s->rx_len;
+ }
+ return 1;
+}
+
+static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ StrongARMUARTState *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ strongarm_uart_rx_push(s, buf[i]);
+ }
+
+ /* call the timeout receive callback in 3 char transmit time */
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static void strongarm_uart_event(void *opaque, int event)
+{
+ StrongARMUARTState *s = opaque;
+ if (event == CHR_EVENT_BREAK) {
+ s->utcr3 |= (1 << 3);
+ strongarm_uart_rx_push(s, RX_FIFO_FRE);
+ s->wait_break_end = true;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_tx(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+ uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ if (s->utcr3 & (1 << 5)) /* loopback */ {
+ strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
+ } else if (s->chr) {
+ qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
+ }
+
+ s->tx_start = (s->tx_start + 1) % 8;
+ s->tx_len--;
+ if (s->tx_len) {
+ qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMUARTState *s = opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case UTCR0:
+ return s->utcr0;
+
+ case UTCR1:
+ return s->brd >> 8;
+
+ case UTCR2:
+ return s->brd & 0xff;
+
+ case UTCR3:
+ return s->utcr3;
+
+ case UTDR:
+ if (s->rx_len != 0) {
+ ret = s->rx_fifo[s->rx_start];
+ s->rx_start = (s->rx_start + 1) % 12;
+ s->rx_len--;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ return ret;
+ }
+ return 0;
+
+ case UTSR0:
+ return s->utsr0;
+
+ case UTSR1:
+ return s->utsr1;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMUARTState *s = opaque;
+
+ switch (addr) {
+ case UTCR0:
+ s->utcr0 = value & 0x7f;
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR1:
+ s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR2:
+ s->brd = (s->brd & 0xf00) | (value & 0xff);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR3:
+ s->utcr3 = value & 0x3f;
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ s->rx_len = 0;
+ }
+ if ((s->utcr3 & (1 << 1)) == 0) {
+ s->tx_len = 0;
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ case UTDR:
+ if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
+ s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
+ s->tx_len++;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ if (s->tx_len == 1) {
+ strongarm_uart_tx(s);
+ }
+ }
+ break;
+
+ case UTSR0:
+ s->utsr0 = s->utsr0 & ~(value & 0x1c);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
+ strongarm_uart_read,
+ strongarm_uart_read,
+ strongarm_uart_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
+ strongarm_uart_write,
+ strongarm_uart_write,
+ strongarm_uart_write,
+};
+
+static int strongarm_uart_init(SysBusDevice *dev)
+{
+ StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
+ strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s);
+ s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr,
+ strongarm_uart_can_receive,
+ strongarm_uart_receive,
+ strongarm_uart_event,
+ s);
+ }
+
+ return 0;
+}
+
+static void strongarm_uart_reset(DeviceState *dev)
+{
+ StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
+
+ s->utcr0 = 0x8; /* 8 data, no parity */
+ s->brd = 23; /* 9600 */
+ s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
+
+ s->rx_len = s->tx_len = 0;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static int strongarm_uart_post_load(void *opaque, int version_id)
+{
+ StrongARMUARTState *s = opaque;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+
+ /* tx and restart timer */
+ if (s->tx_len) {
+ strongarm_uart_tx(s);
+ }
+
+ /* restart rx timeout timer */
+ if (s->rx_len) {
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_uart_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(utcr0, StrongARMUARTState),
+ VMSTATE_UINT16(brd, StrongARMUARTState),
+ VMSTATE_UINT8(utcr3, StrongARMUARTState),
+ VMSTATE_UINT8(utsr0, StrongARMUARTState),
+ VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
+ VMSTATE_UINT8(tx_start, StrongARMUARTState),
+ VMSTATE_UINT8(tx_len, StrongARMUARTState),
+ VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
+ VMSTATE_UINT8(rx_start, StrongARMUARTState),
+ VMSTATE_UINT8(rx_len, StrongARMUARTState),
+ VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_uart_info = {
+ .init = strongarm_uart_init,
+ .qdev.name = "strongarm-uart",
+ .qdev.desc = "StrongARM UART controller",
+ .qdev.size = sizeof(StrongARMUARTState),
+ .qdev.reset = strongarm_uart_reset,
+ .qdev.vmsd = &vmstate_strongarm_uart_regs,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+/* Main CPU functions */
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
+{
+ StrongARMState *s;
+ qemu_irq *pic;
+ int i;
+
+ s = qemu_mallocz(sizeof(StrongARMState));
+
+ if (!rev) {
+ rev = "sa1110-b5";
+ }
+
+ if (strncmp(rev, "sa1110", 6)) {
+ error_report("Machine requires a SA1110 processor.\n");
+ exit(1);
+ }
+
+ s->env = cpu_init(rev);
+
+ if (!s->env) {
+ error_report("Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ cpu_register_physical_memory(SA_SDCS0,
+ sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
+ sdram_size) | IO_MEM_RAM);
+
+ pic = arm_pic_init_cpu(s->env);
+ s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
+ pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
+
+ sysbus_create_varargs("pxa25x-timer", 0x90000000,
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
+ NULL);
+
+ sysbus_create_simple("strongarm-rtc", 0x90010000,
+ qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
+
+ s->gpio = strongarm_gpio_init(0x90040000, s->pic);
+
+ s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
+
+ for (i = 0; sa_serial[i].io_base; i++) {
+ DeviceState *dev = qdev_create(NULL, "strongarm-uart");
+ qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ sa_serial[i].io_base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0,
+ qdev_get_gpio_in(s->pic, sa_serial[i].irq));
+ }
+
+ return s;
+}
+
+static void strongarm_register_devices(void)
+{
+ sysbus_register_withprop(&strongarm_pic_info);
+ sysbus_register_withprop(&strongarm_rtc_sysbus_info);
+ sysbus_register_withprop(&strongarm_gpio_info);
+ sysbus_register_withprop(&strongarm_ppc_info);
+ sysbus_register_withprop(&strongarm_uart_info);
+}
+device_init(strongarm_register_devices)
diff --git a/hw/strongarm.h b/hw/strongarm.h
new file mode 100644
index 0000000..ce7a3d7
--- /dev/null
+++ b/hw/strongarm.h
@@ -0,0 +1,62 @@
+#ifndef _STRONGARM_H
+#define _STRONGARM_H
+
+#define SA_CS0 0x00000000
+#define SA_CS1 0x08000000
+#define SA_CS2 0x10000000
+#define SA_CS3 0x18000000
+#define SA_PCMCIA_CS0 0x20000000
+#define SA_PCMCIA_CS1 0x30000000
+#define SA_CS4 0x40000000
+#define SA_CS5 0x48000000
+/* system registers here */
+#define SA_SDCS0 0xc0000000
+#define SA_SDCS1 0xc8000000
+#define SA_SDCS2 0xd0000000
+#define SA_SDCS3 0xd8000000
+
+enum {
+ SA_PIC_GPIO0_EDGE = 0,
+ SA_PIC_GPIO1_EDGE,
+ SA_PIC_GPIO2_EDGE,
+ SA_PIC_GPIO3_EDGE,
+ SA_PIC_GPIO4_EDGE,
+ SA_PIC_GPIO5_EDGE,
+ SA_PIC_GPIO6_EDGE,
+ SA_PIC_GPIO7_EDGE,
+ SA_PIC_GPIO8_EDGE,
+ SA_PIC_GPIO9_EDGE,
+ SA_PIC_GPIO10_EDGE,
+ SA_PIC_GPIOX_EDGE,
+ SA_PIC_LCD,
+ SA_PIC_UDC,
+ SA_PIC_RSVD1,
+ SA_PIC_UART1,
+ SA_PIC_UART2,
+ SA_PIC_UART3,
+ SA_PIC_MCP,
+ SA_PIC_SSP,
+ SA_PIC_DMA_CH0,
+ SA_PIC_DMA_CH1,
+ SA_PIC_DMA_CH2,
+ SA_PIC_DMA_CH3,
+ SA_PIC_DMA_CH4,
+ SA_PIC_DMA_CH5,
+ SA_PIC_OSTC0,
+ SA_PIC_OSTC1,
+ SA_PIC_OSTC2,
+ SA_PIC_OSTC3,
+ SA_PIC_RTC_HZ,
+ SA_PIC_RTC_ALARM,
+};
+
+typedef struct {
+ CPUState *env;
+ DeviceState *pic;
+ DeviceState *gpio;
+ DeviceState *ppc;
+} StrongARMState;
+
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
+
+#endif
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index e247a7a..d5af644 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -363,6 +363,7 @@ enum arm_features {
ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
ARM_FEATURE_V4T,
ARM_FEATURE_V5,
+ ARM_FEATURE_STRONGARM,
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
+#define ARM_CPUID_SA1100 0x4401A11B
+#define ARM_CPUID_SA1110 0x6901B119
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 4c0b4a2..00447ca 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -214,6 +214,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0xd172172;
env->cp15.c1_sys = 0x00000078;
break;
+ case ARM_CPUID_SA1100:
+ case ARM_CPUID_SA1110:
+ set_feature(env, ARM_FEATURE_STRONGARM);
+ env->cp15.c1_sys = 0x00000070;
+ break;
default:
cpu_abort(env, "Bad CPU ID: %x\n", id);
break;
@@ -374,6 +379,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
{ ARM_CPUID_CORTEXA9, "cortex-a9"},
{ ARM_CPUID_TI925T, "ti925t" },
{ ARM_CPUID_PXA250, "pxa250" },
+ { ARM_CPUID_SA1100, "sa1100" },
+ { ARM_CPUID_SA1110, "sa1110" },
{ ARM_CPUID_PXA255, "pxa255" },
{ ARM_CPUID_PXA260, "pxa260" },
{ ARM_CPUID_PXA261, "pxa261" },
@@ -1548,6 +1555,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 9:
if (arm_feature(env, ARM_FEATURE_OMAPCP))
break;
+ if (arm_feature(env, ARM_FEATURE_STRONGARM))
+ break; /* Ignore ReadBuffer access */
switch (crm) {
case 0: /* Cache lockdown. */
switch (op1) {
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
@ 2011-03-30 11:41 Dmitry Eremin-Solenikov
2011-03-30 11:41 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-30 11:41 UTC (permalink / raw)
To: qemu-devel
Currently target-arm/ assumes at least ARMv5 core. Add support for
handling also ARMv4/ARMv4T. This changes the following instructions:
BX(v4T and later)
BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
SMULxy, SMULWxy, STC2 (v5 and later)
All instructions that are "v5TE and later" are also bound to just v5, as
that's how it was before.
This patch doesn _not_ include disabling of cp15 access and base-updated
data abort model (that will be required to emulate chips based on a
ARM7TDMI), because:
* no ARM7TDMI chips are currently emulated (or planned)
* those features aren't strictly necessary for my purposes (SA-1 core
emulation).
All v5 models are handled as they are v5T. Internally we still have a
check if the model is a v5(T) or v5TE, but as all emulated cores are
v5TE, those two cases are simply aliased (for now).
Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
Sanders <vince@kyllikki.org>.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
target-arm/cpu.h | 4 ++-
target-arm/helper.c | 28 ++++++++++++++++++++++-
target-arm/translate.c | 59 +++++++++++++++++++++++++++++++++++++++--------
3 files changed, 79 insertions(+), 12 deletions(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1ae7982..e247a7a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -360,7 +360,9 @@ enum arm_features {
ARM_FEATURE_M, /* Microcontroller profile. */
ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
ARM_FEATURE_THUMB2EE,
- ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V4T,
+ ARM_FEATURE_V5,
};
static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 78f3d39..44b5c17 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cpuid = id;
switch (id) {
case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
env->cp15.c0_cachetype = 0x1dd20d2;
env->cp15.c1_sys = 0x00090078;
break;
case ARM_CPUID_ARM946:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_MPU);
env->cp15.c0_cachetype = 0x0f004006;
env->cp15.c1_sys = 0x00000078;
break;
case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
@@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_ARM1136_R2:
case ARM_CPUID_ARM1136:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
@@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00050078;
break;
case ARM_CPUID_ARM11MPCORE:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_VFP);
@@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0x1dd20d2;
break;
case ARM_CPUID_CORTEXA8:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXA9:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXM3:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_THUMB2);
set_feature(env, ARM_FEATURE_V7);
@@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_DIV);
break;
case ARM_CPUID_ANY: /* For userspace emulation. */
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_TI915T:
case ARM_CPUID_TI925T:
+ set_feature(env, ARM_FEATURE_V4T);
set_feature(env, ARM_FEATURE_OMAPCP);
env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
env->cp15.c0_cachetype = 0x5109149;
@@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA260:
case ARM_CPUID_PXA261:
case ARM_CPUID_PXA262:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
env->cp15.c0_cachetype = 0xd172172;
@@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA270_B1:
case ARM_CPUID_PXA270_C0:
case ARM_CPUID_PXA270_C5:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
set_feature(env, ARM_FEATURE_IWMMXT);
@@ -856,7 +879,11 @@ void do_interrupt(CPUARMState *env)
/* Switch to the new mode, and to the correct instruction set. */
env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
env->uncached_cpsr |= mask;
- env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+ /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
+ * and we should just guard the thumb mode on V4 */
+ if (arm_feature(env, ARM_FEATURE_V4T)) {
+ env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+ }
env->regs[14] = env->regs[15] + offset;
env->regs[15] = addr;
env->interrupt_request |= CPU_INTERRUPT_EXITTB;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index f69912f..2acba3b 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,6 +34,10 @@
#define GEN_HELPER 1
#include "helpers.h"
+#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
+#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
+/* currently all emulated v5 cores are also v5TE, so don't bother */
+#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5)
#define ENABLE_ARCH_5J 0
#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
@@ -750,6 +754,20 @@ static inline void store_reg_bx(CPUState *env, DisasContext *s,
}
}
+/* Variant of store_reg which uses branch&exchange logic when storing
+ * to r15 in ARM architecture v5T and above. This is used for storing
+ * the results of a LDR/LDM/POP into r15, and corresponds to the cases
+ * in the ARM ARM which use the LoadWritePC() pseudocode function. */
+static inline void store_reg_from_load(CPUState *env, DisasContext *s,
+ int reg, TCGv var)
+{
+ if (reg == 15 && ENABLE_ARCH_5) {
+ gen_bx(s, var);
+ } else {
+ store_reg(s, reg, var);
+ }
+}
+
static inline TCGv gen_ld8s(TCGv addr, int index)
{
TCGv tmp = tcg_temp_new_i32();
@@ -3421,6 +3439,10 @@ static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
/* Mask out undefined bits. */
mask &= ~CPSR_RESERVED;
+ if (!arm_feature(env, ARM_FEATURE_V4T))
+ mask &= ~CPSR_T;
+ if (!arm_feature(env, ARM_FEATURE_V5))
+ mask &= ~CPSR_Q; /* V5TE in reality*/
if (!arm_feature(env, ARM_FEATURE_V6))
mask &= ~(CPSR_E | CPSR_GE);
if (!arm_feature(env, ARM_FEATURE_THUMB2))
@@ -6101,6 +6123,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
cond = insn >> 28;
if (cond == 0xf){
+ /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
+ * choose to UNDEF. In ARMv5 and above the space is used
+ * for miscellaneous unconditional instructions.
+ */
+ ARCH(5);
+
/* Unconditional instructions. */
if (((insn >> 25) & 7) == 1) {
/* NEON Data processing. */
@@ -6129,6 +6157,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
}
/* Otherwise PLD; v5TE+ */
+ ARCH(5TE);
return;
}
if (((insn & 0x0f70f000) == 0x0450f000) ||
@@ -6265,6 +6294,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
val += (offset << 2) | ((insn >> 23) & 2) | 1;
/* pipeline offset */
val += 4;
+ /* protected by ARCH(5); above, near the start of uncond block */
gen_bx_im(s, val);
return;
} else if ((insn & 0x0e000f00) == 0x0c000100) {
@@ -6276,6 +6306,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
} else if ((insn & 0x0fe00000) == 0x0c400000) {
/* Coprocessor double register transfer. */
+ ARCH(5TE);
} else if ((insn & 0x0f000010) == 0x0e000010) {
/* Additional coprocessor register transfer. */
} else if ((insn & 0x0ff10020) == 0x01000000) {
@@ -6376,10 +6407,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
case 0x1:
if (op1 == 1) {
/* branch/exchange thumb (bx). */
+ ARCH(4T);
tmp = load_reg(s, rm);
gen_bx(s, tmp);
} else if (op1 == 3) {
/* clz */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
tmp = load_reg(s, rm);
gen_helper_clz(tmp, tmp);
@@ -6402,6 +6435,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (op1 != 1)
goto illegal_op;
+ ARCH(5);
/* branch link/exchange thumb (blx) */
tmp = load_reg(s, rm);
tmp2 = tcg_temp_new_i32();
@@ -6410,6 +6444,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx(s, tmp);
break;
case 0x5: /* saturating add/subtract */
+ ARCH(5TE);
rd = (insn >> 12) & 0xf;
rn = (insn >> 16) & 0xf;
tmp = load_reg(s, rm);
@@ -6431,12 +6466,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
}
/* bkpt */
+ ARCH(5);
gen_exception_insn(s, 4, EXCP_BKPT);
break;
case 0x8: /* signed multiply */
case 0xa:
case 0xc:
case 0xe:
+ ARCH(5TE);
rs = (insn >> 8) & 0xf;
rn = (insn >> 12) & 0xf;
rd = (insn >> 16) & 0xf;
@@ -6832,6 +6869,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
load = 1;
} else if (sh & 2) {
+ ARCH(5TE);
/* doubleword */
if (sh & 1) {
/* store */
@@ -7172,10 +7210,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
if (insn & (1 << 20)) {
/* Complete the load. */
- if (rd == 15)
- gen_bx(s, tmp);
- else
- store_reg(s, rd, tmp);
+ store_reg_from_load(env, s, rn, tmp);
}
break;
case 0x08:
@@ -7228,9 +7263,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (insn & (1 << 20)) {
/* load */
tmp = gen_ld32(addr, IS_USER(s));
- if (i == 15) {
- gen_bx(s, tmp);
- } else if (user) {
+ if (user) {
tmp2 = tcg_const_i32(i);
gen_helper_set_user_reg(tmp2, tmp);
tcg_temp_free_i32(tmp2);
@@ -7239,7 +7272,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
loaded_var = tmp;
loaded_base = 1;
} else {
- store_reg(s, i, tmp);
+ store_reg_from_load(env, s, i, tmp);
}
} else {
/* store */
@@ -7439,6 +7472,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
16-bit instructions to get correct prefetch abort behavior. */
insn = insn_hw1;
if ((insn & (1 << 12)) == 0) {
+ ARCH(5);
/* Second half of blx. */
offset = ((insn & 0x7ff) << 1);
tmp = load_reg(s, 14);
@@ -8035,6 +8069,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
} else {
/* blx */
offset &= ~(uint32_t)2;
+ /* thumb2 bx, no need to check */
gen_bx_im(s, offset);
}
} else if (((insn >> 23) & 7) == 7) {
@@ -8616,11 +8651,13 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
case 3:/* branch [and link] exchange thumb register */
tmp = load_reg(s, rm);
if (insn & (1 << 7)) {
+ ARCH(5);
val = (uint32_t)s->pc | 1;
tmp2 = tcg_temp_new_i32();
tcg_gen_movi_i32(tmp2, val);
store_reg(s, 14, tmp2);
}
+ /* already thumb, no need to check */
gen_bx(s, tmp);
break;
}
@@ -8980,8 +9017,9 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
/* write back the new stack pointer */
store_reg(s, 13, addr);
/* set the new PC value */
- if ((insn & 0x0900) == 0x0900)
- gen_bx(s, tmp);
+ if ((insn & 0x0900) == 0x0900) {
+ store_reg_from_load(env, s, 15, tmp);
+ }
break;
case 1: case 3: case 9: case 11: /* czb */
@@ -9012,6 +9050,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
break;
case 0xe: /* bkpt */
+ ARCH(5);
gen_exception_insn(s, 2, EXCP_BKPT);
break;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
2011-03-30 11:41 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
@ 2011-03-30 11:41 ` Dmitry Eremin-Solenikov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-30 11:41 UTC (permalink / raw)
To: qemu-devel
Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
Implemented:
- IRQs
- GPIO
- PPC
- RTC
- UARTs (no IrDA/etc.)
- OST reused from pxa25x
Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
hw/strongarm.c
V2:
* removed all strongarm variants except latest
* dropped unused casts
* fixed PIC vmstate
* fixed new devices created with version_id = 1
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
Makefile.target | 1 +
hw/strongarm.c | 1301 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/strongarm.h | 62 +++
target-arm/cpu.h | 3 +
target-arm/helper.c | 9 +
5 files changed, 1376 insertions(+), 0 deletions(-)
create mode 100644 hw/strongarm.c
create mode 100644 hw/strongarm.h
diff --git a/Makefile.target b/Makefile.target
index 62b102a..d071a4d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -328,6 +328,7 @@ obj-arm-y += framebuffer.o
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
+obj-arm-y += strongarm.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/strongarm.c b/hw/strongarm.c
new file mode 100644
index 0000000..9f3df87
--- /dev/null
+++ b/hw/strongarm.c
@@ -0,0 +1,1301 @@
+/*
+ * StrongARM SA-1100/SA-1110 emulation
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * Largely based on StrongARM emulation:
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * UART code based on QEMU 16550A UART emulation
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "strongarm.h"
+#include "qemu-error.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+/*
+ TODO
+ - Implement cp15, c14 ?
+ - Implement cp15, c15 !!! (idle used in L)
+ - Implement idle mode handling/DIM
+ - Implement sleep mode/Wake sources
+ - Implement reset control
+ - Implement memory control regs
+ - PCMCIA handling
+ - Maybe support MBGNT/MBREQ
+ - DMA channels
+ - GPCLK
+ - IrDA
+ - MCP
+ - Enhance UART with modem signals
+ */
+
+static struct {
+ target_phys_addr_t io_base;
+ int irq;
+} sa_serial[] = {
+ { 0x80010000, SA_PIC_UART1 },
+ { 0x80030000, SA_PIC_UART2 },
+ { 0x80050000, SA_PIC_UART3 },
+ { 0, 0 }
+};
+
+/* Interrupt Controller */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ uint32_t pending;
+ uint32_t enabled;
+ uint32_t is_fiq;
+ uint32_t int_idle;
+} StrongARMPICState;
+
+#define ICIP 0x00
+#define ICMR 0x04
+#define ICLR 0x08
+#define ICFP 0x10
+#define ICPR 0x20
+#define ICCR 0x0c
+
+#define SA_PIC_SRCS 32
+
+
+static void strongarm_pic_update(void *opaque)
+{
+ StrongARMPICState *s = opaque;
+
+ /* FIXME: reflect DIM */
+ qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
+ qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
+}
+
+static void strongarm_pic_set_irq(void *opaque, int irq, int level)
+{
+ StrongARMPICState *s = opaque;
+
+ if (level) {
+ s->pending |= 1 << irq;
+ } else {
+ s->pending &= ~(1 << irq);
+ }
+
+ strongarm_pic_update(s);
+}
+
+static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICIP:
+ return s->pending & ~s->is_fiq & s->enabled;
+ case ICMR:
+ return s->enabled;
+ case ICLR:
+ return s->is_fiq;
+ case ICCR:
+ return s->int_idle == 0;
+ case ICFP:
+ return s->pending & s->is_fiq & s->enabled;
+ case ICPR:
+ return s->pending;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICMR:
+ s->enabled = value;
+ break;
+ case ICLR:
+ s->is_fiq = value;
+ break;
+ case ICCR:
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ break;
+ }
+ strongarm_pic_update(s);
+}
+
+static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+};
+
+static int strongarm_pic_initfn(SysBusDevice *dev)
+{
+ StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
+ iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
+ strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ return 0;
+}
+
+static int strongarm_pic_post_load(void *opaque, int version_id)
+{
+ strongarm_pic_update(opaque);
+ return 0;
+}
+
+static VMStateDescription vmstate_strongarm_pic_regs = {
+ .name = "strongarm_pic",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_pic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pending, StrongARMPICState),
+ VMSTATE_UINT32(enabled, StrongARMPICState),
+ VMSTATE_UINT32(is_fiq, StrongARMPICState),
+ VMSTATE_UINT32(int_idle, StrongARMPICState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_pic_info = {
+ .init = strongarm_pic_initfn,
+ .qdev.name = "strongarm_pic",
+ .qdev.desc = "StrongARM PIC",
+ .qdev.size = sizeof(StrongARMPICState),
+ .qdev.vmsd = &vmstate_strongarm_pic_regs,
+};
+
+/* Real-Time Clock */
+#define RTAR 0x00 /* RTC Alarm register */
+#define RCNR 0x04 /* RTC Counter register */
+#define RTTR 0x08 /* RTC Timer Trim register */
+#define RTSR 0x0c /* RTC Status register */
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t last_rcnr;
+ int64_t last_hz;
+ QEMUTimer *rtc_alarm;
+ QEMUTimer *rtc_hz;
+ qemu_irq rtc_irq;
+ qemu_irq rtc_hz_irq;
+} StrongARMRTCState;
+
+static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
+{
+ qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
+ qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
+}
+
+static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rt_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_hz);
+ }
+
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
+ qemu_mod_timer(s->rtc_alarm, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_alarm);
+ }
+}
+
+static inline void strongarm_rtc_alarm_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 0);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static inline void strongarm_rtc_hz_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 1);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RCNR:
+ return s->last_rcnr +
+ ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ strongarm_rtc_hzupdate(s);
+ s->rttr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RTSR:
+ if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
+ strongarm_rtc_timer_update(s, value);
+ }
+
+ s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
+ strongarm_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ strongarm_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+};
+
+static int strongarm_rtc_init(SysBusDevice *dev)
+{
+ StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
+ struct tm tm;
+ int iomemtype;
+
+ s->rttr = 0x0;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_hz = qemu_get_clock_ms(rt_clock);
+
+ s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
+ s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
+
+ sysbus_init_irq(dev, &s->rtc_irq);
+ sysbus_init_irq(dev, &s->rtc_hz_irq);
+
+ iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
+ strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+
+ return 0;
+}
+
+static void strongarm_rtc_pre_save(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_hzupdate(s);
+}
+
+static int strongarm_rtc_post_load(void *opaque, int version_id)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_timer_update(s, s->rtsr);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_rtc_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = strongarm_rtc_pre_save,
+ .post_load = strongarm_rtc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rttr, StrongARMRTCState),
+ VMSTATE_UINT32(rtsr, StrongARMRTCState),
+ VMSTATE_UINT32(rtar, StrongARMRTCState),
+ VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
+ VMSTATE_INT64(last_hz, StrongARMRTCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
+ .init = strongarm_rtc_init,
+ .qdev.name = "strongarm-rtc",
+ .qdev.desc = "StrongARM RTC Controller",
+ .qdev.size = sizeof(StrongARMRTCState),
+ .qdev.vmsd = &vmstate_strongarm_rtc_regs,
+};
+
+/* GPIO */
+#define GPLR 0x00
+#define GPDR 0x04
+#define GPSR 0x08
+#define GPCR 0x0c
+#define GRER 0x10
+#define GFER 0x14
+#define GEDR 0x18
+#define GAFR 0x1c
+
+typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
+struct StrongARMGPIOInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+ qemu_irq irqs[11];
+ qemu_irq irqX;
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t rising;
+ uint32_t falling;
+ uint32_t status;
+ uint32_t gpsr;
+ uint32_t gafr;
+
+ uint32_t prev_level;
+};
+
+
+static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
+{
+ int i;
+ for (i = 0; i < 11; i++) {
+ qemu_set_irq(s->irqs[i], s->status & (1 << i));
+ }
+
+ qemu_set_irq(s->irqX, (s->status & ~0x7ff));
+}
+
+static void strongarm_gpio_set(void *opaque, int line, int level)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+ uint32_t mask;
+
+ mask = 1 << line;
+
+ if (level) {
+ s->status |= s->rising & mask &
+ ~s->ilevel & ~s->dir;
+ s->ilevel |= mask;
+ } else {
+ s->status |= s->falling & mask &
+ s->ilevel & ~s->dir;
+ s->ilevel &= ~mask;
+ }
+
+ if (s->status & mask) {
+ strongarm_gpio_irq_update(s);
+ }
+}
+
+static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return s->gpsr; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ return s->gafr;
+
+ case GPLR: /* GPIO Pin-Level registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir);
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_gpio_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir = value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel |= value;
+ strongarm_gpio_handler_update(s);
+ s->gpsr = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel &= ~value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling = value;
+ break;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ s->gafr = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status &= ~value;
+ strongarm_gpio_irq_update(s);
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
+ strongarm_gpio_read,
+ strongarm_gpio_read,
+ strongarm_gpio_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
+ strongarm_gpio_write,
+ strongarm_gpio_write,
+ strongarm_gpio_write
+};
+
+static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
+ DeviceState *pic)
+{
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "strongarm-gpio");
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ for (i = 0; i < 12; i++)
+ sysbus_connect_irq(sysbus_from_qdev(dev), i,
+ qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
+
+ return dev;
+}
+
+static int strongarm_gpio_initfn(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMGPIOInfo *s;
+ int i;
+
+ s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 28);
+
+ iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
+ strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ for (i = 0; i < 11; i++) {
+ sysbus_init_irq(dev, &s->irqs[i]);
+ }
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_gpio_regs = {
+ .name = "strongarm-gpio",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(dir, StrongARMGPIOInfo),
+ VMSTATE_UINT32(rising, StrongARMGPIOInfo),
+ VMSTATE_UINT32(falling, StrongARMGPIOInfo),
+ VMSTATE_UINT32(status, StrongARMGPIOInfo),
+ VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_gpio_info = {
+ .init = strongarm_gpio_initfn,
+ .qdev.name = "strongarm-gpio",
+ .qdev.desc = "StrongARM GPIO controller",
+ .qdev.size = sizeof(StrongARMGPIOInfo),
+};
+
+/* Peripheral Pin Controller */
+#define PPDR 0x00
+#define PPSR 0x04
+#define PPAR 0x08
+#define PSDR 0x0c
+#define PPFR 0x10
+
+typedef struct StrongARMPPCInfo StrongARMPPCInfo;
+struct StrongARMPPCInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t ppar;
+ uint32_t psdr;
+ uint32_t ppfr;
+
+ uint32_t prev_level;
+};
+
+static void strongarm_ppc_set(void *opaque, int line, int level)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ if (level) {
+ s->ilevel |= 1 << line;
+ } else {
+ s->ilevel &= ~(1 << line);
+ }
+}
+
+static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ return s->dir | ~0x3fffff;
+
+ case PPSR: /* PPC Pin State registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir) |
+ ~0x3fffff;
+
+ case PPAR:
+ return s->ppar | ~0x41000;
+
+ case PSDR:
+ return s->psdr;
+
+ case PPFR:
+ return s->ppfr | ~0x7f001;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_ppc_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ s->dir = value & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPSR: /* PPC Pin State registers */
+ s->olevel = value & s->dir & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPAR:
+ s->ppar = value & 0x41000;
+ break;
+
+ case PSDR:
+ s->psdr = value & 0x3fffff;
+ break;
+
+ case PPFR:
+ s->ppfr = value & 0x7f001;
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
+ strongarm_ppc_read,
+ strongarm_ppc_read,
+ strongarm_ppc_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
+ strongarm_ppc_write,
+ strongarm_ppc_write,
+ strongarm_ppc_write
+};
+
+static int strongarm_ppc_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMPPCInfo *s;
+
+ s = FROM_SYSBUS(StrongARMPPCInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 22);
+
+ iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
+ strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_ppc_regs = {
+ .name = "strongarm-ppc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(olevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(dir, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppar, StrongARMPPCInfo),
+ VMSTATE_UINT32(psdr, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_ppc_info = {
+ .init = strongarm_ppc_init,
+ .qdev.name = "strongarm-ppc",
+ .qdev.desc = "StrongARM PPC controller",
+ .qdev.size = sizeof(StrongARMPPCInfo),
+};
+
+/* UART Ports */
+#define UTCR0 0x00
+#define UTCR1 0x04
+#define UTCR2 0x08
+#define UTCR3 0x0c
+#define UTDR 0x14
+#define UTSR0 0x1c
+#define UTSR1 0x20
+
+#define RX_FIFO_PRE (1 << 8)
+#define RX_FIFO_FRE (1 << 9)
+#define RX_FIFO_ROR (1 << 10)
+
+typedef struct {
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t utcr0;
+ uint16_t brd;
+ uint8_t utcr3;
+ uint8_t utsr0;
+ uint8_t utsr1;
+
+ uint8_t tx_fifo[8];
+ uint8_t tx_start;
+ uint8_t tx_len;
+ uint16_t rx_fifo[12]; /* value + error flags in high bits */
+ uint8_t rx_start;
+ uint8_t rx_len;
+
+ uint64_t char_transmit_time; /* time to transmit a char in ticks*/
+ bool wait_break_end;
+ QEMUTimer *rx_timeout_timer;
+ QEMUTimer *tx_timer;
+} StrongARMUARTState;
+
+static void strongarm_uart_update_status(StrongARMUARTState *s)
+{
+ uint16_t utsr1 = 0;
+
+ if (s->tx_len != 8) {
+ utsr1 |= (1 << 2);
+ }
+
+ if (s->rx_len != 0) {
+ uint16_t ent = s->rx_fifo[s->rx_start];
+
+ utsr1 |= (1 << 1);
+ if (ent & RX_FIFO_PRE) {
+ s->utsr1 |= (1 << 3);
+ }
+ if (ent & RX_FIFO_FRE) {
+ s->utsr1 |= (1 << 4);
+ }
+ if (ent & RX_FIFO_ROR) {
+ s->utsr1 |= (1 << 5);
+ }
+ }
+
+ s->utsr1 = utsr1;
+}
+
+static void strongarm_uart_update_int_status(StrongARMUARTState *s)
+{
+ uint16_t utsr0 = s->utsr0 & 0x1c;
+ int i;
+
+ if ((s->utcr3 & (1 << 1)) &&
+ (s->utcr3 & (1 << 4)) &&
+ s->tx_len <= 4) {
+ utsr0 |= (1 << 0);
+ }
+
+ if ((s->utcr3 & (1 << 0)) &&
+ (s->utcr3 & (1 << 3)) &&
+ s->rx_len > 4) {
+ utsr0 |= (1 << 1);
+ }
+
+ for (i = 0; i < s->rx_len && i < 4; i++)
+ if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
+ utsr0 |= (1 << 5);
+ break;
+ }
+
+ s->utsr0 = utsr0;
+ qemu_set_irq(s->irq, utsr0);
+}
+
+static void strongarm_uart_update_parameters(StrongARMUARTState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->utcr0 & (1 << 0)) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->utcr0 & (1 << 1)) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+ if (s->utcr0 & (1 << 2)) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
+ frame_size += data_bits + stop_bits;
+ speed = 3686400 / 16 / (s->brd + 1);
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ if (s->chr) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+ }
+
+#if 0
+ fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
+ speed, parity, data_bits, stop_bits);
+#endif
+}
+
+static void strongarm_uart_rx_to(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len) {
+ s->utsr0 |= (1 << 2);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
+{
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ /* rx disabled */
+ return;
+ }
+
+ if (s->wait_break_end) {
+ s->utsr0 |= (1 << 4); /* REB */
+ s->wait_break_end = false;
+ }
+
+ if (s->rx_len < 12) {
+ s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
+ s->rx_len++;
+ } else
+ s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
+}
+
+static int strongarm_uart_can_receive(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len == 12) {
+ return 0;
+ }
+ /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */
+ if (s->rx_len < 8) {
+ return 8 - s->rx_len;
+ }
+ return 1;
+}
+
+static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ StrongARMUARTState *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ strongarm_uart_rx_push(s, buf[i]);
+ }
+
+ /* call the timeout receive callback in 3 char transmit time */
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static void strongarm_uart_event(void *opaque, int event)
+{
+ StrongARMUARTState *s = opaque;
+ if (event == CHR_EVENT_BREAK) {
+ s->utcr3 |= (1 << 3);
+ strongarm_uart_rx_push(s, RX_FIFO_FRE);
+ s->wait_break_end = true;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_tx(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+ uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ if (s->utcr3 & (1 << 5)) /* loopback */ {
+ strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
+ } else if (s->chr) {
+ qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
+ }
+
+ s->tx_start = (s->tx_start + 1) % 8;
+ s->tx_len--;
+ if (s->tx_len) {
+ qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMUARTState *s = opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case UTCR0:
+ return s->utcr0;
+
+ case UTCR1:
+ return s->brd >> 8;
+
+ case UTCR2:
+ return s->brd & 0xff;
+
+ case UTCR3:
+ return s->utcr3;
+
+ case UTDR:
+ if (s->rx_len != 0) {
+ ret = s->rx_fifo[s->rx_start];
+ s->rx_start = (s->rx_start + 1) % 12;
+ s->rx_len--;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ return ret;
+ }
+ return 0;
+
+ case UTSR0:
+ return s->utsr0;
+
+ case UTSR1:
+ return s->utsr1;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMUARTState *s = opaque;
+
+ switch (addr) {
+ case UTCR0:
+ s->utcr0 = value & 0x7f;
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR1:
+ s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR2:
+ s->brd = (s->brd & 0xf00) | (value & 0xff);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR3:
+ s->utcr3 = value & 0x3f;
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ s->rx_len = 0;
+ }
+ if ((s->utcr3 & (1 << 1)) == 0) {
+ s->tx_len = 0;
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ case UTDR:
+ if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
+ s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
+ s->tx_len++;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ if (s->tx_len == 1) {
+ strongarm_uart_tx(s);
+ }
+ }
+ break;
+
+ case UTSR0:
+ s->utsr0 = s->utsr0 & ~(value & 0x1c);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
+ strongarm_uart_read,
+ strongarm_uart_read,
+ strongarm_uart_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
+ strongarm_uart_write,
+ strongarm_uart_write,
+ strongarm_uart_write,
+};
+
+static int strongarm_uart_init(SysBusDevice *dev)
+{
+ StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
+ strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s);
+ s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr,
+ strongarm_uart_can_receive,
+ strongarm_uart_receive,
+ strongarm_uart_event,
+ s);
+ }
+
+ return 0;
+}
+
+static void strongarm_uart_reset(DeviceState *dev)
+{
+ StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
+
+ s->utcr0 = 0x8; /* 8 data, no parity */
+ s->brd = 23; /* 9600 */
+ s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
+
+ s->rx_len = s->tx_len = 0;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static int strongarm_uart_post_load(void *opaque, int version_id)
+{
+ StrongARMUARTState *s = opaque;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+
+ /* tx and restart timer */
+ if (s->tx_len) {
+ strongarm_uart_tx(s);
+ }
+
+ /* restart rx timeout timer */
+ if (s->rx_len) {
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_uart_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(utcr0, StrongARMUARTState),
+ VMSTATE_UINT16(brd, StrongARMUARTState),
+ VMSTATE_UINT8(utcr3, StrongARMUARTState),
+ VMSTATE_UINT8(utsr0, StrongARMUARTState),
+ VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
+ VMSTATE_UINT8(tx_start, StrongARMUARTState),
+ VMSTATE_UINT8(tx_len, StrongARMUARTState),
+ VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
+ VMSTATE_UINT8(rx_start, StrongARMUARTState),
+ VMSTATE_UINT8(rx_len, StrongARMUARTState),
+ VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_uart_info = {
+ .init = strongarm_uart_init,
+ .qdev.name = "strongarm-uart",
+ .qdev.desc = "StrongARM UART controller",
+ .qdev.size = sizeof(StrongARMUARTState),
+ .qdev.reset = strongarm_uart_reset,
+ .qdev.vmsd = &vmstate_strongarm_uart_regs,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+/* Main CPU functions */
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
+{
+ StrongARMState *s;
+ qemu_irq *pic;
+ int i;
+
+ s = qemu_mallocz(sizeof(StrongARMState));
+
+ if (!rev) {
+ rev = "sa1110-b5";
+ }
+
+ if (strncmp(rev, "sa1110", 6)) {
+ error_report("Machine requires a SA1110 processor.\n");
+ exit(1);
+ }
+
+ s->env = cpu_init(rev);
+
+ if (!s->env) {
+ error_report("Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ cpu_register_physical_memory(SA_SDCS0,
+ sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
+ sdram_size) | IO_MEM_RAM);
+
+ pic = arm_pic_init_cpu(s->env);
+ s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
+ pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
+
+ sysbus_create_varargs("pxa25x-timer", 0x90000000,
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
+ NULL);
+
+ sysbus_create_simple("strongarm-rtc", 0x90010000,
+ qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
+
+ s->gpio = strongarm_gpio_init(0x90040000, s->pic);
+
+ s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
+
+ for (i = 0; sa_serial[i].io_base; i++) {
+ DeviceState *dev = qdev_create(NULL, "strongarm-uart");
+ qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ sa_serial[i].io_base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0,
+ qdev_get_gpio_in(s->pic, sa_serial[i].irq));
+ }
+
+ return s;
+}
+
+static void strongarm_register_devices(void)
+{
+ sysbus_register_withprop(&strongarm_pic_info);
+ sysbus_register_withprop(&strongarm_rtc_sysbus_info);
+ sysbus_register_withprop(&strongarm_gpio_info);
+ sysbus_register_withprop(&strongarm_ppc_info);
+ sysbus_register_withprop(&strongarm_uart_info);
+}
+device_init(strongarm_register_devices)
diff --git a/hw/strongarm.h b/hw/strongarm.h
new file mode 100644
index 0000000..ce7a3d7
--- /dev/null
+++ b/hw/strongarm.h
@@ -0,0 +1,62 @@
+#ifndef _STRONGARM_H
+#define _STRONGARM_H
+
+#define SA_CS0 0x00000000
+#define SA_CS1 0x08000000
+#define SA_CS2 0x10000000
+#define SA_CS3 0x18000000
+#define SA_PCMCIA_CS0 0x20000000
+#define SA_PCMCIA_CS1 0x30000000
+#define SA_CS4 0x40000000
+#define SA_CS5 0x48000000
+/* system registers here */
+#define SA_SDCS0 0xc0000000
+#define SA_SDCS1 0xc8000000
+#define SA_SDCS2 0xd0000000
+#define SA_SDCS3 0xd8000000
+
+enum {
+ SA_PIC_GPIO0_EDGE = 0,
+ SA_PIC_GPIO1_EDGE,
+ SA_PIC_GPIO2_EDGE,
+ SA_PIC_GPIO3_EDGE,
+ SA_PIC_GPIO4_EDGE,
+ SA_PIC_GPIO5_EDGE,
+ SA_PIC_GPIO6_EDGE,
+ SA_PIC_GPIO7_EDGE,
+ SA_PIC_GPIO8_EDGE,
+ SA_PIC_GPIO9_EDGE,
+ SA_PIC_GPIO10_EDGE,
+ SA_PIC_GPIOX_EDGE,
+ SA_PIC_LCD,
+ SA_PIC_UDC,
+ SA_PIC_RSVD1,
+ SA_PIC_UART1,
+ SA_PIC_UART2,
+ SA_PIC_UART3,
+ SA_PIC_MCP,
+ SA_PIC_SSP,
+ SA_PIC_DMA_CH0,
+ SA_PIC_DMA_CH1,
+ SA_PIC_DMA_CH2,
+ SA_PIC_DMA_CH3,
+ SA_PIC_DMA_CH4,
+ SA_PIC_DMA_CH5,
+ SA_PIC_OSTC0,
+ SA_PIC_OSTC1,
+ SA_PIC_OSTC2,
+ SA_PIC_OSTC3,
+ SA_PIC_RTC_HZ,
+ SA_PIC_RTC_ALARM,
+};
+
+typedef struct {
+ CPUState *env;
+ DeviceState *pic;
+ DeviceState *gpio;
+ DeviceState *ppc;
+} StrongARMState;
+
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
+
+#endif
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index e247a7a..d5af644 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -363,6 +363,7 @@ enum arm_features {
ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
ARM_FEATURE_V4T,
ARM_FEATURE_V5,
+ ARM_FEATURE_STRONGARM,
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
+#define ARM_CPUID_SA1100 0x4401A11B
+#define ARM_CPUID_SA1110 0x6901B119
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 44b5c17..d2e0f63 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -214,6 +214,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0xd172172;
env->cp15.c1_sys = 0x00000078;
break;
+ case ARM_CPUID_SA1100:
+ case ARM_CPUID_SA1110:
+ set_feature(env, ARM_FEATURE_STRONGARM);
+ env->cp15.c1_sys = 0x00000070;
+ break;
default:
cpu_abort(env, "Bad CPU ID: %x\n", id);
break;
@@ -374,6 +379,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
{ ARM_CPUID_CORTEXA9, "cortex-a9"},
{ ARM_CPUID_TI925T, "ti925t" },
{ ARM_CPUID_PXA250, "pxa250" },
+ { ARM_CPUID_SA1100, "sa1100" },
+ { ARM_CPUID_SA1110, "sa1110" },
{ ARM_CPUID_PXA255, "pxa255" },
{ ARM_CPUID_PXA260, "pxa260" },
{ ARM_CPUID_PXA261, "pxa261" },
@@ -1548,6 +1555,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 9:
if (arm_feature(env, ARM_FEATURE_OMAPCP))
break;
+ if (arm_feature(env, ARM_FEATURE_STRONGARM))
+ break; /* Ignore ReadBuffer access */
switch (crm) {
case 0: /* Cache lockdown. */
switch (op1) {
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
@ 2011-03-29 16:58 Dmitry Eremin-Solenikov
2011-03-29 16:58 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-29 16:58 UTC (permalink / raw)
To: qemu-devel
Currently target-arm/ assumes at least ARMv5 core. Add support for
handling also ARMv4/ARMv4T. This changes the following instructions:
BX(v4T and later)
BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
SMULxy, SMULWxy, STC2 (v5 and later)
All instructions that are "v5TE and later" are also bound to just v5, as
that's how it was before.
This patch doesn _not_ include disabling of cp15 access and base-updated
data abort model (that will be required to emulate chips based on a
ARM7TDMI), because:
* no ARM7TDMI chips are currently emulated (or planned)
* those features aren't strictly necessary for my purposes (SA-1 core
emulation).
All v5 models are handled as they are v5T. Internally we still have a
check if the model is a v5(T) or v5TE, but as all emulated cores are
v5TE, those two cases are simply aliased (for now).
Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
Sanders <vince@kyllikki.org>.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
target-arm/cpu.h | 4 +++-
target-arm/helper.c | 23 +++++++++++++++++++++++
target-arm/translate.c | 42 +++++++++++++++++++++++++++++++++++++-----
3 files changed, 63 insertions(+), 6 deletions(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1ae7982..e247a7a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -360,7 +360,9 @@ enum arm_features {
ARM_FEATURE_M, /* Microcontroller profile. */
ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
ARM_FEATURE_THUMB2EE,
- ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V4T,
+ ARM_FEATURE_V5,
};
static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 78f3d39..261e148 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cpuid = id;
switch (id) {
case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
env->cp15.c0_cachetype = 0x1dd20d2;
env->cp15.c1_sys = 0x00090078;
break;
case ARM_CPUID_ARM946:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_MPU);
env->cp15.c0_cachetype = 0x0f004006;
env->cp15.c1_sys = 0x00000078;
break;
case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
@@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_ARM1136_R2:
case ARM_CPUID_ARM1136:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
@@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00050078;
break;
case ARM_CPUID_ARM11MPCORE:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_VFP);
@@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0x1dd20d2;
break;
case ARM_CPUID_CORTEXA8:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXA9:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXM3:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_THUMB2);
set_feature(env, ARM_FEATURE_V7);
@@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_DIV);
break;
case ARM_CPUID_ANY: /* For userspace emulation. */
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_TI915T:
case ARM_CPUID_TI925T:
+ set_feature(env, ARM_FEATURE_V4T);
set_feature(env, ARM_FEATURE_OMAPCP);
env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
env->cp15.c0_cachetype = 0x5109149;
@@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA260:
case ARM_CPUID_PXA261:
case ARM_CPUID_PXA262:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
env->cp15.c0_cachetype = 0xd172172;
@@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA270_B1:
case ARM_CPUID_PXA270_C0:
case ARM_CPUID_PXA270_C5:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
set_feature(env, ARM_FEATURE_IWMMXT);
diff --git a/target-arm/translate.c b/target-arm/translate.c
index f69912f..ea7e777 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,6 +34,10 @@
#define GEN_HELPER 1
#include "helpers.h"
+#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
+#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
+/* currently all emulated v5 cores are also v5TE, so don't bother */
+#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5)
#define ENABLE_ARCH_5J 0
#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
@@ -6101,6 +6105,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
cond = insn >> 28;
if (cond == 0xf){
+ /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
+ * choose to UNDEF. In ARMv5 and above the space is used
+ * for miscellaneous unconditional instructions.
+ */
+ ARCH(5);
+
/* Unconditional instructions. */
if (((insn >> 25) & 7) == 1) {
/* NEON Data processing. */
@@ -6129,6 +6139,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
}
/* Otherwise PLD; v5TE+ */
+ ARCH(5TE);
return;
}
if (((insn & 0x0f70f000) == 0x0450f000) ||
@@ -6276,6 +6287,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
} else if ((insn & 0x0fe00000) == 0x0c400000) {
/* Coprocessor double register transfer. */
+ ARCH(5TE)
} else if ((insn & 0x0f000010) == 0x0e000010) {
/* Additional coprocessor register transfer. */
} else if ((insn & 0x0ff10020) == 0x01000000) {
@@ -6376,10 +6388,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
case 0x1:
if (op1 == 1) {
/* branch/exchange thumb (bx). */
+ ARCH(4T);
tmp = load_reg(s, rm);
gen_bx(s, tmp);
} else if (op1 == 3) {
/* clz */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
tmp = load_reg(s, rm);
gen_helper_clz(tmp, tmp);
@@ -6402,6 +6416,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (op1 != 1)
goto illegal_op;
+ ARCH(5);
/* branch link/exchange thumb (blx) */
tmp = load_reg(s, rm);
tmp2 = tcg_temp_new_i32();
@@ -6410,6 +6425,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx(s, tmp);
break;
case 0x5: /* saturating add/subtract */
+ ARCH(5TE);
rd = (insn >> 12) & 0xf;
rn = (insn >> 16) & 0xf;
tmp = load_reg(s, rm);
@@ -6431,12 +6447,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
}
/* bkpt */
+ ARCH(5);
gen_exception_insn(s, 4, EXCP_BKPT);
break;
case 0x8: /* signed multiply */
case 0xa:
case 0xc:
case 0xe:
+ ARCH(5TE);
rs = (insn >> 8) & 0xf;
rn = (insn >> 12) & 0xf;
rd = (insn >> 16) & 0xf;
@@ -6832,6 +6850,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
load = 1;
} else if (sh & 2) {
+ ARCH(5TE);
/* doubleword */
if (sh & 1) {
/* store */
@@ -7172,10 +7191,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
if (insn & (1 << 20)) {
/* Complete the load. */
- if (rd == 15)
+ if (rd == 15 && ENABLE_ARCH_4T) {
gen_bx(s, tmp);
- else
+ } else {
store_reg(s, rd, tmp);
+ }
}
break;
case 0x08:
@@ -7229,7 +7249,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
/* load */
tmp = gen_ld32(addr, IS_USER(s));
if (i == 15) {
- gen_bx(s, tmp);
+ if (ENABLE_ARCH_5) {
+ gen_bx(s, tmp);
+ } else {
+ store_reg(s, i, tmp);
+ }
} else if (user) {
tmp2 = tcg_const_i32(i);
gen_helper_set_user_reg(tmp2, tmp);
@@ -7439,6 +7463,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
16-bit instructions to get correct prefetch abort behavior. */
insn = insn_hw1;
if ((insn & (1 << 12)) == 0) {
+ ARCH(5);
/* Second half of blx. */
offset = ((insn & 0x7ff) << 1);
tmp = load_reg(s, 14);
@@ -8616,6 +8641,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
case 3:/* branch [and link] exchange thumb register */
tmp = load_reg(s, rm);
if (insn & (1 << 7)) {
+ ARCH(5);
val = (uint32_t)s->pc | 1;
tmp2 = tcg_temp_new_i32();
tcg_gen_movi_i32(tmp2, val);
@@ -8980,8 +9006,13 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
/* write back the new stack pointer */
store_reg(s, 13, addr);
/* set the new PC value */
- if ((insn & 0x0900) == 0x0900)
- gen_bx(s, tmp);
+ if ((insn & 0x0900) == 0x0900) {
+ if (ENABLE_ARCH_5) {
+ gen_bx(s, tmp);
+ } else {
+ store_reg(s, 15, tmp);
+ }
+ }
break;
case 1: case 3: case 9: case 11: /* czb */
@@ -9012,6 +9043,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
break;
case 0xe: /* bkpt */
+ ARCH(5);
gen_exception_insn(s, 2, EXCP_BKPT);
break;
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
2011-03-29 16:58 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
@ 2011-03-29 16:58 ` Dmitry Eremin-Solenikov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-29 16:58 UTC (permalink / raw)
To: qemu-devel
Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
Implemented:
- IRQs
- GPIO
- PPC
- RTC
- UARTs (no IrDA/etc.)
- OST reused from pxa25x
Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
hw/strongarm.c
V2:
* removed all strongarm variants except latest
* dropped unused casts
* fixed PIC vmstate
* fixed new devices created with version_id = 1
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
Makefile.target | 1 +
hw/strongarm.c | 1301 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/strongarm.h | 62 +++
target-arm/cpu.h | 3 +
target-arm/helper.c | 9 +
5 files changed, 1376 insertions(+), 0 deletions(-)
create mode 100644 hw/strongarm.c
create mode 100644 hw/strongarm.h
diff --git a/Makefile.target b/Makefile.target
index 62b102a..d071a4d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -328,6 +328,7 @@ obj-arm-y += framebuffer.o
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
+obj-arm-y += strongarm.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/strongarm.c b/hw/strongarm.c
new file mode 100644
index 0000000..9f3df87
--- /dev/null
+++ b/hw/strongarm.c
@@ -0,0 +1,1301 @@
+/*
+ * StrongARM SA-1100/SA-1110 emulation
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * Largely based on StrongARM emulation:
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * UART code based on QEMU 16550A UART emulation
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "strongarm.h"
+#include "qemu-error.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+/*
+ TODO
+ - Implement cp15, c14 ?
+ - Implement cp15, c15 !!! (idle used in L)
+ - Implement idle mode handling/DIM
+ - Implement sleep mode/Wake sources
+ - Implement reset control
+ - Implement memory control regs
+ - PCMCIA handling
+ - Maybe support MBGNT/MBREQ
+ - DMA channels
+ - GPCLK
+ - IrDA
+ - MCP
+ - Enhance UART with modem signals
+ */
+
+static struct {
+ target_phys_addr_t io_base;
+ int irq;
+} sa_serial[] = {
+ { 0x80010000, SA_PIC_UART1 },
+ { 0x80030000, SA_PIC_UART2 },
+ { 0x80050000, SA_PIC_UART3 },
+ { 0, 0 }
+};
+
+/* Interrupt Controller */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ uint32_t pending;
+ uint32_t enabled;
+ uint32_t is_fiq;
+ uint32_t int_idle;
+} StrongARMPICState;
+
+#define ICIP 0x00
+#define ICMR 0x04
+#define ICLR 0x08
+#define ICFP 0x10
+#define ICPR 0x20
+#define ICCR 0x0c
+
+#define SA_PIC_SRCS 32
+
+
+static void strongarm_pic_update(void *opaque)
+{
+ StrongARMPICState *s = opaque;
+
+ /* FIXME: reflect DIM */
+ qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
+ qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
+}
+
+static void strongarm_pic_set_irq(void *opaque, int irq, int level)
+{
+ StrongARMPICState *s = opaque;
+
+ if (level) {
+ s->pending |= 1 << irq;
+ } else {
+ s->pending &= ~(1 << irq);
+ }
+
+ strongarm_pic_update(s);
+}
+
+static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICIP:
+ return s->pending & ~s->is_fiq & s->enabled;
+ case ICMR:
+ return s->enabled;
+ case ICLR:
+ return s->is_fiq;
+ case ICCR:
+ return s->int_idle == 0;
+ case ICFP:
+ return s->pending & s->is_fiq & s->enabled;
+ case ICPR:
+ return s->pending;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICMR:
+ s->enabled = value;
+ break;
+ case ICLR:
+ s->is_fiq = value;
+ break;
+ case ICCR:
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ break;
+ }
+ strongarm_pic_update(s);
+}
+
+static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+};
+
+static int strongarm_pic_initfn(SysBusDevice *dev)
+{
+ StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
+ iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
+ strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ return 0;
+}
+
+static int strongarm_pic_post_load(void *opaque, int version_id)
+{
+ strongarm_pic_update(opaque);
+ return 0;
+}
+
+static VMStateDescription vmstate_strongarm_pic_regs = {
+ .name = "strongarm_pic",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_pic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pending, StrongARMPICState),
+ VMSTATE_UINT32(enabled, StrongARMPICState),
+ VMSTATE_UINT32(is_fiq, StrongARMPICState),
+ VMSTATE_UINT32(int_idle, StrongARMPICState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_pic_info = {
+ .init = strongarm_pic_initfn,
+ .qdev.name = "strongarm_pic",
+ .qdev.desc = "StrongARM PIC",
+ .qdev.size = sizeof(StrongARMPICState),
+ .qdev.vmsd = &vmstate_strongarm_pic_regs,
+};
+
+/* Real-Time Clock */
+#define RTAR 0x00 /* RTC Alarm register */
+#define RCNR 0x04 /* RTC Counter register */
+#define RTTR 0x08 /* RTC Timer Trim register */
+#define RTSR 0x0c /* RTC Status register */
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t last_rcnr;
+ int64_t last_hz;
+ QEMUTimer *rtc_alarm;
+ QEMUTimer *rtc_hz;
+ qemu_irq rtc_irq;
+ qemu_irq rtc_hz_irq;
+} StrongARMRTCState;
+
+static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
+{
+ qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
+ qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
+}
+
+static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rt_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_hz);
+ }
+
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
+ qemu_mod_timer(s->rtc_alarm, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_alarm);
+ }
+}
+
+static inline void strongarm_rtc_alarm_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 0);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static inline void strongarm_rtc_hz_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 1);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RCNR:
+ return s->last_rcnr +
+ ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ strongarm_rtc_hzupdate(s);
+ s->rttr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RTSR:
+ if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
+ strongarm_rtc_timer_update(s, value);
+ }
+
+ s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
+ strongarm_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ strongarm_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+};
+
+static int strongarm_rtc_init(SysBusDevice *dev)
+{
+ StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
+ struct tm tm;
+ int iomemtype;
+
+ s->rttr = 0x0;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_hz = qemu_get_clock_ms(rt_clock);
+
+ s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
+ s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
+
+ sysbus_init_irq(dev, &s->rtc_irq);
+ sysbus_init_irq(dev, &s->rtc_hz_irq);
+
+ iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
+ strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+
+ return 0;
+}
+
+static void strongarm_rtc_pre_save(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_hzupdate(s);
+}
+
+static int strongarm_rtc_post_load(void *opaque, int version_id)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_timer_update(s, s->rtsr);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_rtc_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = strongarm_rtc_pre_save,
+ .post_load = strongarm_rtc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rttr, StrongARMRTCState),
+ VMSTATE_UINT32(rtsr, StrongARMRTCState),
+ VMSTATE_UINT32(rtar, StrongARMRTCState),
+ VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
+ VMSTATE_INT64(last_hz, StrongARMRTCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
+ .init = strongarm_rtc_init,
+ .qdev.name = "strongarm-rtc",
+ .qdev.desc = "StrongARM RTC Controller",
+ .qdev.size = sizeof(StrongARMRTCState),
+ .qdev.vmsd = &vmstate_strongarm_rtc_regs,
+};
+
+/* GPIO */
+#define GPLR 0x00
+#define GPDR 0x04
+#define GPSR 0x08
+#define GPCR 0x0c
+#define GRER 0x10
+#define GFER 0x14
+#define GEDR 0x18
+#define GAFR 0x1c
+
+typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
+struct StrongARMGPIOInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+ qemu_irq irqs[11];
+ qemu_irq irqX;
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t rising;
+ uint32_t falling;
+ uint32_t status;
+ uint32_t gpsr;
+ uint32_t gafr;
+
+ uint32_t prev_level;
+};
+
+
+static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
+{
+ int i;
+ for (i = 0; i < 11; i++) {
+ qemu_set_irq(s->irqs[i], s->status & (1 << i));
+ }
+
+ qemu_set_irq(s->irqX, (s->status & ~0x7ff));
+}
+
+static void strongarm_gpio_set(void *opaque, int line, int level)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+ uint32_t mask;
+
+ mask = 1 << line;
+
+ if (level) {
+ s->status |= s->rising & mask &
+ ~s->ilevel & ~s->dir;
+ s->ilevel |= mask;
+ } else {
+ s->status |= s->falling & mask &
+ s->ilevel & ~s->dir;
+ s->ilevel &= ~mask;
+ }
+
+ if (s->status & mask) {
+ strongarm_gpio_irq_update(s);
+ }
+}
+
+static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return s->gpsr; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ return s->gafr;
+
+ case GPLR: /* GPIO Pin-Level registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir);
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_gpio_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir = value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel |= value;
+ strongarm_gpio_handler_update(s);
+ s->gpsr = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel &= ~value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling = value;
+ break;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ s->gafr = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status &= ~value;
+ strongarm_gpio_irq_update(s);
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
+ strongarm_gpio_read,
+ strongarm_gpio_read,
+ strongarm_gpio_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
+ strongarm_gpio_write,
+ strongarm_gpio_write,
+ strongarm_gpio_write
+};
+
+static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
+ DeviceState *pic)
+{
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "strongarm-gpio");
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ for (i = 0; i < 12; i++)
+ sysbus_connect_irq(sysbus_from_qdev(dev), i,
+ qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
+
+ return dev;
+}
+
+static int strongarm_gpio_initfn(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMGPIOInfo *s;
+ int i;
+
+ s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 28);
+
+ iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
+ strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ for (i = 0; i < 11; i++) {
+ sysbus_init_irq(dev, &s->irqs[i]);
+ }
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_gpio_regs = {
+ .name = "strongarm-gpio",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(dir, StrongARMGPIOInfo),
+ VMSTATE_UINT32(rising, StrongARMGPIOInfo),
+ VMSTATE_UINT32(falling, StrongARMGPIOInfo),
+ VMSTATE_UINT32(status, StrongARMGPIOInfo),
+ VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_gpio_info = {
+ .init = strongarm_gpio_initfn,
+ .qdev.name = "strongarm-gpio",
+ .qdev.desc = "StrongARM GPIO controller",
+ .qdev.size = sizeof(StrongARMGPIOInfo),
+};
+
+/* Peripheral Pin Controller */
+#define PPDR 0x00
+#define PPSR 0x04
+#define PPAR 0x08
+#define PSDR 0x0c
+#define PPFR 0x10
+
+typedef struct StrongARMPPCInfo StrongARMPPCInfo;
+struct StrongARMPPCInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t ppar;
+ uint32_t psdr;
+ uint32_t ppfr;
+
+ uint32_t prev_level;
+};
+
+static void strongarm_ppc_set(void *opaque, int line, int level)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ if (level) {
+ s->ilevel |= 1 << line;
+ } else {
+ s->ilevel &= ~(1 << line);
+ }
+}
+
+static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ return s->dir | ~0x3fffff;
+
+ case PPSR: /* PPC Pin State registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir) |
+ ~0x3fffff;
+
+ case PPAR:
+ return s->ppar | ~0x41000;
+
+ case PSDR:
+ return s->psdr;
+
+ case PPFR:
+ return s->ppfr | ~0x7f001;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_ppc_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ s->dir = value & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPSR: /* PPC Pin State registers */
+ s->olevel = value & s->dir & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPAR:
+ s->ppar = value & 0x41000;
+ break;
+
+ case PSDR:
+ s->psdr = value & 0x3fffff;
+ break;
+
+ case PPFR:
+ s->ppfr = value & 0x7f001;
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
+ strongarm_ppc_read,
+ strongarm_ppc_read,
+ strongarm_ppc_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
+ strongarm_ppc_write,
+ strongarm_ppc_write,
+ strongarm_ppc_write
+};
+
+static int strongarm_ppc_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMPPCInfo *s;
+
+ s = FROM_SYSBUS(StrongARMPPCInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 22);
+
+ iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
+ strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_ppc_regs = {
+ .name = "strongarm-ppc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(olevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(dir, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppar, StrongARMPPCInfo),
+ VMSTATE_UINT32(psdr, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_ppc_info = {
+ .init = strongarm_ppc_init,
+ .qdev.name = "strongarm-ppc",
+ .qdev.desc = "StrongARM PPC controller",
+ .qdev.size = sizeof(StrongARMPPCInfo),
+};
+
+/* UART Ports */
+#define UTCR0 0x00
+#define UTCR1 0x04
+#define UTCR2 0x08
+#define UTCR3 0x0c
+#define UTDR 0x14
+#define UTSR0 0x1c
+#define UTSR1 0x20
+
+#define RX_FIFO_PRE (1 << 8)
+#define RX_FIFO_FRE (1 << 9)
+#define RX_FIFO_ROR (1 << 10)
+
+typedef struct {
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t utcr0;
+ uint16_t brd;
+ uint8_t utcr3;
+ uint8_t utsr0;
+ uint8_t utsr1;
+
+ uint8_t tx_fifo[8];
+ uint8_t tx_start;
+ uint8_t tx_len;
+ uint16_t rx_fifo[12]; /* value + error flags in high bits */
+ uint8_t rx_start;
+ uint8_t rx_len;
+
+ uint64_t char_transmit_time; /* time to transmit a char in ticks*/
+ bool wait_break_end;
+ QEMUTimer *rx_timeout_timer;
+ QEMUTimer *tx_timer;
+} StrongARMUARTState;
+
+static void strongarm_uart_update_status(StrongARMUARTState *s)
+{
+ uint16_t utsr1 = 0;
+
+ if (s->tx_len != 8) {
+ utsr1 |= (1 << 2);
+ }
+
+ if (s->rx_len != 0) {
+ uint16_t ent = s->rx_fifo[s->rx_start];
+
+ utsr1 |= (1 << 1);
+ if (ent & RX_FIFO_PRE) {
+ s->utsr1 |= (1 << 3);
+ }
+ if (ent & RX_FIFO_FRE) {
+ s->utsr1 |= (1 << 4);
+ }
+ if (ent & RX_FIFO_ROR) {
+ s->utsr1 |= (1 << 5);
+ }
+ }
+
+ s->utsr1 = utsr1;
+}
+
+static void strongarm_uart_update_int_status(StrongARMUARTState *s)
+{
+ uint16_t utsr0 = s->utsr0 & 0x1c;
+ int i;
+
+ if ((s->utcr3 & (1 << 1)) &&
+ (s->utcr3 & (1 << 4)) &&
+ s->tx_len <= 4) {
+ utsr0 |= (1 << 0);
+ }
+
+ if ((s->utcr3 & (1 << 0)) &&
+ (s->utcr3 & (1 << 3)) &&
+ s->rx_len > 4) {
+ utsr0 |= (1 << 1);
+ }
+
+ for (i = 0; i < s->rx_len && i < 4; i++)
+ if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
+ utsr0 |= (1 << 5);
+ break;
+ }
+
+ s->utsr0 = utsr0;
+ qemu_set_irq(s->irq, utsr0);
+}
+
+static void strongarm_uart_update_parameters(StrongARMUARTState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->utcr0 & (1 << 0)) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->utcr0 & (1 << 1)) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+ if (s->utcr0 & (1 << 2)) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
+ frame_size += data_bits + stop_bits;
+ speed = 3686400 / 16 / (s->brd + 1);
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ if (s->chr) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+ }
+
+#if 0
+ fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
+ speed, parity, data_bits, stop_bits);
+#endif
+}
+
+static void strongarm_uart_rx_to(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len) {
+ s->utsr0 |= (1 << 2);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
+{
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ /* rx disabled */
+ return;
+ }
+
+ if (s->wait_break_end) {
+ s->utsr0 |= (1 << 4); /* REB */
+ s->wait_break_end = false;
+ }
+
+ if (s->rx_len < 12) {
+ s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
+ s->rx_len++;
+ } else
+ s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
+}
+
+static int strongarm_uart_can_receive(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len == 12) {
+ return 0;
+ }
+ /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */
+ if (s->rx_len < 8) {
+ return 8 - s->rx_len;
+ }
+ return 1;
+}
+
+static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ StrongARMUARTState *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ strongarm_uart_rx_push(s, buf[i]);
+ }
+
+ /* call the timeout receive callback in 3 char transmit time */
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static void strongarm_uart_event(void *opaque, int event)
+{
+ StrongARMUARTState *s = opaque;
+ if (event == CHR_EVENT_BREAK) {
+ s->utcr3 |= (1 << 3);
+ strongarm_uart_rx_push(s, RX_FIFO_FRE);
+ s->wait_break_end = true;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_tx(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+ uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ if (s->utcr3 & (1 << 5)) /* loopback */ {
+ strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
+ } else if (s->chr) {
+ qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
+ }
+
+ s->tx_start = (s->tx_start + 1) % 8;
+ s->tx_len--;
+ if (s->tx_len) {
+ qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMUARTState *s = opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case UTCR0:
+ return s->utcr0;
+
+ case UTCR1:
+ return s->brd >> 8;
+
+ case UTCR2:
+ return s->brd & 0xff;
+
+ case UTCR3:
+ return s->utcr3;
+
+ case UTDR:
+ if (s->rx_len != 0) {
+ ret = s->rx_fifo[s->rx_start];
+ s->rx_start = (s->rx_start + 1) % 12;
+ s->rx_len--;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ return ret;
+ }
+ return 0;
+
+ case UTSR0:
+ return s->utsr0;
+
+ case UTSR1:
+ return s->utsr1;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMUARTState *s = opaque;
+
+ switch (addr) {
+ case UTCR0:
+ s->utcr0 = value & 0x7f;
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR1:
+ s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR2:
+ s->brd = (s->brd & 0xf00) | (value & 0xff);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR3:
+ s->utcr3 = value & 0x3f;
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ s->rx_len = 0;
+ }
+ if ((s->utcr3 & (1 << 1)) == 0) {
+ s->tx_len = 0;
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ case UTDR:
+ if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
+ s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
+ s->tx_len++;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ if (s->tx_len == 1) {
+ strongarm_uart_tx(s);
+ }
+ }
+ break;
+
+ case UTSR0:
+ s->utsr0 = s->utsr0 & ~(value & 0x1c);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
+ strongarm_uart_read,
+ strongarm_uart_read,
+ strongarm_uart_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
+ strongarm_uart_write,
+ strongarm_uart_write,
+ strongarm_uart_write,
+};
+
+static int strongarm_uart_init(SysBusDevice *dev)
+{
+ StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
+ strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s);
+ s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr,
+ strongarm_uart_can_receive,
+ strongarm_uart_receive,
+ strongarm_uart_event,
+ s);
+ }
+
+ return 0;
+}
+
+static void strongarm_uart_reset(DeviceState *dev)
+{
+ StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
+
+ s->utcr0 = 0x8; /* 8 data, no parity */
+ s->brd = 23; /* 9600 */
+ s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
+
+ s->rx_len = s->tx_len = 0;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static int strongarm_uart_post_load(void *opaque, int version_id)
+{
+ StrongARMUARTState *s = opaque;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+
+ /* tx and restart timer */
+ if (s->tx_len) {
+ strongarm_uart_tx(s);
+ }
+
+ /* restart rx timeout timer */
+ if (s->rx_len) {
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_uart_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(utcr0, StrongARMUARTState),
+ VMSTATE_UINT16(brd, StrongARMUARTState),
+ VMSTATE_UINT8(utcr3, StrongARMUARTState),
+ VMSTATE_UINT8(utsr0, StrongARMUARTState),
+ VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
+ VMSTATE_UINT8(tx_start, StrongARMUARTState),
+ VMSTATE_UINT8(tx_len, StrongARMUARTState),
+ VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
+ VMSTATE_UINT8(rx_start, StrongARMUARTState),
+ VMSTATE_UINT8(rx_len, StrongARMUARTState),
+ VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_uart_info = {
+ .init = strongarm_uart_init,
+ .qdev.name = "strongarm-uart",
+ .qdev.desc = "StrongARM UART controller",
+ .qdev.size = sizeof(StrongARMUARTState),
+ .qdev.reset = strongarm_uart_reset,
+ .qdev.vmsd = &vmstate_strongarm_uart_regs,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+/* Main CPU functions */
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
+{
+ StrongARMState *s;
+ qemu_irq *pic;
+ int i;
+
+ s = qemu_mallocz(sizeof(StrongARMState));
+
+ if (!rev) {
+ rev = "sa1110-b5";
+ }
+
+ if (strncmp(rev, "sa1110", 6)) {
+ error_report("Machine requires a SA1110 processor.\n");
+ exit(1);
+ }
+
+ s->env = cpu_init(rev);
+
+ if (!s->env) {
+ error_report("Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ cpu_register_physical_memory(SA_SDCS0,
+ sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
+ sdram_size) | IO_MEM_RAM);
+
+ pic = arm_pic_init_cpu(s->env);
+ s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
+ pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
+
+ sysbus_create_varargs("pxa25x-timer", 0x90000000,
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
+ NULL);
+
+ sysbus_create_simple("strongarm-rtc", 0x90010000,
+ qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
+
+ s->gpio = strongarm_gpio_init(0x90040000, s->pic);
+
+ s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
+
+ for (i = 0; sa_serial[i].io_base; i++) {
+ DeviceState *dev = qdev_create(NULL, "strongarm-uart");
+ qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ sa_serial[i].io_base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0,
+ qdev_get_gpio_in(s->pic, sa_serial[i].irq));
+ }
+
+ return s;
+}
+
+static void strongarm_register_devices(void)
+{
+ sysbus_register_withprop(&strongarm_pic_info);
+ sysbus_register_withprop(&strongarm_rtc_sysbus_info);
+ sysbus_register_withprop(&strongarm_gpio_info);
+ sysbus_register_withprop(&strongarm_ppc_info);
+ sysbus_register_withprop(&strongarm_uart_info);
+}
+device_init(strongarm_register_devices)
diff --git a/hw/strongarm.h b/hw/strongarm.h
new file mode 100644
index 0000000..ce7a3d7
--- /dev/null
+++ b/hw/strongarm.h
@@ -0,0 +1,62 @@
+#ifndef _STRONGARM_H
+#define _STRONGARM_H
+
+#define SA_CS0 0x00000000
+#define SA_CS1 0x08000000
+#define SA_CS2 0x10000000
+#define SA_CS3 0x18000000
+#define SA_PCMCIA_CS0 0x20000000
+#define SA_PCMCIA_CS1 0x30000000
+#define SA_CS4 0x40000000
+#define SA_CS5 0x48000000
+/* system registers here */
+#define SA_SDCS0 0xc0000000
+#define SA_SDCS1 0xc8000000
+#define SA_SDCS2 0xd0000000
+#define SA_SDCS3 0xd8000000
+
+enum {
+ SA_PIC_GPIO0_EDGE = 0,
+ SA_PIC_GPIO1_EDGE,
+ SA_PIC_GPIO2_EDGE,
+ SA_PIC_GPIO3_EDGE,
+ SA_PIC_GPIO4_EDGE,
+ SA_PIC_GPIO5_EDGE,
+ SA_PIC_GPIO6_EDGE,
+ SA_PIC_GPIO7_EDGE,
+ SA_PIC_GPIO8_EDGE,
+ SA_PIC_GPIO9_EDGE,
+ SA_PIC_GPIO10_EDGE,
+ SA_PIC_GPIOX_EDGE,
+ SA_PIC_LCD,
+ SA_PIC_UDC,
+ SA_PIC_RSVD1,
+ SA_PIC_UART1,
+ SA_PIC_UART2,
+ SA_PIC_UART3,
+ SA_PIC_MCP,
+ SA_PIC_SSP,
+ SA_PIC_DMA_CH0,
+ SA_PIC_DMA_CH1,
+ SA_PIC_DMA_CH2,
+ SA_PIC_DMA_CH3,
+ SA_PIC_DMA_CH4,
+ SA_PIC_DMA_CH5,
+ SA_PIC_OSTC0,
+ SA_PIC_OSTC1,
+ SA_PIC_OSTC2,
+ SA_PIC_OSTC3,
+ SA_PIC_RTC_HZ,
+ SA_PIC_RTC_ALARM,
+};
+
+typedef struct {
+ CPUState *env;
+ DeviceState *pic;
+ DeviceState *gpio;
+ DeviceState *ppc;
+} StrongARMState;
+
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
+
+#endif
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index e247a7a..d5af644 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -363,6 +363,7 @@ enum arm_features {
ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
ARM_FEATURE_V4T,
ARM_FEATURE_V5,
+ ARM_FEATURE_STRONGARM,
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
+#define ARM_CPUID_SA1100 0x4401A11B
+#define ARM_CPUID_SA1110 0x6901B119
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 261e148..3022db8 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -214,6 +214,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0xd172172;
env->cp15.c1_sys = 0x00000078;
break;
+ case ARM_CPUID_SA1100:
+ case ARM_CPUID_SA1110:
+ set_feature(env, ARM_FEATURE_STRONGARM);
+ env->cp15.c1_sys = 0x00000070;
+ break;
default:
cpu_abort(env, "Bad CPU ID: %x\n", id);
break;
@@ -374,6 +379,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
{ ARM_CPUID_CORTEXA9, "cortex-a9"},
{ ARM_CPUID_TI925T, "ti925t" },
{ ARM_CPUID_PXA250, "pxa250" },
+ { ARM_CPUID_SA1100, "sa1100" },
+ { ARM_CPUID_SA1110, "sa1110" },
{ ARM_CPUID_PXA255, "pxa255" },
{ ARM_CPUID_PXA260, "pxa260" },
{ ARM_CPUID_PXA261, "pxa261" },
@@ -1545,6 +1552,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 9:
if (arm_feature(env, ARM_FEATURE_OMAPCP))
break;
+ if (arm_feature(env, ARM_FEATURE_STRONGARM))
+ break; /* Ignore ReadBuffer access */
switch (crm) {
case 0: /* Cache lockdown. */
switch (op1) {
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
@ 2011-03-24 22:07 Dmitry Eremin-Solenikov
2011-03-24 22:07 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-24 22:07 UTC (permalink / raw)
To: qemu-devel
Currently target-arm/ assumes at least ARMv5 core. Add support for
handling also ARMv4/ARMv4T. This changes the following instructions:
BX(v4T and later)
BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
SMULxy, SMULWxy, STC2 (v5 and later)
All instructions that are "v5TE and later" are also bound to just v5, as
that's how it was before.
This patch doesn _not_ include disabling of cp15 access and base-updated
data abort model (that will be required to emulate chips based on a
ARM7TDMI), because:
* no ARM7TDMI chips are currently emulated (or planned)
* those features aren't strictly necessary for my purposes (SA-1 core
emulation).
Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
Sanders <vince@kyllikki.org>.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
target-arm/cpu.h | 4 +++-
target-arm/helper.c | 24 ++++++++++++++++++++++++
target-arm/translate.c | 25 ++++++++++++++++++++++---
3 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1ae7982..e247a7a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -360,7 +360,9 @@ enum arm_features {
ARM_FEATURE_M, /* Microcontroller profile. */
ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
ARM_FEATURE_THUMB2EE,
- ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V4T,
+ ARM_FEATURE_V5,
};
static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 78f3d39..49ff5cf 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cpuid = id;
switch (id) {
case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
env->cp15.c0_cachetype = 0x1dd20d2;
env->cp15.c1_sys = 0x00090078;
break;
case ARM_CPUID_ARM946:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_MPU);
env->cp15.c0_cachetype = 0x0f004006;
env->cp15.c1_sys = 0x00000078;
break;
case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
@@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_ARM1136_R2:
case ARM_CPUID_ARM1136:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
@@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00050078;
break;
case ARM_CPUID_ARM11MPCORE:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_VFP);
@@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0x1dd20d2;
break;
case ARM_CPUID_CORTEXA8:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXA9:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXM3:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_THUMB2);
set_feature(env, ARM_FEATURE_V7);
@@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_DIV);
break;
case ARM_CPUID_ANY: /* For userspace emulation. */
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -161,6 +179,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_TI915T:
case ARM_CPUID_TI925T:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_OMAPCP);
env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
env->cp15.c0_cachetype = 0x5109149;
@@ -173,6 +193,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA260:
case ARM_CPUID_PXA261:
case ARM_CPUID_PXA262:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
env->cp15.c0_cachetype = 0xd172172;
@@ -184,6 +206,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA270_B1:
case ARM_CPUID_PXA270_C0:
case ARM_CPUID_PXA270_C5:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
set_feature(env, ARM_FEATURE_IWMMXT);
diff --git a/target-arm/translate.c b/target-arm/translate.c
index f69912f..c9df87e 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,6 +34,8 @@
#define GEN_HELPER 1
#include "helpers.h"
+#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
+#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
#define ENABLE_ARCH_5J 0
#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
@@ -6129,6 +6131,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
}
/* Otherwise PLD; v5TE+ */
+ ARCH(5);
return;
}
if (((insn & 0x0f70f000) == 0x0450f000) ||
@@ -6255,6 +6258,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
/* branch link and change to thumb (blx <offset>) */
int32_t offset;
+ ARCH(5);
val = (uint32_t)s->pc;
tmp = tcg_temp_new_i32();
tcg_gen_movi_i32(tmp, val);
@@ -6268,6 +6272,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx_im(s, val);
return;
} else if ((insn & 0x0e000f00) == 0x0c000100) {
+ ARCH(5);
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
/* iWMMXt register transfer. */
if (env->cp15.c15_cpar & (1 << 1))
@@ -6276,8 +6281,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
} else if ((insn & 0x0fe00000) == 0x0c400000) {
/* Coprocessor double register transfer. */
+ ARCH(5);
} else if ((insn & 0x0f000010) == 0x0e000010) {
/* Additional coprocessor register transfer. */
+ ARCH(5);
} else if ((insn & 0x0ff10020) == 0x01000000) {
uint32_t mask;
uint32_t val;
@@ -6376,10 +6383,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
case 0x1:
if (op1 == 1) {
/* branch/exchange thumb (bx). */
+ ARCH(4T);
tmp = load_reg(s, rm);
gen_bx(s, tmp);
} else if (op1 == 3) {
/* clz */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
tmp = load_reg(s, rm);
gen_helper_clz(tmp, tmp);
@@ -6402,6 +6411,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (op1 != 1)
goto illegal_op;
+ ARCH(5);
/* branch link/exchange thumb (blx) */
tmp = load_reg(s, rm);
tmp2 = tcg_temp_new_i32();
@@ -6410,6 +6420,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx(s, tmp);
break;
case 0x5: /* saturating add/subtract */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
rn = (insn >> 16) & 0xf;
tmp = load_reg(s, rm);
@@ -6431,12 +6442,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
}
/* bkpt */
+ ARCH(5);
gen_exception_insn(s, 4, EXCP_BKPT);
break;
case 0x8: /* signed multiply */
case 0xa:
case 0xc:
case 0xe:
+ ARCH(5);
rs = (insn >> 8) & 0xf;
rn = (insn >> 12) & 0xf;
rd = (insn >> 16) & 0xf;
@@ -6832,6 +6845,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
load = 1;
} else if (sh & 2) {
+ ARCH(5);
/* doubleword */
if (sh & 1) {
/* store */
@@ -7172,10 +7186,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
if (insn & (1 << 20)) {
/* Complete the load. */
- if (rd == 15)
+ if (rd == 15 && ENABLE_ARCH_4T) {
gen_bx(s, tmp);
- else
+ } else {
store_reg(s, rd, tmp);
+ }
}
break;
case 0x08:
@@ -7229,7 +7244,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
/* load */
tmp = gen_ld32(addr, IS_USER(s));
if (i == 15) {
- gen_bx(s, tmp);
+ if (ENABLE_ARCH_4T) {
+ gen_bx(s, tmp);
+ } else {
+ store_reg(s, i, tmp);
+ }
} else if (user) {
tmp2 = tcg_const_i32(i);
gen_helper_set_user_reg(tmp2, tmp);
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
2011-03-24 22:07 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
@ 2011-03-24 22:07 ` Dmitry Eremin-Solenikov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-24 22:07 UTC (permalink / raw)
To: qemu-devel
Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
Implemented:
- IRQs
- GPIO
- PPC
- RTC
- UARTs (no IrDA/etc.)
- OST reused from pxa25x
Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
hw/strongarm.c
V2:
* removed all strongarm variants except latest
* dropped unused casts
* fixed PIC vmstate
* fixed new devices created with version_id = 1
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
Makefile.target | 1 +
hw/strongarm.c | 1301 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/strongarm.h | 62 +++
target-arm/cpu.h | 3 +
target-arm/helper.c | 9 +
5 files changed, 1376 insertions(+), 0 deletions(-)
create mode 100644 hw/strongarm.c
create mode 100644 hw/strongarm.h
diff --git a/Makefile.target b/Makefile.target
index 62b102a..d071a4d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -328,6 +328,7 @@ obj-arm-y += framebuffer.o
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
+obj-arm-y += strongarm.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/strongarm.c b/hw/strongarm.c
new file mode 100644
index 0000000..9f3df87
--- /dev/null
+++ b/hw/strongarm.c
@@ -0,0 +1,1301 @@
+/*
+ * StrongARM SA-1100/SA-1110 emulation
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * Largely based on StrongARM emulation:
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * UART code based on QEMU 16550A UART emulation
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "strongarm.h"
+#include "qemu-error.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+/*
+ TODO
+ - Implement cp15, c14 ?
+ - Implement cp15, c15 !!! (idle used in L)
+ - Implement idle mode handling/DIM
+ - Implement sleep mode/Wake sources
+ - Implement reset control
+ - Implement memory control regs
+ - PCMCIA handling
+ - Maybe support MBGNT/MBREQ
+ - DMA channels
+ - GPCLK
+ - IrDA
+ - MCP
+ - Enhance UART with modem signals
+ */
+
+static struct {
+ target_phys_addr_t io_base;
+ int irq;
+} sa_serial[] = {
+ { 0x80010000, SA_PIC_UART1 },
+ { 0x80030000, SA_PIC_UART2 },
+ { 0x80050000, SA_PIC_UART3 },
+ { 0, 0 }
+};
+
+/* Interrupt Controller */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ uint32_t pending;
+ uint32_t enabled;
+ uint32_t is_fiq;
+ uint32_t int_idle;
+} StrongARMPICState;
+
+#define ICIP 0x00
+#define ICMR 0x04
+#define ICLR 0x08
+#define ICFP 0x10
+#define ICPR 0x20
+#define ICCR 0x0c
+
+#define SA_PIC_SRCS 32
+
+
+static void strongarm_pic_update(void *opaque)
+{
+ StrongARMPICState *s = opaque;
+
+ /* FIXME: reflect DIM */
+ qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
+ qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
+}
+
+static void strongarm_pic_set_irq(void *opaque, int irq, int level)
+{
+ StrongARMPICState *s = opaque;
+
+ if (level) {
+ s->pending |= 1 << irq;
+ } else {
+ s->pending &= ~(1 << irq);
+ }
+
+ strongarm_pic_update(s);
+}
+
+static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICIP:
+ return s->pending & ~s->is_fiq & s->enabled;
+ case ICMR:
+ return s->enabled;
+ case ICLR:
+ return s->is_fiq;
+ case ICCR:
+ return s->int_idle == 0;
+ case ICFP:
+ return s->pending & s->is_fiq & s->enabled;
+ case ICPR:
+ return s->pending;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICMR:
+ s->enabled = value;
+ break;
+ case ICLR:
+ s->is_fiq = value;
+ break;
+ case ICCR:
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ break;
+ }
+ strongarm_pic_update(s);
+}
+
+static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+};
+
+static int strongarm_pic_initfn(SysBusDevice *dev)
+{
+ StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
+ iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
+ strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ return 0;
+}
+
+static int strongarm_pic_post_load(void *opaque, int version_id)
+{
+ strongarm_pic_update(opaque);
+ return 0;
+}
+
+static VMStateDescription vmstate_strongarm_pic_regs = {
+ .name = "strongarm_pic",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_pic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pending, StrongARMPICState),
+ VMSTATE_UINT32(enabled, StrongARMPICState),
+ VMSTATE_UINT32(is_fiq, StrongARMPICState),
+ VMSTATE_UINT32(int_idle, StrongARMPICState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_pic_info = {
+ .init = strongarm_pic_initfn,
+ .qdev.name = "strongarm_pic",
+ .qdev.desc = "StrongARM PIC",
+ .qdev.size = sizeof(StrongARMPICState),
+ .qdev.vmsd = &vmstate_strongarm_pic_regs,
+};
+
+/* Real-Time Clock */
+#define RTAR 0x00 /* RTC Alarm register */
+#define RCNR 0x04 /* RTC Counter register */
+#define RTTR 0x08 /* RTC Timer Trim register */
+#define RTSR 0x0c /* RTC Status register */
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t last_rcnr;
+ int64_t last_hz;
+ QEMUTimer *rtc_alarm;
+ QEMUTimer *rtc_hz;
+ qemu_irq rtc_irq;
+ qemu_irq rtc_hz_irq;
+} StrongARMRTCState;
+
+static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
+{
+ qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
+ qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
+}
+
+static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rt_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_hz);
+ }
+
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
+ qemu_mod_timer(s->rtc_alarm, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_alarm);
+ }
+}
+
+static inline void strongarm_rtc_alarm_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 0);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static inline void strongarm_rtc_hz_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 1);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RCNR:
+ return s->last_rcnr +
+ ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ strongarm_rtc_hzupdate(s);
+ s->rttr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RTSR:
+ if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
+ strongarm_rtc_timer_update(s, value);
+ }
+
+ s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
+ strongarm_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ strongarm_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+};
+
+static int strongarm_rtc_init(SysBusDevice *dev)
+{
+ StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
+ struct tm tm;
+ int iomemtype;
+
+ s->rttr = 0x0;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_hz = qemu_get_clock_ms(rt_clock);
+
+ s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
+ s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
+
+ sysbus_init_irq(dev, &s->rtc_irq);
+ sysbus_init_irq(dev, &s->rtc_hz_irq);
+
+ iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
+ strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+
+ return 0;
+}
+
+static void strongarm_rtc_pre_save(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_hzupdate(s);
+}
+
+static int strongarm_rtc_post_load(void *opaque, int version_id)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_timer_update(s, s->rtsr);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_rtc_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = strongarm_rtc_pre_save,
+ .post_load = strongarm_rtc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rttr, StrongARMRTCState),
+ VMSTATE_UINT32(rtsr, StrongARMRTCState),
+ VMSTATE_UINT32(rtar, StrongARMRTCState),
+ VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
+ VMSTATE_INT64(last_hz, StrongARMRTCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
+ .init = strongarm_rtc_init,
+ .qdev.name = "strongarm-rtc",
+ .qdev.desc = "StrongARM RTC Controller",
+ .qdev.size = sizeof(StrongARMRTCState),
+ .qdev.vmsd = &vmstate_strongarm_rtc_regs,
+};
+
+/* GPIO */
+#define GPLR 0x00
+#define GPDR 0x04
+#define GPSR 0x08
+#define GPCR 0x0c
+#define GRER 0x10
+#define GFER 0x14
+#define GEDR 0x18
+#define GAFR 0x1c
+
+typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
+struct StrongARMGPIOInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+ qemu_irq irqs[11];
+ qemu_irq irqX;
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t rising;
+ uint32_t falling;
+ uint32_t status;
+ uint32_t gpsr;
+ uint32_t gafr;
+
+ uint32_t prev_level;
+};
+
+
+static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
+{
+ int i;
+ for (i = 0; i < 11; i++) {
+ qemu_set_irq(s->irqs[i], s->status & (1 << i));
+ }
+
+ qemu_set_irq(s->irqX, (s->status & ~0x7ff));
+}
+
+static void strongarm_gpio_set(void *opaque, int line, int level)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+ uint32_t mask;
+
+ mask = 1 << line;
+
+ if (level) {
+ s->status |= s->rising & mask &
+ ~s->ilevel & ~s->dir;
+ s->ilevel |= mask;
+ } else {
+ s->status |= s->falling & mask &
+ s->ilevel & ~s->dir;
+ s->ilevel &= ~mask;
+ }
+
+ if (s->status & mask) {
+ strongarm_gpio_irq_update(s);
+ }
+}
+
+static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return s->gpsr; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ return s->gafr;
+
+ case GPLR: /* GPIO Pin-Level registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir);
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_gpio_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir = value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel |= value;
+ strongarm_gpio_handler_update(s);
+ s->gpsr = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel &= ~value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling = value;
+ break;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ s->gafr = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status &= ~value;
+ strongarm_gpio_irq_update(s);
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
+ strongarm_gpio_read,
+ strongarm_gpio_read,
+ strongarm_gpio_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
+ strongarm_gpio_write,
+ strongarm_gpio_write,
+ strongarm_gpio_write
+};
+
+static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
+ DeviceState *pic)
+{
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "strongarm-gpio");
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ for (i = 0; i < 12; i++)
+ sysbus_connect_irq(sysbus_from_qdev(dev), i,
+ qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
+
+ return dev;
+}
+
+static int strongarm_gpio_initfn(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMGPIOInfo *s;
+ int i;
+
+ s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 28);
+
+ iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
+ strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ for (i = 0; i < 11; i++) {
+ sysbus_init_irq(dev, &s->irqs[i]);
+ }
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_gpio_regs = {
+ .name = "strongarm-gpio",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(dir, StrongARMGPIOInfo),
+ VMSTATE_UINT32(rising, StrongARMGPIOInfo),
+ VMSTATE_UINT32(falling, StrongARMGPIOInfo),
+ VMSTATE_UINT32(status, StrongARMGPIOInfo),
+ VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_gpio_info = {
+ .init = strongarm_gpio_initfn,
+ .qdev.name = "strongarm-gpio",
+ .qdev.desc = "StrongARM GPIO controller",
+ .qdev.size = sizeof(StrongARMGPIOInfo),
+};
+
+/* Peripheral Pin Controller */
+#define PPDR 0x00
+#define PPSR 0x04
+#define PPAR 0x08
+#define PSDR 0x0c
+#define PPFR 0x10
+
+typedef struct StrongARMPPCInfo StrongARMPPCInfo;
+struct StrongARMPPCInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t ppar;
+ uint32_t psdr;
+ uint32_t ppfr;
+
+ uint32_t prev_level;
+};
+
+static void strongarm_ppc_set(void *opaque, int line, int level)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ if (level) {
+ s->ilevel |= 1 << line;
+ } else {
+ s->ilevel &= ~(1 << line);
+ }
+}
+
+static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ return s->dir | ~0x3fffff;
+
+ case PPSR: /* PPC Pin State registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir) |
+ ~0x3fffff;
+
+ case PPAR:
+ return s->ppar | ~0x41000;
+
+ case PSDR:
+ return s->psdr;
+
+ case PPFR:
+ return s->ppfr | ~0x7f001;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_ppc_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ s->dir = value & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPSR: /* PPC Pin State registers */
+ s->olevel = value & s->dir & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPAR:
+ s->ppar = value & 0x41000;
+ break;
+
+ case PSDR:
+ s->psdr = value & 0x3fffff;
+ break;
+
+ case PPFR:
+ s->ppfr = value & 0x7f001;
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
+ strongarm_ppc_read,
+ strongarm_ppc_read,
+ strongarm_ppc_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
+ strongarm_ppc_write,
+ strongarm_ppc_write,
+ strongarm_ppc_write
+};
+
+static int strongarm_ppc_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMPPCInfo *s;
+
+ s = FROM_SYSBUS(StrongARMPPCInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 22);
+
+ iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
+ strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_ppc_regs = {
+ .name = "strongarm-ppc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(olevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(dir, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppar, StrongARMPPCInfo),
+ VMSTATE_UINT32(psdr, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_ppc_info = {
+ .init = strongarm_ppc_init,
+ .qdev.name = "strongarm-ppc",
+ .qdev.desc = "StrongARM PPC controller",
+ .qdev.size = sizeof(StrongARMPPCInfo),
+};
+
+/* UART Ports */
+#define UTCR0 0x00
+#define UTCR1 0x04
+#define UTCR2 0x08
+#define UTCR3 0x0c
+#define UTDR 0x14
+#define UTSR0 0x1c
+#define UTSR1 0x20
+
+#define RX_FIFO_PRE (1 << 8)
+#define RX_FIFO_FRE (1 << 9)
+#define RX_FIFO_ROR (1 << 10)
+
+typedef struct {
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t utcr0;
+ uint16_t brd;
+ uint8_t utcr3;
+ uint8_t utsr0;
+ uint8_t utsr1;
+
+ uint8_t tx_fifo[8];
+ uint8_t tx_start;
+ uint8_t tx_len;
+ uint16_t rx_fifo[12]; /* value + error flags in high bits */
+ uint8_t rx_start;
+ uint8_t rx_len;
+
+ uint64_t char_transmit_time; /* time to transmit a char in ticks*/
+ bool wait_break_end;
+ QEMUTimer *rx_timeout_timer;
+ QEMUTimer *tx_timer;
+} StrongARMUARTState;
+
+static void strongarm_uart_update_status(StrongARMUARTState *s)
+{
+ uint16_t utsr1 = 0;
+
+ if (s->tx_len != 8) {
+ utsr1 |= (1 << 2);
+ }
+
+ if (s->rx_len != 0) {
+ uint16_t ent = s->rx_fifo[s->rx_start];
+
+ utsr1 |= (1 << 1);
+ if (ent & RX_FIFO_PRE) {
+ s->utsr1 |= (1 << 3);
+ }
+ if (ent & RX_FIFO_FRE) {
+ s->utsr1 |= (1 << 4);
+ }
+ if (ent & RX_FIFO_ROR) {
+ s->utsr1 |= (1 << 5);
+ }
+ }
+
+ s->utsr1 = utsr1;
+}
+
+static void strongarm_uart_update_int_status(StrongARMUARTState *s)
+{
+ uint16_t utsr0 = s->utsr0 & 0x1c;
+ int i;
+
+ if ((s->utcr3 & (1 << 1)) &&
+ (s->utcr3 & (1 << 4)) &&
+ s->tx_len <= 4) {
+ utsr0 |= (1 << 0);
+ }
+
+ if ((s->utcr3 & (1 << 0)) &&
+ (s->utcr3 & (1 << 3)) &&
+ s->rx_len > 4) {
+ utsr0 |= (1 << 1);
+ }
+
+ for (i = 0; i < s->rx_len && i < 4; i++)
+ if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
+ utsr0 |= (1 << 5);
+ break;
+ }
+
+ s->utsr0 = utsr0;
+ qemu_set_irq(s->irq, utsr0);
+}
+
+static void strongarm_uart_update_parameters(StrongARMUARTState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->utcr0 & (1 << 0)) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->utcr0 & (1 << 1)) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+ if (s->utcr0 & (1 << 2)) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
+ frame_size += data_bits + stop_bits;
+ speed = 3686400 / 16 / (s->brd + 1);
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ if (s->chr) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+ }
+
+#if 0
+ fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
+ speed, parity, data_bits, stop_bits);
+#endif
+}
+
+static void strongarm_uart_rx_to(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len) {
+ s->utsr0 |= (1 << 2);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
+{
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ /* rx disabled */
+ return;
+ }
+
+ if (s->wait_break_end) {
+ s->utsr0 |= (1 << 4); /* REB */
+ s->wait_break_end = false;
+ }
+
+ if (s->rx_len < 12) {
+ s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
+ s->rx_len++;
+ } else
+ s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
+}
+
+static int strongarm_uart_can_receive(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len == 12) {
+ return 0;
+ }
+ /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */
+ if (s->rx_len < 8) {
+ return 8 - s->rx_len;
+ }
+ return 1;
+}
+
+static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ StrongARMUARTState *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ strongarm_uart_rx_push(s, buf[i]);
+ }
+
+ /* call the timeout receive callback in 3 char transmit time */
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static void strongarm_uart_event(void *opaque, int event)
+{
+ StrongARMUARTState *s = opaque;
+ if (event == CHR_EVENT_BREAK) {
+ s->utcr3 |= (1 << 3);
+ strongarm_uart_rx_push(s, RX_FIFO_FRE);
+ s->wait_break_end = true;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_tx(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+ uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ if (s->utcr3 & (1 << 5)) /* loopback */ {
+ strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
+ } else if (s->chr) {
+ qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
+ }
+
+ s->tx_start = (s->tx_start + 1) % 8;
+ s->tx_len--;
+ if (s->tx_len) {
+ qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMUARTState *s = opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case UTCR0:
+ return s->utcr0;
+
+ case UTCR1:
+ return s->brd >> 8;
+
+ case UTCR2:
+ return s->brd & 0xff;
+
+ case UTCR3:
+ return s->utcr3;
+
+ case UTDR:
+ if (s->rx_len != 0) {
+ ret = s->rx_fifo[s->rx_start];
+ s->rx_start = (s->rx_start + 1) % 12;
+ s->rx_len--;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ return ret;
+ }
+ return 0;
+
+ case UTSR0:
+ return s->utsr0;
+
+ case UTSR1:
+ return s->utsr1;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMUARTState *s = opaque;
+
+ switch (addr) {
+ case UTCR0:
+ s->utcr0 = value & 0x7f;
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR1:
+ s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR2:
+ s->brd = (s->brd & 0xf00) | (value & 0xff);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR3:
+ s->utcr3 = value & 0x3f;
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ s->rx_len = 0;
+ }
+ if ((s->utcr3 & (1 << 1)) == 0) {
+ s->tx_len = 0;
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ case UTDR:
+ if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
+ s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
+ s->tx_len++;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ if (s->tx_len == 1) {
+ strongarm_uart_tx(s);
+ }
+ }
+ break;
+
+ case UTSR0:
+ s->utsr0 = s->utsr0 & ~(value & 0x1c);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
+ strongarm_uart_read,
+ strongarm_uart_read,
+ strongarm_uart_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
+ strongarm_uart_write,
+ strongarm_uart_write,
+ strongarm_uart_write,
+};
+
+static int strongarm_uart_init(SysBusDevice *dev)
+{
+ StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
+ strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s);
+ s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr,
+ strongarm_uart_can_receive,
+ strongarm_uart_receive,
+ strongarm_uart_event,
+ s);
+ }
+
+ return 0;
+}
+
+static void strongarm_uart_reset(DeviceState *dev)
+{
+ StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
+
+ s->utcr0 = 0x8; /* 8 data, no parity */
+ s->brd = 23; /* 9600 */
+ s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
+
+ s->rx_len = s->tx_len = 0;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static int strongarm_uart_post_load(void *opaque, int version_id)
+{
+ StrongARMUARTState *s = opaque;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+
+ /* tx and restart timer */
+ if (s->tx_len) {
+ strongarm_uart_tx(s);
+ }
+
+ /* restart rx timeout timer */
+ if (s->rx_len) {
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_uart_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(utcr0, StrongARMUARTState),
+ VMSTATE_UINT16(brd, StrongARMUARTState),
+ VMSTATE_UINT8(utcr3, StrongARMUARTState),
+ VMSTATE_UINT8(utsr0, StrongARMUARTState),
+ VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
+ VMSTATE_UINT8(tx_start, StrongARMUARTState),
+ VMSTATE_UINT8(tx_len, StrongARMUARTState),
+ VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
+ VMSTATE_UINT8(rx_start, StrongARMUARTState),
+ VMSTATE_UINT8(rx_len, StrongARMUARTState),
+ VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_uart_info = {
+ .init = strongarm_uart_init,
+ .qdev.name = "strongarm-uart",
+ .qdev.desc = "StrongARM UART controller",
+ .qdev.size = sizeof(StrongARMUARTState),
+ .qdev.reset = strongarm_uart_reset,
+ .qdev.vmsd = &vmstate_strongarm_uart_regs,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+/* Main CPU functions */
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
+{
+ StrongARMState *s;
+ qemu_irq *pic;
+ int i;
+
+ s = qemu_mallocz(sizeof(StrongARMState));
+
+ if (!rev) {
+ rev = "sa1110-b5";
+ }
+
+ if (strncmp(rev, "sa1110", 6)) {
+ error_report("Machine requires a SA1110 processor.\n");
+ exit(1);
+ }
+
+ s->env = cpu_init(rev);
+
+ if (!s->env) {
+ error_report("Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ cpu_register_physical_memory(SA_SDCS0,
+ sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
+ sdram_size) | IO_MEM_RAM);
+
+ pic = arm_pic_init_cpu(s->env);
+ s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
+ pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
+
+ sysbus_create_varargs("pxa25x-timer", 0x90000000,
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
+ NULL);
+
+ sysbus_create_simple("strongarm-rtc", 0x90010000,
+ qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
+
+ s->gpio = strongarm_gpio_init(0x90040000, s->pic);
+
+ s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
+
+ for (i = 0; sa_serial[i].io_base; i++) {
+ DeviceState *dev = qdev_create(NULL, "strongarm-uart");
+ qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ sa_serial[i].io_base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0,
+ qdev_get_gpio_in(s->pic, sa_serial[i].irq));
+ }
+
+ return s;
+}
+
+static void strongarm_register_devices(void)
+{
+ sysbus_register_withprop(&strongarm_pic_info);
+ sysbus_register_withprop(&strongarm_rtc_sysbus_info);
+ sysbus_register_withprop(&strongarm_gpio_info);
+ sysbus_register_withprop(&strongarm_ppc_info);
+ sysbus_register_withprop(&strongarm_uart_info);
+}
+device_init(strongarm_register_devices)
diff --git a/hw/strongarm.h b/hw/strongarm.h
new file mode 100644
index 0000000..ce7a3d7
--- /dev/null
+++ b/hw/strongarm.h
@@ -0,0 +1,62 @@
+#ifndef _STRONGARM_H
+#define _STRONGARM_H
+
+#define SA_CS0 0x00000000
+#define SA_CS1 0x08000000
+#define SA_CS2 0x10000000
+#define SA_CS3 0x18000000
+#define SA_PCMCIA_CS0 0x20000000
+#define SA_PCMCIA_CS1 0x30000000
+#define SA_CS4 0x40000000
+#define SA_CS5 0x48000000
+/* system registers here */
+#define SA_SDCS0 0xc0000000
+#define SA_SDCS1 0xc8000000
+#define SA_SDCS2 0xd0000000
+#define SA_SDCS3 0xd8000000
+
+enum {
+ SA_PIC_GPIO0_EDGE = 0,
+ SA_PIC_GPIO1_EDGE,
+ SA_PIC_GPIO2_EDGE,
+ SA_PIC_GPIO3_EDGE,
+ SA_PIC_GPIO4_EDGE,
+ SA_PIC_GPIO5_EDGE,
+ SA_PIC_GPIO6_EDGE,
+ SA_PIC_GPIO7_EDGE,
+ SA_PIC_GPIO8_EDGE,
+ SA_PIC_GPIO9_EDGE,
+ SA_PIC_GPIO10_EDGE,
+ SA_PIC_GPIOX_EDGE,
+ SA_PIC_LCD,
+ SA_PIC_UDC,
+ SA_PIC_RSVD1,
+ SA_PIC_UART1,
+ SA_PIC_UART2,
+ SA_PIC_UART3,
+ SA_PIC_MCP,
+ SA_PIC_SSP,
+ SA_PIC_DMA_CH0,
+ SA_PIC_DMA_CH1,
+ SA_PIC_DMA_CH2,
+ SA_PIC_DMA_CH3,
+ SA_PIC_DMA_CH4,
+ SA_PIC_DMA_CH5,
+ SA_PIC_OSTC0,
+ SA_PIC_OSTC1,
+ SA_PIC_OSTC2,
+ SA_PIC_OSTC3,
+ SA_PIC_RTC_HZ,
+ SA_PIC_RTC_ALARM,
+};
+
+typedef struct {
+ CPUState *env;
+ DeviceState *pic;
+ DeviceState *gpio;
+ DeviceState *ppc;
+} StrongARMState;
+
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
+
+#endif
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index e247a7a..d5af644 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -363,6 +363,7 @@ enum arm_features {
ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
ARM_FEATURE_V4T,
ARM_FEATURE_V5,
+ ARM_FEATURE_STRONGARM,
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
+#define ARM_CPUID_SA1100 0x4401A11B
+#define ARM_CPUID_SA1110 0x6901B119
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 49ff5cf..0e0f5d3 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -215,6 +215,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0xd172172;
env->cp15.c1_sys = 0x00000078;
break;
+ case ARM_CPUID_SA1100:
+ case ARM_CPUID_SA1110:
+ set_feature(env, ARM_FEATURE_STRONGARM);
+ env->cp15.c1_sys = 0x00000070;
+ break;
default:
cpu_abort(env, "Bad CPU ID: %x\n", id);
break;
@@ -375,6 +380,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
{ ARM_CPUID_CORTEXA9, "cortex-a9"},
{ ARM_CPUID_TI925T, "ti925t" },
{ ARM_CPUID_PXA250, "pxa250" },
+ { ARM_CPUID_SA1100, "sa1100" },
+ { ARM_CPUID_SA1110, "sa1110" },
{ ARM_CPUID_PXA255, "pxa255" },
{ ARM_CPUID_PXA260, "pxa260" },
{ ARM_CPUID_PXA261, "pxa261" },
@@ -1546,6 +1553,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 9:
if (arm_feature(env, ARM_FEATURE_OMAPCP))
break;
+ if (arm_feature(env, ARM_FEATURE_STRONGARM))
+ break; /* Ignore ReadBuffer access */
switch (crm) {
case 0: /* Cache lockdown. */
switch (op1) {
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation
@ 2011-03-24 10:22 Dmitry Eremin-Solenikov
2011-03-24 10:22 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-24 10:22 UTC (permalink / raw)
To: qemu-devel
Currently target-arm/ assumes at least ARMv5 core. Add support for
handling also ARMv4/ARMv4T. This changes the following instructions:
BX(v4T and later)
BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
SMULxy, SMULWxy, STC2 (v5 and later)
All instructions that are "v5TE and later" are also bound to just v5, as
that's how it was before.
This patch doesn _not_ include disabling of cp15 access and base-updated
data abort model (that will be required to emulate chips based on a
ARM7TDMI), because:
* no ARM7TDMI chips are currently emulated (or planned)
* those features aren't strictly necessary for my purposes (SA-1 core
emulation).
Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
Sanders <vince@kyllikki.org>.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
target-arm/cpu.h | 4 +++-
target-arm/helper.c | 24 ++++++++++++++++++++++++
target-arm/translate.c | 25 ++++++++++++++++++++++---
3 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1ae7982..e247a7a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -360,7 +360,9 @@ enum arm_features {
ARM_FEATURE_M, /* Microcontroller profile. */
ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
ARM_FEATURE_THUMB2EE,
- ARM_FEATURE_V7MP /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
+ ARM_FEATURE_V4T,
+ ARM_FEATURE_V5,
};
static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 78f3d39..49ff5cf 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cpuid = id;
switch (id) {
case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
env->cp15.c0_cachetype = 0x1dd20d2;
env->cp15.c1_sys = 0x00090078;
break;
case ARM_CPUID_ARM946:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_MPU);
env->cp15.c0_cachetype = 0x0f004006;
env->cp15.c1_sys = 0x00000078;
break;
case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
@@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_ARM1136_R2:
case ARM_CPUID_ARM1136:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_VFP);
set_feature(env, ARM_FEATURE_AUXCR);
@@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00050078;
break;
case ARM_CPUID_ARM11MPCORE:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_VFP);
@@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0x1dd20d2;
break;
case ARM_CPUID_CORTEXA8:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXA9:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c1_sys = 0x00c50078;
break;
case ARM_CPUID_CORTEXM3:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_THUMB2);
set_feature(env, ARM_FEATURE_V7);
@@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
set_feature(env, ARM_FEATURE_DIV);
break;
case ARM_CPUID_ANY: /* For userspace emulation. */
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_V6);
set_feature(env, ARM_FEATURE_V6K);
set_feature(env, ARM_FEATURE_V7);
@@ -161,6 +179,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
break;
case ARM_CPUID_TI915T:
case ARM_CPUID_TI925T:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_OMAPCP);
env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
env->cp15.c0_cachetype = 0x5109149;
@@ -173,6 +193,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA260:
case ARM_CPUID_PXA261:
case ARM_CPUID_PXA262:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
env->cp15.c0_cachetype = 0xd172172;
@@ -184,6 +206,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
case ARM_CPUID_PXA270_B1:
case ARM_CPUID_PXA270_C0:
case ARM_CPUID_PXA270_C5:
+ set_feature(env, ARM_FEATURE_V4T);
+ set_feature(env, ARM_FEATURE_V5);
set_feature(env, ARM_FEATURE_XSCALE);
/* JTAG_ID is ((id << 28) | 0x09265013) */
set_feature(env, ARM_FEATURE_IWMMXT);
diff --git a/target-arm/translate.c b/target-arm/translate.c
index f69912f..c9df87e 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,6 +34,8 @@
#define GEN_HELPER 1
#include "helpers.h"
+#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T)
+#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5)
#define ENABLE_ARCH_5J 0
#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
@@ -6129,6 +6131,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
}
/* Otherwise PLD; v5TE+ */
+ ARCH(5);
return;
}
if (((insn & 0x0f70f000) == 0x0450f000) ||
@@ -6255,6 +6258,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
/* branch link and change to thumb (blx <offset>) */
int32_t offset;
+ ARCH(5);
val = (uint32_t)s->pc;
tmp = tcg_temp_new_i32();
tcg_gen_movi_i32(tmp, val);
@@ -6268,6 +6272,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx_im(s, val);
return;
} else if ((insn & 0x0e000f00) == 0x0c000100) {
+ ARCH(5);
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
/* iWMMXt register transfer. */
if (env->cp15.c15_cpar & (1 << 1))
@@ -6276,8 +6281,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
} else if ((insn & 0x0fe00000) == 0x0c400000) {
/* Coprocessor double register transfer. */
+ ARCH(5);
} else if ((insn & 0x0f000010) == 0x0e000010) {
/* Additional coprocessor register transfer. */
+ ARCH(5);
} else if ((insn & 0x0ff10020) == 0x01000000) {
uint32_t mask;
uint32_t val;
@@ -6376,10 +6383,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
case 0x1:
if (op1 == 1) {
/* branch/exchange thumb (bx). */
+ ARCH(4T);
tmp = load_reg(s, rm);
gen_bx(s, tmp);
} else if (op1 == 3) {
/* clz */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
tmp = load_reg(s, rm);
gen_helper_clz(tmp, tmp);
@@ -6402,6 +6411,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
if (op1 != 1)
goto illegal_op;
+ ARCH(5);
/* branch link/exchange thumb (blx) */
tmp = load_reg(s, rm);
tmp2 = tcg_temp_new_i32();
@@ -6410,6 +6420,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
gen_bx(s, tmp);
break;
case 0x5: /* saturating add/subtract */
+ ARCH(5);
rd = (insn >> 12) & 0xf;
rn = (insn >> 16) & 0xf;
tmp = load_reg(s, rm);
@@ -6431,12 +6442,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
goto illegal_op;
}
/* bkpt */
+ ARCH(5);
gen_exception_insn(s, 4, EXCP_BKPT);
break;
case 0x8: /* signed multiply */
case 0xa:
case 0xc:
case 0xe:
+ ARCH(5);
rs = (insn >> 8) & 0xf;
rn = (insn >> 12) & 0xf;
rd = (insn >> 16) & 0xf;
@@ -6832,6 +6845,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
load = 1;
} else if (sh & 2) {
+ ARCH(5);
/* doubleword */
if (sh & 1) {
/* store */
@@ -7172,10 +7186,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
}
if (insn & (1 << 20)) {
/* Complete the load. */
- if (rd == 15)
+ if (rd == 15 && ENABLE_ARCH_4T) {
gen_bx(s, tmp);
- else
+ } else {
store_reg(s, rd, tmp);
+ }
}
break;
case 0x08:
@@ -7229,7 +7244,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
/* load */
tmp = gen_ld32(addr, IS_USER(s));
if (i == 15) {
- gen_bx(s, tmp);
+ if (ENABLE_ARCH_4T) {
+ gen_bx(s, tmp);
+ } else {
+ store_reg(s, i, tmp);
+ }
} else if (user) {
tmp2 = tcg_const_i32(i);
gen_helper_set_user_reg(tmp2, tmp);
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100
2011-03-24 10:22 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
@ 2011-03-24 10:22 ` Dmitry Eremin-Solenikov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry Eremin-Solenikov @ 2011-03-24 10:22 UTC (permalink / raw)
To: qemu-devel
Basic implementation of DEC/Intel SA-1100/SA-1110 chips emulation.
Implemented:
- IRQs
- GPIO
- PPC
- RTC
- UARTs (no IrDA/etc.)
- OST reused from pxa25x
Everything else is TODO (esp. PM/idle/sleep!) - see the todo in the
hw/strongarm.c
V2:
* removed all strongarm variants except latest
* dropped unused casts
* fixed PIC vmstate
* fixed new devices created with version_id = 1
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
Makefile.target | 2 +
hw/strongarm.c | 1302 +++++++++++++++++++++++++++++++++++++++++++++++++++
hw/strongarm.h | 62 +++
target-arm/cpu.h | 3 +
target-arm/helper.c | 9 +
5 files changed, 1378 insertions(+), 0 deletions(-)
create mode 100644 hw/strongarm.c
create mode 100644 hw/strongarm.h
diff --git a/Makefile.target b/Makefile.target
index 62b102a..abc2978 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -328,6 +328,8 @@ obj-arm-y += framebuffer.o
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
+obj-arm-y += strongarm.o
+obj-arm-y += collie.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/strongarm.c b/hw/strongarm.c
new file mode 100644
index 0000000..f5e300c
--- /dev/null
+++ b/hw/strongarm.c
@@ -0,0 +1,1302 @@
+/*
+ * StrongARM SA-1100/SA-1110 emulation
+ *
+ * Copyright (C) 2011 Dmitry Eremin-Solenikov
+ *
+ * Largely based on StrongARM emulation:
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * UART code based on QEMU 16550A UART emulation
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "strongarm.h"
+#include "qemu-error.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+/*
+ TODO
+ - Implement cp15, c14 ?
+ - Implement cp15, c15 !!! (idle used in L)
+ - Implement idle mode handling/DIM
+ - Implement sleep mode/Wake sources
+ - Implement reset control
+ - Implement memory control regs
+ - PCMCIA handling
+ - Maybe support MBGNT/MBREQ
+ - DMA channels
+ - GPCLK
+ - IrDA
+ - MCP
+ - Enhance UART with modem signals
+ */
+
+static struct {
+ target_phys_addr_t io_base;
+ int irq;
+} sa_serial[] = {
+ { 0x80010000, SA_PIC_UART1 },
+ { 0x80030000, SA_PIC_UART2 },
+ { 0x80050000, SA_PIC_UART3 },
+ { 0, 0 }
+};
+
+/* Interrupt Controller */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ uint32_t pending;
+ uint32_t enabled;
+ uint32_t is_fiq;
+ uint32_t int_idle;
+} StrongARMPICState;
+
+#define ICIP 0x00
+#define ICMR 0x04
+#define ICLR 0x08
+#define ICFP 0x10
+#define ICPR 0x20
+#define ICCR 0x0c
+
+#define SA_PIC_SRCS 32
+
+
+static void strongarm_pic_update(void *opaque)
+{
+ StrongARMPICState *s = opaque;
+
+ /* FIXME: reflect DIM */
+ qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq);
+ qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq);
+}
+
+static void strongarm_pic_set_irq(void *opaque, int irq, int level)
+{
+ StrongARMPICState *s = opaque;
+
+ if (level) {
+ s->pending |= 1 << irq;
+ } else {
+ s->pending &= ~(1 << irq);
+ }
+
+ strongarm_pic_update(s);
+}
+
+static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICIP:
+ return s->pending & ~s->is_fiq & s->enabled;
+ case ICMR:
+ return s->enabled;
+ case ICLR:
+ return s->is_fiq;
+ case ICCR:
+ return s->int_idle == 0;
+ case ICFP:
+ return s->pending & s->is_fiq & s->enabled;
+ case ICPR:
+ return s->pending;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ StrongARMPICState *s = opaque;
+
+ switch (offset) {
+ case ICMR:
+ s->enabled = value;
+ break;
+ case ICLR:
+ s->is_fiq = value;
+ break;
+ case ICCR:
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ default:
+ printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ break;
+ }
+ strongarm_pic_update(s);
+}
+
+static CPUReadMemoryFunc * const strongarm_pic_readfn[] = {
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+ strongarm_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = {
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+ strongarm_pic_mem_write,
+};
+
+static int strongarm_pic_initfn(SysBusDevice *dev)
+{
+ StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS);
+ iomemtype = cpu_register_io_memory(strongarm_pic_readfn,
+ strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+
+ return 0;
+}
+
+static int strongarm_pic_post_load(void *opaque, int version_id)
+{
+ strongarm_pic_update(opaque);
+ return 0;
+}
+
+static VMStateDescription vmstate_strongarm_pic_regs = {
+ .name = "strongarm_pic",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_pic_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(pending, StrongARMPICState),
+ VMSTATE_UINT32(enabled, StrongARMPICState),
+ VMSTATE_UINT32(is_fiq, StrongARMPICState),
+ VMSTATE_UINT32(int_idle, StrongARMPICState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_pic_info = {
+ .init = strongarm_pic_initfn,
+ .qdev.name = "strongarm_pic",
+ .qdev.desc = "StrongARM PIC",
+ .qdev.size = sizeof(StrongARMPICState),
+ .qdev.vmsd = &vmstate_strongarm_pic_regs,
+};
+
+/* Real-Time Clock */
+#define RTAR 0x00 /* RTC Alarm register */
+#define RCNR 0x04 /* RTC Counter register */
+#define RTTR 0x08 /* RTC Timer Trim register */
+#define RTSR 0x0c /* RTC Status register */
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t last_rcnr;
+ int64_t last_hz;
+ QEMUTimer *rtc_alarm;
+ QEMUTimer *rtc_hz;
+ qemu_irq rtc_irq;
+ qemu_irq rtc_hz_irq;
+} StrongARMRTCState;
+
+static inline void strongarm_rtc_int_update(StrongARMRTCState *s)
+{
+ qemu_set_irq(s->rtc_irq, s->rtsr & (1 << 0));
+ qemu_set_irq(s->rtc_hz_irq, s->rtsr & (1 << 1));
+}
+
+static void strongarm_rtc_hzupdate(StrongARMRTCState *s)
+{
+ int64_t rt = qemu_get_clock_ms(rt_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static inline void strongarm_rtc_timer_update(StrongARMRTCState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 4)) && !(rtsr & (1 << 1))) {
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_hz);
+ }
+
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) {
+ qemu_mod_timer(s->rtc_alarm, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ } else {
+ qemu_del_timer(s->rtc_alarm);
+ }
+}
+
+static inline void strongarm_rtc_alarm_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 0);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static inline void strongarm_rtc_hz_tick(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+ s->rtsr |= (1 << 1);
+ strongarm_rtc_timer_update(s, s->rtsr);
+ strongarm_rtc_int_update(s);
+}
+
+static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RCNR:
+ return s->last_rcnr +
+ ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ strongarm_rtc_hzupdate(s);
+ s->rttr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RTSR:
+ if (((s->rtsr ^ value) & 0xc) | (value & ~0xc)) {
+ strongarm_rtc_timer_update(s, value);
+ }
+
+ s->rtsr = (value & 0xc) | (s->rtsr & ~(value & ~0xc));
+ strongarm_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ strongarm_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ strongarm_rtc_timer_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = {
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+ strongarm_rtc_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = {
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+ strongarm_rtc_write,
+};
+
+static int strongarm_rtc_init(SysBusDevice *dev)
+{
+ StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev);
+ struct tm tm;
+ int iomemtype;
+
+ s->rttr = 0x0;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_hz = qemu_get_clock_ms(rt_clock);
+
+ s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s);
+ s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s);
+
+ sysbus_init_irq(dev, &s->rtc_irq);
+ sysbus_init_irq(dev, &s->rtc_hz_irq);
+
+ iomemtype = cpu_register_io_memory(strongarm_rtc_readfn,
+ strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+
+ return 0;
+}
+
+static void strongarm_rtc_pre_save(void *opaque)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_hzupdate(s);
+}
+
+static int strongarm_rtc_post_load(void *opaque, int version_id)
+{
+ StrongARMRTCState *s = (StrongARMRTCState *) opaque;
+
+ strongarm_rtc_timer_update(s, s->rtsr);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_rtc_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = strongarm_rtc_pre_save,
+ .post_load = strongarm_rtc_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rttr, StrongARMRTCState),
+ VMSTATE_UINT32(rtsr, StrongARMRTCState),
+ VMSTATE_UINT32(rtar, StrongARMRTCState),
+ VMSTATE_UINT32(last_rcnr, StrongARMRTCState),
+ VMSTATE_INT64(last_hz, StrongARMRTCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_rtc_sysbus_info = {
+ .init = strongarm_rtc_init,
+ .qdev.name = "strongarm-rtc",
+ .qdev.desc = "StrongARM RTC Controller",
+ .qdev.size = sizeof(StrongARMRTCState),
+ .qdev.vmsd = &vmstate_strongarm_rtc_regs,
+};
+
+/* GPIO */
+#define GPLR 0x00
+#define GPDR 0x04
+#define GPSR 0x08
+#define GPCR 0x0c
+#define GRER 0x10
+#define GFER 0x14
+#define GEDR 0x18
+#define GAFR 0x1c
+
+typedef struct StrongARMGPIOInfo StrongARMGPIOInfo;
+struct StrongARMGPIOInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+ qemu_irq irqs[11];
+ qemu_irq irqX;
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t rising;
+ uint32_t falling;
+ uint32_t status;
+ uint32_t gpsr;
+ uint32_t gafr;
+
+ uint32_t prev_level;
+};
+
+
+static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s)
+{
+ int i;
+ for (i = 0; i < 11; i++) {
+ qemu_set_irq(s->irqs[i], s->status & (1 << i));
+ }
+
+ qemu_set_irq(s->irqX, (s->status & ~0x7ff));
+}
+
+static void strongarm_gpio_set(void *opaque, int line, int level)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+ uint32_t mask;
+
+ mask = 1 << line;
+
+ if (level) {
+ s->status |= s->rising & mask &
+ ~s->ilevel & ~s->dir;
+ s->ilevel |= mask;
+ } else {
+ s->status |= s->falling & mask &
+ s->ilevel & ~s->dir;
+ s->ilevel &= ~mask;
+ }
+
+ if (s->status & mask) {
+ strongarm_gpio_irq_update(s);
+ }
+}
+
+static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return s->gpsr; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n",
+ __func__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ return s->gafr;
+
+ case GPLR: /* GPIO Pin-Level registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir);
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_gpio_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMGPIOInfo *s = (StrongARMGPIOInfo *) opaque;
+
+ switch (offset) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir = value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel |= value;
+ strongarm_gpio_handler_update(s);
+ s->gpsr = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel &= ~value;
+ strongarm_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling = value;
+ break;
+
+ case GAFR: /* GPIO Alternate Function registers */
+ s->gafr = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status &= ~value;
+ strongarm_gpio_irq_update(s);
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = {
+ strongarm_gpio_read,
+ strongarm_gpio_read,
+ strongarm_gpio_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = {
+ strongarm_gpio_write,
+ strongarm_gpio_write,
+ strongarm_gpio_write
+};
+
+static DeviceState *strongarm_gpio_init(target_phys_addr_t base,
+ DeviceState *pic)
+{
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "strongarm-gpio");
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ for (i = 0; i < 12; i++)
+ sysbus_connect_irq(sysbus_from_qdev(dev), i,
+ qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i));
+
+ return dev;
+}
+
+static int strongarm_gpio_initfn(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMGPIOInfo *s;
+ int i;
+
+ s = FROM_SYSBUS(StrongARMGPIOInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 28);
+
+ iomemtype = cpu_register_io_memory(strongarm_gpio_readfn,
+ strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ for (i = 0; i < 11; i++) {
+ sysbus_init_irq(dev, &s->irqs[i]);
+ }
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_gpio_regs = {
+ .name = "strongarm-gpio",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(olevel, StrongARMGPIOInfo),
+ VMSTATE_UINT32(dir, StrongARMGPIOInfo),
+ VMSTATE_UINT32(rising, StrongARMGPIOInfo),
+ VMSTATE_UINT32(falling, StrongARMGPIOInfo),
+ VMSTATE_UINT32(status, StrongARMGPIOInfo),
+ VMSTATE_UINT32(gafr, StrongARMGPIOInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_gpio_info = {
+ .init = strongarm_gpio_initfn,
+ .qdev.name = "strongarm-gpio",
+ .qdev.desc = "StrongARM GPIO controller",
+ .qdev.size = sizeof(StrongARMGPIOInfo),
+};
+
+/* Peripheral Pin Controller */
+#define PPDR 0x00
+#define PPSR 0x04
+#define PPAR 0x08
+#define PSDR 0x0c
+#define PPFR 0x10
+
+typedef struct StrongARMPPCInfo StrongARMPPCInfo;
+struct StrongARMPPCInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[28];
+
+ uint32_t ilevel;
+ uint32_t olevel;
+ uint32_t dir;
+ uint32_t ppar;
+ uint32_t psdr;
+ uint32_t ppfr;
+
+ uint32_t prev_level;
+};
+
+static void strongarm_ppc_set(void *opaque, int line, int level)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ if (level) {
+ s->ilevel |= 1 << line;
+ } else {
+ s->ilevel &= ~(1 << line);
+ }
+}
+
+static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->olevel & s->dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ return s->dir | ~0x3fffff;
+
+ case PPSR: /* PPC Pin State registers */
+ return (s->olevel & s->dir) |
+ (s->ilevel & ~s->dir) |
+ ~0x3fffff;
+
+ case PPAR:
+ return s->ppar | ~0x41000;
+
+ case PSDR:
+ return s->psdr;
+
+ case PPFR:
+ return s->ppfr | ~0x7f001;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+
+ return 0;
+}
+
+static void strongarm_ppc_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ StrongARMPPCInfo *s = (StrongARMPPCInfo *) opaque;
+
+ switch (offset) {
+ case PPDR: /* PPC Pin Direction registers */
+ s->dir = value & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPSR: /* PPC Pin State registers */
+ s->olevel = value & s->dir & 0x3fffff;
+ strongarm_ppc_handler_update(s);
+ break;
+
+ case PPAR:
+ s->ppar = value & 0x41000;
+ break;
+
+ case PSDR:
+ s->psdr = value & 0x3fffff;
+ break;
+
+ case PPFR:
+ s->ppfr = value & 0x7f001;
+ break;
+
+ default:
+ printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = {
+ strongarm_ppc_read,
+ strongarm_ppc_read,
+ strongarm_ppc_read
+};
+
+static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = {
+ strongarm_ppc_write,
+ strongarm_ppc_write,
+ strongarm_ppc_write
+};
+
+static int strongarm_ppc_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ StrongARMPPCInfo *s;
+
+ s = FROM_SYSBUS(StrongARMPPCInfo, dev);
+
+ qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22);
+ qdev_init_gpio_out(&dev->qdev, s->handler, 22);
+
+ iomemtype = cpu_register_io_memory(strongarm_ppc_readfn,
+ strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_ppc_regs = {
+ .name = "strongarm-ppc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ilevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(olevel, StrongARMPPCInfo),
+ VMSTATE_UINT32(dir, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppar, StrongARMPPCInfo),
+ VMSTATE_UINT32(psdr, StrongARMPPCInfo),
+ VMSTATE_UINT32(ppfr, StrongARMPPCInfo),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_ppc_info = {
+ .init = strongarm_ppc_init,
+ .qdev.name = "strongarm-ppc",
+ .qdev.desc = "StrongARM PPC controller",
+ .qdev.size = sizeof(StrongARMPPCInfo),
+};
+
+/* UART Ports */
+#define UTCR0 0x00
+#define UTCR1 0x04
+#define UTCR2 0x08
+#define UTCR3 0x0c
+#define UTDR 0x14
+#define UTSR0 0x1c
+#define UTSR1 0x20
+
+#define RX_FIFO_PRE (1 << 8)
+#define RX_FIFO_FRE (1 << 9)
+#define RX_FIFO_ROR (1 << 10)
+
+typedef struct {
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t utcr0;
+ uint16_t brd;
+ uint8_t utcr3;
+ uint8_t utsr0;
+ uint8_t utsr1;
+
+ uint8_t tx_fifo[8];
+ uint8_t tx_start;
+ uint8_t tx_len;
+ uint16_t rx_fifo[12]; /* value + error flags in high bits */
+ uint8_t rx_start;
+ uint8_t rx_len;
+
+ uint64_t char_transmit_time; /* time to transmit a char in ticks*/
+ bool wait_break_end;
+ QEMUTimer *rx_timeout_timer;
+ QEMUTimer *tx_timer;
+} StrongARMUARTState;
+
+static void strongarm_uart_update_status(StrongARMUARTState *s)
+{
+ uint16_t utsr1 = 0;
+
+ if (s->tx_len != 8) {
+ utsr1 |= (1 << 2);
+ }
+
+ if (s->rx_len != 0) {
+ uint16_t ent = s->rx_fifo[s->rx_start];
+
+ utsr1 |= (1 << 1);
+ if (ent & RX_FIFO_PRE) {
+ s->utsr1 |= (1 << 3);
+ }
+ if (ent & RX_FIFO_FRE) {
+ s->utsr1 |= (1 << 4);
+ }
+ if (ent & RX_FIFO_ROR) {
+ s->utsr1 |= (1 << 5);
+ }
+ }
+
+ s->utsr1 = utsr1;
+}
+
+static void strongarm_uart_update_int_status(StrongARMUARTState *s)
+{
+ uint16_t utsr0 = s->utsr0 & 0x1c;
+ int i;
+
+ if ((s->utcr3 & (1 << 1)) &&
+ (s->utcr3 & (1 << 4)) &&
+ s->tx_len <= 4) {
+ utsr0 |= (1 << 0);
+ }
+
+ if ((s->utcr3 & (1 << 0)) &&
+ (s->utcr3 & (1 << 3)) &&
+ s->rx_len > 4) {
+ utsr0 |= (1 << 1);
+ }
+
+ for (i = 0; i < s->rx_len && i < 4; i++)
+ if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) {
+ utsr0 |= (1 << 5);
+ break;
+ }
+
+ s->utsr0 = utsr0;
+ qemu_set_irq(s->irq, utsr0);
+}
+
+static void strongarm_uart_update_parameters(StrongARMUARTState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->utcr0 & (1 << 0)) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->utcr0 & (1 << 1)) {
+ parity = 'E';
+ } else {
+ parity = 'O';
+ }
+ } else {
+ parity = 'N';
+ }
+ if (s->utcr0 & (1 << 2)) {
+ stop_bits = 2;
+ } else {
+ stop_bits = 1;
+ }
+
+ data_bits = (s->utcr0 & (1 << 3)) ? 8 : 7;
+ frame_size += data_bits + stop_bits;
+ speed = 3686400 / 16 / (s->brd + 1);
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ if (s->chr) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+ }
+
+#if 0 {
+ fprintf(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
+}
+ speed, parity, data_bits, stop_bits);
+#endif
+}
+
+static void strongarm_uart_rx_timeout_int(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len) {
+ s->utsr0 |= (1 << 2);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c)
+{
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ /* rx disabled */
+ return;
+ }
+
+ if (s->wait_break_end) {
+ s->utsr0 |= (1 << 4); /* REB */
+ s->wait_break_end = false;
+ }
+
+ if (s->rx_len < 12) {
+ s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c;
+ s->rx_len++;
+ } else
+ s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR;
+}
+
+static int strongarm_uart_can_receive(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+
+ if (s->rx_len == 12) {
+ return 0;
+ }
+ /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */
+ if (s->rx_len < 8) {
+ return 8 - s->rx_len;
+ }
+ return 1;
+}
+
+static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ StrongARMUARTState *s = opaque;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ strongarm_uart_rx_push(s, buf[i]);
+ }
+
+ /* call the timeout receive callback in 3 char transmit time */
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static void strongarm_uart_event(void *opaque, int event)
+{
+ StrongARMUARTState *s = opaque;
+ if (event == CHR_EVENT_BREAK) {
+ s->utcr3 |= (1 << 3);
+ strongarm_uart_rx_push(s, RX_FIFO_FRE);
+ s->wait_break_end = true;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ }
+}
+
+static void strongarm_uart_tx(void *opaque)
+{
+ StrongARMUARTState *s = opaque;
+ uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ if (s->utcr3 & (1 << 5)) /* loopback */ {
+ strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1);
+ } else if (s->chr) {
+ qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1);
+ }
+
+ s->tx_start = (s->tx_start + 1) % 8;
+ s->tx_len--;
+ if (s->tx_len) {
+ qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time);
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ StrongARMUARTState *s = opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case UTCR0:
+ return s->utcr0;
+
+ case UTCR1:
+ return s->brd >> 8;
+
+ case UTCR2:
+ return s->brd & 0xff;
+
+ case UTCR3:
+ return s->utcr3;
+
+ case UTDR:
+ if (s->rx_len != 0) {
+ ret = s->rx_fifo[s->rx_start];
+ s->rx_start = (s->rx_start + 1) % 12;
+ s->rx_len--;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ return ret;
+ }
+ return 0;
+
+ case UTSR0:
+ return s->utsr0;
+
+ case UTSR1:
+ return s->utsr1;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ return 0;
+ }
+}
+
+static void strongarm_uart_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ StrongARMUARTState *s = opaque;
+
+ switch (addr) {
+ case UTCR0:
+ s->utcr0 = value & 0x7f;
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR1:
+ s->brd = (s->brd & 0xff) | ((value & 0xf) << 8);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR2:
+ s->brd = (s->brd & 0xf00) | (value & 0xff);
+ strongarm_uart_update_parameters(s);
+ break;
+
+ case UTCR3:
+ s->utcr3 = value & 0x3f;
+ if ((s->utcr3 & (1 << 0)) == 0) {
+ s->rx_len = 0;
+ }
+ if ((s->utcr3 & (1 << 1)) == 0) {
+ s->tx_len = 0;
+ }
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ case UTDR:
+ if ((s->utcr3 & (1 << 1)) && s->tx_len != 8) {
+ s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value;
+ s->tx_len++;
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+ if (s->tx_len == 1) {
+ strongarm_uart_tx(s);
+ }
+ }
+ break;
+
+ case UTSR0:
+ s->utsr0 = s->utsr0 & ~(value & 0x1c);
+ strongarm_uart_update_int_status(s);
+ break;
+
+ default:
+ printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const strongarm_uart_readfn[] = {
+ strongarm_uart_read,
+ strongarm_uart_read,
+ strongarm_uart_read,
+};
+
+static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = {
+ strongarm_uart_write,
+ strongarm_uart_write,
+ strongarm_uart_write,
+};
+
+static int strongarm_uart_init(SysBusDevice *dev)
+{
+ StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(strongarm_uart_readfn,
+ strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x10000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s);
+ s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr,
+ strongarm_uart_can_receive,
+ strongarm_uart_receive,
+ strongarm_uart_event,
+ s);
+ }
+
+ return 0;
+}
+
+static void strongarm_uart_reset(DeviceState *dev)
+{
+ StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev);
+
+ s->utcr0 = 0x8; /* 8 data, no parity */
+ s->brd = 23; /* 9600 */
+ s->utcr3 = 0x3; /* enable send & recv -- this actually violates spec */
+
+ s->rx_len = s->tx_len = 0;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+}
+
+static int strongarm_uart_post_load(void *opaque, int version_id)
+{
+ StrongARMUARTState *s = opaque;
+
+ strongarm_uart_update_parameters(s);
+ strongarm_uart_update_status(s);
+ strongarm_uart_update_int_status(s);
+
+ /* tx and restart timer */
+ if (s->tx_len) {
+ strongarm_uart_tx(s);
+ }
+
+ /* restart rx timeout timer */
+ if (s->rx_len) {
+ qemu_mod_timer(s->rx_timeout_timer,
+ qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_strongarm_uart_regs = {
+ .name = "strongarm-rtc",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = strongarm_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(utcr0, StrongARMUARTState),
+ VMSTATE_UINT16(brd, StrongARMUARTState),
+ VMSTATE_UINT8(utcr3, StrongARMUARTState),
+ VMSTATE_UINT8(utsr0, StrongARMUARTState),
+ VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8),
+ VMSTATE_UINT8(tx_start, StrongARMUARTState),
+ VMSTATE_UINT8(tx_len, StrongARMUARTState),
+ VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12),
+ VMSTATE_UINT8(rx_start, StrongARMUARTState),
+ VMSTATE_UINT8(rx_len, StrongARMUARTState),
+ VMSTATE_BOOL(wait_break_end, StrongARMUARTState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo strongarm_uart_info = {
+ .init = strongarm_uart_init,
+ .qdev.name = "strongarm-uart",
+ .qdev.desc = "StrongARM UART controller",
+ .qdev.size = sizeof(StrongARMUARTState),
+ .qdev.reset = strongarm_uart_reset,
+ .qdev.vmsd = &vmstate_strongarm_uart_regs,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+/* Main CPU functions */
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev)
+{
+ StrongARMState *s;
+ qemu_irq *pic;
+ int i;
+
+ s = qemu_mallocz(sizeof(StrongARMState));
+
+ if (!rev) {
+ rev = "sa1110-b5";
+ }
+
+ if (strncmp(rev, "sa1110", 6)) {
+ error_report("Machine requires a SA1110 processor.\n");
+ exit(1);
+ }
+
+ s->env = cpu_init(rev);
+
+ if (!s->env) {
+ error_report("Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ cpu_register_physical_memory(SA_SDCS0,
+ sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram",
+ sdram_size) | IO_MEM_RAM);
+
+ pic = arm_pic_init_cpu(s->env);
+ s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000,
+ pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL);
+
+ sysbus_create_varargs("pxa25x-timer", 0x90000000,
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC0),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC1),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC2),
+ qdev_get_gpio_in(s->pic, SA_PIC_OSTC3),
+ NULL);
+
+ sysbus_create_simple("strongarm-rtc", 0x90010000,
+ qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM));
+
+ s->gpio = strongarm_gpio_init(0x90040000, s->pic);
+
+ s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL);
+
+ for (i = 0; sa_serial[i].io_base; i++) {
+ DeviceState *dev = qdev_create(NULL, "strongarm-uart");
+ qdev_prop_set_chr(dev, "chardev", serial_hds[i]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ sa_serial[i].io_base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0,
+ qdev_get_gpio_in(s->pic, sa_serial[i].irq));
+ }
+
+ return s;
+}
+
+static void strongarm_register_devices(void)
+{
+ sysbus_register_withprop(&strongarm_pic_info);
+ sysbus_register_withprop(&strongarm_rtc_sysbus_info);
+ sysbus_register_withprop(&strongarm_gpio_info);
+ sysbus_register_withprop(&strongarm_ppc_info);
+ sysbus_register_withprop(&strongarm_uart_info);
+}
+device_init(strongarm_register_devices)
diff --git a/hw/strongarm.h b/hw/strongarm.h
new file mode 100644
index 0000000..ce7a3d7
--- /dev/null
+++ b/hw/strongarm.h
@@ -0,0 +1,62 @@
+#ifndef _STRONGARM_H
+#define _STRONGARM_H
+
+#define SA_CS0 0x00000000
+#define SA_CS1 0x08000000
+#define SA_CS2 0x10000000
+#define SA_CS3 0x18000000
+#define SA_PCMCIA_CS0 0x20000000
+#define SA_PCMCIA_CS1 0x30000000
+#define SA_CS4 0x40000000
+#define SA_CS5 0x48000000
+/* system registers here */
+#define SA_SDCS0 0xc0000000
+#define SA_SDCS1 0xc8000000
+#define SA_SDCS2 0xd0000000
+#define SA_SDCS3 0xd8000000
+
+enum {
+ SA_PIC_GPIO0_EDGE = 0,
+ SA_PIC_GPIO1_EDGE,
+ SA_PIC_GPIO2_EDGE,
+ SA_PIC_GPIO3_EDGE,
+ SA_PIC_GPIO4_EDGE,
+ SA_PIC_GPIO5_EDGE,
+ SA_PIC_GPIO6_EDGE,
+ SA_PIC_GPIO7_EDGE,
+ SA_PIC_GPIO8_EDGE,
+ SA_PIC_GPIO9_EDGE,
+ SA_PIC_GPIO10_EDGE,
+ SA_PIC_GPIOX_EDGE,
+ SA_PIC_LCD,
+ SA_PIC_UDC,
+ SA_PIC_RSVD1,
+ SA_PIC_UART1,
+ SA_PIC_UART2,
+ SA_PIC_UART3,
+ SA_PIC_MCP,
+ SA_PIC_SSP,
+ SA_PIC_DMA_CH0,
+ SA_PIC_DMA_CH1,
+ SA_PIC_DMA_CH2,
+ SA_PIC_DMA_CH3,
+ SA_PIC_DMA_CH4,
+ SA_PIC_DMA_CH5,
+ SA_PIC_OSTC0,
+ SA_PIC_OSTC1,
+ SA_PIC_OSTC2,
+ SA_PIC_OSTC3,
+ SA_PIC_RTC_HZ,
+ SA_PIC_RTC_ALARM,
+};
+
+typedef struct {
+ CPUState *env;
+ DeviceState *pic;
+ DeviceState *gpio;
+ DeviceState *ppc;
+} StrongARMState;
+
+StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev);
+
+#endif
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index e247a7a..d5af644 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -363,6 +363,7 @@ enum arm_features {
ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */
ARM_FEATURE_V4T,
ARM_FEATURE_V5,
+ ARM_FEATURE_STRONGARM,
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -393,6 +394,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
#define ARM_CPUID_ARM946 0x41059461
#define ARM_CPUID_TI915T 0x54029152
#define ARM_CPUID_TI925T 0x54029252
+#define ARM_CPUID_SA1100 0x4401A11B
+#define ARM_CPUID_SA1110 0x6901B119
#define ARM_CPUID_PXA250 0x69052100
#define ARM_CPUID_PXA255 0x69052d00
#define ARM_CPUID_PXA260 0x69052903
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 49ff5cf..0e0f5d3 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -215,6 +215,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
env->cp15.c0_cachetype = 0xd172172;
env->cp15.c1_sys = 0x00000078;
break;
+ case ARM_CPUID_SA1100:
+ case ARM_CPUID_SA1110:
+ set_feature(env, ARM_FEATURE_STRONGARM);
+ env->cp15.c1_sys = 0x00000070;
+ break;
default:
cpu_abort(env, "Bad CPU ID: %x\n", id);
break;
@@ -375,6 +380,8 @@ static const struct arm_cpu_t arm_cpu_names[] = {
{ ARM_CPUID_CORTEXA9, "cortex-a9"},
{ ARM_CPUID_TI925T, "ti925t" },
{ ARM_CPUID_PXA250, "pxa250" },
+ { ARM_CPUID_SA1100, "sa1100" },
+ { ARM_CPUID_SA1110, "sa1110" },
{ ARM_CPUID_PXA255, "pxa255" },
{ ARM_CPUID_PXA260, "pxa260" },
{ ARM_CPUID_PXA261, "pxa261" },
@@ -1546,6 +1553,8 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
case 9:
if (arm_feature(env, ARM_FEATURE_OMAPCP))
break;
+ if (arm_feature(env, ARM_FEATURE_STRONGARM))
+ break; /* Ignore ReadBuffer access */
switch (crm) {
case 0: /* Cache lockdown. */
switch (op1) {
--
1.7.4.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
end of thread, other threads:[~2011-04-11 15:18 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-04-04 13:38 [Qemu-devel] [PATCH 0/3] StrongARM/collie support Dmitry Eremin-Solenikov
2011-04-04 13:38 ` [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-04-06 13:56 ` Peter Maydell
2011-04-06 17:41 ` Dmitry Eremin-Solenikov
2011-04-06 17:50 ` Peter Maydell
2011-04-10 11:25 ` Aurelien Jarno
2011-04-10 12:24 ` Marek Vasut
2011-04-11 15:18 ` Dmitry Eremin-Solenikov
2011-04-04 13:38 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
2011-04-10 11:40 ` Aurelien Jarno
2011-04-04 13:38 ` [Qemu-devel] [PATCH 3/3] Basic implementation of Sharp Zaurus SL-5500 collie PDA Dmitry Eremin-Solenikov
2011-04-10 11:41 ` Aurelien Jarno
-- strict thread matches above, loose matches on Subject: below --
2011-04-04 12:15 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-04-04 12:15 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
2011-03-30 11:41 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-03-30 11:41 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
2011-03-29 16:58 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-03-29 16:58 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
2011-03-24 22:07 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-03-24 22:07 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
2011-03-24 10:22 [Qemu-devel] [PATCH 1/3] arm: basic support for ARMv4/ARMv4T emulation Dmitry Eremin-Solenikov
2011-03-24 10:22 ` [Qemu-devel] [PATCH 2/3] Implement basic part of SA-1110/SA-1100 Dmitry Eremin-Solenikov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).