aboutsummaryrefslogtreecommitdiff
path: root/hw/ssi/aspeed_smc.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-01-29 12:00:19 +0000
committerPeter Maydell <peter.maydell@linaro.org>2019-01-29 12:00:19 +0000
commitb4fbe1f65a4769c09e6bf2d79fc84360f840f40e (patch)
tree2565dfb0a8d719063e9682b7887747748eba230a /hw/ssi/aspeed_smc.c
parent3a183e330dbd7dbcac3841737ac874979552cca2 (diff)
parent46f5abc0a2566ac3dc954eeb62fd625f0eaca120 (diff)
downloadqemu-b4fbe1f65a4769c09e6bf2d79fc84360f840f40e.zip
qemu-b4fbe1f65a4769c09e6bf2d79fc84360f840f40e.tar.gz
qemu-b4fbe1f65a4769c09e6bf2d79fc84360f840f40e.tar.bz2
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>
Diffstat (limited to 'hw/ssi/aspeed_smc.c')
-rw-r--r--hw/ssi/aspeed_smc.c128
1 files changed, 120 insertions, 8 deletions
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()
}
};