This adds support for the Sparx5 SoC in the spi-dw-mchp SPI controller.

Reviewed-by: Alexandre Belloni <[email protected]>
Signed-off-by: Lars Povlsen <[email protected]>
---
 drivers/spi/spi-dw-mchp.c | 211 ++++++++++++++++++++++++++++++++++----
 1 file changed, 189 insertions(+), 22 deletions(-)

diff --git a/drivers/spi/spi-dw-mchp.c b/drivers/spi/spi-dw-mchp.c
index 0828a7616d9ab..3abdd44a550ea 100644
--- a/drivers/spi/spi-dw-mchp.c
+++ b/drivers/spi/spi-dw-mchp.c
@@ -28,21 +28,22 @@

 #define MAX_CS         4

-#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL      0x24
-#define OCELOT_IF_SI_OWNER_OFFSET              4
-#define JAGUAR2_IF_SI_OWNER_OFFSET             6
 #define MSCC_IF_SI_OWNER_MASK                  GENMASK(1, 0)
 #define MSCC_IF_SI_OWNER_SISL                  0
 #define MSCC_IF_SI_OWNER_SIBM                  1
 #define MSCC_IF_SI_OWNER_SIMC                  2

 #define MSCC_SPI_MST_SW_MODE                   0x14
-#define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE  BIT(13)
-#define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x)      (x << 5)

 struct dw_spi_mchp_props {
        const char *syscon_name;
-       u32 si_owner_bit;
+       u32 general_ctrl_off;
+       u32 si_owner_bit, si_owner2_bit;
+       u32 pinctrl_bit_off;
+       u32 cs_bit_off;
+       u32 ss_force_ena_off;
+       u32 ss_force_val_off;
+       u32 bootmaster_cs;
 };

 struct dw_spi_mchp {
@@ -53,44 +54,176 @@ struct dw_spi_mchp {
        void __iomem                    *spi_mst;
        const struct dw_spi_mchp_props  *props;
        u32                             gen_owner;
+       u32                             if2mask;
 };

 static const struct dw_spi_mchp_props dw_spi_mchp_props_ocelot = {
        .syscon_name            = "mscc,ocelot-cpu-syscon",
+       .general_ctrl_off       = 0x24,
        .si_owner_bit           = 4,
+       .pinctrl_bit_off        = 13,
+       .cs_bit_off             = 5,
+       .bootmaster_cs          = 0,
 };

 static const struct dw_spi_mchp_props dw_spi_mchp_props_jaguar2 = {
        .syscon_name            = "mscc,ocelot-cpu-syscon",
+       .general_ctrl_off       = 0x24,
        .si_owner_bit           = 6,
+       .pinctrl_bit_off        = 13,
+       .cs_bit_off             = 5,
+       .bootmaster_cs          = 0,
+};
+
+static const struct dw_spi_mchp_props dw_spi_mchp_props_sparx5 = {
+       .syscon_name            = "microchip,sparx5-cpu-syscon",
+       .general_ctrl_off       = 0x88,
+       .si_owner_bit           = 6,
+       .si_owner2_bit          = 4,
+       .ss_force_ena_off       = 0xa4,
+       .ss_force_val_off       = 0xa8,
+       .bootmaster_cs          = 0,
 };

 /*
- * The Designware SPI controller (referred to as master in the documentation)
- * automatically deasserts chip select when the tx fifo is empty. The chip
- * selects then needs to be either driven as GPIOs or, for the first 4 using 
the
- * the SPI boot controller registers. the final chip select is an OR gate
- * between the Designware SPI controller and the SPI boot controller.
+ * Set the owner of the SPI interface
  */
-static void dw_spi_mchp_set_cs(struct spi_device *spi, bool enable)
+static void dw_spi_mchp_set_owner(struct dw_spi_mchp *dwsmchp,
+                                 const struct dw_spi_mchp_props *props,
+                                 u8 owner, u8 owner2)
+{
+       u32 val, msk;
+
+       val = (owner << props->si_owner_bit);
+       msk = (MSCC_IF_SI_OWNER_MASK << props->si_owner_bit);
+       if (props->si_owner2_bit) {
+               val |= owner2 << props->si_owner2_bit;
+               msk |= (MSCC_IF_SI_OWNER_MASK << props->si_owner2_bit);
+       }
+       if (dwsmchp->gen_owner != val) {
+               regmap_update_bits(dwsmchp->syscon, props->general_ctrl_off,
+                                  msk, val);
+               dwsmchp->gen_owner = val;
+       }
+}
+
+static void dw_spi_mchp_set_cs_owner(struct dw_spi_mchp *dwsmchp,
+                                    const struct dw_spi_mchp_props *props,
+                                    u8 cs, u8 owner)
 {
+       u8 dummy = (owner == MSCC_IF_SI_OWNER_SIBM ?
+                   MSCC_IF_SI_OWNER_SIMC : MSCC_IF_SI_OWNER_SIBM);
+       if (props->si_owner2_bit && (dwsmchp->if2mask & BIT(cs))) {
+               /* SPI2 */
+               dw_spi_mchp_set_owner(dwsmchp, props, dummy, owner);
+       } else {
+               /* SPI1 */
+               dw_spi_mchp_set_owner(dwsmchp, props, owner, dummy);
+       }
+}
+
+/*
+ * The Designware SPI controller (referred to as master in the
+ * documentation) automatically deasserts chip select when the tx fifo
+ * is empty. The chip selects then needs to be either driven as GPIOs
+ * or, for the first 4 using the the SPI boot controller
+ * registers. the final chip select is an OR gate between the
+ * Designware SPI controller and the SPI boot controller.  nselect is
+ * an active low signal
+ */
+static void dw_spi_mchp_set_cs(struct spi_device *spi, bool nEnable)
+{
+       bool enable = !nEnable; /* This keeps changing in the API... */
        struct dw_spi *dws = spi_master_get_devdata(spi->master);
        struct dw_spi_mchp *dwsmchp = container_of(dws, struct dw_spi_mchp,
                                                   dws);
-       u32 cs = spi->chip_select;
+       const struct dw_spi_mchp_props *props = dwsmchp->props;
+       u8 cs = spi->chip_select;

-       if (cs < 4) {
-               u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE;
+       if (enable)
+               dw_spi_mchp_set_cs_owner(dwsmchp, props, cs,
+                                        MSCC_IF_SI_OWNER_SIMC);

-               if (!enable)
-                       sw_mode |= MSCC_SPI_MST_SW_MODE_SW_SPI_CS(BIT(cs));
+       if (dwsmchp->spi_mst && (cs < MAX_CS)) {
+               u32 sw_mode;

+               if (enable)
+                       sw_mode = BIT(props->pinctrl_bit_off) |
+                               (BIT(cs) << props->cs_bit_off);
+               else
+                       sw_mode = 0;
                writel(sw_mode, dwsmchp->spi_mst + MSCC_SPI_MST_SW_MODE);
+       } else if (props->ss_force_ena_off) {
+               if (enable) {
+                       /* Ensure CS toggles, so start off all disabled */
+                       regmap_write(dwsmchp->syscon, props->ss_force_val_off,
+                                    ~0);
+                       /* CS override drive enable */
+                       regmap_write(dwsmchp->syscon, props->ss_force_ena_off,
+                                    1);
+                       /* Allow settle */
+                       udelay(1);
+                       /* Now set CSx enabled */
+                       regmap_write(dwsmchp->syscon, props->ss_force_val_off,
+                                    ~BIT(cs));
+               } else {
+                       /* CS value */
+                       regmap_write(dwsmchp->syscon, props->ss_force_val_off,
+                                    ~0);
+                       /* CS override drive disable */
+                       regmap_write(dwsmchp->syscon, props->ss_force_ena_off,
+                                    0);
+               }
        }

-       dw_spi_set_cs(spi, enable);
+       dw_spi_set_cs(spi, nEnable);
+}
+
+static int dw_mchp_bootmaster_exec_mem_op(struct spi_mem *mem,
+                                         const struct spi_mem_op *op)
+{
+       struct spi_device *spi = mem->spi;
+       int ret = -ENOTSUPP;
+
+       /* Only reads, addrsize 1..4 */
+       if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 ||
+           op->data.dir != SPI_MEM_DATA_IN)
+               return ret;
+
+       /* Only handle (normal+fast) 3/4 bytes read */
+       if (op->cmd.opcode != SPINOR_OP_READ &&
+           op->cmd.opcode != SPINOR_OP_READ_FAST &&
+           op->cmd.opcode != SPINOR_OP_READ_4B &&
+           op->cmd.opcode != SPINOR_OP_READ_FAST_4B)
+               return ret;
+
+       /* CS0..3, only 16M reach */
+       if ((spi->chip_select < MAX_CS) &&
+           (op->addr.val + op->data.nbytes) < SZ_16M) {
+               struct dw_spi *dws = spi_master_get_devdata(spi->master);
+               struct dw_spi_mchp *dwsmchp = container_of(dws,
+                                                          struct dw_spi_mchp,
+                                                          dws);
+               const struct dw_spi_mchp_props *props = dwsmchp->props;
+               u8 __iomem *src = dwsmchp->read_map +
+                       (spi->chip_select * SZ_16M) + op->addr.val;
+
+               if (props->bootmaster_cs != spi->chip_select)
+                       return ret;
+
+               /* Make boot master owner of SI interface */
+               dw_spi_mchp_set_cs_owner(dwsmchp, props, spi->chip_select,
+                                        MSCC_IF_SI_OWNER_SIBM);
+               memcpy(op->data.buf.in, src, op->data.nbytes);
+               ret = op->data.nbytes;
+       }
+       return ret;
 }

+static const struct spi_controller_mem_ops dw_mchp_bootmaster_mem_ops = {
+       .exec_op = dw_mchp_bootmaster_exec_mem_op,
+};
+
 static int dw_spi_mchp_init(struct platform_device *pdev,
                            struct dw_spi *dws,
                            struct dw_spi_mchp *dwsmchp,
@@ -107,6 +240,18 @@ static int dw_spi_mchp_init(struct platform_device *pdev,
                }
        }

+       /* See if we have a direct read window */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+       if (res && resource_size(res) >= (SZ_16M*MAX_CS)) {
+               void __iomem *ptr = devm_ioremap_resource(&pdev->dev, res);
+
+               if (!IS_ERR(ptr)) {
+                       dwsmchp->read_map = ptr;
+                       dws->mem_ops = &dw_mchp_bootmaster_mem_ops;
+                       dev_info(&pdev->dev, "Enabling fast memory 
operations\n");
+               }
+       }
+
        dwsmchp->syscon =
                syscon_regmap_lookup_by_compatible(props->syscon_name);
        if (IS_ERR(dwsmchp->syscon)) {
@@ -119,10 +264,9 @@ static int dw_spi_mchp_init(struct platform_device *pdev,
        if (dwsmchp->spi_mst)
                writel(0, dwsmchp->spi_mst + MSCC_SPI_MST_SW_MODE);

-       /* Select the owner of the SI interface */
-       regmap_update_bits(dwsmchp->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL,
-                          MSCC_IF_SI_OWNER_MASK << props->si_owner_bit,
-                          MSCC_IF_SI_OWNER_SIMC << props->si_owner_bit);
+       /* SPI2 mapping bitmask */
+       device_property_read_u32(&pdev->dev, "interface-mapping-mask",
+                                &dwsmchp->if2mask);

        dwsmchp->dws.set_cs = dw_spi_mchp_set_cs;

@@ -180,6 +324,27 @@ static int dw_spi_mchp_probe(struct platform_device *pdev)
        dws->rx_sample_dly = DIV_ROUND_UP(rx_sample_dly,
                                          (dws->max_freq / 1000000));

+       if (pdev->dev.of_node) {
+               int i;
+
+               for (i = 0; i < dws->num_cs; i++) {
+                       int cs_gpio = of_get_named_gpio(pdev->dev.of_node,
+                                       "cs-gpios", i);
+
+                       if (cs_gpio == -EPROBE_DEFER) {
+                               ret = cs_gpio;
+                               goto out;
+                       }
+
+                       if (gpio_is_valid(cs_gpio)) {
+                               ret = devm_gpio_request(&pdev->dev, cs_gpio,
+                                               dev_name(&pdev->dev));
+                               if (ret)
+                                       goto out;
+                       }
+               }
+       }
+
        props = device_get_match_data(&pdev->dev);
        if (props)
                ret = dw_spi_mchp_init(pdev, dws, dwsmchp, props);
@@ -213,6 +378,8 @@ static int dw_spi_mchp_remove(struct platform_device *pdev)
 static const struct of_device_id dw_spi_mchp_of_match[] = {
        { .compatible = "mscc,ocelot-spi", .data = &dw_spi_mchp_props_ocelot},
        { .compatible = "mscc,jaguar2-spi", .data = &dw_spi_mchp_props_jaguar2},
+       { .compatible = "microchip,sparx5-spi",
+         .data = &dw_spi_mchp_props_sparx5},
        { /* end of table */}
 };
 MODULE_DEVICE_TABLE(of, dw_spi_mchp_of_match);
--
2.26.2

Reply via email to