From: Sean Anderson <seanga2@gmail.com>
To: u-boot@lists.denx.de
Subject: [PATCH 12/14] spi: dw: Support DUAL/QUAD/OCTAL
Date: Sun, 31 Jan 2021 19:34:34 -0500 [thread overview]
Message-ID: <20210201003436.1180667-13-seanga2@gmail.com> (raw)
In-Reply-To: <20210201003436.1180667-1-seanga2@gmail.com>
This adds support for DUAL/QUAD/OCTAL transfers. This adds
dw_spi_supports_op to do some sanity checks which would otherwise live in
exec_op. We only support byte transfers, but as far as I could tell only
bytes are supported by mem_ops (e.g. every part of the opcode has nbytes).
Signed-off-by: Sean Anderson <seanga2@gmail.com>
---
drivers/spi/designware_spi.c | 147 +++++++++++++++++++++++++++++++----
1 file changed, 130 insertions(+), 17 deletions(-)
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 169888a06d..4796d55b20 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -215,7 +215,8 @@ static u32 dw_spi_update_cr0(struct dw_spi_priv *priv)
priv->bits_per_word - 1)
| FIELD_PREP(DWC_SSI_CTRLR0_FRF_MASK, priv->type)
| FIELD_PREP(DWC_SSI_CTRLR0_MODE_MASK, priv->mode)
- | FIELD_PREP(DWC_SSI_CTRLR0_TMOD_MASK, priv->tmode);
+ | FIELD_PREP(DWC_SSI_CTRLR0_TMOD_MASK, priv->tmode)
+ | FIELD_PREP(DWC_SSI_CTRLR0_SPI_FRF_MASK, priv->spi_frf);
} else {
if (priv->caps & DW_SPI_CAP_DFS32)
cr0 = FIELD_PREP(CTRLR0_DFS_32_MASK,
@@ -226,12 +227,36 @@ static u32 dw_spi_update_cr0(struct dw_spi_priv *priv)
cr0 |= FIELD_PREP(CTRLR0_FRF_MASK, priv->type)
| FIELD_PREP(CTRLR0_MODE_MASK, priv->mode)
- | FIELD_PREP(CTRLR0_TMOD_MASK, priv->tmode);
+ | FIELD_PREP(CTRLR0_TMOD_MASK, priv->tmode)
+ | FIELD_PREP(CTRLR0_SPI_FRF_MASK, priv->spi_frf);
}
return cr0;
}
+static u32 dw_spi_update_spi_cr0(const struct spi_mem_op *op)
+{
+ uint trans_type, wait_cycles;
+
+ /* This assumes support_op has filtered invalid types */
+ if (op->addr.buswidth == 1)
+ trans_type = SPI_CTRLR0_TRANS_TYPE_1_1_X;
+ else if (op->cmd.buswidth == 1)
+ trans_type = SPI_CTRLR0_TRANS_TYPE_1_X_X;
+ else
+ trans_type = SPI_CTRLR0_TRANS_TYPE_X_X_X;
+
+ if (op->dummy.buswidth)
+ wait_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
+ else
+ wait_cycles = 0;
+
+ return FIELD_PREP(SPI_CTRLR0_TRANS_TYPE, trans_type)
+ | FIELD_PREP(SPI_CTRLR0_ADDR_L_MASK, op->addr.nbytes * 2)
+ | FIELD_PREP(SPI_CTRLR0_INST_L_MASK, 2)
+ | FIELD_PREP(SPI_CTRLR0_WAIT_CYCLES_MASK, wait_cycles);
+}
+
static int request_gpio_cs(struct udevice *bus)
{
#if CONFIG_IS_ENABLED(DM_GPIO) && !defined(CONFIG_SPL_BUILD)
@@ -302,6 +327,17 @@ static void spi_hw_init(struct udevice *bus, struct dw_spi_priv *priv)
if (priv->caps & DW_SPI_CAP_DWC_SSI || !FIELD_GET(CTRLR0_DFS_MASK, cr0))
priv->caps |= DW_SPI_CAP_DFS32;
+ /*
+ * If SPI_FRF exists that means we have DUAL, QUAD, or OCTAL. Since we
+ * can't differentiate, just set DUAL
+ */
+ if (priv->caps & DW_SPI_CAP_DWC_SSI) {
+ if (FIELD_GET(DWC_SSI_CTRLR0_SPI_FRF_MASK, cr0))
+ priv->caps |= DW_SPI_CAP_DUAL;
+ } else if (FIELD_GET(CTRLR0_SPI_FRF_MASK, cr0)) {
+ priv->caps |= DW_SPI_CAP_DUAL;
+ }
+
dw_write(priv, DW_SPI_SSIENR, 1);
/*
@@ -604,6 +640,13 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
u32 val, cs;
uint frames;
+ /* DUAL/QUAD/OCTAL only supported by exec_op for now */
+ if (priv->mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL |
+ SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))
+ return -1;
+
+ priv->spi_frf = CTRLR0_SPI_FRF_BYTE;
+
/* spi core configured to do 8 bit transfers */
if (bitlen % priv->bits_per_word) {
dev_err(dev, "Non byte aligned SPI transfer.\n");
@@ -682,6 +725,9 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
/*
* This function is necessary for reading SPI flash with the native CS
* c.f. https://lkml.org/lkml/2015/12/23/132
+ *
+ * It also lets us handle DUAL/QUAD/OCTAL transfers in a much more idiomatic
+ * way.
*/
static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
{
@@ -692,37 +738,72 @@ static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
struct spi_mem_op *mut_op = (struct spi_mem_op *)op;
u8 op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
u8 op_buf[op_len];
- u32 cr0, val;
+ u32 cr0, spi_cr0, val;
+
+ /* Only bytes are supported for spi-mem transfers */
+ if (priv->bits_per_word != 8)
+ return -EINVAL;
+
+ switch (op->data.buswidth) {
+ case 0:
+ case 1:
+ priv->spi_frf = CTRLR0_SPI_FRF_BYTE;
+ break;
+ case 2:
+ priv->spi_frf = CTRLR0_SPI_FRF_DUAL;
+ break;
+ case 4:
+ priv->spi_frf = CTRLR0_SPI_FRF_QUAD;
+ break;
+ case 8:
+ priv->spi_frf = CTRLR0_SPI_FRF_OCTAL;
+ break;
+ /* BUG: should have been filtered out by supports_op */
+ default:
+ return -EINVAL;
+ }
if (read)
- priv->tmode = CTRLR0_TMOD_EPROMREAD;
+ if (priv->spi_frf == CTRLR0_SPI_FRF_BYTE)
+ priv->tmode = CTRLR0_TMOD_EPROMREAD;
+ else
+ priv->tmode = CTRLR0_TMOD_RO;
else
priv->tmode = CTRLR0_TMOD_TO;
cr0 = dw_spi_update_cr0(priv);
- dev_dbg(bus, "cr0=%08x buf=%p len=%u [bytes]\n", cr0, op->data.buf.in,
- op->data.nbytes);
+ spi_cr0 = dw_spi_update_spi_cr0(op);
+ dev_dbg(bus, "cr0=%08x spi_cr0=%08x buf=%p len=%u [bytes]\n", cr0,
+ spi_cr0, op->data.buf.in, op->data.nbytes);
dw_write(priv, DW_SPI_SSIENR, 0);
dw_write(priv, DW_SPI_CTRLR0, cr0);
if (read)
dw_write(priv, DW_SPI_CTRLR1, op->data.nbytes - 1);
+ if (priv->spi_frf != CTRLR0_SPI_FRF_BYTE)
+ dw_write(priv, DW_SPI_SPI_CTRL0, spi_cr0);
dw_write(priv, DW_SPI_SSIENR, 1);
- /* From spi_mem_exec_op */
- pos = 0;
- op_buf[pos++] = op->cmd.opcode;
- if (op->addr.nbytes) {
- for (i = 0; i < op->addr.nbytes; i++)
- op_buf[pos + i] = op->addr.val >>
- (8 * (op->addr.nbytes - i - 1));
+ /* Write out the instruction */
+ if (priv->spi_frf == CTRLR0_SPI_FRF_BYTE) {
+ /* From spi_mem_exec_op */
+ pos = 0;
+ op_buf[pos++] = op->cmd.opcode;
+ if (op->addr.nbytes) {
+ for (i = 0; i < op->addr.nbytes; i++)
+ op_buf[pos + i] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
- pos += op->addr.nbytes;
- }
- if (op->dummy.nbytes)
+ pos += op->addr.nbytes;
+ }
memset(op_buf + pos, 0xff, op->dummy.nbytes);
- dw_writer(priv, &op_buf, 0, op_len, 0, sizeof(u8));
+ dw_writer(priv, &op_buf, 0, op_len, 0, sizeof(u8));
+ } else {
+ /* MUST be written as byte/long; don't ask me why */
+ writeb(op->cmd.opcode, priv->regs + DW_SPI_DR);
+ writel(op->addr.val, priv->regs + DW_SPI_DR);
+ }
external_cs_manage(slave->dev, false);
dw_write(priv, DW_SPI_SER, 1 << spi_chip_select(slave->dev));
@@ -756,6 +837,37 @@ static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
return ret;
}
+bool dw_spi_supports_op(struct spi_slave *slave, const struct spi_mem_op *op)
+{
+ struct dw_spi_priv *priv = dev_get_priv(slave->dev->parent);
+
+ if (!spi_mem_default_supports_op(slave, op))
+ return false;
+
+ /*
+ * Everything before the data must fit in the fifo.
+ * In EEPROM mode we also need to fit the dummy.
+ */
+ if (1 + op->addr.nbytes +
+ (op->data.buswidth == 1 ? op->dummy.nbytes : 0) > priv->fifo_len)
+ return false;
+
+ /* We only support 1_1_X, 1_X_X, and X_X_X formats */
+ if (op->cmd.buswidth == 1 &&
+ (!op->addr.nbytes || op->addr.buswidth == 1))
+ return true;
+
+ if (op->cmd.buswidth == 1 &&
+ (!op->addr.nbytes || op->addr.buswidth == op->data.buswidth))
+ return true;
+
+ if (op->cmd.buswidth == op->data.buswidth &&
+ (!op->addr.nbytes || op->addr.buswidth == op->data.buswidth))
+ return true;
+
+ return false;
+}
+
/* The size of ctrl1 limits data transfers to 64K */
static int dw_spi_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
{
@@ -766,6 +878,7 @@ static int dw_spi_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
static const struct spi_controller_mem_ops dw_spi_mem_ops = {
.exec_op = dw_spi_exec_op,
+ .supports_op = dw_spi_supports_op,
.adjust_op_size = dw_spi_adjust_op_size,
};
--
2.29.2
next prev parent reply other threads:[~2021-02-01 0:34 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-02-01 0:34 [PATCH 00/14] spi: dw: Add support for DUAL/QUAD/OCTAL modes Sean Anderson
2021-02-01 0:34 ` [PATCH 01/14] cmd: sf: Display errno on erase failure Sean Anderson
2021-02-01 5:06 ` Bin Meng
2021-02-01 12:51 ` Pratyush Yadav
2021-02-01 0:34 ` [PATCH 02/14] cmd: sf: Print error on test failure Sean Anderson
2021-02-01 5:06 ` Bin Meng
2021-02-01 12:54 ` Pratyush Yadav
2021-02-01 0:34 ` [PATCH 03/14] mtd: spi-nor-core: Fix typo in documentation Sean Anderson
2021-02-01 5:06 ` Bin Meng
2021-02-01 12:23 ` Pratyush Yadav
2021-02-01 0:34 ` [PATCH 04/14] mtd: spi-mem: Export spi_mem_default_supports_op Sean Anderson
2021-02-01 5:06 ` Bin Meng
2021-02-01 12:07 ` Pratyush Yadav
2021-02-01 15:15 ` Sean Anderson
2021-02-01 0:34 ` [PATCH 05/14] spi: spi-mem: Add debug message for spi-mem ops Sean Anderson
2021-02-01 5:06 ` Bin Meng
2021-02-01 12:18 ` Pratyush Yadav
2021-02-01 15:33 ` Sean Anderson
2021-02-01 16:34 ` Pratyush Yadav
2021-02-01 0:34 ` [PATCH 06/14] spi: dw: Log status register on timeout Sean Anderson
2021-02-01 0:34 ` [PATCH 07/14] spi: dw: Mask all possible interrupts Sean Anderson
2021-02-04 4:51 ` Sean Anderson
2021-02-01 0:34 ` [PATCH 08/14] spi: dw: Switch to capabilities Sean Anderson
2021-02-01 0:34 ` [PATCH 09/14] spi: dw: Rewrite poll_transfer logic Sean Anderson
2021-02-01 0:34 ` [PATCH 10/14] spi: dw: Add DUAL/QUAD/OCTAL caps Sean Anderson
2021-02-01 18:00 ` Sean Anderson
2021-02-01 0:34 ` [PATCH 11/14] spi: dw: Add registers necessary for DUAL/QUAD/OCTAL Sean Anderson
2021-02-01 0:34 ` Sean Anderson [this message]
2021-02-01 0:34 ` [PATCH 13/14] spi: dw: Support clock stretching Sean Anderson
2021-02-01 0:34 ` [PATCH 14/14] riscv: k210: Enable QSPI for spi3 Sean Anderson
2021-02-01 5:16 ` Bin Meng
2021-02-04 2:32 ` Leo Liang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210201003436.1180667-13-seanga2@gmail.com \
--to=seanga2@gmail.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox