Skip to content

Commit

Permalink
Fix split transfer rates for read and write.
Browse files Browse the repository at this point in the history
Remove unnecessary memory barrier in transfer.
Reorder error read in transfer to allow PCIe pipelining.
Add option spi_noqueue to forcefully disable queue reads and writes for debugging.
Update documentation man-page hm2_rp5spi(9).
  • Loading branch information
BsAtHome committed Nov 24, 2024
1 parent fa7275a commit 171c6b3
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 22 deletions.
19 changes: 12 additions & 7 deletions docs/src/man/man9/hm2_rp5spi.9.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ ____
pin-number in parentheses): - SPI0: MOSI=10(19), MISO=9(21),
SCLK=11(23), CE0=8(24), CE1=7(26) - SPI1: MOSI=20(38), MISO=19(35),
SCLK=21(40), CE0=18(12), CE1=17(11), CE2=16(36).
*spi_noqueue* [default: 0 (off)]::
Force disable queued command processing. Normally, all requests are
queued if requested by upstream and sent in one bulk transfer. This
reduces overhead significantly by up to 35%. Disabling the queue makes
each transfer visible and more easily debug-able. Set to any non-zero
value to disable the queue.
*spi_debug* [default: -1]::
Set the message level of the running process. The message level is set
if *spi_debug* is set to a positive value between 0 and 5, where 0
Expand All @@ -60,7 +65,7 @@ Mesa's SPI based Anything I/O boards with SPI enabled HostMot2 firmware
to the LinuxCNC HAL. This driver is not based on the linux spidev
driver, but on a dedicated RP1 SPI hardware interface driver.

The supported boards are: 7I90HD, 7C80 and 7C81.
The supported boards are: 7I90HD, 7I43, 7C80 and 7C81.

The board must have a compatible firmware (like: 7i90_spi_*-bit,
7c80_*.bit and 7c81_*.bit) loaded on the board by the *mesaflash*(1)
Expand All @@ -70,11 +75,11 @@ hm2_rp5spi is only available when LinuxCNC is configured with "uspace"
realtime. It works with Raspian and PREEMPT_RT kernel.

It is *strongly* recommended that you unload/disable the kernel's SPI
drivers *dw_spi* and *dw_spi_mmio* by disabling SPI using the
*raspi-config* program's "Interface Options" configuration. The
hm2_rp5spi driver attempts to unload the kernel driver at startup and
restore it at exit if loaded at startup. However, there are no
guarantees about the effectiveness of the module unload/load actions. +
drivers *dw_spi* and *dw_spi_mmio* (on Raspbian use the *raspi-config*
program's "Interface Options" configuration). The hm2_rp5spi driver
attempts to unload the kernel driver at startup and restore it at exit
if loaded at startup. However, there are no guarantees about the
effectiveness of the module unload/load actions. +
Please note that having both kernel and user-space SPI drivers installed
can result in unexpected interactions and system instabilities.

Expand Down
43 changes: 28 additions & 15 deletions src/hal/drivers/mesa-hostmot2/hm2_rp5spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,14 @@ RTAPI_MP_ARRAY_INT(spi_pull_sclk, RPSPI_MAX_SPI, "Enable/disable pull-{up,down}
static int spi_probe = SPI0_PROBE_CE0;
RTAPI_MP_INT(spi_probe, "Bit-field to select which SPI/CE combinations to probe (default 1 (SPI0/CE0))")

/*
* Normally, all requests are queued if requested by upstream and sent in one
* bulk transfer. This reduces overhead significantly. Disabling the queue make
* each transfer visible and more easily debugable.
*/
static int spi_noqueue = 0;
RTAPI_MP_INT(spi_noqueue, "Disable queued SPI requests, use for debugging only (default 0 (off))")

/*
* Set the message level for debugging purpose. This has the (side-)effect that
* all modules within this process will start spitting out messages at the
Expand Down Expand Up @@ -345,7 +353,7 @@ static inline void spi_reset(dw_ssi_t *port)
* Transfer a buffer of words to the SPI port and fill the same buffer with the
* data coming from the SPI port.
*/
static int spi_transfer(hm2_rp5spi_t *hm2, uint32_t *wptr, size_t txlen)
static int spi_transfer(hm2_rp5spi_t *hm2, uint32_t *wptr, size_t txlen, int rw)
{
dw_ssi_t *port = hm2->port;
size_t rxlen = txlen; // words to receive
Expand All @@ -360,7 +368,7 @@ static int spi_transfer(hm2_rp5spi_t *hm2, uint32_t *wptr, size_t txlen)
// Setup transfer
// 32-bit, transmit/receive transfers, SPI mode 0 (CPHA=0, CPOL=0)
reg_wr(&port->ctrlr0, DW_SSI_CTRLR0_DFS_32(32-1) | DW_SSI_CTRLR0_TMOD(DW_SSI_CTRLR0_TMOD_TXRX));
reg_wr_raw(&port->baudr, hm2->clkdivr);
reg_wr_raw(&port->baudr, rw ? hm2->clkdivr : hm2->clkdivw);
reg_wr_raw(&port->ser, hm2->cemask);

reg_wr_raw(&port->ssienr, DW_SSI_SSIENR_SSI_EN); // Enable port
Expand All @@ -373,12 +381,15 @@ static int spi_transfer(hm2_rp5spi_t *hm2, uint32_t *wptr, size_t txlen)
fifo--;
}

wmb(); // Ensure all writes before reading
// We don't need to add a memory barrier. The next loop runs about rxlen
// and the code will stall on the register reads. There is no read/write
// overlap that may be problematic.

while(rxlen > 0) {
// Get the rx fifo level and read as many as available
uint32_t tff;
tff = fifo = reg_rd_raw(&port->rxflr);
uint32_t tff = fifo = reg_rd_raw(&port->rxflr);
// Already get the int status register; this read will pipeline
uint32_t risr = reg_rd_raw(&port->risr);
while(rxlen > 0 && fifo > 0) {
*rptr = reg_rd_raw(&port->dr0);
rptr++;
Expand All @@ -398,7 +409,7 @@ static int spi_transfer(hm2_rp5spi_t *hm2, uint32_t *wptr, size_t txlen)
}

// Check for receive errors
if(reg_rd_raw(&port->risr) & (DW_SSI_RISR_RXOIR)) {
if(risr & (DW_SSI_RISR_RXOIR)) {
// The receive fifo overflowed. A bad sign...
// Abort the transfer
rv = -EIO;
Expand Down Expand Up @@ -438,7 +449,7 @@ static int hm2_rp5spi_write(hm2_lowlevel_io_t *llio, rtapi_u32 addr, const void

txbuf[0] = mk_write_cmd(addr, txlen, true); // Setup write command
memcpy(&txbuf[1], buffer, size); // Setup write data
return spi_transfer(hm2, txbuf, txlen + 1); // Do transfer
return spi_transfer(hm2, txbuf, txlen + 1, 0); // Do transfer
}

/*
Expand All @@ -461,7 +472,7 @@ static int hm2_rp5spi_read(hm2_lowlevel_io_t *llio, rtapi_u32 addr, void *buffer

memset(rxbuf, 0, sizeof(rxbuf)); // Clear buffer; reads stuff zero writes
rxbuf[0] = mk_read_cmd(addr, rxlen, true); // Setup read command
rv = spi_transfer(hm2, rxbuf, rxlen + 1); // Do transfer
rv = spi_transfer(hm2, rxbuf, rxlen + 1, 1); // Do transfer
memcpy(buffer, &rxbuf[1], size); // Copy received data (even with errors...)
return rv;
}
Expand Down Expand Up @@ -506,7 +517,7 @@ static int hm2_rp5spi_queue_read(hm2_lowlevel_io_t *llio, rtapi_u32 addr, void *
static int hm2_rp5spi_send_queued_reads(hm2_lowlevel_io_t *llio)
{
hm2_rp5spi_t *hm2 = (hm2_rp5spi_t *)llio;
int rv = spi_transfer(hm2, hm2->rbuf.ptr, hm2->rbuf.n);
int rv = spi_transfer(hm2, hm2->rbuf.ptr, hm2->rbuf.n, 1);
if(rv >= 0) {
// The transfer read the data into the read buffer. Now copy it into
// the individual read requests' buffers.
Expand Down Expand Up @@ -557,7 +568,7 @@ static int hm2_rp5spi_queue_write(hm2_lowlevel_io_t *llio, rtapi_u32 addr, const
static int hm2_rp5spi_send_queued_writes(hm2_lowlevel_io_t *llio)
{
hm2_rp5spi_t *hm2 = (hm2_rp5spi_t *)llio;
int rv = spi_transfer(hm2, hm2->wbuf.ptr, hm2->wbuf.n);
int rv = spi_transfer(hm2, hm2->wbuf.ptr, hm2->wbuf.n, 0);
hm2->wbuf.n = 0; // Reset the queue buffer
return rv;
}
Expand Down Expand Up @@ -1035,12 +1046,14 @@ static int hm2_rp5spi_setup(void)

LL_INFO("SPI%d/CE%d\n", iddev, idce);
boards[j].llio.read = hm2_rp5spi_read;
boards[j].llio.queue_read = hm2_rp5spi_queue_read;
boards[j].llio.send_queued_reads = hm2_rp5spi_send_queued_reads;
boards[j].llio.receive_queued_reads = hm2_rp5spi_receive_queued_reads;
boards[j].llio.write = hm2_rp5spi_write;
boards[j].llio.queue_write = hm2_rp5spi_queue_write;
boards[j].llio.send_queued_writes = hm2_rp5spi_send_queued_writes;
if(!spi_noqueue) {
boards[j].llio.queue_read = hm2_rp5spi_queue_read;
boards[j].llio.send_queued_reads = hm2_rp5spi_send_queued_reads;
boards[j].llio.receive_queued_reads = hm2_rp5spi_receive_queued_reads;
boards[j].llio.queue_write = hm2_rp5spi_queue_write;
boards[j].llio.send_queued_writes = hm2_rp5spi_send_queued_writes;
}
LL_INFO("SPI%d/CE%d write clock rate calculated: %d Hz (clkdiv=%u)\n", iddev, idce, RP1_SPI_CLK / boards[j].clkdivw, boards[j].clkdivw);
LL_INFO("SPI%d/CE%d read clock rate calculated: %d Hz (clkdiv=%u)\n", iddev, idce, RP1_SPI_CLK / boards[j].clkdivr, boards[j].clkdivr);

Expand Down

0 comments on commit 171c6b3

Please sign in to comment.