summaryrefslogtreecommitdiff
path: root/drivers/spi/nxp_fspi.c
diff options
context:
space:
mode:
authorHan Xu <han.xu@nxp.com>2021-12-12 23:16:24 -0600
committerYe Li <ye.li@nxp.com>2022-04-06 18:05:02 +0800
commit8aa3e25a1300c50927af06b5a51780f1f6d91ffd (patch)
tree5305368a07d77eac92379d745f22d19d0f49642a /drivers/spi/nxp_fspi.c
parentad41285297214c40ea2513ad7e2a60f1bbb57d40 (diff)
LF-5091: spi: nxp_fspi: fix some odd address read issues
When enabling the OCTAL DTR read on DXL platform, to fix the UBIFS mount issue, the driver handles the OCTAL DTR read with odd address by reading one more byte and discard the first byte, but it involved some issues. First issue is reading data from odd address and data length is larger than rxfifo. nxp_fspi_adjust_op_size() sets the data size align to rxfifo size, but is over the rxfifo size if reads one more byte and causes controller timeout. Second issue happens when memcpy from src to dst in odd address cases. The fist byte from src discarded, the pointer to src should move N bytes forward and dst pointer moves N-1 bytes. Previous implementation reads one more byte, even there is nothing in FIFO, which triggers the WARN_ON() in driver. This change handles the OCTAL DTR with odd address before normal rxfifo read. nxp_fspi_adjust_op_size() changes the data size to an odd number so this special case only happens once when reading large chunk of data. Signed-off-by: Han Xu <han.xu@nxp.com> Reviewed-by: Ye Li <ye.li@nxp.com> (cherry picked from commit 59767ae1285de786ca4214b0d1078156f08866ec)
Diffstat (limited to 'drivers/spi/nxp_fspi.c')
-rw-r--r--drivers/spi/nxp_fspi.c78
1 files changed, 52 insertions, 26 deletions
diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c
index 9fcdfbc66d..2cab06bdd8 100644
--- a/drivers/spi/nxp_fspi.c
+++ b/drivers/spi/nxp_fspi.c
@@ -738,7 +738,33 @@ static void nxp_fspi_read_rxfifo(struct nxp_fspi *f,
u8 *buf = (u8 *)op->data.buf.in;
/* DTR with ODD address need read one more byte */
- len = (f->flags & FSPI_DTR_ODD_ADDR) ? op-> data.nbytes + 1 : op->data.nbytes;
+ len = (f->flags & FSPI_DTR_ODD_ADDR) ? op->data.nbytes + 1 : op->data.nbytes;
+
+ /* handle the DTR with ODD address case */
+ if (f->flags & FSPI_DTR_ODD_ADDR) {
+ u8 tmp[8];
+ /* Wait for RXFIFO available */
+ ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
+ FSPI_INTR_IPRXWA, 0,
+ POLL_TOUT, true);
+ WARN_ON(ret);
+ /*
+ * DTR read always start from 2bytes alignment address,
+ * if read from an odd address A, it actually read from
+ * address A-1, need to discard the first byte here
+ */
+ *(u32 *)tmp = fspi_readl(f, base + FSPI_RFDR);
+ *(u32 *)(tmp + 4) = fspi_readl(f, base + FSPI_RFDR + 4);
+ cnt = min(len, 8);
+ /* discard the first byte */
+ memcpy(buf, tmp + 1, cnt - 1);
+ len -= cnt;
+ buf = op->data.buf.in + cnt - 1;
+ f->flags &= ~FSPI_DTR_ODD_ADDR;
+
+ /* move the FIFO pointer */
+ fspi_writel(f, FSPI_INTR_IPRXWA, base + FSPI_INTR);
+ }
/*
* Default value of water mark level is 8 bytes, hence in single
@@ -753,23 +779,9 @@ static void nxp_fspi_read_rxfifo(struct nxp_fspi *f,
POLL_TOUT, true);
WARN_ON(ret);
- if (f->flags & FSPI_DTR_ODD_ADDR && !i) {
- /*
- * DTR read always start from 2bytes alignment address,
- * if read from an odd address A, it actually read from
- * address A-1, need to abandon the first byte here
- */
- u8 tmp[8];
- *(u32 *)tmp = fspi_readl(f, base + FSPI_RFDR);
- *(u32 *)(tmp + 4) = fspi_readl(f, base + FSPI_RFDR + 4);
- memcpy(buf, tmp + 1, 7);
- i += 7;
- f->flags &= ~FSPI_DTR_ODD_ADDR;
- } else {
- *(u32 *)(buf + i) = fspi_readl(f, base + FSPI_RFDR);
- *(u32 *)(buf + i + 4) = fspi_readl(f, base + FSPI_RFDR + 4);
- i += 8;
- }
+ *(u32 *)(buf + i) = fspi_readl(f, base + FSPI_RFDR);
+ *(u32 *)(buf + i + 4) = fspi_readl(f, base + FSPI_RFDR + 4);
+ i += 8;
/* move the FIFO pointer */
fspi_writel(f, FSPI_INTR_IPRXWA, base + FSPI_INTR);
}
@@ -778,7 +790,7 @@ static void nxp_fspi_read_rxfifo(struct nxp_fspi *f,
u32 tmp;
int size, j;
- buf = op->data.buf.in + i;
+ buf += i;
len -= i;
/* Wait for RXFIFO available */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
@@ -826,19 +838,16 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
* address A-1, need to read one more byte to get all
* data needed.
*/
- if ((op->addr.val & 1) && (op->data.dir == SPI_MEM_DATA_IN) &&
- op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr) {
- f->flags |= FSPI_DTR_ODD_ADDR;
+ if (f->flags & FSPI_DTR_ODD_ADDR)
fspi_writel(f, (op->data.nbytes + 1) |
(SEQID_LUT << FSPI_IPCR1_SEQID_SHIFT) |
(seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
base + FSPI_IPCR1);
- } else {
+ else
fspi_writel(f, op->data.nbytes |
(SEQID_LUT << FSPI_IPCR1_SEQID_SHIFT) |
(seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
base + FSPI_IPCR1);
- }
/* Trigger the LUT now. */
fspi_writel(f, FSPI_IPCMD_TRG, base + FSPI_IPCMD);
@@ -914,6 +923,12 @@ static int nxp_fspi_adjust_op_size(struct spi_slave *slave,
if (op->data.nbytes > f->devtype_data->txfifo)
op->data.nbytes = f->devtype_data->txfifo;
} else {
+ /* need to handle the OCTAL DTR read with odd dtr case */
+ if ((op->addr.val & 1) && op->cmd.dtr && op->addr.dtr &&
+ op->dummy.dtr && op->data.dtr) {
+ f->flags |= FSPI_DTR_ODD_ADDR;
+ }
+
if (op->data.nbytes > f->devtype_data->ahb_buf_size)
op->data.nbytes = f->devtype_data->ahb_buf_size;
else if (op->data.nbytes > (f->devtype_data->rxfifo - 4))
@@ -923,8 +938,19 @@ static int nxp_fspi_adjust_op_size(struct spi_slave *slave,
/* Limit data bytes to RX FIFO in case of IP read only */
if (needs_ip_only(f) &&
op->data.dir == SPI_MEM_DATA_IN &&
- op->data.nbytes > f->devtype_data->rxfifo)
- op->data.nbytes = f->devtype_data->rxfifo;
+ op->data.nbytes > f->devtype_data->rxfifo) {
+ /*
+ * adjust size to to odd number so the OCTAL DTR
+ * read with odd address only triggers once, when
+ * reading large chunks of data
+ */
+ if (f->flags & FSPI_DTR_ODD_ADDR) {
+ op->data.nbytes = f->devtype_data->rxfifo
+ - 4 - 1;
+ } else {
+ op->data.nbytes = f->devtype_data->rxfifo;
+ }
+ }
return 0;
}