* [Qemu-devel] [PULL 01/21] loader: Fix incorrect parameter name in load_image_mr() macro
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 02/21] target-arm: Implement MRS (banked) and MSR (banked) instructions Peter Maydell
` (21 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Jens Wiklander <jens.wiklander@linaro.org>
Fix a typo in the load_image_mr() macro: 'mr' was written when
the parameter name is '_mr'. (This had no visible effects since
the single use of the macro used 'mr' as the argument.)
Fixes 76151cacfe956248a25b38b5e8429465584f47bb "loader: Add
load_image_mr() to load ROM image to a MemoryRegion"
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
include/hw/loader.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/hw/loader.h b/include/hw/loader.h
index 0ba7808..b3d1358 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -137,7 +137,7 @@ void hmp_info_roms(Monitor *mon, const QDict *qdict);
#define rom_add_blob_fixed(_f, _b, _l, _a) \
rom_add_blob(_f, _b, _l, _l, _a, NULL, NULL, NULL)
#define rom_add_file_mr(_f, _mr, _i) \
- rom_add_file(_f, NULL, 0, _i, false, mr)
+ rom_add_file(_f, NULL, 0, _i, false, _mr)
#define PC_ROM_MIN_VGA 0xc0000
#define PC_ROM_MIN_OPTION 0xc8000
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 02/21] target-arm: Implement MRS (banked) and MSR (banked) instructions
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 01/21] loader: Fix incorrect parameter name in load_image_mr() macro Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 03/21] target-arm: Fix translation level on early translation faults Peter Maydell
` (20 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
Starting with the ARMv7 Virtualization Extensions, the A32 and T32
instruction sets provide instructions "MSR (banked)" and "MRS
(banked)" which can be used to access registers for a mode other
than the current one:
* R<m>_<mode>
* ELR_hyp
* SPSR_<mode>
Implement the missing instructions.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Acked-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Message-id: 1456762734-23939-1-git-send-email-peter.maydell@linaro.org
---
target-arm/helper.h | 3 +
target-arm/op_helper.c | 120 ++++++++++++++++++++++++
target-arm/translate.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 366 insertions(+), 3 deletions(-)
diff --git a/target-arm/helper.h b/target-arm/helper.h
index e3d09d9..84aa637 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -77,6 +77,9 @@ DEF_HELPER_1(exception_return, void, env)
DEF_HELPER_2(get_r13_banked, i32, env, i32)
DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
+DEF_HELPER_3(mrs_banked, i32, env, i32, i32)
+DEF_HELPER_4(msr_banked, void, env, i32, i32, i32)
+
DEF_HELPER_2(get_user_reg, i32, env, i32)
DEF_HELPER_3(set_user_reg, void, env, i32, i32)
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 92fde0a..d626ff1 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -494,6 +494,126 @@ uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
}
}
+static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode,
+ uint32_t regno)
+{
+ /* Raise an exception if the requested access is one of the UNPREDICTABLE
+ * cases; otherwise return. This broadly corresponds to the pseudocode
+ * BankedRegisterAccessValid() and SPSRAccessValid(),
+ * except that we have already handled some cases at translate time.
+ */
+ int curmode = env->uncached_cpsr & CPSR_M;
+
+ if (curmode == tgtmode) {
+ goto undef;
+ }
+
+ if (tgtmode == ARM_CPU_MODE_USR) {
+ switch (regno) {
+ case 8 ... 12:
+ if (curmode != ARM_CPU_MODE_FIQ) {
+ goto undef;
+ }
+ break;
+ case 13:
+ if (curmode == ARM_CPU_MODE_SYS) {
+ goto undef;
+ }
+ break;
+ case 14:
+ if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) {
+ goto undef;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (tgtmode == ARM_CPU_MODE_HYP) {
+ switch (regno) {
+ case 17: /* ELR_Hyp */
+ if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) {
+ goto undef;
+ }
+ break;
+ default:
+ if (curmode != ARM_CPU_MODE_MON) {
+ goto undef;
+ }
+ break;
+ }
+ }
+
+ return;
+
+undef:
+ raise_exception(env, EXCP_UDEF, syn_uncategorized(),
+ exception_target_el(env));
+}
+
+void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode,
+ uint32_t regno)
+{
+ msr_mrs_banked_exc_checks(env, tgtmode, regno);
+
+ switch (regno) {
+ case 16: /* SPSRs */
+ env->banked_spsr[bank_number(tgtmode)] = value;
+ break;
+ case 17: /* ELR_Hyp */
+ env->elr_el[2] = value;
+ break;
+ case 13:
+ env->banked_r13[bank_number(tgtmode)] = value;
+ break;
+ case 14:
+ env->banked_r14[bank_number(tgtmode)] = value;
+ break;
+ case 8 ... 12:
+ switch (tgtmode) {
+ case ARM_CPU_MODE_USR:
+ env->usr_regs[regno - 8] = value;
+ break;
+ case ARM_CPU_MODE_FIQ:
+ env->fiq_regs[regno - 8] = value;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno)
+{
+ msr_mrs_banked_exc_checks(env, tgtmode, regno);
+
+ switch (regno) {
+ case 16: /* SPSRs */
+ return env->banked_spsr[bank_number(tgtmode)];
+ case 17: /* ELR_Hyp */
+ return env->elr_el[2];
+ case 13:
+ return env->banked_r13[bank_number(tgtmode)];
+ case 14:
+ return env->banked_r14[bank_number(tgtmode)];
+ case 8 ... 12:
+ switch (tgtmode) {
+ case ARM_CPU_MODE_USR:
+ return env->usr_regs[regno - 8];
+ case ARM_CPU_MODE_FIQ:
+ return env->fiq_regs[regno - 8];
+ default:
+ g_assert_not_reached();
+ }
+ default:
+ g_assert_not_reached();
+ }
+}
+
void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome,
uint32_t isread)
{
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 025c7a5..2827519 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -4160,6 +4160,195 @@ static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val
return gen_set_psr(s, mask, spsr, tmp);
}
+static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn,
+ int *tgtmode, int *regno)
+{
+ /* Decode the r and sysm fields of MSR/MRS banked accesses into
+ * the target mode and register number, and identify the various
+ * unpredictable cases.
+ * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if:
+ * + executed in user mode
+ * + using R15 as the src/dest register
+ * + accessing an unimplemented register
+ * + accessing a register that's inaccessible at current PL/security state*
+ * + accessing a register that you could access with a different insn
+ * We choose to UNDEF in all these cases.
+ * Since we don't know which of the various AArch32 modes we are in
+ * we have to defer some checks to runtime.
+ * Accesses to Monitor mode registers from Secure EL1 (which implies
+ * that EL3 is AArch64) must trap to EL3.
+ *
+ * If the access checks fail this function will emit code to take
+ * an exception and return false. Otherwise it will return true,
+ * and set *tgtmode and *regno appropriately.
+ */
+ int exc_target = default_exception_el(s);
+
+ /* These instructions are present only in ARMv8, or in ARMv7 with the
+ * Virtualization Extensions.
+ */
+ if (!arm_dc_feature(s, ARM_FEATURE_V8) &&
+ !arm_dc_feature(s, ARM_FEATURE_EL2)) {
+ goto undef;
+ }
+
+ if (IS_USER(s) || rn == 15) {
+ goto undef;
+ }
+
+ /* The table in the v8 ARM ARM section F5.2.3 describes the encoding
+ * of registers into (r, sysm).
+ */
+ if (r) {
+ /* SPSRs for other modes */
+ switch (sysm) {
+ case 0xe: /* SPSR_fiq */
+ *tgtmode = ARM_CPU_MODE_FIQ;
+ break;
+ case 0x10: /* SPSR_irq */
+ *tgtmode = ARM_CPU_MODE_IRQ;
+ break;
+ case 0x12: /* SPSR_svc */
+ *tgtmode = ARM_CPU_MODE_SVC;
+ break;
+ case 0x14: /* SPSR_abt */
+ *tgtmode = ARM_CPU_MODE_ABT;
+ break;
+ case 0x16: /* SPSR_und */
+ *tgtmode = ARM_CPU_MODE_UND;
+ break;
+ case 0x1c: /* SPSR_mon */
+ *tgtmode = ARM_CPU_MODE_MON;
+ break;
+ case 0x1e: /* SPSR_hyp */
+ *tgtmode = ARM_CPU_MODE_HYP;
+ break;
+ default: /* unallocated */
+ goto undef;
+ }
+ /* We arbitrarily assign SPSR a register number of 16. */
+ *regno = 16;
+ } else {
+ /* general purpose registers for other modes */
+ switch (sysm) {
+ case 0x0 ... 0x6: /* 0b00xxx : r8_usr ... r14_usr */
+ *tgtmode = ARM_CPU_MODE_USR;
+ *regno = sysm + 8;
+ break;
+ case 0x8 ... 0xe: /* 0b01xxx : r8_fiq ... r14_fiq */
+ *tgtmode = ARM_CPU_MODE_FIQ;
+ *regno = sysm;
+ break;
+ case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */
+ *tgtmode = ARM_CPU_MODE_IRQ;
+ *regno = sysm & 1 ? 13 : 14;
+ break;
+ case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */
+ *tgtmode = ARM_CPU_MODE_SVC;
+ *regno = sysm & 1 ? 13 : 14;
+ break;
+ case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */
+ *tgtmode = ARM_CPU_MODE_ABT;
+ *regno = sysm & 1 ? 13 : 14;
+ break;
+ case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */
+ *tgtmode = ARM_CPU_MODE_UND;
+ *regno = sysm & 1 ? 13 : 14;
+ break;
+ case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */
+ *tgtmode = ARM_CPU_MODE_MON;
+ *regno = sysm & 1 ? 13 : 14;
+ break;
+ case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */
+ *tgtmode = ARM_CPU_MODE_HYP;
+ /* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */
+ *regno = sysm & 1 ? 13 : 17;
+ break;
+ default: /* unallocated */
+ goto undef;
+ }
+ }
+
+ /* Catch the 'accessing inaccessible register' cases we can detect
+ * at translate time.
+ */
+ switch (*tgtmode) {
+ case ARM_CPU_MODE_MON:
+ if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) {
+ goto undef;
+ }
+ if (s->current_el == 1) {
+ /* If we're in Secure EL1 (which implies that EL3 is AArch64)
+ * then accesses to Mon registers trap to EL3
+ */
+ exc_target = 3;
+ goto undef;
+ }
+ break;
+ case ARM_CPU_MODE_HYP:
+ /* Note that we can forbid accesses from EL2 here because they
+ * must be from Hyp mode itself
+ */
+ if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) {
+ goto undef;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return true;
+
+undef:
+ /* If we get here then some access check did not pass */
+ gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), exc_target);
+ return false;
+}
+
+static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn)
+{
+ TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
+ int tgtmode, regno;
+
+ if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) {
+ return;
+ }
+
+ /* Sync state because msr_banked() can raise exceptions */
+ gen_set_condexec(s);
+ gen_set_pc_im(s, s->pc - 4);
+ tcg_reg = load_reg(s, rn);
+ tcg_tgtmode = tcg_const_i32(tgtmode);
+ tcg_regno = tcg_const_i32(regno);
+ gen_helper_msr_banked(cpu_env, tcg_reg, tcg_tgtmode, tcg_regno);
+ tcg_temp_free_i32(tcg_tgtmode);
+ tcg_temp_free_i32(tcg_regno);
+ tcg_temp_free_i32(tcg_reg);
+ s->is_jmp = DISAS_UPDATE;
+}
+
+static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn)
+{
+ TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
+ int tgtmode, regno;
+
+ if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) {
+ return;
+ }
+
+ /* Sync state because mrs_banked() can raise exceptions */
+ gen_set_condexec(s);
+ gen_set_pc_im(s, s->pc - 4);
+ tcg_reg = tcg_temp_new_i32();
+ tcg_tgtmode = tcg_const_i32(tgtmode);
+ tcg_regno = tcg_const_i32(regno);
+ gen_helper_mrs_banked(tcg_reg, cpu_env, tcg_tgtmode, tcg_regno);
+ tcg_temp_free_i32(tcg_tgtmode);
+ tcg_temp_free_i32(tcg_regno);
+ store_reg(s, rn, tcg_reg);
+ s->is_jmp = DISAS_UPDATE;
+}
+
/* Generate an old-style exception return. Marks pc as dead. */
static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
{
@@ -8022,7 +8211,26 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
sh = (insn >> 4) & 0xf;
rm = insn & 0xf;
switch (sh) {
- case 0x0: /* move program status register */
+ case 0x0: /* MSR, MRS */
+ if (insn & (1 << 9)) {
+ /* MSR (banked) and MRS (banked) */
+ int sysm = extract32(insn, 16, 4) |
+ (extract32(insn, 8, 1) << 4);
+ int r = extract32(insn, 22, 1);
+
+ if (op1 & 1) {
+ /* MSR (banked) */
+ gen_msr_banked(s, r, sysm, rm);
+ } else {
+ /* MRS (banked) */
+ int rd = extract32(insn, 12, 4);
+
+ gen_mrs_banked(s, r, sysm, rd);
+ }
+ break;
+ }
+
+ /* MSR, MRS (for PSRs) */
if (op1 & 1) {
/* PSR = reg */
tmp = load_reg(s, rm);
@@ -10133,6 +10341,18 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
if (arm_dc_feature(s, ARM_FEATURE_M)) {
goto illegal_op;
}
+
+ if (extract32(insn, 5, 1)) {
+ /* MSR (banked) */
+ int sysm = extract32(insn, 8, 4) |
+ (extract32(insn, 4, 1) << 4);
+ int r = op & 1;
+
+ gen_msr_banked(s, r, sysm, rm);
+ break;
+ }
+
+ /* MSR (for PSRs) */
tmp = load_reg(s, rn);
if (gen_set_psr(s,
msr_mask(s, (insn >> 8) & 0xf, op == 1),
@@ -10205,7 +10425,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
tcg_gen_subi_i32(tmp, tmp, insn & 0xff);
gen_exception_return(s, tmp);
break;
- case 6: /* mrs cpsr. */
+ case 6: /* MRS */
+ if (extract32(insn, 5, 1)) {
+ /* MRS (banked) */
+ int sysm = extract32(insn, 16, 4) |
+ (extract32(insn, 4, 1) << 4);
+
+ gen_mrs_banked(s, 0, sysm, rd);
+ break;
+ }
+
+ /* mrs cpsr */
tmp = tcg_temp_new_i32();
if (arm_dc_feature(s, ARM_FEATURE_M)) {
addr = tcg_const_i32(insn & 0xff);
@@ -10216,7 +10446,17 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
}
store_reg(s, rd, tmp);
break;
- case 7: /* mrs spsr. */
+ case 7: /* MRS */
+ if (extract32(insn, 5, 1)) {
+ /* MRS (banked) */
+ int sysm = extract32(insn, 16, 4) |
+ (extract32(insn, 4, 1) << 4);
+
+ gen_mrs_banked(s, 1, sysm, rd);
+ break;
+ }
+
+ /* mrs spsr. */
/* Not accessible in user mode. */
if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) {
goto illegal_op;
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 03/21] target-arm: Fix translation level on early translation faults
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 01/21] loader: Fix incorrect parameter name in load_image_mr() macro Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 02/21] target-arm: Implement MRS (banked) and MSR (banked) instructions Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 04/21] arm: virt: Add an abstract ARM virt machine type Peter Maydell
` (19 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Sergey Sorokin <afarallax@yandex.ru>
Qemu reports translation fault on 1st level instead of 0th level in case of
AArch64 address translation if the translation table walk is disabled or
the address is in the gap between the two regions.
Signed-off-by: Sergey Sorokin <afarallax@yandex.ru>
Message-id: 1457527503-25958-1-git-send-email-afarallax@yandex.ru
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target-arm/helper.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index eaded41..19d5d52 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -7237,7 +7237,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
CPUState *cs = CPU(cpu);
/* Read an LPAE long-descriptor translation table. */
MMUFaultType fault_type = translation_fault;
- uint32_t level = 1;
+ uint32_t level;
uint32_t epd = 0;
int32_t t0sz, t1sz;
uint32_t tg;
@@ -7248,7 +7248,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
target_ulong page_size;
uint32_t attrs;
int32_t stride = 9;
- int32_t va_size = 32;
+ int32_t va_size;
int inputsize;
int32_t tbi = 0;
TCR *tcr = regime_tcr(env, mmu_idx);
@@ -7264,6 +7264,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
* support for those page table walks.
*/
if (arm_el_is_aa64(env, el)) {
+ level = 0;
va_size = 64;
if (el > 1) {
if (mmu_idx != ARMMMUIdx_S2NS) {
@@ -7285,6 +7286,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
ttbr1_valid = false;
}
} else {
+ level = 1;
+ va_size = 32;
/* There is no TTBR1 for EL2 */
if (el == 2) {
ttbr1_valid = false;
@@ -7407,27 +7410,26 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
/* For stage 2 translations the starting level is specified by the
* VTCR_EL2.SL0 field (whose interpretation depends on the page size)
*/
- int startlevel = extract32(tcr->raw_tcr, 6, 2);
+ uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2);
+ uint32_t startlevel;
bool ok;
if (va_size == 32 || stride == 9) {
/* AArch32 or 4KB pages */
- level = 2 - startlevel;
+ startlevel = 2 - sl0;
} else {
/* 16KB or 64KB pages */
- level = 3 - startlevel;
+ startlevel = 3 - sl0;
}
/* Check that the starting level is valid. */
- ok = check_s2_mmu_setup(cpu, va_size == 64, level, inputsize, stride);
+ ok = check_s2_mmu_setup(cpu, va_size == 64, startlevel,
+ inputsize, stride);
if (!ok) {
- /* AArch64 reports these as level 0 faults.
- * AArch32 reports these as level 1 faults.
- */
- level = va_size == 64 ? 0 : 1;
fault_type = translation_fault;
goto do_fault;
}
+ level = startlevel;
}
/* Clear the vaddr bits which aren't part of the within-region address,
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 04/21] arm: virt: Add an abstract ARM virt machine type
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (2 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 03/21] target-arm: Fix translation level on early translation faults Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 05/21] arm: virt: Move machine class init code to the abstract " Peter Maydell
` (18 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Wei Huang <wei@redhat.com>
In preparation for future ARM virt machine types, this patch creates
an abstract type for all ARM machines. The current machine type in
QEMU (i.e. "virt") is renamed to "virt-2.6", whose naming scheme is
similar to other architectures. For the purpose of backward compatibility,
"virt" is converted to an alias, pointing to "virt-2.6". With this patch,
"qemu -M ?" lists the following virtual machine types along with others:
virt QEMU 2.6 ARM Virtual Machine (alias of virt-2.6)
virt-2.6 QEMU 2.6 ARM Virtual Machine
Signed-off-by: Wei Huang <wei@redhat.com>
Message-id: 1457717778-17727-2-git-send-email-wei@redhat.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/virt.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 8c6c996..be9bbfb 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1345,6 +1345,19 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
}
}
+static void virt_machine_class_init(ObjectClass *oc, void *data)
+{
+}
+
+static const TypeInfo virt_machine_info = {
+ .name = TYPE_VIRT_MACHINE,
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .instance_size = sizeof(VirtMachineState),
+ .class_size = sizeof(VirtMachineClass),
+ .class_init = virt_machine_class_init,
+};
+
static void virt_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -1382,7 +1395,8 @@ static void virt_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- mc->desc = "ARM Virtual Machine",
+ mc->desc = "QEMU 2.6 ARM Virtual Machine";
+ mc->alias = "virt";
mc->init = machvirt_init;
/* Start max_cpus at the maximum QEMU supports. We'll further restrict
* it later in machvirt_init, where we have more information about the
@@ -1396,16 +1410,15 @@ static void virt_class_init(ObjectClass *oc, void *data)
}
static const TypeInfo machvirt_info = {
- .name = TYPE_VIRT_MACHINE,
- .parent = TYPE_MACHINE,
- .instance_size = sizeof(VirtMachineState),
+ .name = MACHINE_TYPE_NAME("virt-2.6"),
+ .parent = TYPE_VIRT_MACHINE,
.instance_init = virt_instance_init,
- .class_size = sizeof(VirtMachineClass),
.class_init = virt_class_init,
};
static void machvirt_machine_init(void)
{
+ type_register_static(&virt_machine_info);
type_register_static(&machvirt_info);
}
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 05/21] arm: virt: Move machine class init code to the abstract machine type
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (3 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 04/21] arm: virt: Add an abstract ARM virt machine type Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 06/21] i.MX: Allow GPT timer to rollover Peter Maydell
` (17 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Wei Huang <wei@redhat.com>
This patch moves the common class initialization code from
"virt-2.6" to the new abstract class. An empty property is added to
"virt-2.6" machine. In the meanwhile, related funtions are renamed
to "virt_2_6_*" for consistency.
Signed-off-by: Wei Huang <wei@redhat.com>
Message-id: 1457717778-17727-3-git-send-email-wei@redhat.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/virt.c | 34 ++++++++++++++++++++--------------
1 file changed, 20 insertions(+), 14 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index be9bbfb..8c3ac0d 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1347,6 +1347,18 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
static void virt_machine_class_init(ObjectClass *oc, void *data)
{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = machvirt_init;
+ /* Start max_cpus at the maximum QEMU supports. We'll further restrict
+ * it later in machvirt_init, where we have more information about the
+ * configuration of the particular instance.
+ */
+ mc->max_cpus = MAX_CPUMASK_BITS;
+ mc->has_dynamic_sysbus = true;
+ mc->block_default_type = IF_VIRTIO;
+ mc->no_cdrom = 1;
+ mc->pci_allow_0_address = true;
}
static const TypeInfo virt_machine_info = {
@@ -1358,7 +1370,7 @@ static const TypeInfo virt_machine_info = {
.class_init = virt_machine_class_init,
};
-static void virt_instance_init(Object *obj)
+static void virt_2_6_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -1391,29 +1403,23 @@ static void virt_instance_init(Object *obj)
"Valid values are 2, 3 and host", NULL);
}
-static void virt_class_init(ObjectClass *oc, void *data)
+static void virt_2_6_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ static GlobalProperty compat_props[] = {
+ { /* end of list */ }
+ };
mc->desc = "QEMU 2.6 ARM Virtual Machine";
mc->alias = "virt";
- mc->init = machvirt_init;
- /* Start max_cpus at the maximum QEMU supports. We'll further restrict
- * it later in machvirt_init, where we have more information about the
- * configuration of the particular instance.
- */
- mc->max_cpus = MAX_CPUMASK_BITS;
- mc->has_dynamic_sysbus = true;
- mc->block_default_type = IF_VIRTIO;
- mc->no_cdrom = 1;
- mc->pci_allow_0_address = true;
+ mc->compat_props = compat_props;
}
static const TypeInfo machvirt_info = {
.name = MACHINE_TYPE_NAME("virt-2.6"),
.parent = TYPE_VIRT_MACHINE,
- .instance_init = virt_instance_init,
- .class_init = virt_class_init,
+ .instance_init = virt_2_6_instance_init,
+ .class_init = virt_2_6_class_init,
};
static void machvirt_machine_init(void)
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 06/21] i.MX: Allow GPT timer to rollover.
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (4 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 05/21] arm: virt: Move machine class init code to the abstract " Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 07/21] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency Peter Maydell
` (16 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Jean-Christophe Dubois <jcd@tribudubois.net>
GPT timer need to rollover when it reaches 0xffffffff.
It also need to reset to 0 when in "restart mode" and crossing the
compare 1 register.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Message-id: 6e2b36117a249a78bf822dd59a390368f407136e.1456868959.git.jcd@tribudubois.net
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/timer/imx_gpt.c | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 5cdc3b3..916577b 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -134,7 +134,7 @@ static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg,
static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
{
uint32_t timeout = GPT_TIMER_MAX;
- uint32_t count = 0;
+ uint32_t count;
long long limit;
if (!(s->cr & GPT_CR_EN)) {
@@ -142,20 +142,23 @@ static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event)
return;
}
- if (event) {
- /* This is a timer event */
+ /* update the count */
+ count = imx_gpt_update_count(s);
- if ((s->cr & GPT_CR_FRR) && (s->next_timeout != GPT_TIMER_MAX)) {
- /*
- * if we are in free running mode and we have not reached
- * the GPT_TIMER_MAX limit, then update the count
+ if (event) {
+ /*
+ * This is an event (the ptimer reached 0 and stopped), and the
+ * timer counter is now equal to s->next_timeout.
+ */
+ if (!(s->cr & GPT_CR_FRR) && (count == s->ocr1)) {
+ /* We are in restart mode and we crossed the compare channel 1
+ * value. We need to reset the counter to 0.
*/
- count = imx_gpt_update_count(s);
+ count = s->cnt = s->next_timeout = 0;
+ } else if (count == GPT_TIMER_MAX) {
+ /* We reached GPT_TIMER_MAX so we need to rollover */
+ count = s->cnt = s->next_timeout = 0;
}
- } else {
- /* not a timer event, then just update the count */
-
- count = imx_gpt_update_count(s);
}
/* now, find the next timeout related to count */
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 07/21] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency.
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (5 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 06/21] i.MX: Allow GPT timer to rollover Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 08/21] i.MX: Remove CCM useless clock computation handling Peter Maydell
` (15 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Jean-Christophe Dubois <jcd@tribudubois.net>
This way all CCM clock defines/enums are named CLK_XXX
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Message-id: 8537df765c1713625c7a8b9aca4c7ca60b42e0c0.1456868959.git.jcd@tribudubois.net
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/misc/imx25_ccm.c | 2 +-
hw/misc/imx31_ccm.c | 2 +-
hw/timer/imx_epit.c | 2 +-
hw/timer/imx_gpt.c | 10 +++++-----
include/hw/misc/imx_ccm.h | 2 +-
5 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
index 498e84c..7a29c19 100644
--- a/hw/misc/imx25_ccm.c
+++ b/hw/misc/imx25_ccm.c
@@ -182,7 +182,7 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
DPRINTF("Clock = %d)\n", clock);
switch (clock) {
- case NOCLK:
+ case CLK_NONE:
break;
case CLK_MPLL:
freq = imx25_ccm_get_mpll_clk(dev);
diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
index 17640bf..7a8fcd2 100644
--- a/hw/misc/imx31_ccm.c
+++ b/hw/misc/imx31_ccm.c
@@ -209,7 +209,7 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
uint32_t freq = 0;
switch (clock) {
- case NOCLK:
+ case CLK_NONE:
break;
case CLK_MCU:
freq = imx31_ccm_get_mcu_clk(dev);
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index 38fbf27..9f26ba8 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -52,7 +52,7 @@ static char const *imx_epit_reg_name(uint32_t reg)
* These are typical.
*/
static const IMXClk imx_epit_clocks[] = {
- NOCLK, /* 00 disabled */
+ CLK_NONE, /* 00 disabled */
CLK_IPG, /* 01 ipg_clk, ~532MHz */
CLK_IPG, /* 10 ipg_clk_highfreq */
CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 916577b..40db63c 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -81,14 +81,14 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
};
static const IMXClk imx_gpt_clocks[] = {
- NOCLK, /* 000 No clock source */
+ CLK_NONE, /* 000 No clock source */
CLK_IPG, /* 001 ipg_clk, 532MHz*/
CLK_IPG, /* 010 ipg_clk_highfreq */
- NOCLK, /* 011 not defined */
+ CLK_NONE, /* 011 not defined */
CLK_32k, /* 100 ipg_clk_32k */
- NOCLK, /* 101 not defined */
- NOCLK, /* 110 not defined */
- NOCLK, /* 111 not defined */
+ CLK_NONE, /* 101 not defined */
+ CLK_NONE, /* 110 not defined */
+ CLK_NONE, /* 111 not defined */
};
static void imx_gpt_set_freq(IMXGPTState *s)
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 5c4b795..74e2705 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -43,7 +43,7 @@ typedef struct IMXCCMState {
} IMXCCMState;
typedef enum {
- NOCLK,
+ CLK_NONE,
CLK_MPLL,
CLK_UPLL,
CLK_MCU,
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 08/21] i.MX: Remove CCM useless clock computation handling.
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (6 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 07/21] i.MX: Rename CCM NOCLK to CLK_NONE for naming consistency Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 09/21] i.MX: Add the CLK_IPG_HIGH clock Peter Maydell
` (14 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Jean-Christophe Dubois <jcd@tribudubois.net>
Most clocks supported by the CCM are useless to the qemu framework.
Only clocks related to timers (EPIT, GPT, PWM, WATCHDOG, ...) are usefull
to QEMU code.
Therefore this patch removes clock computation handling for all clocks but:
* CLK_NONE,
* CLK_IPG,
* CLK_32k
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Message-id: 9e7222efb349801032e60c0f6b0fbad0e5dcf648.1456868959.git.jcd@tribudubois.net
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/misc/imx25_ccm.c | 26 --------------------------
hw/misc/imx31_ccm.c | 32 --------------------------------
include/hw/misc/imx_ccm.h | 7 -------
3 files changed, 65 deletions(-)
diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
index 7a29c19..e138fc6 100644
--- a/hw/misc/imx25_ccm.c
+++ b/hw/misc/imx25_ccm.c
@@ -120,20 +120,6 @@ static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
return freq;
}
-static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev)
-{
- uint32_t freq = 0;
- IMX25CCMState *s = IMX25_CCM(dev);
-
- if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) {
- freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ);
- }
-
- DPRINTF("freq = %d\n", freq);
-
- return freq;
-}
-
static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
{
uint32_t freq;
@@ -184,18 +170,6 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
switch (clock) {
case CLK_NONE:
break;
- case CLK_MPLL:
- freq = imx25_ccm_get_mpll_clk(dev);
- break;
- case CLK_UPLL:
- freq = imx25_ccm_get_upll_clk(dev);
- break;
- case CLK_MCU:
- freq = imx25_ccm_get_mcu_clk(dev);
- break;
- case CLK_AHB:
- freq = imx25_ccm_get_ahb_clk(dev);
- break;
case CLK_IPG:
freq = imx25_ccm_get_ipg_clk(dev);
break;
diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
index 7a8fcd2..a5caabb 100644
--- a/hw/misc/imx31_ccm.c
+++ b/hw/misc/imx31_ccm.c
@@ -152,32 +152,6 @@ static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
return freq;
}
-static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev)
-{
- uint32_t freq;
- IMX31CCMState *s = IMX31_CCM(dev);
-
- freq = imx31_ccm_get_mcu_main_clk(dev)
- / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], MCU));
-
- DPRINTF("freq = %d\n", freq);
-
- return freq;
-}
-
-static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev)
-{
- uint32_t freq;
- IMX31CCMState *s = IMX31_CCM(dev);
-
- freq = imx31_ccm_get_mcu_main_clk(dev)
- / (1 + EXTRACT(s->reg[IMX31_CCM_PDR0_REG], HSP));
-
- DPRINTF("freq = %d\n", freq);
-
- return freq;
-}
-
static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
{
uint32_t freq;
@@ -211,12 +185,6 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
switch (clock) {
case CLK_NONE:
break;
- case CLK_MCU:
- freq = imx31_ccm_get_mcu_clk(dev);
- break;
- case CLK_HSP:
- freq = imx31_ccm_get_hsp_clk(dev);
- break;
case CLK_IPG:
freq = imx31_ccm_get_ipg_clk(dev);
break;
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 74e2705..378b78d 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -44,14 +44,7 @@ typedef struct IMXCCMState {
typedef enum {
CLK_NONE,
- CLK_MPLL,
- CLK_UPLL,
- CLK_MCU,
- CLK_HSP,
- CLK_MAX,
- CLK_AHB,
CLK_IPG,
- CLK_PER,
CLK_32k
} IMXClk;
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 09/21] i.MX: Add the CLK_IPG_HIGH clock
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (7 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 08/21] i.MX: Remove CCM useless clock computation handling Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 10/21] i.MX: Add i.MX6 CCM and ANALOG device Peter Maydell
` (13 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Jean-Christophe Dubois <jcd@tribudubois.net>
EPIT, GPT and other i.MX timers are using "abstract" clocks among which
a CLK_IPG_HIGH clock.
On i.MX25 and i.MX31 CLK_IPG and CLK_IPG_HIGH are mapped to the same clock
but on other SOC like i.MX6 they are mapped to distinct clocks.
This patch add the CLK_IPG_HIGH to prepare for SOC where these 2 clocks are
different.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Message-id: 224bf650194760284cb40630e985867e1373276a.1456868959.git.jcd@tribudubois.net
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/misc/imx25_ccm.c | 1 +
hw/misc/imx31_ccm.c | 1 +
hw/timer/imx_epit.c | 8 ++++----
hw/timer/imx_gpt.c | 16 ++++++++--------
include/hw/misc/imx_ccm.h | 1 +
5 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c
index e138fc6..225604d 100644
--- a/hw/misc/imx25_ccm.c
+++ b/hw/misc/imx25_ccm.c
@@ -171,6 +171,7 @@ static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
case CLK_NONE:
break;
case CLK_IPG:
+ case CLK_IPG_HIGH:
freq = imx25_ccm_get_ipg_clk(dev);
break;
case CLK_32k:
diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c
index a5caabb..80c1647 100644
--- a/hw/misc/imx31_ccm.c
+++ b/hw/misc/imx31_ccm.c
@@ -186,6 +186,7 @@ static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
case CLK_NONE:
break;
case CLK_IPG:
+ case CLK_IPG_HIGH:
freq = imx31_ccm_get_ipg_clk(dev);
break;
case CLK_32k:
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index 9f26ba8..f5836e2 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -52,10 +52,10 @@ static char const *imx_epit_reg_name(uint32_t reg)
* These are typical.
*/
static const IMXClk imx_epit_clocks[] = {
- CLK_NONE, /* 00 disabled */
- CLK_IPG, /* 01 ipg_clk, ~532MHz */
- CLK_IPG, /* 10 ipg_clk_highfreq */
- CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
+ CLK_NONE, /* 00 disabled */
+ CLK_IPG, /* 01 ipg_clk, ~532MHz */
+ CLK_IPG_HIGH, /* 10 ipg_clk_highfreq */
+ CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
};
/*
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 40db63c..ab2e213 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -81,14 +81,14 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
};
static const IMXClk imx_gpt_clocks[] = {
- CLK_NONE, /* 000 No clock source */
- CLK_IPG, /* 001 ipg_clk, 532MHz*/
- CLK_IPG, /* 010 ipg_clk_highfreq */
- CLK_NONE, /* 011 not defined */
- CLK_32k, /* 100 ipg_clk_32k */
- CLK_NONE, /* 101 not defined */
- CLK_NONE, /* 110 not defined */
- CLK_NONE, /* 111 not defined */
+ CLK_NONE, /* 000 No clock source */
+ CLK_IPG, /* 001 ipg_clk, 532MHz*/
+ CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */
+ CLK_NONE, /* 011 not defined */
+ CLK_32k, /* 100 ipg_clk_32k */
+ CLK_NONE, /* 101 not defined */
+ CLK_NONE, /* 110 not defined */
+ CLK_NONE, /* 111 not defined */
};
static void imx_gpt_set_freq(IMXGPTState *s)
diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h
index 378b78d..48a7afa 100644
--- a/include/hw/misc/imx_ccm.h
+++ b/include/hw/misc/imx_ccm.h
@@ -45,6 +45,7 @@ typedef struct IMXCCMState {
typedef enum {
CLK_NONE,
CLK_IPG,
+ CLK_IPG_HIGH,
CLK_32k
} IMXClk;
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 10/21] i.MX: Add i.MX6 CCM and ANALOG device.
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (8 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 09/21] i.MX: Add the CLK_IPG_HIGH clock Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 11/21] i.MX: Add missing descriptions in devices Peter Maydell
` (12 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Jean-Christophe Dubois <jcd@tribudubois.net>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Message-id: 9fa80b4d8c5d0f50c94e77d74f952a7a665e168f.1456868959.git.jcd@tribudubois.net
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/misc/Makefile.objs | 1 +
hw/misc/imx6_ccm.c | 774 +++++++++++++++++++++++++++++++++++++++++++++
include/hw/misc/imx6_ccm.h | 197 ++++++++++++
3 files changed, 972 insertions(+)
create mode 100644 hw/misc/imx6_ccm.c
create mode 100644 include/hw/misc/imx6_ccm.h
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ea6cd3c..44ac2e1 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -28,6 +28,7 @@ obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_IMX) += imx_ccm.o
obj-$(CONFIG_IMX) += imx31_ccm.o
obj-$(CONFIG_IMX) += imx25_ccm.o
+obj-$(CONFIG_IMX) += imx6_ccm.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c
new file mode 100644
index 0000000..4e1d49d
--- /dev/null
+++ b/hw/misc/imx6_ccm.c
@@ -0,0 +1,774 @@
+/*
+ * IMX6 Clock Control Module
+ *
+ * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * To get the timer frequencies right, we need to emulate at least part of
+ * the CCM.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx6_ccm.h"
+
+#ifndef DEBUG_IMX6_CCM
+#define DEBUG_IMX6_CCM 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX6_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static char const *imx6_ccm_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case CCM_CCR:
+ return "CCR";
+ case CCM_CCDR:
+ return "CCDR";
+ case CCM_CSR:
+ return "CSR";
+ case CCM_CCSR:
+ return "CCSR";
+ case CCM_CACRR:
+ return "CACRR";
+ case CCM_CBCDR:
+ return "CBCDR";
+ case CCM_CBCMR:
+ return "CBCMR";
+ case CCM_CSCMR1:
+ return "CSCMR1";
+ case CCM_CSCMR2:
+ return "CSCMR2";
+ case CCM_CSCDR1:
+ return "CSCDR1";
+ case CCM_CS1CDR:
+ return "CS1CDR";
+ case CCM_CS2CDR:
+ return "CS2CDR";
+ case CCM_CDCDR:
+ return "CDCDR";
+ case CCM_CHSCCDR:
+ return "CHSCCDR";
+ case CCM_CSCDR2:
+ return "CSCDR2";
+ case CCM_CSCDR3:
+ return "CSCDR3";
+ case CCM_CDHIPR:
+ return "CDHIPR";
+ case CCM_CTOR:
+ return "CTOR";
+ case CCM_CLPCR:
+ return "CLPCR";
+ case CCM_CISR:
+ return "CISR";
+ case CCM_CIMR:
+ return "CIMR";
+ case CCM_CCOSR:
+ return "CCOSR";
+ case CCM_CGPR:
+ return "CGPR";
+ case CCM_CCGR0:
+ return "CCGR0";
+ case CCM_CCGR1:
+ return "CCGR1";
+ case CCM_CCGR2:
+ return "CCGR2";
+ case CCM_CCGR3:
+ return "CCGR3";
+ case CCM_CCGR4:
+ return "CCGR4";
+ case CCM_CCGR5:
+ return "CCGR5";
+ case CCM_CCGR6:
+ return "CCGR6";
+ case CCM_CMEOR:
+ return "CMEOR";
+ default:
+ sprintf(unknown, "%d ?", reg);
+ return unknown;
+ }
+}
+
+static char const *imx6_analog_reg_name(uint32_t reg)
+{
+ static char unknown[20];
+
+ switch (reg) {
+ case CCM_ANALOG_PLL_ARM:
+ return "PLL_ARM";
+ case CCM_ANALOG_PLL_ARM_SET:
+ return "PLL_ARM_SET";
+ case CCM_ANALOG_PLL_ARM_CLR:
+ return "PLL_ARM_CLR";
+ case CCM_ANALOG_PLL_ARM_TOG:
+ return "PLL_ARM_TOG";
+ case CCM_ANALOG_PLL_USB1:
+ return "PLL_USB1";
+ case CCM_ANALOG_PLL_USB1_SET:
+ return "PLL_USB1_SET";
+ case CCM_ANALOG_PLL_USB1_CLR:
+ return "PLL_USB1_CLR";
+ case CCM_ANALOG_PLL_USB1_TOG:
+ return "PLL_USB1_TOG";
+ case CCM_ANALOG_PLL_USB2:
+ return "PLL_USB2";
+ case CCM_ANALOG_PLL_USB2_SET:
+ return "PLL_USB2_SET";
+ case CCM_ANALOG_PLL_USB2_CLR:
+ return "PLL_USB2_CLR";
+ case CCM_ANALOG_PLL_USB2_TOG:
+ return "PLL_USB2_TOG";
+ case CCM_ANALOG_PLL_SYS:
+ return "PLL_SYS";
+ case CCM_ANALOG_PLL_SYS_SET:
+ return "PLL_SYS_SET";
+ case CCM_ANALOG_PLL_SYS_CLR:
+ return "PLL_SYS_CLR";
+ case CCM_ANALOG_PLL_SYS_TOG:
+ return "PLL_SYS_TOG";
+ case CCM_ANALOG_PLL_SYS_SS:
+ return "PLL_SYS_SS";
+ case CCM_ANALOG_PLL_SYS_NUM:
+ return "PLL_SYS_NUM";
+ case CCM_ANALOG_PLL_SYS_DENOM:
+ return "PLL_SYS_DENOM";
+ case CCM_ANALOG_PLL_AUDIO:
+ return "PLL_AUDIO";
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ return "PLL_AUDIO_SET";
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ return "PLL_AUDIO_CLR";
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ return "PLL_AUDIO_TOG";
+ case CCM_ANALOG_PLL_AUDIO_NUM:
+ return "PLL_AUDIO_NUM";
+ case CCM_ANALOG_PLL_AUDIO_DENOM:
+ return "PLL_AUDIO_DENOM";
+ case CCM_ANALOG_PLL_VIDEO:
+ return "PLL_VIDEO";
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ return "PLL_VIDEO_SET";
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ return "PLL_VIDEO_CLR";
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ return "PLL_VIDEO_TOG";
+ case CCM_ANALOG_PLL_VIDEO_NUM:
+ return "PLL_VIDEO_NUM";
+ case CCM_ANALOG_PLL_VIDEO_DENOM:
+ return "PLL_VIDEO_DENOM";
+ case CCM_ANALOG_PLL_MLB:
+ return "PLL_MLB";
+ case CCM_ANALOG_PLL_MLB_SET:
+ return "PLL_MLB_SET";
+ case CCM_ANALOG_PLL_MLB_CLR:
+ return "PLL_MLB_CLR";
+ case CCM_ANALOG_PLL_MLB_TOG:
+ return "PLL_MLB_TOG";
+ case CCM_ANALOG_PLL_ENET:
+ return "PLL_ENET";
+ case CCM_ANALOG_PLL_ENET_SET:
+ return "PLL_ENET_SET";
+ case CCM_ANALOG_PLL_ENET_CLR:
+ return "PLL_ENET_CLR";
+ case CCM_ANALOG_PLL_ENET_TOG:
+ return "PLL_ENET_TOG";
+ case CCM_ANALOG_PFD_480:
+ return "PFD_480";
+ case CCM_ANALOG_PFD_480_SET:
+ return "PFD_480_SET";
+ case CCM_ANALOG_PFD_480_CLR:
+ return "PFD_480_CLR";
+ case CCM_ANALOG_PFD_480_TOG:
+ return "PFD_480_TOG";
+ case CCM_ANALOG_PFD_528:
+ return "PFD_528";
+ case CCM_ANALOG_PFD_528_SET:
+ return "PFD_528_SET";
+ case CCM_ANALOG_PFD_528_CLR:
+ return "PFD_528_CLR";
+ case CCM_ANALOG_PFD_528_TOG:
+ return "PFD_528_TOG";
+ case CCM_ANALOG_MISC0:
+ return "MISC0";
+ case CCM_ANALOG_MISC0_SET:
+ return "MISC0_SET";
+ case CCM_ANALOG_MISC0_CLR:
+ return "MISC0_CLR";
+ case CCM_ANALOG_MISC0_TOG:
+ return "MISC0_TOG";
+ case CCM_ANALOG_MISC2:
+ return "MISC2";
+ case CCM_ANALOG_MISC2_SET:
+ return "MISC2_SET";
+ case CCM_ANALOG_MISC2_CLR:
+ return "MISC2_CLR";
+ case CCM_ANALOG_MISC2_TOG:
+ return "MISC2_TOG";
+ case PMU_REG_1P1:
+ return "PMU_REG_1P1";
+ case PMU_REG_3P0:
+ return "PMU_REG_3P0";
+ case PMU_REG_2P5:
+ return "PMU_REG_2P5";
+ case PMU_REG_CORE:
+ return "PMU_REG_CORE";
+ case PMU_MISC1:
+ return "PMU_MISC1";
+ case PMU_MISC1_SET:
+ return "PMU_MISC1_SET";
+ case PMU_MISC1_CLR:
+ return "PMU_MISC1_CLR";
+ case PMU_MISC1_TOG:
+ return "PMU_MISC1_TOG";
+ case USB_ANALOG_DIGPROG:
+ return "USB_ANALOG_DIGPROG";
+ default:
+ sprintf(unknown, "%d ?", reg);
+ return unknown;
+ }
+}
+
+#define CKIH_FREQ 24000000 /* 24MHz crystal input */
+
+static const VMStateDescription vmstate_imx6_ccm = {
+ .name = TYPE_IMX6_CCM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(ccm, IMX6CCMState, CCM_MAX),
+ VMSTATE_UINT32_ARRAY(analog, IMX6CCMState, CCM_ANALOG_MAX),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 24000000;
+
+ if (EXTRACT(dev->analog[CCM_ANALOG_PLL_SYS], DIV_SELECT)) {
+ freq *= 22;
+ } else {
+ freq *= 20;
+ }
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_pll2_clk(dev) * 18
+ / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC);
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_pll2_clk(dev) * 18
+ / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC);
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ switch (EXTRACT(dev->ccm[CCM_CBCMR], PRE_PERIPH_CLK_SEL)) {
+ case 0:
+ freq = imx6_analog_get_pll2_clk(dev);
+ break;
+ case 1:
+ freq = imx6_analog_get_pll2_pfd2_clk(dev);
+ break;
+ case 2:
+ freq = imx6_analog_get_pll2_pfd0_clk(dev);
+ break;
+ case 3:
+ freq = imx6_analog_get_pll2_pfd2_clk(dev) / 2;
+ break;
+ default:
+ /* We should never get there */
+ g_assert_not_reached();
+ break;
+ }
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_analog_get_periph_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF));
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_ccm_get_ahb_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF));;
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev)
+{
+ uint64_t freq = 0;
+
+ freq = imx6_ccm_get_ipg_clk(dev)
+ / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF));
+
+ DPRINTF("freq = %d\n", (uint32_t)freq);
+
+ return freq;
+}
+
+static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+ uint32_t freq = 0;
+ IMX6CCMState *s = IMX6_CCM(dev);
+
+ switch (clock) {
+ case CLK_NONE:
+ break;
+ case CLK_IPG:
+ freq = imx6_ccm_get_ipg_clk(s);
+ break;
+ case CLK_IPG_HIGH:
+ freq = imx6_ccm_get_per_clk(s);
+ break;
+ case CLK_32k:
+ freq = CKIL_FREQ;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+ TYPE_IMX6_CCM, __func__, clock);
+ break;
+ }
+
+ DPRINTF("Clock = %d) = %d\n", clock, freq);
+
+ return freq;
+}
+
+static void imx6_ccm_reset(DeviceState *dev)
+{
+ IMX6CCMState *s = IMX6_CCM(dev);
+
+ DPRINTF("\n");
+
+ s->ccm[CCM_CCR] = 0x040116FF;
+ s->ccm[CCM_CCDR] = 0x00000000;
+ s->ccm[CCM_CSR] = 0x00000010;
+ s->ccm[CCM_CCSR] = 0x00000100;
+ s->ccm[CCM_CACRR] = 0x00000000;
+ s->ccm[CCM_CBCDR] = 0x00018D40;
+ s->ccm[CCM_CBCMR] = 0x00022324;
+ s->ccm[CCM_CSCMR1] = 0x00F00000;
+ s->ccm[CCM_CSCMR2] = 0x02B92F06;
+ s->ccm[CCM_CSCDR1] = 0x00490B00;
+ s->ccm[CCM_CS1CDR] = 0x0EC102C1;
+ s->ccm[CCM_CS2CDR] = 0x000736C1;
+ s->ccm[CCM_CDCDR] = 0x33F71F92;
+ s->ccm[CCM_CHSCCDR] = 0x0002A150;
+ s->ccm[CCM_CSCDR2] = 0x0002A150;
+ s->ccm[CCM_CSCDR3] = 0x00014841;
+ s->ccm[CCM_CDHIPR] = 0x00000000;
+ s->ccm[CCM_CTOR] = 0x00000000;
+ s->ccm[CCM_CLPCR] = 0x00000079;
+ s->ccm[CCM_CISR] = 0x00000000;
+ s->ccm[CCM_CIMR] = 0xFFFFFFFF;
+ s->ccm[CCM_CCOSR] = 0x000A0001;
+ s->ccm[CCM_CGPR] = 0x0000FE62;
+ s->ccm[CCM_CCGR0] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR1] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR2] = 0xFC3FFFFF;
+ s->ccm[CCM_CCGR3] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR4] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR5] = 0xFFFFFFFF;
+ s->ccm[CCM_CCGR6] = 0xFFFFFFFF;
+ s->ccm[CCM_CMEOR] = 0xFFFFFFFF;
+
+ s->analog[CCM_ANALOG_PLL_ARM] = 0x00013042;
+ s->analog[CCM_ANALOG_PLL_USB1] = 0x00012000;
+ s->analog[CCM_ANALOG_PLL_USB2] = 0x00012000;
+ s->analog[CCM_ANALOG_PLL_SYS] = 0x00013001;
+ s->analog[CCM_ANALOG_PLL_SYS_SS] = 0x00000000;
+ s->analog[CCM_ANALOG_PLL_SYS_NUM] = 0x00000000;
+ s->analog[CCM_ANALOG_PLL_SYS_DENOM] = 0x00000012;
+ s->analog[CCM_ANALOG_PLL_AUDIO] = 0x00011006;
+ s->analog[CCM_ANALOG_PLL_AUDIO_NUM] = 0x05F5E100;
+ s->analog[CCM_ANALOG_PLL_AUDIO_DENOM] = 0x2964619C;
+ s->analog[CCM_ANALOG_PLL_VIDEO] = 0x0001100C;
+ s->analog[CCM_ANALOG_PLL_VIDEO_NUM] = 0x05F5E100;
+ s->analog[CCM_ANALOG_PLL_VIDEO_DENOM] = 0x10A24447;
+ s->analog[CCM_ANALOG_PLL_MLB] = 0x00010000;
+ s->analog[CCM_ANALOG_PLL_ENET] = 0x00011001;
+ s->analog[CCM_ANALOG_PFD_480] = 0x1311100C;
+ s->analog[CCM_ANALOG_PFD_528] = 0x1018101B;
+
+ s->analog[PMU_REG_1P1] = 0x00001073;
+ s->analog[PMU_REG_3P0] = 0x00000F74;
+ s->analog[PMU_REG_2P5] = 0x00005071;
+ s->analog[PMU_REG_CORE] = 0x00402010;
+ s->analog[PMU_MISC0] = 0x04000000;
+ s->analog[PMU_MISC1] = 0x00000000;
+ s->analog[PMU_MISC2] = 0x00272727;
+
+ s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x00000004;
+ s->analog[USB_ANALOG_USB1_CHRG_DETECT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_VBUS_DETECT_STAT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_CHRG_DETECT_STAT] = 0x00000000;
+ s->analog[USB_ANALOG_USB1_MISC] = 0x00000002;
+ s->analog[USB_ANALOG_USB2_VBUS_DETECT] = 0x00000004;
+ s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000;
+ s->analog[USB_ANALOG_USB2_MISC] = 0x00000002;
+ s->analog[USB_ANALOG_DIGPROG] = 0x00000000;
+
+ /* all PLLs need to be locked */
+ s->analog[CCM_ANALOG_PLL_ARM] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_USB1] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_USB2] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_SYS] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_AUDIO] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_VIDEO] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_MLB] |= CCM_ANALOG_PLL_LOCK;
+ s->analog[CCM_ANALOG_PLL_ENET] |= CCM_ANALOG_PLL_LOCK;
+}
+
+static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value = 0;
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ value = s->ccm[index];
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value);
+
+ return (uint64_t)value;
+}
+
+static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index),
+ (uint32_t)value);
+
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->ccm[index] = (uint32_t)value;
+}
+
+static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint32_t value;
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ switch (index) {
+ case CCM_ANALOG_PLL_ARM_SET:
+ case CCM_ANALOG_PLL_USB1_SET:
+ case CCM_ANALOG_PLL_USB2_SET:
+ case CCM_ANALOG_PLL_SYS_SET:
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ case CCM_ANALOG_PLL_MLB_SET:
+ case CCM_ANALOG_PLL_ENET_SET:
+ case CCM_ANALOG_PFD_480_SET:
+ case CCM_ANALOG_PFD_528_SET:
+ case CCM_ANALOG_MISC0_SET:
+ case PMU_MISC1_SET:
+ case CCM_ANALOG_MISC2_SET:
+ case USB_ANALOG_USB1_VBUS_DETECT_SET:
+ case USB_ANALOG_USB1_CHRG_DETECT_SET:
+ case USB_ANALOG_USB1_MISC_SET:
+ case USB_ANALOG_USB2_VBUS_DETECT_SET:
+ case USB_ANALOG_USB2_CHRG_DETECT_SET:
+ case USB_ANALOG_USB2_MISC_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 1];
+ break;
+ case CCM_ANALOG_PLL_ARM_CLR:
+ case CCM_ANALOG_PLL_USB1_CLR:
+ case CCM_ANALOG_PLL_USB2_CLR:
+ case CCM_ANALOG_PLL_SYS_CLR:
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ case CCM_ANALOG_PLL_MLB_CLR:
+ case CCM_ANALOG_PLL_ENET_CLR:
+ case CCM_ANALOG_PFD_480_CLR:
+ case CCM_ANALOG_PFD_528_CLR:
+ case CCM_ANALOG_MISC0_CLR:
+ case PMU_MISC1_CLR:
+ case CCM_ANALOG_MISC2_CLR:
+ case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB1_MISC_CLR:
+ case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB2_MISC_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 2];
+ break;
+ case CCM_ANALOG_PLL_ARM_TOG:
+ case CCM_ANALOG_PLL_USB1_TOG:
+ case CCM_ANALOG_PLL_USB2_TOG:
+ case CCM_ANALOG_PLL_SYS_TOG:
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ case CCM_ANALOG_PLL_MLB_TOG:
+ case CCM_ANALOG_PLL_ENET_TOG:
+ case CCM_ANALOG_PFD_480_TOG:
+ case CCM_ANALOG_PFD_528_TOG:
+ case CCM_ANALOG_MISC0_TOG:
+ case PMU_MISC1_TOG:
+ case CCM_ANALOG_MISC2_TOG:
+ case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB1_MISC_TOG:
+ case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB2_MISC_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * the REG_NAME register.
+ */
+ value = s->analog[index - 3];
+ break;
+ default:
+ value = s->analog[index];
+ break;
+ }
+
+ DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value);
+
+ return (uint64_t)value;
+}
+
+static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ uint32_t index = offset >> 2;
+ IMX6CCMState *s = (IMX6CCMState *)opaque;
+
+ DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index),
+ (uint32_t)value);
+
+ switch (index) {
+ case CCM_ANALOG_PLL_ARM_SET:
+ case CCM_ANALOG_PLL_USB1_SET:
+ case CCM_ANALOG_PLL_USB2_SET:
+ case CCM_ANALOG_PLL_SYS_SET:
+ case CCM_ANALOG_PLL_AUDIO_SET:
+ case CCM_ANALOG_PLL_VIDEO_SET:
+ case CCM_ANALOG_PLL_MLB_SET:
+ case CCM_ANALOG_PLL_ENET_SET:
+ case CCM_ANALOG_PFD_480_SET:
+ case CCM_ANALOG_PFD_528_SET:
+ case CCM_ANALOG_MISC0_SET:
+ case PMU_MISC1_SET:
+ case CCM_ANALOG_MISC2_SET:
+ case USB_ANALOG_USB1_VBUS_DETECT_SET:
+ case USB_ANALOG_USB1_CHRG_DETECT_SET:
+ case USB_ANALOG_USB1_MISC_SET:
+ case USB_ANALOG_USB2_VBUS_DETECT_SET:
+ case USB_ANALOG_USB2_CHRG_DETECT_SET:
+ case USB_ANALOG_USB2_MISC_SET:
+ /*
+ * All REG_NAME_SET register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, setting bits passed in the value.
+ */
+ s->analog[index - 1] |= value;
+ break;
+ case CCM_ANALOG_PLL_ARM_CLR:
+ case CCM_ANALOG_PLL_USB1_CLR:
+ case CCM_ANALOG_PLL_USB2_CLR:
+ case CCM_ANALOG_PLL_SYS_CLR:
+ case CCM_ANALOG_PLL_AUDIO_CLR:
+ case CCM_ANALOG_PLL_VIDEO_CLR:
+ case CCM_ANALOG_PLL_MLB_CLR:
+ case CCM_ANALOG_PLL_ENET_CLR:
+ case CCM_ANALOG_PFD_480_CLR:
+ case CCM_ANALOG_PFD_528_CLR:
+ case CCM_ANALOG_MISC0_CLR:
+ case PMU_MISC1_CLR:
+ case CCM_ANALOG_MISC2_CLR:
+ case USB_ANALOG_USB1_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB1_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB1_MISC_CLR:
+ case USB_ANALOG_USB2_VBUS_DETECT_CLR:
+ case USB_ANALOG_USB2_CHRG_DETECT_CLR:
+ case USB_ANALOG_USB2_MISC_CLR:
+ /*
+ * All REG_NAME_CLR register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, unsetting bits passed in the value.
+ */
+ s->analog[index - 2] &= ~value;
+ break;
+ case CCM_ANALOG_PLL_ARM_TOG:
+ case CCM_ANALOG_PLL_USB1_TOG:
+ case CCM_ANALOG_PLL_USB2_TOG:
+ case CCM_ANALOG_PLL_SYS_TOG:
+ case CCM_ANALOG_PLL_AUDIO_TOG:
+ case CCM_ANALOG_PLL_VIDEO_TOG:
+ case CCM_ANALOG_PLL_MLB_TOG:
+ case CCM_ANALOG_PLL_ENET_TOG:
+ case CCM_ANALOG_PFD_480_TOG:
+ case CCM_ANALOG_PFD_528_TOG:
+ case CCM_ANALOG_MISC0_TOG:
+ case PMU_MISC1_TOG:
+ case CCM_ANALOG_MISC2_TOG:
+ case USB_ANALOG_USB1_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB1_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB1_MISC_TOG:
+ case USB_ANALOG_USB2_VBUS_DETECT_TOG:
+ case USB_ANALOG_USB2_CHRG_DETECT_TOG:
+ case USB_ANALOG_USB2_MISC_TOG:
+ /*
+ * All REG_NAME_TOG register access are in fact targeting the
+ * the REG_NAME register. So we change the value of the
+ * REG_NAME register, toggling bits passed in the value.
+ */
+ s->analog[index - 3] ^= value;
+ break;
+ default:
+ /*
+ * We will do a better implementation later. In particular some bits
+ * cannot be written to.
+ */
+ s->analog[index] = value;
+ break;
+ }
+}
+
+static const struct MemoryRegionOps imx6_ccm_ops = {
+ .read = imx6_ccm_read,
+ .write = imx6_ccm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static const struct MemoryRegionOps imx6_analog_ops = {
+ .read = imx6_analog_read,
+ .write = imx6_analog_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ /*
+ * Our device would not work correctly if the guest was doing
+ * unaligned access. This might not be a limitation on the real
+ * device but in practice there is no reason for a guest to access
+ * this device unaligned.
+ */
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void imx6_ccm_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+ IMX6CCMState *s = IMX6_CCM(obj);
+
+ /* initialize a container for the all memory range */
+ memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6_CCM, 0x5000);
+
+ /* We initialize an IO memory region for the CCM part */
+ memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6_ccm_ops, s,
+ TYPE_IMX6_CCM ".ccm", CCM_MAX * sizeof(uint32_t));
+
+ /* Add the CCM as a subregion at offset 0 */
+ memory_region_add_subregion(&s->container, 0, &s->ioccm);
+
+ /* We initialize an IO memory region for the ANALOG part */
+ memory_region_init_io(&s->ioanalog, OBJECT(dev), &imx6_analog_ops, s,
+ TYPE_IMX6_CCM ".analog",
+ CCM_ANALOG_MAX * sizeof(uint32_t));
+
+ /* Add the ANALOG as a subregion at offset 0x4000 */
+ memory_region_add_subregion(&s->container, 0x4000, &s->ioanalog);
+
+ sysbus_init_mmio(sd, &s->container);
+}
+
+static void imx6_ccm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+ dc->reset = imx6_ccm_reset;
+ dc->vmsd = &vmstate_imx6_ccm;
+ dc->desc = "i.MX6 Clock Control Module";
+
+ ccm->get_clock_frequency = imx6_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx6_ccm_info = {
+ .name = TYPE_IMX6_CCM,
+ .parent = TYPE_IMX_CCM,
+ .instance_size = sizeof(IMX6CCMState),
+ .instance_init = imx6_ccm_init,
+ .class_init = imx6_ccm_class_init,
+};
+
+static void imx6_ccm_register_types(void)
+{
+ type_register_static(&imx6_ccm_info);
+}
+
+type_init(imx6_ccm_register_types)
diff --git a/include/hw/misc/imx6_ccm.h b/include/hw/misc/imx6_ccm.h
new file mode 100644
index 0000000..8050580
--- /dev/null
+++ b/include/hw/misc/imx6_ccm.h
@@ -0,0 +1,197 @@
+/*
+ * IMX6 Clock Control Module
+ *
+ * Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef IMX6_CCM_H
+#define IMX6_CCM_H
+
+#include "hw/misc/imx_ccm.h"
+#include "qemu/bitops.h"
+
+#define CCM_CCR 0
+#define CCM_CCDR 1
+#define CCM_CSR 2
+#define CCM_CCSR 3
+#define CCM_CACRR 4
+#define CCM_CBCDR 5
+#define CCM_CBCMR 6
+#define CCM_CSCMR1 7
+#define CCM_CSCMR2 8
+#define CCM_CSCDR1 9
+#define CCM_CS1CDR 10
+#define CCM_CS2CDR 11
+#define CCM_CDCDR 12
+#define CCM_CHSCCDR 13
+#define CCM_CSCDR2 14
+#define CCM_CSCDR3 15
+#define CCM_CDHIPR 18
+#define CCM_CTOR 20
+#define CCM_CLPCR 21
+#define CCM_CISR 22
+#define CCM_CIMR 23
+#define CCM_CCOSR 24
+#define CCM_CGPR 25
+#define CCM_CCGR0 26
+#define CCM_CCGR1 27
+#define CCM_CCGR2 28
+#define CCM_CCGR3 29
+#define CCM_CCGR4 30
+#define CCM_CCGR5 31
+#define CCM_CCGR6 32
+#define CCM_CMEOR 34
+#define CCM_MAX 35
+
+#define CCM_ANALOG_PLL_ARM 0
+#define CCM_ANALOG_PLL_ARM_SET 1
+#define CCM_ANALOG_PLL_ARM_CLR 2
+#define CCM_ANALOG_PLL_ARM_TOG 3
+#define CCM_ANALOG_PLL_USB1 4
+#define CCM_ANALOG_PLL_USB1_SET 5
+#define CCM_ANALOG_PLL_USB1_CLR 6
+#define CCM_ANALOG_PLL_USB1_TOG 7
+#define CCM_ANALOG_PLL_USB2 8
+#define CCM_ANALOG_PLL_USB2_SET 9
+#define CCM_ANALOG_PLL_USB2_CLR 10
+#define CCM_ANALOG_PLL_USB2_TOG 11
+#define CCM_ANALOG_PLL_SYS 12
+#define CCM_ANALOG_PLL_SYS_SET 13
+#define CCM_ANALOG_PLL_SYS_CLR 14
+#define CCM_ANALOG_PLL_SYS_TOG 15
+#define CCM_ANALOG_PLL_SYS_SS 16
+#define CCM_ANALOG_PLL_SYS_NUM 20
+#define CCM_ANALOG_PLL_SYS_DENOM 24
+#define CCM_ANALOG_PLL_AUDIO 28
+#define CCM_ANALOG_PLL_AUDIO_SET 29
+#define CCM_ANALOG_PLL_AUDIO_CLR 30
+#define CCM_ANALOG_PLL_AUDIO_TOG 31
+#define CCM_ANALOG_PLL_AUDIO_NUM 32
+#define CCM_ANALOG_PLL_AUDIO_DENOM 36
+#define CCM_ANALOG_PLL_VIDEO 40
+#define CCM_ANALOG_PLL_VIDEO_SET 41
+#define CCM_ANALOG_PLL_VIDEO_CLR 42
+#define CCM_ANALOG_PLL_VIDEO_TOG 44
+#define CCM_ANALOG_PLL_VIDEO_NUM 46
+#define CCM_ANALOG_PLL_VIDEO_DENOM 48
+#define CCM_ANALOG_PLL_MLB 52
+#define CCM_ANALOG_PLL_MLB_SET 53
+#define CCM_ANALOG_PLL_MLB_CLR 54
+#define CCM_ANALOG_PLL_MLB_TOG 55
+#define CCM_ANALOG_PLL_ENET 56
+#define CCM_ANALOG_PLL_ENET_SET 57
+#define CCM_ANALOG_PLL_ENET_CLR 58
+#define CCM_ANALOG_PLL_ENET_TOG 59
+#define CCM_ANALOG_PFD_480 60
+#define CCM_ANALOG_PFD_480_SET 61
+#define CCM_ANALOG_PFD_480_CLR 62
+#define CCM_ANALOG_PFD_480_TOG 63
+#define CCM_ANALOG_PFD_528 64
+#define CCM_ANALOG_PFD_528_SET 65
+#define CCM_ANALOG_PFD_528_CLR 66
+#define CCM_ANALOG_PFD_528_TOG 67
+
+/* PMU registers */
+#define PMU_REG_1P1 68
+#define PMU_REG_3P0 72
+#define PMU_REG_2P5 76
+#define PMU_REG_CORE 80
+
+#define CCM_ANALOG_MISC0 84
+#define PMU_MISC0 84
+#define CCM_ANALOG_MISC0_SET 85
+#define CCM_ANALOG_MISC0_CLR 86
+#define CCM_ANALOG_MISC0_TOG 87
+
+#define PMU_MISC1 88
+#define PMU_MISC1_SET 89
+#define PMU_MISC1_CLR 90
+#define PMU_MISC1_TOG 91
+
+#define CCM_ANALOG_MISC2 92
+#define PMU_MISC2 92
+#define CCM_ANALOG_MISC2_SET 93
+#define CCM_ANALOG_MISC2_CLR 94
+#define CCM_ANALOG_MISC2_TOG 95
+
+#define USB_ANALOG_USB1_VBUS_DETECT 104
+#define USB_ANALOG_USB1_VBUS_DETECT_SET 105
+#define USB_ANALOG_USB1_VBUS_DETECT_CLR 106
+#define USB_ANALOG_USB1_VBUS_DETECT_TOG 107
+#define USB_ANALOG_USB1_CHRG_DETECT 108
+#define USB_ANALOG_USB1_CHRG_DETECT_SET 109
+#define USB_ANALOG_USB1_CHRG_DETECT_CLR 110
+#define USB_ANALOG_USB1_CHRG_DETECT_TOG 111
+#define USB_ANALOG_USB1_VBUS_DETECT_STAT 112
+#define USB_ANALOG_USB1_CHRG_DETECT_STAT 116
+#define USB_ANALOG_USB1_MISC 124
+#define USB_ANALOG_USB1_MISC_SET 125
+#define USB_ANALOG_USB1_MISC_CLR 126
+#define USB_ANALOG_USB1_MISC_TOG 127
+#define USB_ANALOG_USB2_VBUS_DETECT 128
+#define USB_ANALOG_USB2_VBUS_DETECT_SET 129
+#define USB_ANALOG_USB2_VBUS_DETECT_CLR 130
+#define USB_ANALOG_USB2_VBUS_DETECT_TOG 131
+#define USB_ANALOG_USB2_CHRG_DETECT 132
+#define USB_ANALOG_USB2_CHRG_DETECT_SET 133
+#define USB_ANALOG_USB2_CHRG_DETECT_CLR 134
+#define USB_ANALOG_USB2_CHRG_DETECT_TOG 135
+#define USB_ANALOG_USB2_VBUS_DETECT_STAT 136
+#define USB_ANALOG_USB2_CHRG_DETECT_STAT 140
+#define USB_ANALOG_USB2_MISC 148
+#define USB_ANALOG_USB2_MISC_SET 149
+#define USB_ANALOG_USB2_MISC_CLR 150
+#define USB_ANALOG_USB2_MISC_TOG 151
+#define USB_ANALOG_DIGPROG 152
+#define CCM_ANALOG_MAX 153
+
+/* CCM_CBCMR */
+#define PRE_PERIPH_CLK_SEL_SHIFT (18)
+#define PRE_PERIPH_CLK_SEL_LENGTH (2)
+
+/* CCM_CBCDR */
+#define AHB_PODF_SHIFT (10)
+#define AHB_PODF_LENGTH (3)
+#define IPG_PODF_SHIFT (8)
+#define IPG_PODF_LENGTH (2)
+
+/* CCM_CSCMR1 */
+#define PERCLK_PODF_SHIFT (0)
+#define PERCLK_PODF_LENGTH (6)
+
+/* CCM_ANALOG_PFD_528 */
+#define PFD0_FRAC_SHIFT (0)
+#define PFD0_FRAC_LENGTH (6)
+#define PFD2_FRAC_SHIFT (16)
+#define PFD2_FRAC_LENGTH (6)
+
+/* CCM_ANALOG_PLL_SYS */
+#define DIV_SELECT_SHIFT (0)
+#define DIV_SELECT_LENGTH (1)
+
+#define CCM_ANALOG_PLL_LOCK (1 << 31);
+
+#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH)
+
+#define TYPE_IMX6_CCM "imx6.ccm"
+#define IMX6_CCM(obj) OBJECT_CHECK(IMX6CCMState, (obj), TYPE_IMX6_CCM)
+
+typedef struct IMX6CCMState {
+ /* <private> */
+ IMXCCMState parent_obj;
+
+ /* <public> */
+ MemoryRegion container;
+ MemoryRegion ioccm;
+ MemoryRegion ioanalog;
+
+ uint32_t ccm[CCM_MAX];
+ uint32_t analog[CCM_ANALOG_MAX];
+
+} IMX6CCMState;
+
+#endif /* IMX6_CCM_H */
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 11/21] i.MX: Add missing descriptions in devices.
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (9 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 10/21] i.MX: Add i.MX6 CCM and ANALOG device Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 12/21] hw/timer: Add ASPEED timer device model Peter Maydell
` (11 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Jean-Christophe Dubois <jcd@tribudubois.net>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Message-id: f1f565eb9dffdeb582feb1b15ba9e8b0afcf5468.1456868959.git.jcd@tribudubois.net
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/fsl-imx25.c | 1 +
hw/arm/fsl-imx31.c | 1 +
hw/i2c/imx_i2c.c | 1 +
hw/net/imx_fec.c | 1 +
4 files changed, 4 insertions(+)
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index fb743bf..1fbc317 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -291,6 +291,7 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
+ dc->desc = "i.MX25 SOC";
}
static const TypeInfo fsl_imx25_type_info = {
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index f2c2ce5..0d69a2c 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -265,6 +265,7 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
* arm_cpu_class_init()
*/
dc->cannot_destroy_with_object_finalize_yet = true;
+ dc->desc = "i.MX31 SOC";
}
static const TypeInfo fsl_imx31_type_info = {
diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c
index e435448..a01e43e 100644
--- a/hw/i2c/imx_i2c.c
+++ b/hw/i2c/imx_i2c.c
@@ -319,6 +319,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data)
dc->vmsd = &imx_i2c_vmstate;
dc->reset = imx_i2c_reset;
dc->realize = imx_i2c_realize;
+ dc->desc = "i.MX I2C Controller";
}
static const TypeInfo imx_i2c_type_info = {
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index 3bd5517..e60e338 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -693,6 +693,7 @@ static void imx_fec_class_init(ObjectClass *klass, void *data)
dc->reset = imx_fec_reset;
dc->props = imx_fec_properties;
dc->realize = imx_fec_realize;
+ dc->desc = "i.MX FEC Ethernet Controller";
}
static const TypeInfo imx_fec_info = {
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 12/21] hw/timer: Add ASPEED timer device model
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (10 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 11/21] i.MX: Add missing descriptions in devices Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 13/21] hw/intc: Add (new) ASPEED VIC " Peter Maydell
` (10 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Andrew Jeffery <andrew@aj.id.au>
Implement basic ASPEED timer functionality for the AST2400 SoC[1]: Up to
8 timers can independently be configured, enabled, reset and disabled.
Some hardware features are not implemented, namely clock value matching
and pulse generation, but the implementation is enough to boot the Linux
kernel configured with aspeed_defconfig.
[1] http://www.aspeedtech.com/products.php?fPath=20&rId=376
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Message-id: 1458096317-25223-2-git-send-email-andrew@aj.id.au
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
default-configs/arm-softmmu.mak | 1 +
hw/timer/Makefile.objs | 1 +
hw/timer/aspeed_timer.c | 449 ++++++++++++++++++++++++++++++++++++++++
include/hw/timer/aspeed_timer.h | 59 ++++++
trace-events | 9 +
5 files changed, 519 insertions(+)
create mode 100644 hw/timer/aspeed_timer.c
create mode 100644 include/hw/timer/aspeed_timer.h
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index a9f82a1..2bcd236 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -110,3 +110,4 @@ CONFIG_IOH3420=y
CONFIG_I82801B11=y
CONFIG_ACPI=y
CONFIG_SMBIOS=y
+CONFIG_ASPEED_SOC=y
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 5cfea6e..003c14f 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -32,3 +32,4 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
+common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
new file mode 100644
index 0000000..51e8303
--- /dev/null
+++ b/hw/timer/aspeed_timer.c
@@ -0,0 +1,449 @@
+/*
+ * ASPEED AST2400 Timer
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ptimer.h"
+#include "hw/sysbus.h"
+#include "hw/timer/aspeed_timer.h"
+#include "qemu-common.h"
+#include "qemu/bitops.h"
+#include "qemu/main-loop.h"
+#include "qemu/timer.h"
+#include "trace.h"
+
+#define TIMER_NR_REGS 4
+
+#define TIMER_CTRL_BITS 4
+#define TIMER_CTRL_MASK ((1 << TIMER_CTRL_BITS) - 1)
+
+#define TIMER_CLOCK_USE_EXT true
+#define TIMER_CLOCK_EXT_HZ 1000000
+#define TIMER_CLOCK_USE_APB false
+#define TIMER_CLOCK_APB_HZ 24000000
+
+#define TIMER_REG_STATUS 0
+#define TIMER_REG_RELOAD 1
+#define TIMER_REG_MATCH_FIRST 2
+#define TIMER_REG_MATCH_SECOND 3
+
+#define TIMER_FIRST_CAP_PULSE 4
+
+enum timer_ctrl_op {
+ op_enable = 0,
+ op_external_clock,
+ op_overflow_interrupt,
+ op_pulse_enable
+};
+
+/**
+ * Avoid mutual references between AspeedTimerCtrlState and AspeedTimer
+ * structs, as it's a waste of memory. The ptimer BH callback needs to know
+ * whether a specific AspeedTimer is enabled, but this information is held in
+ * AspeedTimerCtrlState. So, provide a helper to hoist ourselves from an
+ * arbitrary AspeedTimer to AspeedTimerCtrlState.
+ */
+static inline AspeedTimerCtrlState *timer_to_ctrl(AspeedTimer *t)
+{
+ const AspeedTimer (*timers)[] = (void *)t - (t->id * sizeof(*t));
+ return container_of(timers, AspeedTimerCtrlState, timers);
+}
+
+static inline bool timer_ctrl_status(AspeedTimer *t, enum timer_ctrl_op op)
+{
+ return !!(timer_to_ctrl(t)->ctrl & BIT(t->id * TIMER_CTRL_BITS + op));
+}
+
+static inline bool timer_enabled(AspeedTimer *t)
+{
+ return timer_ctrl_status(t, op_enable);
+}
+
+static inline bool timer_overflow_interrupt(AspeedTimer *t)
+{
+ return timer_ctrl_status(t, op_overflow_interrupt);
+}
+
+static inline bool timer_can_pulse(AspeedTimer *t)
+{
+ return t->id >= TIMER_FIRST_CAP_PULSE;
+}
+
+static void aspeed_timer_expire(void *opaque)
+{
+ AspeedTimer *t = opaque;
+
+ /* Only support interrupts on match values of zero for the moment - this is
+ * sufficient to boot an aspeed_defconfig Linux kernel.
+ *
+ * TODO: matching on arbitrary values (see e.g. hw/timer/a9gtimer.c)
+ */
+ bool match = !(t->match[0] && t->match[1]);
+ bool interrupt = timer_overflow_interrupt(t) || match;
+ if (timer_enabled(t) && interrupt) {
+ t->level = !t->level;
+ qemu_set_irq(t->irq, t->level);
+ }
+}
+
+static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
+{
+ uint64_t value;
+
+ switch (reg) {
+ case TIMER_REG_STATUS:
+ value = ptimer_get_count(t->timer);
+ break;
+ case TIMER_REG_RELOAD:
+ value = t->reload;
+ break;
+ case TIMER_REG_MATCH_FIRST:
+ case TIMER_REG_MATCH_SECOND:
+ value = t->match[reg - 2];
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n",
+ __func__, reg);
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AspeedTimerCtrlState *s = opaque;
+ const int reg = (offset & 0xf) / 4;
+ uint64_t value;
+
+ switch (offset) {
+ case 0x30: /* Control Register */
+ value = s->ctrl;
+ break;
+ case 0x34: /* Control Register 2 */
+ value = s->ctrl2;
+ break;
+ case 0x00 ... 0x2c: /* Timers 1 - 4 */
+ value = aspeed_timer_get_value(&s->timers[(offset >> 4)], reg);
+ break;
+ case 0x40 ... 0x8c: /* Timers 5 - 8 */
+ value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg);
+ break;
+ /* Illegal */
+ case 0x38:
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ value = 0;
+ break;
+ }
+ trace_aspeed_timer_read(offset, size, value);
+ return value;
+}
+
+static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
+ uint32_t value)
+{
+ AspeedTimer *t;
+
+ trace_aspeed_timer_set_value(timer, reg, value);
+ t = &s->timers[timer];
+ switch (reg) {
+ case TIMER_REG_STATUS:
+ if (timer_enabled(t)) {
+ ptimer_set_count(t->timer, value);
+ }
+ break;
+ case TIMER_REG_RELOAD:
+ t->reload = value;
+ ptimer_set_limit(t->timer, value, 1);
+ break;
+ case TIMER_REG_MATCH_FIRST:
+ case TIMER_REG_MATCH_SECOND:
+ if (value) {
+ /* Non-zero match values are unsupported. As such an interrupt will
+ * always be triggered when the timer reaches zero even if the
+ * overflow interrupt control bit is clear.
+ */
+ qemu_log_mask(LOG_UNIMP, "%s: Match value unsupported by device: "
+ "0x%" PRIx32 "\n", __func__, value);
+ } else {
+ t->match[reg - 2] = value;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Programming error: unexpected reg: %d\n",
+ __func__, reg);
+ break;
+ }
+}
+
+/* Control register operations are broken out into helpers that can be
+ * explictly called on aspeed_timer_reset(), but also from
+ * aspeed_timer_ctrl_op().
+ */
+
+static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable)
+{
+ trace_aspeed_timer_ctrl_enable(t->id, enable);
+ if (enable) {
+ ptimer_run(t->timer, 0);
+ } else {
+ ptimer_stop(t->timer);
+ ptimer_set_limit(t->timer, t->reload, 1);
+ }
+}
+
+static void aspeed_timer_ctrl_external_clock(AspeedTimer *t, bool enable)
+{
+ trace_aspeed_timer_ctrl_external_clock(t->id, enable);
+ if (enable) {
+ ptimer_set_freq(t->timer, TIMER_CLOCK_EXT_HZ);
+ } else {
+ ptimer_set_freq(t->timer, TIMER_CLOCK_APB_HZ);
+ }
+}
+
+static void aspeed_timer_ctrl_overflow_interrupt(AspeedTimer *t, bool enable)
+{
+ trace_aspeed_timer_ctrl_overflow_interrupt(t->id, enable);
+}
+
+static void aspeed_timer_ctrl_pulse_enable(AspeedTimer *t, bool enable)
+{
+ if (timer_can_pulse(t)) {
+ trace_aspeed_timer_ctrl_pulse_enable(t->id, enable);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Timer does not support pulse mode\n", __func__);
+ }
+}
+
+/**
+ * Given the actions are fixed in number and completely described in helper
+ * functions, dispatch with a lookup table rather than manage control flow with
+ * a switch statement.
+ */
+static void (*const ctrl_ops[])(AspeedTimer *, bool) = {
+ [op_enable] = aspeed_timer_ctrl_enable,
+ [op_external_clock] = aspeed_timer_ctrl_external_clock,
+ [op_overflow_interrupt] = aspeed_timer_ctrl_overflow_interrupt,
+ [op_pulse_enable] = aspeed_timer_ctrl_pulse_enable,
+};
+
+/**
+ * Conditionally affect changes chosen by a timer's control bit.
+ *
+ * The aspeed_timer_ctrl_op() interface is convenient for the
+ * aspeed_timer_set_ctrl() function as the "no change" early exit can be
+ * calculated for all operations, which cleans up the caller code. However the
+ * interface isn't convenient for the reset function where we want to enter a
+ * specific state without artificially constructing old and new values that
+ * will fall through the change guard (and motivates extracting the actions
+ * out to helper functions).
+ *
+ * @t: The timer to manipulate
+ * @op: The type of operation to be performed
+ * @old: The old state of the timer's control bits
+ * @new: The incoming state for the timer's control bits
+ */
+static void aspeed_timer_ctrl_op(AspeedTimer *t, enum timer_ctrl_op op,
+ uint8_t old, uint8_t new)
+{
+ const uint8_t mask = BIT(op);
+ const bool enable = !!(new & mask);
+ const bool changed = ((old ^ new) & mask);
+ if (!changed) {
+ return;
+ }
+ ctrl_ops[op](t, enable);
+}
+
+static void aspeed_timer_set_ctrl(AspeedTimerCtrlState *s, uint32_t reg)
+{
+ int i;
+ int shift;
+ uint8_t t_old, t_new;
+ AspeedTimer *t;
+ const uint8_t enable_mask = BIT(op_enable);
+
+ /* Handle a dependency between the 'enable' and remaining three
+ * configuration bits - i.e. if more than one bit in the control set has
+ * changed, including the 'enable' bit, then we want either disable the
+ * timer and perform configuration, or perform configuration and then
+ * enable the timer
+ */
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ t = &s->timers[i];
+ shift = (i * TIMER_CTRL_BITS);
+ t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK;
+ t_new = (reg >> shift) & TIMER_CTRL_MASK;
+
+ /* If we are disabling, do so first */
+ if ((t_old & enable_mask) && !(t_new & enable_mask)) {
+ aspeed_timer_ctrl_enable(t, false);
+ }
+ aspeed_timer_ctrl_op(t, op_external_clock, t_old, t_new);
+ aspeed_timer_ctrl_op(t, op_overflow_interrupt, t_old, t_new);
+ aspeed_timer_ctrl_op(t, op_pulse_enable, t_old, t_new);
+ /* If we are enabling, do so last */
+ if (!(t_old & enable_mask) && (t_new & enable_mask)) {
+ aspeed_timer_ctrl_enable(t, true);
+ }
+ }
+ s->ctrl = reg;
+}
+
+static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value)
+{
+ trace_aspeed_timer_set_ctrl2(value);
+}
+
+static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF);
+ const int reg = (offset & 0xf) / 4;
+ AspeedTimerCtrlState *s = opaque;
+
+ switch (offset) {
+ /* Control Registers */
+ case 0x30:
+ aspeed_timer_set_ctrl(s, tv);
+ break;
+ case 0x34:
+ aspeed_timer_set_ctrl2(s, tv);
+ break;
+ /* Timer Registers */
+ case 0x00 ... 0x2c:
+ aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS), reg, tv);
+ break;
+ case 0x40 ... 0x8c:
+ aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv);
+ break;
+ /* Illegal */
+ case 0x38:
+ case 0x3C:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps aspeed_timer_ops = {
+ .read = aspeed_timer_read,
+ .write = aspeed_timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id)
+{
+ QEMUBH *bh;
+ AspeedTimer *t = &s->timers[id];
+
+ t->id = id;
+ bh = qemu_bh_new(aspeed_timer_expire, t);
+ t->timer = ptimer_init(bh);
+}
+
+static void aspeed_timer_realize(DeviceState *dev, Error **errp)
+{
+ int i;
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedTimerCtrlState *s = ASPEED_TIMER(dev);
+
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ aspeed_init_one_timer(s, i);
+ sysbus_init_irq(sbd, &s->timers[i].irq);
+ }
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_timer_ops, s,
+ TYPE_ASPEED_TIMER, 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void aspeed_timer_reset(DeviceState *dev)
+{
+ int i;
+ AspeedTimerCtrlState *s = ASPEED_TIMER(dev);
+
+ for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
+ AspeedTimer *t = &s->timers[i];
+ /* Explictly call helpers to avoid any conditional behaviour through
+ * aspeed_timer_set_ctrl().
+ */
+ aspeed_timer_ctrl_enable(t, false);
+ aspeed_timer_ctrl_external_clock(t, TIMER_CLOCK_USE_APB);
+ aspeed_timer_ctrl_overflow_interrupt(t, false);
+ aspeed_timer_ctrl_pulse_enable(t, false);
+ t->level = 0;
+ t->reload = 0;
+ t->match[0] = 0;
+ t->match[1] = 0;
+ }
+ s->ctrl = 0;
+ s->ctrl2 = 0;
+}
+
+static const VMStateDescription vmstate_aspeed_timer = {
+ .name = "aspeed.timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(id, AspeedTimer),
+ VMSTATE_INT32(level, AspeedTimer),
+ VMSTATE_PTIMER(timer, AspeedTimer),
+ VMSTATE_UINT32(reload, AspeedTimer),
+ VMSTATE_UINT32_ARRAY(match, AspeedTimer, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_aspeed_timer_state = {
+ .name = "aspeed.timerctrl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ctrl, AspeedTimerCtrlState),
+ VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState),
+ VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState,
+ ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer,
+ AspeedTimer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = aspeed_timer_realize;
+ dc->reset = aspeed_timer_reset;
+ dc->desc = "ASPEED Timer";
+ dc->vmsd = &vmstate_aspeed_timer_state;
+}
+
+static const TypeInfo aspeed_timer_info = {
+ .name = TYPE_ASPEED_TIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedTimerCtrlState),
+ .class_init = timer_class_init,
+};
+
+static void aspeed_timer_register_types(void)
+{
+ type_register_static(&aspeed_timer_info);
+}
+
+type_init(aspeed_timer_register_types)
diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h
new file mode 100644
index 0000000..44dc2f8
--- /dev/null
+++ b/include/hw/timer/aspeed_timer.h
@@ -0,0 +1,59 @@
+/*
+ * ASPEED AST2400 Timer
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef ASPEED_TIMER_H
+#define ASPEED_TIMER_H
+
+#include "hw/ptimer.h"
+
+#define ASPEED_TIMER(obj) \
+ OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER);
+#define TYPE_ASPEED_TIMER "aspeed.timer"
+#define ASPEED_TIMER_NR_TIMERS 8
+
+typedef struct AspeedTimer {
+ qemu_irq irq;
+
+ uint8_t id;
+
+ /**
+ * Track the line level as the ASPEED timers implement edge triggered
+ * interrupts, signalling with both the rising and falling edge.
+ */
+ int32_t level;
+ ptimer_state *timer;
+ uint32_t reload;
+ uint32_t match[2];
+} AspeedTimer;
+
+typedef struct AspeedTimerCtrlState {
+ /*< private >*/
+ SysBusDevice parent;
+
+ /*< public >*/
+ MemoryRegion iomem;
+
+ uint32_t ctrl;
+ uint32_t ctrl2;
+ AspeedTimer timers[ASPEED_TIMER_NR_TIMERS];
+} AspeedTimerCtrlState;
+
+#endif /* ASPEED_TIMER_H */
diff --git a/trace-events b/trace-events
index 0ad8a1c..e589230 100644
--- a/trace-events
+++ b/trace-events
@@ -1892,3 +1892,12 @@ qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Comman
qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d"
qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d"
qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d"
+
+# hw/timer/aspeed_timer.c
+aspeed_timer_ctrl_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
+aspeed_timer_ctrl_external_clock(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
+aspeed_timer_ctrl_overflow_interrupt(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
+aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
+aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32
+aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32
+aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 13/21] hw/intc: Add (new) ASPEED VIC device model
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (11 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 12/21] hw/timer: Add ASPEED timer device model Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 14/21] hw/arm: Add ASPEED AST2400 SoC model Peter Maydell
` (9 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Andrew Jeffery <andrew@aj.id.au>
Implement a basic ASPEED VIC device model for the AST2400 SoC[1], with
enough functionality to boot an aspeed_defconfig Linux kernel. The model
implements the 'new' (revised) register set: While the hardware exposes
both the new and legacy register sets, accesses to the model's legacy
register set will not be serviced (however the access will be logged).
[1] http://www.aspeedtech.com/products.php?fPath=20&rId=376
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Message-id: 1458096317-25223-3-git-send-email-andrew@aj.id.au
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/intc/Makefile.objs | 1 +
hw/intc/aspeed_vic.c | 339 +++++++++++++++++++++++++++++++++++++++++++
include/hw/intc/aspeed_vic.h | 48 ++++++
trace-events | 7 +
4 files changed, 395 insertions(+)
create mode 100644 hw/intc/aspeed_vic.c
create mode 100644 include/hw/intc/aspeed_vic.h
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 6a13a39..0e47f0f 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -31,3 +31,4 @@ obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o
diff --git a/hw/intc/aspeed_vic.c b/hw/intc/aspeed_vic.c
new file mode 100644
index 0000000..19a0ff7
--- /dev/null
+++ b/hw/intc/aspeed_vic.c
@@ -0,0 +1,339 @@
+/*
+ * ASPEED Interrupt Controller (New)
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2015, 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+/* The hardware exposes two register sets, a legacy set and a 'new' set. The
+ * model implements the 'new' register set, and logs warnings on accesses to
+ * the legacy IO space.
+ *
+ * The hardware uses 32bit registers to manage 51 IRQs, with low and high
+ * registers for each conceptual register. The device model's implementation
+ * uses 64bit data types to store both low and high register values (in the one
+ * member), but must cope with access offset values in multiples of 4 passed to
+ * the callbacks. As such the read() and write() implementations process the
+ * provided offset to understand whether the access is requesting the lower or
+ * upper 32 bits of the 64bit member.
+ *
+ * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
+ * fields have separate "enable"/"status" and "clear" registers, where set bits
+ * are written to one or the other to change state (avoiding a
+ * read-modify-write sequence).
+ */
+
+#include "qemu/osdep.h"
+#include <inttypes.h>
+#include "hw/intc/aspeed_vic.h"
+#include "qemu/bitops.h"
+#include "trace.h"
+
+#define AVIC_NEW_BASE_OFFSET 0x80
+
+#define AVIC_L_MASK 0xFFFFFFFFU
+#define AVIC_H_MASK 0x0007FFFFU
+#define AVIC_EVENT_W_MASK (0x78000ULL << 32)
+
+static void aspeed_vic_update(AspeedVICState *s)
+{
+ uint64_t new = (s->raw & s->enable);
+ uint64_t flags;
+
+ flags = new & s->select;
+ trace_aspeed_vic_update_fiq(!!flags);
+ qemu_set_irq(s->fiq, !!flags);
+
+ flags = new & ~s->select;
+ trace_aspeed_vic_update_irq(!!flags);
+ qemu_set_irq(s->irq, !!flags);
+}
+
+static void aspeed_vic_set_irq(void *opaque, int irq, int level)
+{
+ uint64_t irq_mask;
+ bool raise;
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (irq > ASPEED_VIC_NR_IRQS) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
+ __func__, irq);
+ return;
+ }
+
+ trace_aspeed_vic_set_irq(irq, level);
+
+ irq_mask = BIT(irq);
+ if (s->sense & irq_mask) {
+ /* level-triggered */
+ if (s->event & irq_mask) {
+ /* high-sensitive */
+ raise = level;
+ } else {
+ /* low-sensitive */
+ raise = !level;
+ }
+ s->raw = deposit64(s->raw, irq, 1, raise);
+ } else {
+ uint64_t old_level = s->level & irq_mask;
+
+ /* edge-triggered */
+ if (s->dual_edge & irq_mask) {
+ raise = (!!old_level) != (!!level);
+ } else {
+ if (s->event & irq_mask) {
+ /* rising-sensitive */
+ raise = !old_level && level;
+ } else {
+ /* falling-sensitive */
+ raise = old_level && !level;
+ }
+ }
+ if (raise) {
+ s->raw = deposit64(s->raw, irq, 1, raise);
+ }
+ }
+ s->level = deposit64(s->level, irq, 1, level);
+ aspeed_vic_update(s);
+}
+
+static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint64_t val;
+ const bool high = !!(offset & 0x4);
+ hwaddr n_offset = (offset & ~0x4);
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (offset < AVIC_NEW_BASE_OFFSET) {
+ qemu_log_mask(LOG_UNIMP, "%s: Ignoring read from legacy registers "
+ "at 0x%" HWADDR_PRIx "[%u]\n", __func__, offset, size);
+ return 0;
+ }
+
+ n_offset -= AVIC_NEW_BASE_OFFSET;
+
+ switch (n_offset) {
+ case 0x0: /* IRQ Status */
+ val = s->raw & ~s->select & s->enable;
+ break;
+ case 0x08: /* FIQ Status */
+ val = s->raw & s->select & s->enable;
+ break;
+ case 0x10: /* Raw Interrupt Status */
+ val = s->raw;
+ break;
+ case 0x18: /* Interrupt Selection */
+ val = s->select;
+ break;
+ case 0x20: /* Interrupt Enable */
+ val = s->enable;
+ break;
+ case 0x30: /* Software Interrupt */
+ val = s->trigger;
+ break;
+ case 0x40: /* Interrupt Sensitivity */
+ val = s->sense;
+ break;
+ case 0x48: /* Interrupt Both Edge Trigger Control */
+ val = s->dual_edge;
+ break;
+ case 0x50: /* Interrupt Event */
+ val = s->event;
+ break;
+ case 0x60: /* Edge Triggered Interrupt Status */
+ val = s->raw & ~s->sense;
+ break;
+ /* Illegal */
+ case 0x28: /* Interrupt Enable Clear */
+ case 0x38: /* Software Interrupt Clear */
+ case 0x58: /* Edge Triggered Interrupt Clear */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Read of write-only register with offset 0x%"
+ HWADDR_PRIx "\n", __func__, offset);
+ val = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ val = 0;
+ break;
+ }
+ if (high) {
+ val = extract64(val, 32, 19);
+ }
+ trace_aspeed_vic_read(offset, size, val);
+ return val;
+}
+
+static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ const bool high = !!(offset & 0x4);
+ hwaddr n_offset = (offset & ~0x4);
+ AspeedVICState *s = (AspeedVICState *)opaque;
+
+ if (offset < AVIC_NEW_BASE_OFFSET) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Ignoring write to legacy registers at 0x%"
+ HWADDR_PRIx "[%u] <- 0x%" PRIx64 "\n", __func__, offset,
+ size, data);
+ return;
+ }
+
+ n_offset -= AVIC_NEW_BASE_OFFSET;
+ trace_aspeed_vic_write(offset, size, data);
+
+ /* Given we have members using separate enable/clear registers, deposit64()
+ * isn't quite the tool for the job. Instead, relocate the incoming bits to
+ * the required bit offset based on the provided access address
+ */
+ if (high) {
+ data &= AVIC_H_MASK;
+ data <<= 32;
+ } else {
+ data &= AVIC_L_MASK;
+ }
+
+ switch (n_offset) {
+ case 0x18: /* Interrupt Selection */
+ /* Register has deposit64() semantics - overwrite requested 32 bits */
+ if (high) {
+ s->select &= AVIC_L_MASK;
+ } else {
+ s->select &= ((uint64_t) AVIC_H_MASK) << 32;
+ }
+ s->select |= data;
+ break;
+ case 0x20: /* Interrupt Enable */
+ s->enable |= data;
+ break;
+ case 0x28: /* Interrupt Enable Clear */
+ s->enable &= ~data;
+ break;
+ case 0x30: /* Software Interrupt */
+ qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
+ "IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
+ break;
+ case 0x38: /* Software Interrupt Clear */
+ qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
+ "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
+ break;
+ case 0x50: /* Interrupt Event */
+ /* Register has deposit64() semantics - overwrite the top four valid
+ * IRQ bits, as only the top four IRQs (GPIOs) can change their event
+ * type */
+ if (high) {
+ s->event &= ~AVIC_EVENT_W_MASK;
+ s->event |= (data & AVIC_EVENT_W_MASK);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Ignoring invalid write to interrupt event register");
+ }
+ break;
+ case 0x58: /* Edge Triggered Interrupt Clear */
+ s->raw &= ~(data & ~s->sense);
+ break;
+ case 0x00: /* IRQ Status */
+ case 0x08: /* FIQ Status */
+ case 0x10: /* Raw Interrupt Status */
+ case 0x40: /* Interrupt Sensitivity */
+ case 0x48: /* Interrupt Both Edge Trigger Control */
+ case 0x60: /* Edge Triggered Interrupt Status */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write of read-only register with offset 0x%"
+ HWADDR_PRIx "\n", __func__, offset);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+ aspeed_vic_update(s);
+}
+
+static const MemoryRegionOps aspeed_vic_ops = {
+ .read = aspeed_vic_read,
+ .write = aspeed_vic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void aspeed_vic_reset(DeviceState *dev)
+{
+ AspeedVICState *s = ASPEED_VIC(dev);
+
+ s->level = 0;
+ s->raw = 0;
+ s->select = 0;
+ s->enable = 0;
+ s->trigger = 0;
+ s->sense = 0x1F07FFF8FFFFULL;
+ s->dual_edge = 0xF800070000ULL;
+ s->event = 0x5F07FFF8FFFFULL;
+}
+
+#define AVIC_IO_REGION_SIZE 0x20000
+
+static void aspeed_vic_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedVICState *s = ASPEED_VIC(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
+ TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
+
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
+ sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_irq(sbd, &s->fiq);
+}
+
+static const VMStateDescription vmstate_aspeed_vic = {
+ .name = "aspeed.new-vic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(level, AspeedVICState),
+ VMSTATE_UINT64(raw, AspeedVICState),
+ VMSTATE_UINT64(select, AspeedVICState),
+ VMSTATE_UINT64(enable, AspeedVICState),
+ VMSTATE_UINT64(trigger, AspeedVICState),
+ VMSTATE_UINT64(sense, AspeedVICState),
+ VMSTATE_UINT64(dual_edge, AspeedVICState),
+ VMSTATE_UINT64(event, AspeedVICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void aspeed_vic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = aspeed_vic_realize;
+ dc->reset = aspeed_vic_reset;
+ dc->desc = "ASPEED Interrupt Controller (New)";
+ dc->vmsd = &vmstate_aspeed_vic;
+}
+
+static const TypeInfo aspeed_vic_info = {
+ .name = TYPE_ASPEED_VIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedVICState),
+ .class_init = aspeed_vic_class_init,
+};
+
+static void aspeed_vic_register_types(void)
+{
+ type_register_static(&aspeed_vic_info);
+}
+
+type_init(aspeed_vic_register_types);
diff --git a/include/hw/intc/aspeed_vic.h b/include/hw/intc/aspeed_vic.h
new file mode 100644
index 0000000..107ff17
--- /dev/null
+++ b/include/hw/intc/aspeed_vic.h
@@ -0,0 +1,48 @@
+/*
+ * ASPEED Interrupt Controller (New)
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ *
+ * Need to add SVIC and CVIC support
+ */
+#ifndef ASPEED_VIC_H
+#define ASPEED_VIC_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ASPEED_VIC "aspeed.vic"
+#define ASPEED_VIC(obj) OBJECT_CHECK(AspeedVICState, (obj), TYPE_ASPEED_VIC)
+
+#define ASPEED_VIC_NR_IRQS 51
+
+typedef struct AspeedVICState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion iomem;
+ qemu_irq irq;
+ qemu_irq fiq;
+
+ uint64_t level;
+ uint64_t raw;
+ uint64_t select;
+ uint64_t enable;
+ uint64_t trigger;
+
+ /* 0=edge, 1=level */
+ uint64_t sense;
+
+ /* 0=single-edge, 1=dual-edge */
+ uint64_t dual_edge;
+
+ /* 0=low-sensitive/falling-edge, 1=high-sensitive/rising-edge */
+ uint64_t event;
+} AspeedVICState;
+
+#endif /* ASPEED_VIC_H */
diff --git a/trace-events b/trace-events
index e589230..d494de1 100644
--- a/trace-events
+++ b/trace-events
@@ -1901,3 +1901,10 @@ aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d"
aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32
aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32
aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64
+
+# hw/intc/aspeed_vic.c
+aspeed_vic_set_irq(int irq, int level) "Enabling IRQ %d: %d"
+aspeed_vic_update_fiq(int flags) "Raising FIQ: %d"
+aspeed_vic_update_irq(int flags) "Raising IRQ: %d"
+aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32
+aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 14/21] hw/arm: Add ASPEED AST2400 SoC model
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (12 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 13/21] hw/intc: Add (new) ASPEED VIC " Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 15/21] hw/arm: Add palmetto-bmc machine Peter Maydell
` (8 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Andrew Jeffery <andrew@aj.id.au>
While the ASPEED AST2400 SoC[1] has a broad range of capabilities this
implementation is minimal, comprising an ARM926 processor, ASPEED VIC
and timer devices, and a 8250 UART.
[1] http://www.aspeedtech.com/products.php?fPath=20&rId=376
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Message-id: 1458096317-25223-4-git-send-email-andrew@aj.id.au
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Makefile.objs | 1 +
hw/arm/ast2400.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++
include/hw/arm/ast2400.h | 35 ++++++++++++
3 files changed, 173 insertions(+)
create mode 100644 hw/arm/ast2400.c
create mode 100644 include/hw/arm/ast2400.h
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index a711e4d..f333b7f 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -16,3 +16,4 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
+obj-$(CONFIG_ASPEED_SOC) += ast2400.o
diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
new file mode 100644
index 0000000..daa5518
--- /dev/null
+++ b/hw/arm/ast2400.c
@@ -0,0 +1,137 @@
+/*
+ * AST2400 SoC
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ * Jeremy Kerr <jk@ozlabs.org>
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "exec/address-spaces.h"
+#include "hw/arm/ast2400.h"
+#include "hw/char/serial.h"
+
+#define AST2400_UART_5_BASE 0x00184000
+#define AST2400_IOMEM_SIZE 0x00200000
+#define AST2400_IOMEM_BASE 0x1E600000
+#define AST2400_VIC_BASE 0x1E6C0000
+#define AST2400_TIMER_BASE 0x1E782000
+
+static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
+static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
+
+/*
+ * IO handlers: simply catch any reads/writes to IO addresses that aren't
+ * handled by a device mapping.
+ */
+
+static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
+ __func__, offset, size);
+ return 0;
+}
+
+static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
+ __func__, offset, value, size);
+}
+
+static const MemoryRegionOps ast2400_io_ops = {
+ .read = ast2400_io_read,
+ .write = ast2400_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ast2400_init(Object *obj)
+{
+ AST2400State *s = AST2400(obj);
+
+ s->cpu = cpu_arm_init("arm926");
+
+ object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC);
+ object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL);
+ qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default());
+
+ object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
+ object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
+ qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
+}
+
+static void ast2400_realize(DeviceState *dev, Error **errp)
+{
+ int i;
+ AST2400State *s = AST2400(dev);
+ Error *err = NULL;
+
+ /* IO space */
+ memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
+ "ast2400.io", AST2400_IOMEM_SIZE);
+ memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE,
+ &s->iomem, -1);
+
+ /* VIC */
+ object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
+ qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
+ qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ));
+
+ /* Timer */
+ object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE);
+ for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
+ qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
+ }
+
+ /* UART - attach an 8250 to the IO space as our UART5 */
+ if (serial_hds[0]) {
+ qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
+ serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
+ uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
+ }
+}
+
+static void ast2400_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = ast2400_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
+static const TypeInfo ast2400_type_info = {
+ .name = TYPE_AST2400,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AST2400State),
+ .instance_init = ast2400_init,
+ .class_init = ast2400_class_init,
+};
+
+static void ast2400_register_types(void)
+{
+ type_register_static(&ast2400_type_info);
+}
+
+type_init(ast2400_register_types)
diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
new file mode 100644
index 0000000..f16a1ed
--- /dev/null
+++ b/include/hw/arm/ast2400.h
@@ -0,0 +1,35 @@
+/*
+ * ASPEED AST2400 SoC
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef AST2400_H
+#define AST2400_H
+
+#include "hw/arm/arm.h"
+#include "hw/intc/aspeed_vic.h"
+#include "hw/timer/aspeed_timer.h"
+
+typedef struct AST2400State {
+ /*< private >*/
+ DeviceState parent;
+
+ /*< public >*/
+ ARMCPU *cpu;
+ MemoryRegion iomem;
+ AspeedVICState vic;
+ AspeedTimerCtrlState timerctrl;
+} AST2400State;
+
+#define TYPE_AST2400 "ast2400"
+#define AST2400(obj) OBJECT_CHECK(AST2400State, (obj), TYPE_AST2400)
+
+#define AST2400_SDRAM_BASE 0x40000000
+
+#endif /* AST2400_H */
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 15/21] hw/arm: Add palmetto-bmc machine
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (13 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 14/21] hw/arm: Add ASPEED AST2400 SoC model Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 16/21] bcm2835_peripherals: enable sdhci pending-insert quirk for raspberry pi Peter Maydell
` (7 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Andrew Jeffery <andrew@aj.id.au>
The new machine is a thin layer over the AST2400 ARM926-based SoC[1].
Between the minimal machine and the current SoC implementation there is
enough functionality to boot an aspeed_defconfig Linux kernel to
userspace. Nothing yet is specific to the Palmetto's BMC (other than
using an AST2400 SoC), but creating specific machine types is preferable
to a generic machine that doesn't match any particular hardware.
[1] http://www.aspeedtech.com/products.php?fPath=20&rId=376
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Message-id: 1458096317-25223-5-git-send-email-andrew@aj.id.au
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/Makefile.objs | 2 +-
hw/arm/palmetto-bmc.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 hw/arm/palmetto-bmc.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index f333b7f..954c9fe 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -16,4 +16,4 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
-obj-$(CONFIG_ASPEED_SOC) += ast2400.o
+obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
new file mode 100644
index 0000000..55d7419
--- /dev/null
+++ b/hw/arm/palmetto-bmc.c
@@ -0,0 +1,65 @@
+/*
+ * OpenPOWER Palmetto BMC
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "exec/address-spaces.h"
+#include "hw/arm/arm.h"
+#include "hw/arm/ast2400.h"
+#include "hw/boards.h"
+
+static struct arm_boot_info palmetto_bmc_binfo = {
+ .loader_start = AST2400_SDRAM_BASE,
+ .board_id = 0,
+ .nb_cpus = 1,
+};
+
+typedef struct PalmettoBMCState {
+ AST2400State soc;
+ MemoryRegion ram;
+} PalmettoBMCState;
+
+static void palmetto_bmc_init(MachineState *machine)
+{
+ PalmettoBMCState *bmc;
+
+ bmc = g_new0(PalmettoBMCState, 1);
+ object_initialize(&bmc->soc, (sizeof(bmc->soc)), TYPE_AST2400);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&bmc->soc),
+ &error_abort);
+
+ memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size);
+ memory_region_add_subregion(get_system_memory(), AST2400_SDRAM_BASE,
+ &bmc->ram);
+ object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
+ &error_abort);
+ object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
+ &error_abort);
+
+ palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
+ palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
+ palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
+ palmetto_bmc_binfo.ram_size = ram_size;
+ arm_load_kernel(ARM_CPU(first_cpu), &palmetto_bmc_binfo);
+}
+
+static void palmetto_bmc_machine_init(MachineClass *mc)
+{
+ mc->desc = "OpenPOWER Palmetto BMC";
+ mc->init = palmetto_bmc_init;
+ mc->max_cpus = 1;
+ mc->no_sdcard = 1;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_sdcard = 1;
+ mc->no_parallel = 1;
+}
+
+DEFINE_MACHINE("palmetto-bmc", palmetto_bmc_machine_init);
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 16/21] bcm2835_peripherals: enable sdhci pending-insert quirk for raspberry pi
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (14 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 15/21] hw/arm: Add palmetto-bmc machine Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 17/21] bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block Peter Maydell
` (6 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Andrew Baumann <Andrew.Baumann@microsoft.com>
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1457467526-8840-2-git-send-email-Andrew.Baumann@microsoft.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/bcm2835_peripherals.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 6d66fa0..6ce9cd1 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -171,6 +171,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
return;
}
+ object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
if (err) {
error_propagate(errp, err);
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 17/21] bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (15 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 16/21] bcm2835_peripherals: enable sdhci pending-insert quirk for raspberry pi Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 18/21] bcm2835_fb: add framebuffer device for Raspberry Pi Peter Maydell
` (5 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Andrew Baumann <Andrew.Baumann@microsoft.com>
At present only the core UART functions (data path for tx/rx) are
implemented, which is enough for UEFI to boot. The following
features/registers are unimplemented:
* Line/modem control
* Scratch register
* Extra control
* Baudrate
* SPI interfaces
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1457467526-8840-3-git-send-email-Andrew.Baumann@microsoft.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/bcm2835_peripherals.c | 32 ++++
hw/char/Makefile.objs | 1 +
hw/char/bcm2835_aux.c | 316 +++++++++++++++++++++++++++++++++++
include/hw/arm/bcm2835_peripherals.h | 2 +
include/hw/char/bcm2835_aux.h | 33 ++++
5 files changed, 384 insertions(+)
create mode 100644 hw/char/bcm2835_aux.c
create mode 100644 include/hw/char/bcm2835_aux.h
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 6ce9cd1..d6453cc 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -12,6 +12,7 @@
#include "hw/arm/bcm2835_peripherals.h"
#include "hw/misc/bcm2835_mbox_defs.h"
#include "hw/arm/raspi_platform.h"
+#include "sysemu/char.h"
/* Peripheral base address on the VC (GPU) system bus */
#define BCM2835_VC_PERI_BASE 0x7e000000
@@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
+ /* AUX / UART1 */
+ object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX);
+ object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL);
+ qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default());
+
/* Mailboxes */
object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
@@ -79,6 +85,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
MemoryRegion *ram;
Error *err = NULL;
uint32_t ram_size;
+ CharDriverState *chr;
int n;
obj = object_property_get_link(OBJECT(dev), "ram", &err);
@@ -131,6 +138,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
INTERRUPT_UART));
+ /* AUX / UART1 */
+ /* TODO: don't call qemu_char_get_next_serial() here, instead set
+ * chardev properties for each uart at the board level, once pl011
+ * (uart0) has been updated to avoid qemu_char_get_next_serial()
+ */
+ chr = qemu_char_get_next_serial();
+ if (chr == NULL) {
+ chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
+ }
+ qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
+
+ object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
+ qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
+ INTERRUPT_AUX));
+
/* Mailboxes */
object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
if (err) {
@@ -203,6 +233,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = bcm2835_peripherals_realize;
+ /* Reason: realize() method uses qemu_char_get_next_serial() */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo bcm2835_peripherals_type_info = {
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index 5931cc8..69a553c 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o
obj-$(CONFIG_PSERIES) += spapr_vty.o
obj-$(CONFIG_DIGIC) += digic-uart.o
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
+obj-$(CONFIG_RASPI) += bcm2835_aux.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
new file mode 100644
index 0000000..0394d11
--- /dev/null
+++ b/hw/char/bcm2835_aux.c
@@ -0,0 +1,316 @@
+/*
+ * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
+ * Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ * Based on pl011.c, copyright terms below:
+ *
+ * Arm PrimeCell PL011 UART
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ *
+ * At present only the core UART functions (data path for tx/rx) are
+ * implemented. The following features/registers are unimplemented:
+ * - Line/modem control
+ * - Scratch register
+ * - Extra control
+ * - Baudrate
+ * - SPI interfaces
+ */
+
+#include "qemu/osdep.h"
+#include "hw/char/bcm2835_aux.h"
+
+#define AUX_IRQ 0x0
+#define AUX_ENABLES 0x4
+#define AUX_MU_IO_REG 0x40
+#define AUX_MU_IER_REG 0x44
+#define AUX_MU_IIR_REG 0x48
+#define AUX_MU_LCR_REG 0x4c
+#define AUX_MU_MCR_REG 0x50
+#define AUX_MU_LSR_REG 0x54
+#define AUX_MU_MSR_REG 0x58
+#define AUX_MU_SCRATCH 0x5c
+#define AUX_MU_CNTL_REG 0x60
+#define AUX_MU_STAT_REG 0x64
+#define AUX_MU_BAUD_REG 0x68
+
+/* bits in IER/IIR registers */
+#define TX_INT 0x1
+#define RX_INT 0x2
+
+static void bcm2835_aux_update(BCM2835AuxState *s)
+{
+ /* signal an interrupt if either:
+ * 1. rx interrupt is enabled and we have a non-empty rx fifo, or
+ * 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
+ */
+ s->iir = 0;
+ if ((s->ier & RX_INT) && s->read_count != 0) {
+ s->iir |= RX_INT;
+ }
+ if (s->ier & TX_INT) {
+ s->iir |= TX_INT;
+ }
+ qemu_set_irq(s->irq, s->iir != 0);
+}
+
+static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835AuxState *s = opaque;
+ uint32_t c, res;
+
+ switch (offset) {
+ case AUX_IRQ:
+ return s->iir != 0;
+
+ case AUX_ENABLES:
+ return 1; /* mini UART permanently enabled */
+
+ case AUX_MU_IO_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ c = s->read_fifo[s->read_pos];
+ if (s->read_count > 0) {
+ s->read_count--;
+ if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
+ s->read_pos = 0;
+ }
+ }
+ if (s->chr) {
+ qemu_chr_accept_input(s->chr);
+ }
+ bcm2835_aux_update(s);
+ return c;
+
+ case AUX_MU_IER_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ return 0xc0 | s->ier; /* FIFO enables always read 1 */
+
+ case AUX_MU_IIR_REG:
+ res = 0xc0; /* FIFO enables */
+ /* The spec is unclear on what happens when both tx and rx
+ * interrupts are active, besides that this cannot occur. At
+ * present, we choose to prioritise the rx interrupt, since
+ * the tx fifo is always empty. */
+ if (s->read_count != 0) {
+ res |= 0x4;
+ } else {
+ res |= 0x2;
+ }
+ if (s->iir == 0) {
+ res |= 0x1;
+ }
+ return res;
+
+ case AUX_MU_LCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_MCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_LSR_REG:
+ res = 0x60; /* tx idle, empty */
+ if (s->read_count != 0) {
+ res |= 0x1;
+ }
+ return res;
+
+ case AUX_MU_MSR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_SCRATCH:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
+ return 0;
+
+ case AUX_MU_CNTL_REG:
+ return 0x3; /* tx, rx enabled */
+
+ case AUX_MU_STAT_REG:
+ res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
+ if (s->read_count > 0) {
+ res |= 0x1; /* data in input buffer */
+ assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
+ res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
+ }
+ return res;
+
+ case AUX_MU_BAUD_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
+ return 0;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+}
+
+static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ BCM2835AuxState *s = opaque;
+ unsigned char ch;
+
+ switch (offset) {
+ case AUX_ENABLES:
+ if (value != 1) {
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
+ "or disable UART\n", __func__);
+ }
+ break;
+
+ case AUX_MU_IO_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ ch = value;
+ if (s->chr) {
+ qemu_chr_fe_write(s->chr, &ch, 1);
+ }
+ break;
+
+ case AUX_MU_IER_REG:
+ /* "DLAB bit set means access baudrate register" is NYI */
+ s->ier = value & (TX_INT | RX_INT);
+ bcm2835_aux_update(s);
+ break;
+
+ case AUX_MU_IIR_REG:
+ if (value & 0x2) {
+ s->read_count = 0;
+ }
+ break;
+
+ case AUX_MU_LCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
+ break;
+
+ case AUX_MU_MCR_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
+ break;
+
+ case AUX_MU_SCRATCH:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
+ break;
+
+ case AUX_MU_CNTL_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
+ break;
+
+ case AUX_MU_BAUD_REG:
+ qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ }
+
+ bcm2835_aux_update(s);
+}
+
+static int bcm2835_aux_can_receive(void *opaque)
+{
+ BCM2835AuxState *s = opaque;
+
+ return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
+}
+
+static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
+{
+ BCM2835AuxState *s = opaque;
+ int slot;
+
+ slot = s->read_pos + s->read_count;
+ if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
+ slot -= BCM2835_AUX_RX_FIFO_LEN;
+ }
+ s->read_fifo[slot] = value;
+ s->read_count++;
+ if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
+ /* buffer full */
+ }
+ bcm2835_aux_update(s);
+}
+
+static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
+{
+ bcm2835_aux_put_fifo(opaque, *buf);
+}
+
+static const MemoryRegionOps bcm2835_aux_ops = {
+ .read = bcm2835_aux_read,
+ .write = bcm2835_aux_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_aux = {
+ .name = TYPE_BCM2835_AUX,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
+ BCM2835_AUX_RX_FIFO_LEN),
+ VMSTATE_UINT8(read_pos, BCM2835AuxState),
+ VMSTATE_UINT8(read_count, BCM2835AuxState),
+ VMSTATE_UINT8(ier, BCM2835AuxState),
+ VMSTATE_UINT8(iir, BCM2835AuxState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_aux_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ BCM2835AuxState *s = BCM2835_AUX(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
+ TYPE_BCM2835_AUX, 0x100);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835AuxState *s = BCM2835_AUX(dev);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
+ bcm2835_aux_receive, NULL, s);
+ }
+}
+
+static Property bcm2835_aux_props[] = {
+ DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = bcm2835_aux_realize;
+ dc->vmsd = &vmstate_bcm2835_aux;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ dc->props = bcm2835_aux_props;
+}
+
+static const TypeInfo bcm2835_aux_info = {
+ .name = TYPE_BCM2835_AUX,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835AuxState),
+ .instance_init = bcm2835_aux_init,
+ .class_init = bcm2835_aux_class_init,
+};
+
+static void bcm2835_aux_register_types(void)
+{
+ type_register_static(&bcm2835_aux_info);
+}
+
+type_init(bcm2835_aux_register_types)
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 5d888dc..889adf5 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -14,6 +14,7 @@
#include "qemu-common.h"
#include "exec/address-spaces.h"
#include "hw/sysbus.h"
+#include "hw/char/bcm2835_aux.h"
#include "hw/intc/bcm2835_ic.h"
#include "hw/misc/bcm2835_property.h"
#include "hw/misc/bcm2835_mbox.h"
@@ -33,6 +34,7 @@ typedef struct BCM2835PeripheralState {
qemu_irq irq, fiq;
SysBusDevice *uart0;
+ BCM2835AuxState aux;
BCM2835ICState ic;
BCM2835PropertyState property;
BCM2835MboxState mboxes;
diff --git a/include/hw/char/bcm2835_aux.h b/include/hw/char/bcm2835_aux.h
new file mode 100644
index 0000000..42f0ee7
--- /dev/null
+++ b/include/hw/char/bcm2835_aux.h
@@ -0,0 +1,33 @@
+/*
+ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#ifndef BCM2835_AUX_H
+#define BCM2835_AUX_H
+
+#include "hw/sysbus.h"
+#include "sysemu/char.h"
+
+#define TYPE_BCM2835_AUX "bcm2835-aux"
+#define BCM2835_AUX(obj) OBJECT_CHECK(BCM2835AuxState, (obj), TYPE_BCM2835_AUX)
+
+#define BCM2835_AUX_RX_FIFO_LEN 8
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN];
+ uint8_t read_pos, read_count;
+ uint8_t ier, iir;
+} BCM2835AuxState;
+
+#endif
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 18/21] bcm2835_fb: add framebuffer device for Raspberry Pi
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (16 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 17/21] bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 19/21] bcm2835_property: implement framebuffer control/configuration properties Peter Maydell
` (4 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Grégory ESTRADE <gregory.estrade@gmail.com>
The framebuffer occupies the upper portion of memory (64MiB by
default), but it can only be controlled/configured via a system
mailbox or property channel (to be added by a subsequent patch).
Signed-off-by: Grégory ESTRADE <gregory.estrade@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Message-id: 1457467526-8840-4-git-send-email-Andrew.Baumann@microsoft.com
[AB: added Windows (BGR) support and cleanup/refactoring for upstream submission]
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/bcm2835_peripherals.c | 38 +++-
hw/arm/bcm2836.c | 2 +
hw/arm/raspi.c | 5 +-
hw/display/Makefile.objs | 1 +
hw/display/bcm2835_fb.c | 424 +++++++++++++++++++++++++++++++++++
include/hw/arm/bcm2835_peripherals.h | 2 +
include/hw/display/bcm2835_fb.h | 47 ++++
7 files changed, 517 insertions(+), 2 deletions(-)
create mode 100644 hw/display/bcm2835_fb.c
create mode 100644 include/hw/display/bcm2835_fb.h
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index d6453cc..c2fe6b7 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -62,6 +62,16 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
OBJECT(&s->mbox_mr), &error_abort);
+ /* Framebuffer */
+ object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB);
+ object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL);
+ object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size",
+ &error_abort);
+ qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default());
+
+ object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
+ OBJECT(&s->gpu_bus_mr), &error_abort);
+
/* Property channel */
object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY);
object_property_add_child(obj, "property", OBJECT(&s->property), NULL);
@@ -84,7 +94,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
Object *obj;
MemoryRegion *ram;
Error *err = NULL;
- uint32_t ram_size;
+ uint32_t ram_size, vcram_size;
CharDriverState *chr;
int n;
@@ -174,6 +184,32 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
INTERRUPT_ARM_MAILBOX));
+ /* Framebuffer */
+ vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size",
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size,
+ "vcram-base", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->fb), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
+ qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
+
/* Property channel */
object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err);
if (err) {
diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c
index 0321439..89a6b35 100644
--- a/hw/arm/bcm2836.c
+++ b/hw/arm/bcm2836.c
@@ -42,6 +42,8 @@ static void bcm2836_init(Object *obj)
&error_abort);
object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals),
"board-rev", &error_abort);
+ object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals),
+ "vcram-size", &error_abort);
qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default());
}
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index 6582279..5498209 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -113,6 +113,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
static void raspi2_init(MachineState *machine)
{
RasPiState *s = g_new0(RasPiState, 1);
+ uint32_t vcram_size;
DriveInfo *di;
BlockBackend *blk;
BusState *bus;
@@ -149,7 +150,9 @@ static void raspi2_init(MachineState *machine)
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
- setup_boot(machine, 2, machine->ram_size);
+ vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size",
+ &error_abort);
+ setup_boot(machine, 2, machine->ram_size - vcram_size);
}
static void raspi2_machine_init(MachineClass *mc)
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index f0cf431..d99780e 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -27,6 +27,7 @@ endif
obj-$(CONFIG_OMAP) += omap_dss.o
obj-$(CONFIG_OMAP) += omap_lcdc.o
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
+obj-$(CONFIG_RASPI) += bcm2835_fb.o
obj-$(CONFIG_SM501) += sm501.o
obj-$(CONFIG_TCX) += tcx.o
obj-$(CONFIG_CG3) += cg3.o
diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c
new file mode 100644
index 0000000..779b56f
--- /dev/null
+++ b/hw/display/bcm2835_fb.c
@@ -0,0 +1,424 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
+ * This code is licensed under the GNU GPLv2 and later.
+ *
+ * Heavily based on milkymist-vgafb.c, copyright terms below:
+ * QEMU model of the Milkymist VGA framebuffer.
+ *
+ * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/display/bcm2835_fb.h"
+#include "hw/display/framebuffer.h"
+#include "ui/pixel_ops.h"
+#include "hw/misc/bcm2835_mbox_defs.h"
+
+#define DEFAULT_VCRAM_SIZE 0x4000000
+#define BCM2835_FB_OFFSET 0x00100000
+
+static void fb_invalidate_display(void *opaque)
+{
+ BCM2835FBState *s = BCM2835_FB(opaque);
+
+ s->invalidate = true;
+}
+
+static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
+ int width, int deststep)
+{
+ BCM2835FBState *s = opaque;
+ uint16_t rgb565;
+ uint32_t rgb888;
+ uint8_t r, g, b;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int bpp = surface_bits_per_pixel(surface);
+
+ while (width--) {
+ switch (s->bpp) {
+ case 8:
+ /* lookup palette starting at video ram base
+ * TODO: cache translation, rather than doing this each time!
+ */
+ rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2));
+ r = (rgb888 >> 0) & 0xff;
+ g = (rgb888 >> 8) & 0xff;
+ b = (rgb888 >> 16) & 0xff;
+ src++;
+ break;
+ case 16:
+ rgb565 = lduw_le_p(src);
+ r = ((rgb565 >> 11) & 0x1f) << 3;
+ g = ((rgb565 >> 5) & 0x3f) << 2;
+ b = ((rgb565 >> 0) & 0x1f) << 3;
+ src += 2;
+ break;
+ case 24:
+ rgb888 = ldl_le_p(src);
+ r = (rgb888 >> 0) & 0xff;
+ g = (rgb888 >> 8) & 0xff;
+ b = (rgb888 >> 16) & 0xff;
+ src += 3;
+ break;
+ case 32:
+ rgb888 = ldl_le_p(src);
+ r = (rgb888 >> 0) & 0xff;
+ g = (rgb888 >> 8) & 0xff;
+ b = (rgb888 >> 16) & 0xff;
+ src += 4;
+ break;
+ default:
+ r = 0;
+ g = 0;
+ b = 0;
+ break;
+ }
+
+ if (s->pixo == 0) {
+ /* swap to BGR pixel format */
+ uint8_t tmp = r;
+ r = b;
+ b = tmp;
+ }
+
+ switch (bpp) {
+ case 8:
+ *dst++ = rgb_to_pixel8(r, g, b);
+ break;
+ case 15:
+ *(uint16_t *)dst = rgb_to_pixel15(r, g, b);
+ dst += 2;
+ break;
+ case 16:
+ *(uint16_t *)dst = rgb_to_pixel16(r, g, b);
+ dst += 2;
+ break;
+ case 24:
+ rgb888 = rgb_to_pixel24(r, g, b);
+ *dst++ = rgb888 & 0xff;
+ *dst++ = (rgb888 >> 8) & 0xff;
+ *dst++ = (rgb888 >> 16) & 0xff;
+ break;
+ case 32:
+ *(uint32_t *)dst = rgb_to_pixel32(r, g, b);
+ dst += 4;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+static void fb_update_display(void *opaque)
+{
+ BCM2835FBState *s = opaque;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int first = 0;
+ int last = 0;
+ int src_width = 0;
+ int dest_width = 0;
+
+ if (s->lock || !s->xres) {
+ return;
+ }
+
+ src_width = s->xres * (s->bpp >> 3);
+ dest_width = s->xres;
+
+ switch (surface_bits_per_pixel(surface)) {
+ case 0:
+ return;
+ case 8:
+ break;
+ case 15:
+ dest_width *= 2;
+ break;
+ case 16:
+ dest_width *= 2;
+ break;
+ case 24:
+ dest_width *= 3;
+ break;
+ case 32:
+ dest_width *= 4;
+ break;
+ default:
+ hw_error("bcm2835_fb: bad color depth\n");
+ break;
+ }
+
+ if (s->invalidate) {
+ framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base,
+ s->yres, src_width);
+ }
+
+ framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
+ src_width, dest_width, 0, s->invalidate,
+ draw_line_src16, s, &first, &last);
+
+ if (first >= 0) {
+ dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1);
+ }
+
+ s->invalidate = false;
+}
+
+static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
+{
+ value &= ~0xf;
+
+ s->lock = true;
+
+ s->xres = ldl_le_phys(&s->dma_as, value);
+ s->yres = ldl_le_phys(&s->dma_as, value + 4);
+ s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
+ s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
+ s->bpp = ldl_le_phys(&s->dma_as, value + 20);
+ s->xoffset = ldl_le_phys(&s->dma_as, value + 24);
+ s->yoffset = ldl_le_phys(&s->dma_as, value + 28);
+
+ s->base = s->vcram_base | (value & 0xc0000000);
+ s->base += BCM2835_FB_OFFSET;
+
+ /* TODO - Manage properly virtual resolution */
+
+ s->pitch = s->xres * (s->bpp >> 3);
+ s->size = s->yres * s->pitch;
+
+ stl_le_phys(&s->dma_as, value + 16, s->pitch);
+ stl_le_phys(&s->dma_as, value + 32, s->base);
+ stl_le_phys(&s->dma_as, value + 36, s->size);
+
+ s->invalidate = true;
+ qemu_console_resize(s->con, s->xres, s->yres);
+ s->lock = false;
+}
+
+void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
+ uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
+ uint32_t *pixo, uint32_t *alpha)
+{
+ s->lock = true;
+
+ /* TODO: input validation! */
+ if (xres) {
+ s->xres = *xres;
+ }
+ if (yres) {
+ s->yres = *yres;
+ }
+ if (xoffset) {
+ s->xoffset = *xoffset;
+ }
+ if (yoffset) {
+ s->yoffset = *yoffset;
+ }
+ if (bpp) {
+ s->bpp = *bpp;
+ }
+ if (pixo) {
+ s->pixo = *pixo;
+ }
+ if (alpha) {
+ s->alpha = *alpha;
+ }
+
+ /* TODO - Manage properly virtual resolution */
+
+ s->pitch = s->xres * (s->bpp >> 3);
+ s->size = s->yres * s->pitch;
+
+ s->invalidate = true;
+ qemu_console_resize(s->con, s->xres, s->yres);
+ s->lock = false;
+}
+
+static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835FBState *s = opaque;
+ uint32_t res = 0;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ res = MBOX_CHAN_FB;
+ s->pending = false;
+ qemu_set_irq(s->mbox_irq, 0);
+ break;
+
+ case MBOX_AS_PENDING:
+ res = s->pending;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+
+ return res;
+}
+
+static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ BCM2835FBState *s = opaque;
+
+ switch (offset) {
+ case MBOX_AS_DATA:
+ /* bcm2835_mbox should check our pending status before pushing */
+ assert(!s->pending);
+ s->pending = true;
+ bcm2835_fb_mbox_push(s, value);
+ qemu_set_irq(s->mbox_irq, 1);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return;
+ }
+}
+
+static const MemoryRegionOps bcm2835_fb_ops = {
+ .read = bcm2835_fb_read,
+ .write = bcm2835_fb_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_fb = {
+ .name = TYPE_BCM2835_FB,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(lock, BCM2835FBState),
+ VMSTATE_BOOL(invalidate, BCM2835FBState),
+ VMSTATE_BOOL(pending, BCM2835FBState),
+ VMSTATE_UINT32(xres, BCM2835FBState),
+ VMSTATE_UINT32(yres, BCM2835FBState),
+ VMSTATE_UINT32(xres_virtual, BCM2835FBState),
+ VMSTATE_UINT32(yres_virtual, BCM2835FBState),
+ VMSTATE_UINT32(xoffset, BCM2835FBState),
+ VMSTATE_UINT32(yoffset, BCM2835FBState),
+ VMSTATE_UINT32(bpp, BCM2835FBState),
+ VMSTATE_UINT32(base, BCM2835FBState),
+ VMSTATE_UINT32(pitch, BCM2835FBState),
+ VMSTATE_UINT32(size, BCM2835FBState),
+ VMSTATE_UINT32(pixo, BCM2835FBState),
+ VMSTATE_UINT32(alpha, BCM2835FBState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const GraphicHwOps vgafb_ops = {
+ .invalidate = fb_invalidate_display,
+ .gfx_update = fb_update_display,
+};
+
+static void bcm2835_fb_init(Object *obj)
+{
+ BCM2835FBState *s = BCM2835_FB(obj);
+
+ memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
+ 0x10);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
+}
+
+static void bcm2835_fb_reset(DeviceState *dev)
+{
+ BCM2835FBState *s = BCM2835_FB(dev);
+
+ s->pending = false;
+
+ s->xres_virtual = s->xres;
+ s->yres_virtual = s->yres;
+ s->xoffset = 0;
+ s->yoffset = 0;
+ s->base = s->vcram_base + BCM2835_FB_OFFSET;
+ s->pitch = s->xres * (s->bpp >> 3);
+ s->size = s->yres * s->pitch;
+
+ s->invalidate = true;
+ s->lock = false;
+}
+
+static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835FBState *s = BCM2835_FB(dev);
+ Error *err = NULL;
+ Object *obj;
+
+ if (s->vcram_base == 0) {
+ error_setg(errp, "%s: required vcram-base property not set", __func__);
+ return;
+ }
+
+ obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required dma-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->dma_mr = MEMORY_REGION(obj);
+ address_space_init(&s->dma_as, s->dma_mr, NULL);
+
+ bcm2835_fb_reset(dev);
+
+ s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
+ qemu_console_resize(s->con, s->xres, s->yres);
+}
+
+static Property bcm2835_fb_props[] = {
+ DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
+ DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
+ DEFAULT_VCRAM_SIZE),
+ DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
+ DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
+ DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
+ DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
+ DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = bcm2835_fb_props;
+ dc->realize = bcm2835_fb_realize;
+ dc->reset = bcm2835_fb_reset;
+ dc->vmsd = &vmstate_bcm2835_fb;
+}
+
+static TypeInfo bcm2835_fb_info = {
+ .name = TYPE_BCM2835_FB,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835FBState),
+ .class_init = bcm2835_fb_class_init,
+ .instance_init = bcm2835_fb_init,
+};
+
+static void bcm2835_fb_register_types(void)
+{
+ type_register_static(&bcm2835_fb_info);
+}
+
+type_init(bcm2835_fb_register_types)
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index 889adf5..e19d360 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -15,6 +15,7 @@
#include "exec/address-spaces.h"
#include "hw/sysbus.h"
#include "hw/char/bcm2835_aux.h"
+#include "hw/display/bcm2835_fb.h"
#include "hw/intc/bcm2835_ic.h"
#include "hw/misc/bcm2835_property.h"
#include "hw/misc/bcm2835_mbox.h"
@@ -35,6 +36,7 @@ typedef struct BCM2835PeripheralState {
SysBusDevice *uart0;
BCM2835AuxState aux;
+ BCM2835FBState fb;
BCM2835ICState ic;
BCM2835PropertyState property;
BCM2835MboxState mboxes;
diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h
new file mode 100644
index 0000000..9a12d7a
--- /dev/null
+++ b/include/hw/display/bcm2835_fb.h
@@ -0,0 +1,47 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
+ *
+ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
+ * Written by Andrew Baumann
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#ifndef BCM2835_FB_H
+#define BCM2835_FB_H
+
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+#include "ui/console.h"
+
+#define TYPE_BCM2835_FB "bcm2835-fb"
+#define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB)
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice busdev;
+ /*< public >*/
+
+ uint32_t vcram_base, vcram_size;
+ MemoryRegion *dma_mr;
+ AddressSpace dma_as;
+ MemoryRegion iomem;
+ MemoryRegionSection fbsection;
+ QemuConsole *con;
+ qemu_irq mbox_irq;
+
+ bool lock, invalidate, pending;
+ uint32_t xres, yres;
+ uint32_t xres_virtual, yres_virtual;
+ uint32_t xoffset, yoffset;
+ uint32_t bpp;
+ uint32_t base, pitch, size;
+ uint32_t pixo, alpha;
+} BCM2835FBState;
+
+void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres,
+ uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
+ uint32_t *pixo, uint32_t *alpha);
+
+#endif
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 19/21] bcm2835_property: implement framebuffer control/configuration properties
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (17 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 18/21] bcm2835_fb: add framebuffer device for Raspberry Pi Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 20/21] bcm2835_dma: add emulation of Raspberry Pi DMA controller Peter Maydell
` (3 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Grégory ESTRADE <gregory.estrade@gmail.com>
The property channel driver now interfaces with the framebuffer device
to query and set framebuffer parameters. As a result of this, the "get
ARM RAM size" query now correctly returns the video RAM base address
(not total RAM size), and the ram-size property is no longer relevant
here.
Signed-off-by: Grégory ESTRADE <gregory.estrade@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Message-id: 1457467526-8840-5-git-send-email-Andrew.Baumann@microsoft.com
[AB: cleanup/refactoring for upstream submission]
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/bcm2835_peripherals.c | 8 +--
hw/arm/raspi.c | 7 +-
hw/misc/bcm2835_property.c | 139 ++++++++++++++++++++++++++++++++++++-
include/hw/misc/bcm2835_property.h | 5 +-
4 files changed, 144 insertions(+), 15 deletions(-)
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index c2fe6b7..4d74a18 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -79,6 +79,8 @@ static void bcm2835_peripherals_init(Object *obj)
"board-rev", &error_abort);
qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default());
+ object_property_add_const_link(OBJECT(&s->property), "fb",
+ OBJECT(&s->fb), &error_abort);
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
@@ -211,12 +213,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
/* Property channel */
- object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
-
object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
if (err) {
error_propagate(errp, err);
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index 5498209..83fe809 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -164,11 +164,6 @@ static void raspi2_machine_init(MachineClass *mc)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->max_cpus = BCM2836_NCPUS;
-
- /* XXX: Temporary restriction in RAM size from the full 1GB. Since
- * we do not yet support the framebuffer / GPU, we need to limit
- * RAM usable by the OS to sit below the peripherals.
- */
- mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */
+ mc->default_ram_size = 1024 * 1024 * 1024;
};
DEFINE_MACHINE("raspi2", raspi2_machine_init)
diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c
index 41fbbe3..15dcc02 100644
--- a/hw/misc/bcm2835_property.c
+++ b/hw/misc/bcm2835_property.c
@@ -17,6 +17,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
uint32_t tot_len;
size_t resplen;
uint32_t tmp;
+ int n;
+ uint32_t offset, length, color;
+ uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha;
+ uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL,
+ *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL;
value &= ~0xf;
@@ -60,7 +65,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
/* base */
stl_le_phys(&s->dma_as, value + 12, 0);
/* size */
- stl_le_phys(&s->dma_as, value + 16, s->ram_size);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
+ resplen = 8;
+ break;
+ case 0x00010006: /* Get VC memory */
+ /* base */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
+ /* size */
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
resplen = 8;
break;
case 0x00028001: /* Set power state */
@@ -122,6 +134,114 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
resplen = 8;
break;
+ /* Frame buffer */
+
+ case 0x00040001: /* Allocate buffer */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->base);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->size);
+ resplen = 8;
+ break;
+ case 0x00048001: /* Release buffer */
+ resplen = 0;
+ break;
+ case 0x00040002: /* Blank screen */
+ resplen = 4;
+ break;
+ case 0x00040003: /* Get display width/height */
+ case 0x00040004:
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres);
+ resplen = 8;
+ break;
+ case 0x00044003: /* Test display width/height */
+ case 0x00044004:
+ resplen = 8;
+ break;
+ case 0x00048003: /* Set display width/height */
+ case 0x00048004:
+ xres = ldl_le_phys(&s->dma_as, value + 12);
+ newxres = &xres;
+ yres = ldl_le_phys(&s->dma_as, value + 16);
+ newyres = &yres;
+ resplen = 8;
+ break;
+ case 0x00040005: /* Get depth */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp);
+ resplen = 4;
+ break;
+ case 0x00044005: /* Test depth */
+ resplen = 4;
+ break;
+ case 0x00048005: /* Set depth */
+ bpp = ldl_le_phys(&s->dma_as, value + 12);
+ newbpp = &bpp;
+ resplen = 4;
+ break;
+ case 0x00040006: /* Get pixel order */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo);
+ resplen = 4;
+ break;
+ case 0x00044006: /* Test pixel order */
+ resplen = 4;
+ break;
+ case 0x00048006: /* Set pixel order */
+ pixo = ldl_le_phys(&s->dma_as, value + 12);
+ newpixo = &pixo;
+ resplen = 4;
+ break;
+ case 0x00040007: /* Get alpha */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha);
+ resplen = 4;
+ break;
+ case 0x00044007: /* Test pixel alpha */
+ resplen = 4;
+ break;
+ case 0x00048007: /* Set alpha */
+ alpha = ldl_le_phys(&s->dma_as, value + 12);
+ newalpha = α
+ resplen = 4;
+ break;
+ case 0x00040008: /* Get pitch */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch);
+ resplen = 4;
+ break;
+ case 0x00040009: /* Get virtual offset */
+ stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset);
+ stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset);
+ resplen = 8;
+ break;
+ case 0x00044009: /* Test virtual offset */
+ resplen = 8;
+ break;
+ case 0x00048009: /* Set virtual offset */
+ xoffset = ldl_le_phys(&s->dma_as, value + 12);
+ newxoffset = &xoffset;
+ yoffset = ldl_le_phys(&s->dma_as, value + 16);
+ newyoffset = &yoffset;
+ resplen = 8;
+ break;
+ case 0x0004000a: /* Get/Test/Set overscan */
+ case 0x0004400a:
+ case 0x0004800a:
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ stl_le_phys(&s->dma_as, value + 16, 0);
+ stl_le_phys(&s->dma_as, value + 20, 0);
+ stl_le_phys(&s->dma_as, value + 24, 0);
+ resplen = 16;
+ break;
+ case 0x0004800b: /* Set palette */
+ offset = ldl_le_phys(&s->dma_as, value + 12);
+ length = ldl_le_phys(&s->dma_as, value + 16);
+ n = 0;
+ while (n < length - offset) {
+ color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
+ stl_le_phys(&s->dma_as,
+ s->fbdev->vcram_base + ((offset + n) << 2), color);
+ n++;
+ }
+ stl_le_phys(&s->dma_as, value + 12, 0);
+ resplen = 4;
+ break;
case 0x00060001: /* Get DMA channels */
/* channels 2-5 */
@@ -147,6 +267,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
value += bufsize + 12;
}
+ /* Reconfigure framebuffer if required */
+ if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo
+ || newalpha) {
+ bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset,
+ newyoffset, newbpp, newpixo, newalpha);
+ }
+
/* Buffer response code */
stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
}
@@ -241,6 +368,15 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp)
Object *obj;
Error *err = NULL;
+ obj = object_property_get_link(OBJECT(dev), "fb", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required fb link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->fbdev = BCM2835_FB(obj);
+
obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
if (obj == NULL) {
error_setg(errp, "%s: required dma-mr link not found: %s",
@@ -259,7 +395,6 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp)
static Property bcm2835_property_props[] = {
DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
- DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h
index df889ea..edcab60 100644
--- a/include/hw/misc/bcm2835_property.h
+++ b/include/hw/misc/bcm2835_property.h
@@ -9,6 +9,7 @@
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "net/net.h"
+#include "hw/display/bcm2835_fb.h"
#define TYPE_BCM2835_PROPERTY "bcm2835-property"
#define BCM2835_PROPERTY(obj) \
@@ -18,13 +19,15 @@ typedef struct {
/*< private >*/
SysBusDevice busdev;
/*< public >*/
+
MemoryRegion *dma_mr;
AddressSpace dma_as;
MemoryRegion iomem;
qemu_irq mbox_irq;
+ BCM2835FBState *fbdev;
+
MACAddr macaddr;
uint32_t board_rev;
- uint32_t ram_size;
uint32_t addr;
bool pending;
} BCM2835PropertyState;
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 20/21] bcm2835_dma: add emulation of Raspberry Pi DMA controller
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (18 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 19/21] bcm2835_property: implement framebuffer control/configuration properties Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:18 ` [Qemu-devel] [PULL 21/21] sd: Fix "info qtree" on boards with SD cards Peter Maydell
` (2 subsequent siblings)
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
From: Grégory ESTRADE <gregory.estrade@gmail.com>
At present, all DMA transfers complete inline (so a looping descriptor
queue will lock up the device). We also do not model pause/abort,
arbitrarion/priority, or debug features.
Signed-off-by: Grégory ESTRADE <gregory.estrade@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Message-id: 1457467526-8840-6-git-send-email-Andrew.Baumann@microsoft.com
[AB: implement 2D mode, cleanup/refactoring for upstream submission]
Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/arm/bcm2835_peripherals.c | 26 +++
hw/dma/Makefile.objs | 1 +
hw/dma/bcm2835_dma.c | 408 +++++++++++++++++++++++++++++++++++
include/hw/arm/bcm2835_peripherals.h | 2 +
include/hw/dma/bcm2835_dma.h | 47 ++++
5 files changed, 484 insertions(+)
create mode 100644 hw/dma/bcm2835_dma.c
create mode 100644 include/hw/dma/bcm2835_dma.h
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 4d74a18..8099a8a 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -88,6 +88,14 @@ static void bcm2835_peripherals_init(Object *obj)
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default());
+
+ /* DMA Channels */
+ object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA);
+ object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL);
+ qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default());
+
+ object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
+ OBJECT(&s->gpu_bus_mr), &error_abort);
}
static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
@@ -258,6 +266,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
return;
}
+ /* DMA Channels */
+ object_property_set_bool(OBJECT(&s->dma), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(&s->peri_mr, DMA_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0));
+ memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
+
+ for (n = 0; n <= 12; n++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
+ qdev_get_gpio_in_named(DEVICE(&s->ic),
+ BCM2835_IC_GPU_IRQ,
+ INTERRUPT_DMA0 + n));
+ }
}
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
index 0e65ed0..a1abbcf 100644
--- a/hw/dma/Makefile.objs
+++ b/hw/dma/Makefile.objs
@@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
+obj-$(CONFIG_RASPI) += bcm2835_dma.o
diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c
new file mode 100644
index 0000000..c7ce4e4
--- /dev/null
+++ b/hw/dma/bcm2835_dma.c
@@ -0,0 +1,408 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/dma/bcm2835_dma.h"
+
+/* DMA CS Control and Status bits */
+#define BCM2708_DMA_ACTIVE (1 << 0)
+#define BCM2708_DMA_END (1 << 1) /* GE */
+#define BCM2708_DMA_INT (1 << 2)
+#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
+#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
+#define BCM2708_DMA_ERR (1 << 8)
+#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
+#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
+
+/* DMA control block "info" field bits */
+#define BCM2708_DMA_INT_EN (1 << 0)
+#define BCM2708_DMA_TDMODE (1 << 1)
+#define BCM2708_DMA_WAIT_RESP (1 << 3)
+#define BCM2708_DMA_D_INC (1 << 4)
+#define BCM2708_DMA_D_WIDTH (1 << 5)
+#define BCM2708_DMA_D_DREQ (1 << 6)
+#define BCM2708_DMA_D_IGNORE (1 << 7)
+#define BCM2708_DMA_S_INC (1 << 8)
+#define BCM2708_DMA_S_WIDTH (1 << 9)
+#define BCM2708_DMA_S_DREQ (1 << 10)
+#define BCM2708_DMA_S_IGNORE (1 << 11)
+
+/* Register offsets */
+#define BCM2708_DMA_CS 0x00 /* Control and Status */
+#define BCM2708_DMA_ADDR 0x04 /* Control block address */
+/* the current control block appears in the following registers - read only */
+#define BCM2708_DMA_INFO 0x08
+#define BCM2708_DMA_SOURCE_AD 0x0c
+#define BCM2708_DMA_DEST_AD 0x10
+#define BCM2708_DMA_TXFR_LEN 0x14
+#define BCM2708_DMA_STRIDE 0x18
+#define BCM2708_DMA_NEXTCB 0x1C
+#define BCM2708_DMA_DEBUG 0x20
+
+#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */
+#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */
+
+#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */
+
+static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
+{
+ BCM2835DMAChan *ch = &s->chan[c];
+ uint32_t data, xlen, ylen;
+ int16_t dst_stride, src_stride;
+
+ if (!(s->enable & (1 << c))) {
+ return;
+ }
+
+ while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
+ /* CB fetch */
+ ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
+ ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
+ ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
+ ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
+ ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
+ ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
+
+ if (ch->ti & BCM2708_DMA_TDMODE) {
+ /* 2D transfer mode */
+ ylen = (ch->txfr_len >> 16) & 0x3fff;
+ xlen = ch->txfr_len & 0xffff;
+ dst_stride = ch->stride >> 16;
+ src_stride = ch->stride & 0xffff;
+ } else {
+ ylen = 1;
+ xlen = ch->txfr_len;
+ dst_stride = 0;
+ src_stride = 0;
+ }
+
+ while (ylen != 0) {
+ /* Normal transfer mode */
+ while (xlen != 0) {
+ if (ch->ti & BCM2708_DMA_S_IGNORE) {
+ /* Ignore reads */
+ data = 0;
+ } else {
+ data = ldl_le_phys(&s->dma_as, ch->source_ad);
+ }
+ if (ch->ti & BCM2708_DMA_S_INC) {
+ ch->source_ad += 4;
+ }
+
+ if (ch->ti & BCM2708_DMA_D_IGNORE) {
+ /* Ignore writes */
+ } else {
+ stl_le_phys(&s->dma_as, ch->dest_ad, data);
+ }
+ if (ch->ti & BCM2708_DMA_D_INC) {
+ ch->dest_ad += 4;
+ }
+
+ /* update remaining transfer length */
+ xlen -= 4;
+ if (ch->ti & BCM2708_DMA_TDMODE) {
+ ch->txfr_len = (ylen << 16) | xlen;
+ } else {
+ ch->txfr_len = xlen;
+ }
+ }
+
+ if (--ylen != 0) {
+ ch->source_ad += src_stride;
+ ch->dest_ad += dst_stride;
+ }
+ }
+ ch->cs |= BCM2708_DMA_END;
+ if (ch->ti & BCM2708_DMA_INT_EN) {
+ ch->cs |= BCM2708_DMA_INT;
+ s->int_status |= (1 << c);
+ qemu_set_irq(ch->irq, 1);
+ }
+
+ /* Process next CB */
+ ch->conblk_ad = ch->nextconbk;
+ }
+
+ ch->cs &= ~BCM2708_DMA_ACTIVE;
+ ch->cs |= BCM2708_DMA_ISPAUSED;
+}
+
+static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
+{
+ ch->cs = 0;
+ ch->conblk_ad = 0;
+}
+
+static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
+ unsigned size, unsigned c)
+{
+ BCM2835DMAChan *ch;
+ uint32_t res = 0;
+
+ assert(size == 4);
+ assert(c < BCM2835_DMA_NCHANS);
+
+ ch = &s->chan[c];
+
+ switch (offset) {
+ case BCM2708_DMA_CS:
+ res = ch->cs;
+ break;
+ case BCM2708_DMA_ADDR:
+ res = ch->conblk_ad;
+ break;
+ case BCM2708_DMA_INFO:
+ res = ch->ti;
+ break;
+ case BCM2708_DMA_SOURCE_AD:
+ res = ch->source_ad;
+ break;
+ case BCM2708_DMA_DEST_AD:
+ res = ch->dest_ad;
+ break;
+ case BCM2708_DMA_TXFR_LEN:
+ res = ch->txfr_len;
+ break;
+ case BCM2708_DMA_STRIDE:
+ res = ch->stride;
+ break;
+ case BCM2708_DMA_NEXTCB:
+ res = ch->nextconbk;
+ break;
+ case BCM2708_DMA_DEBUG:
+ res = ch->debug;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ break;
+ }
+ return res;
+}
+
+static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
+ uint64_t value, unsigned size, unsigned c)
+{
+ BCM2835DMAChan *ch;
+ uint32_t oldcs;
+
+ assert(size == 4);
+ assert(c < BCM2835_DMA_NCHANS);
+
+ ch = &s->chan[c];
+
+ switch (offset) {
+ case BCM2708_DMA_CS:
+ oldcs = ch->cs;
+ if (value & BCM2708_DMA_RESET) {
+ bcm2835_dma_chan_reset(ch);
+ }
+ if (value & BCM2708_DMA_ABORT) {
+ /* abort is a no-op, since we always run to completion */
+ }
+ if (value & BCM2708_DMA_END) {
+ ch->cs &= ~BCM2708_DMA_END;
+ }
+ if (value & BCM2708_DMA_INT) {
+ ch->cs &= ~BCM2708_DMA_INT;
+ s->int_status &= ~(1 << c);
+ qemu_set_irq(ch->irq, 0);
+ }
+ ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
+ ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
+ if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
+ bcm2835_dma_update(s, c);
+ }
+ break;
+ case BCM2708_DMA_ADDR:
+ ch->conblk_ad = value;
+ break;
+ case BCM2708_DMA_DEBUG:
+ ch->debug = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ break;
+ }
+}
+
+static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
+{
+ BCM2835DMAState *s = opaque;
+
+ if (offset < 0xf00) {
+ return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
+ } else {
+ switch (offset) {
+ case BCM2708_DMA_INT_STATUS:
+ return s->int_status;
+ case BCM2708_DMA_ENABLE:
+ return s->enable;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ return 0;
+ }
+ }
+}
+
+static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
+{
+ return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
+}
+
+static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ BCM2835DMAState *s = opaque;
+
+ if (offset < 0xf00) {
+ bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
+ } else {
+ switch (offset) {
+ case BCM2708_DMA_INT_STATUS:
+ break;
+ case BCM2708_DMA_ENABLE:
+ s->enable = (value & 0xffff);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
+ __func__, offset);
+ }
+ }
+
+}
+
+static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
+}
+
+static const MemoryRegionOps bcm2835_dma0_ops = {
+ .read = bcm2835_dma0_read,
+ .write = bcm2835_dma0_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const MemoryRegionOps bcm2835_dma15_ops = {
+ .read = bcm2835_dma15_read,
+ .write = bcm2835_dma15_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_bcm2835_dma_chan = {
+ .name = TYPE_BCM2835_DMA "-chan",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cs, BCM2835DMAChan),
+ VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
+ VMSTATE_UINT32(ti, BCM2835DMAChan),
+ VMSTATE_UINT32(source_ad, BCM2835DMAChan),
+ VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
+ VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
+ VMSTATE_UINT32(stride, BCM2835DMAChan),
+ VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
+ VMSTATE_UINT32(debug, BCM2835DMAChan),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_bcm2835_dma = {
+ .name = TYPE_BCM2835_DMA,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
+ vmstate_bcm2835_dma_chan, BCM2835DMAChan),
+ VMSTATE_UINT32(int_status, BCM2835DMAState),
+ VMSTATE_UINT32(enable, BCM2835DMAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2835_dma_init(Object *obj)
+{
+ BCM2835DMAState *s = BCM2835_DMA(obj);
+ int n;
+
+ /* DMA channels 0-14 occupy a contiguous block of IO memory, along
+ * with the global enable and interrupt status bits. Channel 15
+ * has the same register map, but is mapped at a discontiguous
+ * address in a separate IO block.
+ */
+ memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
+ TYPE_BCM2835_DMA, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
+
+ memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
+ TYPE_BCM2835_DMA "-chan15", 0x100);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
+
+ for (n = 0; n < 16; n++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
+ }
+}
+
+static void bcm2835_dma_reset(DeviceState *dev)
+{
+ BCM2835DMAState *s = BCM2835_DMA(dev);
+ int n;
+
+ s->enable = 0xffff;
+ s->int_status = 0;
+ for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
+ bcm2835_dma_chan_reset(&s->chan[n]);
+ }
+}
+
+static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
+{
+ BCM2835DMAState *s = BCM2835_DMA(dev);
+ Error *err = NULL;
+ Object *obj;
+
+ obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
+ if (obj == NULL) {
+ error_setg(errp, "%s: required dma-mr link not found: %s",
+ __func__, error_get_pretty(err));
+ return;
+ }
+
+ s->dma_mr = MEMORY_REGION(obj);
+ address_space_init(&s->dma_as, s->dma_mr, NULL);
+
+ bcm2835_dma_reset(dev);
+}
+
+static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = bcm2835_dma_realize;
+ dc->reset = bcm2835_dma_reset;
+ dc->vmsd = &vmstate_bcm2835_dma;
+}
+
+static TypeInfo bcm2835_dma_info = {
+ .name = TYPE_BCM2835_DMA,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2835DMAState),
+ .class_init = bcm2835_dma_class_init,
+ .instance_init = bcm2835_dma_init,
+};
+
+static void bcm2835_dma_register_types(void)
+{
+ type_register_static(&bcm2835_dma_info);
+}
+
+type_init(bcm2835_dma_register_types)
diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h
index e19d360..e12ae37 100644
--- a/include/hw/arm/bcm2835_peripherals.h
+++ b/include/hw/arm/bcm2835_peripherals.h
@@ -16,6 +16,7 @@
#include "hw/sysbus.h"
#include "hw/char/bcm2835_aux.h"
#include "hw/display/bcm2835_fb.h"
+#include "hw/dma/bcm2835_dma.h"
#include "hw/intc/bcm2835_ic.h"
#include "hw/misc/bcm2835_property.h"
#include "hw/misc/bcm2835_mbox.h"
@@ -37,6 +38,7 @@ typedef struct BCM2835PeripheralState {
SysBusDevice *uart0;
BCM2835AuxState aux;
BCM2835FBState fb;
+ BCM2835DMAState dma;
BCM2835ICState ic;
BCM2835PropertyState property;
BCM2835MboxState mboxes;
diff --git a/include/hw/dma/bcm2835_dma.h b/include/hw/dma/bcm2835_dma.h
new file mode 100644
index 0000000..75312e2
--- /dev/null
+++ b/include/hw/dma/bcm2835_dma.h
@@ -0,0 +1,47 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#ifndef BCM2835_DMA_H
+#define BCM2835_DMA_H
+
+#include "qemu-common.h"
+#include "exec/address-spaces.h"
+#include "hw/sysbus.h"
+
+typedef struct {
+ uint32_t cs;
+ uint32_t conblk_ad;
+ uint32_t ti;
+ uint32_t source_ad;
+ uint32_t dest_ad;
+ uint32_t txfr_len;
+ uint32_t stride;
+ uint32_t nextconbk;
+ uint32_t debug;
+
+ qemu_irq irq;
+} BCM2835DMAChan;
+
+#define TYPE_BCM2835_DMA "bcm2835-dma"
+#define BCM2835_DMA(obj) \
+ OBJECT_CHECK(BCM2835DMAState, (obj), TYPE_BCM2835_DMA)
+
+#define BCM2835_DMA_NCHANS 16
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice busdev;
+ /*< public >*/
+
+ MemoryRegion iomem0, iomem15;
+ MemoryRegion *dma_mr;
+ AddressSpace dma_as;
+
+ BCM2835DMAChan chan[BCM2835_DMA_NCHANS];
+ uint32_t int_status;
+ uint32_t enable;
+} BCM2835DMAState;
+
+#endif
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [Qemu-devel] [PULL 21/21] sd: Fix "info qtree" on boards with SD cards
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (19 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 20/21] bcm2835_dma: add emulation of Raspberry Pi DMA controller Peter Maydell
@ 2016-03-16 17:18 ` Peter Maydell
2016-03-16 17:42 ` [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
2016-03-16 18:19 ` Peter Maydell
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:18 UTC (permalink / raw)
To: qemu-devel
The SD card object is not a SysBusDevice, so don't create it with
qdev_create() if we're not assigning it to a specific bus; use
object_new() instead.
This was causing 'info qtree' to segfault on boards with SD cards,
because qdev_create(NULL, TYPE_FOO) puts the created object on the
system bus, and then we may try to run functions like sysbus_dev_print()
on it, which fail when casting the object to SysBusDevice.
(This is the same mistake that we made with the NAND device
and fixed in commit 6749695eaaf346c1.)
Reported-by: xiaoqiang.zhao <zxq_yx_007@163.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: xiaoqiang.zhao <zxq_yx_007@163.com>
Message-id: 1458061009-7733-1-git-send-email-peter.maydell@linaro.org
---
hw/sd/sd.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 00c320d..1568057 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -563,17 +563,19 @@ static const VMStateDescription sd_vmstate = {
/* Legacy initialization function for use by non-qdevified callers */
SDState *sd_init(BlockBackend *blk, bool is_spi)
{
+ Object *obj;
DeviceState *dev;
Error *err = NULL;
- dev = qdev_create(NULL, TYPE_SD_CARD);
+ obj = object_new(TYPE_SD_CARD);
+ dev = DEVICE(obj);
qdev_prop_set_drive(dev, "drive", blk, &err);
if (err) {
error_report("sd_init failed: %s", error_get_pretty(err));
return NULL;
}
qdev_prop_set_bit(dev, "spi", is_spi);
- object_property_set_bool(OBJECT(dev), true, "realized", &err);
+ object_property_set_bool(obj, true, "realized", &err);
if (err) {
error_report("sd_init failed: %s", error_get_pretty(err));
return NULL;
--
1.9.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [Qemu-devel] [PULL 00/21] target-arm queue
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (20 preceding siblings ...)
2016-03-16 17:18 ` [Qemu-devel] [PULL 21/21] sd: Fix "info qtree" on boards with SD cards Peter Maydell
@ 2016-03-16 17:42 ` Peter Maydell
2016-03-16 18:19 ` Peter Maydell
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 17:42 UTC (permalink / raw)
To: QEMU Developers
On 16 March 2016 at 17:18, Peter Maydell <peter.maydell@linaro.org> wrote:
> Here's the target-arm queue; I'm a bit hesitant about the late-landing
> various new board/SoC patches, but they won't affect anybody who isn't
> trying to use those boards, so I think it's OK.
>
> (There are a few other patches on list which I definitely want to
> get in before rc0 but they need a bit more review time I think.)
>
> thanks
> -- PMM
>
>
> The following changes since commit 0ebc03bc065329eaefb6493f5fa7df08df528f2a:
>
> util/base64.c: Clean includes (2016-03-16 12:48:11 +0000)
>
> are available in the git repository at:
>
> git://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20160316
>
> for you to fetch changes up to 10b27d1ab391dbf36f92e1a33179662082401d7a:
>
> sd: Fix "info qtree" on boards with SD cards (2016-03-16 17:12:46 +0000)
>
> ----------------------------------------------------------------
> target-arm queue:
> * loader: Fix incorrect parameter name in load_image_mr()
> * Implement MRS (banked) and MSR (banked) instructions
> * virt: Implement versioning for machine model
> * i.MX: some initial patches preparing for i.MX6 support
> * new ASPEED AST2400 SoC and palmetto-bmc machine
> * bcm2835: add some more raspi2 devices
> * sd: fix segfault running "info qtree"
Some versions of gcc appear to give false positive 'may be used
uninitialized' warnings about the msr/mrs code:
/home/petmay01/linaro/qemu-for-merges/target-arm/translate.c: In
function ‘gen_msr_banked
.isra.45’:
/home/petmay01/linaro/qemu-for-merges/target-arm/translate.c:4321:17:
error: ‘tgtmode’ ma
y be used uninitialized in this function [-Werror=maybe-uninitialized]
tcg_tgtmode = tcg_const_i32(tgtmode);
^
/home/petmay01/linaro/qemu-for-merges/target-arm/translate.c:4322:15:
error: ‘regno’ may be used uninitialized in this function
[-Werror=maybe-uninitialized]
tcg_regno = tcg_const_i32(regno);
^
/home/petmay01/linaro/qemu-for-merges/target-arm/translate.c: In
function ‘gen_mrs_banked.isra.48’:
/home/petmay01/linaro/qemu-for-merges/target-arm/translate.c:4343:17:
error: ‘tgtmode’ may be used uninitialized in this function
[-Werror=maybe-uninitialized]
tcg_tgtmode = tcg_const_i32(tgtmode);
^
/home/petmay01/linaro/qemu-for-merges/target-arm/translate.c:4344:15:
error: ‘regno’ may be used uninitialized in this function
[-Werror=maybe-uninitialized]
tcg_regno = tcg_const_i32(regno);
^
Fixup:
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -4308,7 +4308,7 @@ undef:
static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn)
{
TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
- int tgtmode, regno;
+ int tgtmode = 0, regno = 0;
if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) {
return;
@@ -4330,7 +4330,7 @@ static void gen_msr_banked(DisasContext *s, int
r, int sysm, int rn)
static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn)
{
TCGv_i32 tcg_reg, tcg_tgtmode, tcg_regno;
- int tgtmode, regno;
+ int tgtmode = 0, regno = 0;
if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) {
return;
which I'll squash into the appropriate patch and respin.
thanks
-- PMM
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [Qemu-devel] [PULL 00/21] target-arm queue
2016-03-16 17:18 [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
` (21 preceding siblings ...)
2016-03-16 17:42 ` [Qemu-devel] [PULL 00/21] target-arm queue Peter Maydell
@ 2016-03-16 18:19 ` Peter Maydell
22 siblings, 0 replies; 42+ messages in thread
From: Peter Maydell @ 2016-03-16 18:19 UTC (permalink / raw)
To: QEMU Developers
On 16 March 2016 at 17:18, Peter Maydell <peter.maydell@linaro.org> wrote:
> Here's the target-arm queue; I'm a bit hesitant about the late-landing
> various new board/SoC patches, but they won't affect anybody who isn't
> trying to use those boards, so I think it's OK.
>
> (There are a few other patches on list which I definitely want to
> get in before rc0 but they need a bit more review time I think.)
>
> thanks
> -- PMM
>
>
> The following changes since commit 0ebc03bc065329eaefb6493f5fa7df08df528f2a:
>
> util/base64.c: Clean includes (2016-03-16 12:48:11 +0000)
>
> are available in the git repository at:
>
> git://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20160316
>
> for you to fetch changes up to 10b27d1ab391dbf36f92e1a33179662082401d7a:
>
> sd: Fix "info qtree" on boards with SD cards (2016-03-16 17:12:46 +0000)
Respin with fix now applied to master.
-- PMM
^ permalink raw reply [flat|nested] 42+ messages in thread