Both DW SSI APB and DWC SSI devices have an optional XIP mode. When the
xip_en signal is asserted, reads (and writes if SSIC_XIP_WRITE_REG_EN is
set) are mapped to SPI transfers.

If SSIC_CONCURRENT_XIP_EN is disabled, then XIP transfers are controlled
using SPI_CTRLR0. However, if SSIC_CONCURRENT_XIP_EN is enabled, then XIP
transfers can occur concurrently (first-come-first-serve) with non-XIP
transfers. To facilitate this, a separate XIP_CTRL register is used for
configuration which would otherwise by done using CTRLR0 and SPI_CTRLR0.

Signed-off-by: Sean Anderson <sean...@gmail.com>
---

 drivers/spi/designware_spi.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index d7510646e7..c41c5b4982 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -202,6 +202,7 @@
 struct dw_spi_plat {
        s32 frequency;          /* Default clock frequency, -1 for none */
        void __iomem *regs;
+       fdt_size_t regs_size;
 };
 
 struct dw_spi_priv {
@@ -210,12 +211,15 @@ struct dw_spi_priv {
        struct gpio_desc cs_gpio;       /* External chip-select gpio */
 
        void __iomem *regs;
+       fdt_size_t regs_size;
 /* DW SPI capabilities */
 #define DW_SPI_CAP_CS_OVERRIDE         BIT(0) /* Unimplemented */
 #define DW_SPI_CAP_KEEMBAY_MST         BIT(1) /* Unimplemented */
 #define DW_SPI_CAP_DWC_SSI             BIT(2)
 #define DW_SPI_CAP_DFS32               BIT(3)
 #define DW_SPI_CAP_ENHANCED            BIT(4)
+#define DW_SPI_CAP_XIP                 BIT(5)
+#define DW_SPI_CAP_XIP_CONCURRENT      BIT(6)
        unsigned long caps;
        unsigned long bus_clk_rate;
        unsigned int freq;              /* Default frequency */
@@ -322,11 +326,13 @@ static int request_gpio_cs(struct udevice *bus)
 
 static int dw_spi_of_to_plat(struct udevice *bus)
 {
+       fdt_addr_t regs;
        struct dw_spi_plat *plat = dev_get_plat(bus);
 
-       plat->regs = dev_read_addr_ptr(bus);
-       if (!plat->regs)
+       regs = dev_read_addr_size_index(bus, 0, &plat->regs_size);
+       if (regs == FDT_ADDR_T_NONE)
                return -EINVAL;
+       plat->regs = (void *)regs;
 
        /* Use 500KHz as a suitable default */
        plat->frequency = dev_read_u32_default(bus, "spi-max-frequency",
@@ -375,6 +381,19 @@ static void spi_hw_init(struct udevice *bus, struct 
dw_spi_priv *priv)
                priv->caps |= DW_SPI_CAP_ENHANCED;
        }
 
+       /*
+        * DWC_SPI always has this register with SSIC_XIP_EN. There is no way
+        * to detect XIP for DW APB SSI
+        */
+       dw_write(priv, DW_SPI_XIP_INCR_INST, 0xffffffff);
+       if (dw_read(priv, DW_SPI_XIP_INCR_INST))
+               priv->caps |= DW_SPI_CAP_XIP;
+
+       /* Exists with SSIC_CONCURRENT_XIP_EN */
+       dw_write(priv, DW_SPI_XIP_CTRL, 0xffffffff);
+       if (dw_read(priv, DW_SPI_XIP_CTRL))
+               priv->caps |= DW_SPI_CAP_XIP_CONCURRENT;
+
        dw_write(priv, DW_SPI_SSIENR, 1);
 
        /*
@@ -469,6 +488,7 @@ static int dw_spi_probe(struct udevice *bus)
        u32 version;
 
        priv->regs = plat->regs;
+       priv->regs_size = plat->regs_size;
        priv->freq = plat->frequency;
 
        ret = dw_spi_get_clk(bus, &priv->bus_clk_rate);
@@ -1022,7 +1042,10 @@ static const struct udevice_id dw_spi_ids[] = {
         */
        { .compatible = "altr,socfpga-spi" },
        { .compatible = "altr,socfpga-arria10-spi" },
-       { .compatible = "canaan,kendryte-k210-spi" },
+       {
+               .compatible = "canaan,kendryte-k210-spi",
+               .data = DW_SPI_CAP_XIP,
+       },
        {
                .compatible = "canaan,kendryte-k210-ssi",
                .data = DW_SPI_CAP_DWC_SSI,
-- 
2.29.2

Reply via email to