* [PATCH] spi: xilinx: Inhibit transmitter while filling TX FIFO
@ 2021-05-07 21:53 Jonathan Lemon
2021-05-07 23:02 ` Ricardo Ribalda Delgado
0 siblings, 1 reply; 5+ messages in thread
From: Jonathan Lemon @ 2021-05-07 21:53 UTC (permalink / raw)
To: ribalda, broonie; +Cc: linux-spi, kernel-team
Contrary to the comment in xilinx_spi_txrx_bufs(), the transmitter
was not disabled on entry. On my particular board, when sending a PP
sequence, the first address byte was clocked out 3 times, writing data
into the wrong location, and generally locking up the chip.
Fix this by inhibiting the transmitter at initialization time, and
then enabling/disabling it appropriately.
With this patch, I can successfully read/erase/program the flash.
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
---
drivers/spi/spi-xilinx.c | 54 +++++++++++++++++++++++++++++++---------
1 file changed, 42 insertions(+), 12 deletions(-)
diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c
index 523edfdf5dcd..10eccfb09e20 100644
--- a/drivers/spi/spi-xilinx.c
+++ b/drivers/spi/spi-xilinx.c
@@ -179,8 +179,8 @@ static void xspi_init_hw(struct xilinx_spi *xspi)
/* Disable the transmitter, enable Manual Slave Select Assertion,
* put SPI controller into master mode, and enable it */
xspi->write_fn(XSPI_CR_MANUAL_SSELECT | XSPI_CR_MASTER_MODE |
- XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | XSPI_CR_RXFIFO_RESET,
- regs_base + XSPI_CR_OFFSET);
+ XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | XSPI_CR_RXFIFO_RESET |
+ XSPI_CR_TRANS_INHIBIT, regs_base + XSPI_CR_OFFSET);
}
static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
@@ -235,14 +235,46 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi,
return 0;
}
+static void
+xilinx_spi_inhibit_master(struct xilinx_spi *xspi, bool inhibit)
+{
+ u16 cr;
+
+ cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
+ if (inhibit)
+ cr |= XSPI_CR_TRANS_INHIBIT;
+ else
+ cr &= ~XSPI_CR_TRANS_INHIBIT;
+ xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
+}
+
+static void
+xilinx_spi_enable_transfer(struct xilinx_spi *xspi)
+{
+ xilinx_spi_inhibit_master(xspi, false);
+}
+
+static void
+xilinx_spi_disable_transfer(struct xilinx_spi *xspi)
+{
+ xilinx_spi_inhibit_master(xspi, true);
+}
+
+static bool
+xilinx_spi_is_transfer_disabled(struct xilinx_spi *xspi)
+{
+ u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
+ return !!(cr & XSPI_CR_TRANS_INHIBIT);
+}
+
static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
int remaining_words; /* the number of words left to transfer */
bool use_irq = false;
- u16 cr = 0;
/* We get here with transmitter inhibited */
+ WARN_ON_ONCE(!xilinx_spi_is_transfer_disabled(xspi));
xspi->tx_ptr = t->tx_buf;
xspi->rx_ptr = t->rx_buf;
@@ -252,14 +284,13 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
u32 isr;
use_irq = true;
/* Inhibit irq to avoid spurious irqs on tx_empty*/
- cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
- xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
- xspi->regs + XSPI_CR_OFFSET);
+
/* ACK old irqs (if any) */
isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET);
if (isr)
xspi->write_fn(isr,
xspi->regs + XIPIF_V123B_IISR_OFFSET);
+
/* Enable the global IPIF interrupt */
xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
xspi->regs + XIPIF_V123B_DGIER_OFFSET);
@@ -280,9 +311,9 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Start the transfer by not inhibiting the transmitter any
* longer
*/
+ xilinx_spi_enable_transfer(xspi);
if (use_irq) {
- xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
wait_for_completion(&xspi->done);
/* A transmit has just completed. Process received data
* and check for more data to transmit. Always inhibit
@@ -290,8 +321,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
* register/FIFO, or make sure it is stopped if we're
* done.
*/
- xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
- xspi->regs + XSPI_CR_OFFSET);
+ xilinx_spi_disable_transfer(xspi);
sr = XSPI_SR_TX_EMPTY_MASK;
} else
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
@@ -325,10 +355,10 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
remaining_words -= n_words;
}
- if (use_irq) {
+ if (use_irq)
xspi->write_fn(0, xspi->regs + XIPIF_V123B_DGIER_OFFSET);
- xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
- }
+
+ xilinx_spi_disable_transfer(xspi);
return t->len;
}
--
2.27.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH] spi: xilinx: Inhibit transmitter while filling TX FIFO
2021-05-07 21:53 [PATCH] spi: xilinx: Inhibit transmitter while filling TX FIFO Jonathan Lemon
@ 2021-05-07 23:02 ` Ricardo Ribalda Delgado
[not found] ` <3E382801-224D-4B11-961D-4822F51F5496@gmail.com>
0 siblings, 1 reply; 5+ messages in thread
From: Ricardo Ribalda Delgado @ 2021-05-07 23:02 UTC (permalink / raw)
To: Jonathan Lemon; +Cc: Mark Brown, linux-spi, kernel-team
Hi Jonathan
Thanks for your patch.
On Fri, May 7, 2021 at 11:53 PM Jonathan Lemon <jonathan.lemon@gmail.com> wrote:
>
> Contrary to the comment in xilinx_spi_txrx_bufs(), the transmitter
> was not disabled on entry. On my particular board, when sending a PP
> sequence, the first address byte was clocked out 3 times, writing data
> into the wrong location, and generally locking up the chip.
Sorry, what do you mean by PP sequence?
By clocked out 3 times you mean that the sequence is sent like
B0........B1.........B2
instead of:
B0B1B2
?
If your hardware supports IRQ. can you try forcing use_irq to true?
>
> Fix this by inhibiting the transmitter at initialization time, and
> then enabling/disabling it appropriately.
>
> With this patch, I can successfully read/erase/program the flash.
Can you provide a bit more details about your setup? What core version
are you using? C_PARAMS? Flash?
>
In general, I think it makes more sense to title your patch as:
Inhibit transfer while idle. Because the current code already inhibits
before sending data in irq mode.
The current design tries to limit as much as possible the register
access and only enable inhibit in irq mode.
In principle, I believe both approaches shall be equally valid, but if
you have a use case that does not work with the current approach your
patch is very welcome.
> Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
> ---
> drivers/spi/spi-xilinx.c | 54 +++++++++++++++++++++++++++++++---------
> 1 file changed, 42 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c
> index 523edfdf5dcd..10eccfb09e20 100644
> --- a/drivers/spi/spi-xilinx.c
> +++ b/drivers/spi/spi-xilinx.c
> @@ -179,8 +179,8 @@ static void xspi_init_hw(struct xilinx_spi *xspi)
> /* Disable the transmitter, enable Manual Slave Select Assertion,
> * put SPI controller into master mode, and enable it */
> xspi->write_fn(XSPI_CR_MANUAL_SSELECT | XSPI_CR_MASTER_MODE |
> - XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | XSPI_CR_RXFIFO_RESET,
> - regs_base + XSPI_CR_OFFSET);
> + XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | XSPI_CR_RXFIFO_RESET |
> + XSPI_CR_TRANS_INHIBIT, regs_base + XSPI_CR_OFFSET);
> }
>
> static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
> @@ -235,14 +235,46 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi,
> return 0;
> }
>
> +static void
> +xilinx_spi_inhibit_master(struct xilinx_spi *xspi, bool inhibit)
> +{
> + u16 cr;
> +
> + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
> + if (inhibit)
> + cr |= XSPI_CR_TRANS_INHIBIT;
> + else
> + cr &= ~XSPI_CR_TRANS_INHIBIT;
> + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
> +}
> +
> +static void
> +xilinx_spi_enable_transfer(struct xilinx_spi *xspi)
> +{
> + xilinx_spi_inhibit_master(xspi, false);
> +}
> +
> +static void
> +xilinx_spi_disable_transfer(struct xilinx_spi *xspi)
> +{
> + xilinx_spi_inhibit_master(xspi, true);
> +}
> +
> +static bool
> +xilinx_spi_is_transfer_disabled(struct xilinx_spi *xspi)
> +{
> + u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
> + return !!(cr & XSPI_CR_TRANS_INHIBIT);
> +}
> +
Although these helper functions make very clear what you want to
achieve, they run a lot of extra register access.
In some platforms register access is VERY slow.
Please embed them into txrx_bufs (use the cr variable as before)
> static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
> {
> struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
> int remaining_words; /* the number of words left to transfer */
> bool use_irq = false;
> - u16 cr = 0;
>
> /* We get here with transmitter inhibited */
> + WARN_ON_ONCE(!xilinx_spi_is_transfer_disabled(xspi));
>
> xspi->tx_ptr = t->tx_buf;
> xspi->rx_ptr = t->rx_buf;
> @@ -252,14 +284,13 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
> u32 isr;
> use_irq = true;
> /* Inhibit irq to avoid spurious irqs on tx_empty*/
You shall remove this comment also
> - cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
> - xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
> - xspi->regs + XSPI_CR_OFFSET);
> +
> /* ACK old irqs (if any) */
> isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET);
> if (isr)
> xspi->write_fn(isr,
> xspi->regs + XIPIF_V123B_IISR_OFFSET);
> +
> /* Enable the global IPIF interrupt */
> xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
> xspi->regs + XIPIF_V123B_DGIER_OFFSET);
> @@ -280,9 +311,9 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
> /* Start the transfer by not inhibiting the transmitter any
> * longer
> */
> + xilinx_spi_enable_transfer(xspi);
>
> if (use_irq) {
> - xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
> wait_for_completion(&xspi->done);
> /* A transmit has just completed. Process received data
> * and check for more data to transmit. Always inhibit
> @@ -290,8 +321,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
> * register/FIFO, or make sure it is stopped if we're
> * done.
This comment is not valid anymore., the ISR does not refill the FIFO.
Can you send a following patch fixing this?
Thanks!
> */
> - xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
> - xspi->regs + XSPI_CR_OFFSET);
> + xilinx_spi_disable_transfer(xspi);
> sr = XSPI_SR_TX_EMPTY_MASK;
> } else
> sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
> @@ -325,10 +355,10 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
> remaining_words -= n_words;
> }
>
> - if (use_irq) {
> + if (use_irq)
> xspi->write_fn(0, xspi->regs + XIPIF_V123B_DGIER_OFFSET);
> - xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
> - }
> +
> + xilinx_spi_disable_transfer(xspi);
I believe this shall be moved to after:
remaining_words -= n_words;
and be something like:
if (!use_irq)
xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT , xspi->regs + XSPI_CR_OFFSET);
>
> return t->len;
> }
> --
> 2.27.0
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2021-05-08 10:11 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-05-07 21:53 [PATCH] spi: xilinx: Inhibit transmitter while filling TX FIFO Jonathan Lemon
2021-05-07 23:02 ` Ricardo Ribalda Delgado
[not found] ` <3E382801-224D-4B11-961D-4822F51F5496@gmail.com>
2021-05-08 2:44 ` Jonathan Lemon
2021-05-08 10:02 ` Ricardo Ribalda Delgado
2021-05-08 10:01 ` Ricardo Ribalda Delgado
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox