[U-Boot] [PATCH v4 01/16] spi: Add zynq qspi controller driver

Jagan Teki jteki at openedev.com
Thu Nov 5 08:56:56 CET 2015


On 5 November 2015 at 13:03, Hannes Schmelzer <hannes at schmelzer.or.at> wrote:
> Hi Jagan,
>
> did you take notice about that?
> Maybe i've not seen your answer.

I will get my hardware next week, sure we can discuss this.

>
>
> On 15.10.2015 10:39, Hannes Schmelzer wrote:
>>
>>
>> Hi Jagan,
>>
>> during bringing up QSPI within SPL on my ZYNQ ZC702 board i made some
>> review of your code.
>> Have a look.
>>
>> On 01.09.2015 08:11, Jagan Teki wrote:
>>>
>>> Added zynq qspi controller driver for Xilinx Zynq APSOC,
>>> this driver is driver-model driven with devicetree support.
>>
>> (...)
>>>
>>> +
>>> +/* zynq qspi priv */
>>> +struct zynq_qspi_priv {
>>> +    struct zynq_qspi_regs *regs;
>>> +    u8 cs;
>>> +    u8 mode;
>>> +    u8 fifo_depth;
>>> +    u32 freq;        /* required frequency */
>>> +    const void *tx_buf;
>>> +    void *rx_buf;
>>> +    unsigned len;
>>> +    int bytes_to_transfer;
>>> +    int bytes_to_receive;
>>> +    unsigned int is_inst;
>>> +    unsigned cs_change:1;
>>> +};
>>
>> why not use "u32" for len ?
>>>
>>> +static void zynq_qspi_init_hw(struct zynq_qspi_priv *priv)
>>> +{
>>> +    struct zynq_qspi_regs *regs = priv->regs;
>>> +    u32 confr;
>>
>> During bring up this driver within SPL i've figured out, that it is very
>> important to do a clear reset to the QSPI unit.
>> Initially the ROM codes fetches our SPL-binary from the flash into the OCM
>> and executes.
>> We don't know at this point how ROM-code has left the QSPI unit, and have
>> to reset the Unit using responsible Bits in sclr area.
>> Otherwise we can see strange behaviours like hang on reading RxFIFO
>> sometimes and sometimes not.
>>>
>>> +
>>> +    /* Disable QSPI */
>>> +    writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
>>> +
>>> +    /* Disable Interrupts */
>>> +    writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->idr);
>>> +
>>> +    /* Clear the TX and RX threshold reg */
>>> +    writel(ZYNQ_QSPI_TXFIFO_THRESHOLD, &regs->txftr);
>>> +    writel(ZYNQ_QSPI_RXFIFO_THRESHOLD, &regs->rxftr);
>>> +
>>> +    /* Clear the RX FIFO */
>>> +    while (readl(&regs->isr) & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
>>> +        readl(&regs->drxr);
>>> +
>>> +    /* Clear Interrupts */
>>> +    writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->isr);
>>> +
>>> +    /* Manual slave select and Auto start */
>>> +    confr = readl(&regs->cr);
>>> +    confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
>>> +    confr |= ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK |
>>> +        ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
>>> +        ZYNQ_QSPI_CR_MSTREN_MASK;
>>> +    writel(confr, &regs->cr);
>>> +
>>> +    /* Enable SPI */
>>> +    writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
>>> +}
>>
>> (...)
>>>
>>> +
>>> +/*
>>> + * zynq_qspi_read_data - Copy data to RX buffer
>>> + * @zqspi:    Pointer to the zynq_qspi structure
>>> + * @data:    The 32 bit variable where data is stored
>>> + * @size:    Number of bytes to be copied from data to RX buffer
>>> + */
>>> +static void zynq_qspi_read_data(struct zynq_qspi_priv *priv, u32 data,
>>> u8 size)
>>> +{
>>> +    u8 byte3;
>>> +
>>> +    debug("%s: data 0x%04x rx_buf addr: 0x%08x size %d\n", __func__ ,
>>> +          data, (unsigned)(priv->rx_buf), size);
>>
>> use 0x%p instead 0x%08x to display pointer addresses, with the advantage
>> that no cast is necessary.
>>>
>>> +
>>> +    if (priv->rx_buf) {
>>> +        switch (size) {
>>> +        case 1:
>>> +            *((u8 *)priv->rx_buf) = data;
>>> +            priv->rx_buf += 1;
>>> +            break;
>>> +        case 2:
>>> +            *((u16 *)priv->rx_buf) = data;
>>> +            priv->rx_buf += 2;
>>> +            break;
>>> +        case 3:
>>> +            *((u16 *)priv->rx_buf) = data;
>>> +            priv->rx_buf += 2;
>>> +            byte3 = (u8)(data >> 16);
>>> +            *((u8 *)priv->rx_buf) = byte3;
>>> +            priv->rx_buf += 1;
>>> +            break;
>>> +        case 4:
>>> +            /* Can not assume word aligned buffer */
>>> +            memcpy(priv->rx_buf, &data, size);
>>> +            priv->rx_buf += 4;
>>> +            break;
>>> +        default:
>>> +            /* This will never execute */
>>> +            break;
>>> +        }
>>> +    }
>>> +    priv->bytes_to_receive -= size;
>>> +    if (priv->bytes_to_receive < 0)
>>> +        priv->bytes_to_receive = 0;
>>> +}
>>
>> wouldn't it be good enough using always "memcpy" ?
>> maybe we can drop this function completely ?
>>>
>>> +
>>> +/*
>>> + * zynq_qspi_write_data - Copy data from TX buffer
>>> + * @zqspi:    Pointer to the zynq_qspi structure
>>> + * @data:    Pointer to the 32 bit variable where data is to be copied
>>> + * @size:    Number of bytes to be copied from TX buffer to data
>>> + */
>>> +static void zynq_qspi_write_data(struct  zynq_qspi_priv *priv,
>>> +        u32 *data, u8 size)
>>> +{
>>> +    if (priv->tx_buf) {
>>> +        switch (size) {
>>> +        case 1:
>>> +            *data = *((u8 *)priv->tx_buf);
>>> +            priv->tx_buf += 1;
>>> +            *data |= 0xFFFFFF00;
>>> +            break;
>>> +        case 2:
>>> +            *data = *((u16 *)priv->tx_buf);
>>> +            priv->tx_buf += 2;
>>> +            *data |= 0xFFFF0000;
>>> +            break;
>>> +        case 3:
>>> +            *data = *((u16 *)priv->tx_buf);
>>> +            priv->tx_buf += 2;
>>> +            *data |= (*((u8 *)priv->tx_buf) << 16);
>>> +            priv->tx_buf += 1;
>>> +            *data |= 0xFF000000;
>>> +            break;
>>> +        case 4:
>>> +            /* Can not assume word aligned buffer */
>>> +            memcpy(data, priv->tx_buf, size);
>>> +            priv->tx_buf += 4;
>>> +            break;
>>> +        default:
>>> +            /* This will never execute */
>>> +            break;
>>> +        }
>>> +    } else {
>>> +        *data = 0;
>>> +    }
>>> +
>>> +    debug("%s: data 0x%08x tx_buf addr: 0x%08x size %d\n", __func__,
>>> +          *data, (u32)priv->tx_buf, size);
>>> +
>>> +    priv->bytes_to_transfer -= size;
>>> +    if (priv->bytes_to_transfer < 0)
>>> +        priv->bytes_to_transfer = 0;
>>> +}
>>
>> maybe same thing here as above during "zynq_qspi_read_data".
>>>
>>> +
>>> +static void zynq_qspi_chipselect(struct  zynq_qspi_priv *priv, int
>>> is_on)
>>> +{
>>> +    u32 confr;
>>> +    struct zynq_qspi_regs *regs = priv->regs;
>>> +
>>> +    confr = readl(&regs->cr);
>>> +
>>> +    if (is_on) {
>>> +        /* Select the slave */
>>> +        confr &= ~ZYNQ_QSPI_CR_SS_MASK;
>>> +        confr |= (~(1 << priv->cs) << ZYNQ_QSPI_CR_SS_SHIFT) &
>>> +                    ZYNQ_QSPI_CR_SS_MASK;
>>
>> in TRM (UG585, V1.1) pg. 1525, these bits 13..11 are described as
>> reserved, only bit 10 (if priv->cs == 0) is allowed to write.
>> For my reading we actually have only one CS.
>>
>> So:
>>
>> +    if (is_on) {
>> +        /* Select the slave */
>> +        confr &= ~(1 << ZYNQ_QSPI_CR_SS_SHIFT);
>> +    }
>> would be enough.
>>
>> Or do we use here some undocumented feature ?
>>>
>>> +    } else
>>> +        /* Deselect the slave */
>>> +        confr |= ZYNQ_QSPI_CR_SS_MASK;
>>> +
>>> +    writel(confr, &regs->cr);
>>> +}
>>> +
>>> +/*
>>> + * zynq_qspi_fill_tx_fifo - Fills the TX FIFO with as many bytes as
>>> possible
>>> + * @zqspi:    Pointer to the zynq_qspi structure
>>> + */
>>> +static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32
>>> size)
>>> +{
>>> +    u32 data = 0;
>>> +    u32 fifocount = 0;
>>> +    unsigned len, offset;
>>
>> why not using u32 instead unsigned ?
>>>
>>> +    struct zynq_qspi_regs *regs = priv->regs;
>>> +    static const unsigned offsets[4] = {
>>> +        ZYNQ_QSPI_TXD_00_00_OFFSET, ZYNQ_QSPI_TXD_00_01_OFFSET,
>>> +        ZYNQ_QSPI_TXD_00_10_OFFSET, ZYNQ_QSPI_TXD_00_11_OFFSET };
>>> +
>>> +    while ((fifocount < size) &&
>>> +            (priv->bytes_to_transfer > 0)) {
>>> +        if (priv->bytes_to_transfer >= 4) {
>>> +            if (priv->tx_buf) {
>>> +                memcpy(&data, priv->tx_buf, 4);
>>> +                priv->tx_buf += 4;
>>> +            } else {
>>> +                data = 0;
>>> +            }
>>> +            writel(data, &regs->txd0r);
>>> +            priv->bytes_to_transfer -= 4;
>>> +            fifocount++;
>>> +        } else {
>>> +            /* Write TXD1, TXD2, TXD3 only if TxFIFO is empty. */
>>> +            if (!(readl(&regs->isr)
>>> +                    & ZYNQ_QSPI_IXR_TXOW_MASK) &&
>>> +                    !priv->rx_buf)
>>> +                return;
>>
>> would suggest "continue" instead "return", otherwise we may loose data in
>> case of busy TX-FIFO.
>>>
>>> +            len = priv->bytes_to_transfer;
>>> +            zynq_qspi_write_data(priv, &data, len);
>>> +            offset = (priv->rx_buf) ? offsets[0] : offsets[len];
>>> +            writel(data, &regs->cr + (offset / 4));
>>
>> during review i understood my question from yesterday :-) sorry for noise.
>>>
>>> +        }
>>> +    }
>>> +}
>>> +
>>> +/*
>>> + * zynq_qspi_irq_poll - Interrupt service routine of the QSPI controller
>>> + * @zqspi:    Pointer to the zynq_qspi structure
>>> + *
>>> + * This function handles TX empty and Mode Fault interrupts only.
>>> + * On TX empty interrupt this function reads the received data from RX
>>> FIFO and
>>> + * fills the TX FIFO if there is any data remaining to be transferred.
>>> + * On Mode Fault interrupt this function indicates that transfer is
>>> completed,
>>> + * the SPI subsystem will identify the error as the remaining bytes to
>>> be
>>> + * transferred is non-zero.
>>> + *
>>> + * returns:    0 for poll timeout
>>> + *        1 transfer operation complete
>>> + */
>>> +static int zynq_qspi_irq_poll(struct zynq_qspi_priv *priv)
>>> +{
>>> +    struct zynq_qspi_regs *regs = priv->regs;
>>> +    u32 rxindex = 0;
>>> +    u32 rxcount;
>>> +    u32 status, timeout;
>>> +
>>> +    /* Poll until any of the interrupt status bits are set */
>>> +    timeout = get_timer(0);
>>> +    do {
>>> +        status = readl(&regs->isr);
>>> +    } while ((status == 0) &&
>>> +        (get_timer(timeout) < CONFIG_SYS_ZYNQ_QSPI_WAIT));
>>> +
>>> +    if (status == 0) {
>>> +        printf("zynq_qspi_irq_poll: Timeout!\n");
>>> +        return -ETIMEDOUT;
>>> +    }
>>> +
>>> +    writel(status, &regs->isr);
>>> +
>>> +    /* Disable all interrupts */
>>> +    writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->idr);
>>> +    if ((status & ZYNQ_QSPI_IXR_TXOW_MASK) ||
>>> +        (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)) {
>>> +        /*
>>> +         * This bit is set when Tx FIFO has < THRESHOLD entries. We have
>>> +         * the THRESHOLD value set to 1, so this bit indicates Tx FIFO
>>> +         * is empty
>>> +         */
>>> +        rxcount = priv->bytes_to_receive - priv->bytes_to_transfer;
>>> +        rxcount = (rxcount % 4) ? ((rxcount/4)+1) : (rxcount/4);
>>> +        while ((rxindex < rxcount) &&
>>> +                (rxindex < ZYNQ_QSPI_RXFIFO_THRESHOLD)) {
>>> +            /* Read out the data from the RX FIFO */
>>> +            u32 data;
>>> +            data = readl(&regs->drxr);
>>> +
>>> +            if (priv->bytes_to_receive >= 4) {
>>> +                if (priv->rx_buf) {
>>> +                    memcpy(priv->rx_buf, &data, 4);
>>> +                    priv->rx_buf += 4;
>>> +                }
>>> +                priv->bytes_to_receive -= 4;
>>> +            } else {
>>> +                zynq_qspi_read_data(priv, data,
>>> +                            priv->bytes_to_receive);
>>> +            }
>>> +            rxindex++;
>>> +        }
>>> +
>>> +        if (priv->bytes_to_transfer) {
>>> +            /* There is more data to send */
>>> +            zynq_qspi_fill_tx_fifo(priv,
>>> +                           ZYNQ_QSPI_RXFIFO_THRESHOLD);
>>> +
>>> +            writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->ier);
>>> +        } else {
>>> +            /*
>>> +             * If transfer and receive is completed then only send
>>> +             * complete signal
>>> +             */
>>> +            if (!priv->bytes_to_receive) {
>>> +                /* return operation complete */
>>> +                writel(ZYNQ_QSPI_IXR_ALL_MASK,
>>> +                       &regs->idr);
>>> +                return 1;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +/*
>>> + * zynq_qspi_start_transfer - Initiates the QSPI transfer
>>> + * @qspi:    Pointer to the spi_device structure
>>> + * @transfer:    Pointer to the spi_transfer structure which provide
>>> information
>>> + *        about next transfer parameters
>>> + *
>>> + * This function fills the TX FIFO, starts the QSPI transfer, and waits
>>> for the
>>> + * transfer to be completed.
>>> + *
>>> + * returns:    Number of bytes transferred in the last transfer
>>> + */
>>> +static int zynq_qspi_start_transfer(struct zynq_qspi_priv *priv)
>>> +{
>>> +    u32 data = 0;
>>> +    struct zynq_qspi_regs *regs = priv->regs;
>>> +
>>> +    debug("%s: qspi: 0x%08x transfer: 0x%08x len: %d\n", __func__,
>>> +          (u32)priv, (u32)priv, priv->len);
>>
>> use 0x%p for displaying pointer addresses.
>>>
>>> +
>>> +    priv->bytes_to_transfer = priv->len;
>>> +    priv->bytes_to_receive = priv->len;
>>> +
>>> +    if (priv->len < 4)
>>> +        zynq_qspi_fill_tx_fifo(priv, priv->len);
>>> +    else
>>> +        zynq_qspi_fill_tx_fifo(priv, priv->fifo_depth);
>>
>> i think we can call the function always with "priv->fifo_depth",
>> because the decision if the "size" parameter is used or not is there made
>> based on priv->bytes_to_transfer.
>>>
>>> +
>>> +    writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->ier);
>>> +    /* Start the transfer by enabling manual start bit */
>>
>> this comment is confusing since we are in auto-mode with manually forced
>> chipselect.
>>>
>>> +
>>> +    /* wait for completion */
>>> +    do {
>>> +        data = zynq_qspi_irq_poll(priv);
>>> +    } while (data == 0);
>>> +
>>> +    return (priv->len) - (priv->bytes_to_transfer);
>>> +}
>>
>> best regards,
>> Hannes
>>
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot



-- 
Jagan | openedev.


More information about the U-Boot mailing list