[U-Boot] [PATCH 2/3] spi: Add support for the Aspeed ast2500 SPI controllers
Joel Stanley
joel at jms.id.au
Tue Sep 11 22:38:06 UTC 2018
On Mon, 10 Sep 2018 at 23:48, Cédric Le Goater <clg at kaod.org> wrote:
>
> The Aspeed AST2500 SoC comes with three static memory controllers, all
> with a similar interface :
>
> * Firmware SPI Memory Controller (FMC)
> . BMC firmware
> . 3 chip select pins (CE0 ~ CE2)
> . supports SPI type flash memory (CE0 ~ CE1)
> . CE2 can be of NOR type flash but this is not supported by the
> driver
>
> * SPI Flash Controller (SPI1 and SPI2)
> . host firmware
> . 2 chip select pins (CE0 ~ CE1)
>
> Each controller has a defined AHB window for its registers and another
> AHB window on which all the flash devices are mapped. Each device is
> assigned a memory range through a set of registers called the Segment
> Address Registers.
>
> Devices are controlled using two different modes: the USER command
> mode or the READ/WRITE command mode. When in USER command mode,
> accesses to the AHB window of the SPI flash device are translated into
> SPI command transfers. When in READ/WRITE command mode, the HW
> generates the SPI commands depending on the setting of the CE control
> register.
>
> The following driver supports the FMC and the SPI controllers with the
> attached SPI flash devices. When the controller is probed, the driver
> performs a read timing calibration using specific DMA control
> registers (FMC only). The driver's primary goal is to support the
> first device of the FMC controller on which reside U-Boot but it
> should support the other controllers also.
>
> The Aspeed FMC controller automatically detects at boot time if a
> flash device is in 4BYTE address mode and self configures to use the
> appropriate address width. This can be a problem for the U-Boot SPI
> Flash layer which only supports 3 byte addresses. In such a case, a
> warning is emitted and the address width is fixed when sent on the
> bus.
>
> Signed-off-by: Cédric Le Goater <clg at kaod.org>
Looks good. I compared some things against the data sheet, and read
through the driver. There were to small questions I had, so please
clear those up and add my:
Reviewed-by: Joel Stanley <joel at jms.id.au>
> +static u32 aspeed_spi_hclk_divisor(u32 hclk_rate, u32 max_hz)
> +{
> + /* HCLK/1 .. HCLK/16 */
> + const u8 hclk_divisors[] = {
> + 15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0
> + };
> +
> + u32 i;
> +
> + for (i = 0; i < ARRAY_SIZE(hclk_divisors); i++) {
> + if (max_hz >= (hclk_rate / (i + 1)))
We spoke offline about why the values in hclk_divisors are not used in
this calculation. I think we decided to change the name of
hclk_divisors to something like hclk_masks?
> + break;
> + }
> +
> + debug("hclk=%d required=%d divisor is %d (mask %x) speed=%d\n",
> + hclk_rate, max_hz, i + 1, hclk_divisors[i], hclk_rate / (i + 1));
> +
> + return hclk_divisors[i];
> +}
> +static u32 aspeed_spi_read_checksum(struct aspeed_spi_priv *priv, u8 div,
> + u8 delay)
> +{
> + /* TODO: the SPI controllers do not have the DMA registers.
> + * The algorithm is the same.
> + */
> + if (!priv->is_fmc) {
> + pr_warn("No timing calibration support for SPI controllers");
> + return 0xbadc0de;
> + }
> +
> + return aspeed_spi_fmc_checksum(priv, div, delay);
> +}
> +
> +static int aspeed_spi_timing_calibration(struct aspeed_spi_priv *priv)
> +{
> + /* HCLK/5 .. HCLK/1 */
> + const u8 hclk_divisors[] = { 13, 6, 14, 7, 15 };
> +
> + u32 timing_reg = 0;
> + u32 checksum, gold_checksum;
> + int i, j;
> +
> + debug("Read timing calibration :\n");
> +
> + /* Compute reference checksum at lowest freq HCLK/16 */
> + gold_checksum = aspeed_spi_read_checksum(priv, 0, 0);
> +
> + /*
> + * Set CE0 Control Register to FAST READ command mode. The
> + * HCLK divisor will be set through the DMA Control Register.
> + */
> + writel(CE_CTRL_CMD(0xB) | CE_CTRL_DUMMY(1) | CE_CTRL_FREADMODE,
> + &priv->regs->ce_ctrl[0]);
> +
> + /* Increase HCLK freq */
> + for (i = 0; i < ARRAY_SIZE(hclk_divisors); i++) {
> + u32 hdiv = 5 - i;
> + u32 hshift = (hdiv - 1) << 2;
> + bool pass = false;
> + u8 delay;
> +
> + if (priv->hclk_rate / hdiv > priv->max_hz) {
> + debug("skipping freq %ld\n", priv->hclk_rate / hdiv);
> + continue;
> + }
> +
> + /* Increase HCLK delays until read succeeds */
> + for (j = 0; j < 6; j++) {
6 here is the number of items in hclk_divisors?
> + /* Try first with a 4ns DI delay */
> + delay = 0x8 | j;
> + checksum = aspeed_spi_read_checksum(
> + priv, hclk_divisors[i], delay);
> + pass = (checksum == gold_checksum);
> + debug(" HCLK/%d, 4ns DI delay, %d HCLK delay : %s\n",
> + hdiv, j, pass ? "PASS" : "FAIL");
> +
> + /* Try again with more HCLK delays */
> + if (!pass)
> + continue;
> +
> + /* Try without the 4ns DI delay */
> + delay = j;
> + checksum = aspeed_spi_read_checksum(
> + priv, hclk_divisors[i], delay);
> + pass = (checksum == gold_checksum);
> + debug(" HCLK/%d, 0ns DI delay, %d HCLK delay : %s\n",
> + hdiv, j, pass ? "PASS" : "FAIL");
> +
> + /* All good for this freq */
> + if (pass)
> + break;
> + }
> +
> + if (pass) {
> + timing_reg &= ~(0xfu << hshift);
> + timing_reg |= delay << hshift;
> + }
> + }
> +
> + debug("Timing Register set to 0x%08x\n", timing_reg);
> + writel(timing_reg, &priv->regs->timings);
> +
> + /* Reset CE0 Control Register */
> + writel(0x0, &priv->regs->ce_ctrl[0]);
> + return 0;
> +}
> +
More information about the U-Boot
mailing list