Skip to content

Commit 7e1c28f

Browse files
Thangaraj Samynathanbroonie
authored andcommitted
spi: spi-pci1xxxx: enable concurrent DMA read/write across SPI transfers
Refactor the pci1xxxx SPI driver to allow overlapping DMA read and write operations across SPI transfers. This improves throughput and reduces idle time between SPI transactions. Transfer sequence: - Start with a DMA read to load TX data from host to device buffer. - After DMA read completes, trigger the SPI transfer. - On SPI completion: - Start DMA write to copy received data from RX buffer to host. - Start the next DMA read to prepare TX data for the following transfer. - Begin the next SPI transfer after both DMA write and read complete. To implement this sequence, the following changes were made: - Added dma_completion_count to track and synchronize DMA completions. - Split DMA setup into separate functions for TX (read) and RX (write). - Introduced separate spinlocks for safe access to RD and WR DMA registers. This new flow enables efficient pipelining by overlapping data preparation and completion stages, leading to better SPI transfer performance and utilization of DMA engines. Signed-off-by: Thangaraj Samynathan <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 46e001c commit 7e1c28f

File tree

1 file changed

+47
-35
lines changed

1 file changed

+47
-35
lines changed

drivers/spi/spi-pci1xxxx.c

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ struct pci1xxxx_spi_internal {
140140
int irq[NUM_VEC_PER_INST];
141141
int mode;
142142
bool spi_xfer_in_progress;
143+
atomic_t dma_completion_count;
143144
void *rx_buf;
144145
bool dma_aborted_rd;
145146
u32 bytes_recvd;
@@ -163,8 +164,10 @@ struct pci1xxxx_spi {
163164
u8 dev_rev;
164165
void __iomem *reg_base;
165166
void __iomem *dma_offset_bar;
166-
/* lock to safely access the DMA registers in isr */
167-
spinlock_t dma_reg_lock;
167+
/* lock to safely access the DMA RD registers in isr */
168+
spinlock_t dma_rd_reg_lock;
169+
/* lock to safely access the DMA RD registers in isr */
170+
spinlock_t dma_wr_reg_lock;
168171
bool can_dma;
169172
struct pci1xxxx_spi_internal *spi_int[] __counted_by(total_hw_instances);
170173
};
@@ -330,7 +333,8 @@ static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int hw_inst, int
330333
if (ret)
331334
return ret;
332335

333-
spin_lock_init(&spi_bus->dma_reg_lock);
336+
spin_lock_init(&spi_bus->dma_rd_reg_lock);
337+
spin_lock_init(&spi_bus->dma_wr_reg_lock);
334338
writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN);
335339
writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN);
336340

@@ -464,6 +468,7 @@ static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p)
464468
{
465469
u32 regval;
466470

471+
atomic_set(&p->dma_completion_count, 0);
467472
regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
468473
regval |= SPI_MST_CTL_GO;
469474
writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
@@ -536,7 +541,6 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
536541
{
537542
struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi_ctlr);
538543
struct pci1xxxx_spi *par = p->parent;
539-
dma_addr_t rx_dma_addr = 0;
540544
dma_addr_t tx_dma_addr = 0;
541545
int ret = 0;
542546
u32 regval;
@@ -545,6 +549,7 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
545549
p->tx_sgl = xfer->tx_sg.sgl;
546550
p->rx_sgl = xfer->rx_sg.sgl;
547551
p->rx_buf = xfer->rx_buf;
552+
atomic_set(&p->dma_completion_count, 1);
548553
regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
549554
writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
550555

@@ -561,13 +566,9 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
561566
writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
562567

563568
tx_dma_addr = sg_dma_address(p->tx_sgl);
564-
rx_dma_addr = sg_dma_address(p->rx_sgl);
565569
p->tx_sgl_len = sg_dma_len(p->tx_sgl);
566-
p->rx_sgl_len = sg_dma_len(p->rx_sgl);
567570
pci1xxxx_spi_setup(par, p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len);
568571
pci1xxxx_spi_setup_dma_to_io(p, (tx_dma_addr), p->tx_sgl_len);
569-
if (rx_dma_addr)
570-
pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
571572
writel(p->hw_inst, par->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
572573

573574
reinit_completion(&p->spi_xfer_done);
@@ -657,32 +658,33 @@ static irqreturn_t pci1xxxx_spi_isr_io(int irq, void *dev)
657658
return spi_int_fired;
658659
}
659660

660-
static void pci1xxxx_spi_setup_next_dma_transfer(struct pci1xxxx_spi_internal *p)
661+
static void pci1xxxx_spi_setup_next_dma_to_io_transfer(struct pci1xxxx_spi_internal *p)
661662
{
662663
dma_addr_t tx_dma_addr = 0;
663-
dma_addr_t rx_dma_addr = 0;
664664
u32 prev_len;
665665

666666
p->tx_sgl = sg_next(p->tx_sgl);
667-
if (p->rx_sgl)
668-
p->rx_sgl = sg_next(p->rx_sgl);
669-
if (!p->tx_sgl) {
670-
/* Clear xfer_done */
671-
complete(&p->spi_xfer_done);
672-
} else {
667+
if (p->tx_sgl) {
673668
tx_dma_addr = sg_dma_address(p->tx_sgl);
674669
prev_len = p->tx_sgl_len;
675670
p->tx_sgl_len = sg_dma_len(p->tx_sgl);
671+
pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len);
672+
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
676673
if (prev_len != p->tx_sgl_len)
677674
pci1xxxx_spi_setup(p->parent,
678675
p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len);
679-
pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len);
680-
if (p->rx_sgl) {
681-
rx_dma_addr = sg_dma_address(p->rx_sgl);
682-
p->rx_sgl_len = sg_dma_len(p->rx_sgl);
683-
pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
684-
}
685-
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
676+
}
677+
}
678+
679+
static void pci1xxxx_spi_setup_next_dma_from_io_transfer(struct pci1xxxx_spi_internal *p)
680+
{
681+
dma_addr_t rx_dma_addr = 0;
682+
683+
if (p->rx_sgl) {
684+
rx_dma_addr = sg_dma_address(p->rx_sgl);
685+
p->rx_sgl_len = sg_dma_len(p->rx_sgl);
686+
pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
687+
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG);
686688
}
687689
}
688690

@@ -693,22 +695,24 @@ static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev)
693695
unsigned long flags;
694696
u32 regval;
695697

696-
spin_lock_irqsave(&p->parent->dma_reg_lock, flags);
697698
/* Clear the DMA RD INT and start spi xfer*/
698699
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS);
699700
if (regval) {
700701
if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) {
701-
pci1xxxx_start_spi_xfer(p);
702+
/* Start the SPI transfer only if both DMA read and write are completed */
703+
if (atomic_inc_return(&p->dma_completion_count) == 2)
704+
pci1xxxx_start_spi_xfer(p);
702705
spi_int_fired = IRQ_HANDLED;
703706
}
704707
if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) {
705708
p->dma_aborted_rd = true;
706709
spi_int_fired = IRQ_HANDLED;
707710
}
711+
spin_lock_irqsave(&p->parent->dma_rd_reg_lock, flags);
712+
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
713+
p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR);
714+
spin_unlock_irqrestore(&p->parent->dma_rd_reg_lock, flags);
708715
}
709-
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
710-
p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR);
711-
spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags);
712716
return spi_int_fired;
713717
}
714718

@@ -719,22 +723,29 @@ static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev)
719723
unsigned long flags;
720724
u32 regval;
721725

722-
spin_lock_irqsave(&p->parent->dma_reg_lock, flags);
723726
/* Clear the DMA WR INT */
724727
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS);
725728
if (regval) {
726729
if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) {
727-
pci1xxxx_spi_setup_next_dma_transfer(p);
728730
spi_int_fired = IRQ_HANDLED;
731+
if (sg_is_last(p->rx_sgl)) {
732+
complete(&p->spi_xfer_done);
733+
} else {
734+
p->rx_sgl = sg_next(p->rx_sgl);
735+
if (atomic_inc_return(&p->dma_completion_count) == 2)
736+
pci1xxxx_start_spi_xfer(p);
737+
}
738+
729739
}
730740
if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) {
731741
p->dma_aborted_wr = true;
732742
spi_int_fired = IRQ_HANDLED;
733743
}
744+
spin_lock_irqsave(&p->parent->dma_wr_reg_lock, flags);
745+
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
746+
p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR);
747+
spin_unlock_irqrestore(&p->parent->dma_wr_reg_lock, flags);
734748
}
735-
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
736-
p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR);
737-
spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags);
738749
return spi_int_fired;
739750
}
740751

@@ -747,10 +758,11 @@ static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev)
747758
/* Clear the SPI GO_BIT Interrupt */
748759
regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
749760
if (regval & SPI_INTR) {
750-
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG);
761+
pci1xxxx_spi_setup_next_dma_from_io_transfer(p);
762+
pci1xxxx_spi_setup_next_dma_to_io_transfer(p);
751763
spi_int_fired = IRQ_HANDLED;
764+
writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
752765
}
753-
writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
754766
return spi_int_fired;
755767
}
756768

0 commit comments

Comments
 (0)