]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
spi: spi-xilinx: Added workaround when startup block is enabled
authorSai Krishna Potthuri <lakshmi.sai.krishna.potthuri@xilinx.com>
Wed, 27 Mar 2019 09:25:03 +0000 (14:55 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Thu, 28 Mar 2019 14:32:14 +0000 (15:32 +0100)
In AXI SPI when the startup block is enabled in the hardware
STARTUP block don't provide clock as soon as SPI provides command,
first command fails. So to overcome this issue added a workaround
to perform a dummy Read Id if startup block is enabled in the
hardware.

Signed-off-by: Sai Krishna Potthuri <lakshmi.sai.krishna.potthuri@xilinx.com>
Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
Acked-by: Naga Sureshkumar Relli <naga.sureshkumar.relli@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/spi/spi-xilinx.c

index 01e6a6cc8930a1bc67a147d2add7de5fb4f228b9..ae4e444a7f4ff996a869f79a2c61f48ff928f00d 100644 (file)
@@ -81,6 +81,9 @@
 /* Auto suspend timeout in milliseconds */
 #define SPI_AUTOSUSPEND_TIMEOUT                3000
 
+/* Command used for Dummy read Id */
+#define SPI_READ_ID            0x9F
+
 /**
  * struct xilinx_spi - This definition define spi driver instance
  * @regs:              virt. address of the control registers
@@ -329,6 +332,55 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
        return status;
 }
 
+/**
+ * xilinx_spi_startup_block - Perform a dummy read as a
+ * work around for the startup block issue in the spi controller.
+ * @xspi:      Pointer to the xilinx_spi structure
+ * @num_cs:    Number of chip selects used.
+ *
+ * Perform a dummy read if startup block is enabled in the
+ * spi controller.
+ *
+ * Return:     None
+ */
+static void xilinx_spi_startup_block(struct xilinx_spi *xspi, int num_cs)
+{
+       void __iomem *regs_base = xspi->regs;
+       u32 chip_sel, config_reg, status_reg;
+
+       /* Activate the chip select */
+       chip_sel = xspi->cs_inactive;
+       chip_sel ^= BIT(num_cs);
+       xspi->write_fn(chip_sel, regs_base + XSPI_SSR_OFFSET);
+
+       /* Write ReadId to the TXD register */
+       xspi->write_fn(SPI_READ_ID, regs_base + XSPI_TXD_OFFSET);
+       xspi->write_fn(0x0, regs_base + XSPI_TXD_OFFSET);
+       xspi->write_fn(0x0, regs_base + XSPI_TXD_OFFSET);
+
+       config_reg = xspi->read_fn(regs_base + XSPI_CR_OFFSET);
+       /* Enable master transaction  */
+       config_reg &= ~XSPI_CR_TRANS_INHIBIT;
+       xspi->write_fn(config_reg, regs_base + XSPI_CR_OFFSET);
+
+       status_reg = xspi->read_fn(regs_base + XSPI_SR_OFFSET);
+       while ((status_reg & XSPI_SR_TX_EMPTY_MASK) == 0)
+               status_reg = xspi->read_fn(regs_base + XSPI_SR_OFFSET);
+
+       /* Disable master transaction */
+       config_reg |= XSPI_CR_TRANS_INHIBIT;
+       xspi->write_fn(config_reg, regs_base + XSPI_CR_OFFSET);
+
+       /* Read the RXD Register */
+       status_reg = xspi->read_fn(regs_base + XSPI_SR_OFFSET);
+       while ((status_reg & XSPI_SR_RX_EMPTY_MASK) == 0) {
+               xspi->read_fn(regs_base + XSPI_RXD_OFFSET);
+               status_reg = xspi->read_fn(regs_base + XSPI_SR_OFFSET);
+       }
+
+       xspi_init_hw(xspi);
+}
+
 /**
  * xspi_setup_transfer - Configure SPI controller for specified
  *                      transfer
@@ -635,6 +687,7 @@ static int xilinx_spi_probe(struct platform_device *pdev)
        struct spi_master *master;
        struct device_node *nc;
        u32 tmp, rx_bus_width, fifo_size;
+       bool startup_block;
 
        of_property_read_u32(pdev->dev.of_node, "num-cs",
                                &num_cs);
@@ -646,6 +699,9 @@ static int xilinx_spi_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       startup_block = of_property_read_bool(pdev->dev.of_node,
+                                             "xlnx,startup-block");
+
        master = spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi));
        if (!master)
                return -ENODEV;
@@ -806,6 +862,16 @@ static int xilinx_spi_probe(struct platform_device *pdev)
                goto clk_unprepare_all;
        }
        xspi->cs_inactive = 0xffffffff;
+
+       /*
+        * This is the work around for the startup block issue in
+        * the spi controller. SPI clock is passing through STARTUP
+        * block to FLASH. STARTUP block don't provide clock as soon
+        * as QSPI provides command. So first command fails.
+        */
+       if (startup_block)
+               xilinx_spi_startup_block(xspi, num_cs);
+
        ret = spi_register_master(master);
        if (ret) {
                dev_err(&pdev->dev, "spi_register_master failed\n");