public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
From: Hannes Schmelzer <hannes@schmelzer.or.at>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v4 01/16] spi: Add zynq qspi controller driver
Date: Thu, 05 Nov 2015 08:33:42 +0100	[thread overview]
Message-ID: <563B0656.9000308@schmelzer.or.at> (raw)
In-Reply-To: <561F6625.30901@schmelzer.or.at>

Hi Jagan,

did you take notice about that?
Maybe i've not seen your answer.

regards,
Hannes


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
>

  reply	other threads:[~2015-11-05  7:33 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-01  6:11 [U-Boot] [PATCH v4 00/16] spi: zynq qspi support Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 01/16] spi: Add zynq qspi controller driver Jagan Teki
2015-09-03  7:26   ` Siva Durga Prasad Paladugu
2015-09-03  8:07     ` Jagan Teki
2015-09-03  8:38       ` Siva Durga Prasad Paladugu
2015-09-03  9:47         ` Jagan Teki
2015-09-04 11:38       ` Siva Durga Prasad Paladugu
2015-10-13 12:22   ` Hannes Schmelzer
2015-10-15  8:39   ` Hannes Schmelzer
2015-11-05  7:33     ` Hannes Schmelzer [this message]
2015-11-05  7:56       ` Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 02/16] dts: zynq: Add zynq qspi controller nodes Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 03/16] doc: device-tree-bindings: spi: Add zynq qspi info Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 04/16] dts: microzed: Enable zynq qspi controller node Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 05/16] dts: zc702: " Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 06/16] dts: zc706: " Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 07/16] dts: zc770-xm010: " Jagan Teki
2015-09-01  8:22   ` Michal Simek
2015-09-01  8:43     ` Jagan Teki
2015-09-01  9:00       ` Michal Simek
2015-09-01  6:11 ` [U-Boot] [PATCH v4 08/16] dts: zed: " Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 09/16] configs: Enable legacy SPI flash interface support Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 10/16] zynq-common: Enable zynq qspi controller support Jagan Teki
2015-09-01  8:19   ` Michal Simek
2015-09-01  6:11 ` [U-Boot] [PATCH v4 11/16] zynq-common: Enable Bank/Extended address register support Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 12/16] configs: zynq: Enable zynq qspi controller Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 13/16] spi: Kconfig: Add Zynq QSPI controller entry Jagan Teki
2015-09-01  8:18   ` Michal Simek
2015-09-01  6:11 ` [U-Boot] [PATCH v4 14/16] spi: zynq_spi: Add config reg shift named macros Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 15/16] spi: zynq_spi: Rename baudrate divisor mask name Jagan Teki
2015-09-01  6:11 ` [U-Boot] [PATCH v4 16/16] spi: zynq_spi: Store cs value into private data Jagan Teki
2015-09-01  6:14 ` [U-Boot] [PATCH v4 00/16] spi: zynq qspi support Jagan Teki
2015-09-01  8:23   ` Michal Simek
2015-09-04 12:33     ` Jagan Teki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=563B0656.9000308@schmelzer.or.at \
    --to=hannes@schmelzer.or.at \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox