This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM4378: QSPI set prescaler API issue

Part Number: AM4378

The code of QSPISetPreScaler procedure is not right, it doesn't disable clock before set prescaler. 

C:\ti\pdk_am437x_1_0_11\packages\ti\starterware\dal\qspi.c


void QSPISetPreScaler(uint32_t baseAddr, uint32_t clkDividerVal)
{
/* Read the value of Clock control register */
uint32_t regVal = HW_RD_REG32(baseAddr + QSPI_CLOCK_CNTRL_REG);
/* wait for QSPI to be idle */
while (TRUE == QSPIIsBusy(baseAddr));

/* Set the QSPI clock divider bit field value*/
HW_SET_FIELD(regVal, QSPI_CLOCK_CNTRL_REG_DCLK_DIV, clkDividerVal);

/* Enable the QSPI data clock */
HW_SET_FIELD(regVal, QSPI_CLOCK_CNTRL_REG_CLKEN,
QSPI_CLOCK_CNTRL_REG_CLKEN_DCLOCK_ON);
/* Set the value of QSPI clock control register */
HW_WR_REG32(baseAddr + QSPI_CLOCK_CNTRL_REG, regVal);
}

But the code in this file is correct: C:\ti\pdk_am437x_1_0_11\packages\ti\csl\src\ip\qspi\V1\priv\qspi.c

 void QSPISetPreScaler(uint32_t baseAddr, uint32_t clkDividerVal)

{

    /* Read the value of Clock control register */

    uint32_t regVal = HW_RD_REG32(baseAddr + QSPI_CLOCK_CNTRL_REG);

    /* wait for QSPI to be idle */

    while (TRUE == QSPIIsBusy(baseAddr));

 

    /* Set the QSPI clock divider bit field value*/

    HW_SET_FIELD(regVal, QSPI_CLOCK_CNTRL_REG_DCLK_DIV, clkDividerVal);

 

    /* Enable the QSPI data clock */

    HW_SET_FIELD(regVal, QSPI_CLOCK_CNTRL_REG_CLKEN,

                QSPI_CLOCK_CNTRL_REG_CLKEN_DCLOCK_ON);

    /* Set the value of QSPI clock control register */

    HW_WR_REG32(baseAddr + QSPI_CLOCK_CNTRL_REG, regVal);

}

  • The RTOS team have been notified. They will respond here.
  • Tony,

    I looked at the C:\ti\pdk_am437x_1_0_11\packages\ti\csl\src\ip\qspi\src_files_qspi.mk and confirmed that V1 is for Sitara device.

    Then in C:\ti\pdk_am437x_1_0_11\packages\ti\csl\src\ip\qspi\V1\priv\qspi.c:
    void QSPISetPreScaler(uint32_t baseAddr, uint32_t clkDividerVal)
    {
    /* Read the value of Clock control register */
    uint32_t regVal =
    HW_RD_REG32(baseAddr + QSPI_SPI_CLOCK_CNTRL_REG);
    /* wait for QSPI to be idle */
    QSPIWaitIdle(baseAddr);

    /* turn off QSPI data clock */
    HW_SET_FIELD32(regVal, QSPI_SPI_CLOCK_CNTRL_REG_CLKEN,
    QSPI_SPI_CLOCK_CNTRL_REG_CLKEN_DCLOCK_OFF);
    /* Set the value of QSPI clock control register */
    HW_WR_REG32(baseAddr + QSPI_SPI_CLOCK_CNTRL_REG, regVal);

    /* Set the QSPI clock divider bit field value*/
    HW_SET_FIELD32(regVal, QSPI_SPI_CLOCK_CNTRL_REG_DCLK_DIV,
    clkDividerVal);
    /* Set the value of QSPI clock control register */
    HW_WR_REG32(baseAddr + QSPI_SPI_CLOCK_CNTRL_REG, regVal);

    /* Enable the QSPI data clock */
    HW_SET_FIELD32(regVal, QSPI_SPI_CLOCK_CNTRL_REG_CLKEN,
    QSPI_SPI_CLOCK_CNTRL_REG_CLKEN_DCLOCK_ON);
    /* Set the value of QSPI clock control register */
    HW_WR_REG32(baseAddr + QSPI_SPI_CLOCK_CNTRL_REG, regVal);
    }

    It has the sequence to turn off QSPI CLOCK, set the divider value, then turn on the QSPI CLOCK. The same sequence is not there in the Starterware DAL code.

    I further looked at AM4378 TRM, section

    27.3.1.4 SPI Clock Generator
    The SPI clock generator uses the QSPI_FCLK clock as an input, and generates the qspi1_sclk, which is a
    divided version of the QSPI_FCLK clock. The divide ratio is a 16-bit value configured through the
    QSPI_CLOCK_CNTRL_REG[15:0] DCLK_DIV bit field and thus provides a division factor in a range from
    1 to 65536. The QSPI_FCLK clock is divided by the DCLK_DIV value + 1 to provide the qspi1_sclk clock.
    When DCLK_DIV = 0x0 the QSPI_FCLK clock equals the DCLK clock. The value in the DCLK_DIV bit
    field applies only when the QSPI_CLOCK_CNTRL_REG[31] CLKEN bit is set to 0x1. Figure 27-3 shows
    the SPI_CLKGEN block.
    If the CLKEN bit is 0x0 the command specified in the QSPI_CMD_REG[18:16] CMD bit field is not
    executed and the QSPI_STS_REG[0] BUSY bit is not set. The command is executed only if the CLKEN
    bit is 0x1 before write to the CMD bit field.

    27.4.1.8 QSPI_CLOCK_CNTRL_REG Register (offset = 40h) [reset = 0h]
    QSPI_CLOCK_CNTRL_REG is shown in Figure 27-12 and described in Table 27-15.
    SPI Clock Control (SPICC) Register. SPICC controls the SPI clock generation. The SPICC controls the
    SPI clock generation. The input for clock division is the input SPI_CLK signal. The output clock will be
    divided by DCLK_DIV+1 to provide the output SPI interface clock as well as controlling the main portion of
    the design. Note that loading a value or 0 input DCLK_DIV will force the input SPI_CLK to be used directly
    for the SPI interface clock. The value in DCLK_DIV is only loaded when CLKEN transitions from a 0 to 1
    state. This register can only be written to when the SPI is not busy, as defined by SPISR[0].

    So a transition of CLKEN from 0 to 1 is needed for this divider to take effects.

    I agree this is a bug in Starterware. P-SDK RTOS uses the QSPI driver and CSL lib/code, so it doesn't have this issue. The Starterware folder is ported from old Sitara release for reference. It may take times to update or not maintained at all.

    I will open a ticket for this. As a workaround, please either uses the CSL code or patch the DAL code. Thanks!

    Regards, Eric