target-arm queue:
* Fix validation of 32-bit address spaces for aa32 (fixes an assert introduced in ba97be9f4a4) * v8m: Ensure IDAU is respected if SAU is disabled * gdbstub: fix gdb_get_cpu(s, pid, tid) when pid and/or tid are 0 * exec.c: Use correct attrs in cpu_memory_rw_debug() * accel/tcg/user-exec: Don't parse aarch64 insns to test for read vs write * target/arm: Don't clear supported PMU events when initializing PMCEID1 * memory: add memory_region_flush_rom_device() * microbit: Add stub NRF51 TWI magnetometer/accelerometer detection * tests/microbit-test: extend testing of microbit devices * checkpatch: Don't emit spurious warnings about block comments * aspeed/smc: misc bug fixes * xlnx-zynqmp: Don't create rpu-cluster if there are no RPUs * xlnx-zynqmp: Realize cluster after putting RPUs in it * accel/tcg: Add cluster number to TCG TB hash so differently configured CPUs don't pick up cached TBs for the wrong kind of CPU -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAlxQQA4ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3rFzD/4mOedFLCEubFGFgpwEQf3P g/oEIOPm6xNFfk/cZPUwBqoANBIxb5sTZCJuNyEWEpziNLtxc+BRzq21UwazsaGV fxBJr3XHZxeX/XeQD8SioPeMeW9SGoOmj4Wigu3dMcQ8wqeOUb0/lGVdqCOJoPmV h7CCW0OufmX9PpLYR/IgFFfCeIGBxCqE+Kld0ZUlXs70ax8JxJK9JsbamJhEpDtC A888r2xbVlj9Pa9eu8MBKqaLF3KS8qWrgrBssPsX3I28p3MykjQAHgnVlNP1X0uo h03+9DiCbCPq/+hr763VaIrbugYeFL1zTo7B7C2cCgwPso2zsiExYR96/0hkZ/6b Yov759LeidcmxoelrJU5AUBgLonWAV4S0lbTN54pxPJcs4l9cvF65U8mgEs7ONAs mbCrLT5plzJrEK/Qj2pVJLY8OggBBg2/jNTW/CRda8Xdh9fNgKeTK2vLDsFygexk ARSOZ4yEzCP6HlSJ7z3s3IEEg1VUv41SY5mWhkhJU5s0WOe/UD5b3KLj7PMkD9bu +ITHKQ+/2OO46klhNWQ3Ipcjz/jHruAFCQQFtHeiK9pFQGgcAbEHzZrmVVoXmk0N cyNk9+e/hiRm/x6IWzJidV8x3iEEtRoNiukwd7jEgkUg99SiGevPrLYD6GVYnZsd R9PYmo6/X2Cjr/SFcOY9Fg== =SzmU -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190129' into staging target-arm queue: * Fix validation of 32-bit address spaces for aa32 (fixes an assert introduced in ba97be9f4a4) * v8m: Ensure IDAU is respected if SAU is disabled * gdbstub: fix gdb_get_cpu(s, pid, tid) when pid and/or tid are 0 * exec.c: Use correct attrs in cpu_memory_rw_debug() * accel/tcg/user-exec: Don't parse aarch64 insns to test for read vs write * target/arm: Don't clear supported PMU events when initializing PMCEID1 * memory: add memory_region_flush_rom_device() * microbit: Add stub NRF51 TWI magnetometer/accelerometer detection * tests/microbit-test: extend testing of microbit devices * checkpatch: Don't emit spurious warnings about block comments * aspeed/smc: misc bug fixes * xlnx-zynqmp: Don't create rpu-cluster if there are no RPUs * xlnx-zynqmp: Realize cluster after putting RPUs in it * accel/tcg: Add cluster number to TCG TB hash so differently configured CPUs don't pick up cached TBs for the wrong kind of CPU # gpg: Signature made Tue 29 Jan 2019 11:59:10 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20190129: (23 commits) gdbstub: Simplify gdb_get_cpu_pid() to use cpu->cluster_index accel/tcg: Add cluster number to TCG TB hash qom/cpu: Add cluster_index to CPUState hw/arm/xlnx-zynqmp: Realize cluster after putting RPUs in it aspeed/smc: snoop SPI transfers to fake dummy cycles aspeed/smc: Add dummy data register aspeed/smc: define registers for all possible CS aspeed/smc: fix default read value xlnx-zynqmp: Don't create rpu-cluster if there are no RPUs checkpatch: Don't emit spurious warnings about block comments tests/microbit-test: Check nRF51 UART functionality tests/microbit-test: Make test independent of global_qtest tests/libqtest: Introduce qtest_init_with_serial() memory: add memory_region_flush_rom_device() target/arm: Don't clear supported PMU events when initializing PMCEID1 MAINTAINERS: update microbit ARM board files accel/tcg/user-exec: Don't parse aarch64 insns to test for read vs write exec.c: Use correct attrs in cpu_memory_rw_debug() tests/microbit-test: add TWI stub device test arm: Stub out NRF51 TWI magnetometer/accelerometer detection ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
b4fbe1f65a
@ -829,9 +829,11 @@ M: Joel Stanley <joel@jms.id.au>
|
|||||||
M: Peter Maydell <peter.maydell@linaro.org>
|
M: Peter Maydell <peter.maydell@linaro.org>
|
||||||
L: qemu-arm@nongnu.org
|
L: qemu-arm@nongnu.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: hw/arm/nrf51_soc.c
|
F: hw/*/nrf51*.c
|
||||||
F: hw/arm/microbit.c
|
F: hw/*/microbit*.c
|
||||||
F: include/hw/arm/nrf51_soc.h
|
F: include/hw/*/nrf51*.h
|
||||||
|
F: include/hw/*/microbit*.h
|
||||||
|
F: tests/microbit-test.c
|
||||||
|
|
||||||
CRIS Machines
|
CRIS Machines
|
||||||
-------------
|
-------------
|
||||||
|
@ -325,6 +325,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
|||||||
struct tb_desc desc;
|
struct tb_desc desc;
|
||||||
uint32_t h;
|
uint32_t h;
|
||||||
|
|
||||||
|
cf_mask &= ~CF_CLUSTER_MASK;
|
||||||
|
cf_mask |= cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||||
|
|
||||||
desc.env = (CPUArchState *)cpu->env_ptr;
|
desc.env = (CPUArchState *)cpu->env_ptr;
|
||||||
desc.cs_base = cs_base;
|
desc.cs_base = cs_base;
|
||||||
desc.flags = flags;
|
desc.flags = flags;
|
||||||
|
@ -1688,6 +1688,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||||||
cflags |= CF_NOCACHE | 1;
|
cflags |= CF_NOCACHE | 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cflags &= ~CF_CLUSTER_MASK;
|
||||||
|
cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||||
|
|
||||||
buffer_overflow:
|
buffer_overflow:
|
||||||
tb = tb_alloc(pc);
|
tb = tb_alloc(pc);
|
||||||
if (unlikely(!tb)) {
|
if (unlikely(!tb)) {
|
||||||
|
@ -479,15 +479,53 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
|||||||
|
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
|
|
||||||
|
#ifndef ESR_MAGIC
|
||||||
|
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
|
||||||
|
#define ESR_MAGIC 0x45535201
|
||||||
|
struct esr_context {
|
||||||
|
struct _aarch64_ctx head;
|
||||||
|
uint64_t esr;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
|
||||||
|
{
|
||||||
|
return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
|
||||||
|
{
|
||||||
|
return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
|
||||||
|
}
|
||||||
|
|
||||||
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
||||||
{
|
{
|
||||||
siginfo_t *info = pinfo;
|
siginfo_t *info = pinfo;
|
||||||
ucontext_t *uc = puc;
|
ucontext_t *uc = puc;
|
||||||
uintptr_t pc = uc->uc_mcontext.pc;
|
uintptr_t pc = uc->uc_mcontext.pc;
|
||||||
uint32_t insn = *(uint32_t *)pc;
|
|
||||||
bool is_write;
|
bool is_write;
|
||||||
|
struct _aarch64_ctx *hdr;
|
||||||
|
struct esr_context const *esrctx = NULL;
|
||||||
|
|
||||||
|
/* Find the esr_context, which has the WnR bit in it */
|
||||||
|
for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
|
||||||
|
if (hdr->magic == ESR_MAGIC) {
|
||||||
|
esrctx = (struct esr_context const *)hdr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esrctx) {
|
||||||
|
/* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
|
||||||
|
uint64_t esr = esrctx->esr;
|
||||||
|
is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Fall back to parsing instructions; will only be needed
|
||||||
|
* for really ancient (pre-3.16) kernels.
|
||||||
|
*/
|
||||||
|
uint32_t insn = *(uint32_t *)pc;
|
||||||
|
|
||||||
/* XXX: need kernel patch to get write flag faster. */
|
|
||||||
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
||||||
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
||||||
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
||||||
@ -495,12 +533,12 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
|
|||||||
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
||||||
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
||||||
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
||||||
/* Ingore bits 10, 11 & 21, controlling indexing. */
|
/* Ignore bits 10, 11 & 21, controlling indexing. */
|
||||||
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
||||||
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
||||||
/* Ignore bits 23 & 24, controlling indexing. */
|
/* Ignore bits 23 & 24, controlling indexing. */
|
||||||
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
||||||
|
}
|
||||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
exec.c
19
exec.c
@ -3162,6 +3162,19 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
|
|||||||
cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask);
|
cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* In principle this function would work on other memory region types too,
|
||||||
|
* but the ROM device use case is the only one where this operation is
|
||||||
|
* necessary. Other memory regions should use the
|
||||||
|
* address_space_read/write() APIs.
|
||||||
|
*/
|
||||||
|
assert(memory_region_is_romd(mr));
|
||||||
|
|
||||||
|
invalidate_and_set_dirty(mr, addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
|
static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
|
||||||
{
|
{
|
||||||
unsigned access_size_max = mr->ops->valid.max_access_size;
|
unsigned access_size_max = mr->ops->valid.max_access_size;
|
||||||
@ -3882,12 +3895,10 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
|||||||
phys_addr += (addr & ~TARGET_PAGE_MASK);
|
phys_addr += (addr & ~TARGET_PAGE_MASK);
|
||||||
if (is_write) {
|
if (is_write) {
|
||||||
address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr,
|
address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr,
|
||||||
MEMTXATTRS_UNSPECIFIED,
|
attrs, buf, l);
|
||||||
buf, l);
|
|
||||||
} else {
|
} else {
|
||||||
address_space_rw(cpu->cpu_ases[asidx].as, phys_addr,
|
address_space_rw(cpu->cpu_ases[asidx].as, phys_addr,
|
||||||
MEMTXATTRS_UNSPECIFIED,
|
attrs, buf, l, 0);
|
||||||
buf, l, 0);
|
|
||||||
}
|
}
|
||||||
len -= l;
|
len -= l;
|
||||||
buf += l;
|
buf += l;
|
||||||
|
118
gdbstub.c
118
gdbstub.c
@ -644,50 +644,12 @@ static int memtox(char *buf, const char *mem, int len)
|
|||||||
|
|
||||||
static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu)
|
static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
gchar *path, *name = NULL;
|
|
||||||
Object *obj;
|
|
||||||
CPUClusterState *cluster;
|
|
||||||
uint32_t ret;
|
|
||||||
|
|
||||||
path = object_get_canonical_path(OBJECT(cpu));
|
|
||||||
|
|
||||||
if (path == NULL) {
|
|
||||||
/* Return the default process' PID */
|
|
||||||
ret = s->processes[s->process_num - 1].pid;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = object_get_canonical_path_component(OBJECT(cpu));
|
|
||||||
assert(name != NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Retrieve the CPU parent path by removing the last '/' and the CPU name
|
|
||||||
* from the CPU canonical path.
|
|
||||||
*/
|
|
||||||
path[strlen(path) - strlen(name) - 1] = '\0';
|
|
||||||
|
|
||||||
obj = object_resolve_path_type(path, TYPE_CPU_CLUSTER, NULL);
|
|
||||||
|
|
||||||
if (obj == NULL) {
|
|
||||||
/* Return the default process' PID */
|
|
||||||
ret = s->processes[s->process_num - 1].pid;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
cluster = CPU_CLUSTER(obj);
|
|
||||||
ret = cluster->cluster_id + 1;
|
|
||||||
|
|
||||||
out:
|
|
||||||
g_free(name);
|
|
||||||
g_free(path);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
#else
|
|
||||||
/* TODO: In user mode, we should use the task state PID */
|
/* TODO: In user mode, we should use the task state PID */
|
||||||
|
if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
|
||||||
|
/* Return the default process' PID */
|
||||||
return s->processes[s->process_num - 1].pid;
|
return s->processes[s->process_num - 1].pid;
|
||||||
#endif
|
}
|
||||||
|
return cpu->cluster_index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid)
|
static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid)
|
||||||
@ -756,35 +718,6 @@ static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu)
|
|||||||
return cpu;
|
return cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid)
|
|
||||||
{
|
|
||||||
GDBProcess *process;
|
|
||||||
CPUState *cpu;
|
|
||||||
|
|
||||||
if (!tid) {
|
|
||||||
/* 0 means any thread, we take the first one */
|
|
||||||
tid = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu = find_cpu(tid);
|
|
||||||
|
|
||||||
if (cpu == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
process = gdb_get_cpu_process(s, cpu);
|
|
||||||
|
|
||||||
if (process->pid != pid) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process->attached) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the cpu following @cpu, while ignoring unattached processes. */
|
/* Return the cpu following @cpu, while ignoring unattached processes. */
|
||||||
static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu)
|
static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu)
|
||||||
{
|
{
|
||||||
@ -814,6 +747,49 @@ static CPUState *gdb_first_attached_cpu(const GDBState *s)
|
|||||||
return cpu;
|
return cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid)
|
||||||
|
{
|
||||||
|
GDBProcess *process;
|
||||||
|
CPUState *cpu;
|
||||||
|
|
||||||
|
if (!pid && !tid) {
|
||||||
|
/* 0 means any process/thread, we take the first attached one */
|
||||||
|
return gdb_first_attached_cpu(s);
|
||||||
|
} else if (pid && !tid) {
|
||||||
|
/* any thread in a specific process */
|
||||||
|
process = gdb_get_process(s, pid);
|
||||||
|
|
||||||
|
if (process == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process->attached) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_first_cpu_in_process(s, process);
|
||||||
|
} else {
|
||||||
|
/* a specific thread */
|
||||||
|
cpu = find_cpu(tid);
|
||||||
|
|
||||||
|
if (cpu == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
process = gdb_get_cpu_process(s, cpu);
|
||||||
|
|
||||||
|
if (pid && process->pid != pid) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process->attached) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char *get_feature_xml(const GDBState *s, const char *p,
|
static const char *get_feature_xml(const GDBState *s, const char *p,
|
||||||
const char **newp, GDBProcess *process)
|
const char **newp, GDBProcess *process)
|
||||||
{
|
{
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
|
|
||||||
#include "hw/arm/nrf51_soc.h"
|
#include "hw/arm/nrf51_soc.h"
|
||||||
|
#include "hw/i2c/microbit_i2c.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MachineState parent;
|
MachineState parent;
|
||||||
|
|
||||||
NRF51State nrf51;
|
NRF51State nrf51;
|
||||||
|
MicrobitI2CState i2c;
|
||||||
} MicrobitMachineState;
|
} MicrobitMachineState;
|
||||||
|
|
||||||
#define TYPE_MICROBIT_MACHINE MACHINE_TYPE_NAME("microbit")
|
#define TYPE_MICROBIT_MACHINE MACHINE_TYPE_NAME("microbit")
|
||||||
@ -32,7 +34,9 @@ static void microbit_init(MachineState *machine)
|
|||||||
{
|
{
|
||||||
MicrobitMachineState *s = MICROBIT_MACHINE(machine);
|
MicrobitMachineState *s = MICROBIT_MACHINE(machine);
|
||||||
MemoryRegion *system_memory = get_system_memory();
|
MemoryRegion *system_memory = get_system_memory();
|
||||||
|
MemoryRegion *mr;
|
||||||
Object *soc = OBJECT(&s->nrf51);
|
Object *soc = OBJECT(&s->nrf51);
|
||||||
|
Object *i2c = OBJECT(&s->i2c);
|
||||||
|
|
||||||
sysbus_init_child_obj(OBJECT(machine), "nrf51", soc, sizeof(s->nrf51),
|
sysbus_init_child_obj(OBJECT(machine), "nrf51", soc, sizeof(s->nrf51),
|
||||||
TYPE_NRF51_SOC);
|
TYPE_NRF51_SOC);
|
||||||
@ -41,6 +45,18 @@ static void microbit_init(MachineState *machine)
|
|||||||
&error_fatal);
|
&error_fatal);
|
||||||
object_property_set_bool(soc, true, "realized", &error_fatal);
|
object_property_set_bool(soc, true, "realized", &error_fatal);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Overlap the TWI stub device into the SoC. This is a microbit-specific
|
||||||
|
* hack until we implement the nRF51 TWI controller properly and the
|
||||||
|
* magnetometer/accelerometer devices.
|
||||||
|
*/
|
||||||
|
sysbus_init_child_obj(OBJECT(machine), "microbit.twi", i2c,
|
||||||
|
sizeof(s->i2c), TYPE_MICROBIT_I2C);
|
||||||
|
object_property_set_bool(i2c, true, "realized", &error_fatal);
|
||||||
|
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(i2c), 0);
|
||||||
|
memory_region_add_subregion_overlap(&s->nrf51.container, NRF51_TWI_BASE,
|
||||||
|
mr, -1);
|
||||||
|
|
||||||
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
|
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
|
||||||
NRF51_SOC(soc)->flash_size);
|
NRF51_SOC(soc)->flash_size);
|
||||||
}
|
}
|
||||||
|
@ -178,13 +178,16 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
|
|||||||
int i;
|
int i;
|
||||||
int num_rpus = MIN(smp_cpus - XLNX_ZYNQMP_NUM_APU_CPUS, XLNX_ZYNQMP_NUM_RPU_CPUS);
|
int num_rpus = MIN(smp_cpus - XLNX_ZYNQMP_NUM_APU_CPUS, XLNX_ZYNQMP_NUM_RPU_CPUS);
|
||||||
|
|
||||||
|
if (num_rpus <= 0) {
|
||||||
|
/* Don't create rpu-cluster object if there's nothing to put in it */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
object_initialize_child(OBJECT(s), "rpu-cluster", &s->rpu_cluster,
|
object_initialize_child(OBJECT(s), "rpu-cluster", &s->rpu_cluster,
|
||||||
sizeof(s->rpu_cluster), TYPE_CPU_CLUSTER,
|
sizeof(s->rpu_cluster), TYPE_CPU_CLUSTER,
|
||||||
&error_abort, NULL);
|
&error_abort, NULL);
|
||||||
qdev_prop_set_uint32(DEVICE(&s->rpu_cluster), "cluster-id", 1);
|
qdev_prop_set_uint32(DEVICE(&s->rpu_cluster), "cluster-id", 1);
|
||||||
|
|
||||||
qdev_init_nofail(DEVICE(&s->rpu_cluster));
|
|
||||||
|
|
||||||
for (i = 0; i < num_rpus; i++) {
|
for (i = 0; i < num_rpus; i++) {
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
@ -212,6 +215,8 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qdev_init_nofail(DEVICE(&s->rpu_cluster));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xlnx_zynqmp_init(Object *obj)
|
static void xlnx_zynqmp_init(Object *obj)
|
||||||
|
@ -20,19 +20,65 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "hw/cpu/cluster.h"
|
#include "hw/cpu/cluster.h"
|
||||||
|
#include "qom/cpu.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
|
||||||
static Property cpu_cluster_properties[] = {
|
static Property cpu_cluster_properties[] = {
|
||||||
DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0),
|
DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0),
|
||||||
DEFINE_PROP_END_OF_LIST()
|
DEFINE_PROP_END_OF_LIST()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct CallbackData {
|
||||||
|
CPUClusterState *cluster;
|
||||||
|
int cpu_count;
|
||||||
|
} CallbackData;
|
||||||
|
|
||||||
|
static int add_cpu_to_cluster(Object *obj, void *opaque)
|
||||||
|
{
|
||||||
|
CallbackData *cbdata = opaque;
|
||||||
|
CPUState *cpu = (CPUState *)object_dynamic_cast(obj, TYPE_CPU);
|
||||||
|
|
||||||
|
if (cpu) {
|
||||||
|
cpu->cluster_index = cbdata->cluster->cluster_id;
|
||||||
|
cbdata->cpu_count++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cpu_cluster_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
/* Iterate through all our CPU children and set their cluster_index */
|
||||||
|
CPUClusterState *cluster = CPU_CLUSTER(dev);
|
||||||
|
Object *cluster_obj = OBJECT(dev);
|
||||||
|
CallbackData cbdata = {
|
||||||
|
.cluster = cluster,
|
||||||
|
.cpu_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cluster->cluster_id >= MAX_CLUSTERS) {
|
||||||
|
error_setg(errp, "cluster-id must be less than %d", MAX_CLUSTERS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object_child_foreach_recursive(cluster_obj, add_cpu_to_cluster, &cbdata);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A cluster with no CPUs is a bug in the board/SoC code that created it;
|
||||||
|
* if you hit this during development of new code, check that you have
|
||||||
|
* created the CPUs and parented them into the cluster object before
|
||||||
|
* realizing the cluster object.
|
||||||
|
*/
|
||||||
|
assert(cbdata.cpu_count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void cpu_cluster_class_init(ObjectClass *klass, void *data)
|
static void cpu_cluster_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
dc->props = cpu_cluster_properties;
|
dc->props = cpu_cluster_properties;
|
||||||
|
dc->realize = cpu_cluster_realize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo cpu_cluster_type_info = {
|
static const TypeInfo cpu_cluster_type_info = {
|
||||||
|
@ -7,5 +7,6 @@ common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o
|
|||||||
common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o
|
common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o
|
||||||
common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
|
common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
|
||||||
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
|
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
|
||||||
|
common-obj-$(CONFIG_NRF51_SOC) += microbit_i2c.o
|
||||||
obj-$(CONFIG_OMAP) += omap_i2c.o
|
obj-$(CONFIG_OMAP) += omap_i2c.o
|
||||||
obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o
|
obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o
|
||||||
|
127
hw/i2c/microbit_i2c.c
Normal file
127
hw/i2c/microbit_i2c.c
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Microbit stub for Nordic Semiconductor nRF51 SoC Two-Wire Interface
|
||||||
|
* http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf
|
||||||
|
*
|
||||||
|
* This is a microbit-specific stub for the TWI controller on the nRF51 SoC.
|
||||||
|
* We don't emulate I2C devices but the firmware probes the
|
||||||
|
* accelerometer/magnetometer on startup and panics if they are not found.
|
||||||
|
* Therefore we stub out the probing.
|
||||||
|
*
|
||||||
|
* In the future this file could evolve into a full nRF51 TWI controller
|
||||||
|
* device.
|
||||||
|
*
|
||||||
|
* Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
|
||||||
|
* Copyright 2019 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* 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 "qemu/log.h"
|
||||||
|
#include "hw/i2c/microbit_i2c.h"
|
||||||
|
|
||||||
|
static const uint32_t twi_read_sequence[] = {0x5A, 0x5A, 0x40};
|
||||||
|
|
||||||
|
static uint64_t microbit_i2c_read(void *opaque, hwaddr addr, unsigned int size)
|
||||||
|
{
|
||||||
|
MicrobitI2CState *s = opaque;
|
||||||
|
uint64_t data = 0x00;
|
||||||
|
|
||||||
|
switch (addr) {
|
||||||
|
case NRF51_TWI_EVENT_STOPPED:
|
||||||
|
data = 0x01;
|
||||||
|
break;
|
||||||
|
case NRF51_TWI_EVENT_RXDREADY:
|
||||||
|
data = 0x01;
|
||||||
|
break;
|
||||||
|
case NRF51_TWI_EVENT_TXDSENT:
|
||||||
|
data = 0x01;
|
||||||
|
break;
|
||||||
|
case NRF51_TWI_REG_RXD:
|
||||||
|
data = twi_read_sequence[s->read_idx];
|
||||||
|
if (s->read_idx < G_N_ELEMENTS(twi_read_sequence)) {
|
||||||
|
s->read_idx++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data = s->regs[addr / sizeof(s->regs[0])];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u] = %" PRIx32 "\n",
|
||||||
|
__func__, addr, size, (uint32_t)data);
|
||||||
|
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void microbit_i2c_write(void *opaque, hwaddr addr, uint64_t data,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
MicrobitI2CState *s = opaque;
|
||||||
|
|
||||||
|
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
|
||||||
|
__func__, addr, data, size);
|
||||||
|
s->regs[addr / sizeof(s->regs[0])] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps microbit_i2c_ops = {
|
||||||
|
.read = microbit_i2c_read,
|
||||||
|
.write = microbit_i2c_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.impl.min_access_size = 4,
|
||||||
|
.impl.max_access_size = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription microbit_i2c_vmstate = {
|
||||||
|
.name = TYPE_MICROBIT_I2C,
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32_ARRAY(regs, MicrobitI2CState, MICROBIT_I2C_NREGS),
|
||||||
|
VMSTATE_UINT32(read_idx, MicrobitI2CState),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void microbit_i2c_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
MicrobitI2CState *s = MICROBIT_I2C(dev);
|
||||||
|
|
||||||
|
memset(s->regs, 0, sizeof(s->regs));
|
||||||
|
s->read_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void microbit_i2c_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
|
MicrobitI2CState *s = MICROBIT_I2C(dev);
|
||||||
|
|
||||||
|
memory_region_init_io(&s->iomem, OBJECT(s), µbit_i2c_ops, s,
|
||||||
|
"microbit.twi", NRF51_TWI_SIZE);
|
||||||
|
sysbus_init_mmio(sbd, &s->iomem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void microbit_i2c_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->vmsd = µbit_i2c_vmstate;
|
||||||
|
dc->reset = microbit_i2c_reset;
|
||||||
|
dc->realize = microbit_i2c_realize;
|
||||||
|
dc->desc = "Microbit I2C controller";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo microbit_i2c_info = {
|
||||||
|
.name = TYPE_MICROBIT_I2C,
|
||||||
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
|
.instance_size = sizeof(MicrobitI2CState),
|
||||||
|
.class_init = microbit_i2c_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void microbit_i2c_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(µbit_i2c_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(microbit_i2c_register_types)
|
@ -98,8 +98,8 @@
|
|||||||
/* Misc Control Register #1 */
|
/* Misc Control Register #1 */
|
||||||
#define R_MISC_CTRL1 (0x50 / 4)
|
#define R_MISC_CTRL1 (0x50 / 4)
|
||||||
|
|
||||||
/* Misc Control Register #2 */
|
/* SPI dummy cycle data */
|
||||||
#define R_MISC_CTRL2 (0x54 / 4)
|
#define R_DUMMY_DATA (0x54 / 4)
|
||||||
|
|
||||||
/* DMA Control/Status Register */
|
/* DMA Control/Status Register */
|
||||||
#define R_DMA_CTRL (0x80 / 4)
|
#define R_DMA_CTRL (0x80 / 4)
|
||||||
@ -145,6 +145,9 @@
|
|||||||
/* Flash opcodes. */
|
/* Flash opcodes. */
|
||||||
#define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */
|
#define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */
|
||||||
|
|
||||||
|
#define SNOOP_OFF 0xFF
|
||||||
|
#define SNOOP_START 0x0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default segments mapping addresses and size for each slave per
|
* Default segments mapping addresses and size for each slave per
|
||||||
* controller. These can be changed when board is initialized with the
|
* controller. These can be changed when board is initialized with the
|
||||||
@ -529,7 +532,7 @@ static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr)
|
|||||||
*/
|
*/
|
||||||
if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) {
|
if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) {
|
||||||
for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) {
|
for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) {
|
||||||
ssi_transfer(fl->controller->spi, 0xFF);
|
ssi_transfer(fl->controller->spi, s->regs[R_DUMMY_DATA] & 0xff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -566,6 +569,101 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO (clg@kaod.org): stolen from xilinx_spips.c. Should move to a
|
||||||
|
* common include header.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
READ = 0x3, READ_4 = 0x13,
|
||||||
|
FAST_READ = 0xb, FAST_READ_4 = 0x0c,
|
||||||
|
DOR = 0x3b, DOR_4 = 0x3c,
|
||||||
|
QOR = 0x6b, QOR_4 = 0x6c,
|
||||||
|
DIOR = 0xbb, DIOR_4 = 0xbc,
|
||||||
|
QIOR = 0xeb, QIOR_4 = 0xec,
|
||||||
|
|
||||||
|
PP = 0x2, PP_4 = 0x12,
|
||||||
|
DPP = 0xa2,
|
||||||
|
QPP = 0x32, QPP_4 = 0x34,
|
||||||
|
} FlashCMD;
|
||||||
|
|
||||||
|
static int aspeed_smc_num_dummies(uint8_t command)
|
||||||
|
{
|
||||||
|
switch (command) { /* check for dummies */
|
||||||
|
case READ: /* no dummy bytes/cycles */
|
||||||
|
case PP:
|
||||||
|
case DPP:
|
||||||
|
case QPP:
|
||||||
|
case READ_4:
|
||||||
|
case PP_4:
|
||||||
|
case QPP_4:
|
||||||
|
return 0;
|
||||||
|
case FAST_READ:
|
||||||
|
case DOR:
|
||||||
|
case QOR:
|
||||||
|
case DOR_4:
|
||||||
|
case QOR_4:
|
||||||
|
return 1;
|
||||||
|
case DIOR:
|
||||||
|
case FAST_READ_4:
|
||||||
|
case DIOR_4:
|
||||||
|
return 2;
|
||||||
|
case QIOR:
|
||||||
|
case QIOR_4:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
AspeedSMCState *s = fl->controller;
|
||||||
|
uint8_t addr_width = aspeed_smc_flash_is_4byte(fl) ? 4 : 3;
|
||||||
|
|
||||||
|
if (s->snoop_index == SNOOP_OFF) {
|
||||||
|
return false; /* Do nothing */
|
||||||
|
|
||||||
|
} else if (s->snoop_index == SNOOP_START) {
|
||||||
|
uint8_t cmd = data & 0xff;
|
||||||
|
int ndummies = aspeed_smc_num_dummies(cmd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No dummy cycles are expected with the current command. Turn
|
||||||
|
* off snooping and let the transfer proceed normally.
|
||||||
|
*/
|
||||||
|
if (ndummies <= 0) {
|
||||||
|
s->snoop_index = SNOOP_OFF;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->snoop_dummies = ndummies * 8;
|
||||||
|
|
||||||
|
} else if (s->snoop_index >= addr_width + 1) {
|
||||||
|
|
||||||
|
/* The SPI transfer has reached the dummy cycles sequence */
|
||||||
|
for (; s->snoop_dummies; s->snoop_dummies--) {
|
||||||
|
ssi_transfer(s->spi, s->regs[R_DUMMY_DATA] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no more dummy cycles are expected, turn off snooping */
|
||||||
|
if (!s->snoop_dummies) {
|
||||||
|
s->snoop_index = SNOOP_OFF;
|
||||||
|
} else {
|
||||||
|
s->snoop_index += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dummy cycles have been faked already. Ignore the current
|
||||||
|
* SPI transfer
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->snoop_index += size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
|
static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
@ -581,6 +679,10 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
|
|||||||
|
|
||||||
switch (aspeed_smc_flash_mode(fl)) {
|
switch (aspeed_smc_flash_mode(fl)) {
|
||||||
case CTRL_USERMODE:
|
case CTRL_USERMODE:
|
||||||
|
if (aspeed_smc_do_snoop(fl, data, size)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
|
ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
|
||||||
}
|
}
|
||||||
@ -613,7 +715,9 @@ static const MemoryRegionOps aspeed_smc_flash_ops = {
|
|||||||
|
|
||||||
static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl)
|
static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl)
|
||||||
{
|
{
|
||||||
const AspeedSMCState *s = fl->controller;
|
AspeedSMCState *s = fl->controller;
|
||||||
|
|
||||||
|
s->snoop_index = aspeed_smc_is_ce_stop_active(fl) ? SNOOP_OFF : SNOOP_START;
|
||||||
|
|
||||||
qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl));
|
qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl));
|
||||||
}
|
}
|
||||||
@ -652,6 +756,9 @@ static void aspeed_smc_reset(DeviceState *d)
|
|||||||
if (s->ctrl->segments == aspeed_segments_fmc) {
|
if (s->ctrl->segments == aspeed_segments_fmc) {
|
||||||
s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0);
|
s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->snoop_index = SNOOP_OFF;
|
||||||
|
s->snoop_dummies = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
|
static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
|
||||||
@ -664,13 +771,14 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
|
|||||||
addr == s->r_timings ||
|
addr == s->r_timings ||
|
||||||
addr == s->r_ce_ctrl ||
|
addr == s->r_ce_ctrl ||
|
||||||
addr == R_INTR_CTRL ||
|
addr == R_INTR_CTRL ||
|
||||||
|
addr == R_DUMMY_DATA ||
|
||||||
(addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) ||
|
(addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) ||
|
||||||
(addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs)) {
|
(addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->ctrl->max_slaves)) {
|
||||||
return s->regs[addr];
|
return s->regs[addr];
|
||||||
} else {
|
} else {
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
|
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
|
||||||
__func__, addr);
|
__func__, addr);
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,6 +805,8 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
|
|||||||
if (value != s->regs[R_SEG_ADDR0 + cs]) {
|
if (value != s->regs[R_SEG_ADDR0 + cs]) {
|
||||||
aspeed_smc_flash_set_segment(s, cs, value);
|
aspeed_smc_flash_set_segment(s, cs, value);
|
||||||
}
|
}
|
||||||
|
} else if (addr == R_DUMMY_DATA) {
|
||||||
|
s->regs[addr] = value & 0xff;
|
||||||
} else {
|
} else {
|
||||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
|
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
|
||||||
__func__, addr);
|
__func__, addr);
|
||||||
@ -790,10 +900,12 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
|
|||||||
|
|
||||||
static const VMStateDescription vmstate_aspeed_smc = {
|
static const VMStateDescription vmstate_aspeed_smc = {
|
||||||
.name = "aspeed.smc",
|
.name = "aspeed.smc",
|
||||||
.version_id = 1,
|
.version_id = 2,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 2,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
|
VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
|
||||||
|
VMSTATE_UINT8(snoop_index, AspeedSMCState),
|
||||||
|
VMSTATE_UINT8(snoop_dummies, AspeedSMCState),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -351,9 +351,11 @@ struct TranslationBlock {
|
|||||||
#define CF_USE_ICOUNT 0x00020000
|
#define CF_USE_ICOUNT 0x00020000
|
||||||
#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */
|
#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */
|
||||||
#define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */
|
#define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */
|
||||||
|
#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */
|
||||||
|
#define CF_CLUSTER_SHIFT 24
|
||||||
/* cflags' mask for hashing/comparison */
|
/* cflags' mask for hashing/comparison */
|
||||||
#define CF_HASH_MASK \
|
#define CF_HASH_MASK \
|
||||||
(CF_COUNT_MASK | CF_LAST_IO | CF_USE_ICOUNT | CF_PARALLEL)
|
(CF_COUNT_MASK | CF_LAST_IO | CF_USE_ICOUNT | CF_PARALLEL | CF_CLUSTER_MASK)
|
||||||
|
|
||||||
/* Per-vCPU dynamic tracing state used to generate this TB */
|
/* Per-vCPU dynamic tracing state used to generate this TB */
|
||||||
uint32_t trace_vcpu_dstate;
|
uint32_t trace_vcpu_dstate;
|
||||||
|
@ -1344,6 +1344,24 @@ bool memory_region_snapshot_get_dirty(MemoryRegion *mr,
|
|||||||
void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
|
void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
|
||||||
hwaddr size, unsigned client);
|
hwaddr size, unsigned client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memory_region_flush_rom_device: Mark a range of pages dirty and invalidate
|
||||||
|
* TBs (for self-modifying code).
|
||||||
|
*
|
||||||
|
* The MemoryRegionOps->write() callback of a ROM device must use this function
|
||||||
|
* to mark byte ranges that have been modified internally, such as by directly
|
||||||
|
* accessing the memory returned by memory_region_get_ram_ptr().
|
||||||
|
*
|
||||||
|
* This function marks the range dirty and invalidates TBs so that TCG can
|
||||||
|
* detect self-modifying code.
|
||||||
|
*
|
||||||
|
* @mr: the region being flushed.
|
||||||
|
* @addr: the start, relative to the start of the region, of the range being
|
||||||
|
* flushed.
|
||||||
|
* @size: the size, in bytes, of the range being flushed.
|
||||||
|
*/
|
||||||
|
void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* memory_region_set_readonly: Turn a memory region read-only (or read-write)
|
* memory_region_set_readonly: Turn a memory region read-only (or read-write)
|
||||||
*
|
*
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#define NRF51_IOMEM_SIZE 0x20000000
|
#define NRF51_IOMEM_SIZE 0x20000000
|
||||||
|
|
||||||
#define NRF51_UART_BASE 0x40002000
|
#define NRF51_UART_BASE 0x40002000
|
||||||
|
#define NRF51_TWI_BASE 0x40003000
|
||||||
|
#define NRF51_TWI_SIZE 0x00001000
|
||||||
#define NRF51_TIMER_BASE 0x40008000
|
#define NRF51_TIMER_BASE 0x40008000
|
||||||
#define NRF51_TIMER_SIZE 0x00001000
|
#define NRF51_TIMER_SIZE 0x00001000
|
||||||
#define NRF51_RNG_BASE 0x4000D000
|
#define NRF51_RNG_BASE 0x4000D000
|
||||||
|
@ -39,6 +39,7 @@ typedef struct NRF51State {
|
|||||||
MemoryRegion sram;
|
MemoryRegion sram;
|
||||||
MemoryRegion flash;
|
MemoryRegion flash;
|
||||||
MemoryRegion clock;
|
MemoryRegion clock;
|
||||||
|
MemoryRegion twi;
|
||||||
|
|
||||||
uint32_t sram_size;
|
uint32_t sram_size;
|
||||||
uint32_t flash_size;
|
uint32_t flash_size;
|
||||||
|
@ -34,12 +34,36 @@
|
|||||||
* Arm big.LITTLE system) they should be in different clusters. If the CPUs do
|
* Arm big.LITTLE system) they should be in different clusters. If the CPUs do
|
||||||
* not have the same view of memory (for example the main CPU and a management
|
* not have the same view of memory (for example the main CPU and a management
|
||||||
* controller processor) they should be in different clusters.
|
* controller processor) they should be in different clusters.
|
||||||
|
*
|
||||||
|
* A cluster is created by creating an object of TYPE_CPU_CLUSTER, and then
|
||||||
|
* adding the CPUs to it as QOM child objects (e.g. using the
|
||||||
|
* object_initialize_child() or object_property_add_child() functions).
|
||||||
|
* The CPUs may be either direct children of the cluster object, or indirect
|
||||||
|
* children (e.g. children of children of the cluster object).
|
||||||
|
*
|
||||||
|
* All CPUs must be added as children before the cluster is realized.
|
||||||
|
* (Regrettably QOM provides no way to prevent adding children to a realized
|
||||||
|
* object and no way for the parent to be notified when a new child is added
|
||||||
|
* to it, so this restriction is not checked for, but the system will not
|
||||||
|
* behave correctly if it is not adhered to. The cluster will assert that
|
||||||
|
* it contains at least one CPU, which should catch most inadvertent
|
||||||
|
* violations of this constraint.)
|
||||||
|
*
|
||||||
|
* A CPU which is not put into any cluster will be considered implicitly
|
||||||
|
* to be in a cluster with all the other "loose" CPUs, so all CPUs that are
|
||||||
|
* not assigned to clusters must be identical.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define TYPE_CPU_CLUSTER "cpu-cluster"
|
#define TYPE_CPU_CLUSTER "cpu-cluster"
|
||||||
#define CPU_CLUSTER(obj) \
|
#define CPU_CLUSTER(obj) \
|
||||||
OBJECT_CHECK(CPUClusterState, (obj), TYPE_CPU_CLUSTER)
|
OBJECT_CHECK(CPUClusterState, (obj), TYPE_CPU_CLUSTER)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This limit is imposed by TCG, which puts the cluster ID into an
|
||||||
|
* 8 bit field (and uses all-1s for the default "not in any cluster").
|
||||||
|
*/
|
||||||
|
#define MAX_CLUSTERS 255
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CPUClusterState:
|
* CPUClusterState:
|
||||||
* @cluster_id: The cluster ID. This value is for internal use only and should
|
* @cluster_id: The cluster ID. This value is for internal use only and should
|
||||||
|
42
include/hw/i2c/microbit_i2c.h
Normal file
42
include/hw/i2c/microbit_i2c.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Microbit stub for Nordic Semiconductor nRF51 SoC Two-Wire Interface
|
||||||
|
* http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf
|
||||||
|
*
|
||||||
|
* Copyright 2019 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This code is licensed under the GPL version 2 or later. See
|
||||||
|
* the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MICROBIT_I2C_H
|
||||||
|
#define MICROBIT_I2C_H
|
||||||
|
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "hw/arm/nrf51.h"
|
||||||
|
|
||||||
|
#define NRF51_TWI_TASK_STARTRX 0x000
|
||||||
|
#define NRF51_TWI_TASK_STARTTX 0x008
|
||||||
|
#define NRF51_TWI_TASK_STOP 0x014
|
||||||
|
#define NRF51_TWI_EVENT_STOPPED 0x104
|
||||||
|
#define NRF51_TWI_EVENT_RXDREADY 0x108
|
||||||
|
#define NRF51_TWI_EVENT_TXDSENT 0x11c
|
||||||
|
#define NRF51_TWI_REG_ENABLE 0x500
|
||||||
|
#define NRF51_TWI_REG_RXD 0x518
|
||||||
|
#define NRF51_TWI_REG_TXD 0x51c
|
||||||
|
#define NRF51_TWI_REG_ADDRESS 0x588
|
||||||
|
|
||||||
|
#define TYPE_MICROBIT_I2C "microbit.i2c"
|
||||||
|
#define MICROBIT_I2C(obj) \
|
||||||
|
OBJECT_CHECK(MicrobitI2CState, (obj), TYPE_MICROBIT_I2C)
|
||||||
|
|
||||||
|
#define MICROBIT_I2C_NREGS (NRF51_TWI_SIZE / sizeof(uint32_t))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
|
MemoryRegion iomem;
|
||||||
|
uint32_t regs[MICROBIT_I2C_NREGS];
|
||||||
|
uint32_t read_idx;
|
||||||
|
} MicrobitI2CState;
|
||||||
|
|
||||||
|
#endif /* MICROBIT_I2C_H */
|
@ -98,6 +98,9 @@ typedef struct AspeedSMCState {
|
|||||||
uint8_t conf_enable_w0;
|
uint8_t conf_enable_w0;
|
||||||
|
|
||||||
AspeedSMCFlash *flashes;
|
AspeedSMCFlash *flashes;
|
||||||
|
|
||||||
|
uint8_t snoop_index;
|
||||||
|
uint8_t snoop_dummies;
|
||||||
} AspeedSMCState;
|
} AspeedSMCState;
|
||||||
|
|
||||||
#endif /* ASPEED_SMC_H */
|
#endif /* ASPEED_SMC_H */
|
||||||
|
@ -280,6 +280,11 @@ struct qemu_work_item;
|
|||||||
/**
|
/**
|
||||||
* CPUState:
|
* CPUState:
|
||||||
* @cpu_index: CPU index (informative).
|
* @cpu_index: CPU index (informative).
|
||||||
|
* @cluster_index: Identifies which cluster this CPU is in.
|
||||||
|
* For boards which don't define clusters or for "loose" CPUs not assigned
|
||||||
|
* to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will
|
||||||
|
* be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER
|
||||||
|
* QOM parent.
|
||||||
* @nr_cores: Number of cores within this CPU package.
|
* @nr_cores: Number of cores within this CPU package.
|
||||||
* @nr_threads: Number of threads within this CPU.
|
* @nr_threads: Number of threads within this CPU.
|
||||||
* @running: #true if CPU is currently running (lockless).
|
* @running: #true if CPU is currently running (lockless).
|
||||||
@ -405,6 +410,7 @@ struct CPUState {
|
|||||||
|
|
||||||
/* TODO Move common fields from CPUArchState here. */
|
/* TODO Move common fields from CPUArchState here. */
|
||||||
int cpu_index;
|
int cpu_index;
|
||||||
|
int cluster_index;
|
||||||
uint32_t halted;
|
uint32_t halted;
|
||||||
uint32_t can_do_io;
|
uint32_t can_do_io;
|
||||||
int32_t exception_index;
|
int32_t exception_index;
|
||||||
@ -1111,5 +1117,6 @@ extern const struct VMStateDescription vmstate_cpu_common;
|
|||||||
#endif /* NEED_CPU_H */
|
#endif /* NEED_CPU_H */
|
||||||
|
|
||||||
#define UNASSIGNED_CPU_INDEX -1
|
#define UNASSIGNED_CPU_INDEX -1
|
||||||
|
#define UNASSIGNED_CLUSTER_INDEX -1
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -364,6 +364,7 @@ static void cpu_common_initfn(Object *obj)
|
|||||||
CPUClass *cc = CPU_GET_CLASS(obj);
|
CPUClass *cc = CPU_GET_CLASS(obj);
|
||||||
|
|
||||||
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
|
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
|
||||||
|
cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX;
|
||||||
cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
|
cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
|
||||||
/* *-user doesn't have configurable SMP topology */
|
/* *-user doesn't have configurable SMP topology */
|
||||||
/* the default value is changed by qemu_init_vcpu() for softmmu */
|
/* the default value is changed by qemu_init_vcpu() for softmmu */
|
||||||
|
@ -1624,7 +1624,7 @@ sub process {
|
|||||||
|
|
||||||
# Block comments use /* on a line of its own
|
# Block comments use /* on a line of its own
|
||||||
if ($rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/
|
if ($rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/
|
||||||
$rawline =~ m@^\+.*/\*\*?[ \t]*.+[ \t]*$@) { # /* or /** non-blank
|
$rawline =~ m@^\+.*/\*\*?+[ \t]*[^ \t]@) { # /* or /** non-blank
|
||||||
WARN("Block comments use a leading /* on a separate line\n" . $herecurr);
|
WARN("Block comments use a leading /* on a separate line\n" . $herecurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1039,8 +1039,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
|||||||
unset_feature(env, ARM_FEATURE_PMU);
|
unset_feature(env, ARM_FEATURE_PMU);
|
||||||
}
|
}
|
||||||
if (arm_feature(env, ARM_FEATURE_PMU)) {
|
if (arm_feature(env, ARM_FEATURE_PMU)) {
|
||||||
cpu->pmceid0 = get_pmceid(&cpu->env, 0);
|
pmu_init(cpu);
|
||||||
cpu->pmceid1 = get_pmceid(&cpu->env, 1);
|
|
||||||
|
|
||||||
if (!kvm_enabled()) {
|
if (!kvm_enabled()) {
|
||||||
arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0);
|
arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0);
|
||||||
|
@ -1012,14 +1012,13 @@ void pmu_pre_el_change(ARMCPU *cpu, void *ignored);
|
|||||||
void pmu_post_el_change(ARMCPU *cpu, void *ignored);
|
void pmu_post_el_change(ARMCPU *cpu, void *ignored);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_pmceid
|
* pmu_init
|
||||||
* @env: CPUARMState
|
* @cpu: ARMCPU
|
||||||
* @which: which PMCEID register to return (0 or 1)
|
|
||||||
*
|
*
|
||||||
* Return the PMCEID[01]_EL0 register values corresponding to the counters
|
* Initialize the CPU's PMCEID[01]_EL0 registers and associated internal state
|
||||||
* which are supported given the current configuration
|
* for the current configuration
|
||||||
*/
|
*/
|
||||||
uint64_t get_pmceid(CPUARMState *env, unsigned which);
|
void pmu_init(ARMCPU *cpu);
|
||||||
|
|
||||||
/* SCTLR bit meanings. Several bits have been reused in newer
|
/* SCTLR bit meanings. Several bits have been reused in newer
|
||||||
* versions of the architecture; in that case we define constants
|
* versions of the architecture; in that case we define constants
|
||||||
|
@ -1090,22 +1090,24 @@ static const pm_event pm_events[] = {
|
|||||||
static uint16_t supported_event_map[MAX_EVENT_ID + 1];
|
static uint16_t supported_event_map[MAX_EVENT_ID + 1];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called upon initialization to build PMCEID0_EL0 or PMCEID1_EL0 (indicated by
|
* Called upon CPU initialization to initialize PMCEID[01]_EL0 and build a map
|
||||||
* 'which'). We also use it to build a map of ARM event numbers to indices in
|
* of ARM event numbers to indices in our pm_events array.
|
||||||
* our pm_events array.
|
|
||||||
*
|
*
|
||||||
* Note: Events in the 0x40XX range are not currently supported.
|
* Note: Events in the 0x40XX range are not currently supported.
|
||||||
*/
|
*/
|
||||||
uint64_t get_pmceid(CPUARMState *env, unsigned which)
|
void pmu_init(ARMCPU *cpu)
|
||||||
{
|
{
|
||||||
uint64_t pmceid = 0;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
assert(which <= 1);
|
/*
|
||||||
|
* Empty supported_event_map and cpu->pmceid[01] before adding supported
|
||||||
|
* events to them
|
||||||
|
*/
|
||||||
for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) {
|
for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) {
|
||||||
supported_event_map[i] = UNSUPPORTED_EVENT;
|
supported_event_map[i] = UNSUPPORTED_EVENT;
|
||||||
}
|
}
|
||||||
|
cpu->pmceid0 = 0;
|
||||||
|
cpu->pmceid1 = 0;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(pm_events); i++) {
|
for (i = 0; i < ARRAY_SIZE(pm_events); i++) {
|
||||||
const pm_event *cnt = &pm_events[i];
|
const pm_event *cnt = &pm_events[i];
|
||||||
@ -1113,13 +1115,16 @@ uint64_t get_pmceid(CPUARMState *env, unsigned which)
|
|||||||
/* We do not currently support events in the 0x40xx range */
|
/* We do not currently support events in the 0x40xx range */
|
||||||
assert(cnt->number <= 0x3f);
|
assert(cnt->number <= 0x3f);
|
||||||
|
|
||||||
if ((cnt->number & 0x20) == (which << 6) &&
|
if (cnt->supported(&cpu->env)) {
|
||||||
cnt->supported(env)) {
|
|
||||||
pmceid |= (1 << (cnt->number & 0x1f));
|
|
||||||
supported_event_map[cnt->number] = i;
|
supported_event_map[cnt->number] = i;
|
||||||
|
uint64_t event_mask = 1 << (cnt->number & 0x1f);
|
||||||
|
if (cnt->number & 0x20) {
|
||||||
|
cpu->pmceid1 |= event_mask;
|
||||||
|
} else {
|
||||||
|
cpu->pmceid0 |= event_mask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pmceid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -10447,7 +10452,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
|||||||
uint64_t ttbr;
|
uint64_t ttbr;
|
||||||
hwaddr descaddr, indexmask, indexmask_grainsize;
|
hwaddr descaddr, indexmask, indexmask_grainsize;
|
||||||
uint32_t tableattrs;
|
uint32_t tableattrs;
|
||||||
target_ulong page_size, top_bits;
|
target_ulong page_size;
|
||||||
uint32_t attrs;
|
uint32_t attrs;
|
||||||
int32_t stride;
|
int32_t stride;
|
||||||
int addrsize, inputsize;
|
int addrsize, inputsize;
|
||||||
@ -10487,13 +10492,20 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
|
|||||||
* We determined the region when collecting the parameters, but we
|
* We determined the region when collecting the parameters, but we
|
||||||
* have not yet validated that the address is valid for the region.
|
* have not yet validated that the address is valid for the region.
|
||||||
* Extract the top bits and verify that they all match select.
|
* Extract the top bits and verify that they all match select.
|
||||||
|
*
|
||||||
|
* For aa32, if inputsize == addrsize, then we have selected the
|
||||||
|
* region by exclusion in aa32_va_parameters and there is no more
|
||||||
|
* validation to do here.
|
||||||
*/
|
*/
|
||||||
top_bits = sextract64(address, inputsize, addrsize - inputsize);
|
if (inputsize < addrsize) {
|
||||||
|
target_ulong top_bits = sextract64(address, inputsize,
|
||||||
|
addrsize - inputsize);
|
||||||
if (-top_bits != param.select || (param.select && !ttbr1_valid)) {
|
if (-top_bits != param.select || (param.select && !ttbr1_valid)) {
|
||||||
/* In the gap between the two regions, this is a Translation fault */
|
/* The gap between the two regions is a Translation fault */
|
||||||
fault_type = ARMFault_Translation;
|
fault_type = ARMFault_Translation;
|
||||||
goto do_fault;
|
goto do_fault;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (param.using64k) {
|
if (param.using64k) {
|
||||||
stride = 13;
|
stride = 13;
|
||||||
@ -11071,8 +11083,11 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* The IDAU will override the SAU lookup results if it specifies
|
/*
|
||||||
|
* The IDAU will override the SAU lookup results if it specifies
|
||||||
* higher security than the SAU does.
|
* higher security than the SAU does.
|
||||||
*/
|
*/
|
||||||
if (!idau_ns) {
|
if (!idau_ns) {
|
||||||
@ -11081,8 +11096,6 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address,
|
|||||||
sattrs->nsc = idau_nsc;
|
sattrs->nsc = idau_nsc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
||||||
|
@ -315,6 +315,31 @@ QTestState *qtest_initf(const char *fmt, ...)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd)
|
||||||
|
{
|
||||||
|
int sock_fd_init;
|
||||||
|
char *sock_path, sock_dir[] = "/tmp/qtest-serial-XXXXXX";
|
||||||
|
QTestState *qts;
|
||||||
|
|
||||||
|
g_assert_true(mkdtemp(sock_dir) != NULL);
|
||||||
|
sock_path = g_strdup_printf("%s/sock", sock_dir);
|
||||||
|
|
||||||
|
sock_fd_init = init_socket(sock_path);
|
||||||
|
|
||||||
|
qts = qtest_initf("-chardev socket,id=s0,path=%s -serial chardev:s0 %s",
|
||||||
|
sock_path, extra_args);
|
||||||
|
|
||||||
|
*sock_fd = socket_accept(sock_fd_init);
|
||||||
|
|
||||||
|
unlink(sock_path);
|
||||||
|
g_free(sock_path);
|
||||||
|
rmdir(sock_dir);
|
||||||
|
|
||||||
|
g_assert_true(*sock_fd >= 0);
|
||||||
|
|
||||||
|
return qts;
|
||||||
|
}
|
||||||
|
|
||||||
void qtest_quit(QTestState *s)
|
void qtest_quit(QTestState *s)
|
||||||
{
|
{
|
||||||
g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s));
|
g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s));
|
||||||
|
@ -62,6 +62,17 @@ QTestState *qtest_init(const char *extra_args);
|
|||||||
*/
|
*/
|
||||||
QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
|
QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qtest_init_with_serial:
|
||||||
|
* @extra_args: other arguments to pass to QEMU. CAUTION: these
|
||||||
|
* arguments are subject to word splitting and shell evaluation.
|
||||||
|
* @sock_fd: pointer to store the socket file descriptor for
|
||||||
|
* connection with serial.
|
||||||
|
*
|
||||||
|
* Returns: #QTestState instance.
|
||||||
|
*/
|
||||||
|
QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qtest_quit:
|
* qtest_quit:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
|
@ -19,8 +19,142 @@
|
|||||||
#include "libqtest.h"
|
#include "libqtest.h"
|
||||||
|
|
||||||
#include "hw/arm/nrf51.h"
|
#include "hw/arm/nrf51.h"
|
||||||
|
#include "hw/char/nrf51_uart.h"
|
||||||
#include "hw/gpio/nrf51_gpio.h"
|
#include "hw/gpio/nrf51_gpio.h"
|
||||||
#include "hw/timer/nrf51_timer.h"
|
#include "hw/timer/nrf51_timer.h"
|
||||||
|
#include "hw/i2c/microbit_i2c.h"
|
||||||
|
|
||||||
|
static bool uart_wait_for_event(QTestState *qts, uint32_t event_addr)
|
||||||
|
{
|
||||||
|
time_t now, start = time(NULL);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (qtest_readl(qts, event_addr) == 1) {
|
||||||
|
qtest_writel(qts, event_addr, 0x00);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait at most 10 minutes */
|
||||||
|
now = time(NULL);
|
||||||
|
if (now - start > 600) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_rw_to_rxd(QTestState *qts, int sock_fd, const char *in,
|
||||||
|
char *out)
|
||||||
|
{
|
||||||
|
int i, in_len = strlen(in);
|
||||||
|
|
||||||
|
g_assert_true(write(sock_fd, in, in_len) == in_len);
|
||||||
|
for (i = 0; i < in_len; i++) {
|
||||||
|
g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE +
|
||||||
|
A_UART_RXDRDY));
|
||||||
|
out[i] = qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD);
|
||||||
|
}
|
||||||
|
out[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_w_to_txd(QTestState *qts, const char *in)
|
||||||
|
{
|
||||||
|
int i, in_len = strlen(in);
|
||||||
|
|
||||||
|
for (i = 0; i < in_len; i++) {
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, in[i]);
|
||||||
|
g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE +
|
||||||
|
A_UART_TXDRDY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_nrf51_uart(void)
|
||||||
|
{
|
||||||
|
int sock_fd;
|
||||||
|
char s[10];
|
||||||
|
QTestState *qts = qtest_init_with_serial("-M microbit", &sock_fd);
|
||||||
|
|
||||||
|
g_assert_true(write(sock_fd, "c", 1) == 1);
|
||||||
|
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 0x00);
|
||||||
|
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_ENABLE, 0x04);
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTRX, 0x01);
|
||||||
|
|
||||||
|
g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + A_UART_RXDRDY));
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_RXDRDY, 0x00);
|
||||||
|
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 'c');
|
||||||
|
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENSET, 0x04);
|
||||||
|
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x04);
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENCLR, 0x04);
|
||||||
|
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x00);
|
||||||
|
|
||||||
|
uart_rw_to_rxd(qts, sock_fd, "hello", s);
|
||||||
|
g_assert_true(memcmp(s, "hello", 5) == 0);
|
||||||
|
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01);
|
||||||
|
uart_w_to_txd(qts, "d");
|
||||||
|
g_assert_true(read(sock_fd, s, 10) == 1);
|
||||||
|
g_assert_cmphex(s[0], ==, 'd');
|
||||||
|
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_SUSPEND, 0x01);
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, 'h');
|
||||||
|
qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01);
|
||||||
|
uart_w_to_txd(qts, "world");
|
||||||
|
g_assert_true(read(sock_fd, s, 10) == 5);
|
||||||
|
g_assert_true(memcmp(s, "world", 5) == 0);
|
||||||
|
|
||||||
|
close(sock_fd);
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a byte from I2C device at @addr from register @reg */
|
||||||
|
static uint32_t i2c_read_byte(QTestState *qts, uint32_t addr, uint32_t reg)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ADDRESS, addr);
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTTX, 1);
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_TXD, reg);
|
||||||
|
val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_TXDSENT);
|
||||||
|
g_assert_cmpuint(val, ==, 1);
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1);
|
||||||
|
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTRX, 1);
|
||||||
|
val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_RXDREADY);
|
||||||
|
g_assert_cmpuint(val, ==, 1);
|
||||||
|
val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_REG_RXD);
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_microbit_i2c(void)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
QTestState *qts = qtest_init("-M microbit");
|
||||||
|
|
||||||
|
/* We don't program pins/irqs but at least enable the device */
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 5);
|
||||||
|
|
||||||
|
/* MMA8653 magnetometer detection */
|
||||||
|
val = i2c_read_byte(qts, 0x3A, 0x0D);
|
||||||
|
g_assert_cmpuint(val, ==, 0x5A);
|
||||||
|
|
||||||
|
val = i2c_read_byte(qts, 0x3A, 0x0D);
|
||||||
|
g_assert_cmpuint(val, ==, 0x5A);
|
||||||
|
|
||||||
|
/* LSM303 accelerometer detection */
|
||||||
|
val = i2c_read_byte(qts, 0x3C, 0x4F);
|
||||||
|
g_assert_cmpuint(val, ==, 0x40);
|
||||||
|
|
||||||
|
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0);
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_nrf51_gpio(void)
|
static void test_nrf51_gpio(void)
|
||||||
{
|
{
|
||||||
@ -37,219 +171,229 @@ static void test_nrf51_gpio(void)
|
|||||||
{NRF51_GPIO_REG_DIRCLR, 0x00000000}
|
{NRF51_GPIO_REG_DIRCLR, 0x00000000}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QTestState *qts = qtest_init("-M microbit");
|
||||||
|
|
||||||
/* Check reset state */
|
/* Check reset state */
|
||||||
for (i = 0; i < ARRAY_SIZE(reset_state); i++) {
|
for (i = 0; i < ARRAY_SIZE(reset_state); i++) {
|
||||||
expected = reset_state[i].expected;
|
expected = reset_state[i].expected;
|
||||||
actual = readl(NRF51_GPIO_BASE + reset_state[i].addr);
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + reset_state[i].addr);
|
||||||
g_assert_cmpuint(actual, ==, expected);
|
g_assert_cmpuint(actual, ==, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < NRF51_GPIO_PINS; i++) {
|
for (i = 0; i < NRF51_GPIO_PINS; i++) {
|
||||||
expected = 0x00000002;
|
expected = 0x00000002;
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START + i * 4);
|
actual = qtest_readl(qts, NRF51_GPIO_BASE +
|
||||||
|
NRF51_GPIO_REG_CNF_START + i * 4);
|
||||||
g_assert_cmpuint(actual, ==, expected);
|
g_assert_cmpuint(actual, ==, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check dir bit consistency between dir and cnf */
|
/* Check dir bit consistency between dir and cnf */
|
||||||
/* Check set via DIRSET */
|
/* Check set via DIRSET */
|
||||||
expected = 0x80000001;
|
expected = 0x80000001;
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
|
||||||
g_assert_cmpuint(actual, ==, expected);
|
g_assert_cmpuint(actual, ==, expected);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START)
|
||||||
|
& 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
|
|
||||||
/* Check clear via DIRCLR */
|
/* Check clear via DIRCLR */
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
|
||||||
g_assert_cmpuint(actual, ==, 0x00000000);
|
g_assert_cmpuint(actual, ==, 0x00000000);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START)
|
||||||
|
& 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x00);
|
g_assert_cmpuint(actual, ==, 0x00);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x00);
|
g_assert_cmpuint(actual, ==, 0x00);
|
||||||
|
|
||||||
/* Check set via DIR */
|
/* Check set via DIR */
|
||||||
expected = 0x80000001;
|
expected = 0x80000001;
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
|
||||||
g_assert_cmpuint(actual, ==, expected);
|
g_assert_cmpuint(actual, ==, expected);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START)
|
||||||
|
& 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
|
|
||||||
/* Reset DIR */
|
/* Reset DIR */
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000);
|
||||||
|
|
||||||
/* Check Input propagates */
|
/* Check Input propagates */
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00);
|
||||||
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
|
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x00);
|
g_assert_cmpuint(actual, ==, 0x00);
|
||||||
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
|
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
|
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
|
||||||
|
|
||||||
/* Check pull-up working */
|
/* Check pull-up working */
|
||||||
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
|
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x00);
|
g_assert_cmpuint(actual, ==, 0x00);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
|
||||||
|
|
||||||
/* Check pull-down working */
|
/* Check pull-down working */
|
||||||
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
|
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x00);
|
g_assert_cmpuint(actual, ==, 0x00);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
|
||||||
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
|
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
|
||||||
|
|
||||||
/* Check Output propagates */
|
/* Check Output propagates */
|
||||||
irq_intercept_out("/machine/nrf51");
|
qtest_irq_intercept_out(qts, "/machine/nrf51");
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
|
||||||
g_assert_true(get_irq(0));
|
g_assert_true(qtest_get_irq(qts, 0));
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
|
||||||
g_assert_false(get_irq(0));
|
g_assert_false(qtest_get_irq(qts, 0));
|
||||||
|
|
||||||
/* Check self-stimulation */
|
/* Check self-stimulation */
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x01);
|
g_assert_cmpuint(actual, ==, 0x01);
|
||||||
|
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
|
||||||
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
|
||||||
g_assert_cmpuint(actual, ==, 0x00);
|
g_assert_cmpuint(actual, ==, 0x00);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check short-circuit - generates an guest_error which must be checked
|
* Check short-circuit - generates an guest_error which must be checked
|
||||||
* manually as long as qtest can not scan qemu_log messages
|
* manually as long as qtest can not scan qemu_log messages
|
||||||
*/
|
*/
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
|
||||||
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
|
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
|
||||||
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
|
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_task(hwaddr task)
|
static void timer_task(QTestState *qts, hwaddr task)
|
||||||
{
|
{
|
||||||
writel(NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK);
|
qtest_writel(qts, NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_clear_event(hwaddr event)
|
static void timer_clear_event(QTestState *qts, hwaddr event)
|
||||||
{
|
{
|
||||||
writel(NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR);
|
qtest_writel(qts, NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_set_bitmode(uint8_t mode)
|
static void timer_set_bitmode(QTestState *qts, uint8_t mode)
|
||||||
{
|
{
|
||||||
writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode);
|
qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_set_prescaler(uint8_t prescaler)
|
static void timer_set_prescaler(QTestState *qts, uint8_t prescaler)
|
||||||
{
|
{
|
||||||
writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler);
|
qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_set_cc(size_t idx, uint32_t value)
|
static void timer_set_cc(QTestState *qts, size_t idx, uint32_t value)
|
||||||
{
|
{
|
||||||
writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value);
|
qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_assert_events(uint32_t ev0, uint32_t ev1, uint32_t ev2,
|
static void timer_assert_events(QTestState *qts, uint32_t ev0, uint32_t ev1,
|
||||||
uint32_t ev3)
|
uint32_t ev2, uint32_t ev3)
|
||||||
{
|
{
|
||||||
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0) == ev0);
|
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0)
|
||||||
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1) == ev1);
|
== ev0);
|
||||||
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2) == ev2);
|
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1)
|
||||||
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3) == ev3);
|
== ev1);
|
||||||
|
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2)
|
||||||
|
== ev2);
|
||||||
|
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3)
|
||||||
|
== ev3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_nrf51_timer(void)
|
static void test_nrf51_timer(void)
|
||||||
{
|
{
|
||||||
uint32_t steps_to_overflow = 408;
|
uint32_t steps_to_overflow = 408;
|
||||||
|
QTestState *qts = qtest_init("-M microbit");
|
||||||
|
|
||||||
/* Compare Match */
|
/* Compare Match */
|
||||||
timer_task(NRF51_TIMER_TASK_STOP);
|
timer_task(qts, NRF51_TIMER_TASK_STOP);
|
||||||
timer_task(NRF51_TIMER_TASK_CLEAR);
|
timer_task(qts, NRF51_TIMER_TASK_CLEAR);
|
||||||
|
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_0);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0);
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_1);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1);
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_2);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2);
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_3);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3);
|
||||||
|
|
||||||
timer_set_bitmode(NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */
|
timer_set_bitmode(qts, NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */
|
||||||
timer_set_prescaler(0);
|
timer_set_prescaler(qts, 0);
|
||||||
/* Swept over in first step */
|
/* Swept over in first step */
|
||||||
timer_set_cc(0, 2);
|
timer_set_cc(qts, 0, 2);
|
||||||
/* Barely miss on first step */
|
/* Barely miss on first step */
|
||||||
timer_set_cc(1, 162);
|
timer_set_cc(qts, 1, 162);
|
||||||
/* Spot on on third step */
|
/* Spot on on third step */
|
||||||
timer_set_cc(2, 480);
|
timer_set_cc(qts, 2, 480);
|
||||||
|
|
||||||
timer_assert_events(0, 0, 0, 0);
|
timer_assert_events(qts, 0, 0, 0, 0);
|
||||||
|
|
||||||
timer_task(NRF51_TIMER_TASK_START);
|
timer_task(qts, NRF51_TIMER_TASK_START);
|
||||||
clock_step(10000);
|
qtest_clock_step(qts, 10000);
|
||||||
timer_assert_events(1, 0, 0, 0);
|
timer_assert_events(qts, 1, 0, 0, 0);
|
||||||
|
|
||||||
/* Swept over on first overflow */
|
/* Swept over on first overflow */
|
||||||
timer_set_cc(3, 114);
|
timer_set_cc(qts, 3, 114);
|
||||||
|
|
||||||
clock_step(10000);
|
qtest_clock_step(qts, 10000);
|
||||||
timer_assert_events(1, 1, 0, 0);
|
timer_assert_events(qts, 1, 1, 0, 0);
|
||||||
|
|
||||||
clock_step(10000);
|
qtest_clock_step(qts, 10000);
|
||||||
timer_assert_events(1, 1, 1, 0);
|
timer_assert_events(qts, 1, 1, 1, 0);
|
||||||
|
|
||||||
/* Wrap time until internal counter overflows */
|
/* Wrap time until internal counter overflows */
|
||||||
while (steps_to_overflow--) {
|
while (steps_to_overflow--) {
|
||||||
timer_assert_events(1, 1, 1, 0);
|
timer_assert_events(qts, 1, 1, 1, 0);
|
||||||
clock_step(10000);
|
qtest_clock_step(qts, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
timer_assert_events(1, 1, 1, 1);
|
timer_assert_events(qts, 1, 1, 1, 1);
|
||||||
|
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_0);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0);
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_1);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1);
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_2);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2);
|
||||||
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_3);
|
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3);
|
||||||
timer_assert_events(0, 0, 0, 0);
|
timer_assert_events(qts, 0, 0, 0, 0);
|
||||||
|
|
||||||
timer_task(NRF51_TIMER_TASK_STOP);
|
timer_task(qts, NRF51_TIMER_TASK_STOP);
|
||||||
|
|
||||||
/* Test Proposal: Stop/Shutdown */
|
/* Test Proposal: Stop/Shutdown */
|
||||||
/* Test Proposal: Shortcut Compare -> Clear */
|
/* Test Proposal: Shortcut Compare -> Clear */
|
||||||
/* Test Proposal: Shortcut Compare -> Stop */
|
/* Test Proposal: Shortcut Compare -> Stop */
|
||||||
/* Test Proposal: Counter Mode */
|
/* Test Proposal: Counter Mode */
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
g_test_init(&argc, &argv, NULL);
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
|
||||||
global_qtest = qtest_initf("-machine microbit");
|
qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart);
|
||||||
|
|
||||||
qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio);
|
qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio);
|
||||||
qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer);
|
qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer);
|
||||||
|
qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c);
|
||||||
|
|
||||||
ret = g_test_run();
|
return g_test_run();
|
||||||
|
|
||||||
qtest_quit(global_qtest);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user