Message ID | 20180831103816.13479-10-clg@kaod.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | aspeed: misc fixes and enhancements (SMC) | expand |
On 31 August 2018 at 11:38, Cédric Le Goater <clg@kaod.org> wrote: > 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 <clg@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. > + */ Not entirely sure what you have in mind by "on the field" here? Not used by Linux? thanks -- PMM
On 09/18/2018 08:54 PM, Peter Maydell wrote: > On 31 August 2018 at 11:38, Cédric Le Goater <clg@kaod.org> wrote: >> 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 <clg@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. >> + */ > > Not entirely sure what you have in mind by "on the field" here? > Not used by Linux? Today, the FMC/SPI calibration using the DMA registers is done by the U-Boot bootloader from the SDK. I don't know of any other implementation a part from the patchset I just sent adding a new Aspeed FMC/SPI driver in U-Boot, which is not merged yet. Linux uses a similar algorithm but without the DMA registers because they are not available on the non-FMC/SPI controllers. That was for the "on the field" survey. As for the comment statement it self, There is not much reason choosing a DMA Flash address pointing to another CS, because the Read Timing Compensation Register values apply to all CS on the SPI bus. So the model limitation is not a big problem. Nevertheless we could compute the CS from the DMA address and the segment registers if needed. Thanks, C. C.
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);
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 <clg@kaod.org> --- hw/ssi/aspeed_smc.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)