Merge https://gitlab.denx.de/u-boot/custodians/u-boot-mpc85xx
[oweals/u-boot.git] / drivers / spi / cadence_qspi.c
index 91742ba5f157ee22a43444ed2f5a40c7c4c50cd3..1e8574920907ad8bff6401c2a3fc8b57518953d8 100644 (file)
@@ -5,19 +5,25 @@
  */
 
 #include <common.h>
+#include <clk.h>
+#include <log.h>
+#include <asm-generic/io.h>
 #include <dm.h>
 #include <fdtdec.h>
 #include <malloc.h>
+#include <reset.h>
 #include <spi.h>
+#include <spi-mem.h>
+#include <dm/device_compat.h>
+#include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/sizes.h>
 #include "cadence_qspi.h"
 
 #define CQSPI_STIG_READ                        0
 #define CQSPI_STIG_WRITE               1
-#define CQSPI_INDIRECT_READ            2
-#define CQSPI_INDIRECT_WRITE           3
-
-DECLARE_GLOBAL_DATA_PTR;
+#define CQSPI_READ                     2
+#define CQSPI_WRITE                    3
 
 static int cadence_spi_write_speed(struct udevice *bus, uint hz)
 {
@@ -25,22 +31,31 @@ static int cadence_spi_write_speed(struct udevice *bus, uint hz)
        struct cadence_spi_priv *priv = dev_get_priv(bus);
 
        cadence_qspi_apb_config_baudrate_div(priv->regbase,
-                                            CONFIG_CQSPI_REF_CLK, hz);
+                                            plat->ref_clk_hz, hz);
 
        /* Reconfigure delay timing if speed is changed. */
-       cadence_qspi_apb_delay(priv->regbase, CONFIG_CQSPI_REF_CLK, hz,
+       cadence_qspi_apb_delay(priv->regbase, plat->ref_clk_hz, hz,
                               plat->tshsl_ns, plat->tsd2d_ns,
                               plat->tchsh_ns, plat->tslch_ns);
 
        return 0;
 }
 
+static int cadence_spi_read_id(void *reg_base, u8 len, u8 *idcode)
+{
+       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1),
+                                         SPI_MEM_OP_NO_ADDR,
+                                         SPI_MEM_OP_NO_DUMMY,
+                                         SPI_MEM_OP_DATA_IN(len, idcode, 1));
+
+       return cadence_qspi_apb_command_read(reg_base, &op);
+}
+
 /* Calibration sequence to determine the read data capture delay register */
 static int spi_calibration(struct udevice *bus, uint hz)
 {
        struct cadence_spi_priv *priv = dev_get_priv(bus);
        void *base = priv->regbase;
-       u8 opcode_rdid = 0x9F;
        unsigned int idcode = 0, temp = 0;
        int err = 0, i, range_lo = -1, range_hi = -1;
 
@@ -54,8 +69,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
        cadence_qspi_apb_controller_enable(base);
 
        /* read the ID which will be our golden value */
-       err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid,
-               3, (u8 *)&idcode);
+       err = cadence_spi_read_id(base, 3, (u8 *)&idcode);
        if (err) {
                puts("SF: Calibration failed (read)\n");
                return err;
@@ -74,8 +88,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
                cadence_qspi_apb_controller_enable(base);
 
                /* issue a RDID to get the ID value */
-               err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid,
-                       3, (u8 *)&temp);
+               err = cadence_spi_read_id(base, 3, (u8 *)&temp);
                if (err) {
                        puts("SF: Calibration failed (read)\n");
                        return err;
@@ -154,10 +167,34 @@ static int cadence_spi_probe(struct udevice *bus)
 {
        struct cadence_spi_platdata *plat = bus->platdata;
        struct cadence_spi_priv *priv = dev_get_priv(bus);
+       struct clk clk;
+       int ret;
 
        priv->regbase = plat->regbase;
        priv->ahbbase = plat->ahbbase;
 
+       if (plat->ref_clk_hz == 0) {
+               ret = clk_get_by_index(bus, 0, &clk);
+               if (ret) {
+#ifdef CONFIG_CQSPI_REF_CLK
+                       plat->ref_clk_hz = CONFIG_CQSPI_REF_CLK;
+#else
+                       return ret;
+#endif
+               } else {
+                       plat->ref_clk_hz = clk_get_rate(&clk);
+                       clk_free(&clk);
+                       if (IS_ERR_VALUE(plat->ref_clk_hz))
+                               return plat->ref_clk_hz;
+               }
+       }
+
+       ret = reset_get_bulk(bus, &priv->resets);
+       if (ret)
+               dev_warn(bus, "Can't get reset: %d\n", ret);
+       else
+               reset_deassert_bulk(&priv->resets);
+
        if (!priv->qspi_is_init) {
                cadence_qspi_apb_controller_init(plat);
                priv->qspi_is_init = 1;
@@ -166,8 +203,16 @@ static int cadence_spi_probe(struct udevice *bus)
        return 0;
 }
 
+static int cadence_spi_remove(struct udevice *dev)
+{
+       struct cadence_spi_priv *priv = dev_get_priv(dev);
+
+       return reset_release_bulk(&priv->resets);
+}
+
 static int cadence_spi_set_mode(struct udevice *bus, uint mode)
 {
+       struct cadence_spi_platdata *plat = bus->platdata;
        struct cadence_spi_priv *priv = dev_get_priv(bus);
 
        /* Disable QSPI */
@@ -176,102 +221,62 @@ static int cadence_spi_set_mode(struct udevice *bus, uint mode)
        /* Set SPI mode */
        cadence_qspi_apb_set_clk_mode(priv->regbase, mode);
 
+       /* Enable Direct Access Controller */
+       if (plat->use_dac_mode)
+               cadence_qspi_apb_dac_mode_enable(priv->regbase);
+
        /* Enable QSPI */
        cadence_qspi_apb_controller_enable(priv->regbase);
 
        return 0;
 }
 
-static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen,
-                           const void *dout, void *din, unsigned long flags)
+static int cadence_spi_mem_exec_op(struct spi_slave *spi,
+                                  const struct spi_mem_op *op)
 {
-       struct udevice *bus = dev->parent;
+       struct udevice *bus = spi->dev->parent;
        struct cadence_spi_platdata *plat = bus->platdata;
        struct cadence_spi_priv *priv = dev_get_priv(bus);
-       struct dm_spi_slave_platdata *dm_plat = dev_get_parent_platdata(dev);
        void *base = priv->regbase;
-       u8 *cmd_buf = priv->cmd_buf;
-       size_t data_bytes;
        int err = 0;
-       u32 mode = CQSPI_STIG_WRITE;
-
-       if (flags & SPI_XFER_BEGIN) {
-               /* copy command to local buffer */
-               priv->cmd_len = bitlen / 8;
-               memcpy(cmd_buf, dout, priv->cmd_len);
-       }
-
-       if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) {
-               /* if start and end bit are set, the data bytes is 0. */
-               data_bytes = 0;
-       } else {
-               data_bytes = bitlen / 8;
-       }
-       debug("%s: len=%d [bytes]\n", __func__, data_bytes);
+       u32 mode;
 
        /* Set Chip select */
-       cadence_qspi_apb_chipselect(base, spi_chip_select(dev),
+       cadence_qspi_apb_chipselect(base, spi_chip_select(spi->dev),
                                    plat->is_decoded_cs);
 
-       if ((flags & SPI_XFER_END) || (flags == 0)) {
-               if (priv->cmd_len == 0) {
-                       printf("QSPI: Error, command is empty.\n");
-                       return -1;
-               }
-
-               if (din && data_bytes) {
-                       /* read */
-                       /* Use STIG if no address. */
-                       if (!CQSPI_IS_ADDR(priv->cmd_len))
-                               mode = CQSPI_STIG_READ;
-                       else
-                               mode = CQSPI_INDIRECT_READ;
-               } else if (dout && !(flags & SPI_XFER_BEGIN)) {
-                       /* write */
-                       if (!CQSPI_IS_ADDR(priv->cmd_len))
-                               mode = CQSPI_STIG_WRITE;
-                       else
-                               mode = CQSPI_INDIRECT_WRITE;
-               }
-
-               switch (mode) {
-               case CQSPI_STIG_READ:
-                       err = cadence_qspi_apb_command_read(
-                               base, priv->cmd_len, cmd_buf,
-                               data_bytes, din);
+       if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) {
+               if (!op->addr.nbytes)
+                       mode = CQSPI_STIG_READ;
+               else
+                       mode = CQSPI_READ;
+       } else {
+               if (!op->addr.nbytes || !op->data.buf.out)
+                       mode = CQSPI_STIG_WRITE;
+               else
+                       mode = CQSPI_WRITE;
+       }
 
+       switch (mode) {
+       case CQSPI_STIG_READ:
+               err = cadence_qspi_apb_command_read(base, op);
                break;
-               case CQSPI_STIG_WRITE:
-                       err = cadence_qspi_apb_command_write(base,
-                               priv->cmd_len, cmd_buf,
-                               data_bytes, dout);
+       case CQSPI_STIG_WRITE:
+               err = cadence_qspi_apb_command_write(base, op);
                break;
-               case CQSPI_INDIRECT_READ:
-                       err = cadence_qspi_apb_indirect_read_setup(plat,
-                               priv->cmd_len, dm_plat->mode, cmd_buf);
-                       if (!err) {
-                               err = cadence_qspi_apb_indirect_read_execute
-                               (plat, data_bytes, din);
-                       }
+       case CQSPI_READ:
+               err = cadence_qspi_apb_read_setup(plat, op);
+               if (!err)
+                       err = cadence_qspi_apb_read_execute(plat, op);
                break;
-               case CQSPI_INDIRECT_WRITE:
-                       err = cadence_qspi_apb_indirect_write_setup
-                               (plat, priv->cmd_len, cmd_buf);
-                       if (!err) {
-                               err = cadence_qspi_apb_indirect_write_execute
-                               (plat, data_bytes, dout);
-                       }
+       case CQSPI_WRITE:
+               err = cadence_qspi_apb_write_setup(plat, op);
+               if (!err)
+                       err = cadence_qspi_apb_write_execute(plat, op);
+               break;
+       default:
+               err = -1;
                break;
-               default:
-                       err = -1;
-                       break;
-               }
-
-               if (flags & SPI_XFER_END) {
-                       /* clear command buffer */
-                       memset(cmd_buf, 0, sizeof(priv->cmd_buf));
-                       priv->cmd_len = 0;
-               }
        }
 
        return err;
@@ -280,36 +285,41 @@ static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen,
 static int cadence_spi_ofdata_to_platdata(struct udevice *bus)
 {
        struct cadence_spi_platdata *plat = bus->platdata;
-       const void *blob = gd->fdt_blob;
-       int node = dev_of_offset(bus);
-       int subnode;
+       ofnode subnode;
 
        plat->regbase = (void *)devfdt_get_addr_index(bus, 0);
-       plat->ahbbase = (void *)devfdt_get_addr_index(bus, 1);
-       plat->is_decoded_cs = fdtdec_get_bool(blob, node, "cdns,is-decoded-cs");
-       plat->fifo_depth = fdtdec_get_uint(blob, node, "cdns,fifo-depth", 128);
-       plat->fifo_width = fdtdec_get_uint(blob, node, "cdns,fifo-width", 4);
-       plat->trigger_address = fdtdec_get_uint(blob, node,
-                                               "cdns,trigger-address", 0);
+       plat->ahbbase = (void *)devfdt_get_addr_size_index(bus, 1,
+                       &plat->ahbsize);
+       plat->is_decoded_cs = dev_read_bool(bus, "cdns,is-decoded-cs");
+       plat->fifo_depth = dev_read_u32_default(bus, "cdns,fifo-depth", 128);
+       plat->fifo_width = dev_read_u32_default(bus, "cdns,fifo-width", 4);
+       plat->trigger_address = dev_read_u32_default(bus,
+                                                    "cdns,trigger-address",
+                                                    0);
+       /* Use DAC mode only when MMIO window is at least 8M wide */
+       if (plat->ahbsize >= SZ_8M)
+               plat->use_dac_mode = true;
 
        /* All other paramters are embedded in the child node */
-       subnode = fdt_first_subnode(blob, node);
-       if (subnode < 0) {
+       subnode = dev_read_first_subnode(bus);
+       if (!ofnode_valid(subnode)) {
                printf("Error: subnode with SPI flash config missing!\n");
                return -ENODEV;
        }
 
        /* Use 500 KHz as a suitable default */
-       plat->max_hz = fdtdec_get_uint(blob, subnode, "spi-max-frequency",
-                                      500000);
+       plat->max_hz = ofnode_read_u32_default(subnode, "spi-max-frequency",
+                                              500000);
 
        /* Read other parameters from DT */
-       plat->page_size = fdtdec_get_uint(blob, subnode, "page-size", 256);
-       plat->block_size = fdtdec_get_uint(blob, subnode, "block-size", 16);
-       plat->tshsl_ns = fdtdec_get_uint(blob, subnode, "cdns,tshsl-ns", 200);
-       plat->tsd2d_ns = fdtdec_get_uint(blob, subnode, "cdns,tsd2d-ns", 255);
-       plat->tchsh_ns = fdtdec_get_uint(blob, subnode, "cdns,tchsh-ns", 20);
-       plat->tslch_ns = fdtdec_get_uint(blob, subnode, "cdns,tslch-ns", 20);
+       plat->page_size = ofnode_read_u32_default(subnode, "page-size", 256);
+       plat->block_size = ofnode_read_u32_default(subnode, "block-size", 16);
+       plat->tshsl_ns = ofnode_read_u32_default(subnode, "cdns,tshsl-ns",
+                                                200);
+       plat->tsd2d_ns = ofnode_read_u32_default(subnode, "cdns,tsd2d-ns",
+                                                255);
+       plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20);
+       plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20);
 
        debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
              __func__, plat->regbase, plat->ahbbase, plat->max_hz,
@@ -318,10 +328,14 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus)
        return 0;
 }
 
+static const struct spi_controller_mem_ops cadence_spi_mem_ops = {
+       .exec_op = cadence_spi_mem_exec_op,
+};
+
 static const struct dm_spi_ops cadence_spi_ops = {
-       .xfer           = cadence_spi_xfer,
        .set_speed      = cadence_spi_set_speed,
        .set_mode       = cadence_spi_set_mode,
+       .mem_ops        = &cadence_spi_mem_ops,
        /*
         * cs_info is not needed, since we require all chip selects to be
         * in the device tree explicitly
@@ -329,7 +343,8 @@ static const struct dm_spi_ops cadence_spi_ops = {
 };
 
 static const struct udevice_id cadence_spi_ids[] = {
-       { .compatible = "cadence,qspi" },
+       { .compatible = "cdns,qspi-nor" },
+       { .compatible = "ti,am654-ospi" },
        { }
 };
 
@@ -342,4 +357,6 @@ U_BOOT_DRIVER(cadence_spi) = {
        .platdata_auto_alloc_size = sizeof(struct cadence_spi_platdata),
        .priv_auto_alloc_size = sizeof(struct cadence_spi_priv),
        .probe = cadence_spi_probe,
+       .remove = cadence_spi_remove,
+       .flags = DM_FLAG_OS_PREPARE,
 };