aboutsummaryrefslogtreecommitdiff
path: root/hw/ssi/aspeed_smc.c
diff options
context:
space:
mode:
authorCédric Le Goater <clg@kaod.org>2019-09-04 09:05:02 +0200
committerPeter Maydell <peter.maydell@linaro.org>2019-09-13 16:05:01 +0100
commit0d72c717029f59fa0531fee419734ad7f14b1331 (patch)
tree608c9267ddb37dfa24eef77d88d1531c059cd2a2 /hw/ssi/aspeed_smc.c
parentc4e1f0b48322a9bc98c37f8413553cb6131daafe (diff)
downloadqemu-0d72c717029f59fa0531fee419734ad7f14b1331.zip
qemu-0d72c717029f59fa0531fee419734ad7f14b1331.tar.gz
qemu-0d72c717029f59fa0531fee419734ad7f14b1331.tar.bz2
aspeed/smc: Add DMA calibration settings
When doing calibration, the SPI clock rate in the CE0 Control Register and the read delay cycles in the Read Timing Compensation Register are set using bit[11:4] of the DMA Control Register. Signed-off-by: Cédric Le Goater <clg@kaod.org> Acked-by: Joel Stanley <joel@jms.id.au> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20190904070506.1052-7-clg@kaod.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/ssi/aspeed_smc.c')
-rw-r--r--hw/ssi/aspeed_smc.c64
1 files changed, 63 insertions, 1 deletions
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index c1a45c1..7a0cd76 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -77,6 +77,10 @@
#define CTRL_CMD_MASK 0xff
#define CTRL_DUMMY_HIGH_SHIFT 14
#define CTRL_AST2400_SPI_4BYTE (1 << 13)
+#define CE_CTRL_CLOCK_FREQ_SHIFT 8
+#define CE_CTRL_CLOCK_FREQ_MASK 0xf
+#define CE_CTRL_CLOCK_FREQ(div) \
+ (((div) & CE_CTRL_CLOCK_FREQ_MASK) << CE_CTRL_CLOCK_FREQ_SHIFT)
#define CTRL_DUMMY_LOW_SHIFT 6 /* 2 bits [7:6] */
#define CTRL_CE_STOP_ACTIVE (1 << 2)
#define CTRL_CMD_MODE_MASK 0x3
@@ -112,7 +116,7 @@
#define DMA_CTRL_DELAY_SHIFT 8
#define DMA_CTRL_FREQ_MASK 0xf
#define DMA_CTRL_FREQ_SHIFT 4
-#define DMA_CTRL_MODE (1 << 3)
+#define DMA_CTRL_CALIB (1 << 3)
#define DMA_CTRL_CKSUM (1 << 2)
#define DMA_CTRL_WRITE (1 << 1)
#define DMA_CTRL_ENABLE (1 << 0)
@@ -811,6 +815,60 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
}
}
+static uint8_t aspeed_smc_hclk_divisor(uint8_t hclk_mask)
+{
+ /* HCLK/1 .. HCLK/16 */
+ const uint8_t hclk_divisors[] = {
+ 15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hclk_divisors); i++) {
+ if (hclk_mask == hclk_divisors[i]) {
+ return i + 1;
+ }
+ }
+
+ qemu_log_mask(LOG_GUEST_ERROR, "invalid HCLK mask %x", hclk_mask);
+ return 0;
+}
+
+/*
+ * When doing calibration, the SPI clock rate in the CE0 Control
+ * Register and the read delay cycles in the Read Timing Compensation
+ * Register are set using bit[11:4] of the DMA Control Register.
+ */
+static void aspeed_smc_dma_calibration(AspeedSMCState *s)
+{
+ uint8_t delay =
+ (s->regs[R_DMA_CTRL] >> DMA_CTRL_DELAY_SHIFT) & DMA_CTRL_DELAY_MASK;
+ uint8_t hclk_mask =
+ (s->regs[R_DMA_CTRL] >> DMA_CTRL_FREQ_SHIFT) & DMA_CTRL_FREQ_MASK;
+ uint8_t hclk_div = aspeed_smc_hclk_divisor(hclk_mask);
+ uint32_t hclk_shift = (hclk_div - 1) << 2;
+ uint8_t cs;
+
+ /*
+ * The Read Timing Compensation Register values apply to all CS on
+ * the SPI bus and only HCLK/1 - HCLK/5 can have tunable delays
+ */
+ if (hclk_div && hclk_div < 6) {
+ s->regs[s->r_timings] &= ~(0xf << hclk_shift);
+ s->regs[s->r_timings] |= delay << hclk_shift;
+ }
+
+ /*
+ * TODO: compute the CS from the DMA address and the segment
+ * registers. This is not really a problem for now because the
+ * Timing Register values apply to all CS and software uses CS0 to
+ * do calibration.
+ */
+ cs = 0;
+ s->regs[s->r_ctrl0 + cs] &=
+ ~(CE_CTRL_CLOCK_FREQ_MASK << CE_CTRL_CLOCK_FREQ_SHIFT);
+ s->regs[s->r_ctrl0 + cs] |= CE_CTRL_CLOCK_FREQ(hclk_div);
+}
+
/*
* Accumulate the result of the reads to provide a checksum that will
* be used to validate the read timing settings.
@@ -826,6 +884,10 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s)
return;
}
+ if (s->regs[R_DMA_CTRL] & DMA_CTRL_CALIB) {
+ aspeed_smc_dma_calibration(s);
+ }
+
while (s->regs[R_DMA_LEN]) {
data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR],
MEMTXATTRS_UNSPECIFIED, &result);