mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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 ba97be9f4a)
* 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:
+5
-3
@@ -829,9 +829,11 @@ M: Joel Stanley <joel@jms.id.au>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/nrf51_soc.c
|
||||
F: hw/arm/microbit.c
|
||||
F: include/hw/arm/nrf51_soc.h
|
||||
F: hw/*/nrf51*.c
|
||||
F: hw/*/microbit*.c
|
||||
F: include/hw/*/nrf51*.h
|
||||
F: include/hw/*/microbit*.h
|
||||
F: tests/microbit-test.c
|
||||
|
||||
CRIS Machines
|
||||
-------------
|
||||
|
||||
@@ -325,6 +325,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
||||
struct tb_desc desc;
|
||||
uint32_t h;
|
||||
|
||||
cf_mask &= ~CF_CLUSTER_MASK;
|
||||
cf_mask |= cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||
|
||||
desc.env = (CPUArchState *)cpu->env_ptr;
|
||||
desc.cs_base = cs_base;
|
||||
desc.flags = flags;
|
||||
|
||||
@@ -1688,6 +1688,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
cflags |= CF_NOCACHE | 1;
|
||||
}
|
||||
|
||||
cflags &= ~CF_CLUSTER_MASK;
|
||||
cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT;
|
||||
|
||||
buffer_overflow:
|
||||
tb = tb_alloc(pc);
|
||||
if (unlikely(!tb)) {
|
||||
|
||||
+52
-14
@@ -479,28 +479,66 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
|
||||
#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)
|
||||
{
|
||||
siginfo_t *info = pinfo;
|
||||
ucontext_t *uc = puc;
|
||||
uintptr_t pc = uc->uc_mcontext.pc;
|
||||
uint32_t insn = *(uint32_t *)pc;
|
||||
bool is_write;
|
||||
struct _aarch64_ctx *hdr;
|
||||
struct esr_context const *esrctx = NULL;
|
||||
|
||||
/* XXX: need kernel patch to get write flag faster. */
|
||||
is_write = ( (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
||||
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
||||
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
||||
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|
||||
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
||||
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
||||
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
||||
/* Ingore bits 10, 11 & 21, controlling indexing. */
|
||||
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
||||
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
||||
/* Ignore bits 23 & 24, controlling indexing. */
|
||||
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
||||
/* 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;
|
||||
|
||||
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|
||||
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|
||||
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|
||||
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|
||||
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|
||||
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|
||||
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
|
||||
/* Ignore bits 10, 11 & 21, controlling indexing. */
|
||||
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|
||||
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
|
||||
/* Ignore bits 23 & 24, controlling indexing. */
|
||||
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
|
||||
}
|
||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (is_write) {
|
||||
address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED,
|
||||
buf, l);
|
||||
attrs, buf, l);
|
||||
} else {
|
||||
address_space_rw(cpu->cpu_ases[asidx].as, phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED,
|
||||
buf, l, 0);
|
||||
attrs, buf, l, 0);
|
||||
}
|
||||
len -= l;
|
||||
buf += l;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
#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 */
|
||||
return s->processes[s->process_num - 1].pid;
|
||||
#endif
|
||||
if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
|
||||
/* Return the default process' PID */
|
||||
return s->processes[s->process_num - 1].pid;
|
||||
}
|
||||
return cpu->cluster_index + 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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. */
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
const char **newp, GDBProcess *process)
|
||||
{
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
#include "hw/arm/nrf51_soc.h"
|
||||
#include "hw/i2c/microbit_i2c.h"
|
||||
|
||||
typedef struct {
|
||||
MachineState parent;
|
||||
|
||||
NRF51State nrf51;
|
||||
MicrobitI2CState i2c;
|
||||
} MicrobitMachineState;
|
||||
|
||||
#define TYPE_MICROBIT_MACHINE MACHINE_TYPE_NAME("microbit")
|
||||
@@ -32,7 +34,9 @@ static void microbit_init(MachineState *machine)
|
||||
{
|
||||
MicrobitMachineState *s = MICROBIT_MACHINE(machine);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *mr;
|
||||
Object *soc = OBJECT(&s->nrf51);
|
||||
Object *i2c = OBJECT(&s->i2c);
|
||||
|
||||
sysbus_init_child_obj(OBJECT(machine), "nrf51", soc, sizeof(s->nrf51),
|
||||
TYPE_NRF51_SOC);
|
||||
@@ -41,6 +45,18 @@ static void microbit_init(MachineState *machine)
|
||||
&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,
|
||||
NRF51_SOC(soc)->flash_size);
|
||||
}
|
||||
|
||||
@@ -178,13 +178,16 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
|
||||
int i;
|
||||
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,
|
||||
sizeof(s->rpu_cluster), TYPE_CPU_CLUSTER,
|
||||
&error_abort, NULL);
|
||||
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++) {
|
||||
char *name;
|
||||
|
||||
@@ -212,6 +215,8 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qdev_init_nofail(DEVICE(&s->rpu_cluster));
|
||||
}
|
||||
|
||||
static void xlnx_zynqmp_init(Object *obj)
|
||||
|
||||
@@ -20,19 +20,65 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/cpu/cluster.h"
|
||||
#include "qom/cpu.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
static Property cpu_cluster_properties[] = {
|
||||
DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0),
|
||||
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)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = cpu_cluster_properties;
|
||||
dc->realize = cpu_cluster_realize;
|
||||
}
|
||||
|
||||
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_IMX_I2C) += imx_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_PPC4XX) += ppc4xx_i2c.o
|
||||
|
||||
@@ -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)
|
||||
+120
-8
@@ -98,8 +98,8 @@
|
||||
/* Misc Control Register #1 */
|
||||
#define R_MISC_CTRL1 (0x50 / 4)
|
||||
|
||||
/* Misc Control Register #2 */
|
||||
#define R_MISC_CTRL2 (0x54 / 4)
|
||||
/* SPI dummy cycle data */
|
||||
#define R_DUMMY_DATA (0x54 / 4)
|
||||
|
||||
/* DMA Control/Status Register */
|
||||
#define R_DMA_CTRL (0x80 / 4)
|
||||
@@ -145,6 +145,9 @@
|
||||
/* Flash opcodes. */
|
||||
#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
|
||||
* 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
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)) {
|
||||
case CTRL_USERMODE:
|
||||
if (aspeed_smc_do_snoop(fl, data, size)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
@@ -652,6 +756,9 @@ static void aspeed_smc_reset(DeviceState *d)
|
||||
if (s->ctrl->segments == aspeed_segments_fmc) {
|
||||
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)
|
||||
@@ -664,13 +771,14 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
addr == s->r_timings ||
|
||||
addr == s->r_ce_ctrl ||
|
||||
addr == R_INTR_CTRL ||
|
||||
addr == R_DUMMY_DATA ||
|
||||
(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];
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
|
||||
__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]) {
|
||||
aspeed_smc_flash_set_segment(s, cs, value);
|
||||
}
|
||||
} else if (addr == R_DUMMY_DATA) {
|
||||
s->regs[addr] = value & 0xff;
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
|
||||
__func__, addr);
|
||||
@@ -790,10 +900,12 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
static const VMStateDescription vmstate_aspeed_smc = {
|
||||
.name = "aspeed.smc",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
|
||||
VMSTATE_UINT8(snoop_index, AspeedSMCState),
|
||||
VMSTATE_UINT8(snoop_dummies, AspeedSMCState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -351,9 +351,11 @@ struct TranslationBlock {
|
||||
#define CF_USE_ICOUNT 0x00020000
|
||||
#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */
|
||||
#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 */
|
||||
#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 */
|
||||
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,
|
||||
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)
|
||||
*
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#define NRF51_IOMEM_SIZE 0x20000000
|
||||
|
||||
#define NRF51_UART_BASE 0x40002000
|
||||
#define NRF51_TWI_BASE 0x40003000
|
||||
#define NRF51_TWI_SIZE 0x00001000
|
||||
#define NRF51_TIMER_BASE 0x40008000
|
||||
#define NRF51_TIMER_SIZE 0x00001000
|
||||
#define NRF51_RNG_BASE 0x4000D000
|
||||
|
||||
@@ -39,6 +39,7 @@ typedef struct NRF51State {
|
||||
MemoryRegion sram;
|
||||
MemoryRegion flash;
|
||||
MemoryRegion clock;
|
||||
MemoryRegion twi;
|
||||
|
||||
uint32_t sram_size;
|
||||
uint32_t flash_size;
|
||||
|
||||
@@ -34,12 +34,36 @@
|
||||
* 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
|
||||
* 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 CPU_CLUSTER(obj) \
|
||||
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:
|
||||
* @cluster_id: The cluster ID. This value is for internal use only and should
|
||||
|
||||
@@ -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;
|
||||
|
||||
AspeedSMCFlash *flashes;
|
||||
|
||||
uint8_t snoop_index;
|
||||
uint8_t snoop_dummies;
|
||||
} AspeedSMCState;
|
||||
|
||||
#endif /* ASPEED_SMC_H */
|
||||
|
||||
@@ -280,6 +280,11 @@ struct qemu_work_item;
|
||||
/**
|
||||
* CPUState:
|
||||
* @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_threads: Number of threads within this CPU.
|
||||
* @running: #true if CPU is currently running (lockless).
|
||||
@@ -405,6 +410,7 @@ struct CPUState {
|
||||
|
||||
/* TODO Move common fields from CPUArchState here. */
|
||||
int cpu_index;
|
||||
int cluster_index;
|
||||
uint32_t halted;
|
||||
uint32_t can_do_io;
|
||||
int32_t exception_index;
|
||||
@@ -1111,5 +1117,6 @@ extern const struct VMStateDescription vmstate_cpu_common;
|
||||
#endif /* NEED_CPU_H */
|
||||
|
||||
#define UNASSIGNED_CPU_INDEX -1
|
||||
#define UNASSIGNED_CLUSTER_INDEX -1
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user