When doing calibration, the SPI clock rate in the CE0 Control Register and the read delay cycles in the Read Timing Compensation Register are replaced by bit[11:4] of the DMA Control Register.
Signed-off-by: Cédric Le Goater <c...@kaod.org> --- hw/ssi/aspeed_smc.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 534faec4c111..983066f5ad1d 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -695,6 +695,56 @@ 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 replaced by 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; + + /* Only HCLK/1 - HCLK/5 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: choose CS depending on the DMA address. This is not used + * on the field. + */ + 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. @@ -709,6 +759,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]) { cpu_physical_memory_read(s->regs[R_DMA_FLASH_ADDR], &data, 4); -- 2.17.1