[U-Boot] [PATCH 2/3] spi: Add support for the Aspeed ast2500 SPI controllers

Cédric Le Goater clg at kaod.org
Wed Sep 12 06:03:28 UTC 2018


On 09/12/2018 12:38 AM, Joel Stanley wrote:
> 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?

yes. I have changed the name everywhere as it makes more sense.  
 
>> +                       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?

no. these are counts of HCLK cycles before input, possible values are [0-5]. 
I will add a define to clarify '6'
 
> 
>> +                       /* 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