aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/microbit.c16
-rw-r--r--hw/arm/xlnx-zynqmp.c9
-rw-r--r--hw/cpu/cluster.c46
-rw-r--r--hw/i2c/Makefile.objs1
-rw-r--r--hw/i2c/microbit_i2c.c127
-rw-r--r--hw/ssi/aspeed_smc.c128
6 files changed, 317 insertions, 10 deletions
diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c
index a734e7f..da67bf6 100644
--- a/hw/arm/microbit.c
+++ b/hw/arm/microbit.c
@@ -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);
}
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index c67ac2e..4f8bc41 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -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)
diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c
index 9d50a23..25f9070 100644
--- a/hw/cpu/cluster.c
+++ b/hw/cpu/cluster.c
@@ -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 = {
diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs
index 37cacde..82e747e 100644
--- a/hw/i2c/Makefile.objs
+++ b/hw/i2c/Makefile.objs
@@ -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
diff --git a/hw/i2c/microbit_i2c.c b/hw/i2c/microbit_i2c.c
new file mode 100644
index 0000000..793f1b0
--- /dev/null
+++ b/hw/i2c/microbit_i2c.c
@@ -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), &microbit_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 = &microbit_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(&microbit_i2c_info);
+}
+
+type_init(microbit_i2c_register_types)
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 1270842..f1e6687 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -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()
}
};