Skip to content

Commit fa08b56

Browse files
Demon000broonie
authored andcommitted
spi: rzv2h-rspi: add support for DMA mode
The DMA controller can be used to transfer data to and from the SPI controller without involving the CPU for each word of a SPI transfer. Add support for DMA mode. Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com> Link: https://patch.msgid.link/20251201134229.600817-12-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 163345e commit fa08b56

1 file changed

Lines changed: 168 additions & 1 deletion

File tree

drivers/spi/spi-rzv2h-rspi.c

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/bitops.h>
1010
#include <linux/bits.h>
1111
#include <linux/clk.h>
12+
#include <linux/dmaengine.h>
1213
#include <linux/interrupt.h>
1314
#include <linux/io.h>
1415
#include <linux/limits.h>
@@ -21,6 +22,8 @@
2122
#include <linux/spi/spi.h>
2223
#include <linux/wait.h>
2324

25+
#include "internals.h"
26+
2427
/* Registers */
2528
#define RSPI_SPDR 0x00
2629
#define RSPI_SPCR 0x08
@@ -96,6 +99,7 @@ struct rzv2h_rspi_info {
9699
struct rzv2h_rspi_priv {
97100
struct spi_controller *controller;
98101
const struct rzv2h_rspi_info *info;
102+
struct platform_device *pdev;
99103
void __iomem *base;
100104
struct clk *tclk;
101105
struct clk *pclk;
@@ -108,6 +112,7 @@ struct rzv2h_rspi_priv {
108112
u8 spr;
109113
u8 brdv;
110114
bool use_pclk;
115+
bool dma_callbacked;
111116
};
112117

113118
#define RZV2H_RSPI_TX(func, type) \
@@ -219,6 +224,20 @@ static int rzv2h_rspi_receive(struct rzv2h_rspi_priv *rspi, void *rxbuf,
219224
return 0;
220225
}
221226

227+
static bool rzv2h_rspi_can_dma(struct spi_controller *ctlr, struct spi_device *spi,
228+
struct spi_transfer *xfer)
229+
{
230+
struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr);
231+
232+
if (ctlr->fallback)
233+
return false;
234+
235+
if (!ctlr->dma_tx || !ctlr->dma_rx)
236+
return false;
237+
238+
return xfer->len > rspi->info->fifo_size;
239+
}
240+
222241
static int rzv2h_rspi_transfer_pio(struct rzv2h_rspi_priv *rspi,
223242
struct spi_device *spi,
224243
struct spi_transfer *transfer,
@@ -240,21 +259,149 @@ static int rzv2h_rspi_transfer_pio(struct rzv2h_rspi_priv *rspi,
240259
return ret;
241260
}
242261

262+
static void rzv2h_rspi_dma_complete(void *arg)
263+
{
264+
struct rzv2h_rspi_priv *rspi = arg;
265+
266+
rspi->dma_callbacked = 1;
267+
wake_up_interruptible(&rspi->wait);
268+
}
269+
270+
static struct dma_async_tx_descriptor *
271+
rzv2h_rspi_setup_dma_channel(struct rzv2h_rspi_priv *rspi,
272+
struct dma_chan *chan, struct sg_table *sg,
273+
enum dma_slave_buswidth width,
274+
enum dma_transfer_direction direction)
275+
{
276+
struct dma_slave_config config = {
277+
.dst_addr = rspi->pdev->resource->start + RSPI_SPDR,
278+
.src_addr = rspi->pdev->resource->start + RSPI_SPDR,
279+
.dst_addr_width = width,
280+
.src_addr_width = width,
281+
.direction = direction,
282+
};
283+
struct dma_async_tx_descriptor *desc;
284+
int ret;
285+
286+
ret = dmaengine_slave_config(chan, &config);
287+
if (ret)
288+
return ERR_PTR(ret);
289+
290+
desc = dmaengine_prep_slave_sg(chan, sg->sgl, sg->nents, direction,
291+
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
292+
if (!desc)
293+
return ERR_PTR(-EAGAIN);
294+
295+
if (direction == DMA_DEV_TO_MEM) {
296+
desc->callback = rzv2h_rspi_dma_complete;
297+
desc->callback_param = rspi;
298+
}
299+
300+
return desc;
301+
}
302+
303+
static enum dma_slave_buswidth
304+
rzv2h_rspi_dma_width(struct rzv2h_rspi_priv *rspi)
305+
{
306+
switch (rspi->bytes_per_word) {
307+
case 4:
308+
return DMA_SLAVE_BUSWIDTH_4_BYTES;
309+
case 2:
310+
return DMA_SLAVE_BUSWIDTH_2_BYTES;
311+
case 1:
312+
return DMA_SLAVE_BUSWIDTH_1_BYTE;
313+
default:
314+
return DMA_SLAVE_BUSWIDTH_UNDEFINED;
315+
}
316+
}
317+
318+
static int rzv2h_rspi_transfer_dma(struct rzv2h_rspi_priv *rspi,
319+
struct spi_device *spi,
320+
struct spi_transfer *transfer,
321+
unsigned int words_to_transfer)
322+
{
323+
struct dma_async_tx_descriptor *tx_desc = NULL, *rx_desc = NULL;
324+
enum dma_slave_buswidth width;
325+
dma_cookie_t cookie;
326+
int ret;
327+
328+
width = rzv2h_rspi_dma_width(rspi);
329+
if (width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
330+
return -EINVAL;
331+
332+
rx_desc = rzv2h_rspi_setup_dma_channel(rspi, rspi->controller->dma_rx,
333+
&transfer->rx_sg, width,
334+
DMA_DEV_TO_MEM);
335+
if (IS_ERR(rx_desc))
336+
return PTR_ERR(rx_desc);
337+
338+
tx_desc = rzv2h_rspi_setup_dma_channel(rspi, rspi->controller->dma_tx,
339+
&transfer->tx_sg, width,
340+
DMA_MEM_TO_DEV);
341+
if (IS_ERR(tx_desc))
342+
return PTR_ERR(tx_desc);
343+
344+
cookie = dmaengine_submit(rx_desc);
345+
if (dma_submit_error(cookie))
346+
return cookie;
347+
348+
cookie = dmaengine_submit(tx_desc);
349+
if (dma_submit_error(cookie)) {
350+
dmaengine_terminate_sync(rspi->controller->dma_rx);
351+
return cookie;
352+
}
353+
354+
/*
355+
* DMA transfer does not need IRQs to be enabled.
356+
* For PIO, we only use RX IRQ, so disable that.
357+
*/
358+
disable_irq(rspi->irq_rx);
359+
360+
rspi->dma_callbacked = 0;
361+
362+
dma_async_issue_pending(rspi->controller->dma_rx);
363+
dma_async_issue_pending(rspi->controller->dma_tx);
364+
rzv2h_rspi_clear_all_irqs(rspi);
365+
366+
ret = wait_event_interruptible_timeout(rspi->wait, rspi->dma_callbacked, HZ);
367+
if (ret) {
368+
dmaengine_synchronize(rspi->controller->dma_tx);
369+
dmaengine_synchronize(rspi->controller->dma_rx);
370+
ret = 0;
371+
} else {
372+
dmaengine_terminate_sync(rspi->controller->dma_tx);
373+
dmaengine_terminate_sync(rspi->controller->dma_rx);
374+
ret = -ETIMEDOUT;
375+
}
376+
377+
enable_irq(rspi->irq_rx);
378+
379+
return ret;
380+
}
381+
243382
static int rzv2h_rspi_transfer_one(struct spi_controller *controller,
244383
struct spi_device *spi,
245384
struct spi_transfer *transfer)
246385
{
247386
struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(controller);
387+
bool is_dma = spi_xfer_is_dma_mapped(controller, spi, transfer);
248388
unsigned int words_to_transfer;
249389
int ret;
250390

251391
transfer->effective_speed_hz = rspi->freq;
252392
words_to_transfer = transfer->len / rspi->bytes_per_word;
253393

254-
ret = rzv2h_rspi_transfer_pio(rspi, spi, transfer, words_to_transfer);
394+
if (is_dma)
395+
ret = rzv2h_rspi_transfer_dma(rspi, spi, transfer, words_to_transfer);
396+
else
397+
ret = rzv2h_rspi_transfer_pio(rspi, spi, transfer, words_to_transfer);
255398

256399
rzv2h_rspi_clear_all_irqs(rspi);
257400

401+
if (is_dma && ret == -EAGAIN)
402+
/* Retry with PIO */
403+
transfer->error = SPI_TRANS_FAIL_NO_START;
404+
258405
return ret;
259406
}
260407

@@ -557,6 +704,7 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
557704
platform_set_drvdata(pdev, rspi);
558705

559706
rspi->controller = controller;
707+
rspi->pdev = pdev;
560708

561709
rspi->info = device_get_match_data(dev);
562710

@@ -613,6 +761,7 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
613761
controller->unprepare_message = rzv2h_rspi_unprepare_message;
614762
controller->num_chipselect = 4;
615763
controller->transfer_one = rzv2h_rspi_transfer_one;
764+
controller->can_dma = rzv2h_rspi_can_dma;
616765

617766
tclk_rate = clk_round_rate(rspi->tclk, 0);
618767
if (tclk_rate < 0)
@@ -630,6 +779,24 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
630779
RSPI_SPBR_SPR_MIN,
631780
RSPI_SPCMD_BRDV_MIN);
632781

782+
controller->dma_tx = devm_dma_request_chan(dev, "tx");
783+
if (IS_ERR(controller->dma_tx)) {
784+
ret = dev_warn_probe(dev, PTR_ERR(controller->dma_tx),
785+
"failed to request TX DMA channel\n");
786+
if (ret == -EPROBE_DEFER)
787+
return ret;
788+
controller->dma_tx = NULL;
789+
}
790+
791+
controller->dma_rx = devm_dma_request_chan(dev, "rx");
792+
if (IS_ERR(controller->dma_rx)) {
793+
ret = dev_warn_probe(dev, PTR_ERR(controller->dma_rx),
794+
"failed to request RX DMA channel\n");
795+
if (ret == -EPROBE_DEFER)
796+
return ret;
797+
controller->dma_rx = NULL;
798+
}
799+
633800
device_set_node(&controller->dev, dev_fwnode(dev));
634801

635802
ret = devm_spi_register_controller(dev, controller);

0 commit comments

Comments
 (0)