// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021-2022 Digiteq Automotive * author: Martin Tuma * * This module handles the DMA transfers. A standard dmaengine API as provided * by the XDMA module is used. */ #include #include #include "mgb4_core.h" #include "mgb4_dma.h" static void chan_irq(void *param) { struct mgb4_dma_channel *chan = param; complete(&chan->req_compl); } int mgb4_dma_transfer(struct mgb4_dev *mgbdev, u32 channel, bool write, u64 paddr, struct sg_table *sgt) { struct dma_slave_config cfg; struct mgb4_dma_channel *chan; struct dma_async_tx_descriptor *tx; struct pci_dev *pdev = mgbdev->pdev; int ret; memset(&cfg, 0, sizeof(cfg)); if (write) { cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = paddr; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; chan = &mgbdev->h2c_chan[channel]; } else { cfg.direction = DMA_DEV_TO_MEM; cfg.src_addr = paddr; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; chan = &mgbdev->c2h_chan[channel]; } ret = dmaengine_slave_config(chan->chan, &cfg); if (ret) { dev_err(&pdev->dev, "failed to config dma: %d\n", ret); return ret; } tx = dmaengine_prep_slave_sg(chan->chan, sgt->sgl, sgt->nents, cfg.direction, 0); if (!tx) { dev_err(&pdev->dev, "failed to prep slave sg\n"); return -EIO; } tx->callback = chan_irq; tx->callback_param = chan; ret = dma_submit_error(dmaengine_submit(tx)); if (ret) { dev_err(&pdev->dev, "failed to submit sg\n"); return -EIO; } dma_async_issue_pending(chan->chan); if (!wait_for_completion_timeout(&chan->req_compl, msecs_to_jiffies(10000))) { dev_err(&pdev->dev, "dma timeout\n"); dmaengine_terminate_sync(chan->chan); return -EIO; } return 0; } int mgb4_dma_channel_init(struct mgb4_dev *mgbdev) { int i, ret; char name[16]; struct pci_dev *pdev = mgbdev->pdev; for (i = 0; i < MGB4_VIN_DEVICES; i++) { sprintf(name, "c2h%d", i); mgbdev->c2h_chan[i].chan = dma_request_chan(&pdev->dev, name); if (IS_ERR(mgbdev->c2h_chan[i].chan)) { dev_err(&pdev->dev, "failed to initialize %s", name); ret = PTR_ERR(mgbdev->c2h_chan[i].chan); mgbdev->c2h_chan[i].chan = NULL; return ret; } init_completion(&mgbdev->c2h_chan[i].req_compl); } for (i = 0; i < MGB4_VOUT_DEVICES; i++) { sprintf(name, "h2c%d", i); mgbdev->h2c_chan[i].chan = dma_request_chan(&pdev->dev, name); if (IS_ERR(mgbdev->h2c_chan[i].chan)) { dev_err(&pdev->dev, "failed to initialize %s", name); ret = PTR_ERR(mgbdev->h2c_chan[i].chan); mgbdev->h2c_chan[i].chan = NULL; return ret; } init_completion(&mgbdev->h2c_chan[i].req_compl); } return 0; } void mgb4_dma_channel_free(struct mgb4_dev *mgbdev) { int i; for (i = 0; i < MGB4_VIN_DEVICES; i++) { if (mgbdev->c2h_chan[i].chan) dma_release_channel(mgbdev->c2h_chan[i].chan); } for (i = 0; i < MGB4_VOUT_DEVICES; i++) { if (mgbdev->h2c_chan[i].chan) dma_release_channel(mgbdev->h2c_chan[i].chan); } }