Files
Koichiro Den 0613e7934e dmaengine: dw-edma: Program endpoint function numbers
The eDMA/HDMA transfers the driver issues carry a requester function
number in their TLPs, but nothing ever programs it: eDMA v0 leaves the
FUNC_NUM field of the channel control word zero and HDMA leaves the
per-channel func_num register at its reset value, so every transfer is
attributed to function 0. That is invisible in single-function setups,
but once the DMA block serves a non-zero endpoint function, its
requests must carry that function's number for the host to attribute
and translate them correctly.

Record the function number in the chip data (PCI_FUNC() of the probing
device for dw-edma-pcie) and program it per channel.

Endpoint-local chip instances keep func_no at 0, so transfers issued by
the endpoint-side driver remain PF0-attributed. Delegated channels are
programmed by the host-side dw-edma-pcie instance when it takes over the
channel, using that instance's PCI_FUNC().

Reviewed-by: Frank Li <[email protected]>
Signed-off-by: Koichiro Den <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Vinod Koul <[email protected]>
2026-07-21 16:00:36 +05:30

169 lines
5.4 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
* Synopsys DesignWare eDMA core driver
*
* Author: Gustavo Pimentel <[email protected]>
*/
#ifndef _DW_EDMA_H
#define _DW_EDMA_H
#include <linux/device.h>
#include <linux/dmaengine.h>
#define EDMA_MAX_WR_CH 8
#define EDMA_MAX_RD_CH 8
#define HDMA_MAX_WR_CH 64
#define HDMA_MAX_RD_CH 64
struct dw_edma;
struct dw_edma_region {
u64 paddr;
union {
void *mem;
void __iomem *io;
} vaddr;
size_t sz;
};
/**
* 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
* first in the row.
* @pci_address: Get PCIe bus address corresponding to the passed CPU
* address. Note there is no need in specifying this
* function if the address translation is performed by
* the DW PCIe RP/EP controller with the DW eDMA device in
* subject and DMA_BYPASS isn't set for all the outbound
* iATU windows. That will be done by the controller
* automatically.
*/
struct dw_edma_plat_ops {
int (*irq_vector)(struct device *dev, unsigned int nr);
u64 (*pci_address)(struct device *dev, phys_addr_t cpu_addr);
};
enum dw_edma_map_format {
EDMA_MF_EDMA_LEGACY = 0x0,
EDMA_MF_EDMA_UNROLL = 0x1,
EDMA_MF_HDMA_COMPAT = 0x5,
EDMA_MF_HDMA_NATIVE = 0x7,
};
/**
* enum dw_edma_chip_flags - Flags specific to an eDMA chip
* @DW_EDMA_CHIP_LOCAL: eDMA is used locally by an endpoint
* @DW_EDMA_CHIP_PARTIAL: Only channels described by this instance are
* owned by this driver. Controller-wide state
* must be preserved, and layouts with shared
* direction-wide registers must only be shared at
* direction granularity. Layouts with per-channel
* registers may be shared at channel granularity.
*/
enum dw_edma_chip_flags {
DW_EDMA_CHIP_LOCAL = BIT(0),
DW_EDMA_CHIP_PARTIAL = BIT(1),
};
/**
* enum dw_edma_ch_irq_mode - per-channel interrupt routing control
* @DW_EDMA_CH_IRQ_LOCAL: local interrupt only (edma_int[])
* @DW_EDMA_CH_IRQ_REMOTE: remote interrupt only (IMWr/MSI), without
* delivering local edma_int[].
*
* DesignWare EP eDMA can signal interrupts locally through the edma_int[]
* bus, and remotely using posted memory writes (IMWr) that may be
* interpreted as MSI/MSI-X by the RC.
*
* For the v0 eDMA linked-list programming path, DMA_*_INT_MASK gates the local
* edma_int[] assertion, while there is no dedicated per-channel mask for IMWr
* generation. To request a remote-only interrupt, Synopsys recommends setting
* both LIE and RIE, and masking the local interrupt in DMA_*_INT_MASK. See the
* DesignWare endpoint databook 6.30a, Linked List Mode interrupt handling
* ("Software Programming of an Endpoint's LIE and RIE Bits for Linked List
* Transfers", Attention).
*
* A local (DW_EDMA_CHIP_LOCAL) instance never issues transfers on a
* remote-routed channel: REMOTE routing on such an instance denotes a channel
* handed over to and driven by the remote side, and the recipe above is
* applied by the driving instance.
*
* HDMA linked-list watermark interrupts have the same LWIE/RWIE guidance. HDMA
* non-linked-list mode has dedicated local and remote stop/abort interrupt
* enables.
*/
enum dw_edma_ch_irq_mode {
DW_EDMA_CH_IRQ_LOCAL = 0,
DW_EDMA_CH_IRQ_REMOTE,
};
/**
* struct dw_edma_chip - representation of DesignWare eDMA controller hardware
* @dev: struct device of the eDMA controller
* @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
* @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
* @db_irq: Virtual IRQ dedicated to interrupt emulation
* @db_offset: Offset from DMA register base
* @mf: DMA register map format
* @func_no: PCI endpoint function number used by DMA TLPs
* @dw: struct dw_edma that is filled by dw_edma_probe()
*/
struct dw_edma_chip {
struct device *dev;
int nr_irqs;
const struct dw_edma_plat_ops *ops;
u32 flags;
void __iomem *reg_base;
u16 ll_wr_cnt;
u16 ll_rd_cnt;
/* link list address */
struct dw_edma_region ll_region_wr[HDMA_MAX_WR_CH];
struct dw_edma_region ll_region_rd[HDMA_MAX_RD_CH];
/* data region */
struct dw_edma_region dt_region_wr[HDMA_MAX_WR_CH];
struct dw_edma_region dt_region_rd[HDMA_MAX_RD_CH];
/* interrupt emulation */
int db_irq;
resource_size_t db_offset;
enum dw_edma_map_format mf;
u8 func_no;
struct dw_edma *dw;
bool cfg_non_ll;
};
/* Export to the platform drivers */
#if IS_REACHABLE(CONFIG_DW_EDMA)
int dw_edma_probe(struct dw_edma_chip *chip);
int dw_edma_remove(struct dw_edma_chip *chip);
#else
static inline int dw_edma_probe(struct dw_edma_chip *chip)
{
return -ENODEV;
}
static inline int dw_edma_remove(struct dw_edma_chip *chip)
{
return 0;
}
#endif /* CONFIG_DW_EDMA */
#endif /* _DW_EDMA_H */