mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-17 15:14:35 +08:00
dmaengine updates for 4.6
This is smallish update with minor changes to core and new driver and usual updates. Nothing super exciting here.. - We have made slave address as physical to enable driver to do the mapping. - We now expose the maxburst for slave dma as new capability so clients can know this and program accordingly - addition of device synchronize callbacks on omap and edma. - pl330 updates to support DMAFLUSHP for Rockchip platforms. - Updates and improved sg handling in Xilinx VDMA driver. - New hidma qualcomm dma driver, though some bits are still in progress -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJW6W4OAAoJEHwUBw8lI4NHIj0P/0UEXOn9Laj1dQ++3RuEHtJH AvolC574yj/jdvhNNRAu3TBq214VDtVu+OEi6cAwybSMUOT0lbrSEI4a6K6iDIdH QGfyz2PFDBMnNLqqNfeQulgB6YgoZ/7PXUOz9D+FX4wyM3poTBb9J2JI5okFuuJI r4jmiZrXTZSmm2NTbG0QxHogoyvMDA59EB8cIgAUrl1rDssPkdvzU7ygW6qc5CMW 33tQFyz6Q74EI9ImPeYUkSf1zzT1va4uRce+3lEmLSvtOWG2pjOOZ1Vw89vtkyal yX1eH06glVTQwpfV+fgmbjpn72Ftk+G6rqcB4aICSyN2dH7Gf4D+Dqjp1mdEHyFf Oum5pWNPzJ97HoGLwxd8FEuA3ma3C0nC+nDl+ffNWLmIDGgeqFHSQaNBlf2S6y+a VtGFJ0EaR//qIpwvPNfpJbkwjrEaEFdSYQcdpGcPPeTeOOpaLGkmJ/2kD2rpGSNC iPh+G/h7sJYLFyiG7C6GeuWxShzSL+LpZqv0ks5i/QKmSNXWsvVQexAlBW43R385 uQkZSWOlzUwmGlTj9XUI2mUxhI73SgKt+WZ9wrJWvIThBHRwwSIln+72SzQ8d4ys Smv3DkGt4gCxHmsV+G3nEIBlviECJn2KaaN450D6FVxgQ40yGV5gWAVX4yAWo2De uMnQMDamjoajgbeanpbM =wCCJ -----END PGP SIGNATURE----- Merge tag 'dmaengine-4.6-rc1' of git://git.infradead.org/users/vkoul/slave-dma Pull dmaengine updates from Vinod Koul: "This is smallish update with minor changes to core and new driver and usual updates. Nothing super exciting here.. - We have made slave address as physical to enable driver to do the mapping. - We now expose the maxburst for slave dma as new capability so clients can know this and program accordingly - addition of device synchronize callbacks on omap and edma. - pl330 updates to support DMAFLUSHP for Rockchip platforms. - Updates and improved sg handling in Xilinx VDMA driver. - New hidma qualcomm dma driver, though some bits are still in progress" * tag 'dmaengine-4.6-rc1' of git://git.infradead.org/users/vkoul/slave-dma: (40 commits) dmaengine: IOATDMA: revise channel reset workaround on CB3.3 platforms dmaengine: add Qualcomm Technologies HIDMA channel driver dmaengine: add Qualcomm Technologies HIDMA management driver dmaengine: hidma: Add Device Tree binding dmaengine: qcom_bam_dma: move to qcom directory dmaengine: tegra: Move of_device_id table near to its user dmaengine: xilinx_vdma: Remove unnecessary variable initializations dmaengine: sirf: use __maybe_unused to hide pm functions dmaengine: rcar-dmac: clear pertinence number of channels dmaengine: sh: shdmac: don't open code of_device_get_match_data() dmaengine: tegra: don't open code of_device_get_match_data() dmaengine: qcom_bam_dma: Make driver work for BE dmaengine: sun4i: support module autoloading dma/mic_x100_dma: IS_ERR() vs PTR_ERR() typo dmaengine: xilinx_vdma: Use readl_poll_timeout instead of do while loop's dmaengine: xilinx_vdma: Simplify spin lock handling dmaengine: xilinx_vdma: Fix issues with non-parking mode dmaengine: xilinx_vdma: Improve SG engine handling dmaengine: pl330: fix to support the burst mode dmaengine: make slave address physical ...
This commit is contained in:
commit
b5b131c747
97
Documentation/ABI/testing/sysfs-platform-hidma-mgmt
Normal file
97
Documentation/ABI/testing/sysfs-platform-hidma-mgmt
Normal file
@ -0,0 +1,97 @@
|
||||
What: /sys/devices/platform/hidma-mgmt*/chanops/chan*/priority
|
||||
/sys/devices/platform/QCOM8060:*/chanops/chan*/priority
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Contains either 0 or 1 and indicates if the DMA channel is a
|
||||
low priority (0) or high priority (1) channel.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/chanops/chan*/weight
|
||||
/sys/devices/platform/QCOM8060:*/chanops/chan*/weight
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Contains 0..15 and indicates the weight of the channel among
|
||||
equal priority channels during round robin scheduling.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/chreset_timeout_cycles
|
||||
/sys/devices/platform/QCOM8060:*/chreset_timeout_cycles
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Contains the platform specific cycle value to wait after a
|
||||
reset command is issued. If the value is chosen too short,
|
||||
then the HW will issue a reset failure interrupt. The value
|
||||
is platform specific and should not be changed without
|
||||
consultance.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/dma_channels
|
||||
/sys/devices/platform/QCOM8060:*/dma_channels
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Contains the number of dma channels supported by one instance
|
||||
of HIDMA hardware. The value may change from chip to chip.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/hw_version_major
|
||||
/sys/devices/platform/QCOM8060:*/hw_version_major
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Version number major for the hardware.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/hw_version_minor
|
||||
/sys/devices/platform/QCOM8060:*/hw_version_minor
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Version number minor for the hardware.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/max_rd_xactions
|
||||
/sys/devices/platform/QCOM8060:*/max_rd_xactions
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Contains a value between 0 and 31. Maximum number of
|
||||
read transactions that can be issued back to back.
|
||||
Choosing a higher number gives better performance but
|
||||
can also cause performance reduction to other peripherals
|
||||
sharing the same bus.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/max_read_request
|
||||
/sys/devices/platform/QCOM8060:*/max_read_request
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Size of each read request. The value needs to be a power
|
||||
of two and can be between 128 and 1024.
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/max_wr_xactions
|
||||
/sys/devices/platform/QCOM8060:*/max_wr_xactions
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Contains a value between 0 and 31. Maximum number of
|
||||
write transactions that can be issued back to back.
|
||||
Choosing a higher number gives better performance but
|
||||
can also cause performance reduction to other peripherals
|
||||
sharing the same bus.
|
||||
|
||||
|
||||
What: /sys/devices/platform/hidma-mgmt*/max_write_request
|
||||
/sys/devices/platform/QCOM8060:*/max_write_request
|
||||
Date: Nov 2015
|
||||
KernelVersion: 4.4
|
||||
Contact: "Sinan Kaya <okaya@cudeaurora.org>"
|
||||
Description:
|
||||
Size of each write request. The value needs to be a power
|
||||
of two and can be between 128 and 1024.
|
@ -15,6 +15,7 @@ Optional properties:
|
||||
cells in the dmas property of client device.
|
||||
- dma-channels: contains the total number of DMA channels supported by the DMAC
|
||||
- dma-requests: contains the total number of DMA requests supported by the DMAC
|
||||
- arm,pl330-broken-no-flushp: quirk for avoiding to execute DMAFLUSHP
|
||||
|
||||
Example:
|
||||
|
||||
|
89
Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt
Normal file
89
Documentation/devicetree/bindings/dma/qcom_hidma_mgmt.txt
Normal file
@ -0,0 +1,89 @@
|
||||
Qualcomm Technologies HIDMA Management interface
|
||||
|
||||
Qualcomm Technologies HIDMA is a high speed DMA device. It only supports
|
||||
memcpy and memset capabilities. It has been designed for virtualized
|
||||
environments.
|
||||
|
||||
Each HIDMA HW instance consists of multiple DMA channels. These channels
|
||||
share the same bandwidth. The bandwidth utilization can be parititioned
|
||||
among channels based on the priority and weight assignments.
|
||||
|
||||
There are only two priority levels and 15 weigh assignments possible.
|
||||
|
||||
Other parameters here determine how much of the system bus this HIDMA
|
||||
instance can use like maximum read/write request and and number of bytes to
|
||||
read/write in a single burst.
|
||||
|
||||
Main node required properties:
|
||||
- compatible: "qcom,hidma-mgmt-1.0";
|
||||
- reg: Address range for DMA device
|
||||
- dma-channels: Number of channels supported by this DMA controller.
|
||||
- max-write-burst-bytes: Maximum write burst in bytes that HIDMA can
|
||||
occupy the bus for in a single transaction. A memcpy requested is
|
||||
fragmented to multiples of this amount. This parameter is used while
|
||||
writing into destination memory. Setting this value incorrectly can
|
||||
starve other peripherals in the system.
|
||||
- max-read-burst-bytes: Maximum read burst in bytes that HIDMA can
|
||||
occupy the bus for in a single transaction. A memcpy request is
|
||||
fragmented to multiples of this amount. This parameter is used while
|
||||
reading the source memory. Setting this value incorrectly can starve
|
||||
other peripherals in the system.
|
||||
- max-write-transactions: This value is how many times a write burst is
|
||||
applied back to back while writing to the destination before yielding
|
||||
the bus.
|
||||
- max-read-transactions: This value is how many times a read burst is
|
||||
applied back to back while reading the source before yielding the bus.
|
||||
- channel-reset-timeout-cycles: Channel reset timeout in cycles for this SOC.
|
||||
Once a reset is applied to the HW, HW starts a timer for reset operation
|
||||
to confirm. If reset is not completed within this time, HW reports reset
|
||||
failure.
|
||||
|
||||
Sub-nodes:
|
||||
|
||||
HIDMA has one or more DMA channels that are used to move data from one
|
||||
memory location to another.
|
||||
|
||||
When the OS is not in control of the management interface (i.e. it's a guest),
|
||||
the channel nodes appear on their own, not under a management node.
|
||||
|
||||
Required properties:
|
||||
- compatible: must contain "qcom,hidma-1.0"
|
||||
- reg: Addresses for the transfer and event channel
|
||||
- interrupts: Should contain the event interrupt
|
||||
- desc-count: Number of asynchronous requests this channel can handle
|
||||
- iommus: required a iommu node
|
||||
|
||||
Example:
|
||||
|
||||
Hypervisor OS configuration:
|
||||
|
||||
hidma-mgmt@f9984000 = {
|
||||
compatible = "qcom,hidma-mgmt-1.0";
|
||||
reg = <0xf9984000 0x15000>;
|
||||
dma-channels = <6>;
|
||||
max-write-burst-bytes = <1024>;
|
||||
max-read-burst-bytes = <1024>;
|
||||
max-write-transactions = <31>;
|
||||
max-read-transactions = <31>;
|
||||
channel-reset-timeout-cycles = <0x500>;
|
||||
|
||||
hidma_24: dma-controller@0x5c050000 {
|
||||
compatible = "qcom,hidma-1.0";
|
||||
reg = <0 0x5c050000 0x0 0x1000>,
|
||||
<0 0x5c0b0000 0x0 0x1000>;
|
||||
interrupts = <0 389 0>;
|
||||
desc-count = <10>;
|
||||
iommus = <&system_mmu>;
|
||||
};
|
||||
};
|
||||
|
||||
Guest OS configuration:
|
||||
|
||||
hidma_24: dma-controller@0x5c050000 {
|
||||
compatible = "qcom,hidma-1.0";
|
||||
reg = <0 0x5c050000 0x0 0x1000>,
|
||||
<0 0x5c0b0000 0x0 0x1000>;
|
||||
interrupts = <0 389 0>;
|
||||
desc-count = <10>;
|
||||
iommus = <&system_mmu>;
|
||||
};
|
@ -341,12 +341,13 @@ config MV_XOR
|
||||
|
||||
config MXS_DMA
|
||||
bool "MXS DMA support"
|
||||
depends on SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q
|
||||
depends on SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q || SOC_IMX6UL
|
||||
select STMP_DEVICE
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Support the MXS DMA engine. This engine including APBH-DMA
|
||||
and APBX-DMA is integrated into Freescale i.MX23/28/MX6Q/MX6DL chips.
|
||||
and APBX-DMA is integrated into Freescale
|
||||
i.MX23/28/MX6Q/MX6DL/MX6UL chips.
|
||||
|
||||
config MX3_IPU
|
||||
bool "MX3x Image Processing Unit support"
|
||||
@ -408,15 +409,6 @@ config PXA_DMA
|
||||
16 to 32 channels for peripheral to memory or memory to memory
|
||||
transfers.
|
||||
|
||||
config QCOM_BAM_DMA
|
||||
tristate "QCOM BAM DMA support"
|
||||
depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
---help---
|
||||
Enable support for the QCOM BAM DMA controller. This controller
|
||||
provides DMA capabilities for a variety of on-chip devices.
|
||||
|
||||
config SIRF_DMA
|
||||
tristate "CSR SiRFprimaII/SiRFmarco DMA support"
|
||||
depends on ARCH_SIRF
|
||||
@ -539,6 +531,8 @@ config ZX_DMA
|
||||
# driver files
|
||||
source "drivers/dma/bestcomm/Kconfig"
|
||||
|
||||
source "drivers/dma/qcom/Kconfig"
|
||||
|
||||
source "drivers/dma/dw/Kconfig"
|
||||
|
||||
source "drivers/dma/hsu/Kconfig"
|
||||
|
@ -52,7 +52,6 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o
|
||||
obj-$(CONFIG_PL330_DMA) += pl330.o
|
||||
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
|
||||
obj-$(CONFIG_PXA_DMA) += pxa_dma.o
|
||||
obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
|
||||
obj-$(CONFIG_RENESAS_DMA) += sh/
|
||||
obj-$(CONFIG_SIRF_DMA) += sirf-dma.o
|
||||
obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
|
||||
@ -67,4 +66,5 @@ obj-$(CONFIG_TI_EDMA) += edma.o
|
||||
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
|
||||
obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
|
||||
|
||||
obj-y += qcom/
|
||||
obj-y += xilinx/
|
||||
|
@ -438,7 +438,7 @@ struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev,
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "found DMA channel \"%s\" at index %d\n", name, index);
|
||||
dev_dbg(dev, "Looking for DMA channel \"%s\" at index %d...\n", name, index);
|
||||
return acpi_dma_request_slave_chan_by_index(dev, index);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_name);
|
||||
|
@ -496,6 +496,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
|
||||
caps->src_addr_widths = device->src_addr_widths;
|
||||
caps->dst_addr_widths = device->dst_addr_widths;
|
||||
caps->directions = device->directions;
|
||||
caps->max_burst = device->max_burst;
|
||||
caps->residue_granularity = device->residue_granularity;
|
||||
caps->descriptor_reuse = device->descriptor_reuse;
|
||||
|
||||
|
@ -150,7 +150,7 @@ enum dw_dma_msize {
|
||||
#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */
|
||||
#define DWC_CTLL_DST_DEC (1<<7)
|
||||
#define DWC_CTLL_DST_FIX (2<<7)
|
||||
#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */
|
||||
#define DWC_CTLL_SRC_INC (0<<9) /* SAR update/not */
|
||||
#define DWC_CTLL_SRC_DEC (1<<9)
|
||||
#define DWC_CTLL_SRC_FIX (2<<9)
|
||||
#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */
|
||||
|
@ -869,6 +869,13 @@ static int edma_terminate_all(struct dma_chan *chan)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void edma_synchronize(struct dma_chan *chan)
|
||||
{
|
||||
struct edma_chan *echan = to_edma_chan(chan);
|
||||
|
||||
vchan_synchronize(&echan->vchan);
|
||||
}
|
||||
|
||||
static int edma_slave_config(struct dma_chan *chan,
|
||||
struct dma_slave_config *cfg)
|
||||
{
|
||||
@ -1365,36 +1372,36 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
|
||||
static void edma_completion_handler(struct edma_chan *echan)
|
||||
{
|
||||
struct device *dev = echan->vchan.chan.device->dev;
|
||||
struct edma_desc *edesc = echan->edesc;
|
||||
|
||||
if (!edesc)
|
||||
return;
|
||||
struct edma_desc *edesc;
|
||||
|
||||
spin_lock(&echan->vchan.lock);
|
||||
if (edesc->cyclic) {
|
||||
vchan_cyclic_callback(&edesc->vdesc);
|
||||
spin_unlock(&echan->vchan.lock);
|
||||
return;
|
||||
} else if (edesc->processed == edesc->pset_nr) {
|
||||
edesc->residue = 0;
|
||||
edma_stop(echan);
|
||||
vchan_cookie_complete(&edesc->vdesc);
|
||||
echan->edesc = NULL;
|
||||
edesc = echan->edesc;
|
||||
if (edesc) {
|
||||
if (edesc->cyclic) {
|
||||
vchan_cyclic_callback(&edesc->vdesc);
|
||||
spin_unlock(&echan->vchan.lock);
|
||||
return;
|
||||
} else if (edesc->processed == edesc->pset_nr) {
|
||||
edesc->residue = 0;
|
||||
edma_stop(echan);
|
||||
vchan_cookie_complete(&edesc->vdesc);
|
||||
echan->edesc = NULL;
|
||||
|
||||
dev_dbg(dev, "Transfer completed on channel %d\n",
|
||||
echan->ch_num);
|
||||
} else {
|
||||
dev_dbg(dev, "Sub transfer completed on channel %d\n",
|
||||
echan->ch_num);
|
||||
dev_dbg(dev, "Transfer completed on channel %d\n",
|
||||
echan->ch_num);
|
||||
} else {
|
||||
dev_dbg(dev, "Sub transfer completed on channel %d\n",
|
||||
echan->ch_num);
|
||||
|
||||
edma_pause(echan);
|
||||
edma_pause(echan);
|
||||
|
||||
/* Update statistics for tx_status */
|
||||
edesc->residue -= edesc->sg_len;
|
||||
edesc->residue_stat = edesc->residue;
|
||||
edesc->processed_stat = edesc->processed;
|
||||
/* Update statistics for tx_status */
|
||||
edesc->residue -= edesc->sg_len;
|
||||
edesc->residue_stat = edesc->residue;
|
||||
edesc->processed_stat = edesc->processed;
|
||||
}
|
||||
edma_execute(echan);
|
||||
}
|
||||
edma_execute(echan);
|
||||
|
||||
spin_unlock(&echan->vchan.lock);
|
||||
}
|
||||
@ -1837,6 +1844,7 @@ static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
|
||||
s_ddev->device_pause = edma_dma_pause;
|
||||
s_ddev->device_resume = edma_dma_resume;
|
||||
s_ddev->device_terminate_all = edma_terminate_all;
|
||||
s_ddev->device_synchronize = edma_synchronize;
|
||||
|
||||
s_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS;
|
||||
s_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS;
|
||||
@ -1862,6 +1870,7 @@ static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
|
||||
m_ddev->device_pause = edma_dma_pause;
|
||||
m_ddev->device_resume = edma_dma_resume;
|
||||
m_ddev->device_terminate_all = edma_terminate_all;
|
||||
m_ddev->device_synchronize = edma_synchronize;
|
||||
|
||||
m_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS;
|
||||
m_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS;
|
||||
|
@ -421,23 +421,25 @@ static int m2p_hw_interrupt(struct ep93xx_dma_chan *edmac)
|
||||
desc->size);
|
||||
}
|
||||
|
||||
switch (irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)) {
|
||||
case M2P_INTERRUPT_STALL:
|
||||
/* Disable interrupts */
|
||||
control = readl(edmac->regs + M2P_CONTROL);
|
||||
control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
|
||||
m2p_set_control(edmac, control);
|
||||
|
||||
return INTERRUPT_DONE;
|
||||
|
||||
case M2P_INTERRUPT_NFB:
|
||||
if (ep93xx_dma_advance_active(edmac))
|
||||
m2p_fill_desc(edmac);
|
||||
/*
|
||||
* Even latest E2 silicon revision sometimes assert STALL interrupt
|
||||
* instead of NFB. Therefore we treat them equally, basing on the
|
||||
* amount of data we still have to transfer.
|
||||
*/
|
||||
if (!(irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)))
|
||||
return INTERRUPT_UNKNOWN;
|
||||
|
||||
if (ep93xx_dma_advance_active(edmac)) {
|
||||
m2p_fill_desc(edmac);
|
||||
return INTERRUPT_NEXT_BUFFER;
|
||||
}
|
||||
|
||||
return INTERRUPT_UNKNOWN;
|
||||
/* Disable interrupts */
|
||||
control = readl(edmac->regs + M2P_CONTROL);
|
||||
control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
|
||||
m2p_set_control(edmac, control);
|
||||
|
||||
return INTERRUPT_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -289,6 +289,9 @@ static void idma64_desc_fill(struct idma64_chan *idma64c,
|
||||
|
||||
/* Trigger an interrupt after the last block is transfered */
|
||||
lli->ctllo |= IDMA64C_CTLL_INT_EN;
|
||||
|
||||
/* Disable LLP transfer in the last block */
|
||||
lli->ctllo &= ~(IDMA64C_CTLL_LLP_S_EN | IDMA64C_CTLL_LLP_D_EN);
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *idma64_prep_slave_sg(
|
||||
|
@ -71,7 +71,7 @@
|
||||
#define IDMA64C_CFGH_SRC_PER(x) ((x) << 0) /* src peripheral */
|
||||
#define IDMA64C_CFGH_DST_PER(x) ((x) << 4) /* dst peripheral */
|
||||
#define IDMA64C_CFGH_RD_ISSUE_THD(x) ((x) << 8)
|
||||
#define IDMA64C_CFGH_RW_ISSUE_THD(x) ((x) << 18)
|
||||
#define IDMA64C_CFGH_WR_ISSUE_THD(x) ((x) << 18)
|
||||
|
||||
/* Interrupt registers */
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/sizes.h>
|
||||
#include "dma.h"
|
||||
#include "registers.h"
|
||||
#include "hw.h"
|
||||
@ -290,24 +291,30 @@ static dma_cookie_t ioat_tx_submit_unlock(struct dma_async_tx_descriptor *tx)
|
||||
}
|
||||
|
||||
static struct ioat_ring_ent *
|
||||
ioat_alloc_ring_ent(struct dma_chan *chan, gfp_t flags)
|
||||
ioat_alloc_ring_ent(struct dma_chan *chan, int idx, gfp_t flags)
|
||||
{
|
||||
struct ioat_dma_descriptor *hw;
|
||||
struct ioat_ring_ent *desc;
|
||||
struct ioatdma_device *ioat_dma;
|
||||
struct ioatdma_chan *ioat_chan = to_ioat_chan(chan);
|
||||
int chunk;
|
||||
dma_addr_t phys;
|
||||
u8 *pos;
|
||||
off_t offs;
|
||||
|
||||
ioat_dma = to_ioatdma_device(chan->device);
|
||||
hw = pci_pool_alloc(ioat_dma->dma_pool, flags, &phys);
|
||||
if (!hw)
|
||||
return NULL;
|
||||
|
||||
chunk = idx / IOAT_DESCS_PER_2M;
|
||||
idx &= (IOAT_DESCS_PER_2M - 1);
|
||||
offs = idx * IOAT_DESC_SZ;
|
||||
pos = (u8 *)ioat_chan->descs[chunk].virt + offs;
|
||||
phys = ioat_chan->descs[chunk].hw + offs;
|
||||
hw = (struct ioat_dma_descriptor *)pos;
|
||||
memset(hw, 0, sizeof(*hw));
|
||||
|
||||
desc = kmem_cache_zalloc(ioat_cache, flags);
|
||||
if (!desc) {
|
||||
pci_pool_free(ioat_dma->dma_pool, hw, phys);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dma_async_tx_descriptor_init(&desc->txd, chan);
|
||||
desc->txd.tx_submit = ioat_tx_submit_unlock;
|
||||
@ -318,32 +325,63 @@ ioat_alloc_ring_ent(struct dma_chan *chan, gfp_t flags)
|
||||
|
||||
void ioat_free_ring_ent(struct ioat_ring_ent *desc, struct dma_chan *chan)
|
||||
{
|
||||
struct ioatdma_device *ioat_dma;
|
||||
|
||||
ioat_dma = to_ioatdma_device(chan->device);
|
||||
pci_pool_free(ioat_dma->dma_pool, desc->hw, desc->txd.phys);
|
||||
kmem_cache_free(ioat_cache, desc);
|
||||
}
|
||||
|
||||
struct ioat_ring_ent **
|
||||
ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags)
|
||||
{
|
||||
struct ioatdma_chan *ioat_chan = to_ioat_chan(c);
|
||||
struct ioat_ring_ent **ring;
|
||||
int descs = 1 << order;
|
||||
int i;
|
||||
|
||||
if (order > ioat_get_max_alloc_order())
|
||||
return NULL;
|
||||
int total_descs = 1 << order;
|
||||
int i, chunks;
|
||||
|
||||
/* allocate the array to hold the software ring */
|
||||
ring = kcalloc(descs, sizeof(*ring), flags);
|
||||
ring = kcalloc(total_descs, sizeof(*ring), flags);
|
||||
if (!ring)
|
||||
return NULL;
|
||||
for (i = 0; i < descs; i++) {
|
||||
ring[i] = ioat_alloc_ring_ent(c, flags);
|
||||
|
||||
ioat_chan->desc_chunks = chunks = (total_descs * IOAT_DESC_SZ) / SZ_2M;
|
||||
|
||||
for (i = 0; i < chunks; i++) {
|
||||
struct ioat_descs *descs = &ioat_chan->descs[i];
|
||||
|
||||
descs->virt = dma_alloc_coherent(to_dev(ioat_chan),
|
||||
SZ_2M, &descs->hw, flags);
|
||||
if (!descs->virt && (i > 0)) {
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < i; idx++) {
|
||||
dma_free_coherent(to_dev(ioat_chan), SZ_2M,
|
||||
descs->virt, descs->hw);
|
||||
descs->virt = NULL;
|
||||
descs->hw = 0;
|
||||
}
|
||||
|
||||
ioat_chan->desc_chunks = 0;
|
||||
kfree(ring);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < total_descs; i++) {
|
||||
ring[i] = ioat_alloc_ring_ent(c, i, flags);
|
||||
if (!ring[i]) {
|
||||
int idx;
|
||||
|
||||
while (i--)
|
||||
ioat_free_ring_ent(ring[i], c);
|
||||
|
||||
for (idx = 0; idx < ioat_chan->desc_chunks; idx++) {
|
||||
dma_free_coherent(to_dev(ioat_chan),
|
||||
SZ_2M,
|
||||
ioat_chan->descs[idx].virt,
|
||||
ioat_chan->descs[idx].hw);
|
||||
ioat_chan->descs[idx].virt = NULL;
|
||||
ioat_chan->descs[idx].hw = 0;
|
||||
}
|
||||
|
||||
ioat_chan->desc_chunks = 0;
|
||||
kfree(ring);
|
||||
return NULL;
|
||||
}
|
||||
@ -351,7 +389,7 @@ ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags)
|
||||
}
|
||||
|
||||
/* link descs */
|
||||
for (i = 0; i < descs-1; i++) {
|
||||
for (i = 0; i < total_descs-1; i++) {
|
||||
struct ioat_ring_ent *next = ring[i+1];
|
||||
struct ioat_dma_descriptor *hw = ring[i]->hw;
|
||||
|
||||
@ -362,114 +400,6 @@ ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags)
|
||||
return ring;
|
||||
}
|
||||
|
||||
static bool reshape_ring(struct ioatdma_chan *ioat_chan, int order)
|
||||
{
|
||||
/* reshape differs from normal ring allocation in that we want
|
||||
* to allocate a new software ring while only
|
||||
* extending/truncating the hardware ring
|
||||
*/
|
||||
struct dma_chan *c = &ioat_chan->dma_chan;
|
||||
const u32 curr_size = ioat_ring_size(ioat_chan);
|
||||
const u16 active = ioat_ring_active(ioat_chan);
|
||||
const u32 new_size = 1 << order;
|
||||
struct ioat_ring_ent **ring;
|
||||
u32 i;
|
||||
|
||||
if (order > ioat_get_max_alloc_order())
|
||||
return false;
|
||||
|
||||
/* double check that we have at least 1 free descriptor */
|
||||
if (active == curr_size)
|
||||
return false;
|
||||
|
||||
/* when shrinking, verify that we can hold the current active
|
||||
* set in the new ring
|
||||
*/
|
||||
if (active >= new_size)
|
||||
return false;
|
||||
|
||||
/* allocate the array to hold the software ring */
|
||||
ring = kcalloc(new_size, sizeof(*ring), GFP_NOWAIT);
|
||||
if (!ring)
|
||||
return false;
|
||||
|
||||
/* allocate/trim descriptors as needed */
|
||||
if (new_size > curr_size) {
|
||||
/* copy current descriptors to the new ring */
|
||||
for (i = 0; i < curr_size; i++) {
|
||||
u16 curr_idx = (ioat_chan->tail+i) & (curr_size-1);
|
||||
u16 new_idx = (ioat_chan->tail+i) & (new_size-1);
|
||||
|
||||
ring[new_idx] = ioat_chan->ring[curr_idx];
|
||||
set_desc_id(ring[new_idx], new_idx);
|
||||
}
|
||||
|
||||
/* add new descriptors to the ring */
|
||||
for (i = curr_size; i < new_size; i++) {
|
||||
u16 new_idx = (ioat_chan->tail+i) & (new_size-1);
|
||||
|
||||
ring[new_idx] = ioat_alloc_ring_ent(c, GFP_NOWAIT);
|
||||
if (!ring[new_idx]) {
|
||||
while (i--) {
|
||||
u16 new_idx = (ioat_chan->tail+i) &
|
||||
(new_size-1);
|
||||
|
||||
ioat_free_ring_ent(ring[new_idx], c);
|
||||
}
|
||||
kfree(ring);
|
||||
return false;
|
||||
}
|
||||
set_desc_id(ring[new_idx], new_idx);
|
||||
}
|
||||
|
||||
/* hw link new descriptors */
|
||||
for (i = curr_size-1; i < new_size; i++) {
|
||||
u16 new_idx = (ioat_chan->tail+i) & (new_size-1);
|
||||
struct ioat_ring_ent *next =
|
||||
ring[(new_idx+1) & (new_size-1)];
|
||||
struct ioat_dma_descriptor *hw = ring[new_idx]->hw;
|
||||
|
||||
hw->next = next->txd.phys;
|
||||
}
|
||||
} else {
|
||||
struct ioat_dma_descriptor *hw;
|
||||
struct ioat_ring_ent *next;
|
||||
|
||||
/* copy current descriptors to the new ring, dropping the
|
||||
* removed descriptors
|
||||
*/
|
||||
for (i = 0; i < new_size; i++) {
|
||||
u16 curr_idx = (ioat_chan->tail+i) & (curr_size-1);
|
||||
u16 new_idx = (ioat_chan->tail+i) & (new_size-1);
|
||||
|
||||
ring[new_idx] = ioat_chan->ring[curr_idx];
|
||||
set_desc_id(ring[new_idx], new_idx);
|
||||
}
|
||||
|
||||
/* free deleted descriptors */
|
||||
for (i = new_size; i < curr_size; i++) {
|
||||
struct ioat_ring_ent *ent;
|
||||
|
||||
ent = ioat_get_ring_ent(ioat_chan, ioat_chan->tail+i);
|
||||
ioat_free_ring_ent(ent, c);
|
||||
}
|
||||
|
||||
/* fix up hardware ring */
|
||||
hw = ring[(ioat_chan->tail+new_size-1) & (new_size-1)]->hw;
|
||||
next = ring[(ioat_chan->tail+new_size) & (new_size-1)];
|
||||
hw->next = next->txd.phys;
|
||||
}
|
||||
|
||||
dev_dbg(to_dev(ioat_chan), "%s: allocated %d descriptors\n",
|
||||
__func__, new_size);
|
||||
|
||||
kfree(ioat_chan->ring);
|
||||
ioat_chan->ring = ring;
|
||||
ioat_chan->alloc_order = order;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ioat_check_space_lock - verify space and grab ring producer lock
|
||||
* @ioat: ioat,3 channel (ring) to operate on
|
||||
@ -478,9 +408,6 @@ static bool reshape_ring(struct ioatdma_chan *ioat_chan, int order)
|
||||
int ioat_check_space_lock(struct ioatdma_chan *ioat_chan, int num_descs)
|
||||
__acquires(&ioat_chan->prep_lock)
|
||||
{
|
||||
bool retry;
|
||||
|
||||
retry:
|
||||
spin_lock_bh(&ioat_chan->prep_lock);
|
||||
/* never allow the last descriptor to be consumed, we need at
|
||||
* least one free at all times to allow for on-the-fly ring
|
||||
@ -493,24 +420,8 @@ int ioat_check_space_lock(struct ioatdma_chan *ioat_chan, int num_descs)
|
||||
ioat_chan->produce = num_descs;
|
||||
return 0; /* with ioat->prep_lock held */
|
||||
}
|
||||
retry = test_and_set_bit(IOAT_RESHAPE_PENDING, &ioat_chan->state);
|
||||
spin_unlock_bh(&ioat_chan->prep_lock);
|
||||
|
||||
/* is another cpu already trying to expand the ring? */
|
||||
if (retry)
|
||||
goto retry;
|
||||
|
||||
spin_lock_bh(&ioat_chan->cleanup_lock);
|
||||
spin_lock_bh(&ioat_chan->prep_lock);
|
||||
retry = reshape_ring(ioat_chan, ioat_chan->alloc_order + 1);
|
||||
clear_bit(IOAT_RESHAPE_PENDING, &ioat_chan->state);
|
||||
spin_unlock_bh(&ioat_chan->prep_lock);
|
||||
spin_unlock_bh(&ioat_chan->cleanup_lock);
|
||||
|
||||
/* if we were able to expand the ring retry the allocation */
|
||||
if (retry)
|
||||
goto retry;
|
||||
|
||||
dev_dbg_ratelimited(to_dev(ioat_chan),
|
||||
"%s: ring full! num_descs: %d (%x:%x:%x)\n",
|
||||
__func__, num_descs, ioat_chan->head,
|
||||
@ -823,19 +734,6 @@ static void check_active(struct ioatdma_chan *ioat_chan)
|
||||
|
||||
if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &ioat_chan->state))
|
||||
mod_timer(&ioat_chan->timer, jiffies + IDLE_TIMEOUT);
|
||||
else if (ioat_chan->alloc_order > ioat_get_alloc_order()) {
|
||||
/* if the ring is idle, empty, and oversized try to step
|
||||
* down the size
|
||||
*/
|
||||
reshape_ring(ioat_chan, ioat_chan->alloc_order - 1);
|
||||
|
||||
/* keep shrinking until we get back to our minimum
|
||||
* default size
|
||||
*/
|
||||
if (ioat_chan->alloc_order > ioat_get_alloc_order())
|
||||
mod_timer(&ioat_chan->timer, jiffies + IDLE_TIMEOUT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ioat_timer_event(unsigned long data)
|
||||
@ -916,40 +814,6 @@ ioat_tx_status(struct dma_chan *c, dma_cookie_t cookie,
|
||||
return dma_cookie_status(c, cookie, txstate);
|
||||
}
|
||||
|
||||
static int ioat_irq_reinit(struct ioatdma_device *ioat_dma)
|
||||
{
|
||||
struct pci_dev *pdev = ioat_dma->pdev;
|
||||
int irq = pdev->irq, i;
|
||||
|
||||
if (!is_bwd_ioat(pdev))
|
||||
return 0;
|
||||
|
||||
switch (ioat_dma->irq_mode) {
|
||||
case IOAT_MSIX:
|
||||
for (i = 0; i < ioat_dma->dma_dev.chancnt; i++) {
|
||||
struct msix_entry *msix = &ioat_dma->msix_entries[i];
|
||||
struct ioatdma_chan *ioat_chan;
|
||||
|
||||
ioat_chan = ioat_chan_by_index(ioat_dma, i);
|
||||
devm_free_irq(&pdev->dev, msix->vector, ioat_chan);
|
||||
}
|
||||
|
||||
pci_disable_msix(pdev);
|
||||
break;
|
||||
case IOAT_MSI:
|
||||
pci_disable_msi(pdev);
|
||||
/* fall through */
|
||||
case IOAT_INTX:
|
||||
devm_free_irq(&pdev->dev, irq, ioat_dma);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
ioat_dma->irq_mode = IOAT_NOIRQ;
|
||||
|
||||
return ioat_dma_setup_interrupts(ioat_dma);
|
||||
}
|
||||
|
||||
int ioat_reset_hw(struct ioatdma_chan *ioat_chan)
|
||||
{
|
||||
/* throw away whatever the channel was doing and get it
|
||||
@ -989,9 +853,21 @@ int ioat_reset_hw(struct ioatdma_chan *ioat_chan)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_bwd_ioat(pdev) && (ioat_dma->irq_mode == IOAT_MSIX)) {
|
||||
ioat_dma->msixtba0 = readq(ioat_dma->reg_base + 0x1000);
|
||||
ioat_dma->msixdata0 = readq(ioat_dma->reg_base + 0x1008);
|
||||
ioat_dma->msixpba = readq(ioat_dma->reg_base + 0x1800);
|
||||
}
|
||||
|
||||
|
||||
err = ioat_reset_sync(ioat_chan, msecs_to_jiffies(200));
|
||||
if (!err)
|
||||
err = ioat_irq_reinit(ioat_dma);
|
||||
if (!err) {
|
||||
if (is_bwd_ioat(pdev) && (ioat_dma->irq_mode == IOAT_MSIX)) {
|
||||
writeq(ioat_dma->msixtba0, ioat_dma->reg_base + 0x1000);
|
||||
writeq(ioat_dma->msixdata0, ioat_dma->reg_base + 0x1008);
|
||||
writeq(ioat_dma->msixpba, ioat_dma->reg_base + 0x1800);
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
dev_err(&pdev->dev, "Failed to reset: %d\n", err);
|
||||
|
@ -62,7 +62,6 @@ enum ioat_irq_mode {
|
||||
* struct ioatdma_device - internal representation of a IOAT device
|
||||
* @pdev: PCI-Express device
|
||||
* @reg_base: MMIO register space base address
|
||||
* @dma_pool: for allocating DMA descriptors
|
||||
* @completion_pool: DMA buffers for completion ops
|
||||
* @sed_hw_pool: DMA super descriptor pools
|
||||
* @dma_dev: embedded struct dma_device
|
||||
@ -76,8 +75,7 @@ enum ioat_irq_mode {
|
||||
struct ioatdma_device {
|
||||
struct pci_dev *pdev;
|
||||
void __iomem *reg_base;
|
||||
struct pci_pool *dma_pool;
|
||||
struct pci_pool *completion_pool;
|
||||
struct dma_pool *completion_pool;
|
||||
#define MAX_SED_POOLS 5
|
||||
struct dma_pool *sed_hw_pool[MAX_SED_POOLS];
|
||||
struct dma_device dma_dev;
|
||||
@ -88,6 +86,16 @@ struct ioatdma_device {
|
||||
struct dca_provider *dca;
|
||||
enum ioat_irq_mode irq_mode;
|
||||
u32 cap;
|
||||
|
||||
/* shadow version for CB3.3 chan reset errata workaround */
|
||||
u64 msixtba0;
|
||||
u64 msixdata0;
|
||||
u32 msixpba;
|
||||
};
|
||||
|
||||
struct ioat_descs {
|
||||
void *virt;
|
||||
dma_addr_t hw;
|
||||
};
|
||||
|
||||
struct ioatdma_chan {
|
||||
@ -100,7 +108,6 @@ struct ioatdma_chan {
|
||||
#define IOAT_COMPLETION_ACK 1
|
||||
#define IOAT_RESET_PENDING 2
|
||||
#define IOAT_KOBJ_INIT_FAIL 3
|
||||
#define IOAT_RESHAPE_PENDING 4
|
||||
#define IOAT_RUN 5
|
||||
#define IOAT_CHAN_ACTIVE 6
|
||||
struct timer_list timer;
|
||||
@ -133,6 +140,8 @@ struct ioatdma_chan {
|
||||
u16 produce;
|
||||
struct ioat_ring_ent **ring;
|
||||
spinlock_t prep_lock;
|
||||
struct ioat_descs descs[2];
|
||||
int desc_chunks;
|
||||
};
|
||||
|
||||
struct ioat_sysfs_entry {
|
||||
@ -302,10 +311,8 @@ static inline bool is_ioat_bug(unsigned long err)
|
||||
}
|
||||
|
||||
#define IOAT_MAX_ORDER 16
|
||||
#define ioat_get_alloc_order() \
|
||||
(min(ioat_ring_alloc_order, IOAT_MAX_ORDER))
|
||||
#define ioat_get_max_alloc_order() \
|
||||
(min(ioat_ring_max_alloc_order, IOAT_MAX_ORDER))
|
||||
#define IOAT_MAX_DESCS 65536
|
||||
#define IOAT_DESCS_PER_2M 32768
|
||||
|
||||
static inline u32 ioat_ring_size(struct ioatdma_chan *ioat_chan)
|
||||
{
|
||||
|
@ -73,6 +73,8 @@
|
||||
|
||||
int system_has_dca_enabled(struct pci_dev *pdev);
|
||||
|
||||
#define IOAT_DESC_SZ 64
|
||||
|
||||
struct ioat_dma_descriptor {
|
||||
uint32_t size;
|
||||
union {
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/dca.h>
|
||||
#include <linux/aer.h>
|
||||
#include <linux/sizes.h>
|
||||
#include "dma.h"
|
||||
#include "registers.h"
|
||||
#include "hw.h"
|
||||
@ -136,14 +137,6 @@ int ioat_pending_level = 4;
|
||||
module_param(ioat_pending_level, int, 0644);
|
||||
MODULE_PARM_DESC(ioat_pending_level,
|
||||
"high-water mark for pushing ioat descriptors (default: 4)");
|
||||
int ioat_ring_alloc_order = 8;
|
||||
module_param(ioat_ring_alloc_order, int, 0644);
|
||||
MODULE_PARM_DESC(ioat_ring_alloc_order,
|
||||
"ioat+: allocate 2^n descriptors per channel (default: 8 max: 16)");
|
||||
int ioat_ring_max_alloc_order = IOAT_MAX_ORDER;
|
||||
module_param(ioat_ring_max_alloc_order, int, 0644);
|
||||
MODULE_PARM_DESC(ioat_ring_max_alloc_order,
|
||||
"ioat+: upper limit for ring size (default: 16)");
|
||||
static char ioat_interrupt_style[32] = "msix";
|
||||
module_param_string(ioat_interrupt_style, ioat_interrupt_style,
|
||||
sizeof(ioat_interrupt_style), 0644);
|
||||
@ -504,23 +497,14 @@ static int ioat_probe(struct ioatdma_device *ioat_dma)
|
||||
struct pci_dev *pdev = ioat_dma->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/* DMA coherent memory pool for DMA descriptor allocations */
|
||||
ioat_dma->dma_pool = pci_pool_create("dma_desc_pool", pdev,
|
||||
sizeof(struct ioat_dma_descriptor),
|
||||
64, 0);
|
||||
if (!ioat_dma->dma_pool) {
|
||||
err = -ENOMEM;
|
||||
goto err_dma_pool;
|
||||
}
|
||||
|
||||
ioat_dma->completion_pool = pci_pool_create("completion_pool", pdev,
|
||||
ioat_dma->completion_pool = dma_pool_create("completion_pool", dev,
|
||||
sizeof(u64),
|
||||
SMP_CACHE_BYTES,
|
||||
SMP_CACHE_BYTES);
|
||||
|
||||
if (!ioat_dma->completion_pool) {
|
||||
err = -ENOMEM;
|
||||
goto err_completion_pool;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ioat_enumerate_channels(ioat_dma);
|
||||
@ -546,10 +530,8 @@ static int ioat_probe(struct ioatdma_device *ioat_dma)
|
||||
err_self_test:
|
||||
ioat_disable_interrupts(ioat_dma);
|
||||
err_setup_interrupts:
|
||||
pci_pool_destroy(ioat_dma->completion_pool);
|
||||
err_completion_pool:
|
||||
pci_pool_destroy(ioat_dma->dma_pool);
|
||||
err_dma_pool:
|
||||
dma_pool_destroy(ioat_dma->completion_pool);
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -559,8 +541,7 @@ static int ioat_register(struct ioatdma_device *ioat_dma)
|
||||
|
||||
if (err) {
|
||||
ioat_disable_interrupts(ioat_dma);
|
||||
pci_pool_destroy(ioat_dma->completion_pool);
|
||||
pci_pool_destroy(ioat_dma->dma_pool);
|
||||
dma_pool_destroy(ioat_dma->completion_pool);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -576,8 +557,7 @@ static void ioat_dma_remove(struct ioatdma_device *ioat_dma)
|
||||
|
||||
dma_async_device_unregister(dma);
|
||||
|
||||
pci_pool_destroy(ioat_dma->dma_pool);
|
||||
pci_pool_destroy(ioat_dma->completion_pool);
|
||||
dma_pool_destroy(ioat_dma->completion_pool);
|
||||
|
||||
INIT_LIST_HEAD(&dma->channels);
|
||||
}
|
||||
@ -666,10 +646,19 @@ static void ioat_free_chan_resources(struct dma_chan *c)
|
||||
ioat_free_ring_ent(desc, c);
|
||||
}
|
||||
|
||||
for (i = 0; i < ioat_chan->desc_chunks; i++) {
|
||||
dma_free_coherent(to_dev(ioat_chan), SZ_2M,
|
||||
ioat_chan->descs[i].virt,
|
||||
ioat_chan->descs[i].hw);
|
||||
ioat_chan->descs[i].virt = NULL;
|
||||
ioat_chan->descs[i].hw = 0;
|
||||
}
|
||||
ioat_chan->desc_chunks = 0;
|
||||
|
||||
kfree(ioat_chan->ring);
|
||||
ioat_chan->ring = NULL;
|
||||
ioat_chan->alloc_order = 0;
|
||||
pci_pool_free(ioat_dma->completion_pool, ioat_chan->completion,
|
||||
dma_pool_free(ioat_dma->completion_pool, ioat_chan->completion,
|
||||
ioat_chan->completion_dma);
|
||||
spin_unlock_bh(&ioat_chan->prep_lock);
|
||||
spin_unlock_bh(&ioat_chan->cleanup_lock);
|
||||
@ -701,7 +690,7 @@ static int ioat_alloc_chan_resources(struct dma_chan *c)
|
||||
/* allocate a completion writeback area */
|
||||
/* doing 2 32bit writes to mmio since 1 64b write doesn't work */
|
||||
ioat_chan->completion =
|
||||
pci_pool_alloc(ioat_chan->ioat_dma->completion_pool,
|
||||
dma_pool_alloc(ioat_chan->ioat_dma->completion_pool,
|
||||
GFP_KERNEL, &ioat_chan->completion_dma);
|
||||
if (!ioat_chan->completion)
|
||||
return -ENOMEM;
|
||||
@ -712,7 +701,7 @@ static int ioat_alloc_chan_resources(struct dma_chan *c)
|
||||
writel(((u64)ioat_chan->completion_dma) >> 32,
|
||||
ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH);
|
||||
|
||||
order = ioat_get_alloc_order();
|
||||
order = IOAT_MAX_ORDER;
|
||||
ring = ioat_alloc_ring(c, order, GFP_KERNEL);
|
||||
if (!ring)
|
||||
return -ENOMEM;
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "hw.h"
|
||||
#include "dma.h"
|
||||
|
||||
#define MAX_SCF 1024
|
||||
#define MAX_SCF 256
|
||||
|
||||
/* provide a lookup table for setting the source address in the base or
|
||||
* extended descriptor of an xor or pq descriptor
|
||||
|
@ -483,7 +483,7 @@ static int mic_dma_setup_irq(struct mic_dma_chan *ch)
|
||||
mic_dma_intr_handler, mic_dma_thread_fn,
|
||||
"mic dma_channel", ch, ch->ch_num);
|
||||
if (IS_ERR(ch->cookie))
|
||||
return IS_ERR(ch->cookie);
|
||||
return PTR_ERR(ch->cookie);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1009,6 +1009,13 @@ static int omap_dma_terminate_all(struct dma_chan *chan)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_dma_synchronize(struct dma_chan *chan)
|
||||
{
|
||||
struct omap_chan *c = to_omap_dma_chan(chan);
|
||||
|
||||
vchan_synchronize(&c->vc);
|
||||
}
|
||||
|
||||
static int omap_dma_pause(struct dma_chan *chan)
|
||||
{
|
||||
struct omap_chan *c = to_omap_dma_chan(chan);
|
||||
@ -1112,6 +1119,7 @@ static int omap_dma_probe(struct platform_device *pdev)
|
||||
od->ddev.device_pause = omap_dma_pause;
|
||||
od->ddev.device_resume = omap_dma_resume;
|
||||
od->ddev.device_terminate_all = omap_dma_terminate_all;
|
||||
od->ddev.device_synchronize = omap_dma_synchronize;
|
||||
od->ddev.src_addr_widths = OMAP_DMA_BUSWIDTHS;
|
||||
od->ddev.dst_addr_widths = OMAP_DMA_BUSWIDTHS;
|
||||
od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
|
||||
|
@ -33,6 +33,9 @@
|
||||
#define PL330_MAX_CHAN 8
|
||||
#define PL330_MAX_IRQS 32
|
||||
#define PL330_MAX_PERI 32
|
||||
#define PL330_MAX_BURST 16
|
||||
|
||||
#define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0)
|
||||
|
||||
enum pl330_cachectrl {
|
||||
CCTRL0, /* Noncacheable and nonbufferable */
|
||||
@ -488,6 +491,17 @@ struct pl330_dmac {
|
||||
/* Peripheral channels connected to this DMAC */
|
||||
unsigned int num_peripherals;
|
||||
struct dma_pl330_chan *peripherals; /* keep at end */
|
||||
int quirks;
|
||||
};
|
||||
|
||||
static struct pl330_of_quirks {
|
||||
char *quirk;
|
||||
int id;
|
||||
} of_quirks[] = {
|
||||
{
|
||||
.quirk = "arm,pl330-broken-no-flushp",
|
||||
.id = PL330_QUIRK_BROKEN_NO_FLUSHP,
|
||||
}
|
||||
};
|
||||
|
||||
struct dma_pl330_desc {
|
||||
@ -1137,47 +1151,67 @@ static inline int _ldst_memtomem(unsigned dry_run, u8 buf[],
|
||||
return off;
|
||||
}
|
||||
|
||||
static inline int _ldst_devtomem(unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs, int cyc)
|
||||
static inline int _ldst_devtomem(struct pl330_dmac *pl330, unsigned dry_run,
|
||||
u8 buf[], const struct _xfer_spec *pxs,
|
||||
int cyc)
|
||||
{
|
||||
int off = 0;
|
||||
enum pl330_cond cond;
|
||||
|
||||
if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)
|
||||
cond = BURST;
|
||||
else
|
||||
cond = SINGLE;
|
||||
|
||||
while (cyc--) {
|
||||
off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
|
||||
off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
|
||||
off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
|
||||
off += _emit_LDP(dry_run, &buf[off], cond, pxs->desc->peri);
|
||||
off += _emit_ST(dry_run, &buf[off], ALWAYS);
|
||||
off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
|
||||
|
||||
if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
|
||||
off += _emit_FLUSHP(dry_run, &buf[off],
|
||||
pxs->desc->peri);
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
static inline int _ldst_memtodev(unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs, int cyc)
|
||||
static inline int _ldst_memtodev(struct pl330_dmac *pl330,
|
||||
unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs, int cyc)
|
||||
{
|
||||
int off = 0;
|
||||
enum pl330_cond cond;
|
||||
|
||||
if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)
|
||||
cond = BURST;
|
||||
else
|
||||
cond = SINGLE;
|
||||
|
||||
while (cyc--) {
|
||||
off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
|
||||
off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
|
||||
off += _emit_LD(dry_run, &buf[off], ALWAYS);
|
||||
off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
|
||||
off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
|
||||
off += _emit_STP(dry_run, &buf[off], cond, pxs->desc->peri);
|
||||
|
||||
if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
|
||||
off += _emit_FLUSHP(dry_run, &buf[off],
|
||||
pxs->desc->peri);
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
static int _bursts(unsigned dry_run, u8 buf[],
|
||||
static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs, int cyc)
|
||||
{
|
||||
int off = 0;
|
||||
|
||||
switch (pxs->desc->rqtype) {
|
||||
case DMA_MEM_TO_DEV:
|
||||
off += _ldst_memtodev(dry_run, &buf[off], pxs, cyc);
|
||||
off += _ldst_memtodev(pl330, dry_run, &buf[off], pxs, cyc);
|
||||
break;
|
||||
case DMA_DEV_TO_MEM:
|
||||
off += _ldst_devtomem(dry_run, &buf[off], pxs, cyc);
|
||||
off += _ldst_devtomem(pl330, dry_run, &buf[off], pxs, cyc);
|
||||
break;
|
||||
case DMA_MEM_TO_MEM:
|
||||
off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc);
|
||||
@ -1191,7 +1225,7 @@ static int _bursts(unsigned dry_run, u8 buf[],
|
||||
}
|
||||
|
||||
/* Returns bytes consumed and updates bursts */
|
||||
static inline int _loop(unsigned dry_run, u8 buf[],
|
||||
static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
|
||||
unsigned long *bursts, const struct _xfer_spec *pxs)
|
||||
{
|
||||
int cyc, cycmax, szlp, szlpend, szbrst, off;
|
||||
@ -1199,7 +1233,7 @@ static inline int _loop(unsigned dry_run, u8 buf[],
|
||||
struct _arg_LPEND lpend;
|
||||
|
||||
if (*bursts == 1)
|
||||
return _bursts(dry_run, buf, pxs, 1);
|
||||
return _bursts(pl330, dry_run, buf, pxs, 1);
|
||||
|
||||
/* Max iterations possible in DMALP is 256 */
|
||||
if (*bursts >= 256*256) {
|
||||
@ -1217,7 +1251,7 @@ static inline int _loop(unsigned dry_run, u8 buf[],
|
||||
}
|
||||
|
||||
szlp = _emit_LP(1, buf, 0, 0);
|
||||
szbrst = _bursts(1, buf, pxs, 1);
|
||||
szbrst = _bursts(pl330, 1, buf, pxs, 1);
|
||||
|
||||
lpend.cond = ALWAYS;
|
||||
lpend.forever = false;
|
||||
@ -1249,7 +1283,7 @@ static inline int _loop(unsigned dry_run, u8 buf[],
|
||||
off += _emit_LP(dry_run, &buf[off], 1, lcnt1);
|
||||
ljmp1 = off;
|
||||
|
||||
off += _bursts(dry_run, &buf[off], pxs, cyc);
|
||||
off += _bursts(pl330, dry_run, &buf[off], pxs, cyc);
|
||||
|
||||
lpend.cond = ALWAYS;
|
||||
lpend.forever = false;
|
||||
@ -1272,8 +1306,9 @@ static inline int _loop(unsigned dry_run, u8 buf[],
|
||||
return off;
|
||||
}
|
||||
|
||||
static inline int _setup_loops(unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs)
|
||||
static inline int _setup_loops(struct pl330_dmac *pl330,
|
||||
unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs)
|
||||
{
|
||||
struct pl330_xfer *x = &pxs->desc->px;
|
||||
u32 ccr = pxs->ccr;
|
||||
@ -1282,15 +1317,16 @@ static inline int _setup_loops(unsigned dry_run, u8 buf[],
|
||||
|
||||
while (bursts) {
|
||||
c = bursts;
|
||||
off += _loop(dry_run, &buf[off], &c, pxs);
|
||||
off += _loop(pl330, dry_run, &buf[off], &c, pxs);
|
||||
bursts -= c;
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
static inline int _setup_xfer(unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs)
|
||||
static inline int _setup_xfer(struct pl330_dmac *pl330,
|
||||
unsigned dry_run, u8 buf[],
|
||||
const struct _xfer_spec *pxs)
|
||||
{
|
||||
struct pl330_xfer *x = &pxs->desc->px;
|
||||
int off = 0;
|
||||
@ -1301,7 +1337,7 @@ static inline int _setup_xfer(unsigned dry_run, u8 buf[],
|
||||
off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr);
|
||||
|
||||
/* Setup Loop(s) */
|
||||
off += _setup_loops(dry_run, &buf[off], pxs);
|
||||
off += _setup_loops(pl330, dry_run, &buf[off], pxs);
|
||||
|
||||
return off;
|
||||
}
|
||||
@ -1310,8 +1346,9 @@ static inline int _setup_xfer(unsigned dry_run, u8 buf[],
|
||||
* A req is a sequence of one or more xfer units.
|
||||
* Returns the number of bytes taken to setup the MC for the req.
|
||||
*/
|
||||
static int _setup_req(unsigned dry_run, struct pl330_thread *thrd,
|
||||
unsigned index, struct _xfer_spec *pxs)
|
||||
static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run,
|
||||
struct pl330_thread *thrd, unsigned index,
|
||||
struct _xfer_spec *pxs)
|
||||
{
|
||||
struct _pl330_req *req = &thrd->req[index];
|
||||
struct pl330_xfer *x;
|
||||
@ -1328,7 +1365,7 @@ static int _setup_req(unsigned dry_run, struct pl330_thread *thrd,
|
||||
if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr)))
|
||||
return -EINVAL;
|
||||
|
||||
off += _setup_xfer(dry_run, &buf[off], pxs);
|
||||
off += _setup_xfer(pl330, dry_run, &buf[off], pxs);
|
||||
|
||||
/* DMASEV peripheral/event */
|
||||
off += _emit_SEV(dry_run, &buf[off], thrd->ev);
|
||||
@ -1422,7 +1459,7 @@ static int pl330_submit_req(struct pl330_thread *thrd,
|
||||
xs.desc = desc;
|
||||
|
||||
/* First dry run to check if req is acceptable */
|
||||
ret = _setup_req(1, thrd, idx, &xs);
|
||||
ret = _setup_req(pl330, 1, thrd, idx, &xs);
|
||||
if (ret < 0)
|
||||
goto xfer_exit;
|
||||
|
||||
@ -1436,7 +1473,7 @@ static int pl330_submit_req(struct pl330_thread *thrd,
|
||||
/* Hook the request */
|
||||
thrd->lstenq = idx;
|
||||
thrd->req[idx].desc = desc;
|
||||
_setup_req(0, thrd, idx, &xs);
|
||||
_setup_req(pl330, 0, thrd, idx, &xs);
|
||||
|
||||
ret = 0;
|
||||
|
||||
@ -2781,6 +2818,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
struct resource *res;
|
||||
int i, ret, irq;
|
||||
int num_chan;
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
||||
pdat = dev_get_platdata(&adev->dev);
|
||||
|
||||
@ -2800,6 +2838,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
|
||||
pl330->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
|
||||
|
||||
/* get quirk */
|
||||
for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
|
||||
if (of_property_read_bool(np, of_quirks[i].quirk))
|
||||
pl330->quirks |= of_quirks[i].id;
|
||||
|
||||
res = &adev->res;
|
||||
pl330->base = devm_ioremap_resource(&adev->dev, res);
|
||||
if (IS_ERR(pl330->base))
|
||||
@ -2895,6 +2938,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
pd->dst_addr_widths = PL330_DMA_BUSWIDTHS;
|
||||
pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
|
||||
pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
|
||||
pd->max_burst = ((pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) ?
|
||||
1 : PL330_MAX_BURST);
|
||||
|
||||
ret = dma_async_device_register(pd);
|
||||
if (ret) {
|
||||
|
29
drivers/dma/qcom/Kconfig
Normal file
29
drivers/dma/qcom/Kconfig
Normal file
@ -0,0 +1,29 @@
|
||||
config QCOM_BAM_DMA
|
||||
tristate "QCOM BAM DMA support"
|
||||
depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
---help---
|
||||
Enable support for the QCOM BAM DMA controller. This controller
|
||||
provides DMA capabilities for a variety of on-chip devices.
|
||||
|
||||
config QCOM_HIDMA_MGMT
|
||||
tristate "Qualcomm Technologies HIDMA Management support"
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Enable support for the Qualcomm Technologies HIDMA Management.
|
||||
Each DMA device requires one management interface driver
|
||||
for basic initialization before QCOM_HIDMA channel driver can
|
||||
start managing the channels. In a virtualized environment,
|
||||
the guest OS would run QCOM_HIDMA channel driver and the
|
||||
host would run the QCOM_HIDMA_MGMT management driver.
|
||||
|
||||
config QCOM_HIDMA
|
||||
tristate "Qualcomm Technologies HIDMA Channel support"
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Enable support for the Qualcomm Technologies HIDMA controller.
|
||||
The HIDMA controller supports optimized buffer copies
|
||||
(user to kernel, kernel to kernel, etc.). It only supports
|
||||
memcpy interface. The core is not intended for general
|
||||
purpose slave DMA.
|
3
drivers/dma/qcom/Makefile
Normal file
3
drivers/dma/qcom/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o
|
||||
obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o
|
||||
hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o
|
@ -49,13 +49,13 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
#include "virt-dma.h"
|
||||
#include "../dmaengine.h"
|
||||
#include "../virt-dma.h"
|
||||
|
||||
struct bam_desc_hw {
|
||||
u32 addr; /* Buffer physical address */
|
||||
u16 size; /* Buffer size in bytes */
|
||||
u16 flags;
|
||||
__le32 addr; /* Buffer physical address */
|
||||
__le16 size; /* Buffer size in bytes */
|
||||
__le16 flags;
|
||||
};
|
||||
|
||||
#define DESC_FLAG_INT BIT(15)
|
||||
@ -632,14 +632,15 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
|
||||
unsigned int curr_offset = 0;
|
||||
|
||||
do {
|
||||
desc->addr = sg_dma_address(sg) + curr_offset;
|
||||
desc->addr = cpu_to_le32(sg_dma_address(sg) +
|
||||
curr_offset);
|
||||
|
||||
if (remainder > BAM_MAX_DATA_SIZE) {
|
||||
desc->size = BAM_MAX_DATA_SIZE;
|
||||
desc->size = cpu_to_le16(BAM_MAX_DATA_SIZE);
|
||||
remainder -= BAM_MAX_DATA_SIZE;
|
||||
curr_offset += BAM_MAX_DATA_SIZE;
|
||||
} else {
|
||||
desc->size = remainder;
|
||||
desc->size = cpu_to_le16(remainder);
|
||||
remainder = 0;
|
||||
}
|
||||
|
||||
@ -915,9 +916,11 @@ static void bam_start_dma(struct bam_chan *bchan)
|
||||
|
||||
/* set any special flags on the last descriptor */
|
||||
if (async_desc->num_desc == async_desc->xfer_len)
|
||||
desc[async_desc->xfer_len - 1].flags = async_desc->flags;
|
||||
desc[async_desc->xfer_len - 1].flags =
|
||||
cpu_to_le16(async_desc->flags);
|
||||
else
|
||||
desc[async_desc->xfer_len - 1].flags |= DESC_FLAG_INT;
|
||||
desc[async_desc->xfer_len - 1].flags |=
|
||||
cpu_to_le16(DESC_FLAG_INT);
|
||||
|
||||
if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) {
|
||||
u32 partial = MAX_DESCRIPTORS - bchan->tail;
|
706
drivers/dma/qcom/hidma.c
Normal file
706
drivers/dma/qcom/hidma.c
Normal file
@ -0,0 +1,706 @@
|
||||
/*
|
||||
* Qualcomm Technologies HIDMA DMA engine interface
|
||||
*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Freescale Semicondutor, Inc. 2007, 2008.
|
||||
* Copyright (C) Semihalf 2009
|
||||
* Copyright (C) Ilya Yanok, Emcraft Systems 2010
|
||||
* Copyright (C) Alexander Popov, Promcontroller 2014
|
||||
*
|
||||
* Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
|
||||
* (defines, structures and comments) was taken from MPC5121 DMA driver
|
||||
* written by Hongjun Chen <hong-jun.chen@freescale.com>.
|
||||
*
|
||||
* Approved as OSADL project by a majority of OSADL members and funded
|
||||
* by OSADL membership fees in 2009; for details see www.osadl.org.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called COPYING.
|
||||
*/
|
||||
|
||||
/* Linux Foundation elects GPLv2 license only. */
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "../dmaengine.h"
|
||||
#include "hidma.h"
|
||||
|
||||
/*
|
||||
* Default idle time is 2 seconds. This parameter can
|
||||
* be overridden by changing the following
|
||||
* /sys/bus/platform/devices/QCOM8061:<xy>/power/autosuspend_delay_ms
|
||||
* during kernel boot.
|
||||
*/
|
||||
#define HIDMA_AUTOSUSPEND_TIMEOUT 2000
|
||||
#define HIDMA_ERR_INFO_SW 0xFF
|
||||
#define HIDMA_ERR_CODE_UNEXPECTED_TERMINATE 0x0
|
||||
#define HIDMA_NR_DEFAULT_DESC 10
|
||||
|
||||
static inline struct hidma_dev *to_hidma_dev(struct dma_device *dmadev)
|
||||
{
|
||||
return container_of(dmadev, struct hidma_dev, ddev);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct hidma_dev *to_hidma_dev_from_lldev(struct hidma_lldev **_lldevp)
|
||||
{
|
||||
return container_of(_lldevp, struct hidma_dev, lldev);
|
||||
}
|
||||
|
||||
static inline struct hidma_chan *to_hidma_chan(struct dma_chan *dmach)
|
||||
{
|
||||
return container_of(dmach, struct hidma_chan, chan);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct hidma_desc *to_hidma_desc(struct dma_async_tx_descriptor *t)
|
||||
{
|
||||
return container_of(t, struct hidma_desc, desc);
|
||||
}
|
||||
|
||||
static void hidma_free(struct hidma_dev *dmadev)
|
||||
{
|
||||
INIT_LIST_HEAD(&dmadev->ddev.channels);
|
||||
}
|
||||
|
||||
static unsigned int nr_desc_prm;
|
||||
module_param(nr_desc_prm, uint, 0644);
|
||||
MODULE_PARM_DESC(nr_desc_prm, "number of descriptors (default: 0)");
|
||||
|
||||
|
||||
/* process completed descriptors */
|
||||
static void hidma_process_completed(struct hidma_chan *mchan)
|
||||
{
|
||||
struct dma_device *ddev = mchan->chan.device;
|
||||
struct hidma_dev *mdma = to_hidma_dev(ddev);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
dma_cookie_t last_cookie;
|
||||
struct hidma_desc *mdesc;
|
||||
unsigned long irqflags;
|
||||
struct list_head list;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
|
||||
/* Get all completed descriptors */
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
list_splice_tail_init(&mchan->completed, &list);
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
/* Execute callbacks and run dependencies */
|
||||
list_for_each_entry(mdesc, &list, node) {
|
||||
enum dma_status llstat;
|
||||
|
||||
desc = &mdesc->desc;
|
||||
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
dma_cookie_complete(desc);
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
llstat = hidma_ll_status(mdma->lldev, mdesc->tre_ch);
|
||||
if (desc->callback && (llstat == DMA_COMPLETE))
|
||||
desc->callback(desc->callback_param);
|
||||
|
||||
last_cookie = desc->cookie;
|
||||
dma_run_dependencies(desc);
|
||||
}
|
||||
|
||||
/* Free descriptors */
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
list_splice_tail_init(&list, &mchan->free);
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Called once for each submitted descriptor.
|
||||
* PM is locked once for each descriptor that is currently
|
||||
* in execution.
|
||||
*/
|
||||
static void hidma_callback(void *data)
|
||||
{
|
||||
struct hidma_desc *mdesc = data;
|
||||
struct hidma_chan *mchan = to_hidma_chan(mdesc->desc.chan);
|
||||
struct dma_device *ddev = mchan->chan.device;
|
||||
struct hidma_dev *dmadev = to_hidma_dev(ddev);
|
||||
unsigned long irqflags;
|
||||
bool queued = false;
|
||||
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
if (mdesc->node.next) {
|
||||
/* Delete from the active list, add to completed list */
|
||||
list_move_tail(&mdesc->node, &mchan->completed);
|
||||
queued = true;
|
||||
|
||||
/* calculate the next running descriptor */
|
||||
mchan->running = list_first_entry(&mchan->active,
|
||||
struct hidma_desc, node);
|
||||
}
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
hidma_process_completed(mchan);
|
||||
|
||||
if (queued) {
|
||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int hidma_chan_init(struct hidma_dev *dmadev, u32 dma_sig)
|
||||
{
|
||||
struct hidma_chan *mchan;
|
||||
struct dma_device *ddev;
|
||||
|
||||
mchan = devm_kzalloc(dmadev->ddev.dev, sizeof(*mchan), GFP_KERNEL);
|
||||
if (!mchan)
|
||||
return -ENOMEM;
|
||||
|
||||
ddev = &dmadev->ddev;
|
||||
mchan->dma_sig = dma_sig;
|
||||
mchan->dmadev = dmadev;
|
||||
mchan->chan.device = ddev;
|
||||
dma_cookie_init(&mchan->chan);
|
||||
|
||||
INIT_LIST_HEAD(&mchan->free);
|
||||
INIT_LIST_HEAD(&mchan->prepared);
|
||||
INIT_LIST_HEAD(&mchan->active);
|
||||
INIT_LIST_HEAD(&mchan->completed);
|
||||
|
||||
spin_lock_init(&mchan->lock);
|
||||
list_add_tail(&mchan->chan.device_node, &ddev->channels);
|
||||
dmadev->ddev.chancnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hidma_issue_task(unsigned long arg)
|
||||
{
|
||||
struct hidma_dev *dmadev = (struct hidma_dev *)arg;
|
||||
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
hidma_ll_start(dmadev->lldev);
|
||||
}
|
||||
|
||||
static void hidma_issue_pending(struct dma_chan *dmach)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(dmach);
|
||||
struct hidma_dev *dmadev = mchan->dmadev;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
spin_lock_irqsave(&mchan->lock, flags);
|
||||
if (!mchan->running) {
|
||||
struct hidma_desc *desc = list_first_entry(&mchan->active,
|
||||
struct hidma_desc,
|
||||
node);
|
||||
mchan->running = desc;
|
||||
}
|
||||
spin_unlock_irqrestore(&mchan->lock, flags);
|
||||
|
||||
/* PM will be released in hidma_callback function. */
|
||||
status = pm_runtime_get(dmadev->ddev.dev);
|
||||
if (status < 0)
|
||||
tasklet_schedule(&dmadev->task);
|
||||
else
|
||||
hidma_ll_start(dmadev->lldev);
|
||||
}
|
||||
|
||||
static enum dma_status hidma_tx_status(struct dma_chan *dmach,
|
||||
dma_cookie_t cookie,
|
||||
struct dma_tx_state *txstate)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(dmach);
|
||||
enum dma_status ret;
|
||||
|
||||
ret = dma_cookie_status(dmach, cookie, txstate);
|
||||
if (ret == DMA_COMPLETE)
|
||||
return ret;
|
||||
|
||||
if (mchan->paused && (ret == DMA_IN_PROGRESS)) {
|
||||
unsigned long flags;
|
||||
dma_cookie_t runcookie;
|
||||
|
||||
spin_lock_irqsave(&mchan->lock, flags);
|
||||
if (mchan->running)
|
||||
runcookie = mchan->running->desc.cookie;
|
||||
else
|
||||
runcookie = -EINVAL;
|
||||
|
||||
if (runcookie == cookie)
|
||||
ret = DMA_PAUSED;
|
||||
|
||||
spin_unlock_irqrestore(&mchan->lock, flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit descriptor to hardware.
|
||||
* Lock the PM for each descriptor we are sending.
|
||||
*/
|
||||
static dma_cookie_t hidma_tx_submit(struct dma_async_tx_descriptor *txd)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(txd->chan);
|
||||
struct hidma_dev *dmadev = mchan->dmadev;
|
||||
struct hidma_desc *mdesc;
|
||||
unsigned long irqflags;
|
||||
dma_cookie_t cookie;
|
||||
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
if (!hidma_ll_isenabled(dmadev->lldev)) {
|
||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mdesc = container_of(txd, struct hidma_desc, desc);
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
|
||||
/* Move descriptor to active */
|
||||
list_move_tail(&mdesc->node, &mchan->active);
|
||||
|
||||
/* Update cookie */
|
||||
cookie = dma_cookie_assign(txd);
|
||||
|
||||
hidma_ll_queue_request(dmadev->lldev, mdesc->tre_ch);
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
static int hidma_alloc_chan_resources(struct dma_chan *dmach)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(dmach);
|
||||
struct hidma_dev *dmadev = mchan->dmadev;
|
||||
struct hidma_desc *mdesc, *tmp;
|
||||
unsigned long irqflags;
|
||||
LIST_HEAD(descs);
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
|
||||
if (mchan->allocated)
|
||||
return 0;
|
||||
|
||||
/* Alloc descriptors for this channel */
|
||||
for (i = 0; i < dmadev->nr_descriptors; i++) {
|
||||
mdesc = kzalloc(sizeof(struct hidma_desc), GFP_NOWAIT);
|
||||
if (!mdesc) {
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
dma_async_tx_descriptor_init(&mdesc->desc, dmach);
|
||||
mdesc->desc.tx_submit = hidma_tx_submit;
|
||||
|
||||
rc = hidma_ll_request(dmadev->lldev, mchan->dma_sig,
|
||||
"DMA engine", hidma_callback, mdesc,
|
||||
&mdesc->tre_ch);
|
||||
if (rc) {
|
||||
dev_err(dmach->device->dev,
|
||||
"channel alloc failed at %u\n", i);
|
||||
kfree(mdesc);
|
||||
break;
|
||||
}
|
||||
list_add_tail(&mdesc->node, &descs);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
/* return the allocated descriptors */
|
||||
list_for_each_entry_safe(mdesc, tmp, &descs, node) {
|
||||
hidma_ll_free(dmadev->lldev, mdesc->tre_ch);
|
||||
kfree(mdesc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
list_splice_tail_init(&descs, &mchan->free);
|
||||
mchan->allocated = true;
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *
|
||||
hidma_prep_dma_memcpy(struct dma_chan *dmach, dma_addr_t dest, dma_addr_t src,
|
||||
size_t len, unsigned long flags)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(dmach);
|
||||
struct hidma_desc *mdesc = NULL;
|
||||
struct hidma_dev *mdma = mchan->dmadev;
|
||||
unsigned long irqflags;
|
||||
|
||||
/* Get free descriptor */
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
if (!list_empty(&mchan->free)) {
|
||||
mdesc = list_first_entry(&mchan->free, struct hidma_desc, node);
|
||||
list_del(&mdesc->node);
|
||||
}
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
if (!mdesc)
|
||||
return NULL;
|
||||
|
||||
hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch,
|
||||
src, dest, len, flags);
|
||||
|
||||
/* Place descriptor in prepared list */
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
list_add_tail(&mdesc->node, &mchan->prepared);
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
return &mdesc->desc;
|
||||
}
|
||||
|
||||
static int hidma_terminate_channel(struct dma_chan *chan)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(chan);
|
||||
struct hidma_dev *dmadev = to_hidma_dev(mchan->chan.device);
|
||||
struct hidma_desc *tmp, *mdesc;
|
||||
unsigned long irqflags;
|
||||
LIST_HEAD(list);
|
||||
int rc;
|
||||
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
/* give completed requests a chance to finish */
|
||||
hidma_process_completed(mchan);
|
||||
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
list_splice_init(&mchan->active, &list);
|
||||
list_splice_init(&mchan->prepared, &list);
|
||||
list_splice_init(&mchan->completed, &list);
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
|
||||
/* this suspends the existing transfer */
|
||||
rc = hidma_ll_pause(dmadev->lldev);
|
||||
if (rc) {
|
||||
dev_err(dmadev->ddev.dev, "channel did not pause\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* return all user requests */
|
||||
list_for_each_entry_safe(mdesc, tmp, &list, node) {
|
||||
struct dma_async_tx_descriptor *txd = &mdesc->desc;
|
||||
dma_async_tx_callback callback = mdesc->desc.callback;
|
||||
void *param = mdesc->desc.callback_param;
|
||||
|
||||
dma_descriptor_unmap(txd);
|
||||
|
||||
if (callback)
|
||||
callback(param);
|
||||
|
||||
dma_run_dependencies(txd);
|
||||
|
||||
/* move myself to free_list */
|
||||
list_move(&mdesc->node, &mchan->free);
|
||||
}
|
||||
|
||||
rc = hidma_ll_resume(dmadev->lldev);
|
||||
out:
|
||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hidma_terminate_all(struct dma_chan *chan)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(chan);
|
||||
struct hidma_dev *dmadev = to_hidma_dev(mchan->chan.device);
|
||||
int rc;
|
||||
|
||||
rc = hidma_terminate_channel(chan);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* reinitialize the hardware */
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
rc = hidma_ll_setup(dmadev->lldev);
|
||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void hidma_free_chan_resources(struct dma_chan *dmach)
|
||||
{
|
||||
struct hidma_chan *mchan = to_hidma_chan(dmach);
|
||||
struct hidma_dev *mdma = mchan->dmadev;
|
||||
struct hidma_desc *mdesc, *tmp;
|
||||
unsigned long irqflags;
|
||||
LIST_HEAD(descs);
|
||||
|
||||
/* terminate running transactions and free descriptors */
|
||||
hidma_terminate_channel(dmach);
|
||||
|
||||
spin_lock_irqsave(&mchan->lock, irqflags);
|
||||
|
||||
/* Move data */
|
||||
list_splice_tail_init(&mchan->free, &descs);
|
||||
|
||||
/* Free descriptors */
|
||||
list_for_each_entry_safe(mdesc, tmp, &descs, node) {
|
||||
hidma_ll_free(mdma->lldev, mdesc->tre_ch);
|
||||
list_del(&mdesc->node);
|
||||
kfree(mdesc);
|
||||
}
|
||||
|
||||
mchan->allocated = 0;
|
||||
spin_unlock_irqrestore(&mchan->lock, irqflags);
|
||||
}
|
||||
|
||||
static int hidma_pause(struct dma_chan *chan)
|
||||
{
|
||||
struct hidma_chan *mchan;
|
||||
struct hidma_dev *dmadev;
|
||||
|
||||
mchan = to_hidma_chan(chan);
|
||||
dmadev = to_hidma_dev(mchan->chan.device);
|
||||
if (!mchan->paused) {
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
if (hidma_ll_pause(dmadev->lldev))
|
||||
dev_warn(dmadev->ddev.dev, "channel did not stop\n");
|
||||
mchan->paused = true;
|
||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidma_resume(struct dma_chan *chan)
|
||||
{
|
||||
struct hidma_chan *mchan;
|
||||
struct hidma_dev *dmadev;
|
||||
int rc = 0;
|
||||
|
||||
mchan = to_hidma_chan(chan);
|
||||
dmadev = to_hidma_dev(mchan->chan.device);
|
||||
if (mchan->paused) {
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
rc = hidma_ll_resume(dmadev->lldev);
|
||||
if (!rc)
|
||||
mchan->paused = false;
|
||||
else
|
||||
dev_err(dmadev->ddev.dev,
|
||||
"failed to resume the channel");
|
||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static irqreturn_t hidma_chirq_handler(int chirq, void *arg)
|
||||
{
|
||||
struct hidma_lldev *lldev = arg;
|
||||
|
||||
/*
|
||||
* All interrupts are request driven.
|
||||
* HW doesn't send an interrupt by itself.
|
||||
*/
|
||||
return hidma_ll_inthandler(chirq, lldev);
|
||||
}
|
||||
|
||||
static int hidma_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hidma_dev *dmadev;
|
||||
struct resource *trca_resource;
|
||||
struct resource *evca_resource;
|
||||
int chirq;
|
||||
void __iomem *evca;
|
||||
void __iomem *trca;
|
||||
int rc;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
trca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
trca = devm_ioremap_resource(&pdev->dev, trca_resource);
|
||||
if (IS_ERR(trca)) {
|
||||
rc = -ENOMEM;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
evca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
evca = devm_ioremap_resource(&pdev->dev, evca_resource);
|
||||
if (IS_ERR(evca)) {
|
||||
rc = -ENOMEM;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/*
|
||||
* This driver only handles the channel IRQs.
|
||||
* Common IRQ is handled by the management driver.
|
||||
*/
|
||||
chirq = platform_get_irq(pdev, 0);
|
||||
if (chirq < 0) {
|
||||
rc = -ENODEV;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
|
||||
if (!dmadev) {
|
||||
rc = -ENOMEM;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&dmadev->ddev.channels);
|
||||
spin_lock_init(&dmadev->lock);
|
||||
dmadev->ddev.dev = &pdev->dev;
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
|
||||
dma_cap_set(DMA_MEMCPY, dmadev->ddev.cap_mask);
|
||||
if (WARN_ON(!pdev->dev.dma_mask)) {
|
||||
rc = -ENXIO;
|
||||
goto dmafree;
|
||||
}
|
||||
|
||||
dmadev->dev_evca = evca;
|
||||
dmadev->evca_resource = evca_resource;
|
||||
dmadev->dev_trca = trca;
|
||||
dmadev->trca_resource = trca_resource;
|
||||
dmadev->ddev.device_prep_dma_memcpy = hidma_prep_dma_memcpy;
|
||||
dmadev->ddev.device_alloc_chan_resources = hidma_alloc_chan_resources;
|
||||
dmadev->ddev.device_free_chan_resources = hidma_free_chan_resources;
|
||||
dmadev->ddev.device_tx_status = hidma_tx_status;
|
||||
dmadev->ddev.device_issue_pending = hidma_issue_pending;
|
||||
dmadev->ddev.device_pause = hidma_pause;
|
||||
dmadev->ddev.device_resume = hidma_resume;
|
||||
dmadev->ddev.device_terminate_all = hidma_terminate_all;
|
||||
dmadev->ddev.copy_align = 8;
|
||||
|
||||
device_property_read_u32(&pdev->dev, "desc-count",
|
||||
&dmadev->nr_descriptors);
|
||||
|
||||
if (!dmadev->nr_descriptors && nr_desc_prm)
|
||||
dmadev->nr_descriptors = nr_desc_prm;
|
||||
|
||||
if (!dmadev->nr_descriptors)
|
||||
dmadev->nr_descriptors = HIDMA_NR_DEFAULT_DESC;
|
||||
|
||||
dmadev->chidx = readl(dmadev->dev_trca + 0x28);
|
||||
|
||||
/* Set DMA mask to 64 bits. */
|
||||
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (rc) {
|
||||
dev_warn(&pdev->dev, "unable to set coherent mask to 64");
|
||||
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (rc)
|
||||
goto dmafree;
|
||||
}
|
||||
|
||||
dmadev->lldev = hidma_ll_init(dmadev->ddev.dev,
|
||||
dmadev->nr_descriptors, dmadev->dev_trca,
|
||||
dmadev->dev_evca, dmadev->chidx);
|
||||
if (!dmadev->lldev) {
|
||||
rc = -EPROBE_DEFER;
|
||||
goto dmafree;
|
||||
}
|
||||
|
||||
rc = devm_request_irq(&pdev->dev, chirq, hidma_chirq_handler, 0,
|
||||
"qcom-hidma", dmadev->lldev);
|
||||
if (rc)
|
||||
goto uninit;
|
||||
|
||||
INIT_LIST_HEAD(&dmadev->ddev.channels);
|
||||
rc = hidma_chan_init(dmadev, 0);
|
||||
if (rc)
|
||||
goto uninit;
|
||||
|
||||
rc = dma_async_device_register(&dmadev->ddev);
|
||||
if (rc)
|
||||
goto uninit;
|
||||
|
||||
dmadev->irq = chirq;
|
||||
tasklet_init(&dmadev->task, hidma_issue_task, (unsigned long)dmadev);
|
||||
dev_info(&pdev->dev, "HI-DMA engine driver registration complete\n");
|
||||
platform_set_drvdata(pdev, dmadev);
|
||||
pm_runtime_mark_last_busy(dmadev->ddev.dev);
|
||||
pm_runtime_put_autosuspend(dmadev->ddev.dev);
|
||||
return 0;
|
||||
|
||||
uninit:
|
||||
hidma_ll_uninit(dmadev->lldev);
|
||||
dmafree:
|
||||
if (dmadev)
|
||||
hidma_free(dmadev);
|
||||
bailout:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hidma_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hidma_dev *dmadev = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(dmadev->ddev.dev);
|
||||
dma_async_device_unregister(&dmadev->ddev);
|
||||
devm_free_irq(dmadev->ddev.dev, dmadev->irq, dmadev->lldev);
|
||||
hidma_ll_uninit(dmadev->lldev);
|
||||
hidma_free(dmadev);
|
||||
|
||||
dev_info(&pdev->dev, "HI-DMA engine removed\n");
|
||||
pm_runtime_put_sync_suspend(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
static const struct acpi_device_id hidma_acpi_ids[] = {
|
||||
{"QCOM8061"},
|
||||
{},
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct of_device_id hidma_match[] = {
|
||||
{.compatible = "qcom,hidma-1.0",},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, hidma_match);
|
||||
|
||||
static struct platform_driver hidma_driver = {
|
||||
.probe = hidma_probe,
|
||||
.remove = hidma_remove,
|
||||
.driver = {
|
||||
.name = "hidma",
|
||||
.of_match_table = hidma_match,
|
||||
.acpi_match_table = ACPI_PTR(hidma_acpi_ids),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hidma_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
160
drivers/dma/qcom/hidma.h
Normal file
160
drivers/dma/qcom/hidma.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Qualcomm Technologies HIDMA data structures
|
||||
*
|
||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef QCOM_HIDMA_H
|
||||
#define QCOM_HIDMA_H
|
||||
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
#define TRE_SIZE 32 /* each TRE is 32 bytes */
|
||||
#define TRE_CFG_IDX 0
|
||||
#define TRE_LEN_IDX 1
|
||||
#define TRE_SRC_LOW_IDX 2
|
||||
#define TRE_SRC_HI_IDX 3
|
||||
#define TRE_DEST_LOW_IDX 4
|
||||
#define TRE_DEST_HI_IDX 5
|
||||
|
||||
struct hidma_tx_status {
|
||||
u8 err_info; /* error record in this transfer */
|
||||
u8 err_code; /* completion code */
|
||||
};
|
||||
|
||||
struct hidma_tre {
|
||||
atomic_t allocated; /* if this channel is allocated */
|
||||
bool queued; /* flag whether this is pending */
|
||||
u16 status; /* status */
|
||||
u32 chidx; /* index of the tre */
|
||||
u32 dma_sig; /* signature of the tre */
|
||||
const char *dev_name; /* name of the device */
|
||||
void (*callback)(void *data); /* requester callback */
|
||||
void *data; /* Data associated with this channel*/
|
||||
struct hidma_lldev *lldev; /* lldma device pointer */
|
||||
u32 tre_local[TRE_SIZE / sizeof(u32) + 1]; /* TRE local copy */
|
||||
u32 tre_index; /* the offset where this was written*/
|
||||
u32 int_flags; /* interrupt flags */
|
||||
};
|
||||
|
||||
struct hidma_lldev {
|
||||
bool initialized; /* initialized flag */
|
||||
u8 trch_state; /* trch_state of the device */
|
||||
u8 evch_state; /* evch_state of the device */
|
||||
u8 chidx; /* channel index in the core */
|
||||
u32 nr_tres; /* max number of configs */
|
||||
spinlock_t lock; /* reentrancy */
|
||||
struct hidma_tre *trepool; /* trepool of user configs */
|
||||
struct device *dev; /* device */
|
||||
void __iomem *trca; /* Transfer Channel address */
|
||||
void __iomem *evca; /* Event Channel address */
|
||||
struct hidma_tre
|
||||
**pending_tre_list; /* Pointers to pending TREs */
|
||||
struct hidma_tx_status
|
||||
*tx_status_list; /* Pointers to pending TREs status*/
|
||||
s32 pending_tre_count; /* Number of TREs pending */
|
||||
|
||||
void *tre_ring; /* TRE ring */
|
||||
dma_addr_t tre_ring_handle; /* TRE ring to be shared with HW */
|
||||
u32 tre_ring_size; /* Byte size of the ring */
|
||||
u32 tre_processed_off; /* last processed TRE */
|
||||
|
||||
void *evre_ring; /* EVRE ring */
|
||||
dma_addr_t evre_ring_handle; /* EVRE ring to be shared with HW */
|
||||
u32 evre_ring_size; /* Byte size of the ring */
|
||||
u32 evre_processed_off; /* last processed EVRE */
|
||||
|
||||
u32 tre_write_offset; /* TRE write location */
|
||||
struct tasklet_struct task; /* task delivering notifications */
|
||||
DECLARE_KFIFO_PTR(handoff_fifo,
|
||||
struct hidma_tre *); /* pending TREs FIFO */
|
||||
};
|
||||
|
||||
struct hidma_desc {
|
||||
struct dma_async_tx_descriptor desc;
|
||||
/* link list node for this channel*/
|
||||
struct list_head node;
|
||||
u32 tre_ch;
|
||||
};
|
||||
|
||||
struct hidma_chan {
|
||||
bool paused;
|
||||
bool allocated;
|
||||
char dbg_name[16];
|
||||
u32 dma_sig;
|
||||
|
||||
/*
|
||||
* active descriptor on this channel
|
||||
* It is used by the DMA complete notification to
|
||||
* locate the descriptor that initiated the transfer.
|
||||
*/
|
||||
struct dentry *debugfs;
|
||||
struct dentry *stats;
|
||||
struct hidma_dev *dmadev;
|
||||
struct hidma_desc *running;
|
||||
|
||||
struct dma_chan chan;
|
||||
struct list_head free;
|
||||
struct list_head prepared;
|
||||
struct list_head active;
|
||||
struct list_head completed;
|
||||
|
||||
/* Lock for this structure */
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct hidma_dev {
|
||||
int irq;
|
||||
int chidx;
|
||||
u32 nr_descriptors;
|
||||
|
||||
struct hidma_lldev *lldev;
|
||||
void __iomem *dev_trca;
|
||||
struct resource *trca_resource;
|
||||
void __iomem *dev_evca;
|
||||
struct resource *evca_resource;
|
||||
|
||||
/* used to protect the pending channel list*/
|
||||
spinlock_t lock;
|
||||
struct dma_device ddev;
|
||||
|
||||
struct dentry *debugfs;
|
||||
struct dentry *stats;
|
||||
|
||||
/* Task delivering issue_pending */
|
||||
struct tasklet_struct task;
|
||||
};
|
||||
|
||||
int hidma_ll_request(struct hidma_lldev *llhndl, u32 dev_id,
|
||||
const char *dev_name,
|
||||
void (*callback)(void *data), void *data, u32 *tre_ch);
|
||||
|
||||
void hidma_ll_free(struct hidma_lldev *llhndl, u32 tre_ch);
|
||||
enum dma_status hidma_ll_status(struct hidma_lldev *llhndl, u32 tre_ch);
|
||||
bool hidma_ll_isenabled(struct hidma_lldev *llhndl);
|
||||
void hidma_ll_queue_request(struct hidma_lldev *llhndl, u32 tre_ch);
|
||||
void hidma_ll_start(struct hidma_lldev *llhndl);
|
||||
int hidma_ll_pause(struct hidma_lldev *llhndl);
|
||||
int hidma_ll_resume(struct hidma_lldev *llhndl);
|
||||
void hidma_ll_set_transfer_params(struct hidma_lldev *llhndl, u32 tre_ch,
|
||||
dma_addr_t src, dma_addr_t dest, u32 len, u32 flags);
|
||||
int hidma_ll_setup(struct hidma_lldev *lldev);
|
||||
struct hidma_lldev *hidma_ll_init(struct device *dev, u32 max_channels,
|
||||
void __iomem *trca, void __iomem *evca,
|
||||
u8 chidx);
|
||||
int hidma_ll_uninit(struct hidma_lldev *llhndl);
|
||||
irqreturn_t hidma_ll_inthandler(int irq, void *arg);
|
||||
void hidma_cleanup_pending_tre(struct hidma_lldev *llhndl, u8 err_info,
|
||||
u8 err_code);
|
||||
#endif
|
302
drivers/dma/qcom/hidma_mgmt.c
Normal file
302
drivers/dma/qcom/hidma_mgmt.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Qualcomm Technologies HIDMA DMA engine Management interface
|
||||
*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include "hidma_mgmt.h"
|
||||
|
||||
#define HIDMA_QOS_N_OFFSET 0x300
|
||||
#define HIDMA_CFG_OFFSET 0x400
|
||||
#define HIDMA_MAX_BUS_REQ_LEN_OFFSET 0x41C
|
||||
#define HIDMA_MAX_XACTIONS_OFFSET 0x420
|
||||
#define HIDMA_HW_VERSION_OFFSET 0x424
|
||||
#define HIDMA_CHRESET_TIMEOUT_OFFSET 0x418
|
||||
|
||||
#define HIDMA_MAX_WR_XACTIONS_MASK GENMASK(4, 0)
|
||||
#define HIDMA_MAX_RD_XACTIONS_MASK GENMASK(4, 0)
|
||||
#define HIDMA_WEIGHT_MASK GENMASK(6, 0)
|
||||
#define HIDMA_MAX_BUS_REQ_LEN_MASK GENMASK(15, 0)
|
||||
#define HIDMA_CHRESET_TIMEOUT_MASK GENMASK(19, 0)
|
||||
|
||||
#define HIDMA_MAX_WR_XACTIONS_BIT_POS 16
|
||||
#define HIDMA_MAX_BUS_WR_REQ_BIT_POS 16
|
||||
#define HIDMA_WRR_BIT_POS 8
|
||||
#define HIDMA_PRIORITY_BIT_POS 15
|
||||
|
||||
#define HIDMA_AUTOSUSPEND_TIMEOUT 2000
|
||||
#define HIDMA_MAX_CHANNEL_WEIGHT 15
|
||||
|
||||
int hidma_mgmt_setup(struct hidma_mgmt_dev *mgmtdev)
|
||||
{
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
if (!is_power_of_2(mgmtdev->max_write_request) ||
|
||||
(mgmtdev->max_write_request < 128) ||
|
||||
(mgmtdev->max_write_request > 1024)) {
|
||||
dev_err(&mgmtdev->pdev->dev, "invalid write request %d\n",
|
||||
mgmtdev->max_write_request);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(mgmtdev->max_read_request) ||
|
||||
(mgmtdev->max_read_request < 128) ||
|
||||
(mgmtdev->max_read_request > 1024)) {
|
||||
dev_err(&mgmtdev->pdev->dev, "invalid read request %d\n",
|
||||
mgmtdev->max_read_request);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mgmtdev->max_wr_xactions > HIDMA_MAX_WR_XACTIONS_MASK) {
|
||||
dev_err(&mgmtdev->pdev->dev,
|
||||
"max_wr_xactions cannot be bigger than %ld\n",
|
||||
HIDMA_MAX_WR_XACTIONS_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mgmtdev->max_rd_xactions > HIDMA_MAX_RD_XACTIONS_MASK) {
|
||||
dev_err(&mgmtdev->pdev->dev,
|
||||
"max_rd_xactions cannot be bigger than %ld\n",
|
||||
HIDMA_MAX_RD_XACTIONS_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < mgmtdev->dma_channels; i++) {
|
||||
if (mgmtdev->priority[i] > 1) {
|
||||
dev_err(&mgmtdev->pdev->dev,
|
||||
"priority can be 0 or 1\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mgmtdev->weight[i] > HIDMA_MAX_CHANNEL_WEIGHT) {
|
||||
dev_err(&mgmtdev->pdev->dev,
|
||||
"max value of weight can be %d.\n",
|
||||
HIDMA_MAX_CHANNEL_WEIGHT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* weight needs to be at least one */
|
||||
if (mgmtdev->weight[i] == 0)
|
||||
mgmtdev->weight[i] = 1;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&mgmtdev->pdev->dev);
|
||||
val = readl(mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET);
|
||||
val &= ~(HIDMA_MAX_BUS_REQ_LEN_MASK << HIDMA_MAX_BUS_WR_REQ_BIT_POS);
|
||||
val |= mgmtdev->max_write_request << HIDMA_MAX_BUS_WR_REQ_BIT_POS;
|
||||
val &= ~HIDMA_MAX_BUS_REQ_LEN_MASK;
|
||||
val |= mgmtdev->max_read_request;
|
||||
writel(val, mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET);
|
||||
|
||||
val = readl(mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET);
|
||||
val &= ~(HIDMA_MAX_WR_XACTIONS_MASK << HIDMA_MAX_WR_XACTIONS_BIT_POS);
|
||||
val |= mgmtdev->max_wr_xactions << HIDMA_MAX_WR_XACTIONS_BIT_POS;
|
||||
val &= ~HIDMA_MAX_RD_XACTIONS_MASK;
|
||||
val |= mgmtdev->max_rd_xactions;
|
||||
writel(val, mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET);
|
||||
|
||||
mgmtdev->hw_version =
|
||||
readl(mgmtdev->virtaddr + HIDMA_HW_VERSION_OFFSET);
|
||||
mgmtdev->hw_version_major = (mgmtdev->hw_version >> 28) & 0xF;
|
||||
mgmtdev->hw_version_minor = (mgmtdev->hw_version >> 16) & 0xF;
|
||||
|
||||
for (i = 0; i < mgmtdev->dma_channels; i++) {
|
||||
u32 weight = mgmtdev->weight[i];
|
||||
u32 priority = mgmtdev->priority[i];
|
||||
|
||||
val = readl(mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i));
|
||||
val &= ~(1 << HIDMA_PRIORITY_BIT_POS);
|
||||
val |= (priority & 0x1) << HIDMA_PRIORITY_BIT_POS;
|
||||
val &= ~(HIDMA_WEIGHT_MASK << HIDMA_WRR_BIT_POS);
|
||||
val |= (weight & HIDMA_WEIGHT_MASK) << HIDMA_WRR_BIT_POS;
|
||||
writel(val, mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i));
|
||||
}
|
||||
|
||||
val = readl(mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET);
|
||||
val &= ~HIDMA_CHRESET_TIMEOUT_MASK;
|
||||
val |= mgmtdev->chreset_timeout_cycles & HIDMA_CHRESET_TIMEOUT_MASK;
|
||||
writel(val, mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET);
|
||||
|
||||
pm_runtime_mark_last_busy(&mgmtdev->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&mgmtdev->pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidma_mgmt_setup);
|
||||
|
||||
static int hidma_mgmt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hidma_mgmt_dev *mgmtdev;
|
||||
struct resource *res;
|
||||
void __iomem *virtaddr;
|
||||
int irq;
|
||||
int rc;
|
||||
u32 val;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
virtaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(virtaddr)) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "irq resources not found\n");
|
||||
rc = irq;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mgmtdev = devm_kzalloc(&pdev->dev, sizeof(*mgmtdev), GFP_KERNEL);
|
||||
if (!mgmtdev) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mgmtdev->pdev = pdev;
|
||||
mgmtdev->addrsize = resource_size(res);
|
||||
mgmtdev->virtaddr = virtaddr;
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev, "dma-channels",
|
||||
&mgmtdev->dma_channels);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "number of channels missing\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev,
|
||||
"channel-reset-timeout-cycles",
|
||||
&mgmtdev->chreset_timeout_cycles);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "channel reset timeout missing\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev, "max-write-burst-bytes",
|
||||
&mgmtdev->max_write_request);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "max-write-burst-bytes missing\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev, "max-read-burst-bytes",
|
||||
&mgmtdev->max_read_request);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "max-read-burst-bytes missing\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev, "max-write-transactions",
|
||||
&mgmtdev->max_wr_xactions);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "max-write-transactions missing\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev, "max-read-transactions",
|
||||
&mgmtdev->max_rd_xactions);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "max-read-transactions missing\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
mgmtdev->priority = devm_kcalloc(&pdev->dev,
|
||||
mgmtdev->dma_channels,
|
||||
sizeof(*mgmtdev->priority),
|
||||
GFP_KERNEL);
|
||||
if (!mgmtdev->priority) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mgmtdev->weight = devm_kcalloc(&pdev->dev,
|
||||
mgmtdev->dma_channels,
|
||||
sizeof(*mgmtdev->weight), GFP_KERNEL);
|
||||
if (!mgmtdev->weight) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = hidma_mgmt_setup(mgmtdev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "setup failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* start the HW */
|
||||
val = readl(mgmtdev->virtaddr + HIDMA_CFG_OFFSET);
|
||||
val |= 1;
|
||||
writel(val, mgmtdev->virtaddr + HIDMA_CFG_OFFSET);
|
||||
|
||||
rc = hidma_mgmt_init_sys(mgmtdev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "sysfs setup failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"HW rev: %d.%d @ %pa with %d physical channels\n",
|
||||
mgmtdev->hw_version_major, mgmtdev->hw_version_minor,
|
||||
&res->start, mgmtdev->dma_channels);
|
||||
|
||||
platform_set_drvdata(pdev, mgmtdev);
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
return 0;
|
||||
out:
|
||||
pm_runtime_put_sync_suspend(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
static const struct acpi_device_id hidma_mgmt_acpi_ids[] = {
|
||||
{"QCOM8060"},
|
||||
{},
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct of_device_id hidma_mgmt_match[] = {
|
||||
{.compatible = "qcom,hidma-mgmt-1.0",},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hidma_mgmt_match);
|
||||
|
||||
static struct platform_driver hidma_mgmt_driver = {
|
||||
.probe = hidma_mgmt_probe,
|
||||
.driver = {
|
||||
.name = "hidma-mgmt",
|
||||
.of_match_table = hidma_mgmt_match,
|
||||
.acpi_match_table = ACPI_PTR(hidma_mgmt_acpi_ids),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hidma_mgmt_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
39
drivers/dma/qcom/hidma_mgmt.h
Normal file
39
drivers/dma/qcom/hidma_mgmt.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Qualcomm Technologies HIDMA Management common header
|
||||
*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
struct hidma_mgmt_dev {
|
||||
u8 hw_version_major;
|
||||
u8 hw_version_minor;
|
||||
|
||||
u32 max_wr_xactions;
|
||||
u32 max_rd_xactions;
|
||||
u32 max_write_request;
|
||||
u32 max_read_request;
|
||||
u32 dma_channels;
|
||||
u32 chreset_timeout_cycles;
|
||||
u32 hw_version;
|
||||
u32 *priority;
|
||||
u32 *weight;
|
||||
|
||||
/* Hardware device constants */
|
||||
void __iomem *virtaddr;
|
||||
resource_size_t addrsize;
|
||||
|
||||
struct kobject **chroots;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
|
||||
int hidma_mgmt_init_sys(struct hidma_mgmt_dev *dev);
|
||||
int hidma_mgmt_setup(struct hidma_mgmt_dev *mgmtdev);
|
295
drivers/dma/qcom/hidma_mgmt_sys.c
Normal file
295
drivers/dma/qcom/hidma_mgmt_sys.c
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Qualcomm Technologies HIDMA Management SYS interface
|
||||
*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "hidma_mgmt.h"
|
||||
|
||||
struct hidma_chan_attr {
|
||||
struct hidma_mgmt_dev *mdev;
|
||||
int index;
|
||||
struct kobj_attribute attr;
|
||||
};
|
||||
|
||||
struct hidma_mgmt_fileinfo {
|
||||
char *name;
|
||||
int mode;
|
||||
int (*get)(struct hidma_mgmt_dev *mdev);
|
||||
int (*set)(struct hidma_mgmt_dev *mdev, u64 val);
|
||||
};
|
||||
|
||||
#define IMPLEMENT_GETSET(name) \
|
||||
static int get_##name(struct hidma_mgmt_dev *mdev) \
|
||||
{ \
|
||||
return mdev->name; \
|
||||
} \
|
||||
static int set_##name(struct hidma_mgmt_dev *mdev, u64 val) \
|
||||
{ \
|
||||
u64 tmp; \
|
||||
int rc; \
|
||||
\
|
||||
tmp = mdev->name; \
|
||||
mdev->name = val; \
|
||||
rc = hidma_mgmt_setup(mdev); \
|
||||
if (rc) \
|
||||
mdev->name = tmp; \
|
||||
return rc; \
|
||||
}
|
||||
|
||||
#define DECLARE_ATTRIBUTE(name, mode) \
|
||||
{#name, mode, get_##name, set_##name}
|
||||
|
||||
IMPLEMENT_GETSET(hw_version_major)
|
||||
IMPLEMENT_GETSET(hw_version_minor)
|
||||
IMPLEMENT_GETSET(max_wr_xactions)
|
||||
IMPLEMENT_GETSET(max_rd_xactions)
|
||||
IMPLEMENT_GETSET(max_write_request)
|
||||
IMPLEMENT_GETSET(max_read_request)
|
||||
IMPLEMENT_GETSET(dma_channels)
|
||||
IMPLEMENT_GETSET(chreset_timeout_cycles)
|
||||
|
||||
static int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
|
||||
{
|
||||
u64 tmp;
|
||||
int rc;
|
||||
|
||||
if (i >= mdev->dma_channels)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = mdev->priority[i];
|
||||
mdev->priority[i] = val;
|
||||
rc = hidma_mgmt_setup(mdev);
|
||||
if (rc)
|
||||
mdev->priority[i] = tmp;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
|
||||
{
|
||||
u64 tmp;
|
||||
int rc;
|
||||
|
||||
if (i >= mdev->dma_channels)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = mdev->weight[i];
|
||||
mdev->weight[i] = val;
|
||||
rc = hidma_mgmt_setup(mdev);
|
||||
if (rc)
|
||||
mdev->weight[i] = tmp;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {
|
||||
DECLARE_ATTRIBUTE(hw_version_major, S_IRUGO),
|
||||
DECLARE_ATTRIBUTE(hw_version_minor, S_IRUGO),
|
||||
DECLARE_ATTRIBUTE(dma_channels, S_IRUGO),
|
||||
DECLARE_ATTRIBUTE(chreset_timeout_cycles, S_IRUGO),
|
||||
DECLARE_ATTRIBUTE(max_wr_xactions, S_IRUGO),
|
||||
DECLARE_ATTRIBUTE(max_rd_xactions, S_IRUGO),
|
||||
DECLARE_ATTRIBUTE(max_write_request, S_IRUGO),
|
||||
DECLARE_ATTRIBUTE(max_read_request, S_IRUGO),
|
||||
};
|
||||
|
||||
static ssize_t show_values(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct hidma_mgmt_dev *mdev = platform_get_drvdata(pdev);
|
||||
unsigned int i;
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
|
||||
if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
|
||||
sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static ssize_t set_values(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct hidma_mgmt_dev *mdev = platform_get_drvdata(pdev);
|
||||
unsigned long tmp;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
rc = kstrtoul(buf, 0, &tmp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
|
||||
if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
|
||||
rc = hidma_mgmt_files[i].set(mdev, tmp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_values_channel(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct hidma_chan_attr *chattr;
|
||||
struct hidma_mgmt_dev *mdev;
|
||||
|
||||
buf[0] = 0;
|
||||
chattr = container_of(attr, struct hidma_chan_attr, attr);
|
||||
mdev = chattr->mdev;
|
||||
if (strcmp(attr->attr.name, "priority") == 0)
|
||||
sprintf(buf, "%d\n", mdev->priority[chattr->index]);
|
||||
else if (strcmp(attr->attr.name, "weight") == 0)
|
||||
sprintf(buf, "%d\n", mdev->weight[chattr->index]);
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static ssize_t set_values_channel(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct hidma_chan_attr *chattr;
|
||||
struct hidma_mgmt_dev *mdev;
|
||||
unsigned long tmp;
|
||||
int rc;
|
||||
|
||||
chattr = container_of(attr, struct hidma_chan_attr, attr);
|
||||
mdev = chattr->mdev;
|
||||
|
||||
rc = kstrtoul(buf, 0, &tmp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (strcmp(attr->attr.name, "priority") == 0) {
|
||||
rc = set_priority(mdev, chattr->index, tmp);
|
||||
if (rc)
|
||||
return rc;
|
||||
} else if (strcmp(attr->attr.name, "weight") == 0) {
|
||||
rc = set_weight(mdev, chattr->index, tmp);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
|
||||
{
|
||||
struct device_attribute *attrs;
|
||||
char *name_copy;
|
||||
|
||||
attrs = devm_kmalloc(&dev->pdev->dev,
|
||||
sizeof(struct device_attribute), GFP_KERNEL);
|
||||
if (!attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);
|
||||
if (!name_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
attrs->attr.name = name_copy;
|
||||
attrs->attr.mode = mode;
|
||||
attrs->show = show_values;
|
||||
attrs->store = set_values;
|
||||
sysfs_attr_init(&attrs->attr);
|
||||
|
||||
return device_create_file(&dev->pdev->dev, attrs);
|
||||
}
|
||||
|
||||
static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
|
||||
int mode, int index,
|
||||
struct kobject *parent)
|
||||
{
|
||||
struct hidma_chan_attr *chattr;
|
||||
char *name_copy;
|
||||
|
||||
chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
|
||||
if (!chattr)
|
||||
return -ENOMEM;
|
||||
|
||||
name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);
|
||||
if (!name_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
chattr->mdev = mdev;
|
||||
chattr->index = index;
|
||||
chattr->attr.attr.name = name_copy;
|
||||
chattr->attr.attr.mode = mode;
|
||||
chattr->attr.show = show_values_channel;
|
||||
chattr->attr.store = set_values_channel;
|
||||
sysfs_attr_init(&chattr->attr.attr);
|
||||
|
||||
return sysfs_create_file(parent, &chattr->attr.attr);
|
||||
}
|
||||
|
||||
int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
int required;
|
||||
struct kobject *chanops;
|
||||
|
||||
required = sizeof(*mdev->chroots) * mdev->dma_channels;
|
||||
mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
|
||||
if (!mdev->chroots)
|
||||
return -ENOMEM;
|
||||
|
||||
chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
|
||||
if (!chanops)
|
||||
return -ENOMEM;
|
||||
|
||||
/* create each channel directory here */
|
||||
for (i = 0; i < mdev->dma_channels; i++) {
|
||||
char name[20];
|
||||
|
||||
snprintf(name, sizeof(name), "chan%d", i);
|
||||
mdev->chroots[i] = kobject_create_and_add(name, chanops);
|
||||
if (!mdev->chroots[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* populate common parameters */
|
||||
for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
|
||||
rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
|
||||
hidma_mgmt_files[i].mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* populate parameters that are per channel */
|
||||
for (i = 0; i < mdev->dma_channels; i++) {
|
||||
rc = create_sysfs_entry_channel(mdev, "priority",
|
||||
(S_IRUGO | S_IWUGO), i,
|
||||
mdev->chroots[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = create_sysfs_entry_channel(mdev, "weight",
|
||||
(S_IRUGO | S_IWUGO), i,
|
||||
mdev->chroots[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);
|
@ -12,7 +12,7 @@ config RENESAS_DMA
|
||||
|
||||
config SH_DMAE_BASE
|
||||
bool "Renesas SuperH DMA Engine support"
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
||||
depends on !SUPERH || SH_DMA
|
||||
depends on !SH_DMA_API
|
||||
default y
|
||||
@ -41,7 +41,7 @@ endif
|
||||
|
||||
config RCAR_DMAC
|
||||
tristate "Renesas R-Car Gen2 DMA Controller"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select RENESAS_DMA
|
||||
help
|
||||
This driver supports the general purpose DMA controller found in the
|
||||
@ -49,7 +49,7 @@ config RCAR_DMAC
|
||||
|
||||
config RENESAS_USB_DMAC
|
||||
tristate "Renesas USB-DMA Controller"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select RENESAS_DMA
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
help
|
||||
|
@ -413,7 +413,7 @@ static int rcar_dmac_init(struct rcar_dmac *dmac)
|
||||
u16 dmaor;
|
||||
|
||||
/* Clear all channels and enable the DMAC globally. */
|
||||
rcar_dmac_write(dmac, RCAR_DMACHCLR, 0x7fff);
|
||||
rcar_dmac_write(dmac, RCAR_DMACHCLR, GENMASK(dmac->n_channels - 1, 0));
|
||||
rcar_dmac_write(dmac, RCAR_DMAOR,
|
||||
RCAR_DMAOR_PRI_FIXED | RCAR_DMAOR_DME);
|
||||
|
||||
|
@ -699,7 +699,7 @@ static int sh_dmae_probe(struct platform_device *pdev)
|
||||
struct resource *chan, *dmars, *errirq_res, *chanirq_res;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
pdata = of_match_device(sh_dmae_of_match, &pdev->dev)->data;
|
||||
pdata = of_device_get_match_data(&pdev->dev);
|
||||
else
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
|
@ -989,7 +989,7 @@ static int sirfsoc_dma_remove(struct platform_device *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sirfsoc_dma_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused sirfsoc_dma_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
|
||||
|
||||
@ -997,7 +997,7 @@ static int sirfsoc_dma_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sirfsoc_dma_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused sirfsoc_dma_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -1010,8 +1010,7 @@ static int sirfsoc_dma_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sirfsoc_dma_pm_suspend(struct device *dev)
|
||||
static int __maybe_unused sirfsoc_dma_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
|
||||
struct sirfsoc_dma_regs *save = &sdma->regs_save;
|
||||
@ -1062,7 +1061,7 @@ static int sirfsoc_dma_pm_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sirfsoc_dma_pm_resume(struct device *dev)
|
||||
static int __maybe_unused sirfsoc_dma_pm_resume(struct device *dev)
|
||||
{
|
||||
struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
|
||||
struct sirfsoc_dma_regs *save = &sdma->regs_save;
|
||||
@ -1121,7 +1120,6 @@ static int sirfsoc_dma_pm_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sirfsoc_dma_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(sirfsoc_dma_runtime_suspend, sirfsoc_dma_runtime_resume, NULL)
|
||||
|
@ -1271,6 +1271,7 @@ static const struct of_device_id sun4i_dma_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-dma" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_dma_match);
|
||||
|
||||
static struct platform_driver sun4i_dma_driver = {
|
||||
.probe = sun4i_dma_probe,
|
||||
|
@ -1292,40 +1292,19 @@ static const struct tegra_dma_chip_data tegra148_dma_chip_data = {
|
||||
.support_separate_wcount_reg = true,
|
||||
};
|
||||
|
||||
|
||||
static const struct of_device_id tegra_dma_of_match[] = {
|
||||
{
|
||||
.compatible = "nvidia,tegra148-apbdma",
|
||||
.data = &tegra148_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra114-apbdma",
|
||||
.data = &tegra114_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra30-apbdma",
|
||||
.data = &tegra30_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra20-apbdma",
|
||||
.data = &tegra20_dma_chip_data,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_dma_of_match);
|
||||
|
||||
static int tegra_dma_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct tegra_dma *tdma;
|
||||
int ret;
|
||||
int i;
|
||||
const struct tegra_dma_chip_data *cdata = NULL;
|
||||
const struct of_device_id *match;
|
||||
const struct tegra_dma_chip_data *cdata;
|
||||
|
||||
match = of_match_device(tegra_dma_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "Error: No device match found\n");
|
||||
cdata = of_device_get_match_data(&pdev->dev);
|
||||
if (!cdata) {
|
||||
dev_err(&pdev->dev, "Error: No device match data found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
cdata = match->data;
|
||||
|
||||
tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels *
|
||||
sizeof(struct tegra_dma_channel), GFP_KERNEL);
|
||||
@ -1612,6 +1591,24 @@ static const struct dev_pm_ops tegra_dma_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_dma_pm_suspend, tegra_dma_pm_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_dma_of_match[] = {
|
||||
{
|
||||
.compatible = "nvidia,tegra148-apbdma",
|
||||
.data = &tegra148_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra114-apbdma",
|
||||
.data = &tegra114_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra30-apbdma",
|
||||
.data = &tegra30_dma_chip_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra20-apbdma",
|
||||
.data = &tegra20_dma_chip_data,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_dma_of_match);
|
||||
|
||||
static struct platform_driver tegra_dmac_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-apbdma",
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_dma.h>
|
||||
@ -190,8 +191,7 @@ struct xilinx_vdma_tx_descriptor {
|
||||
* @desc_offset: TX descriptor registers offset
|
||||
* @lock: Descriptor operation lock
|
||||
* @pending_list: Descriptors waiting
|
||||
* @active_desc: Active descriptor
|
||||
* @allocated_desc: Allocated descriptor
|
||||
* @active_list: Descriptors ready to submit
|
||||
* @done_list: Complete descriptors
|
||||
* @common: DMA common channel
|
||||
* @desc_pool: Descriptors pool
|
||||
@ -206,6 +206,7 @@ struct xilinx_vdma_tx_descriptor {
|
||||
* @tasklet: Cleanup work after irq
|
||||
* @config: Device configuration info
|
||||
* @flush_on_fsync: Flush on Frame sync
|
||||
* @desc_pendingcount: Descriptor pending count
|
||||
*/
|
||||
struct xilinx_vdma_chan {
|
||||
struct xilinx_vdma_device *xdev;
|
||||
@ -213,8 +214,7 @@ struct xilinx_vdma_chan {
|
||||
u32 desc_offset;
|
||||
spinlock_t lock;
|
||||
struct list_head pending_list;
|
||||
struct xilinx_vdma_tx_descriptor *active_desc;
|
||||
struct xilinx_vdma_tx_descriptor *allocated_desc;
|
||||
struct list_head active_list;
|
||||
struct list_head done_list;
|
||||
struct dma_chan common;
|
||||
struct dma_pool *desc_pool;
|
||||
@ -229,6 +229,7 @@ struct xilinx_vdma_chan {
|
||||
struct tasklet_struct tasklet;
|
||||
struct xilinx_vdma_config config;
|
||||
bool flush_on_fsync;
|
||||
u32 desc_pendingcount;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -254,6 +255,9 @@ struct xilinx_vdma_device {
|
||||
container_of(chan, struct xilinx_vdma_chan, common)
|
||||
#define to_vdma_tx_descriptor(tx) \
|
||||
container_of(tx, struct xilinx_vdma_tx_descriptor, async_tx)
|
||||
#define xilinx_vdma_poll_timeout(chan, reg, val, cond, delay_us, timeout_us) \
|
||||
readl_poll_timeout(chan->xdev->regs + chan->ctrl_offset + reg, val, \
|
||||
cond, delay_us, timeout_us)
|
||||
|
||||
/* IO accessors */
|
||||
static inline u32 vdma_read(struct xilinx_vdma_chan *chan, u32 reg)
|
||||
@ -342,19 +346,11 @@ static struct xilinx_vdma_tx_descriptor *
|
||||
xilinx_vdma_alloc_tx_descriptor(struct xilinx_vdma_chan *chan)
|
||||
{
|
||||
struct xilinx_vdma_tx_descriptor *desc;
|
||||
unsigned long flags;
|
||||
|
||||
if (chan->allocated_desc)
|
||||
return chan->allocated_desc;
|
||||
|
||||
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
chan->allocated_desc = desc;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
INIT_LIST_HEAD(&desc->segments);
|
||||
|
||||
return desc;
|
||||
@ -412,9 +408,7 @@ static void xilinx_vdma_free_descriptors(struct xilinx_vdma_chan *chan)
|
||||
|
||||
xilinx_vdma_free_desc_list(chan, &chan->pending_list);
|
||||
xilinx_vdma_free_desc_list(chan, &chan->done_list);
|
||||
|
||||
xilinx_vdma_free_tx_descriptor(chan, chan->active_desc);
|
||||
chan->active_desc = NULL;
|
||||
xilinx_vdma_free_desc_list(chan, &chan->active_list);
|
||||
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
}
|
||||
@ -560,18 +554,17 @@ static bool xilinx_vdma_is_idle(struct xilinx_vdma_chan *chan)
|
||||
*/
|
||||
static void xilinx_vdma_halt(struct xilinx_vdma_chan *chan)
|
||||
{
|
||||
int loop = XILINX_VDMA_LOOP_COUNT;
|
||||
int err;
|
||||
u32 val;
|
||||
|
||||
vdma_ctrl_clr(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP);
|
||||
|
||||
/* Wait for the hardware to halt */
|
||||
do {
|
||||
if (vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) &
|
||||
XILINX_VDMA_DMASR_HALTED)
|
||||
break;
|
||||
} while (loop--);
|
||||
err = xilinx_vdma_poll_timeout(chan, XILINX_VDMA_REG_DMASR, val,
|
||||
(val & XILINX_VDMA_DMASR_HALTED), 0,
|
||||
XILINX_VDMA_LOOP_COUNT);
|
||||
|
||||
if (!loop) {
|
||||
if (err) {
|
||||
dev_err(chan->dev, "Cannot stop channel %p: %x\n",
|
||||
chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
|
||||
chan->err = true;
|
||||
@ -586,18 +579,17 @@ static void xilinx_vdma_halt(struct xilinx_vdma_chan *chan)
|
||||
*/
|
||||
static void xilinx_vdma_start(struct xilinx_vdma_chan *chan)
|
||||
{
|
||||
int loop = XILINX_VDMA_LOOP_COUNT;
|
||||
int err;
|
||||
u32 val;
|
||||
|
||||
vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP);
|
||||
|
||||
/* Wait for the hardware to start */
|
||||
do {
|
||||
if (!(vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) &
|
||||
XILINX_VDMA_DMASR_HALTED))
|
||||
break;
|
||||
} while (loop--);
|
||||
err = xilinx_vdma_poll_timeout(chan, XILINX_VDMA_REG_DMASR, val,
|
||||
!(val & XILINX_VDMA_DMASR_HALTED), 0,
|
||||
XILINX_VDMA_LOOP_COUNT);
|
||||
|
||||
if (!loop) {
|
||||
if (err) {
|
||||
dev_err(chan->dev, "Cannot start channel %p: %x\n",
|
||||
chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
|
||||
|
||||
@ -614,45 +606,39 @@ static void xilinx_vdma_start(struct xilinx_vdma_chan *chan)
|
||||
static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan)
|
||||
{
|
||||
struct xilinx_vdma_config *config = &chan->config;
|
||||
struct xilinx_vdma_tx_descriptor *desc;
|
||||
unsigned long flags;
|
||||
struct xilinx_vdma_tx_descriptor *desc, *tail_desc;
|
||||
u32 reg;
|
||||
struct xilinx_vdma_tx_segment *head, *tail = NULL;
|
||||
struct xilinx_vdma_tx_segment *tail_segment;
|
||||
|
||||
/* This function was invoked with lock held */
|
||||
if (chan->err)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
/* There's already an active descriptor, bail out. */
|
||||
if (chan->active_desc)
|
||||
goto out_unlock;
|
||||
|
||||
if (list_empty(&chan->pending_list))
|
||||
goto out_unlock;
|
||||
return;
|
||||
|
||||
desc = list_first_entry(&chan->pending_list,
|
||||
struct xilinx_vdma_tx_descriptor, node);
|
||||
tail_desc = list_last_entry(&chan->pending_list,
|
||||
struct xilinx_vdma_tx_descriptor, node);
|
||||
|
||||
tail_segment = list_last_entry(&tail_desc->segments,
|
||||
struct xilinx_vdma_tx_segment, node);
|
||||
|
||||
/* If it is SG mode and hardware is busy, cannot submit */
|
||||
if (chan->has_sg && xilinx_vdma_is_running(chan) &&
|
||||
!xilinx_vdma_is_idle(chan)) {
|
||||
dev_dbg(chan->dev, "DMA controller still busy\n");
|
||||
goto out_unlock;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If hardware is idle, then all descriptors on the running lists are
|
||||
* done, start new transfers
|
||||
*/
|
||||
if (chan->has_sg) {
|
||||
head = list_first_entry(&desc->segments,
|
||||
struct xilinx_vdma_tx_segment, node);
|
||||
tail = list_entry(desc->segments.prev,
|
||||
struct xilinx_vdma_tx_segment, node);
|
||||
|
||||
vdma_ctrl_write(chan, XILINX_VDMA_REG_CURDESC, head->phys);
|
||||
}
|
||||
if (chan->has_sg)
|
||||
vdma_ctrl_write(chan, XILINX_VDMA_REG_CURDESC,
|
||||
desc->async_tx.phys);
|
||||
|
||||
/* Configure the hardware using info in the config structure */
|
||||
reg = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR);
|
||||
@ -662,6 +648,10 @@ static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan)
|
||||
else
|
||||
reg &= ~XILINX_VDMA_DMACR_FRAMECNT_EN;
|
||||
|
||||
/* Configure channel to allow number frame buffers */
|
||||
vdma_ctrl_write(chan, XILINX_VDMA_REG_FRMSTORE,
|
||||
chan->desc_pendingcount);
|
||||
|
||||
/*
|
||||
* With SG, start with circular mode, so that BDs can be fetched.
|
||||
* In direct register mode, if not parking, enable circular mode
|
||||
@ -690,16 +680,19 @@ static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan)
|
||||
xilinx_vdma_start(chan);
|
||||
|
||||
if (chan->err)
|
||||
goto out_unlock;
|
||||
return;
|
||||
|
||||
/* Start the transfer */
|
||||
if (chan->has_sg) {
|
||||
vdma_ctrl_write(chan, XILINX_VDMA_REG_TAILDESC, tail->phys);
|
||||
vdma_ctrl_write(chan, XILINX_VDMA_REG_TAILDESC,
|
||||
tail_segment->phys);
|
||||
} else {
|
||||
struct xilinx_vdma_tx_segment *segment, *last = NULL;
|
||||
int i = 0;
|
||||
|
||||
list_for_each_entry(segment, &desc->segments, node) {
|
||||
list_for_each_entry(desc, &chan->pending_list, node) {
|
||||
segment = list_first_entry(&desc->segments,
|
||||
struct xilinx_vdma_tx_segment, node);
|
||||
vdma_desc_write(chan,
|
||||
XILINX_VDMA_REG_START_ADDRESS(i++),
|
||||
segment->hw.buf_addr);
|
||||
@ -707,7 +700,7 @@ static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan)
|
||||
}
|
||||
|
||||
if (!last)
|
||||
goto out_unlock;
|
||||
return;
|
||||
|
||||
/* HW expects these parameters to be same for one transaction */
|
||||
vdma_desc_write(chan, XILINX_VDMA_REG_HSIZE, last->hw.hsize);
|
||||
@ -716,11 +709,8 @@ static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan)
|
||||
vdma_desc_write(chan, XILINX_VDMA_REG_VSIZE, last->hw.vsize);
|
||||
}
|
||||
|
||||
list_del(&desc->node);
|
||||
chan->active_desc = desc;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
list_splice_tail_init(&chan->pending_list, &chan->active_list);
|
||||
chan->desc_pendingcount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -730,8 +720,11 @@ out_unlock:
|
||||
static void xilinx_vdma_issue_pending(struct dma_chan *dchan)
|
||||
{
|
||||
struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
xilinx_vdma_start_transfer(chan);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -742,24 +735,17 @@ static void xilinx_vdma_issue_pending(struct dma_chan *dchan)
|
||||
*/
|
||||
static void xilinx_vdma_complete_descriptor(struct xilinx_vdma_chan *chan)
|
||||
{
|
||||
struct xilinx_vdma_tx_descriptor *desc;
|
||||
unsigned long flags;
|
||||
struct xilinx_vdma_tx_descriptor *desc, *next;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
/* This function was invoked with lock held */
|
||||
if (list_empty(&chan->active_list))
|
||||
return;
|
||||
|
||||
desc = chan->active_desc;
|
||||
if (!desc) {
|
||||
dev_dbg(chan->dev, "no running descriptors\n");
|
||||
goto out_unlock;
|
||||
list_for_each_entry_safe(desc, next, &chan->active_list, node) {
|
||||
list_del(&desc->node);
|
||||
dma_cookie_complete(&desc->async_tx);
|
||||
list_add_tail(&desc->node, &chan->done_list);
|
||||
}
|
||||
|
||||
dma_cookie_complete(&desc->async_tx);
|
||||
list_add_tail(&desc->node, &chan->done_list);
|
||||
|
||||
chan->active_desc = NULL;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -770,21 +756,17 @@ out_unlock:
|
||||
*/
|
||||
static int xilinx_vdma_reset(struct xilinx_vdma_chan *chan)
|
||||
{
|
||||
int loop = XILINX_VDMA_LOOP_COUNT;
|
||||
int err;
|
||||
u32 tmp;
|
||||
|
||||
vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RESET);
|
||||
|
||||
tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) &
|
||||
XILINX_VDMA_DMACR_RESET;
|
||||
|
||||
/* Wait for the hardware to finish reset */
|
||||
do {
|
||||
tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) &
|
||||
XILINX_VDMA_DMACR_RESET;
|
||||
} while (loop-- && tmp);
|
||||
err = xilinx_vdma_poll_timeout(chan, XILINX_VDMA_REG_DMACR, tmp,
|
||||
!(tmp & XILINX_VDMA_DMACR_RESET), 0,
|
||||
XILINX_VDMA_LOOP_COUNT);
|
||||
|
||||
if (!loop) {
|
||||
if (err) {
|
||||
dev_err(chan->dev, "reset timeout, cr %x, sr %x\n",
|
||||
vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR),
|
||||
vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
|
||||
@ -793,7 +775,7 @@ static int xilinx_vdma_reset(struct xilinx_vdma_chan *chan)
|
||||
|
||||
chan->err = false;
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -870,14 +852,54 @@ static irqreturn_t xilinx_vdma_irq_handler(int irq, void *data)
|
||||
}
|
||||
|
||||
if (status & XILINX_VDMA_DMASR_FRM_CNT_IRQ) {
|
||||
spin_lock(&chan->lock);
|
||||
xilinx_vdma_complete_descriptor(chan);
|
||||
xilinx_vdma_start_transfer(chan);
|
||||
spin_unlock(&chan->lock);
|
||||
}
|
||||
|
||||
tasklet_schedule(&chan->tasklet);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* append_desc_queue - Queuing descriptor
|
||||
* @chan: Driver specific dma channel
|
||||
* @desc: dma transaction descriptor
|
||||
*/
|
||||
static void append_desc_queue(struct xilinx_vdma_chan *chan,
|
||||
struct xilinx_vdma_tx_descriptor *desc)
|
||||
{
|
||||
struct xilinx_vdma_tx_segment *tail_segment;
|
||||
struct xilinx_vdma_tx_descriptor *tail_desc;
|
||||
|
||||
if (list_empty(&chan->pending_list))
|
||||
goto append;
|
||||
|
||||
/*
|
||||
* Add the hardware descriptor to the chain of hardware descriptors
|
||||
* that already exists in memory.
|
||||
*/
|
||||
tail_desc = list_last_entry(&chan->pending_list,
|
||||
struct xilinx_vdma_tx_descriptor, node);
|
||||
tail_segment = list_last_entry(&tail_desc->segments,
|
||||
struct xilinx_vdma_tx_segment, node);
|
||||
tail_segment->hw.next_desc = (u32)desc->async_tx.phys;
|
||||
|
||||
/*
|
||||
* Add the software descriptor and all children to the list
|
||||
* of pending transactions
|
||||
*/
|
||||
append:
|
||||
list_add_tail(&desc->node, &chan->pending_list);
|
||||
chan->desc_pendingcount++;
|
||||
|
||||
if (unlikely(chan->desc_pendingcount > chan->num_frms)) {
|
||||
dev_dbg(chan->dev, "desc pendingcount is too high\n");
|
||||
chan->desc_pendingcount = chan->num_frms;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_vdma_tx_submit - Submit DMA transaction
|
||||
* @tx: Async transaction descriptor
|
||||
@ -906,11 +928,8 @@ static dma_cookie_t xilinx_vdma_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
|
||||
cookie = dma_cookie_assign(tx);
|
||||
|
||||
/* Append the transaction to the pending transactions queue. */
|
||||
list_add_tail(&desc->node, &chan->pending_list);
|
||||
|
||||
/* Free the allocated desc */
|
||||
chan->allocated_desc = NULL;
|
||||
/* Put this transaction onto the tail of the pending queue */
|
||||
append_desc_queue(chan, desc);
|
||||
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
@ -973,13 +992,6 @@ xilinx_vdma_dma_prep_interleaved(struct dma_chan *dchan,
|
||||
else
|
||||
hw->buf_addr = xt->src_start;
|
||||
|
||||
/* Link the previous next descriptor to current */
|
||||
if (!list_empty(&desc->segments)) {
|
||||
prev = list_last_entry(&desc->segments,
|
||||
struct xilinx_vdma_tx_segment, node);
|
||||
prev->hw.next_desc = segment->phys;
|
||||
}
|
||||
|
||||
/* Insert the segment into the descriptor segments list. */
|
||||
list_add_tail(&segment->node, &desc->segments);
|
||||
|
||||
@ -988,7 +1000,7 @@ xilinx_vdma_dma_prep_interleaved(struct dma_chan *dchan,
|
||||
/* Link the last hardware descriptor with the first. */
|
||||
segment = list_first_entry(&desc->segments,
|
||||
struct xilinx_vdma_tx_segment, node);
|
||||
prev->hw.next_desc = segment->phys;
|
||||
desc->async_tx.phys = segment->phys;
|
||||
|
||||
return &desc->async_tx;
|
||||
|
||||
@ -1127,10 +1139,12 @@ static int xilinx_vdma_chan_probe(struct xilinx_vdma_device *xdev,
|
||||
chan->dev = xdev->dev;
|
||||
chan->xdev = xdev;
|
||||
chan->has_sg = xdev->has_sg;
|
||||
chan->desc_pendingcount = 0x0;
|
||||
|
||||
spin_lock_init(&chan->lock);
|
||||
INIT_LIST_HEAD(&chan->pending_list);
|
||||
INIT_LIST_HEAD(&chan->done_list);
|
||||
INIT_LIST_HEAD(&chan->active_list);
|
||||
|
||||
/* Retrieve the channel properties from the device tree */
|
||||
has_dre = of_property_read_bool(node, "xlnx,include-dre");
|
||||
|
@ -191,6 +191,7 @@ struct rockchip_spi {
|
||||
struct sg_table rx_sg;
|
||||
struct rockchip_spi_dma_data dma_rx;
|
||||
struct rockchip_spi_dma_data dma_tx;
|
||||
struct dma_slave_caps dma_caps;
|
||||
};
|
||||
|
||||
static inline void spi_enable_chip(struct rockchip_spi *rs, int enable)
|
||||
@ -446,7 +447,10 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs)
|
||||
rxconf.direction = rs->dma_rx.direction;
|
||||
rxconf.src_addr = rs->dma_rx.addr;
|
||||
rxconf.src_addr_width = rs->n_bytes;
|
||||
rxconf.src_maxburst = rs->n_bytes;
|
||||
if (rs->dma_caps.max_burst > 4)
|
||||
rxconf.src_maxburst = 4;
|
||||
else
|
||||
rxconf.src_maxburst = 1;
|
||||
dmaengine_slave_config(rs->dma_rx.ch, &rxconf);
|
||||
|
||||
rxdesc = dmaengine_prep_slave_sg(
|
||||
@ -465,7 +469,10 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs)
|
||||
txconf.direction = rs->dma_tx.direction;
|
||||
txconf.dst_addr = rs->dma_tx.addr;
|
||||
txconf.dst_addr_width = rs->n_bytes;
|
||||
txconf.dst_maxburst = rs->n_bytes;
|
||||
if (rs->dma_caps.max_burst > 4)
|
||||
txconf.dst_maxburst = 4;
|
||||
else
|
||||
txconf.dst_maxburst = 1;
|
||||
dmaengine_slave_config(rs->dma_tx.ch, &txconf);
|
||||
|
||||
txdesc = dmaengine_prep_slave_sg(
|
||||
@ -743,6 +750,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (rs->dma_tx.ch && rs->dma_rx.ch) {
|
||||
dma_get_slave_caps(rs->dma_rx.ch, &(rs->dma_caps));
|
||||
rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
|
||||
rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
|
||||
rs->dma_tx.direction = DMA_MEM_TO_DEV;
|
||||
|
@ -357,8 +357,8 @@ enum dma_slave_buswidth {
|
||||
*/
|
||||
struct dma_slave_config {
|
||||
enum dma_transfer_direction direction;
|
||||
dma_addr_t src_addr;
|
||||
dma_addr_t dst_addr;
|
||||
phys_addr_t src_addr;
|
||||
phys_addr_t dst_addr;
|
||||
enum dma_slave_buswidth src_addr_width;
|
||||
enum dma_slave_buswidth dst_addr_width;
|
||||
u32 src_maxburst;
|
||||
@ -401,6 +401,7 @@ enum dma_residue_granularity {
|
||||
* since the enum dma_transfer_direction is not defined as bits for each
|
||||
* type of direction, the dma controller should fill (1 << <TYPE>) and same
|
||||
* should be checked by controller as well
|
||||
* @max_burst: max burst capability per-transfer
|
||||
* @cmd_pause: true, if pause and thereby resume is supported
|
||||
* @cmd_terminate: true, if terminate cmd is supported
|
||||
* @residue_granularity: granularity of the reported transfer residue
|
||||
@ -411,6 +412,7 @@ struct dma_slave_caps {
|
||||
u32 src_addr_widths;
|
||||
u32 dst_addr_widths;
|
||||
u32 directions;
|
||||
u32 max_burst;
|
||||
bool cmd_pause;
|
||||
bool cmd_terminate;
|
||||
enum dma_residue_granularity residue_granularity;
|
||||
@ -654,6 +656,7 @@ struct dma_filter {
|
||||
* the enum dma_transfer_direction is not defined as bits for
|
||||
* each type of direction, the dma controller should fill (1 <<
|
||||
* <TYPE>) and same should be checked by controller as well
|
||||
* @max_burst: max burst capability per-transfer
|
||||
* @residue_granularity: granularity of the transfer residue reported
|
||||
* by tx_status
|
||||
* @device_alloc_chan_resources: allocate resources and return the
|
||||
@ -712,6 +715,7 @@ struct dma_device {
|
||||
u32 src_addr_widths;
|
||||
u32 dst_addr_widths;
|
||||
u32 directions;
|
||||
u32 max_burst;
|
||||
bool descriptor_reuse;
|
||||
enum dma_residue_granularity residue_granularity;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user