Hi Sean > The dw spi devices on the Kendryte K210 must be operated in a specific > fasion which cannot be achived through multiple writes to via dw_spi_xfer > (as it is currently written). This patch adds an implementation of exec_op, > which gives correct behaviour when reading/writing spi flash. > > I would like to be able to modify the existing dw_spi_xfer function such > that it works properly (e.g. with the mmc_spi driver). However, the only > example code I have to work off is Kendryte's sdk (which is written in the > exec_op style), and I do not have access to the datasheet (if anyone does, > I would love to have a look!). > > Signed-off-by: Sean Anderson <sean...@gmail.com> > --- > > Changes in v4: > - New > > drivers/spi/designware_spi.c | 123 +++++++++++++++++++++++++++++++++-- > 1 file changed, 119 insertions(+), 4 deletions(-) >
Patch 6 and 7 shall be applied via spi tree. You mix them together, the patchwork will become complicated. It will be better for me to wait for a period of time to get the approval(Reviewed-by or Acked-by) of SPI MAINTAINER. Thanks Rick > diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c > index 04cc873754..277eb19a0b 100644 > --- a/drivers/spi/designware_spi.c > +++ b/drivers/spi/designware_spi.c > @@ -17,6 +17,7 @@ > #include <errno.h> > #include <malloc.h> > #include <spi.h> > +#include <spi-mem.h> > #include <fdtdec.h> > #include <reset.h> > #include <linux/compat.h> > @@ -107,8 +108,8 @@ struct dw_spi_priv { > int len; > > u32 fifo_len; /* depth of the FIFO buffer */ > - void *tx; > - void *tx_end; > + const void *tx; > + const void *tx_end; > void *rx; > void *rx_end; > > @@ -344,7 +345,7 @@ static void dw_writer(struct dw_spi_priv *priv) > txw = *(u16 *)(priv->tx); > } > dw_write(priv, DW_SPI_DR, txw); > - debug("%s: tx=0x%02x\n", __func__, txw); > + log_io("tx=0x%02x\n", txw); > priv->tx += priv->bits_per_word >> 3; > } > } > @@ -356,7 +357,7 @@ static void dw_reader(struct dw_spi_priv *priv) > > while (max--) { > rxw = dw_read(priv, DW_SPI_DR); > - debug("%s: rx=0x%02x\n", __func__, rxw); > + log_io("rx=0x%02x\n", rxw); > > /* Care about rx if the transfer's original "rx" is not null > */ > if (priv->rx_end - priv->len) { > @@ -483,6 +484,115 @@ static int dw_spi_xfer(struct udevice *dev, unsigned > int bitlen, > return ret; > } > > +static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op > *op) > +{ > + bool read = op->data.dir == SPI_MEM_DATA_IN; > + int pos, i, ret = 0; > + struct udevice *bus = slave->dev->parent; > + struct dw_spi_platdata *plat = dev_get_platdata(bus); > + struct dw_spi_priv *priv = dev_get_priv(bus); > + u8 op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + > op->dummy.nbytes; > + u8 op_buf[op_len]; > + u32 cr0; > + > + if (read) > + priv->tmode = SPI_TMOD_EPROMREAD; > + else > + priv->tmode = SPI_TMOD_TO; > + > + debug("%s: buf=%p len=%u [bytes]\n", > + __func__, op->data.buf.in, op->data.nbytes); > + > + cr0 = GEN_CTRL0(priv, plat); > + debug("%s: cr0=%08x\n", __func__, cr0); > + > + spi_enable_chip(priv, 0); > + dw_write(priv, DW_SPI_CTRL0, cr0); > + if (read) > + dw_write(priv, DW_SPI_CTRL1, op->data.nbytes - 1); > + spi_enable_chip(priv, 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)); > + > + pos += op->addr.nbytes; > + } > + if (op->dummy.nbytes) > + memset(op_buf + pos, 0xff, op->dummy.nbytes); > + > + priv->tx = &op_buf; > + priv->tx_end = priv->tx + op_len; > + while (priv->tx != priv->tx_end) > + dw_writer(priv); > + > + /* > + * XXX: The following are tight loops! Enabling debug messages may > cause > + * them to fail because we are not reading/writing the fifo fast > enough. > + * > + * We heuristically break out of the loop when we stop getting data. > + * This is to stop us from hanging if the device doesn't send any data > + * (either at all, or after sending a response). For example, one > flash > + * chip I tested did not send anything back after the first 64K of > data. > + */ > + if (read) { > + /* If we have gotten any data back yet */ > + bool got_data = false; > + /* How many times we have looped without reading anything */ > + int loops_since_read = 0; > + struct spi_mem_op *mut_op = (struct spi_mem_op *)op; > + > + priv->rx = op->data.buf.in; > + priv->rx_end = priv->rx + op->data.nbytes; > + > + dw_write(priv, DW_SPI_SER, 1 << spi_chip_select(slave->dev)); > + while (priv->rx != priv->rx_end) { > + void *last_rx = priv->rx; > + > + dw_reader(priv); > + if (priv->rx == last_rx) { > + loops_since_read++; > + /* Thresholds are arbitrary */ > + if (loops_since_read > 256) > + break; > + else if (got_data && loops_since_read > 32) > + break; > + } else { > + got_data = true; > + loops_since_read = 0; > + } > + } > + > + /* Update with the actual amount of data read */ > + mut_op->data.nbytes -= priv->rx_end - priv->rx; > + } else { > + u32 val; > + > + priv->tx = op->data.buf.out; > + priv->tx_end = priv->tx + op->data.nbytes; > + > + /* Fill up the write fifo before starting the transfer */ > + dw_writer(priv); > + dw_write(priv, DW_SPI_SER, 1 << spi_chip_select(slave->dev)); > + while (priv->tx != priv->tx_end) > + dw_writer(priv); > + > + if (readl_poll_timeout(priv->regs + DW_SPI_SR, val, > + (val & SR_TF_EMPT) && !(val & SR_BUSY), > + RX_TIMEOUT * 1000)) { > + ret = -ETIMEDOUT; > + } > + } > + > + dw_write(priv, DW_SPI_SER, 0); > + debug("%s: %u bytes xfered\n", __func__, op->data.nbytes); > + return ret; > +} > + > static int dw_spi_set_speed(struct udevice *bus, uint speed) > { > struct dw_spi_platdata *plat = dev_get_platdata(bus); > @@ -546,8 +656,13 @@ static int dw_spi_remove(struct udevice *bus) > return 0; > } > > +static const struct spi_controller_mem_ops dw_spi_mem_ops = { > + .exec_op = dw_spi_exec_op, > +}; > + > static const struct dm_spi_ops dw_spi_ops = { > .xfer = dw_spi_xfer, > + .mem_ops = &dw_spi_mem_ops, > .set_speed = dw_spi_set_speed, > .set_mode = dw_spi_set_mode, > /* > -- > 2.25.0 >