Merge tag 'dmaengine-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine

Pull dmaengine updates from Vinod Koul:
 "Core:
   - Add Frank Li as susbstem reviewer to help with reviews

  New Support:
   - Mediatek support for Dimensity 6300 and 9200 controller
   - Qualcomm Kaanapali and Glymur GPI DMA engine
   - Synopsis DW AXI Agilex5
   - Renesas RZ/V2N SoC
   - Atmel microchip lan9691-dma
   - Tegra ADMA tegra264

  Updates:
   - sg_nents_for_dma() helper use in subsystem
   - pm_runtime_mark_last_busy() redundant call update for subsystem
   - Residue support for xilinx AXIDMA driver
   - Intel Max SGL Size Support and capabilities for DSA3.0
   - AXI dma larger than 32bits address support"

* tag 'dmaengine-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (64 commits)
  dmaengine: add Frank Li as reviewer
  dt-bindings: dma: qcom,gpi: Update max interrupts lines to 16
  dmaengine: fsl-edma: don't explicitly disable clocks in .remove()
  dmaengine: xilinx: xdma: use sg_nents_for_dma() helper
  dmaengine: sh: use sg_nents_for_dma() helper
  dmaengine: sa11x0: use sg_nents_for_dma() helper
  dmaengine: qcom: bam_dma: use sg_nents_for_dma() helper
  dmaengine: qcom: adm: use sg_nents_for_dma() helper
  dmaengine: pxa-dma: use sg_nents_for_dma() helper
  dmaengine: lgm: use sg_nents_for_dma() helper
  dmaengine: k3dma: use sg_nents_for_dma() helper
  dmaengine: dw-axi-dmac: use sg_nents_for_dma() helper
  dmaengine: bcm2835-dma: use sg_nents_for_dma() helper
  dmaengine: axi-dmac: use sg_nents_for_dma() helper
  dmaengine: altera-msgdma: use sg_nents_for_dma() helper
  scatterlist: introduce sg_nents_for_dma() helper
  dmaengine: idxd: Add Max SGL Size Support for DSA3.0
  dmaengine: idxd: Expose DSA3.0 capabilities through sysfs
  dmaengine: sh: rz-dmac: Make channel irq local
  dmaengine: pl08x: Fix comment stating the difference between PL080 and PL081
  ...
This commit is contained in:
Linus Torvalds
2026-02-17 11:47:17 -08:00
49 changed files with 704 additions and 455 deletions

View File

@@ -136,6 +136,21 @@ Description: The last executed device administrative command's status/error.
Also last configuration error overloaded.
Writing to it will clear the status.
What: /sys/bus/dsa/devices/dsa<m>/dsacaps
Date: April 5, 2026
KernelVersion: 6.20.0
Contact: dmaengine@vger.kernel.org
Description: The DSA3 specification introduces three new capability
registers: dsacap[0-2]. User components (e.g., configuration
libraries and workload applications) require this information
to properly utilize the DSA3 features.
This includes SGL capability support, Enabling hardware-specific
optimizations, Configuring memory, etc.
The output format is '<dsacap2>,<dsacap1>,<dsacap0>' where each
DSA cap value is a 64 bit hex value.
This attribute should only be visible on DSA devices of version
3 or later.
What: /sys/bus/dsa/devices/dsa<m>/iaa_cap
Date: Sept 14, 2022
KernelVersion: 6.0.0

View File

@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/dma/arm-pl08x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ARM PrimeCells PL080 and PL081 and derivatives DMA controller
title: ARM PrimeCell PL080 and PL081 and derivatives DMA controller
maintainers:
- Vinod Koul <vkoul@kernel.org>

View File

@@ -33,7 +33,9 @@ properties:
- microchip,sam9x7-dma
- const: atmel,sama5d4-dma
- items:
- const: microchip,sama7d65-dma
- enum:
- microchip,lan9691-dma
- microchip,sama7d65-dma
- const: microchip,sama7g5-dma
"#dma-cells":

View File

@@ -7,6 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek UART APDMA controller
maintainers:
- AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
- Long Cheng <long.cheng@mediatek.com>
description: |
@@ -23,11 +24,29 @@ properties:
- enum:
- mediatek,mt2712-uart-dma
- mediatek,mt6795-uart-dma
- mediatek,mt8173-uart-dma
- mediatek,mt8183-uart-dma
- mediatek,mt8365-uart-dma
- mediatek,mt8516-uart-dma
- const: mediatek,mt6577-uart-dma
- items:
- enum:
- mediatek,mt7988-uart-dma
- mediatek,mt8186-uart-dma
- mediatek,mt8188-uart-dma
- mediatek,mt8192-uart-dma
- mediatek,mt8195-uart-dma
- const: mediatek,mt6835-uart-dma
- items:
- enum:
- mediatek,mt6991-uart-dma
- mediatek,mt8196-uart-dma
- const: mediatek,mt6985-uart-dma
- enum:
- mediatek,mt6577-uart-dma
- mediatek,mt6795-uart-dma
- mediatek,mt6835-uart-dma
- mediatek,mt6985-uart-dma
reg:
minItems: 1
@@ -58,6 +77,7 @@ properties:
mediatek,dma-33bits:
type: boolean
deprecated: true
description: Enable 33-bits UART APDMA support
required:

View File

@@ -24,6 +24,8 @@ properties:
- qcom,sm6350-gpi-dma
- items:
- enum:
- qcom,glymur-gpi-dma
- qcom,kaanapali-gpi-dma
- qcom,milos-gpi-dma
- qcom,qcm2290-gpi-dma
- qcom,qcs8300-gpi-dma
@@ -58,7 +60,7 @@ properties:
description:
Interrupt lines for each GPI instance
minItems: 1
maxItems: 13
maxItems: 16
"#dma-cells":
const: 3

View File

@@ -24,6 +24,7 @@ properties:
- items:
- enum:
- renesas,r9a09g047-dmac # RZ/G3E
- renesas,r9a09g056-dmac # RZ/V2N
- const: renesas,r9a09g057-dmac
- const: renesas,r9a09g057-dmac # RZ/V2H(P)

View File

@@ -17,11 +17,15 @@ allOf:
properties:
compatible:
enum:
- snps,axi-dma-1.01a
- intel,kmb-axi-dma
- starfive,jh7110-axi-dma
- starfive,jh8100-axi-dma
oneOf:
- enum:
- snps,axi-dma-1.01a
- intel,kmb-axi-dma
- starfive,jh7110-axi-dma
- starfive,jh8100-axi-dma
- items:
- const: altr,agilex5-axi-dma
- const: snps,axi-dma-1.01a
reg:
minItems: 1

View File

@@ -411,7 +411,7 @@ supported.
- This structure can be initialized using the function
``dma_async_tx_descriptor_init``.
- You'll also need to set two fields in this structure:
- You'll also need to set following fields in this structure:
- flags:
TODO: Can it be modified by the driver itself, or
@@ -421,6 +421,9 @@ supported.
that is supposed to push the current transaction descriptor to a
pending queue, waiting for issue_pending to be called.
- phys: Physical address of the descriptor which is used later by
the dma engine to read the descriptor and initiate transfer.
- In this structure the function pointer callback_result can be
initialized in order for the submitter to be notified that a
transaction has completed. In the earlier code the function pointer

View File

@@ -7542,6 +7542,7 @@ K: \bdma_(?:buf|fence|resv)\b
DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
M: Vinod Koul <vkoul@kernel.org>
R: Frank Li <Frank.Li@kernel.org>
L: dmaengine@vger.kernel.org
S: Maintained
Q: https://patchwork.kernel.org/project/linux-dmaengine/list/

View File

@@ -590,7 +590,7 @@ config STE_DMA40
config ST_FDMA
tristate "ST FDMA dmaengine support"
depends on ARCH_STI
depends on ARCH_STI || COMPILE_TEST
depends on REMOTEPROC
select ST_SLIM_REMOTEPROC
select DMA_ENGINE

View File

@@ -396,13 +396,11 @@ msgdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
void *desc = NULL;
size_t len, avail;
dma_addr_t dma_dst, dma_src;
u32 desc_cnt = 0, i;
struct scatterlist *sg;
u32 desc_cnt;
u32 stride;
unsigned long irqflags;
for_each_sg(sgl, sg, sg_len, i)
desc_cnt += DIV_ROUND_UP(sg_dma_len(sg), MSGDMA_MAX_TRANS_LEN);
desc_cnt = sg_nents_for_dma(sgl, sg_len, MSGDMA_MAX_TRANS_LEN);
spin_lock_irqsave(&mdev->lock, irqflags);
if (desc_cnt > mdev->desc_free_cnt) {

View File

@@ -1010,7 +1010,7 @@ static inline u32 pl08x_lli_control_bits(struct pl08x_driver_data *pl08x,
/*
* Remove all src, dst and transfer size bits, then set the
* width and size according to the parameters. The bit offsets
* are different in the FTDMAC020 so we need to accound for this.
* are different in the FTDMAC020 so we need to account for this.
*/
if (pl08x->vd->ftdmac020) {
retbits &= ~FTDMAC020_LLI_DST_WIDTH_MSK;
@@ -2978,7 +2978,7 @@ out_no_pl08x:
return ret;
}
/* PL080 has 8 channels and the PL080 have just 2 */
/* PL080 has 8 channels and the PL081 have just 2 */
static struct vendor_data vendor_pl080 = {
.config_offset = PL080_CH_CONFIG,
.channels = 8,

View File

@@ -379,7 +379,6 @@ static void at_xdmac_runtime_suspend_descriptors(struct at_xdmac_chan *atchan)
if (!desc->active_xfer)
continue;
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
}
}
@@ -413,7 +412,6 @@ static bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan)
ret = !!(at_xdmac_chan_read(atchan, AT_XDMAC_GS) & atchan->mask);
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
return ret;
@@ -446,7 +444,6 @@ static void at_xdmac_off(struct at_xdmac *atxdmac, bool suspend_descriptors)
}
}
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
}
@@ -1676,7 +1673,6 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
spin_unlock:
spin_unlock_irqrestore(&atchan->lock, flags);
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
return ret;
}
@@ -1758,7 +1754,6 @@ static void at_xdmac_handle_error(struct at_xdmac_chan *atchan)
__func__, &bad_desc->lld.mbr_sa, &bad_desc->lld.mbr_da,
bad_desc->lld.mbr_ubc);
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
/* Then continue with usual descriptor management */
@@ -1822,7 +1817,6 @@ static void at_xdmac_tasklet(struct tasklet_struct *t)
* Decrement runtime PM ref counter incremented in
* at_xdmac_start_xfer().
*/
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
}
@@ -1954,7 +1948,6 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
spin_unlock_irqrestore(&atchan->lock, flags);
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
return 0;
@@ -1998,7 +1991,6 @@ static int at_xdmac_device_resume(struct dma_chan *chan)
unlock:
spin_unlock_irqrestore(&atchan->lock, flags);
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
return ret;
@@ -2041,7 +2033,6 @@ static int at_xdmac_device_terminate_all(struct dma_chan *chan)
clear_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status);
spin_unlock_irqrestore(&atchan->lock, flags);
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
return 0;
@@ -2235,7 +2226,6 @@ static int __maybe_unused atmel_xdmac_resume(struct device *dev)
}
}
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_autosuspend(atxdmac->dev);
return 0;
@@ -2257,12 +2247,29 @@ static int __maybe_unused atmel_xdmac_runtime_resume(struct device *dev)
return clk_enable(atxdmac->clk);
}
static inline int at_xdmac_get_channel_number(struct platform_device *pdev,
u32 reg, u32 *pchannels)
{
int ret;
if (reg) {
*pchannels = AT_XDMAC_NB_CH(reg);
return 0;
}
ret = of_property_read_u32(pdev->dev.of_node, "dma-channels", pchannels);
if (ret)
dev_err(&pdev->dev, "can't get number of channels\n");
return ret;
}
static int at_xdmac_probe(struct platform_device *pdev)
{
struct at_xdmac *atxdmac;
int irq, nr_channels, i, ret;
int irq, ret;
void __iomem *base;
u32 reg;
u32 nr_channels, i, reg;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -2278,7 +2285,10 @@ static int at_xdmac_probe(struct platform_device *pdev)
* of channels to do the allocation.
*/
reg = readl_relaxed(base + AT_XDMAC_GTYPE);
nr_channels = AT_XDMAC_NB_CH(reg);
ret = at_xdmac_get_channel_number(pdev, reg, &nr_channels);
if (ret)
return ret;
if (nr_channels > AT_XDMAC_MAX_CHAN) {
dev_err(&pdev->dev, "invalid number of channels (%u)\n",
nr_channels);
@@ -2412,7 +2422,6 @@ static int at_xdmac_probe(struct platform_device *pdev)
at_xdmac_axi_config(pdev);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;

View File

@@ -260,23 +260,6 @@ static void bcm2835_dma_create_cb_set_length(
control_block->info |= finalextrainfo;
}
static inline size_t bcm2835_dma_count_frames_for_sg(
struct bcm2835_chan *c,
struct scatterlist *sgl,
unsigned int sg_len)
{
size_t frames = 0;
struct scatterlist *sgent;
unsigned int i;
size_t plength = bcm2835_dma_max_frame_length(c);
for_each_sg(sgl, sgent, sg_len, i)
frames += bcm2835_dma_frames_for_length(
sg_dma_len(sgent), plength);
return frames;
}
/**
* bcm2835_dma_create_cb_chain - create a control block and fills data in
*
@@ -672,7 +655,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
}
/* count frames in sg list */
frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len);
frames = sg_nents_for_dma(sgl, sg_len, bcm2835_dma_max_frame_length(c));
/* allocate the CB chain */
d = bcm2835_dma_create_cb_chain(chan, direction, false,

View File

@@ -8,6 +8,7 @@
#include <linux/adi-axi-common.h>
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
@@ -69,7 +70,9 @@
#define AXI_DMAC_REG_START_TRANSFER 0x408
#define AXI_DMAC_REG_FLAGS 0x40c
#define AXI_DMAC_REG_DEST_ADDRESS 0x410
#define AXI_DMAC_REG_DEST_ADDRESS_HIGH 0x490
#define AXI_DMAC_REG_SRC_ADDRESS 0x414
#define AXI_DMAC_REG_SRC_ADDRESS_HIGH 0x494
#define AXI_DMAC_REG_X_LENGTH 0x418
#define AXI_DMAC_REG_Y_LENGTH 0x41c
#define AXI_DMAC_REG_DEST_STRIDE 0x420
@@ -233,11 +236,9 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
unsigned int flags = 0;
unsigned int val;
if (!chan->hw_sg) {
val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER);
if (val) /* Queue is full, wait for the next SOT IRQ */
return;
}
val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER);
if (val) /* Queue is full, wait for the next SOT IRQ */
return;
desc = chan->next_desc;
@@ -247,6 +248,7 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
return;
list_move_tail(&vdesc->node, &chan->active_descs);
desc = to_axi_dmac_desc(vdesc);
chan->next_desc = desc;
}
sg = &desc->sg[desc->num_submitted];
@@ -265,8 +267,6 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
else
chan->next_desc = NULL;
flags |= AXI_DMAC_FLAG_LAST;
} else {
chan->next_desc = desc;
}
sg->hw->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID);
@@ -274,11 +274,14 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan)
if (!chan->hw_sg) {
if (axi_dmac_dest_is_mem(chan)) {
axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, sg->hw->dest_addr);
axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS_HIGH,
sg->hw->dest_addr >> 32);
axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, sg->hw->dst_stride);
}
if (axi_dmac_src_is_mem(chan)) {
axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, sg->hw->src_addr);
axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS_HIGH, sg->hw->src_addr >> 32);
axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, sg->hw->src_stride);
}
}
@@ -674,10 +677,7 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_slave_sg(
if (direction != chan->direction)
return NULL;
num_sgs = 0;
for_each_sg(sgl, sg, sg_len, i)
num_sgs += DIV_ROUND_UP(sg_dma_len(sg), chan->max_length);
num_sgs = sg_nents_for_dma(sgl, sg_len, chan->max_length);
desc = axi_dmac_alloc_desc(chan, num_sgs);
if (!desc)
return NULL;
@@ -925,22 +925,18 @@ static int axi_dmac_parse_chan_dt(struct device_node *of_chan,
static int axi_dmac_parse_dt(struct device *dev, struct axi_dmac *dmac)
{
struct device_node *of_channels, *of_chan;
int ret;
of_channels = of_get_child_by_name(dev->of_node, "adi,channels");
struct device_node *of_channels __free(device_node) = of_get_child_by_name(dev->of_node,
"adi,channels");
if (of_channels == NULL)
return -ENODEV;
for_each_child_of_node(of_channels, of_chan) {
for_each_child_of_node_scoped(of_channels, of_chan) {
ret = axi_dmac_parse_chan_dt(of_chan, &dmac->chan);
if (ret) {
of_node_put(of_chan);
of_node_put(of_channels);
if (ret)
return -EINVAL;
}
}
of_node_put(of_channels);
return 0;
}
@@ -993,6 +989,9 @@ static int axi_dmac_read_chan_config(struct device *dev, struct axi_dmac *dmac)
static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
{
struct axi_dmac_chan *chan = &dmac->chan;
struct device *dev = dmac->dma_dev.dev;
u32 mask;
int ret;
axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, AXI_DMAC_FLAG_CYCLIC);
if (axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS) == AXI_DMAC_FLAG_CYCLIC)
@@ -1027,6 +1026,22 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
return -ENODEV;
}
if (axi_dmac_dest_is_mem(chan)) {
axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS_HIGH, 0xffffffff);
mask = axi_dmac_read(dmac, AXI_DMAC_REG_DEST_ADDRESS_HIGH);
} else {
axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS_HIGH, 0xffffffff);
mask = axi_dmac_read(dmac, AXI_DMAC_REG_SRC_ADDRESS_HIGH);
}
mask = 32 + fls(mask);
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(mask));
if (ret) {
dev_err(dev, "DMA mask set error %d\n", ret);
return ret;
}
if (version >= ADI_AXI_PCORE_VER(4, 2, 'a'))
chan->hw_partial_xfer = true;

View File

@@ -850,7 +850,7 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
unsigned int loop = 0;
struct scatterlist *sg;
size_t axi_block_len;
u32 len, num_sgs = 0;
u32 len, num_sgs;
unsigned int i;
dma_addr_t mem;
int status;
@@ -867,9 +867,7 @@ dw_axi_dma_chan_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
if (axi_block_len == 0)
return NULL;
for_each_sg(sgl, sg, sg_len, i)
num_sgs += DIV_ROUND_UP(sg_dma_len(sg), axi_block_len);
num_sgs = sg_nents_for_dma(sgl, sg_len, axi_block_len);
desc = axi_desc_alloc(num_sgs);
if (unlikely(!desc))
goto err_desc_get;

View File

@@ -161,13 +161,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
const struct pci_device_id *pid)
{
struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
struct dw_edma_pcie_data *vsec_data __free(kfree) = NULL;
struct device *dev = &pdev->dev;
struct dw_edma_chip *chip;
int err, nr_irqs;
int i, mask;
vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
struct dw_edma_pcie_data *vsec_data __free(kfree) =
kmalloc(sizeof(*vsec_data), GFP_KERNEL);
if (!vsec_data)
return -ENOMEM;

View File

@@ -915,7 +915,6 @@ static void fsl_edma_remove(struct platform_device *pdev)
of_dma_controller_free(np);
dma_async_device_unregister(&fsl_edma->dma_dev);
fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
fsl_disable_clocks(fsl_edma, fsl_edma->drvdata->dmamuxs);
}
static int fsl_edma_suspend_late(struct device *dev)

View File

@@ -390,6 +390,7 @@ static void idxd_wq_disable_cleanup(struct idxd_wq *wq)
memset(wq->name, 0, WQ_NAME_SIZE);
wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER;
idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH);
idxd_wq_set_init_max_sgl_size(idxd, wq);
if (wq->opcap_bmap)
bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS);
}
@@ -989,6 +990,8 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
/* bytes 12-15 */
wq->wqcfg->max_xfer_shift = ilog2(wq->max_xfer_bytes);
idxd_wqcfg_set_max_batch_shift(idxd->data->type, wq->wqcfg, ilog2(wq->max_batch_size));
if (idxd_sgl_supported(idxd))
wq->wqcfg->max_sgl_shift = ilog2(wq->max_sgl_size);
/* bytes 32-63 */
if (idxd->hw.wq_cap.op_config && wq->opcap_bmap) {
@@ -1167,6 +1170,8 @@ static int idxd_wq_load_config(struct idxd_wq *wq)
wq->max_xfer_bytes = 1ULL << wq->wqcfg->max_xfer_shift;
idxd_wq_set_max_batch_size(idxd->data->type, wq, 1U << wq->wqcfg->max_batch_shift);
if (idxd_sgl_supported(idxd))
wq->max_sgl_size = 1U << wq->wqcfg->max_sgl_shift;
for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
wqcfg_offset = WQCFG_OFFSET(idxd, wq->id, i);

View File

@@ -227,6 +227,7 @@ struct idxd_wq {
char name[WQ_NAME_SIZE + 1];
u64 max_xfer_bytes;
u32 max_batch_size;
u32 max_sgl_size;
/* Lock to protect upasid_xa access. */
struct mutex uc_lock;
@@ -252,6 +253,9 @@ struct idxd_hw {
struct opcap opcap;
u32 cmd_cap;
union iaa_cap_reg iaa_cap;
union dsacap0_reg dsacap0;
union dsacap1_reg dsacap1;
union dsacap2_reg dsacap2;
};
enum idxd_device_state {
@@ -345,6 +349,7 @@ struct idxd_device {
u64 max_xfer_bytes;
u32 max_batch_size;
u32 max_sgl_size;
int max_groups;
int max_engines;
int max_rdbufs;
@@ -689,6 +694,20 @@ static inline void idxd_wq_set_max_batch_size(int idxd_type, struct idxd_wq *wq,
wq->max_batch_size = max_batch_size;
}
static bool idxd_sgl_supported(struct idxd_device *idxd)
{
return idxd->data->type == IDXD_TYPE_DSA &&
idxd->hw.version >= DEVICE_VERSION_3 &&
idxd->hw.dsacap0.sgl_formats;
}
static inline void idxd_wq_set_init_max_sgl_size(struct idxd_device *idxd,
struct idxd_wq *wq)
{
if (idxd_sgl_supported(idxd))
wq->max_sgl_size = 1U << idxd->hw.dsacap0.max_sgl_shift;
}
static inline void idxd_wqcfg_set_max_batch_shift(int idxd_type, union wqcfg *wqcfg,
u32 max_batch_shift)
{

View File

@@ -222,6 +222,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
init_completion(&wq->wq_resurrect);
wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER;
idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH);
idxd_wq_set_init_max_sgl_size(idxd, wq);
wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES;
wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
if (!wq->wqcfg) {
@@ -585,6 +586,16 @@ static void idxd_read_caps(struct idxd_device *idxd)
}
multi_u64_to_bmap(idxd->opcap_bmap, &idxd->hw.opcap.bits[0], 4);
if (idxd->hw.version >= DEVICE_VERSION_3) {
idxd->hw.dsacap0.bits = ioread64(idxd->reg_base + IDXD_DSACAP0_OFFSET);
idxd->hw.dsacap1.bits = ioread64(idxd->reg_base + IDXD_DSACAP1_OFFSET);
idxd->hw.dsacap2.bits = ioread64(idxd->reg_base + IDXD_DSACAP2_OFFSET);
}
if (idxd_sgl_supported(idxd)) {
idxd->max_sgl_size = 1U << idxd->hw.dsacap0.max_sgl_shift;
dev_dbg(dev, "max sgl size: %u\n", idxd->max_sgl_size);
}
/* read iaa cap */
if (idxd->data->type == IDXD_TYPE_IAX && idxd->hw.version >= DEVICE_VERSION_2)
idxd->hw.iaa_cap.bits = ioread64(idxd->reg_base + IDXD_IAACAP_OFFSET);

View File

@@ -18,6 +18,7 @@
#define DEVICE_VERSION_1 0x100
#define DEVICE_VERSION_2 0x200
#define DEVICE_VERSION_3 0x300
#define IDXD_MMIO_BAR 0
#define IDXD_WQ_BAR 2
@@ -389,7 +390,8 @@ union wqcfg {
/* bytes 12-15 */
u32 max_xfer_shift:5;
u32 max_batch_shift:4;
u32 rsvd4:23;
u32 max_sgl_shift:4;
u32 rsvd4:19;
/* bytes 16-19 */
u16 occupancy_inth;
@@ -587,6 +589,30 @@ union evl_status_reg {
u64 bits;
};
#define IDXD_DSACAP0_OFFSET 0x180
union dsacap0_reg {
u64 bits;
struct {
u64 max_sgl_shift:4;
u64 max_gr_block_shift:4;
u64 ops_inter_domain:7;
u64 rsvd1:17;
u64 sgl_formats:16;
u64 max_sg_process:8;
u64 rsvd2:8;
};
};
#define IDXD_DSACAP1_OFFSET 0x188
union dsacap1_reg {
u64 bits;
};
#define IDXD_DSACAP2_OFFSET 0x190
union dsacap2_reg {
u64 bits;
};
#define IDXD_MAX_BATCH_IDENT 256
struct __evl_entry {

View File

@@ -1713,6 +1713,18 @@ static ssize_t event_log_size_store(struct device *dev,
}
static DEVICE_ATTR_RW(event_log_size);
static ssize_t dsacaps_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct idxd_device *idxd = confdev_to_idxd(dev);
return sysfs_emit(buf, "%016llx,%016llx,%016llx\n",
(u64)idxd->hw.dsacap2.bits,
(u64)idxd->hw.dsacap1.bits,
(u64)idxd->hw.dsacap0.bits);
}
static DEVICE_ATTR_RO(dsacaps);
static bool idxd_device_attr_max_batch_size_invisible(struct attribute *attr,
struct idxd_device *idxd)
{
@@ -1750,6 +1762,14 @@ static bool idxd_device_attr_event_log_size_invisible(struct attribute *attr,
!idxd->hw.gen_cap.evl_support);
}
static bool idxd_device_attr_dsacaps_invisible(struct attribute *attr,
struct idxd_device *idxd)
{
return attr == &dev_attr_dsacaps.attr &&
(idxd->data->type != IDXD_TYPE_DSA ||
idxd->hw.version < DEVICE_VERSION_3);
}
static umode_t idxd_device_attr_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
@@ -1768,6 +1788,9 @@ static umode_t idxd_device_attr_visible(struct kobject *kobj,
if (idxd_device_attr_event_log_size_invisible(attr, idxd))
return 0;
if (idxd_device_attr_dsacaps_invisible(attr, idxd))
return 0;
return attr->mode;
}
@@ -1795,6 +1818,7 @@ static struct attribute *idxd_device_attributes[] = {
&dev_attr_cmd_status.attr,
&dev_attr_iaa_cap.attr,
&dev_attr_event_log_size.attr,
&dev_attr_dsacaps.attr,
NULL,
};

View File

@@ -536,19 +536,14 @@ static struct dma_async_tx_descriptor *k3_dma_prep_slave_sg(
size_t len, avail, total = 0;
struct scatterlist *sg;
dma_addr_t addr, src = 0, dst = 0;
int num = sglen, i;
int num, i;
if (sgl == NULL)
return NULL;
c->cyclic = 0;
for_each_sg(sgl, sg, sglen, i) {
avail = sg_dma_len(sg);
if (avail > DMA_MAX_SIZE)
num += DIV_ROUND_UP(avail, DMA_MAX_SIZE) - 1;
}
num = sg_nents_for_dma(sgl, sglen, DMA_MAX_SIZE);
ds = k3_dma_alloc_desc_resource(num, chan);
if (!ds)
return NULL;

View File

@@ -1164,8 +1164,8 @@ ldma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct dw2_desc *hw_ds;
struct dw2_desc_sw *ds;
struct scatterlist *sg;
int num = sglen, i;
dma_addr_t addr;
int num, i;
if (!sgl)
return NULL;
@@ -1173,12 +1173,7 @@ ldma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (d->ver > DMA_VER22)
return ldma_chan_desc_cfg(chan, sgl->dma_address, sglen);
for_each_sg(sgl, sg, sglen, i) {
avail = sg_dma_len(sg);
if (avail > DMA_MAX_SIZE)
num += DIV_ROUND_UP(avail, DMA_MAX_SIZE) - 1;
}
num = sg_nents_for_dma(sgl, sglen, DMA_MAX_SIZE);
ds = dma_alloc_desc_resource(num, c);
if (!ds)
return NULL;

View File

@@ -41,7 +41,7 @@
#define VFF_STOP_CLR_B 0
#define VFF_EN_CLR_B 0
#define VFF_INT_EN_CLR_B 0
#define VFF_4G_SUPPORT_CLR_B 0
#define VFF_ADDR2_CLR_B 0
/*
* interrupt trigger level for tx
@@ -72,12 +72,12 @@
/* TX: the buffer size SW can write. RX: the buffer size HW can write. */
#define VFF_LEFT_SIZE 0x40
#define VFF_DEBUG_STATUS 0x50
#define VFF_4G_SUPPORT 0x54
#define VFF_ADDR2 0x54
struct mtk_uart_apdmadev {
struct dma_device ddev;
struct clk *clk;
bool support_33bits;
bool support_ext_addr;
unsigned int dma_requests;
};
@@ -148,8 +148,8 @@ static void mtk_uart_apdma_start_tx(struct mtk_chan *c)
mtk_uart_apdma_write(c, VFF_WPT, 0);
mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_TX_INT_CLR_B);
if (mtkd->support_33bits)
mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_EN_B);
if (mtkd->support_ext_addr)
mtk_uart_apdma_write(c, VFF_ADDR2, upper_32_bits(d->addr));
}
mtk_uart_apdma_write(c, VFF_EN, VFF_EN_B);
@@ -191,8 +191,8 @@ static void mtk_uart_apdma_start_rx(struct mtk_chan *c)
mtk_uart_apdma_write(c, VFF_RPT, 0);
mtk_uart_apdma_write(c, VFF_INT_FLAG, VFF_RX_INT_CLR_B);
if (mtkd->support_33bits)
mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_EN_B);
if (mtkd->support_ext_addr)
mtk_uart_apdma_write(c, VFF_ADDR2, upper_32_bits(d->addr));
}
mtk_uart_apdma_write(c, VFF_INT_EN, VFF_RX_INT_EN_B);
@@ -297,8 +297,8 @@ static int mtk_uart_apdma_alloc_chan_resources(struct dma_chan *chan)
goto err_pm;
}
if (mtkd->support_33bits)
mtk_uart_apdma_write(c, VFF_4G_SUPPORT, VFF_4G_SUPPORT_CLR_B);
if (mtkd->support_ext_addr)
mtk_uart_apdma_write(c, VFF_ADDR2, VFF_ADDR2_CLR_B);
err_pm:
pm_runtime_put_noidle(mtkd->ddev.dev);
@@ -468,7 +468,10 @@ static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd)
}
static const struct of_device_id mtk_uart_apdma_match[] = {
{ .compatible = "mediatek,mt6577-uart-dma", },
{ .compatible = "mediatek,mt6577-uart-dma", .data = (void *)32 },
{ .compatible = "mediatek,mt6795-uart-dma", .data = (void *)33 },
{ .compatible = "mediatek,mt6835-uart-dma", .data = (void *)34 },
{ .compatible = "mediatek,mt6985-uart-dma", .data = (void *)35 },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mtk_uart_apdma_match);
@@ -477,9 +480,9 @@ static int mtk_uart_apdma_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mtk_uart_apdmadev *mtkd;
int bit_mask = 32, rc;
struct mtk_chan *c;
unsigned int i;
unsigned int bit_mask, i;
int rc;
mtkd = devm_kzalloc(&pdev->dev, sizeof(*mtkd), GFP_KERNEL);
if (!mtkd)
@@ -492,11 +495,9 @@ static int mtk_uart_apdma_probe(struct platform_device *pdev)
return rc;
}
if (of_property_read_bool(np, "mediatek,dma-33bits"))
mtkd->support_33bits = true;
if (mtkd->support_33bits)
bit_mask = 33;
bit_mask = (unsigned int)(uintptr_t)of_device_get_match_data(&pdev->dev);
if (bit_mask > 32)
mtkd->support_ext_addr = true;
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(bit_mask));
if (rc)

View File

@@ -2133,10 +2133,8 @@ static void pl330_tasklet(struct tasklet_struct *t)
spin_unlock_irqrestore(&pch->lock, flags);
/* If work list empty, power down */
if (power_down) {
pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
if (power_down)
pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
}
}
static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec,
@@ -2313,7 +2311,6 @@ static int pl330_terminate_all(struct dma_chan *chan)
list_splice_tail_init(&pch->work_list, &pl330->desc_pool);
list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags);
pm_runtime_mark_last_busy(pl330->ddma.dev);
if (power_down)
pm_runtime_put_autosuspend(pl330->ddma.dev);
pm_runtime_put_autosuspend(pl330->ddma.dev);
@@ -2347,7 +2344,6 @@ static int pl330_pause(struct dma_chan *chan)
desc->status = PAUSED;
}
spin_unlock_irqrestore(&pch->lock, flags);
pm_runtime_mark_last_busy(pl330->ddma.dev);
pm_runtime_put_autosuspend(pl330->ddma.dev);
return 0;
@@ -2371,7 +2367,6 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
spin_unlock_irqrestore(&pl330->lock, flags);
pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
pl330_unprep_slave_fifo(pch);
}
@@ -3176,7 +3171,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_irq_safe(&adev->dev);
pm_runtime_use_autosuspend(&adev->dev);
pm_runtime_set_autosuspend_delay(&adev->dev, PL330_AUTOSUSPEND_DELAY);
pm_runtime_mark_last_busy(&adev->dev);
pm_runtime_put_autosuspend(&adev->dev);
return 0;

View File

@@ -970,7 +970,7 @@ pxad_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
struct scatterlist *sg;
dma_addr_t dma;
u32 dcmd, dsadr = 0, dtadr = 0;
unsigned int nb_desc = 0, i, j = 0;
unsigned int nb_desc, i, j = 0;
if ((sgl == NULL) || (sg_len == 0))
return NULL;
@@ -979,8 +979,7 @@ pxad_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
dev_dbg(&chan->vc.chan.dev->device,
"%s(): dir=%d flags=%lx\n", __func__, dir, flags);
for_each_sg(sgl, sg, sg_len, i)
nb_desc += DIV_ROUND_UP(sg_dma_len(sg), PDMA_MAX_DESC_BYTES);
nb_desc = sg_nents_for_dma(sgl, sg_len, PDMA_MAX_DESC_BYTES);
sw_desc = pxad_alloc_desc(chan, nb_desc + 1);
if (!sw_desc)
return NULL;

View File

@@ -23,24 +23,25 @@
* indication of where the hardware is currently working.
*/
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_dma.h>
#include <linux/circ_buf.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_dma.h>
#include <linux/of_irq.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include "../dmaengine.h"
#include "../virt-dma.h"
@@ -570,7 +571,6 @@ static void bam_free_chan(struct dma_chan *chan)
struct bam_chan *bchan = to_bam_chan(chan);
struct bam_device *bdev = bchan->bdev;
u32 val;
unsigned long flags;
int ret;
ret = pm_runtime_get_sync(bdev->dev);
@@ -584,9 +584,8 @@ static void bam_free_chan(struct dma_chan *chan)
goto err;
}
spin_lock_irqsave(&bchan->vc.lock, flags);
bam_reset_channel(bchan);
spin_unlock_irqrestore(&bchan->vc.lock, flags);
scoped_guard(spinlock_irqsave, &bchan->vc.lock)
bam_reset_channel(bchan);
dma_free_wc(bdev->dev, BAM_DESC_FIFO_SIZE, bchan->fifo_virt,
bchan->fifo_phys);
@@ -624,12 +623,11 @@ static int bam_slave_config(struct dma_chan *chan,
struct dma_slave_config *cfg)
{
struct bam_chan *bchan = to_bam_chan(chan);
unsigned long flag;
spin_lock_irqsave(&bchan->vc.lock, flag);
guard(spinlock_irqsave)(&bchan->vc.lock);
memcpy(&bchan->slave, cfg, sizeof(*cfg));
bchan->reconfigure = 1;
spin_unlock_irqrestore(&bchan->vc.lock, flag);
return 0;
}
@@ -655,22 +653,17 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
struct scatterlist *sg;
u32 i;
struct bam_desc_hw *desc;
unsigned int num_alloc = 0;
unsigned int num_alloc;
if (!is_slave_direction(direction)) {
dev_err(bdev->dev, "invalid dma direction\n");
return NULL;
}
/* calculate number of required entries */
for_each_sg(sgl, sg, sg_len, i)
num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_FIFO_SIZE);
/* allocate enough room to accommodate the number of entries */
num_alloc = sg_nents_for_dma(sgl, sg_len, BAM_FIFO_SIZE);
async_desc = kzalloc(struct_size(async_desc, desc, num_alloc),
GFP_NOWAIT);
if (!async_desc)
return NULL;
@@ -726,38 +719,37 @@ static int bam_dma_terminate_all(struct dma_chan *chan)
{
struct bam_chan *bchan = to_bam_chan(chan);
struct bam_async_desc *async_desc, *tmp;
unsigned long flag;
LIST_HEAD(head);
/* remove all transactions, including active transaction */
spin_lock_irqsave(&bchan->vc.lock, flag);
/*
* If we have transactions queued, then some might be committed to the
* hardware in the desc fifo. The only way to reset the desc fifo is
* to do a hardware reset (either by pipe or the entire block).
* bam_chan_init_hw() will trigger a pipe reset, and also reinit the
* pipe. If the pipe is left disabled (default state after pipe reset)
* and is accessed by a connected hardware engine, a fatal error in
* the BAM will occur. There is a small window where this could happen
* with bam_chan_init_hw(), but it is assumed that the caller has
* stopped activity on any attached hardware engine. Make sure to do
* this first so that the BAM hardware doesn't cause memory corruption
* by accessing freed resources.
*/
if (!list_empty(&bchan->desc_list)) {
async_desc = list_first_entry(&bchan->desc_list,
struct bam_async_desc, desc_node);
bam_chan_init_hw(bchan, async_desc->dir);
}
scoped_guard(spinlock_irqsave, &bchan->vc.lock) {
/*
* If we have transactions queued, then some might be committed to the
* hardware in the desc fifo. The only way to reset the desc fifo is
* to do a hardware reset (either by pipe or the entire block).
* bam_chan_init_hw() will trigger a pipe reset, and also reinit the
* pipe. If the pipe is left disabled (default state after pipe reset)
* and is accessed by a connected hardware engine, a fatal error in
* the BAM will occur. There is a small window where this could happen
* with bam_chan_init_hw(), but it is assumed that the caller has
* stopped activity on any attached hardware engine. Make sure to do
* this first so that the BAM hardware doesn't cause memory corruption
* by accessing freed resources.
*/
if (!list_empty(&bchan->desc_list)) {
async_desc = list_first_entry(&bchan->desc_list,
struct bam_async_desc, desc_node);
bam_chan_init_hw(bchan, async_desc->dir);
}
list_for_each_entry_safe(async_desc, tmp,
&bchan->desc_list, desc_node) {
list_add(&async_desc->vd.node, &bchan->vc.desc_issued);
list_del(&async_desc->desc_node);
}
list_for_each_entry_safe(async_desc, tmp,
&bchan->desc_list, desc_node) {
list_add(&async_desc->vd.node, &bchan->vc.desc_issued);
list_del(&async_desc->desc_node);
}
vchan_get_all_descriptors(&bchan->vc, &head);
spin_unlock_irqrestore(&bchan->vc.lock, flag);
vchan_get_all_descriptors(&bchan->vc, &head);
}
vchan_dma_desc_free_list(&bchan->vc, &head);
@@ -773,17 +765,16 @@ static int bam_pause(struct dma_chan *chan)
{
struct bam_chan *bchan = to_bam_chan(chan);
struct bam_device *bdev = bchan->bdev;
unsigned long flag;
int ret;
ret = pm_runtime_get_sync(bdev->dev);
if (ret < 0)
return ret;
spin_lock_irqsave(&bchan->vc.lock, flag);
writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_HALT));
bchan->paused = 1;
spin_unlock_irqrestore(&bchan->vc.lock, flag);
scoped_guard(spinlock_irqsave, &bchan->vc.lock) {
writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_HALT));
bchan->paused = 1;
}
pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
@@ -799,17 +790,16 @@ static int bam_resume(struct dma_chan *chan)
{
struct bam_chan *bchan = to_bam_chan(chan);
struct bam_device *bdev = bchan->bdev;
unsigned long flag;
int ret;
ret = pm_runtime_get_sync(bdev->dev);
if (ret < 0)
return ret;
spin_lock_irqsave(&bchan->vc.lock, flag);
writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_HALT));
bchan->paused = 0;
spin_unlock_irqrestore(&bchan->vc.lock, flag);
scoped_guard(spinlock_irqsave, &bchan->vc.lock) {
writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_HALT));
bchan->paused = 0;
}
pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
@@ -826,7 +816,6 @@ static int bam_resume(struct dma_chan *chan)
static u32 process_channel_irqs(struct bam_device *bdev)
{
u32 i, srcs, pipe_stts, offset, avail;
unsigned long flags;
struct bam_async_desc *async_desc, *tmp;
srcs = readl_relaxed(bam_addr(bdev, 0, BAM_IRQ_SRCS_EE));
@@ -846,7 +835,7 @@ static u32 process_channel_irqs(struct bam_device *bdev)
writel_relaxed(pipe_stts, bam_addr(bdev, i, BAM_P_IRQ_CLR));
spin_lock_irqsave(&bchan->vc.lock, flags);
guard(spinlock_irqsave)(&bchan->vc.lock);
offset = readl_relaxed(bam_addr(bdev, i, BAM_P_SW_OFSTS)) &
P_SW_OFSTS_MASK;
@@ -885,8 +874,6 @@ static u32 process_channel_irqs(struct bam_device *bdev)
}
list_del(&async_desc->desc_node);
}
spin_unlock_irqrestore(&bchan->vc.lock, flags);
}
return srcs;
@@ -950,7 +937,6 @@ static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
int ret;
size_t residue = 0;
unsigned int i;
unsigned long flags;
ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_COMPLETE)
@@ -959,23 +945,22 @@ static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
if (!txstate)
return bchan->paused ? DMA_PAUSED : ret;
spin_lock_irqsave(&bchan->vc.lock, flags);
vd = vchan_find_desc(&bchan->vc, cookie);
if (vd) {
residue = container_of(vd, struct bam_async_desc, vd)->length;
} else {
list_for_each_entry(async_desc, &bchan->desc_list, desc_node) {
if (async_desc->vd.tx.cookie != cookie)
continue;
scoped_guard(spinlock_irqsave, &bchan->vc.lock) {
vd = vchan_find_desc(&bchan->vc, cookie);
if (vd) {
residue = container_of(vd, struct bam_async_desc, vd)->length;
} else {
list_for_each_entry(async_desc, &bchan->desc_list, desc_node) {
if (async_desc->vd.tx.cookie != cookie)
continue;
for (i = 0; i < async_desc->num_desc; i++)
residue += le16_to_cpu(
async_desc->curr_desc[i].size);
for (i = 0; i < async_desc->num_desc; i++)
residue += le16_to_cpu(
async_desc->curr_desc[i].size);
}
}
}
spin_unlock_irqrestore(&bchan->vc.lock, flags);
dma_set_residue(txstate, residue);
if (ret == DMA_IN_PROGRESS && bchan->paused)
@@ -1116,17 +1101,16 @@ static void dma_tasklet(struct tasklet_struct *t)
{
struct bam_device *bdev = from_tasklet(bdev, t, task);
struct bam_chan *bchan;
unsigned long flags;
unsigned int i;
/* go through the channels and kick off transactions */
for (i = 0; i < bdev->num_channels; i++) {
bchan = &bdev->channels[i];
spin_lock_irqsave(&bchan->vc.lock, flags);
guard(spinlock_irqsave)(&bchan->vc.lock);
if (!list_empty(&bchan->vc.desc_issued) && !IS_BUSY(bchan))
bam_start_dma(bchan);
spin_unlock_irqrestore(&bchan->vc.lock, flags);
}
}
@@ -1140,15 +1124,12 @@ static void dma_tasklet(struct tasklet_struct *t)
static void bam_issue_pending(struct dma_chan *chan)
{
struct bam_chan *bchan = to_bam_chan(chan);
unsigned long flags;
spin_lock_irqsave(&bchan->vc.lock, flags);
guard(spinlock_irqsave)(&bchan->vc.lock);
/* if work pending and idle, start a transaction */
if (vchan_issue_pending(&bchan->vc) && !IS_BUSY(bchan))
bam_start_dma(bchan);
spin_unlock_irqrestore(&bchan->vc.lock, flags);
}
/**

View File

@@ -390,16 +390,15 @@ static struct dma_async_tx_descriptor *adm_prep_slave_sg(struct dma_chan *chan,
}
/* iterate through sgs and compute allocation size of structures */
for_each_sg(sgl, sg, sg_len, i) {
if (achan->slave.device_fc) {
if (achan->slave.device_fc) {
for_each_sg(sgl, sg, sg_len, i) {
box_count += DIV_ROUND_UP(sg_dma_len(sg) / burst,
ADM_MAX_ROWS);
if (sg_dma_len(sg) % burst)
single_count++;
} else {
single_count += DIV_ROUND_UP(sg_dma_len(sg),
ADM_MAX_XFER);
}
} else {
single_count = sg_nents_for_dma(sgl, sg_len, ADM_MAX_XFER);
}
async_desc = kzalloc(sizeof(*async_desc), GFP_NOWAIT);

View File

@@ -526,7 +526,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_desc *txd;
struct scatterlist *sgent;
unsigned i, j = sglen;
unsigned int i, j;
size_t size = 0;
/* SA11x0 channels can only operate in their native direction */
@@ -542,10 +542,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
for_each_sg(sg, sgent, sglen, i) {
dma_addr_t addr = sg_dma_address(sgent);
unsigned int len = sg_dma_len(sgent);
if (len > DMA_MAX_SIZE)
j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1;
if (addr & DMA_ALIGN) {
dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %pad\n",
&c->vc, &addr);
@@ -553,6 +550,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
}
}
j = sg_nents_for_dma(sg, sglen, DMA_MAX_SIZE & ~DMA_ALIGN);
txd = kzalloc(struct_size(txd, sg, j), GFP_ATOMIC);
if (!txd) {
dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);

View File

@@ -65,7 +65,6 @@ struct rz_dmac_chan {
void __iomem *ch_base;
void __iomem *ch_cmn_base;
unsigned int index;
int irq;
struct rz_dmac_desc *desc;
int descs_allocated;
@@ -800,29 +799,27 @@ static int rz_dmac_chan_probe(struct rz_dmac *dmac,
struct rz_lmdesc *lmdesc;
char pdev_irqname[6];
char *irqname;
int ret;
int irq, ret;
channel->index = index;
channel->mid_rid = -EINVAL;
/* Request the channel interrupt. */
scnprintf(pdev_irqname, sizeof(pdev_irqname), "ch%u", index);
channel->irq = platform_get_irq_byname(pdev, pdev_irqname);
if (channel->irq < 0)
return channel->irq;
irq = platform_get_irq_byname(pdev, pdev_irqname);
if (irq < 0)
return irq;
irqname = devm_kasprintf(dmac->dev, GFP_KERNEL, "%s:%u",
dev_name(dmac->dev), index);
if (!irqname)
return -ENOMEM;
ret = devm_request_threaded_irq(dmac->dev, channel->irq,
rz_dmac_irq_handler,
ret = devm_request_threaded_irq(dmac->dev, irq, rz_dmac_irq_handler,
rz_dmac_irq_handler_thread, 0,
irqname, channel);
if (ret) {
dev_err(dmac->dev, "failed to request IRQ %u (%d)\n",
channel->irq, ret);
dev_err(dmac->dev, "failed to request IRQ %u (%d)\n", irq, ret);
return ret;
}

View File

@@ -143,7 +143,7 @@ static dma_cookie_t shdma_tx_submit(struct dma_async_tx_descriptor *tx)
}
schan->pm_state = SHDMA_PM_ESTABLISHED;
ret = pm_runtime_put(schan->dev);
pm_runtime_put(schan->dev);
spin_unlock_irq(&schan->chan_lock);
return ret;
@@ -577,12 +577,11 @@ static struct dma_async_tx_descriptor *shdma_prep_sg(struct shdma_chan *schan,
struct scatterlist *sg;
struct shdma_desc *first = NULL, *new = NULL /* compiler... */;
LIST_HEAD(tx_list);
int chunks = 0;
int chunks;
unsigned long irq_flags;
int i;
for_each_sg(sgl, sg, sg_len, i)
chunks += DIV_ROUND_UP(sg_dma_len(sg), schan->max_xfer_len);
chunks = sg_nents_for_dma(sgl, sg_len, schan->max_xfer_len);
/* Have to lock the whole loop to protect against concurrent release */
spin_lock_irqsave(&schan->chan_lock, irq_flags);

View File

@@ -68,7 +68,7 @@ static void st_fdma_dreq_put(struct st_fdma_chan *fchan)
{
struct st_fdma_dev *fdev = fchan->fdev;
dev_dbg(fdev->dev, "put dreq_line:%#x\n", fchan->dreq_line);
dev_dbg(fdev->dev, "put dreq_line:%#lx\n", fchan->dreq_line);
clear_bit(fchan->dreq_line, &fdev->dreq_mask);
}

View File

@@ -120,7 +120,7 @@ struct st_fdma_chan {
struct dma_slave_config scfg;
struct st_fdma_cfg cfg;
int dreq_line;
long dreq_line;
struct virt_dma_chan vchan;
struct st_fdma_desc *fdesc;

View File

@@ -1452,7 +1452,6 @@ static int d40_pause(struct dma_chan *chan)
res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ);
pm_runtime_mark_last_busy(d40c->base->dev);
pm_runtime_put_autosuspend(d40c->base->dev);
spin_unlock_irqrestore(&d40c->lock, flags);
return res;
@@ -1479,7 +1478,6 @@ static int d40_resume(struct dma_chan *chan)
if (d40_residue(d40c) || d40_tx_is_linked(d40c))
res = d40_channel_execute_command(d40c, D40_DMA_RUN);
pm_runtime_mark_last_busy(d40c->base->dev);
pm_runtime_put_autosuspend(d40c->base->dev);
spin_unlock_irqrestore(&d40c->lock, flags);
return res;
@@ -1581,7 +1579,6 @@ static void dma_tc_handle(struct d40_chan *d40c)
if (d40_queue_start(d40c) == NULL) {
d40c->busy = false;
pm_runtime_mark_last_busy(d40c->base->dev);
pm_runtime_put_autosuspend(d40c->base->dev);
}
@@ -2054,16 +2051,13 @@ static int d40_free_dma(struct d40_chan *d40c)
else
d40c->base->lookup_phy_chans[phy->num] = NULL;
if (d40c->busy) {
pm_runtime_mark_last_busy(d40c->base->dev);
if (d40c->busy)
pm_runtime_put_autosuspend(d40c->base->dev);
}
d40c->busy = false;
d40c->phy_chan = NULL;
d40c->configured = false;
mark_last_busy:
pm_runtime_mark_last_busy(d40c->base->dev);
pm_runtime_put_autosuspend(d40c->base->dev);
return res;
}
@@ -2466,7 +2460,6 @@ static int d40_alloc_chan_resources(struct dma_chan *chan)
if (is_free_phy)
d40_config_write(d40c);
mark_last_busy:
pm_runtime_mark_last_busy(d40c->base->dev);
pm_runtime_put_autosuspend(d40c->base->dev);
spin_unlock_irqrestore(&d40c->lock, flags);
return err;
@@ -2618,12 +2611,9 @@ static int d40_terminate_all(struct dma_chan *chan)
chan_err(d40c, "Failed to stop channel\n");
d40_term_all(d40c);
pm_runtime_mark_last_busy(d40c->base->dev);
pm_runtime_put_autosuspend(d40c->base->dev);
if (d40c->busy) {
pm_runtime_mark_last_busy(d40c->base->dev);
if (d40c->busy)
pm_runtime_put_autosuspend(d40c->base->dev);
}
d40c->busy = false;
spin_unlock_irqrestore(&d40c->lock, flags);

View File

@@ -288,6 +288,7 @@ struct stm32_dma3_chan {
u32 fifo_size;
u32 max_burst;
bool semaphore_mode;
bool semaphore_taken;
struct stm32_dma3_dt_conf dt_config;
struct dma_slave_config dma_config;
u8 config_set;
@@ -332,6 +333,11 @@ static struct device *chan2dev(struct stm32_dma3_chan *chan)
return &chan->vchan.chan.dev->device;
}
static struct device *ddata2dev(struct stm32_dma3_ddata *ddata)
{
return ddata->dma_dev.dev;
}
static void stm32_dma3_chan_dump_reg(struct stm32_dma3_chan *chan)
{
struct stm32_dma3_ddata *ddata = to_stm32_dma3_ddata(chan);
@@ -1063,14 +1069,53 @@ static irqreturn_t stm32_dma3_chan_irq(int irq, void *devid)
return IRQ_HANDLED;
}
static int stm32_dma3_get_chan_sem(struct stm32_dma3_chan *chan)
{
struct stm32_dma3_ddata *ddata = to_stm32_dma3_ddata(chan);
u32 csemcr, ccid;
csemcr = readl_relaxed(ddata->base + STM32_DMA3_CSEMCR(chan->id));
/* Make an attempt to take the channel semaphore if not already taken */
if (!(csemcr & CSEMCR_SEM_MUTEX)) {
writel_relaxed(CSEMCR_SEM_MUTEX, ddata->base + STM32_DMA3_CSEMCR(chan->id));
csemcr = readl_relaxed(ddata->base + STM32_DMA3_CSEMCR(chan->id));
}
/* Check if channel is under CID1 control */
ccid = FIELD_GET(CSEMCR_SEM_CCID, csemcr);
if (!(csemcr & CSEMCR_SEM_MUTEX) || ccid != CCIDCFGR_CID1)
goto bad_cid;
chan->semaphore_taken = true;
dev_dbg(chan2dev(chan), "under CID1 control (semcr=0x%08x)\n", csemcr);
return 0;
bad_cid:
chan->semaphore_taken = false;
dev_err(chan2dev(chan), "not under CID1 control (in-use by CID%d)\n", ccid);
return -EACCES;
}
static void stm32_dma3_put_chan_sem(struct stm32_dma3_chan *chan)
{
struct stm32_dma3_ddata *ddata = to_stm32_dma3_ddata(chan);
if (chan->semaphore_taken) {
writel_relaxed(0, ddata->base + STM32_DMA3_CSEMCR(chan->id));
chan->semaphore_taken = false;
dev_dbg(chan2dev(chan), "no more under CID1 control\n");
}
}
static int stm32_dma3_alloc_chan_resources(struct dma_chan *c)
{
struct stm32_dma3_chan *chan = to_stm32_dma3_chan(c);
struct stm32_dma3_ddata *ddata = to_stm32_dma3_ddata(chan);
u32 id = chan->id, csemcr, ccid;
int ret;
ret = pm_runtime_resume_and_get(ddata->dma_dev.dev);
ret = pm_runtime_resume_and_get(ddata2dev(ddata));
if (ret < 0)
return ret;
@@ -1092,16 +1137,9 @@ static int stm32_dma3_alloc_chan_resources(struct dma_chan *c)
/* Take the channel semaphore */
if (chan->semaphore_mode) {
writel_relaxed(CSEMCR_SEM_MUTEX, ddata->base + STM32_DMA3_CSEMCR(id));
csemcr = readl_relaxed(ddata->base + STM32_DMA3_CSEMCR(id));
ccid = FIELD_GET(CSEMCR_SEM_CCID, csemcr);
/* Check that the channel is well taken */
if (ccid != CCIDCFGR_CID1) {
dev_err(chan2dev(chan), "Not under CID1 control (in-use by CID%d)\n", ccid);
ret = -EPERM;
ret = stm32_dma3_get_chan_sem(chan);
if (ret)
goto err_pool_destroy;
}
dev_dbg(chan2dev(chan), "Under CID1 control (semcr=0x%08x)\n", csemcr);
}
return 0;
@@ -1111,7 +1149,7 @@ err_pool_destroy:
chan->lli_pool = NULL;
err_put_sync:
pm_runtime_put_sync(ddata->dma_dev.dev);
pm_runtime_put_sync(ddata2dev(ddata));
return ret;
}
@@ -1135,9 +1173,9 @@ static void stm32_dma3_free_chan_resources(struct dma_chan *c)
/* Release the channel semaphore */
if (chan->semaphore_mode)
writel_relaxed(0, ddata->base + STM32_DMA3_CSEMCR(chan->id));
stm32_dma3_put_chan_sem(chan);
pm_runtime_put_sync(ddata->dma_dev.dev);
pm_runtime_put_sync(ddata2dev(ddata));
/* Reset configuration */
memset(&chan->dt_config, 0, sizeof(chan->dt_config));
@@ -1204,6 +1242,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_dma_memcpy(struct dma_cha
bool prevent_refactor = !!FIELD_GET(STM32_DMA3_DT_NOPACK, chan->dt_config.tr_conf) ||
!!FIELD_GET(STM32_DMA3_DT_NOREFACT, chan->dt_config.tr_conf);
/* Semaphore could be lost during suspend/resume */
if (chan->semaphore_mode && !chan->semaphore_taken)
return NULL;
count = stm32_dma3_get_ll_count(chan, len, prevent_refactor);
swdesc = stm32_dma3_chan_desc_alloc(chan, count);
@@ -1264,6 +1306,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_slave_sg(struct dma_chan
!!FIELD_GET(STM32_DMA3_DT_NOREFACT, chan->dt_config.tr_conf);
int ret;
/* Semaphore could be lost during suspend/resume */
if (chan->semaphore_mode && !chan->semaphore_taken)
return NULL;
count = 0;
for_each_sg(sgl, sg, sg_len, i)
count += stm32_dma3_get_ll_count(chan, sg_dma_len(sg), prevent_refactor);
@@ -1350,6 +1396,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_dma_cyclic(struct dma_cha
u32 count, i, ctr1, ctr2;
int ret;
/* Semaphore could be lost during suspend/resume */
if (chan->semaphore_mode && !chan->semaphore_taken)
return NULL;
if (!buf_len || !period_len || period_len > STM32_DMA3_MAX_BLOCK_SIZE) {
dev_err(chan2dev(chan), "Invalid buffer/period length\n");
return NULL;
@@ -1565,11 +1615,11 @@ static bool stm32_dma3_filter_fn(struct dma_chan *c, void *fn_param)
if (!(mask & BIT(chan->id)))
return false;
ret = pm_runtime_resume_and_get(ddata->dma_dev.dev);
ret = pm_runtime_resume_and_get(ddata2dev(ddata));
if (ret < 0)
return false;
semcr = readl_relaxed(ddata->base + STM32_DMA3_CSEMCR(chan->id));
pm_runtime_put_sync(ddata->dma_dev.dev);
pm_runtime_put_sync(ddata2dev(ddata));
/* Check if chan is free */
if (semcr & CSEMCR_SEM_MUTEX)
@@ -1591,7 +1641,7 @@ static struct dma_chan *stm32_dma3_of_xlate(struct of_phandle_args *dma_spec, st
struct dma_chan *c;
if (dma_spec->args_count < 3) {
dev_err(ddata->dma_dev.dev, "Invalid args count\n");
dev_err(ddata2dev(ddata), "Invalid args count\n");
return NULL;
}
@@ -1600,14 +1650,14 @@ static struct dma_chan *stm32_dma3_of_xlate(struct of_phandle_args *dma_spec, st
conf.tr_conf = dma_spec->args[2];
if (conf.req_line >= ddata->dma_requests) {
dev_err(ddata->dma_dev.dev, "Invalid request line\n");
dev_err(ddata2dev(ddata), "Invalid request line\n");
return NULL;
}
/* Request dma channel among the generic dma controller list */
c = dma_request_channel(mask, stm32_dma3_filter_fn, &conf);
if (!c) {
dev_err(ddata->dma_dev.dev, "No suitable channel found\n");
dev_err(ddata2dev(ddata), "No suitable channel found\n");
return NULL;
}
@@ -1620,6 +1670,7 @@ static struct dma_chan *stm32_dma3_of_xlate(struct of_phandle_args *dma_spec, st
static u32 stm32_dma3_check_rif(struct stm32_dma3_ddata *ddata)
{
struct device *dev = ddata2dev(ddata);
u32 chan_reserved, mask = 0, i, ccidcfgr, invalid_cid = 0;
/* Reserve Secure channels */
@@ -1631,7 +1682,7 @@ static u32 stm32_dma3_check_rif(struct stm32_dma3_ddata *ddata)
* In case CID filtering is not configured, dma-channel-mask property can be used to
* specify available DMA channels to the kernel.
*/
of_property_read_u32(ddata->dma_dev.dev->of_node, "dma-channel-mask", &mask);
of_property_read_u32(dev->of_node, "dma-channel-mask", &mask);
/* Reserve !CID-filtered not in dma-channel-mask, static CID != CID1, CID1 not allowed */
for (i = 0; i < ddata->dma_channels; i++) {
@@ -1651,7 +1702,7 @@ static u32 stm32_dma3_check_rif(struct stm32_dma3_ddata *ddata)
ddata->chans[i].semaphore_mode = true;
}
}
dev_dbg(ddata->dma_dev.dev, "chan%d: %s mode, %s\n", i,
dev_dbg(dev, "chan%d: %s mode, %s\n", i,
!(ccidcfgr & CCIDCFGR_CFEN) ? "!CID-filtered" :
ddata->chans[i].semaphore_mode ? "Semaphore" : "Static CID",
(chan_reserved & BIT(i)) ? "denied" :
@@ -1659,7 +1710,7 @@ static u32 stm32_dma3_check_rif(struct stm32_dma3_ddata *ddata)
}
if (invalid_cid)
dev_warn(ddata->dma_dev.dev, "chan%*pbl have invalid CID configuration\n",
dev_warn(dev, "chan%*pbl have invalid CID configuration\n",
ddata->dma_channels, &invalid_cid);
return chan_reserved;
@@ -1899,8 +1950,69 @@ static int stm32_dma3_runtime_resume(struct device *dev)
return ret;
}
static int stm32_dma3_pm_suspend(struct device *dev)
{
struct stm32_dma3_ddata *ddata = dev_get_drvdata(dev);
struct dma_device *dma_dev = &ddata->dma_dev;
struct dma_chan *c;
int ccr, ret;
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
list_for_each_entry(c, &dma_dev->channels, device_node) {
struct stm32_dma3_chan *chan = to_stm32_dma3_chan(c);
ccr = readl_relaxed(ddata->base + STM32_DMA3_CCR(chan->id));
if (ccr & CCR_EN) {
dev_warn(dev, "Suspend is prevented: %s still in use by %s\n",
dma_chan_name(c), dev_name(c->slave));
pm_runtime_put_sync(dev);
return -EBUSY;
}
}
pm_runtime_put_sync(dev);
pm_runtime_force_suspend(dev);
return 0;
}
static int stm32_dma3_pm_resume(struct device *dev)
{
struct stm32_dma3_ddata *ddata = dev_get_drvdata(dev);
struct dma_device *dma_dev = &ddata->dma_dev;
struct dma_chan *c;
int ret;
ret = pm_runtime_force_resume(dev);
if (ret < 0)
return ret;
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
/*
* Channel semaphores need to be restored in case of registers reset during low power.
* stm32_dma3_get_chan_sem() will prior check the semaphore status.
*/
list_for_each_entry(c, &dma_dev->channels, device_node) {
struct stm32_dma3_chan *chan = to_stm32_dma3_chan(c);
if (chan->semaphore_mode && chan->semaphore_taken)
stm32_dma3_get_chan_sem(chan);
}
pm_runtime_put_sync(dev);
return 0;
}
static const struct dev_pm_ops stm32_dma3_pm_ops = {
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
SYSTEM_SLEEP_PM_OPS(stm32_dma3_pm_suspend, stm32_dma3_pm_resume)
RUNTIME_PM_OPS(stm32_dma3_runtime_suspend, stm32_dma3_runtime_resume, NULL)
};
@@ -1914,12 +2026,7 @@ static struct platform_driver stm32_dma3_driver = {
},
};
static int __init stm32_dma3_init(void)
{
return platform_driver_register(&stm32_dma3_driver);
}
subsys_initcall(stm32_dma3_init);
module_platform_driver(stm32_dma3_driver);
MODULE_DESCRIPTION("STM32 DMA3 controller driver");
MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@foss.st.com>");

View File

@@ -731,7 +731,7 @@ static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan,
struct stm32_mdma_chan_config *chan_config = &chan->chan_config;
struct scatterlist *sg;
dma_addr_t src_addr, dst_addr;
u32 m2m_hw_period, ccr, ctcr, ctbr;
u32 m2m_hw_period = 0, ccr = 0, ctcr, ctbr;
int i, ret = 0;
if (chan_config->m2m_hw)

View File

@@ -583,6 +583,22 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
return ret;
}
static u32 find_burst_size(const u32 burst_lengths, u32 maxburst)
{
if (!maxburst)
return 1;
if (BIT(maxburst) & burst_lengths)
return maxburst;
/* Hardware only does power-of-two bursts. */
for (u32 burst = rounddown_pow_of_two(maxburst); burst > 0; burst /= 2)
if (BIT(burst) & burst_lengths)
return burst;
return 1;
}
static int set_config(struct sun6i_dma_dev *sdev,
struct dma_slave_config *sconfig,
enum dma_transfer_direction direction,
@@ -616,15 +632,13 @@ static int set_config(struct sun6i_dma_dev *sdev,
return -EINVAL;
if (!(BIT(dst_addr_width) & sdev->slave.dst_addr_widths))
return -EINVAL;
if (!(BIT(src_maxburst) & sdev->cfg->src_burst_lengths))
return -EINVAL;
if (!(BIT(dst_maxburst) & sdev->cfg->dst_burst_lengths))
return -EINVAL;
src_width = convert_buswidth(src_addr_width);
dst_width = convert_buswidth(dst_addr_width);
dst_burst = convert_burst(dst_maxburst);
src_burst = convert_burst(src_maxburst);
src_burst = find_burst_size(sdev->cfg->src_burst_lengths, src_maxburst);
dst_burst = find_burst_size(sdev->cfg->dst_burst_lengths, dst_maxburst);
dst_burst = convert_burst(dst_burst);
src_burst = convert_burst(src_burst);
*p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) |
DMA_CHAN_CFG_DST_WIDTH(dst_width);
@@ -826,6 +840,11 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
v_lli->cfg = lli_cfg;
sdev->cfg->set_drq(&v_lli->cfg, DRQ_SDRAM, vchan->port);
sdev->cfg->set_mode(&v_lli->cfg, LINEAR_MODE, IO_MODE);
dev_dbg(chan2dev(chan),
"%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n",
__func__, vchan->vc.chan.chan_id,
&sconfig->dst_addr, &buf_addr,
buf_len, flags);
} else {
sun6i_dma_set_addr(sdev, v_lli,
sconfig->src_addr,
@@ -833,6 +852,11 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
v_lli->cfg = lli_cfg;
sdev->cfg->set_drq(&v_lli->cfg, vchan->port, DRQ_SDRAM);
sdev->cfg->set_mode(&v_lli->cfg, IO_MODE, LINEAR_MODE);
dev_dbg(chan2dev(chan),
"%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n",
__func__, vchan->vc.chan.chan_id,
&buf_addr, &sconfig->src_addr,
buf_len, flags);
}
prev = sun6i_dma_lli_add(prev, v_lli, p_lli, txd);

View File

@@ -36,11 +36,12 @@ config DMA_OMAP
config TI_K3_UDMA
tristate "Texas Instruments UDMA support"
depends on ARCH_K3
depends on ARCH_K3 || COMPILE_TEST
depends on TI_SCI_PROTOCOL
depends on TI_SCI_INTA_IRQCHIP
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
select SOC_TI
select TI_K3_RINGACC
select TI_K3_PSIL
help
@@ -49,7 +50,7 @@ config TI_K3_UDMA
config TI_K3_UDMA_GLUE_LAYER
tristate "Texas Instruments UDMA Glue layer for non DMAengine users"
depends on ARCH_K3
depends on ARCH_K3 || COMPILE_TEST
depends on TI_K3_UDMA
help
Say y here to support the K3 NAVSS DMA glue interface

View File

@@ -390,7 +390,6 @@ static int cppi41_dma_alloc_chan_resources(struct dma_chan *chan)
if (!c->is_tx)
cppi_writel(c->q_num, c->gcr_reg + RXHPCRA0);
pm_runtime_mark_last_busy(cdd->ddev.dev);
pm_runtime_put_autosuspend(cdd->ddev.dev);
return 0;
@@ -411,7 +410,6 @@ static void cppi41_dma_free_chan_resources(struct dma_chan *chan)
WARN_ON(!list_empty(&cdd->pending));
pm_runtime_mark_last_busy(cdd->ddev.dev);
pm_runtime_put_autosuspend(cdd->ddev.dev);
}
@@ -509,7 +507,6 @@ static void cppi41_dma_issue_pending(struct dma_chan *chan)
cppi41_run_queue(cdd);
spin_unlock_irqrestore(&cdd->lock, flags);
pm_runtime_mark_last_busy(cdd->ddev.dev);
pm_runtime_put_autosuspend(cdd->ddev.dev);
}
@@ -627,7 +624,6 @@ static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg(
txd = &c->txd;
err_out_not_ready:
pm_runtime_mark_last_busy(cdd->ddev.dev);
pm_runtime_put_autosuspend(cdd->ddev.dev);
return txd;
@@ -1139,7 +1135,6 @@ static int cppi41_dma_probe(struct platform_device *pdev)
if (ret)
goto err_of;
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;

View File

@@ -605,13 +605,11 @@ xdma_prep_device_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct xdma_chan *xdma_chan = to_xdma_chan(chan);
struct dma_async_tx_descriptor *tx_desc;
struct xdma_desc *sw_desc;
u32 desc_num = 0, i;
u64 addr, dev_addr, *src, *dst;
u32 desc_num, i;
struct scatterlist *sg;
for_each_sg(sgl, sg, sg_len, i)
desc_num += DIV_ROUND_UP(sg_dma_len(sg), XDMA_DESC_BLEN_MAX);
desc_num = sg_nents_for_dma(sgl, sg_len, XDMA_DESC_BLEN_MAX);
sw_desc = xdma_alloc_desc(xdma_chan, desc_num, false);
if (!sw_desc)
return NULL;

View File

@@ -1022,6 +1022,24 @@ static u32 xilinx_dma_get_residue(struct xilinx_dma_chan *chan,
return residue;
}
static u32
xilinx_dma_get_residue_axidma_direct_s2mm(struct xilinx_dma_chan *chan,
struct xilinx_dma_tx_descriptor *desc)
{
struct xilinx_axidma_tx_segment *seg;
struct xilinx_axidma_desc_hw *hw;
u32 finished_len;
finished_len = dma_ctrl_read(chan, XILINX_DMA_REG_BTT);
seg = list_first_entry(&desc->segments, struct xilinx_axidma_tx_segment,
node);
hw = &seg->hw;
return hw->control - finished_len;
}
/**
* xilinx_dma_chan_handle_cyclic - Cyclic dma callback
* @chan: Driver specific dma channel
@@ -1733,6 +1751,9 @@ static void xilinx_dma_complete_descriptor(struct xilinx_dma_chan *chan)
if (chan->has_sg && chan->xdev->dma_config->dmatype !=
XDMA_TYPE_VDMA)
desc->residue = xilinx_dma_get_residue(chan, desc);
else if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA &&
chan->direction == DMA_DEV_TO_MEM && !chan->has_sg)
desc->residue = xilinx_dma_get_residue_axidma_direct_s2mm(chan, desc);
else
desc->residue = 0;
desc->err = chan->err;

View File

@@ -695,7 +695,6 @@ static void zynqmp_dma_free_chan_resources(struct dma_chan *dchan)
(2 * ZYNQMP_DMA_DESC_SIZE(chan) * ZYNQMP_DMA_NUM_DESCS),
chan->desc_pool_v, chan->desc_pool_p);
kfree(chan->sw_desc_pool);
pm_runtime_mark_last_busy(chan->dev);
pm_runtime_put_autosuspend(chan->dev);
}
@@ -1145,7 +1144,6 @@ static int zynqmp_dma_probe(struct platform_device *pdev)
goto free_chan_resources;
}
pm_runtime_mark_last_busy(zdev->dev);
pm_runtime_put_sync_autosuspend(zdev->dev);
return 0;

View File

@@ -27,7 +27,7 @@ struct dw_edma_region {
};
/**
* struct dw_edma_core_ops - platform-specific eDMA methods
* struct dw_edma_plat_ops - platform-specific eDMA methods
* @irq_vector: Get IRQ number of the passed eDMA channel. Note the
* method accepts the channel id in the end-to-end
* numbering with the eDMA write channels being placed
@@ -63,19 +63,17 @@ enum dw_edma_chip_flags {
/**
* struct dw_edma_chip - representation of DesignWare eDMA controller hardware
* @dev: struct device of the eDMA controller
* @id: instance ID
* @nr_irqs: total number of DMA IRQs
* @ops DMA channel to IRQ number mapping
* @flags dw_edma_chip_flags
* @reg_base DMA register base address
* @ll_wr_cnt DMA write link list count
* @ll_rd_cnt DMA read link list count
* @rg_region DMA register region
* @ll_region_wr DMA descriptor link list memory for write channel
* @ll_region_rd DMA descriptor link list memory for read channel
* @dt_region_wr DMA data memory for write channel
* @dt_region_rd DMA data memory for read channel
* @mf DMA register map format
* @ops: DMA channel to IRQ number mapping
* @flags: dw_edma_chip_flags
* @reg_base: DMA register base address
* @ll_wr_cnt: DMA write link list count
* @ll_rd_cnt: DMA read link list count
* @ll_region_wr: DMA descriptor link list memory for write channel
* @ll_region_rd: DMA descriptor link list memory for read channel
* @dt_region_wr: DMA data memory for write channel
* @dt_region_rd: DMA data memory for read channel
* @mf: DMA register map format
* @dw: struct dw_edma that is filled by dw_edma_probe()
*/
struct dw_edma_chip {

View File

@@ -441,6 +441,8 @@ static inline void sg_init_marker(struct scatterlist *sgl,
int sg_nents(struct scatterlist *sg);
int sg_nents_for_len(struct scatterlist *sg, u64 len);
int sg_nents_for_dma(struct scatterlist *sgl, unsigned int sglen, size_t len);
struct scatterlist *sg_last(struct scatterlist *s, unsigned int);
void sg_init_table(struct scatterlist *, unsigned int);
void sg_init_one(struct scatterlist *, const void *, unsigned int);

View File

@@ -19,11 +19,11 @@
#include <linux/types.h>
/**
* shdma_pm_state - DMA channel PM state
* SHDMA_PM_ESTABLISHED: either idle or during data transfer
* SHDMA_PM_BUSY: during the transfer preparation, when we have to
* enum shdma_pm_state - DMA channel PM state
* @SHDMA_PM_ESTABLISHED: either idle or during data transfer
* @SHDMA_PM_BUSY: during the transfer preparation, when we have to
* drop the lock temporarily
* SHDMA_PM_PENDING: transfers pending
* @SHDMA_PM_PENDING: transfers pending
*/
enum shdma_pm_state {
SHDMA_PM_ESTABLISHED,
@@ -74,18 +74,18 @@ struct shdma_chan {
/**
* struct shdma_ops - simple DMA driver operations
* desc_completed: return true, if this is the descriptor, that just has
* @desc_completed: return true, if this is the descriptor, that just has
* completed (atomic)
* halt_channel: stop DMA channel operation (atomic)
* channel_busy: return true, if the channel is busy (atomic)
* slave_addr: return slave DMA address
* desc_setup: set up the hardware specific descriptor portion (atomic)
* set_slave: bind channel to a slave
* setup_xfer: configure channel hardware for operation (atomic)
* start_xfer: start the DMA transfer (atomic)
* embedded_desc: return Nth struct shdma_desc pointer from the
* @halt_channel: stop DMA channel operation (atomic)
* @channel_busy: return true, if the channel is busy (atomic)
* @slave_addr: return slave DMA address
* @desc_setup: set up the hardware specific descriptor portion (atomic)
* @set_slave: bind channel to a slave
* @setup_xfer: configure channel hardware for operation (atomic)
* @start_xfer: start the DMA transfer (atomic)
* @embedded_desc: return Nth struct shdma_desc pointer from the
* descriptor array
* chan_irq: process channel IRQ, return true if a transfer has
* @chan_irq: process channel IRQ, return true if a transfer has
* completed (atomic)
*/
struct shdma_ops {

View File

@@ -3,11 +3,7 @@
#ifndef _USR_IDXD_H_
#define _USR_IDXD_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
/* Driver command error status */
enum idxd_scmd_stat {
@@ -176,132 +172,132 @@ enum iax_completion_status {
#define DSA_COMP_STATUS(status) ((status) & DSA_COMP_STATUS_MASK)
struct dsa_hw_desc {
uint32_t pasid:20;
uint32_t rsvd:11;
uint32_t priv:1;
uint32_t flags:24;
uint32_t opcode:8;
uint64_t completion_addr;
__u32 pasid:20;
__u32 rsvd:11;
__u32 priv:1;
__u32 flags:24;
__u32 opcode:8;
__u64 completion_addr;
union {
uint64_t src_addr;
uint64_t rdback_addr;
uint64_t pattern;
uint64_t desc_list_addr;
uint64_t pattern_lower;
uint64_t transl_fetch_addr;
__u64 src_addr;
__u64 rdback_addr;
__u64 pattern;
__u64 desc_list_addr;
__u64 pattern_lower;
__u64 transl_fetch_addr;
};
union {
uint64_t dst_addr;
uint64_t rdback_addr2;
uint64_t src2_addr;
uint64_t comp_pattern;
__u64 dst_addr;
__u64 rdback_addr2;
__u64 src2_addr;
__u64 comp_pattern;
};
union {
uint32_t xfer_size;
uint32_t desc_count;
uint32_t region_size;
__u32 xfer_size;
__u32 desc_count;
__u32 region_size;
};
uint16_t int_handle;
uint16_t rsvd1;
__u16 int_handle;
__u16 rsvd1;
union {
uint8_t expected_res;
__u8 expected_res;
/* create delta record */
struct {
uint64_t delta_addr;
uint32_t max_delta_size;
uint32_t delt_rsvd;
uint8_t expected_res_mask;
__u64 delta_addr;
__u32 max_delta_size;
__u32 delt_rsvd;
__u8 expected_res_mask;
};
uint32_t delta_rec_size;
uint64_t dest2;
__u32 delta_rec_size;
__u64 dest2;
/* CRC */
struct {
uint32_t crc_seed;
uint32_t crc_rsvd;
uint64_t seed_addr;
__u32 crc_seed;
__u32 crc_rsvd;
__u64 seed_addr;
};
/* DIF check or strip */
struct {
uint8_t src_dif_flags;
uint8_t dif_chk_res;
uint8_t dif_chk_flags;
uint8_t dif_chk_res2[5];
uint32_t chk_ref_tag_seed;
uint16_t chk_app_tag_mask;
uint16_t chk_app_tag_seed;
__u8 src_dif_flags;
__u8 dif_chk_res;
__u8 dif_chk_flags;
__u8 dif_chk_res2[5];
__u32 chk_ref_tag_seed;
__u16 chk_app_tag_mask;
__u16 chk_app_tag_seed;
};
/* DIF insert */
struct {
uint8_t dif_ins_res;
uint8_t dest_dif_flag;
uint8_t dif_ins_flags;
uint8_t dif_ins_res2[13];
uint32_t ins_ref_tag_seed;
uint16_t ins_app_tag_mask;
uint16_t ins_app_tag_seed;
__u8 dif_ins_res;
__u8 dest_dif_flag;
__u8 dif_ins_flags;
__u8 dif_ins_res2[13];
__u32 ins_ref_tag_seed;
__u16 ins_app_tag_mask;
__u16 ins_app_tag_seed;
};
/* DIF update */
struct {
uint8_t src_upd_flags;
uint8_t upd_dest_flags;
uint8_t dif_upd_flags;
uint8_t dif_upd_res[5];
uint32_t src_ref_tag_seed;
uint16_t src_app_tag_mask;
uint16_t src_app_tag_seed;
uint32_t dest_ref_tag_seed;
uint16_t dest_app_tag_mask;
uint16_t dest_app_tag_seed;
__u8 src_upd_flags;
__u8 upd_dest_flags;
__u8 dif_upd_flags;
__u8 dif_upd_res[5];
__u32 src_ref_tag_seed;
__u16 src_app_tag_mask;
__u16 src_app_tag_seed;
__u32 dest_ref_tag_seed;
__u16 dest_app_tag_mask;
__u16 dest_app_tag_seed;
};
/* Fill */
uint64_t pattern_upper;
__u64 pattern_upper;
/* Translation fetch */
struct {
uint64_t transl_fetch_res;
uint32_t region_stride;
__u64 transl_fetch_res;
__u32 region_stride;
};
/* DIX generate */
struct {
uint8_t dix_gen_res;
uint8_t dest_dif_flags;
uint8_t dif_flags;
uint8_t dix_gen_res2[13];
uint32_t ref_tag_seed;
uint16_t app_tag_mask;
uint16_t app_tag_seed;
__u8 dix_gen_res;
__u8 dest_dif_flags;
__u8 dif_flags;
__u8 dix_gen_res2[13];
__u32 ref_tag_seed;
__u16 app_tag_mask;
__u16 app_tag_seed;
};
uint8_t op_specific[24];
__u8 op_specific[24];
};
} __attribute__((packed));
struct iax_hw_desc {
uint32_t pasid:20;
uint32_t rsvd:11;
uint32_t priv:1;
uint32_t flags:24;
uint32_t opcode:8;
uint64_t completion_addr;
uint64_t src1_addr;
uint64_t dst_addr;
uint32_t src1_size;
uint16_t int_handle;
__u32 pasid:20;
__u32 rsvd:11;
__u32 priv:1;
__u32 flags:24;
__u32 opcode:8;
__u64 completion_addr;
__u64 src1_addr;
__u64 dst_addr;
__u32 src1_size;
__u16 int_handle;
union {
uint16_t compr_flags;
uint16_t decompr_flags;
__u16 compr_flags;
__u16 decompr_flags;
};
uint64_t src2_addr;
uint32_t max_dst_size;
uint32_t src2_size;
uint32_t filter_flags;
uint32_t num_inputs;
__u64 src2_addr;
__u32 max_dst_size;
__u32 src2_size;
__u32 filter_flags;
__u32 num_inputs;
} __attribute__((packed));
struct dsa_raw_desc {
uint64_t field[8];
__u64 field[8];
} __attribute__((packed));
/*
@@ -309,91 +305,91 @@ struct dsa_raw_desc {
* volatile and prevent the compiler from optimize the read.
*/
struct dsa_completion_record {
volatile uint8_t status;
volatile __u8 status;
union {
uint8_t result;
uint8_t dif_status;
__u8 result;
__u8 dif_status;
};
uint8_t fault_info;
uint8_t rsvd;
__u8 fault_info;
__u8 rsvd;
union {
uint32_t bytes_completed;
uint32_t descs_completed;
__u32 bytes_completed;
__u32 descs_completed;
};
uint64_t fault_addr;
__u64 fault_addr;
union {
/* common record */
struct {
uint32_t invalid_flags:24;
uint32_t rsvd2:8;
__u32 invalid_flags:24;
__u32 rsvd2:8;
};
uint32_t delta_rec_size;
uint64_t crc_val;
__u32 delta_rec_size;
__u64 crc_val;
/* DIF check & strip */
struct {
uint32_t dif_chk_ref_tag;
uint16_t dif_chk_app_tag_mask;
uint16_t dif_chk_app_tag;
__u32 dif_chk_ref_tag;
__u16 dif_chk_app_tag_mask;
__u16 dif_chk_app_tag;
};
/* DIF insert */
struct {
uint64_t dif_ins_res;
uint32_t dif_ins_ref_tag;
uint16_t dif_ins_app_tag_mask;
uint16_t dif_ins_app_tag;
__u64 dif_ins_res;
__u32 dif_ins_ref_tag;
__u16 dif_ins_app_tag_mask;
__u16 dif_ins_app_tag;
};
/* DIF update */
struct {
uint32_t dif_upd_src_ref_tag;
uint16_t dif_upd_src_app_tag_mask;
uint16_t dif_upd_src_app_tag;
uint32_t dif_upd_dest_ref_tag;
uint16_t dif_upd_dest_app_tag_mask;
uint16_t dif_upd_dest_app_tag;
__u32 dif_upd_src_ref_tag;
__u16 dif_upd_src_app_tag_mask;
__u16 dif_upd_src_app_tag;
__u32 dif_upd_dest_ref_tag;
__u16 dif_upd_dest_app_tag_mask;
__u16 dif_upd_dest_app_tag;
};
/* DIX generate */
struct {
uint64_t dix_gen_res;
uint32_t dix_ref_tag;
uint16_t dix_app_tag_mask;
uint16_t dix_app_tag;
__u64 dix_gen_res;
__u32 dix_ref_tag;
__u16 dix_app_tag_mask;
__u16 dix_app_tag;
};
uint8_t op_specific[16];
__u8 op_specific[16];
};
} __attribute__((packed));
struct dsa_raw_completion_record {
uint64_t field[4];
__u64 field[4];
} __attribute__((packed));
struct iax_completion_record {
volatile uint8_t status;
uint8_t error_code;
uint8_t fault_info;
uint8_t rsvd;
uint32_t bytes_completed;
uint64_t fault_addr;
uint32_t invalid_flags;
uint32_t rsvd2;
uint32_t output_size;
uint8_t output_bits;
uint8_t rsvd3;
uint16_t xor_csum;
uint32_t crc;
uint32_t min;
uint32_t max;
uint32_t sum;
uint64_t rsvd4[2];
volatile __u8 status;
__u8 error_code;
__u8 fault_info;
__u8 rsvd;
__u32 bytes_completed;
__u64 fault_addr;
__u32 invalid_flags;
__u32 rsvd2;
__u32 output_size;
__u8 output_bits;
__u8 rsvd3;
__u16 xor_csum;
__u32 crc;
__u32 min;
__u32 max;
__u32 sum;
__u64 rsvd4[2];
} __attribute__((packed));
struct iax_raw_completion_record {
uint64_t field[8];
__u64 field[8];
} __attribute__((packed));
#endif

View File

@@ -64,6 +64,32 @@ int sg_nents_for_len(struct scatterlist *sg, u64 len)
}
EXPORT_SYMBOL(sg_nents_for_len);
/**
* sg_nents_for_dma - return the count of DMA-capable entries in scatterlist
* @sgl: The scatterlist
* @sglen: The current number of entries
* @len: The maximum length of DMA-capable block
*
* Description:
* Determines the number of entries in @sgl which would be permitted in
* DMA-capable transfer if list had been split accordingly, taking into
* account chaining as well.
*
* Returns:
* the number of sgl entries needed
*
**/
int sg_nents_for_dma(struct scatterlist *sgl, unsigned int sglen, size_t len)
{
struct scatterlist *sg;
int i, nents = 0;
for_each_sg(sgl, sg, sglen, i)
nents += DIV_ROUND_UP(sg_dma_len(sg), len);
return nents;
}
EXPORT_SYMBOL(sg_nents_for_dma);
/**
* sg_last - return the last scatterlist entry in a list
* @sgl: First entry in the scatterlist