summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/nxp_fspi.c44
-rw-r--r--drivers/spi/spi-mem.c45
2 files changed, 82 insertions, 7 deletions
diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c
index 6d97b8eefc9..7086a2a264a 100644
--- a/drivers/spi/nxp_fspi.c
+++ b/drivers/spi/nxp_fspi.c
@@ -337,6 +337,33 @@ static struct nxp_fspi_devtype_data imxrt1170_data = {
.little_endian = true,
};
+static const struct nxp_fspi_devtype_data imx8qxp_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .lut_num = 32,
+ .little_endian = true, /* little-endian */
+};
+
+static const struct nxp_fspi_devtype_data imx8dxl_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = FSPI_QUIRK_USE_IP_ONLY,
+ .lut_num = 32,
+ .little_endian = true, /* little-endian */
+};
+
+static const struct nxp_fspi_devtype_data imx8ulp_data = {
+ .rxfifo = SZ_1K, /* (128 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .lut_num = 16,
+ .little_endian = true, /* little-endian */
+};
+
struct nxp_fspi {
struct udevice *dev;
void __iomem *iobase;
@@ -539,6 +566,15 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
fspi_writel(f, lutval[i], base + target_lut_reg);
}
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN &&
+ op->addr.nbytes) {
+ lut_offset = (f->devtype_data->lut_num - 2) * 4 * 4;
+ for (i = 0; i < ARRAY_SIZE(lutval); i++) {
+ target_lut_reg = FSPI_LUT_BASE + lut_offset + i * 4;
+ fspi_writel(f, lutval[i], base + target_lut_reg);
+ }
+ }
+
dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);
@@ -943,9 +979,10 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
/*
* The driver only uses one single LUT entry, that is updated on
* each call of exec_op(). Index 0 is preset at boot with a basic
- * read operation, so let's use the last entry.
+ * read operation, last entry is used for dynamic lut, the second
+ * last entry is used for AHB read.
*/
- seqid_lut = f->devtype_data->lut_num - 1;
+ seqid_lut = f->devtype_data->lut_num - 2;
/* AHB Read - Set lut sequence ID for all CS. */
fspi_writel(f, seqid_lut, base + FSPI_FLSHA1CR2);
fspi_writel(f, seqid_lut, base + FSPI_FLSHA2CR2);
@@ -1071,6 +1108,9 @@ static const struct udevice_id nxp_fspi_ids[] = {
{ .compatible = "nxp,lx2160a-fspi", .data = (ulong)&lx2160a_data, },
{ .compatible = "nxp,imx8mm-fspi", .data = (ulong)&imx8mm_data, },
{ .compatible = "nxp,imx8mp-fspi", .data = (ulong)&imx8mm_data, },
+ { .compatible = "nxp,imx8qxp-fspi", .data = (ulong)&imx8qxp_data, },
+ { .compatible = "nxp,imx8dxl-fspi", .data = (ulong)&imx8dxl_data, },
+ { .compatible = "nxp,imx8ulp-fspi", .data = (ulong)&imx8ulp_data, },
{ .compatible = "nxp,imxrt1170-fspi", .data = (ulong)&imxrt1170_data, },
{ }
};
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 3579b7d7db5..db44a7b26eb 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -120,18 +120,21 @@ static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx)
return 0;
case 2:
- if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
- (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
+ if ((tx &&
+ (mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL))) ||
+ (!tx &&
+ (mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))))
return 0;
break;
case 4:
- if ((tx && (mode & SPI_TX_QUAD)) ||
- (!tx && (mode & SPI_RX_QUAD)))
+ if ((tx && (mode & (SPI_TX_QUAD | SPI_TX_OCTAL))) ||
+ (!tx && (mode & (SPI_RX_QUAD | SPI_RX_OCTAL))))
return 0;
break;
+
case 8:
if ((tx && (mode & SPI_TX_OCTAL)) ||
(!tx && (mode & SPI_RX_OCTAL)))
@@ -300,7 +303,7 @@ int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
* read path) and expect the core to use the regular SPI
* interface in other cases.
*/
- if (!ret || ret != -ENOTSUPP) {
+ if (!ret || (ret != -ENOTSUPP && ret != -EOPNOTSUPP)) {
spi_release_bus(slave);
return ret;
}
@@ -496,6 +499,38 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
}
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
+/**
+ * spi_mem_calc_op_duration() - Derives the theoretical length (in cpu cycles)
+ * of an operation. This helps finding the best
+ * variant among a list of possible choices.
+ * @op: the operation to benchmark
+ *
+ * Some chips have per-op frequency limitations, PCBs usually have their own
+ * limitations as well, and controllers can support dual, quad or even octal
+ * modes, sometimes in DTR. All these combinations make it impossible to
+ * statically list the best combination for all situations. If we want something
+ * accurate, all these combinations should be rated (eg. with a time estimate)
+ * and the best pick should be taken based on these calculations.
+ *
+ * Returns a estimate for the time this op would take.
+ */
+u64 spi_mem_calc_op_duration(struct spi_mem_op *op)
+{
+ u64 ncycles = 0;
+
+ ncycles += ((op->cmd.nbytes * 8) / op->cmd.buswidth) / (op->cmd.dtr ? 2 : 1);
+ ncycles += ((op->addr.nbytes * 8) / op->addr.buswidth) / (op->addr.dtr ? 2 : 1);
+
+ /* Dummy bytes are optional for some SPI flash memory operations */
+ if (op->dummy.nbytes)
+ ncycles += ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1);
+
+ ncycles += ((op->data.nbytes * 8) / op->data.buswidth) / (op->data.dtr ? 2 : 1);
+
+ return ncycles;
+}
+EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration);
+
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{