2
0
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:
Linus Torvalds 2016-03-17 12:34:54 -07:00
commit b5b131c747
37 changed files with 2143 additions and 461 deletions

View 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.

View File

@ -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:

View 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>;
};

View File

@ -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"

View File

@ -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/

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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;
}
/*

View File

@ -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(

View File

@ -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 */

View File

@ -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);

View File

@ -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)
{

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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
View 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.

View 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

View File

@ -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
View 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
View 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

View 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");

View 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);

View 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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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",

View File

@ -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");

View File

@ -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;

View File

@ -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;