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>
master
Cédric Le Goater 2019-09-04 09:05:02 +02:00 committed by Peter Maydell
parent c4e1f0b483
commit 0d72c71702
1 changed files with 63 additions and 1 deletions

View File

@ -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);