mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 12:21:37 +00:00
Merge branch 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma
Pull slave-dmaengine updates from Vinod Koul: "This time we have dmatest improvements from Andy along with dw_dmac fixes. He has also done support for acpi for dmanegine. Also we have bunch of fixes going in DT support for dmanegine for various folks. Then Haswell and other ioat changes from Dave and SUDMAC support from Shimoda." * 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma: (53 commits) dma: tegra: implement suspend/resume callbacks dma:of: Use a mutex to protect the of_dma_list dma: of: Fix of_node reference leak dmaengine: sirf: move driver init from module_init to subsys_initcall sudmac: add support for SUDMAC dma: sh: add Kconfig at_hdmac: move to generic DMA binding ioatdma: ioat3_alloc_sed can be static ioatdma: Adding write back descriptor error status support for ioatdma 3.3 ioatdma: S1200 platforms ioatdma channel 2 and 3 falsely advertise RAID cap ioatdma: Adding support for 16 src PQ ops and super extended descriptors ioatdma: Removing hw bug workaround for CB3.x .2 and earlier dw_dmac: add ACPI support dmaengine: call acpi_dma_request_slave_channel as well dma: acpi-dma: introduce ACPI DMA helpers dma: of: Remove unnecessary list_empty check DMA: OF: Check properties value before running be32_to_cpup() on it DMA: of: Constant names ioatdma: skip silicon bug workaround for pq_align for cb3.3 ioatdma: Removing PQ val disable for cb3.3 ...
This commit is contained in:
commit
1763e735b0
@ -66,6 +66,83 @@ the ACPI device explicitly to acpi_platform_device_ids list defined in
|
||||
drivers/acpi/acpi_platform.c. This limitation is only for the platform
|
||||
devices, SPI and I2C devices are created automatically as described below.
|
||||
|
||||
DMA support
|
||||
~~~~~~~~~~~
|
||||
DMA controllers enumerated via ACPI should be registered in the system to
|
||||
provide generic access to their resources. For example, a driver that would
|
||||
like to be accessible to slave devices via generic API call
|
||||
dma_request_slave_channel() must register itself at the end of the probe
|
||||
function like this:
|
||||
|
||||
err = devm_acpi_dma_controller_register(dev, xlate_func, dw);
|
||||
/* Handle the error if it's not a case of !CONFIG_ACPI */
|
||||
|
||||
and implement custom xlate function if needed (usually acpi_dma_simple_xlate()
|
||||
is enough) which converts the FixedDMA resource provided by struct
|
||||
acpi_dma_spec into the corresponding DMA channel. A piece of code for that case
|
||||
could look like:
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
struct filter_args {
|
||||
/* Provide necessary information for the filter_func */
|
||||
...
|
||||
};
|
||||
|
||||
static bool filter_func(struct dma_chan *chan, void *param)
|
||||
{
|
||||
/* Choose the proper channel */
|
||||
...
|
||||
}
|
||||
|
||||
static struct dma_chan *xlate_func(struct acpi_dma_spec *dma_spec,
|
||||
struct acpi_dma *adma)
|
||||
{
|
||||
dma_cap_mask_t cap;
|
||||
struct filter_args args;
|
||||
|
||||
/* Prepare arguments for filter_func */
|
||||
...
|
||||
return dma_request_channel(cap, filter_func, &args);
|
||||
}
|
||||
#else
|
||||
static struct dma_chan *xlate_func(struct acpi_dma_spec *dma_spec,
|
||||
struct acpi_dma *adma)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
dma_request_slave_channel() will call xlate_func() for each registered DMA
|
||||
controller. In the xlate function the proper channel must be chosen based on
|
||||
information in struct acpi_dma_spec and the properties of the controller
|
||||
provided by struct acpi_dma.
|
||||
|
||||
Clients must call dma_request_slave_channel() with the string parameter that
|
||||
corresponds to a specific FixedDMA resource. By default "tx" means the first
|
||||
entry of the FixedDMA resource array, "rx" means the second entry. The table
|
||||
below shows a layout:
|
||||
|
||||
Device (I2C0)
|
||||
{
|
||||
...
|
||||
Method (_CRS, 0, NotSerialized)
|
||||
{
|
||||
Name (DBUF, ResourceTemplate ()
|
||||
{
|
||||
FixedDMA (0x0018, 0x0004, Width32bit, _Y48)
|
||||
FixedDMA (0x0019, 0x0005, Width32bit, )
|
||||
})
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
So, the FixedDMA with request line 0x0018 is "tx" and next one is "rx" in
|
||||
this example.
|
||||
|
||||
In robust cases the client unfortunately needs to call
|
||||
acpi_dma_request_slave_chan_by_index() directly and therefore choose the
|
||||
specific FixedDMA resource by its index.
|
||||
|
||||
SPI serial bus support
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
Slave devices behind SPI bus have SpiSerialBus resource attached to them.
|
||||
|
@ -1,14 +1,39 @@
|
||||
* Atmel Direct Memory Access Controller (DMA)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "atmel,<chip>-dma"
|
||||
- reg: Should contain DMA registers location and length
|
||||
- interrupts: Should contain DMA interrupt
|
||||
- compatible: Should be "atmel,<chip>-dma".
|
||||
- reg: Should contain DMA registers location and length.
|
||||
- interrupts: Should contain DMA interrupt.
|
||||
- #dma-cells: Must be <2>, used to represent the number of integer cells in
|
||||
the dmas property of client devices.
|
||||
|
||||
Examples:
|
||||
Example:
|
||||
|
||||
dma@ffffec00 {
|
||||
dma0: dma@ffffec00 {
|
||||
compatible = "atmel,at91sam9g45-dma";
|
||||
reg = <0xffffec00 0x200>;
|
||||
interrupts = <21>;
|
||||
#dma-cells = <2>;
|
||||
};
|
||||
|
||||
DMA clients connected to the Atmel DMA controller must use the format
|
||||
described in the dma.txt file, using a three-cell specifier for each channel:
|
||||
a phandle plus two interger cells.
|
||||
The three cells in order are:
|
||||
|
||||
1. A phandle pointing to the DMA controller.
|
||||
2. The memory interface (16 most significant bits), the peripheral interface
|
||||
(16 less significant bits).
|
||||
3. The peripheral identifier for the hardware handshaking interface. The
|
||||
identifier can be different for tx and rx.
|
||||
|
||||
Example:
|
||||
|
||||
i2c0@i2c@f8010000 {
|
||||
compatible = "atmel,at91sam9x5-i2c";
|
||||
reg = <0xf8010000 0x100>;
|
||||
interrupts = <9 4 6>;
|
||||
dmas = <&dma0 1 7>,
|
||||
<&dma0 1 8>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
81
Documentation/dmatest.txt
Normal file
81
Documentation/dmatest.txt
Normal file
@ -0,0 +1,81 @@
|
||||
DMA Test Guide
|
||||
==============
|
||||
|
||||
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
|
||||
This small document introduces how to test DMA drivers using dmatest module.
|
||||
|
||||
Part 1 - How to build the test module
|
||||
|
||||
The menuconfig contains an option that could be found by following path:
|
||||
Device Drivers -> DMA Engine support -> DMA Test client
|
||||
|
||||
In the configuration file the option called CONFIG_DMATEST. The dmatest could
|
||||
be built as module or inside kernel. Let's consider those cases.
|
||||
|
||||
Part 2 - When dmatest is built as a module...
|
||||
|
||||
After mounting debugfs and loading the module, the /sys/kernel/debug/dmatest
|
||||
folder with nodes will be created. They are the same as module parameters with
|
||||
addition of the 'run' node that controls run and stop phases of the test.
|
||||
|
||||
Note that in this case test will not run on load automatically.
|
||||
|
||||
Example of usage:
|
||||
% echo dma0chan0 > /sys/kernel/debug/dmatest/channel
|
||||
% echo 2000 > /sys/kernel/debug/dmatest/timeout
|
||||
% echo 1 > /sys/kernel/debug/dmatest/iterations
|
||||
% echo 1 > /sys/kernel/debug/dmatest/run
|
||||
|
||||
Hint: available channel list could be extracted by running the following
|
||||
command:
|
||||
% ls -1 /sys/class/dma/
|
||||
|
||||
After a while you will start to get messages about current status or error like
|
||||
in the original code.
|
||||
|
||||
Note that running a new test will stop any in progress test.
|
||||
|
||||
The following command should return actual state of the test.
|
||||
% cat /sys/kernel/debug/dmatest/run
|
||||
|
||||
To wait for test done the user may perform a busy loop that checks the state.
|
||||
|
||||
% while [ $(cat /sys/kernel/debug/dmatest/run) = "Y" ]
|
||||
> do
|
||||
> echo -n "."
|
||||
> sleep 1
|
||||
> done
|
||||
> echo
|
||||
|
||||
Part 3 - When built-in in the kernel...
|
||||
|
||||
The module parameters that is supplied to the kernel command line will be used
|
||||
for the first performed test. After user gets a control, the test could be
|
||||
interrupted or re-run with same or different parameters. For the details see
|
||||
the above section "Part 2 - When dmatest is built as a module..."
|
||||
|
||||
In both cases the module parameters are used as initial values for the test case.
|
||||
You always could check them at run-time by running
|
||||
% grep -H . /sys/module/dmatest/parameters/*
|
||||
|
||||
Part 4 - Gathering the test results
|
||||
|
||||
The module provides a storage for the test results in the memory. The gathered
|
||||
data could be used after test is done.
|
||||
|
||||
The special file 'results' in the debugfs represents gathered data of the in
|
||||
progress test. The messages collected are printed to the kernel log as well.
|
||||
|
||||
Example of output:
|
||||
% cat /sys/kernel/debug/dmatest/results
|
||||
dma0chan0-copy0: #1: No errors with src_off=0x7bf dst_off=0x8ad len=0x3fea (0)
|
||||
|
||||
The message format is unified across the different types of errors. A number in
|
||||
the parens represents additional information, e.g. error code, error counter,
|
||||
or status.
|
||||
|
||||
Comparison between buffers is stored to the dedicated structure.
|
||||
|
||||
Note that the verify result is now accessible only via file 'results' in the
|
||||
debugfs.
|
@ -28,6 +28,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/omap-dma.h>
|
||||
|
||||
#include "soc.h"
|
||||
@ -304,6 +305,9 @@ static int __init omap2_system_dma_init(void)
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (of_have_populated_dt())
|
||||
return res;
|
||||
|
||||
pdev = platform_device_register_full(&omap_dma_dev_info);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
@ -63,8 +63,6 @@ config INTEL_IOATDMA
|
||||
depends on PCI && X86
|
||||
select DMA_ENGINE
|
||||
select DCA
|
||||
select ASYNC_TX_DISABLE_PQ_VAL_DMA
|
||||
select ASYNC_TX_DISABLE_XOR_VAL_DMA
|
||||
help
|
||||
Enable support for the Intel(R) I/OAT DMA engine present
|
||||
in recent Intel Xeon chipsets.
|
||||
@ -174,15 +172,7 @@ config TEGRA20_APB_DMA
|
||||
This DMA controller transfers data from memory to peripheral fifo
|
||||
or vice versa. It does not support memory to memory data transfer.
|
||||
|
||||
|
||||
|
||||
config SH_DMAE
|
||||
tristate "Renesas SuperH DMAC support"
|
||||
depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
|
||||
depends on !SH_DMA_API
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Enable support for the Renesas SuperH DMA controllers.
|
||||
source "drivers/dma/sh/Kconfig"
|
||||
|
||||
config COH901318
|
||||
bool "ST-Ericsson COH901318 DMA support"
|
||||
@ -328,6 +318,10 @@ config DMA_ENGINE
|
||||
config DMA_VIRTUAL_CHANNELS
|
||||
tristate
|
||||
|
||||
config DMA_ACPI
|
||||
def_bool y
|
||||
depends on ACPI
|
||||
|
||||
config DMA_OF
|
||||
def_bool y
|
||||
depends on OF
|
||||
|
@ -3,6 +3,7 @@ ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
|
||||
|
||||
obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
|
||||
obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
|
||||
obj-$(CONFIG_DMA_ACPI) += acpi-dma.o
|
||||
obj-$(CONFIG_DMA_OF) += of-dma.o
|
||||
|
||||
obj-$(CONFIG_NET_DMA) += iovlock.o
|
||||
@ -18,7 +19,7 @@ obj-$(CONFIG_DW_DMAC) += dw_dmac.o
|
||||
obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
|
||||
obj-$(CONFIG_MX3_IPU) += ipu/
|
||||
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
|
||||
obj-$(CONFIG_SH_DMAE) += sh/
|
||||
obj-$(CONFIG_SH_DMAE_BASE) += sh/
|
||||
obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
|
||||
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
|
||||
obj-$(CONFIG_IMX_SDMA) += imx-sdma.o
|
||||
|
279
drivers/dma/acpi-dma.c
Normal file
279
drivers/dma/acpi-dma.c
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* ACPI helpers for DMA request / controller
|
||||
*
|
||||
* Based on of-dma.c
|
||||
*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_dma.h>
|
||||
|
||||
static LIST_HEAD(acpi_dma_list);
|
||||
static DEFINE_MUTEX(acpi_dma_lock);
|
||||
|
||||
/**
|
||||
* acpi_dma_controller_register - Register a DMA controller to ACPI DMA helpers
|
||||
* @dev: struct device of DMA controller
|
||||
* @acpi_dma_xlate: translation function which converts a dma specifier
|
||||
* into a dma_chan structure
|
||||
* @data pointer to controller specific data to be used by
|
||||
* translation function
|
||||
*
|
||||
* Returns 0 on success or appropriate errno value on error.
|
||||
*
|
||||
* Allocated memory should be freed with appropriate acpi_dma_controller_free()
|
||||
* call.
|
||||
*/
|
||||
int acpi_dma_controller_register(struct device *dev,
|
||||
struct dma_chan *(*acpi_dma_xlate)
|
||||
(struct acpi_dma_spec *, struct acpi_dma *),
|
||||
void *data)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
struct acpi_dma *adma;
|
||||
|
||||
if (!dev || !acpi_dma_xlate)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if the device was enumerated by ACPI */
|
||||
if (!ACPI_HANDLE(dev))
|
||||
return -EINVAL;
|
||||
|
||||
if (acpi_bus_get_device(ACPI_HANDLE(dev), &adev))
|
||||
return -EINVAL;
|
||||
|
||||
adma = kzalloc(sizeof(*adma), GFP_KERNEL);
|
||||
if (!adma)
|
||||
return -ENOMEM;
|
||||
|
||||
adma->dev = dev;
|
||||
adma->acpi_dma_xlate = acpi_dma_xlate;
|
||||
adma->data = data;
|
||||
|
||||
/* Now queue acpi_dma controller structure in list */
|
||||
mutex_lock(&acpi_dma_lock);
|
||||
list_add_tail(&adma->dma_controllers, &acpi_dma_list);
|
||||
mutex_unlock(&acpi_dma_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dma_controller_register);
|
||||
|
||||
/**
|
||||
* acpi_dma_controller_free - Remove a DMA controller from ACPI DMA helpers list
|
||||
* @dev: struct device of DMA controller
|
||||
*
|
||||
* Memory allocated by acpi_dma_controller_register() is freed here.
|
||||
*/
|
||||
int acpi_dma_controller_free(struct device *dev)
|
||||
{
|
||||
struct acpi_dma *adma;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&acpi_dma_lock);
|
||||
|
||||
list_for_each_entry(adma, &acpi_dma_list, dma_controllers)
|
||||
if (adma->dev == dev) {
|
||||
list_del(&adma->dma_controllers);
|
||||
mutex_unlock(&acpi_dma_lock);
|
||||
kfree(adma);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&acpi_dma_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dma_controller_free);
|
||||
|
||||
static void devm_acpi_dma_release(struct device *dev, void *res)
|
||||
{
|
||||
acpi_dma_controller_free(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_acpi_dma_controller_register - resource managed acpi_dma_controller_register()
|
||||
* @dev: device that is registering this DMA controller
|
||||
* @acpi_dma_xlate: translation function
|
||||
* @data pointer to controller specific data
|
||||
*
|
||||
* Managed acpi_dma_controller_register(). DMA controller registered by this
|
||||
* function are automatically freed on driver detach. See
|
||||
* acpi_dma_controller_register() for more information.
|
||||
*/
|
||||
int devm_acpi_dma_controller_register(struct device *dev,
|
||||
struct dma_chan *(*acpi_dma_xlate)
|
||||
(struct acpi_dma_spec *, struct acpi_dma *),
|
||||
void *data)
|
||||
{
|
||||
void *res;
|
||||
int ret;
|
||||
|
||||
res = devres_alloc(devm_acpi_dma_release, 0, GFP_KERNEL);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = acpi_dma_controller_register(dev, acpi_dma_xlate, data);
|
||||
if (ret) {
|
||||
devres_free(res);
|
||||
return ret;
|
||||
}
|
||||
devres_add(dev, res);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_register);
|
||||
|
||||
/**
|
||||
* devm_acpi_dma_controller_free - resource managed acpi_dma_controller_free()
|
||||
*
|
||||
* Unregister a DMA controller registered with
|
||||
* devm_acpi_dma_controller_register(). Normally this function will not need to
|
||||
* be called and the resource management code will ensure that the resource is
|
||||
* freed.
|
||||
*/
|
||||
void devm_acpi_dma_controller_free(struct device *dev)
|
||||
{
|
||||
WARN_ON(devres_destroy(dev, devm_acpi_dma_release, NULL, NULL));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_free);
|
||||
|
||||
struct acpi_dma_parser_data {
|
||||
struct acpi_dma_spec dma_spec;
|
||||
size_t index;
|
||||
size_t n;
|
||||
};
|
||||
|
||||
/**
|
||||
* acpi_dma_parse_fixed_dma - Parse FixedDMA ACPI resources to a DMA specifier
|
||||
* @res: struct acpi_resource to get FixedDMA resources from
|
||||
* @data: pointer to a helper struct acpi_dma_parser_data
|
||||
*/
|
||||
static int acpi_dma_parse_fixed_dma(struct acpi_resource *res, void *data)
|
||||
{
|
||||
struct acpi_dma_parser_data *pdata = data;
|
||||
|
||||
if (res->type == ACPI_RESOURCE_TYPE_FIXED_DMA) {
|
||||
struct acpi_resource_fixed_dma *dma = &res->data.fixed_dma;
|
||||
|
||||
if (pdata->n++ == pdata->index) {
|
||||
pdata->dma_spec.chan_id = dma->channels;
|
||||
pdata->dma_spec.slave_id = dma->request_lines;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the ACPI core to skip this resource */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dma_request_slave_chan_by_index - Get the DMA slave channel
|
||||
* @dev: struct device to get DMA request from
|
||||
* @index: index of FixedDMA descriptor for @dev
|
||||
*
|
||||
* Returns pointer to appropriate dma channel on success or NULL on error.
|
||||
*/
|
||||
struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,
|
||||
size_t index)
|
||||
{
|
||||
struct acpi_dma_parser_data pdata;
|
||||
struct acpi_dma_spec *dma_spec = &pdata.dma_spec;
|
||||
struct list_head resource_list;
|
||||
struct acpi_device *adev;
|
||||
struct acpi_dma *adma;
|
||||
struct dma_chan *chan = NULL;
|
||||
|
||||
/* Check if the device was enumerated by ACPI */
|
||||
if (!dev || !ACPI_HANDLE(dev))
|
||||
return NULL;
|
||||
|
||||
if (acpi_bus_get_device(ACPI_HANDLE(dev), &adev))
|
||||
return NULL;
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
pdata.index = index;
|
||||
|
||||
/* Initial values for the request line and channel */
|
||||
dma_spec->chan_id = -1;
|
||||
dma_spec->slave_id = -1;
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
acpi_dev_get_resources(adev, &resource_list,
|
||||
acpi_dma_parse_fixed_dma, &pdata);
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (dma_spec->slave_id < 0 || dma_spec->chan_id < 0)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&acpi_dma_lock);
|
||||
|
||||
list_for_each_entry(adma, &acpi_dma_list, dma_controllers) {
|
||||
dma_spec->dev = adma->dev;
|
||||
chan = adma->acpi_dma_xlate(dma_spec, adma);
|
||||
if (chan)
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&acpi_dma_lock);
|
||||
return chan;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index);
|
||||
|
||||
/**
|
||||
* acpi_dma_request_slave_chan_by_name - Get the DMA slave channel
|
||||
* @dev: struct device to get DMA request from
|
||||
* @name: represents corresponding FixedDMA descriptor for @dev
|
||||
*
|
||||
* In order to support both Device Tree and ACPI in a single driver we
|
||||
* translate the names "tx" and "rx" here based on the most common case where
|
||||
* the first FixedDMA descriptor is TX and second is RX.
|
||||
*
|
||||
* Returns pointer to appropriate dma channel on success or NULL on error.
|
||||
*/
|
||||
struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if (!strcmp(name, "tx"))
|
||||
index = 0;
|
||||
else if (!strcmp(name, "rx"))
|
||||
index = 1;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
return acpi_dma_request_slave_chan_by_index(dev, index);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_name);
|
||||
|
||||
/**
|
||||
* acpi_dma_simple_xlate - Simple ACPI DMA engine translation helper
|
||||
* @dma_spec: pointer to ACPI DMA specifier
|
||||
* @adma: pointer to ACPI DMA controller data
|
||||
*
|
||||
* A simple translation function for ACPI based devices. Passes &struct
|
||||
* dma_spec to the DMA controller driver provided filter function. Returns
|
||||
* pointer to the channel if found or %NULL otherwise.
|
||||
*/
|
||||
struct dma_chan *acpi_dma_simple_xlate(struct acpi_dma_spec *dma_spec,
|
||||
struct acpi_dma *adma)
|
||||
{
|
||||
struct acpi_dma_filter_info *info = adma->data;
|
||||
|
||||
if (!info || !info->filter_fn)
|
||||
return NULL;
|
||||
|
||||
return dma_request_channel(info->dma_cap, info->filter_fn, dma_spec);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dma_simple_xlate);
|
@ -24,6 +24,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
|
||||
#include "at_hdmac_regs.h"
|
||||
#include "dmaengine.h"
|
||||
@ -677,7 +678,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
ctrlb |= ATC_DST_ADDR_MODE_FIXED
|
||||
| ATC_SRC_ADDR_MODE_INCR
|
||||
| ATC_FC_MEM2PER
|
||||
| ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF);
|
||||
| ATC_SIF(atchan->mem_if) | ATC_DIF(atchan->per_if);
|
||||
reg = sconfig->dst_addr;
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
struct at_desc *desc;
|
||||
@ -716,7 +717,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
ctrlb |= ATC_DST_ADDR_MODE_INCR
|
||||
| ATC_SRC_ADDR_MODE_FIXED
|
||||
| ATC_FC_PER2MEM
|
||||
| ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF);
|
||||
| ATC_SIF(atchan->per_if) | ATC_DIF(atchan->mem_if);
|
||||
|
||||
reg = sconfig->src_addr;
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
@ -822,8 +823,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
|
||||
desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED
|
||||
| ATC_SRC_ADDR_MODE_INCR
|
||||
| ATC_FC_MEM2PER
|
||||
| ATC_SIF(AT_DMA_MEM_IF)
|
||||
| ATC_DIF(AT_DMA_PER_IF);
|
||||
| ATC_SIF(atchan->mem_if)
|
||||
| ATC_DIF(atchan->per_if);
|
||||
break;
|
||||
|
||||
case DMA_DEV_TO_MEM:
|
||||
@ -833,8 +834,8 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
|
||||
desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR
|
||||
| ATC_SRC_ADDR_MODE_FIXED
|
||||
| ATC_FC_PER2MEM
|
||||
| ATC_SIF(AT_DMA_PER_IF)
|
||||
| ATC_DIF(AT_DMA_MEM_IF);
|
||||
| ATC_SIF(atchan->per_if)
|
||||
| ATC_DIF(atchan->mem_if);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1188,6 +1189,67 @@ static void atc_free_chan_resources(struct dma_chan *chan)
|
||||
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static bool at_dma_filter(struct dma_chan *chan, void *slave)
|
||||
{
|
||||
struct at_dma_slave *atslave = slave;
|
||||
|
||||
if (atslave->dma_dev == chan->device->dev) {
|
||||
chan->private = atslave;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *of_dma)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
struct at_dma_chan *atchan;
|
||||
struct at_dma_slave *atslave;
|
||||
dma_cap_mask_t mask;
|
||||
unsigned int per_id;
|
||||
struct platform_device *dmac_pdev;
|
||||
|
||||
if (dma_spec->args_count != 2)
|
||||
return NULL;
|
||||
|
||||
dmac_pdev = of_find_device_by_node(dma_spec->np);
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL);
|
||||
if (!atslave)
|
||||
return NULL;
|
||||
/*
|
||||
* We can fill both SRC_PER and DST_PER, one of these fields will be
|
||||
* ignored depending on DMA transfer direction.
|
||||
*/
|
||||
per_id = dma_spec->args[1];
|
||||
atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW
|
||||
| ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id)
|
||||
| ATC_SRC_PER(per_id);
|
||||
atslave->dma_dev = &dmac_pdev->dev;
|
||||
|
||||
chan = dma_request_channel(mask, at_dma_filter, atslave);
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
atchan = to_at_dma_chan(chan);
|
||||
atchan->per_if = dma_spec->args[0] & 0xff;
|
||||
atchan->mem_if = (dma_spec->args[0] >> 16) & 0xff;
|
||||
|
||||
return chan;
|
||||
}
|
||||
#else
|
||||
static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *of_dma)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*-- Module Management -----------------------------------------------*/
|
||||
|
||||
@ -1342,6 +1404,8 @@ static int __init at_dma_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < plat_dat->nr_channels; i++) {
|
||||
struct at_dma_chan *atchan = &atdma->chan[i];
|
||||
|
||||
atchan->mem_if = AT_DMA_MEM_IF;
|
||||
atchan->per_if = AT_DMA_PER_IF;
|
||||
atchan->chan_common.device = &atdma->dma_common;
|
||||
dma_cookie_init(&atchan->chan_common);
|
||||
list_add_tail(&atchan->chan_common.device_node,
|
||||
@ -1388,8 +1452,25 @@ static int __init at_dma_probe(struct platform_device *pdev)
|
||||
|
||||
dma_async_device_register(&atdma->dma_common);
|
||||
|
||||
/*
|
||||
* Do not return an error if the dmac node is not present in order to
|
||||
* not break the existing way of requesting channel with
|
||||
* dma_request_channel().
|
||||
*/
|
||||
if (pdev->dev.of_node) {
|
||||
err = of_dma_controller_register(pdev->dev.of_node,
|
||||
at_dma_xlate, atdma);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not register of_dma_controller\n");
|
||||
goto err_of_dma_controller_register;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_of_dma_controller_register:
|
||||
dma_async_device_unregister(&atdma->dma_common);
|
||||
dma_pool_destroy(atdma->dma_desc_pool);
|
||||
err_pool_create:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
free_irq(platform_get_irq(pdev, 0), atdma);
|
||||
@ -1406,7 +1487,7 @@ err_kfree:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __exit at_dma_remove(struct platform_device *pdev)
|
||||
static int at_dma_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct at_dma *atdma = platform_get_drvdata(pdev);
|
||||
struct dma_chan *chan, *_chan;
|
||||
@ -1564,7 +1645,7 @@ static const struct dev_pm_ops at_dma_dev_pm_ops = {
|
||||
};
|
||||
|
||||
static struct platform_driver at_dma_driver = {
|
||||
.remove = __exit_p(at_dma_remove),
|
||||
.remove = at_dma_remove,
|
||||
.shutdown = at_dma_shutdown,
|
||||
.id_table = atdma_devtypes,
|
||||
.driver = {
|
||||
|
@ -220,6 +220,8 @@ enum atc_status {
|
||||
* @device: parent device
|
||||
* @ch_regs: memory mapped register base
|
||||
* @mask: channel index in a mask
|
||||
* @per_if: peripheral interface
|
||||
* @mem_if: memory interface
|
||||
* @status: transmit status information from irq/prep* functions
|
||||
* to tasklet (use atomic operations)
|
||||
* @tasklet: bottom half to finish transaction work
|
||||
@ -238,6 +240,8 @@ struct at_dma_chan {
|
||||
struct at_dma *device;
|
||||
void __iomem *ch_regs;
|
||||
u8 mask;
|
||||
u8 per_if;
|
||||
u8 mem_if;
|
||||
unsigned long status;
|
||||
struct tasklet_struct tasklet;
|
||||
u32 save_cfg;
|
||||
|
@ -2748,7 +2748,7 @@ static int __init coh901318_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __exit coh901318_remove(struct platform_device *pdev)
|
||||
static int coh901318_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct coh901318_base *base = platform_get_drvdata(pdev);
|
||||
|
||||
@ -2760,7 +2760,7 @@ static int __exit coh901318_remove(struct platform_device *pdev)
|
||||
|
||||
|
||||
static struct platform_driver coh901318_driver = {
|
||||
.remove = __exit_p(coh901318_remove),
|
||||
.remove = coh901318_remove,
|
||||
.driver = {
|
||||
.name = "coh901318",
|
||||
},
|
||||
|
@ -62,6 +62,8 @@
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_dma.h>
|
||||
#include <linux/of_dma.h>
|
||||
|
||||
static DEFINE_MUTEX(dma_list_mutex);
|
||||
@ -174,7 +176,8 @@ static struct class dma_devclass = {
|
||||
#define dma_device_satisfies_mask(device, mask) \
|
||||
__dma_device_satisfies_mask((device), &(mask))
|
||||
static int
|
||||
__dma_device_satisfies_mask(struct dma_device *device, dma_cap_mask_t *want)
|
||||
__dma_device_satisfies_mask(struct dma_device *device,
|
||||
const dma_cap_mask_t *want)
|
||||
{
|
||||
dma_cap_mask_t has;
|
||||
|
||||
@ -463,7 +466,8 @@ static void dma_channel_rebalance(void)
|
||||
}
|
||||
}
|
||||
|
||||
static struct dma_chan *private_candidate(dma_cap_mask_t *mask, struct dma_device *dev,
|
||||
static struct dma_chan *private_candidate(const dma_cap_mask_t *mask,
|
||||
struct dma_device *dev,
|
||||
dma_filter_fn fn, void *fn_param)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
@ -505,7 +509,8 @@ static struct dma_chan *private_candidate(dma_cap_mask_t *mask, struct dma_devic
|
||||
* @fn: optional callback to disposition available channels
|
||||
* @fn_param: opaque parameter to pass to dma_filter_fn
|
||||
*/
|
||||
struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param)
|
||||
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
|
||||
dma_filter_fn fn, void *fn_param)
|
||||
{
|
||||
struct dma_device *device, *_d;
|
||||
struct dma_chan *chan = NULL;
|
||||
@ -555,12 +560,16 @@ EXPORT_SYMBOL_GPL(__dma_request_channel);
|
||||
* @dev: pointer to client device structure
|
||||
* @name: slave channel name
|
||||
*/
|
||||
struct dma_chan *dma_request_slave_channel(struct device *dev, char *name)
|
||||
struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name)
|
||||
{
|
||||
/* If device-tree is present get slave info from here */
|
||||
if (dev->of_node)
|
||||
return of_dma_request_slave_channel(dev->of_node, name);
|
||||
|
||||
/* If device was enumerated by ACPI get slave info from here */
|
||||
if (ACPI_HANDLE(dev))
|
||||
return acpi_dma_request_slave_chan_by_name(dev, name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_request_slave_channel);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,8 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_dma.h>
|
||||
|
||||
#include "dw_dmac_regs.h"
|
||||
#include "dmaengine.h"
|
||||
@ -49,29 +51,22 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave)
|
||||
return slave ? slave->src_master : 1;
|
||||
}
|
||||
|
||||
#define SRC_MASTER 0
|
||||
#define DST_MASTER 1
|
||||
|
||||
static inline unsigned int dwc_get_master(struct dma_chan *chan, int master)
|
||||
static inline void dwc_set_masters(struct dw_dma_chan *dwc)
|
||||
{
|
||||
struct dw_dma *dw = to_dw_dma(chan->device);
|
||||
struct dw_dma_slave *dws = chan->private;
|
||||
unsigned int m;
|
||||
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
|
||||
struct dw_dma_slave *dws = dwc->chan.private;
|
||||
unsigned char mmax = dw->nr_masters - 1;
|
||||
|
||||
if (master == SRC_MASTER)
|
||||
m = dwc_get_sms(dws);
|
||||
else
|
||||
m = dwc_get_dms(dws);
|
||||
|
||||
return min_t(unsigned int, dw->nr_masters - 1, m);
|
||||
if (dwc->request_line == ~0) {
|
||||
dwc->src_master = min_t(unsigned char, mmax, dwc_get_sms(dws));
|
||||
dwc->dst_master = min_t(unsigned char, mmax, dwc_get_dms(dws));
|
||||
}
|
||||
}
|
||||
|
||||
#define DWC_DEFAULT_CTLLO(_chan) ({ \
|
||||
struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \
|
||||
struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \
|
||||
bool _is_slave = is_slave_direction(_dwc->direction); \
|
||||
int _dms = dwc_get_master(_chan, DST_MASTER); \
|
||||
int _sms = dwc_get_master(_chan, SRC_MASTER); \
|
||||
u8 _smsize = _is_slave ? _sconfig->src_maxburst : \
|
||||
DW_DMA_MSIZE_16; \
|
||||
u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \
|
||||
@ -81,8 +76,8 @@ static inline unsigned int dwc_get_master(struct dma_chan *chan, int master)
|
||||
| DWC_CTLL_SRC_MSIZE(_smsize) \
|
||||
| DWC_CTLL_LLP_D_EN \
|
||||
| DWC_CTLL_LLP_S_EN \
|
||||
| DWC_CTLL_DMS(_dms) \
|
||||
| DWC_CTLL_SMS(_sms)); \
|
||||
| DWC_CTLL_DMS(_dwc->dst_master) \
|
||||
| DWC_CTLL_SMS(_dwc->src_master)); \
|
||||
})
|
||||
|
||||
/*
|
||||
@ -92,13 +87,6 @@ static inline unsigned int dwc_get_master(struct dma_chan *chan, int master)
|
||||
*/
|
||||
#define NR_DESCS_PER_CHANNEL 64
|
||||
|
||||
static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master)
|
||||
{
|
||||
struct dw_dma *dw = to_dw_dma(chan->device);
|
||||
|
||||
return dw->data_width[dwc_get_master(chan, master)];
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static struct device *chan2dev(struct dma_chan *chan)
|
||||
@ -172,13 +160,7 @@ static void dwc_initialize(struct dw_dma_chan *dwc)
|
||||
if (dwc->initialized == true)
|
||||
return;
|
||||
|
||||
if (dws && dws->cfg_hi == ~0 && dws->cfg_lo == ~0) {
|
||||
/* autoconfigure based on request line from DT */
|
||||
if (dwc->direction == DMA_MEM_TO_DEV)
|
||||
cfghi = DWC_CFGH_DST_PER(dwc->request_line);
|
||||
else if (dwc->direction == DMA_DEV_TO_MEM)
|
||||
cfghi = DWC_CFGH_SRC_PER(dwc->request_line);
|
||||
} else if (dws) {
|
||||
if (dws) {
|
||||
/*
|
||||
* We need controller-specific data to set up slave
|
||||
* transfers.
|
||||
@ -189,9 +171,9 @@ static void dwc_initialize(struct dw_dma_chan *dwc)
|
||||
cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK;
|
||||
} else {
|
||||
if (dwc->direction == DMA_MEM_TO_DEV)
|
||||
cfghi = DWC_CFGH_DST_PER(dwc->dma_sconfig.slave_id);
|
||||
cfghi = DWC_CFGH_DST_PER(dwc->request_line);
|
||||
else if (dwc->direction == DMA_DEV_TO_MEM)
|
||||
cfghi = DWC_CFGH_SRC_PER(dwc->dma_sconfig.slave_id);
|
||||
cfghi = DWC_CFGH_SRC_PER(dwc->request_line);
|
||||
}
|
||||
|
||||
channel_writel(dwc, CFG_LO, cfglo);
|
||||
@ -473,16 +455,16 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
|
||||
(unsigned long long)llp);
|
||||
|
||||
list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) {
|
||||
/* initial residue value */
|
||||
/* Initial residue value */
|
||||
dwc->residue = desc->total_len;
|
||||
|
||||
/* check first descriptors addr */
|
||||
/* Check first descriptors addr */
|
||||
if (desc->txd.phys == llp) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/* check first descriptors llp */
|
||||
/* Check first descriptors llp */
|
||||
if (desc->lli.llp == llp) {
|
||||
/* This one is currently in progress */
|
||||
dwc->residue -= dwc_get_sent(dwc);
|
||||
@ -588,7 +570,7 @@ inline dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan)
|
||||
}
|
||||
EXPORT_SYMBOL(dw_dma_get_dst_addr);
|
||||
|
||||
/* called with dwc->lock held and all DMAC interrupts disabled */
|
||||
/* Called with dwc->lock held and all DMAC interrupts disabled */
|
||||
static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
|
||||
u32 status_err, u32 status_xfer)
|
||||
{
|
||||
@ -626,7 +608,7 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
|
||||
|
||||
dwc_chan_disable(dw, dwc);
|
||||
|
||||
/* make sure DMA does not restart by loading a new list */
|
||||
/* Make sure DMA does not restart by loading a new list */
|
||||
channel_writel(dwc, LLP, 0);
|
||||
channel_writel(dwc, CTL_LO, 0);
|
||||
channel_writel(dwc, CTL_HI, 0);
|
||||
@ -745,6 +727,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
|
||||
size_t len, unsigned long flags)
|
||||
{
|
||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||
struct dw_dma *dw = to_dw_dma(chan->device);
|
||||
struct dw_desc *desc;
|
||||
struct dw_desc *first;
|
||||
struct dw_desc *prev;
|
||||
@ -767,8 +750,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
|
||||
|
||||
dwc->direction = DMA_MEM_TO_MEM;
|
||||
|
||||
data_width = min_t(unsigned int, dwc_get_data_width(chan, SRC_MASTER),
|
||||
dwc_get_data_width(chan, DST_MASTER));
|
||||
data_width = min_t(unsigned int, dw->data_width[dwc->src_master],
|
||||
dw->data_width[dwc->dst_master]);
|
||||
|
||||
src_width = dst_width = min_t(unsigned int, data_width,
|
||||
dwc_fast_fls(src | dest | len));
|
||||
@ -826,6 +809,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
unsigned long flags, void *context)
|
||||
{
|
||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||
struct dw_dma *dw = to_dw_dma(chan->device);
|
||||
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
|
||||
struct dw_desc *prev;
|
||||
struct dw_desc *first;
|
||||
@ -859,7 +843,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
|
||||
DWC_CTLL_FC(DW_DMA_FC_D_M2P);
|
||||
|
||||
data_width = dwc_get_data_width(chan, SRC_MASTER);
|
||||
data_width = dw->data_width[dwc->src_master];
|
||||
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
struct dw_desc *desc;
|
||||
@ -919,7 +903,7 @@ slave_sg_todev_fill_desc:
|
||||
ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
|
||||
DWC_CTLL_FC(DW_DMA_FC_D_P2M);
|
||||
|
||||
data_width = dwc_get_data_width(chan, DST_MASTER);
|
||||
data_width = dw->data_width[dwc->dst_master];
|
||||
|
||||
for_each_sg(sgl, sg, sg_len, i) {
|
||||
struct dw_desc *desc;
|
||||
@ -1001,13 +985,6 @@ static inline void convert_burst(u32 *maxburst)
|
||||
*maxburst = 0;
|
||||
}
|
||||
|
||||
static inline void convert_slave_id(struct dw_dma_chan *dwc)
|
||||
{
|
||||
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
|
||||
|
||||
dwc->dma_sconfig.slave_id -= dw->request_line_base;
|
||||
}
|
||||
|
||||
static int
|
||||
set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
|
||||
{
|
||||
@ -1020,9 +997,12 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
|
||||
memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
|
||||
dwc->direction = sconfig->direction;
|
||||
|
||||
/* Take the request line from slave_id member */
|
||||
if (dwc->request_line == ~0)
|
||||
dwc->request_line = sconfig->slave_id;
|
||||
|
||||
convert_burst(&dwc->dma_sconfig.src_maxburst);
|
||||
convert_burst(&dwc->dma_sconfig.dst_maxburst);
|
||||
convert_slave_id(dwc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1030,10 +1010,11 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
|
||||
static inline void dwc_chan_pause(struct dw_dma_chan *dwc)
|
||||
{
|
||||
u32 cfglo = channel_readl(dwc, CFG_LO);
|
||||
unsigned int count = 20; /* timeout iterations */
|
||||
|
||||
channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP);
|
||||
while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY))
|
||||
cpu_relax();
|
||||
while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--)
|
||||
udelay(2);
|
||||
|
||||
dwc->paused = true;
|
||||
}
|
||||
@ -1169,6 +1150,8 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
|
||||
* doesn't mean what you think it means), and status writeback.
|
||||
*/
|
||||
|
||||
dwc_set_masters(dwc);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
i = dwc->descs_allocated;
|
||||
while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) {
|
||||
@ -1226,6 +1209,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
|
||||
list_splice_init(&dwc->free_list, &list);
|
||||
dwc->descs_allocated = 0;
|
||||
dwc->initialized = false;
|
||||
dwc->request_line = ~0;
|
||||
|
||||
/* Disable interrupts */
|
||||
channel_clear_bit(dw, MASK.XFER, dwc->mask);
|
||||
@ -1241,42 +1225,36 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
|
||||
dev_vdbg(chan2dev(chan), "%s: done\n", __func__);
|
||||
}
|
||||
|
||||
struct dw_dma_filter_args {
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
struct dw_dma_of_filter_args {
|
||||
struct dw_dma *dw;
|
||||
unsigned int req;
|
||||
unsigned int src;
|
||||
unsigned int dst;
|
||||
};
|
||||
|
||||
static bool dw_dma_generic_filter(struct dma_chan *chan, void *param)
|
||||
static bool dw_dma_of_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||
struct dw_dma *dw = to_dw_dma(chan->device);
|
||||
struct dw_dma_filter_args *fargs = param;
|
||||
struct dw_dma_slave *dws = &dwc->slave;
|
||||
struct dw_dma_of_filter_args *fargs = param;
|
||||
|
||||
/* ensure the device matches our channel */
|
||||
/* Ensure the device matches our channel */
|
||||
if (chan->device != &fargs->dw->dma)
|
||||
return false;
|
||||
|
||||
dws->dma_dev = dw->dma.dev;
|
||||
dws->cfg_hi = ~0;
|
||||
dws->cfg_lo = ~0;
|
||||
dws->src_master = fargs->src;
|
||||
dws->dst_master = fargs->dst;
|
||||
|
||||
dwc->request_line = fargs->req;
|
||||
|
||||
chan->private = dws;
|
||||
dwc->src_master = fargs->src;
|
||||
dwc->dst_master = fargs->dst;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct dma_chan *dw_dma_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma)
|
||||
{
|
||||
struct dw_dma *dw = ofdma->of_dma_data;
|
||||
struct dw_dma_filter_args fargs = {
|
||||
struct dw_dma_of_filter_args fargs = {
|
||||
.dw = dw,
|
||||
};
|
||||
dma_cap_mask_t cap;
|
||||
@ -1297,9 +1275,49 @@ static struct dma_chan *dw_dma_xlate(struct of_phandle_args *dma_spec,
|
||||
dma_cap_set(DMA_SLAVE, cap);
|
||||
|
||||
/* TODO: there should be a simpler way to do this */
|
||||
return dma_request_channel(cap, dw_dma_generic_filter, &fargs);
|
||||
return dma_request_channel(cap, dw_dma_of_filter, &fargs);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
|
||||
struct acpi_dma_spec *dma_spec = param;
|
||||
|
||||
if (chan->device->dev != dma_spec->dev ||
|
||||
chan->chan_id != dma_spec->chan_id)
|
||||
return false;
|
||||
|
||||
dwc->request_line = dma_spec->slave_id;
|
||||
dwc->src_master = dwc_get_sms(NULL);
|
||||
dwc->dst_master = dwc_get_dms(NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dw_dma_acpi_controller_register(struct dw_dma *dw)
|
||||
{
|
||||
struct device *dev = dw->dma.dev;
|
||||
struct acpi_dma_filter_info *info;
|
||||
int ret;
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
dma_cap_zero(info->dma_cap);
|
||||
dma_cap_set(DMA_SLAVE, info->dma_cap);
|
||||
info->filter_fn = dw_dma_acpi_filter;
|
||||
|
||||
ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate,
|
||||
info);
|
||||
if (ret)
|
||||
dev_err(dev, "could not register acpi_dma_controller\n");
|
||||
}
|
||||
#else /* !CONFIG_ACPI */
|
||||
static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {}
|
||||
#endif /* !CONFIG_ACPI */
|
||||
|
||||
/* --------------------- Cyclic DMA API extensions -------------------- */
|
||||
|
||||
/**
|
||||
@ -1322,7 +1340,7 @@ int dw_dma_cyclic_start(struct dma_chan *chan)
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
/* assert channel is idle */
|
||||
/* Assert channel is idle */
|
||||
if (dma_readl(dw, CH_EN) & dwc->mask) {
|
||||
dev_err(chan2dev(&dwc->chan),
|
||||
"BUG: Attempted to start non-idle channel\n");
|
||||
@ -1334,7 +1352,7 @@ int dw_dma_cyclic_start(struct dma_chan *chan)
|
||||
dma_writel(dw, CLEAR.ERROR, dwc->mask);
|
||||
dma_writel(dw, CLEAR.XFER, dwc->mask);
|
||||
|
||||
/* setup DMAC channel registers */
|
||||
/* Setup DMAC channel registers */
|
||||
channel_writel(dwc, LLP, dwc->cdesc->desc[0]->txd.phys);
|
||||
channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN);
|
||||
channel_writel(dwc, CTL_HI, 0);
|
||||
@ -1501,7 +1519,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
|
||||
last = desc;
|
||||
}
|
||||
|
||||
/* lets make a cyclic list */
|
||||
/* Let's make a cyclic list */
|
||||
last->lli.llp = cdesc->desc[0]->txd.phys;
|
||||
|
||||
dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu "
|
||||
@ -1636,7 +1654,6 @@ dw_dma_parse_dt(struct platform_device *pdev)
|
||||
|
||||
static int dw_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *match;
|
||||
struct dw_dma_platform_data *pdata;
|
||||
struct resource *io;
|
||||
struct dw_dma *dw;
|
||||
@ -1706,7 +1723,7 @@ static int dw_probe(struct platform_device *pdev)
|
||||
|
||||
dw->regs = regs;
|
||||
|
||||
/* get hardware configuration parameters */
|
||||
/* Get hardware configuration parameters */
|
||||
if (autocfg) {
|
||||
max_blk_size = dma_readl(dw, MAX_BLK_SIZE);
|
||||
|
||||
@ -1720,18 +1737,13 @@ static int dw_probe(struct platform_device *pdev)
|
||||
memcpy(dw->data_width, pdata->data_width, 4);
|
||||
}
|
||||
|
||||
/* Get the base request line if set */
|
||||
match = platform_get_device_id(pdev);
|
||||
if (match)
|
||||
dw->request_line_base = (unsigned int)match->driver_data;
|
||||
|
||||
/* Calculate all channel mask before DMA setup */
|
||||
dw->all_chan_mask = (1 << nr_channels) - 1;
|
||||
|
||||
/* force dma off, just in case */
|
||||
/* Force dma off, just in case */
|
||||
dw_dma_off(dw);
|
||||
|
||||
/* disable BLOCK interrupts as well */
|
||||
/* Disable BLOCK interrupts as well */
|
||||
channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask);
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, dw_dma_interrupt, 0,
|
||||
@ -1741,7 +1753,7 @@ static int dw_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, dw);
|
||||
|
||||
/* create a pool of consistent memory blocks for hardware descriptors */
|
||||
/* Create a pool of consistent memory blocks for hardware descriptors */
|
||||
dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev,
|
||||
sizeof(struct dw_desc), 4, 0);
|
||||
if (!dw->desc_pool) {
|
||||
@ -1781,8 +1793,9 @@ static int dw_probe(struct platform_device *pdev)
|
||||
channel_clear_bit(dw, CH_EN, dwc->mask);
|
||||
|
||||
dwc->direction = DMA_TRANS_NONE;
|
||||
dwc->request_line = ~0;
|
||||
|
||||
/* hardware configuration */
|
||||
/* Hardware configuration */
|
||||
if (autocfg) {
|
||||
unsigned int dwc_params;
|
||||
|
||||
@ -1842,12 +1855,15 @@ static int dw_probe(struct platform_device *pdev)
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
err = of_dma_controller_register(pdev->dev.of_node,
|
||||
dw_dma_xlate, dw);
|
||||
if (err && err != -ENODEV)
|
||||
dw_dma_of_xlate, dw);
|
||||
if (err)
|
||||
dev_err(&pdev->dev,
|
||||
"could not register of_dma_controller\n");
|
||||
}
|
||||
|
||||
if (ACPI_HANDLE(&pdev->dev))
|
||||
dw_dma_acpi_controller_register(dw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1912,18 +1928,19 @@ static const struct dev_pm_ops dw_dev_pm_ops = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id dw_dma_id_table[] = {
|
||||
static const struct of_device_id dw_dma_of_id_table[] = {
|
||||
{ .compatible = "snps,dma-spear1340" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_dma_id_table);
|
||||
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
|
||||
#endif
|
||||
|
||||
static const struct platform_device_id dw_dma_ids[] = {
|
||||
/* Name, Request Line Base */
|
||||
{ "INTL9C60", (kernel_ulong_t)16 },
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id dw_dma_acpi_id_table[] = {
|
||||
{ "INTL9C60", 0 },
|
||||
{ }
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver dw_driver = {
|
||||
.probe = dw_probe,
|
||||
@ -1932,9 +1949,9 @@ static struct platform_driver dw_driver = {
|
||||
.driver = {
|
||||
.name = "dw_dmac",
|
||||
.pm = &dw_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(dw_dma_id_table),
|
||||
.of_match_table = of_match_ptr(dw_dma_of_id_table),
|
||||
.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
|
||||
},
|
||||
.id_table = dw_dma_ids,
|
||||
};
|
||||
|
||||
static int __init dw_init(void)
|
||||
|
@ -212,8 +212,11 @@ struct dw_dma_chan {
|
||||
/* hardware configuration */
|
||||
unsigned int block_size;
|
||||
bool nollp;
|
||||
|
||||
/* custom slave configuration */
|
||||
unsigned int request_line;
|
||||
struct dw_dma_slave slave;
|
||||
unsigned char src_master;
|
||||
unsigned char dst_master;
|
||||
|
||||
/* configuration passed via DMA_SLAVE_CONFIG */
|
||||
struct dma_slave_config dma_sconfig;
|
||||
@ -247,7 +250,6 @@ struct dw_dma {
|
||||
/* hardware configuration */
|
||||
unsigned char nr_masters;
|
||||
unsigned char data_width[4];
|
||||
unsigned int request_line_base;
|
||||
|
||||
struct dw_dma_chan chan[0];
|
||||
};
|
||||
|
@ -859,8 +859,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
|
||||
|
||||
desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
|
||||
|
||||
if (imxdmac->sg_list)
|
||||
kfree(imxdmac->sg_list);
|
||||
kfree(imxdmac->sg_list);
|
||||
|
||||
imxdmac->sg_list = kcalloc(periods + 1,
|
||||
sizeof(struct scatterlist), GFP_KERNEL);
|
||||
@ -1145,7 +1144,7 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit imxdma_remove(struct platform_device *pdev)
|
||||
static int imxdma_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1162,7 +1161,7 @@ static struct platform_driver imxdma_driver = {
|
||||
.name = "imx-dma",
|
||||
},
|
||||
.id_table = imx_dma_devtype,
|
||||
.remove = __exit_p(imxdma_remove),
|
||||
.remove = imxdma_remove,
|
||||
};
|
||||
|
||||
static int __init imxdma_module_init(void)
|
||||
|
@ -1462,7 +1462,7 @@ err_irq:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit sdma_remove(struct platform_device *pdev)
|
||||
static int sdma_remove(struct platform_device *pdev)
|
||||
{
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -1473,7 +1473,7 @@ static struct platform_driver sdma_driver = {
|
||||
.of_match_table = sdma_dt_ids,
|
||||
},
|
||||
.id_table = sdma_devtypes,
|
||||
.remove = __exit_p(sdma_remove),
|
||||
.remove = sdma_remove,
|
||||
};
|
||||
|
||||
static int __init sdma_module_init(void)
|
||||
|
@ -892,7 +892,7 @@ MODULE_PARM_DESC(ioat_interrupt_style,
|
||||
* ioat_dma_setup_interrupts - setup interrupt handler
|
||||
* @device: ioat device
|
||||
*/
|
||||
static int ioat_dma_setup_interrupts(struct ioatdma_device *device)
|
||||
int ioat_dma_setup_interrupts(struct ioatdma_device *device)
|
||||
{
|
||||
struct ioat_chan_common *chan;
|
||||
struct pci_dev *pdev = device->pdev;
|
||||
@ -941,6 +941,7 @@ msix:
|
||||
}
|
||||
}
|
||||
intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL;
|
||||
device->irq_mode = IOAT_MSIX;
|
||||
goto done;
|
||||
|
||||
msix_single_vector:
|
||||
@ -956,6 +957,7 @@ msix_single_vector:
|
||||
pci_disable_msix(pdev);
|
||||
goto msi;
|
||||
}
|
||||
device->irq_mode = IOAT_MSIX_SINGLE;
|
||||
goto done;
|
||||
|
||||
msi:
|
||||
@ -969,6 +971,7 @@ msi:
|
||||
pci_disable_msi(pdev);
|
||||
goto intx;
|
||||
}
|
||||
device->irq_mode = IOAT_MSIX;
|
||||
goto done;
|
||||
|
||||
intx:
|
||||
@ -977,6 +980,7 @@ intx:
|
||||
if (err)
|
||||
goto err_no_irq;
|
||||
|
||||
device->irq_mode = IOAT_INTX;
|
||||
done:
|
||||
if (device->intr_quirk)
|
||||
device->intr_quirk(device);
|
||||
@ -987,9 +991,11 @@ done:
|
||||
err_no_irq:
|
||||
/* Disable all interrupt generation */
|
||||
writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET);
|
||||
device->irq_mode = IOAT_NOIRQ;
|
||||
dev_err(dev, "no usable interrupts\n");
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(ioat_dma_setup_interrupts);
|
||||
|
||||
static void ioat_disable_interrupts(struct ioatdma_device *device)
|
||||
{
|
||||
|
@ -39,6 +39,7 @@
|
||||
#define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node)
|
||||
#define tx_to_ioat_desc(tx) container_of(tx, struct ioat_desc_sw, txd)
|
||||
#define to_dev(ioat_chan) (&(ioat_chan)->device->pdev->dev)
|
||||
#define to_pdev(ioat_chan) ((ioat_chan)->device->pdev)
|
||||
|
||||
#define chan_num(ch) ((int)((ch)->reg_base - (ch)->device->reg_base) / 0x80)
|
||||
|
||||
@ -48,6 +49,14 @@
|
||||
*/
|
||||
#define NULL_DESC_BUFFER_SIZE 1
|
||||
|
||||
enum ioat_irq_mode {
|
||||
IOAT_NOIRQ = 0,
|
||||
IOAT_MSIX,
|
||||
IOAT_MSIX_SINGLE,
|
||||
IOAT_MSI,
|
||||
IOAT_INTX
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ioatdma_device - internal representation of a IOAT device
|
||||
* @pdev: PCI-Express device
|
||||
@ -72,11 +81,16 @@ struct ioatdma_device {
|
||||
void __iomem *reg_base;
|
||||
struct pci_pool *dma_pool;
|
||||
struct pci_pool *completion_pool;
|
||||
#define MAX_SED_POOLS 5
|
||||
struct dma_pool *sed_hw_pool[MAX_SED_POOLS];
|
||||
struct kmem_cache *sed_pool;
|
||||
struct dma_device common;
|
||||
u8 version;
|
||||
struct msix_entry msix_entries[4];
|
||||
struct ioat_chan_common *idx[4];
|
||||
struct dca_provider *dca;
|
||||
enum ioat_irq_mode irq_mode;
|
||||
u32 cap;
|
||||
void (*intr_quirk)(struct ioatdma_device *device);
|
||||
int (*enumerate_channels)(struct ioatdma_device *device);
|
||||
int (*reset_hw)(struct ioat_chan_common *chan);
|
||||
@ -131,6 +145,20 @@ struct ioat_dma_chan {
|
||||
u16 active;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ioat_sed_ent - wrapper around super extended hardware descriptor
|
||||
* @hw: hardware SED
|
||||
* @sed_dma: dma address for the SED
|
||||
* @list: list member
|
||||
* @parent: point to the dma descriptor that's the parent
|
||||
*/
|
||||
struct ioat_sed_ent {
|
||||
struct ioat_sed_raw_descriptor *hw;
|
||||
dma_addr_t dma;
|
||||
struct ioat_ring_ent *parent;
|
||||
unsigned int hw_pool;
|
||||
};
|
||||
|
||||
static inline struct ioat_chan_common *to_chan_common(struct dma_chan *c)
|
||||
{
|
||||
return container_of(c, struct ioat_chan_common, common);
|
||||
@ -179,7 +207,7 @@ __dump_desc_dbg(struct ioat_chan_common *chan, struct ioat_dma_descriptor *hw,
|
||||
struct device *dev = to_dev(chan);
|
||||
|
||||
dev_dbg(dev, "desc[%d]: (%#llx->%#llx) cookie: %d flags: %#x"
|
||||
" ctl: %#x (op: %d int_en: %d compl: %d)\n", id,
|
||||
" ctl: %#10.8x (op: %#x int_en: %d compl: %d)\n", id,
|
||||
(unsigned long long) tx->phys,
|
||||
(unsigned long long) hw->next, tx->cookie, tx->flags,
|
||||
hw->ctl, hw->ctl_f.op, hw->ctl_f.int_en, hw->ctl_f.compl_write);
|
||||
@ -201,7 +229,7 @@ ioat_chan_by_index(struct ioatdma_device *device, int index)
|
||||
return device->idx[index];
|
||||
}
|
||||
|
||||
static inline u64 ioat_chansts(struct ioat_chan_common *chan)
|
||||
static inline u64 ioat_chansts_32(struct ioat_chan_common *chan)
|
||||
{
|
||||
u8 ver = chan->device->version;
|
||||
u64 status;
|
||||
@ -218,6 +246,26 @@ static inline u64 ioat_chansts(struct ioat_chan_common *chan)
|
||||
return status;
|
||||
}
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
|
||||
static inline u64 ioat_chansts(struct ioat_chan_common *chan)
|
||||
{
|
||||
u8 ver = chan->device->version;
|
||||
u64 status;
|
||||
|
||||
/* With IOAT v3.3 the status register is 64bit. */
|
||||
if (ver >= IOAT_VER_3_3)
|
||||
status = readq(chan->reg_base + IOAT_CHANSTS_OFFSET(ver));
|
||||
else
|
||||
status = ioat_chansts_32(chan);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
#define ioat_chansts ioat_chansts_32
|
||||
#endif
|
||||
|
||||
static inline void ioat_start(struct ioat_chan_common *chan)
|
||||
{
|
||||
u8 ver = chan->device->version;
|
||||
@ -321,6 +369,7 @@ bool ioat_cleanup_preamble(struct ioat_chan_common *chan,
|
||||
dma_addr_t *phys_complete);
|
||||
void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type);
|
||||
void ioat_kobject_del(struct ioatdma_device *device);
|
||||
int ioat_dma_setup_interrupts(struct ioatdma_device *device);
|
||||
extern const struct sysfs_ops ioat_sysfs_ops;
|
||||
extern struct ioat_sysfs_entry ioat_version_attr;
|
||||
extern struct ioat_sysfs_entry ioat_cap_attr;
|
||||
|
@ -137,6 +137,7 @@ struct ioat_ring_ent {
|
||||
#ifdef DEBUG
|
||||
int id;
|
||||
#endif
|
||||
struct ioat_sed_ent *sed;
|
||||
};
|
||||
|
||||
static inline struct ioat_ring_ent *
|
||||
@ -157,6 +158,7 @@ static inline void ioat2_set_chainaddr(struct ioat2_dma_chan *ioat, u64 addr)
|
||||
|
||||
int ioat2_dma_probe(struct ioatdma_device *dev, int dca);
|
||||
int ioat3_dma_probe(struct ioatdma_device *dev, int dca);
|
||||
void ioat3_dma_remove(struct ioatdma_device *dev);
|
||||
struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase);
|
||||
struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase);
|
||||
int ioat2_check_space_lock(struct ioat2_dma_chan *ioat, int num_descs);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,11 +30,6 @@
|
||||
#define IOAT_PCI_DID_SCNB 0x65FF
|
||||
#define IOAT_PCI_DID_SNB 0x402F
|
||||
|
||||
#define IOAT_VER_1_2 0x12 /* Version 1.2 */
|
||||
#define IOAT_VER_2_0 0x20 /* Version 2.0 */
|
||||
#define IOAT_VER_3_0 0x30 /* Version 3.0 */
|
||||
#define IOAT_VER_3_2 0x32 /* Version 3.2 */
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_IVB0 0x0e20
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_IVB1 0x0e21
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_IVB2 0x0e22
|
||||
@ -46,6 +41,29 @@
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_IVB8 0x0e2e
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_IVB9 0x0e2f
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW0 0x2f20
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW1 0x2f21
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW2 0x2f22
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW3 0x2f23
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW4 0x2f24
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW5 0x2f25
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW6 0x2f26
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW7 0x2f27
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW8 0x2f2e
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_HSW9 0x2f2f
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_BWD0 0x0C50
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_BWD1 0x0C51
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_BWD2 0x0C52
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_BWD3 0x0C53
|
||||
|
||||
#define IOAT_VER_1_2 0x12 /* Version 1.2 */
|
||||
#define IOAT_VER_2_0 0x20 /* Version 2.0 */
|
||||
#define IOAT_VER_3_0 0x30 /* Version 3.0 */
|
||||
#define IOAT_VER_3_2 0x32 /* Version 3.2 */
|
||||
#define IOAT_VER_3_3 0x33 /* Version 3.3 */
|
||||
|
||||
|
||||
int system_has_dca_enabled(struct pci_dev *pdev);
|
||||
|
||||
struct ioat_dma_descriptor {
|
||||
@ -147,7 +165,17 @@ struct ioat_xor_ext_descriptor {
|
||||
};
|
||||
|
||||
struct ioat_pq_descriptor {
|
||||
uint32_t size;
|
||||
union {
|
||||
uint32_t size;
|
||||
uint32_t dwbes;
|
||||
struct {
|
||||
unsigned int rsvd:25;
|
||||
unsigned int p_val_err:1;
|
||||
unsigned int q_val_err:1;
|
||||
unsigned int rsvd1:4;
|
||||
unsigned int wbes:1;
|
||||
} dwbes_f;
|
||||
};
|
||||
union {
|
||||
uint32_t ctl;
|
||||
struct {
|
||||
@ -162,9 +190,14 @@ struct ioat_pq_descriptor {
|
||||
unsigned int hint:1;
|
||||
unsigned int p_disable:1;
|
||||
unsigned int q_disable:1;
|
||||
unsigned int rsvd:11;
|
||||
unsigned int rsvd2:2;
|
||||
unsigned int wb_en:1;
|
||||
unsigned int prl_en:1;
|
||||
unsigned int rsvd3:7;
|
||||
#define IOAT_OP_PQ 0x89
|
||||
#define IOAT_OP_PQ_VAL 0x8a
|
||||
#define IOAT_OP_PQ_16S 0xa0
|
||||
#define IOAT_OP_PQ_VAL_16S 0xa1
|
||||
unsigned int op:8;
|
||||
} ctl_f;
|
||||
};
|
||||
@ -172,7 +205,10 @@ struct ioat_pq_descriptor {
|
||||
uint64_t p_addr;
|
||||
uint64_t next;
|
||||
uint64_t src_addr2;
|
||||
uint64_t src_addr3;
|
||||
union {
|
||||
uint64_t src_addr3;
|
||||
uint64_t sed_addr;
|
||||
};
|
||||
uint8_t coef[8];
|
||||
uint64_t q_addr;
|
||||
};
|
||||
@ -221,4 +257,40 @@ struct ioat_pq_update_descriptor {
|
||||
struct ioat_raw_descriptor {
|
||||
uint64_t field[8];
|
||||
};
|
||||
|
||||
struct ioat_pq16a_descriptor {
|
||||
uint8_t coef[8];
|
||||
uint64_t src_addr3;
|
||||
uint64_t src_addr4;
|
||||
uint64_t src_addr5;
|
||||
uint64_t src_addr6;
|
||||
uint64_t src_addr7;
|
||||
uint64_t src_addr8;
|
||||
uint64_t src_addr9;
|
||||
};
|
||||
|
||||
struct ioat_pq16b_descriptor {
|
||||
uint64_t src_addr10;
|
||||
uint64_t src_addr11;
|
||||
uint64_t src_addr12;
|
||||
uint64_t src_addr13;
|
||||
uint64_t src_addr14;
|
||||
uint64_t src_addr15;
|
||||
uint64_t src_addr16;
|
||||
uint64_t rsvd;
|
||||
};
|
||||
|
||||
union ioat_sed_pq_descriptor {
|
||||
struct ioat_pq16a_descriptor a;
|
||||
struct ioat_pq16b_descriptor b;
|
||||
};
|
||||
|
||||
#define SED_SIZE 64
|
||||
|
||||
struct ioat_sed_raw_descriptor {
|
||||
uint64_t a[8];
|
||||
uint64_t b[8];
|
||||
uint64_t c[8];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -94,6 +94,23 @@ static struct pci_device_id ioat_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB8) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_IVB9) },
|
||||
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW0) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW1) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW2) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW3) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW4) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW5) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW6) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW7) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW8) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_HSW9) },
|
||||
|
||||
/* I/OAT v3.3 platforms */
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD0) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD1) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD2) },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_BWD3) },
|
||||
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ioat_pci_tbl);
|
||||
@ -190,6 +207,9 @@ static void ioat_remove(struct pci_dev *pdev)
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (device->version >= IOAT_VER_3_0)
|
||||
ioat3_dma_remove(device);
|
||||
|
||||
dev_err(&pdev->dev, "Removing dma and dca services\n");
|
||||
if (device->dca) {
|
||||
unregister_dca_provider(device->dca, &pdev->dev);
|
||||
|
@ -79,6 +79,8 @@
|
||||
#define IOAT_CAP_APIC 0x00000080
|
||||
#define IOAT_CAP_XOR 0x00000100
|
||||
#define IOAT_CAP_PQ 0x00000200
|
||||
#define IOAT_CAP_DWBES 0x00002000
|
||||
#define IOAT_CAP_RAID16SS 0x00020000
|
||||
|
||||
#define IOAT_CHANNEL_MMIO_SIZE 0x80 /* Each Channel MMIO space is this size */
|
||||
|
||||
@ -93,6 +95,8 @@
|
||||
#define IOAT_CHANCTRL_ERR_COMPLETION_EN 0x0004
|
||||
#define IOAT_CHANCTRL_INT_REARM 0x0001
|
||||
#define IOAT_CHANCTRL_RUN (IOAT_CHANCTRL_INT_REARM |\
|
||||
IOAT_CHANCTRL_ERR_INT_EN |\
|
||||
IOAT_CHANCTRL_ERR_COMPLETION_EN |\
|
||||
IOAT_CHANCTRL_ANY_ERR_ABORT_EN)
|
||||
|
||||
#define IOAT_DMA_COMP_OFFSET 0x02 /* 16-bit DMA channel compatibility */
|
||||
|
@ -1642,7 +1642,7 @@ static int __init ipu_idmac_init(struct ipu *ipu)
|
||||
return dma_async_device_register(&idmac->dma);
|
||||
}
|
||||
|
||||
static void __exit ipu_idmac_exit(struct ipu *ipu)
|
||||
static void ipu_idmac_exit(struct ipu *ipu)
|
||||
{
|
||||
int i;
|
||||
struct idmac *idmac = &ipu->idmac;
|
||||
@ -1756,7 +1756,7 @@ err_noirq:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit ipu_remove(struct platform_device *pdev)
|
||||
static int ipu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ipu *ipu = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1781,7 +1781,7 @@ static struct platform_driver ipu_platform_driver = {
|
||||
.name = "ipu-core",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = __exit_p(ipu_remove),
|
||||
.remove = ipu_remove,
|
||||
};
|
||||
|
||||
static int __init ipu_init(void)
|
||||
|
@ -13,43 +13,31 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_dma.h>
|
||||
|
||||
static LIST_HEAD(of_dma_list);
|
||||
static DEFINE_SPINLOCK(of_dma_lock);
|
||||
static DEFINE_MUTEX(of_dma_lock);
|
||||
|
||||
/**
|
||||
* of_dma_get_controller - Get a DMA controller in DT DMA helpers list
|
||||
* of_dma_find_controller - Get a DMA controller in DT DMA helpers list
|
||||
* @dma_spec: pointer to DMA specifier as found in the device tree
|
||||
*
|
||||
* Finds a DMA controller with matching device node and number for dma cells
|
||||
* in a list of registered DMA controllers. If a match is found the use_count
|
||||
* variable is increased and a valid pointer to the DMA data stored is retuned.
|
||||
* A NULL pointer is returned if no match is found.
|
||||
* in a list of registered DMA controllers. If a match is found a valid pointer
|
||||
* to the DMA data stored is retuned. A NULL pointer is returned if no match is
|
||||
* found.
|
||||
*/
|
||||
static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec)
|
||||
static struct of_dma *of_dma_find_controller(struct of_phandle_args *dma_spec)
|
||||
{
|
||||
struct of_dma *ofdma;
|
||||
|
||||
spin_lock(&of_dma_lock);
|
||||
|
||||
if (list_empty(&of_dma_list)) {
|
||||
spin_unlock(&of_dma_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers)
|
||||
if ((ofdma->of_node == dma_spec->np) &&
|
||||
(ofdma->of_dma_nbcells == dma_spec->args_count)) {
|
||||
ofdma->use_count++;
|
||||
spin_unlock(&of_dma_lock);
|
||||
(ofdma->of_dma_nbcells == dma_spec->args_count))
|
||||
return ofdma;
|
||||
}
|
||||
|
||||
spin_unlock(&of_dma_lock);
|
||||
|
||||
pr_debug("%s: can't find DMA controller %s\n", __func__,
|
||||
dma_spec->np->full_name);
|
||||
@ -57,22 +45,6 @@ static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_dma_put_controller - Decrement use count for a registered DMA controller
|
||||
* @of_dma: pointer to DMA controller data
|
||||
*
|
||||
* Decrements the use_count variable in the DMA data structure. This function
|
||||
* should be called only when a valid pointer is returned from
|
||||
* of_dma_get_controller() and no further accesses to data referenced by that
|
||||
* pointer are needed.
|
||||
*/
|
||||
static void of_dma_put_controller(struct of_dma *ofdma)
|
||||
{
|
||||
spin_lock(&of_dma_lock);
|
||||
ofdma->use_count--;
|
||||
spin_unlock(&of_dma_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_dma_controller_register - Register a DMA controller to DT DMA helpers
|
||||
* @np: device node of DMA controller
|
||||
@ -93,6 +65,7 @@ int of_dma_controller_register(struct device_node *np,
|
||||
{
|
||||
struct of_dma *ofdma;
|
||||
int nbcells;
|
||||
const __be32 *prop;
|
||||
|
||||
if (!np || !of_dma_xlate) {
|
||||
pr_err("%s: not enough information provided\n", __func__);
|
||||
@ -103,8 +76,11 @@ int of_dma_controller_register(struct device_node *np,
|
||||
if (!ofdma)
|
||||
return -ENOMEM;
|
||||
|
||||
nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL));
|
||||
if (!nbcells) {
|
||||
prop = of_get_property(np, "#dma-cells", NULL);
|
||||
if (prop)
|
||||
nbcells = be32_to_cpup(prop);
|
||||
|
||||
if (!prop || !nbcells) {
|
||||
pr_err("%s: #dma-cells property is missing or invalid\n",
|
||||
__func__);
|
||||
kfree(ofdma);
|
||||
@ -115,12 +91,11 @@ int of_dma_controller_register(struct device_node *np,
|
||||
ofdma->of_dma_nbcells = nbcells;
|
||||
ofdma->of_dma_xlate = of_dma_xlate;
|
||||
ofdma->of_dma_data = data;
|
||||
ofdma->use_count = 0;
|
||||
|
||||
/* Now queue of_dma controller structure in list */
|
||||
spin_lock(&of_dma_lock);
|
||||
mutex_lock(&of_dma_lock);
|
||||
list_add_tail(&ofdma->of_dma_controllers, &of_dma_list);
|
||||
spin_unlock(&of_dma_lock);
|
||||
mutex_unlock(&of_dma_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -132,32 +107,20 @@ EXPORT_SYMBOL_GPL(of_dma_controller_register);
|
||||
*
|
||||
* Memory allocated by of_dma_controller_register() is freed here.
|
||||
*/
|
||||
int of_dma_controller_free(struct device_node *np)
|
||||
void of_dma_controller_free(struct device_node *np)
|
||||
{
|
||||
struct of_dma *ofdma;
|
||||
|
||||
spin_lock(&of_dma_lock);
|
||||
|
||||
if (list_empty(&of_dma_list)) {
|
||||
spin_unlock(&of_dma_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
mutex_lock(&of_dma_lock);
|
||||
|
||||
list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers)
|
||||
if (ofdma->of_node == np) {
|
||||
if (ofdma->use_count) {
|
||||
spin_unlock(&of_dma_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
list_del(&ofdma->of_dma_controllers);
|
||||
spin_unlock(&of_dma_lock);
|
||||
kfree(ofdma);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&of_dma_lock);
|
||||
return -ENODEV;
|
||||
mutex_unlock(&of_dma_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_dma_controller_free);
|
||||
|
||||
@ -172,8 +135,8 @@ EXPORT_SYMBOL_GPL(of_dma_controller_free);
|
||||
* specifiers, matches the name provided. Returns 0 if the name matches and
|
||||
* a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV.
|
||||
*/
|
||||
static int of_dma_match_channel(struct device_node *np, char *name, int index,
|
||||
struct of_phandle_args *dma_spec)
|
||||
static int of_dma_match_channel(struct device_node *np, const char *name,
|
||||
int index, struct of_phandle_args *dma_spec)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
@ -198,7 +161,7 @@ static int of_dma_match_channel(struct device_node *np, char *name, int index,
|
||||
* Returns pointer to appropriate dma channel on success or NULL on error.
|
||||
*/
|
||||
struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
|
||||
char *name)
|
||||
const char *name)
|
||||
{
|
||||
struct of_phandle_args dma_spec;
|
||||
struct of_dma *ofdma;
|
||||
@ -220,14 +183,15 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
|
||||
if (of_dma_match_channel(np, name, i, &dma_spec))
|
||||
continue;
|
||||
|
||||
ofdma = of_dma_get_controller(&dma_spec);
|
||||
mutex_lock(&of_dma_lock);
|
||||
ofdma = of_dma_find_controller(&dma_spec);
|
||||
|
||||
if (!ofdma)
|
||||
continue;
|
||||
if (ofdma)
|
||||
chan = ofdma->of_dma_xlate(&dma_spec, ofdma);
|
||||
else
|
||||
chan = NULL;
|
||||
|
||||
chan = ofdma->of_dma_xlate(&dma_spec, ofdma);
|
||||
|
||||
of_dma_put_controller(ofdma);
|
||||
mutex_unlock(&of_dma_lock);
|
||||
|
||||
of_node_put(dma_spec.np);
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "virt-dma.h"
|
||||
|
||||
@ -67,6 +69,10 @@ static const unsigned es_bytes[] = {
|
||||
[OMAP_DMA_DATA_TYPE_S32] = 4,
|
||||
};
|
||||
|
||||
static struct of_dma_filter_info omap_dma_info = {
|
||||
.filter_fn = omap_dma_filter_fn,
|
||||
};
|
||||
|
||||
static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
|
||||
{
|
||||
return container_of(d, struct omap_dmadev, ddev);
|
||||
@ -629,8 +635,22 @@ static int omap_dma_probe(struct platform_device *pdev)
|
||||
pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
|
||||
rc);
|
||||
omap_dma_free(od);
|
||||
} else {
|
||||
platform_set_drvdata(pdev, od);
|
||||
return rc;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, od);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
omap_dma_info.dma_cap = od->ddev.cap_mask;
|
||||
|
||||
/* Device-tree DMA controller registration */
|
||||
rc = of_dma_controller_register(pdev->dev.of_node,
|
||||
of_dma_simple_xlate, &omap_dma_info);
|
||||
if (rc) {
|
||||
pr_warn("OMAP-DMA: failed to register DMA controller\n");
|
||||
dma_async_device_unregister(&od->ddev);
|
||||
omap_dma_free(od);
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "OMAP DMA engine driver\n");
|
||||
@ -642,18 +662,32 @@ static int omap_dma_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_dmadev *od = platform_get_drvdata(pdev);
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
of_dma_controller_free(pdev->dev.of_node);
|
||||
|
||||
dma_async_device_unregister(&od->ddev);
|
||||
omap_dma_free(od);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id omap_dma_match[] = {
|
||||
{ .compatible = "ti,omap2420-sdma", },
|
||||
{ .compatible = "ti,omap2430-sdma", },
|
||||
{ .compatible = "ti,omap3430-sdma", },
|
||||
{ .compatible = "ti,omap3630-sdma", },
|
||||
{ .compatible = "ti,omap4430-sdma", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_dma_match);
|
||||
|
||||
static struct platform_driver omap_dma_driver = {
|
||||
.probe = omap_dma_probe,
|
||||
.remove = omap_dma_remove,
|
||||
.driver = {
|
||||
.name = "omap-dma-engine",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(omap_dma_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -476,7 +476,7 @@ static struct pch_dma_desc *pdc_desc_get(struct pch_dma_chan *pd_chan)
|
||||
dev_dbg(chan2dev(&pd_chan->chan), "scanned %d descriptors\n", i);
|
||||
|
||||
if (!ret) {
|
||||
ret = pdc_alloc_desc(&pd_chan->chan, GFP_NOIO);
|
||||
ret = pdc_alloc_desc(&pd_chan->chan, GFP_ATOMIC);
|
||||
if (ret) {
|
||||
spin_lock(&pd_chan->lock);
|
||||
pd_chan->descs_allocated++;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
#define PL330_MAX_CHAN 8
|
||||
@ -2288,13 +2289,12 @@ static inline void fill_queue(struct dma_pl330_chan *pch)
|
||||
|
||||
/* If already submitted */
|
||||
if (desc->status == BUSY)
|
||||
break;
|
||||
continue;
|
||||
|
||||
ret = pl330_submit_req(pch->pl330_chid,
|
||||
&desc->req);
|
||||
if (!ret) {
|
||||
desc->status = BUSY;
|
||||
break;
|
||||
} else if (ret == -EAGAIN) {
|
||||
/* QFull or DMAC Dying */
|
||||
break;
|
||||
@ -2904,9 +2904,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
|
||||
|
||||
res = &adev->res;
|
||||
pi->base = devm_request_and_ioremap(&adev->dev, res);
|
||||
if (!pi->base)
|
||||
return -ENXIO;
|
||||
pi->base = devm_ioremap_resource(&adev->dev, res);
|
||||
if (IS_ERR(pi->base))
|
||||
return PTR_ERR(pi->base);
|
||||
|
||||
amba_set_drvdata(adev, pdmac);
|
||||
|
||||
|
24
drivers/dma/sh/Kconfig
Normal file
24
drivers/dma/sh/Kconfig
Normal file
@ -0,0 +1,24 @@
|
||||
#
|
||||
# DMA engine configuration for sh
|
||||
#
|
||||
|
||||
config SH_DMAE_BASE
|
||||
bool "Renesas SuperH DMA Engine support"
|
||||
depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
|
||||
depends on !SH_DMA_API
|
||||
default y
|
||||
select DMA_ENGINE
|
||||
help
|
||||
Enable support for the Renesas SuperH DMA controllers.
|
||||
|
||||
config SH_DMAE
|
||||
tristate "Renesas SuperH DMAC support"
|
||||
depends on SH_DMAE_BASE
|
||||
help
|
||||
Enable support for the Renesas SuperH DMA controllers.
|
||||
|
||||
config SUDMAC
|
||||
tristate "Renesas SUDMAC support"
|
||||
depends on SH_DMAE_BASE
|
||||
help
|
||||
Enable support for the Renesas SUDMAC controllers.
|
@ -1,2 +1,3 @@
|
||||
obj-$(CONFIG_SH_DMAE) += shdma-base.o
|
||||
obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o
|
||||
obj-$(CONFIG_SH_DMAE) += shdma.o
|
||||
obj-$(CONFIG_SUDMAC) += sudmac.o
|
||||
|
428
drivers/dma/sh/sudmac.c
Normal file
428
drivers/dma/sh/sudmac.c
Normal file
@ -0,0 +1,428 @@
|
||||
/*
|
||||
* Renesas SUDMAC support
|
||||
*
|
||||
* Copyright (C) 2013 Renesas Solutions Corp.
|
||||
*
|
||||
* based on drivers/dma/sh/shdma.c:
|
||||
* Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
* Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
|
||||
* Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
|
||||
* Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sudmac.h>
|
||||
|
||||
struct sudmac_chan {
|
||||
struct shdma_chan shdma_chan;
|
||||
void __iomem *base;
|
||||
char dev_id[16]; /* unique name per DMAC of channel */
|
||||
|
||||
u32 offset; /* for CFG, BA, BBC, CA, CBC, DEN */
|
||||
u32 cfg;
|
||||
u32 dint_end_bit;
|
||||
};
|
||||
|
||||
struct sudmac_device {
|
||||
struct shdma_dev shdma_dev;
|
||||
struct sudmac_pdata *pdata;
|
||||
void __iomem *chan_reg;
|
||||
};
|
||||
|
||||
struct sudmac_regs {
|
||||
u32 base_addr;
|
||||
u32 base_byte_count;
|
||||
};
|
||||
|
||||
struct sudmac_desc {
|
||||
struct sudmac_regs hw;
|
||||
struct shdma_desc shdma_desc;
|
||||
};
|
||||
|
||||
#define to_chan(schan) container_of(schan, struct sudmac_chan, shdma_chan)
|
||||
#define to_desc(sdesc) container_of(sdesc, struct sudmac_desc, shdma_desc)
|
||||
#define to_sdev(sc) container_of(sc->shdma_chan.dma_chan.device, \
|
||||
struct sudmac_device, shdma_dev.dma_dev)
|
||||
|
||||
/* SUDMAC register */
|
||||
#define SUDMAC_CH0CFG 0x00
|
||||
#define SUDMAC_CH0BA 0x10
|
||||
#define SUDMAC_CH0BBC 0x18
|
||||
#define SUDMAC_CH0CA 0x20
|
||||
#define SUDMAC_CH0CBC 0x28
|
||||
#define SUDMAC_CH0DEN 0x30
|
||||
#define SUDMAC_DSTSCLR 0x38
|
||||
#define SUDMAC_DBUFCTRL 0x3C
|
||||
#define SUDMAC_DINTCTRL 0x40
|
||||
#define SUDMAC_DINTSTS 0x44
|
||||
#define SUDMAC_DINTSTSCLR 0x48
|
||||
#define SUDMAC_CH0SHCTRL 0x50
|
||||
|
||||
/* Definitions for the sudmac_channel.config */
|
||||
#define SUDMAC_SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */
|
||||
#define SUDMAC_RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */
|
||||
#define SUDMAC_LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */
|
||||
|
||||
/* Definitions for the sudmac_channel.dint_end_bit */
|
||||
#define SUDMAC_CH1ENDE 0x0002 /* b1: Ch1 DMA Transfer End Int Enable */
|
||||
#define SUDMAC_CH0ENDE 0x0001 /* b0: Ch0 DMA Transfer End Int Enable */
|
||||
|
||||
#define SUDMAC_DRV_NAME "sudmac"
|
||||
|
||||
static void sudmac_writel(struct sudmac_chan *sc, u32 data, u32 reg)
|
||||
{
|
||||
iowrite32(data, sc->base + reg);
|
||||
}
|
||||
|
||||
static u32 sudmac_readl(struct sudmac_chan *sc, u32 reg)
|
||||
{
|
||||
return ioread32(sc->base + reg);
|
||||
}
|
||||
|
||||
static bool sudmac_is_busy(struct sudmac_chan *sc)
|
||||
{
|
||||
u32 den = sudmac_readl(sc, SUDMAC_CH0DEN + sc->offset);
|
||||
|
||||
if (den)
|
||||
return true; /* working */
|
||||
|
||||
return false; /* waiting */
|
||||
}
|
||||
|
||||
static void sudmac_set_reg(struct sudmac_chan *sc, struct sudmac_regs *hw,
|
||||
struct shdma_desc *sdesc)
|
||||
{
|
||||
sudmac_writel(sc, sc->cfg, SUDMAC_CH0CFG + sc->offset);
|
||||
sudmac_writel(sc, hw->base_addr, SUDMAC_CH0BA + sc->offset);
|
||||
sudmac_writel(sc, hw->base_byte_count, SUDMAC_CH0BBC + sc->offset);
|
||||
}
|
||||
|
||||
static void sudmac_start(struct sudmac_chan *sc)
|
||||
{
|
||||
u32 dintctrl = sudmac_readl(sc, SUDMAC_DINTCTRL);
|
||||
|
||||
sudmac_writel(sc, dintctrl | sc->dint_end_bit, SUDMAC_DINTCTRL);
|
||||
sudmac_writel(sc, 1, SUDMAC_CH0DEN + sc->offset);
|
||||
}
|
||||
|
||||
static void sudmac_start_xfer(struct shdma_chan *schan,
|
||||
struct shdma_desc *sdesc)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
struct sudmac_desc *sd = to_desc(sdesc);
|
||||
|
||||
sudmac_set_reg(sc, &sd->hw, sdesc);
|
||||
sudmac_start(sc);
|
||||
}
|
||||
|
||||
static bool sudmac_channel_busy(struct shdma_chan *schan)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
|
||||
return sudmac_is_busy(sc);
|
||||
}
|
||||
|
||||
static void sudmac_setup_xfer(struct shdma_chan *schan, int slave_id)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct sudmac_slave_config *sudmac_find_slave(
|
||||
struct sudmac_chan *sc, int slave_id)
|
||||
{
|
||||
struct sudmac_device *sdev = to_sdev(sc);
|
||||
struct sudmac_pdata *pdata = sdev->pdata;
|
||||
const struct sudmac_slave_config *cfg;
|
||||
int i;
|
||||
|
||||
for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
|
||||
if (cfg->slave_id == slave_id)
|
||||
return cfg;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sudmac_set_slave(struct shdma_chan *schan, int slave_id, bool try)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
const struct sudmac_slave_config *cfg = sudmac_find_slave(sc, slave_id);
|
||||
|
||||
if (!cfg)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sudmac_dma_halt(struct sudmac_chan *sc)
|
||||
{
|
||||
u32 dintctrl = sudmac_readl(sc, SUDMAC_DINTCTRL);
|
||||
|
||||
sudmac_writel(sc, 0, SUDMAC_CH0DEN + sc->offset);
|
||||
sudmac_writel(sc, dintctrl & ~sc->dint_end_bit, SUDMAC_DINTCTRL);
|
||||
sudmac_writel(sc, sc->dint_end_bit, SUDMAC_DINTSTSCLR);
|
||||
}
|
||||
|
||||
static int sudmac_desc_setup(struct shdma_chan *schan,
|
||||
struct shdma_desc *sdesc,
|
||||
dma_addr_t src, dma_addr_t dst, size_t *len)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
struct sudmac_desc *sd = to_desc(sdesc);
|
||||
|
||||
dev_dbg(sc->shdma_chan.dev, "%s: src=%x, dst=%x, len=%d\n",
|
||||
__func__, src, dst, *len);
|
||||
|
||||
if (*len > schan->max_xfer_len)
|
||||
*len = schan->max_xfer_len;
|
||||
|
||||
if (dst)
|
||||
sd->hw.base_addr = dst;
|
||||
else if (src)
|
||||
sd->hw.base_addr = src;
|
||||
sd->hw.base_byte_count = *len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sudmac_halt(struct shdma_chan *schan)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
|
||||
sudmac_dma_halt(sc);
|
||||
}
|
||||
|
||||
static bool sudmac_chan_irq(struct shdma_chan *schan, int irq)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
u32 dintsts = sudmac_readl(sc, SUDMAC_DINTSTS);
|
||||
|
||||
if (!(dintsts & sc->dint_end_bit))
|
||||
return false;
|
||||
|
||||
/* DMA stop */
|
||||
sudmac_dma_halt(sc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t sudmac_get_partial(struct shdma_chan *schan,
|
||||
struct shdma_desc *sdesc)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
struct sudmac_desc *sd = to_desc(sdesc);
|
||||
u32 current_byte_count = sudmac_readl(sc, SUDMAC_CH0CBC + sc->offset);
|
||||
|
||||
return sd->hw.base_byte_count - current_byte_count;
|
||||
}
|
||||
|
||||
static bool sudmac_desc_completed(struct shdma_chan *schan,
|
||||
struct shdma_desc *sdesc)
|
||||
{
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
struct sudmac_desc *sd = to_desc(sdesc);
|
||||
u32 current_addr = sudmac_readl(sc, SUDMAC_CH0CA + sc->offset);
|
||||
|
||||
return sd->hw.base_addr + sd->hw.base_byte_count == current_addr;
|
||||
}
|
||||
|
||||
static int sudmac_chan_probe(struct sudmac_device *su_dev, int id, int irq,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct shdma_dev *sdev = &su_dev->shdma_dev;
|
||||
struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev);
|
||||
struct sudmac_chan *sc;
|
||||
struct shdma_chan *schan;
|
||||
int err;
|
||||
|
||||
sc = devm_kzalloc(&pdev->dev, sizeof(struct sudmac_chan), GFP_KERNEL);
|
||||
if (!sc) {
|
||||
dev_err(sdev->dma_dev.dev,
|
||||
"No free memory for allocating dma channels!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
schan = &sc->shdma_chan;
|
||||
schan->max_xfer_len = 64 * 1024 * 1024 - 1;
|
||||
|
||||
shdma_chan_probe(sdev, schan, id);
|
||||
|
||||
sc->base = su_dev->chan_reg;
|
||||
|
||||
/* get platform_data */
|
||||
sc->offset = su_dev->pdata->channel->offset;
|
||||
if (su_dev->pdata->channel->config & SUDMAC_TX_BUFFER_MODE)
|
||||
sc->cfg |= SUDMAC_SENDBUFM;
|
||||
if (su_dev->pdata->channel->config & SUDMAC_RX_END_MODE)
|
||||
sc->cfg |= SUDMAC_RCVENDM;
|
||||
sc->cfg |= (su_dev->pdata->channel->wait << 4) & SUDMAC_LBA_WAIT;
|
||||
|
||||
if (su_dev->pdata->channel->dint_end_bit & SUDMAC_DMA_BIT_CH0)
|
||||
sc->dint_end_bit |= SUDMAC_CH0ENDE;
|
||||
if (su_dev->pdata->channel->dint_end_bit & SUDMAC_DMA_BIT_CH1)
|
||||
sc->dint_end_bit |= SUDMAC_CH1ENDE;
|
||||
|
||||
/* set up channel irq */
|
||||
if (pdev->id >= 0)
|
||||
snprintf(sc->dev_id, sizeof(sc->dev_id), "sudmac%d.%d",
|
||||
pdev->id, id);
|
||||
else
|
||||
snprintf(sc->dev_id, sizeof(sc->dev_id), "sudmac%d", id);
|
||||
|
||||
err = shdma_request_irq(schan, irq, flags, sc->dev_id);
|
||||
if (err) {
|
||||
dev_err(sdev->dma_dev.dev,
|
||||
"DMA channel %d request_irq failed %d\n", id, err);
|
||||
goto err_no_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_no_irq:
|
||||
/* remove from dmaengine device node */
|
||||
shdma_chan_remove(schan);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sudmac_chan_remove(struct sudmac_device *su_dev)
|
||||
{
|
||||
struct dma_device *dma_dev = &su_dev->shdma_dev.dma_dev;
|
||||
struct shdma_chan *schan;
|
||||
int i;
|
||||
|
||||
shdma_for_each_chan(schan, &su_dev->shdma_dev, i) {
|
||||
struct sudmac_chan *sc = to_chan(schan);
|
||||
|
||||
BUG_ON(!schan);
|
||||
|
||||
shdma_free_irq(&sc->shdma_chan);
|
||||
shdma_chan_remove(schan);
|
||||
}
|
||||
dma_dev->chancnt = 0;
|
||||
}
|
||||
|
||||
static dma_addr_t sudmac_slave_addr(struct shdma_chan *schan)
|
||||
{
|
||||
/* SUDMAC doesn't need the address */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct shdma_desc *sudmac_embedded_desc(void *buf, int i)
|
||||
{
|
||||
return &((struct sudmac_desc *)buf)[i].shdma_desc;
|
||||
}
|
||||
|
||||
static const struct shdma_ops sudmac_shdma_ops = {
|
||||
.desc_completed = sudmac_desc_completed,
|
||||
.halt_channel = sudmac_halt,
|
||||
.channel_busy = sudmac_channel_busy,
|
||||
.slave_addr = sudmac_slave_addr,
|
||||
.desc_setup = sudmac_desc_setup,
|
||||
.set_slave = sudmac_set_slave,
|
||||
.setup_xfer = sudmac_setup_xfer,
|
||||
.start_xfer = sudmac_start_xfer,
|
||||
.embedded_desc = sudmac_embedded_desc,
|
||||
.chan_irq = sudmac_chan_irq,
|
||||
.get_partial = sudmac_get_partial,
|
||||
};
|
||||
|
||||
static int sudmac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sudmac_pdata *pdata = pdev->dev.platform_data;
|
||||
int err, i;
|
||||
struct sudmac_device *su_dev;
|
||||
struct dma_device *dma_dev;
|
||||
struct resource *chan, *irq_res;
|
||||
|
||||
/* get platform data */
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
chan = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!chan || !irq_res)
|
||||
return -ENODEV;
|
||||
|
||||
err = -ENOMEM;
|
||||
su_dev = devm_kzalloc(&pdev->dev, sizeof(struct sudmac_device),
|
||||
GFP_KERNEL);
|
||||
if (!su_dev) {
|
||||
dev_err(&pdev->dev, "Not enough memory\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
dma_dev = &su_dev->shdma_dev.dma_dev;
|
||||
|
||||
su_dev->chan_reg = devm_request_and_ioremap(&pdev->dev, chan);
|
||||
if (!su_dev->chan_reg)
|
||||
return err;
|
||||
|
||||
dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
|
||||
|
||||
su_dev->shdma_dev.ops = &sudmac_shdma_ops;
|
||||
su_dev->shdma_dev.desc_size = sizeof(struct sudmac_desc);
|
||||
err = shdma_init(&pdev->dev, &su_dev->shdma_dev, pdata->channel_num);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* platform data */
|
||||
su_dev->pdata = pdev->dev.platform_data;
|
||||
|
||||
platform_set_drvdata(pdev, su_dev);
|
||||
|
||||
/* Create DMA Channel */
|
||||
for (i = 0; i < pdata->channel_num; i++) {
|
||||
err = sudmac_chan_probe(su_dev, i, irq_res->start, IRQF_SHARED);
|
||||
if (err)
|
||||
goto chan_probe_err;
|
||||
}
|
||||
|
||||
err = dma_async_device_register(&su_dev->shdma_dev.dma_dev);
|
||||
if (err < 0)
|
||||
goto chan_probe_err;
|
||||
|
||||
return err;
|
||||
|
||||
chan_probe_err:
|
||||
sudmac_chan_remove(su_dev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
shdma_cleanup(&su_dev->shdma_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sudmac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sudmac_device *su_dev = platform_get_drvdata(pdev);
|
||||
struct dma_device *dma_dev = &su_dev->shdma_dev.dma_dev;
|
||||
|
||||
dma_async_device_unregister(dma_dev);
|
||||
sudmac_chan_remove(su_dev);
|
||||
shdma_cleanup(&su_dev->shdma_dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sudmac_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = SUDMAC_DRV_NAME,
|
||||
},
|
||||
.probe = sudmac_probe,
|
||||
.remove = sudmac_remove,
|
||||
};
|
||||
module_platform_driver(sudmac_driver);
|
||||
|
||||
MODULE_AUTHOR("Yoshihiro Shimoda");
|
||||
MODULE_DESCRIPTION("Renesas SUDMAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" SUDMAC_DRV_NAME);
|
@ -16,6 +16,7 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/sirfsoc_dma.h>
|
||||
|
||||
#include "dmaengine.h"
|
||||
@ -78,6 +79,7 @@ struct sirfsoc_dma {
|
||||
struct sirfsoc_dma_chan channels[SIRFSOC_DMA_CHANNELS];
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
bool is_marco;
|
||||
};
|
||||
|
||||
@ -639,6 +641,12 @@ static int sirfsoc_dma_probe(struct platform_device *op)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sdma->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(sdma->clk)) {
|
||||
dev_err(dev, "failed to get a clock.\n");
|
||||
return PTR_ERR(sdma->clk);
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(dn, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error parsing memory region!\n");
|
||||
@ -698,6 +706,8 @@ static int sirfsoc_dma_probe(struct platform_device *op)
|
||||
|
||||
tasklet_init(&sdma->tasklet, sirfsoc_dma_tasklet, (unsigned long)sdma);
|
||||
|
||||
clk_prepare_enable(sdma->clk);
|
||||
|
||||
/* Register DMA engine */
|
||||
dev_set_drvdata(dev, sdma);
|
||||
ret = dma_async_device_register(dma);
|
||||
@ -720,6 +730,7 @@ static int sirfsoc_dma_remove(struct platform_device *op)
|
||||
struct device *dev = &op->dev;
|
||||
struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(sdma->clk);
|
||||
dma_async_device_unregister(&sdma->dma);
|
||||
free_irq(sdma->irq, sdma);
|
||||
irq_dispose_mapping(sdma->irq);
|
||||
@ -742,7 +753,18 @@ static struct platform_driver sirfsoc_dma_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sirfsoc_dma_driver);
|
||||
static __init int sirfsoc_dma_init(void)
|
||||
{
|
||||
return platform_driver_register(&sirfsoc_dma_driver);
|
||||
}
|
||||
|
||||
static void __exit sirfsoc_dma_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sirfsoc_dma_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(sirfsoc_dma_init);
|
||||
module_exit(sirfsoc_dma_exit);
|
||||
|
||||
MODULE_AUTHOR("Rongjun Ying <rongjun.ying@csr.com>, "
|
||||
"Barry Song <baohua.song@csr.com>");
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
@ -199,6 +200,7 @@ struct tegra_dma_channel {
|
||||
|
||||
/* Channel-slave specific configuration */
|
||||
struct dma_slave_config dma_sconfig;
|
||||
struct tegra_dma_channel_regs channel_reg;
|
||||
};
|
||||
|
||||
/* tegra_dma: Tegra DMA specific information */
|
||||
@ -1213,7 +1215,6 @@ static const struct tegra_dma_chip_data tegra20_dma_chip_data = {
|
||||
.support_channel_pause = false,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
/* Tegra30 specific DMA controller information */
|
||||
static const struct tegra_dma_chip_data tegra30_dma_chip_data = {
|
||||
.nr_channels = 32,
|
||||
@ -1243,7 +1244,6 @@ static const struct of_device_id tegra_dma_of_match[] = {
|
||||
},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_dma_of_match);
|
||||
#endif
|
||||
|
||||
static int tegra_dma_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -1252,20 +1252,14 @@ static int tegra_dma_probe(struct platform_device *pdev)
|
||||
int ret;
|
||||
int i;
|
||||
const struct tegra_dma_chip_data *cdata = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
match = of_match_device(of_match_ptr(tegra_dma_of_match),
|
||||
&pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "Error: No device match found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
cdata = match->data;
|
||||
} else {
|
||||
/* If no device tree then fallback to tegra20 */
|
||||
cdata = &tegra20_dma_chip_data;
|
||||
match = of_match_device(tegra_dma_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "Error: No device match found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
cdata = match->data;
|
||||
|
||||
tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels *
|
||||
sizeof(struct tegra_dma_channel), GFP_KERNEL);
|
||||
@ -1448,11 +1442,74 @@ static int tegra_dma_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_dma_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_dma *tdma = dev_get_drvdata(dev);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* Enable clock before accessing register */
|
||||
ret = tegra_dma_runtime_resume(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tdma->reg_gen = tdma_read(tdma, TEGRA_APBDMA_GENERAL);
|
||||
for (i = 0; i < tdma->chip_data->nr_channels; i++) {
|
||||
struct tegra_dma_channel *tdc = &tdma->channels[i];
|
||||
struct tegra_dma_channel_regs *ch_reg = &tdc->channel_reg;
|
||||
|
||||
ch_reg->csr = tdc_read(tdc, TEGRA_APBDMA_CHAN_CSR);
|
||||
ch_reg->ahb_ptr = tdc_read(tdc, TEGRA_APBDMA_CHAN_AHBPTR);
|
||||
ch_reg->apb_ptr = tdc_read(tdc, TEGRA_APBDMA_CHAN_APBPTR);
|
||||
ch_reg->ahb_seq = tdc_read(tdc, TEGRA_APBDMA_CHAN_AHBSEQ);
|
||||
ch_reg->apb_seq = tdc_read(tdc, TEGRA_APBDMA_CHAN_APBSEQ);
|
||||
}
|
||||
|
||||
/* Disable clock */
|
||||
tegra_dma_runtime_suspend(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_dma_pm_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_dma *tdma = dev_get_drvdata(dev);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* Enable clock before accessing register */
|
||||
ret = tegra_dma_runtime_resume(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tdma_write(tdma, TEGRA_APBDMA_GENERAL, tdma->reg_gen);
|
||||
tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0);
|
||||
tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFFul);
|
||||
|
||||
for (i = 0; i < tdma->chip_data->nr_channels; i++) {
|
||||
struct tegra_dma_channel *tdc = &tdma->channels[i];
|
||||
struct tegra_dma_channel_regs *ch_reg = &tdc->channel_reg;
|
||||
|
||||
tdc_write(tdc, TEGRA_APBDMA_CHAN_APBSEQ, ch_reg->apb_seq);
|
||||
tdc_write(tdc, TEGRA_APBDMA_CHAN_APBPTR, ch_reg->apb_ptr);
|
||||
tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBSEQ, ch_reg->ahb_seq);
|
||||
tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBPTR, ch_reg->ahb_ptr);
|
||||
tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR,
|
||||
(ch_reg->csr & ~TEGRA_APBDMA_CSR_ENB));
|
||||
}
|
||||
|
||||
/* Disable clock */
|
||||
tegra_dma_runtime_suspend(dev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops tegra_dma_dev_pm_ops = {
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
.runtime_suspend = tegra_dma_runtime_suspend,
|
||||
.runtime_resume = tegra_dma_runtime_resume,
|
||||
#endif
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_dma_pm_suspend, tegra_dma_pm_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_dmac_driver = {
|
||||
@ -1460,7 +1517,7 @@ static struct platform_driver tegra_dmac_driver = {
|
||||
.name = "tegra-apbdma",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tegra_dma_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(tegra_dma_of_match),
|
||||
.of_match_table = tegra_dma_of_match,
|
||||
},
|
||||
.probe = tegra_dma_probe,
|
||||
.remove = tegra_dma_remove,
|
||||
|
@ -823,7 +823,7 @@ static struct platform_driver td_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = td_probe,
|
||||
.remove = __exit_p(td_remove),
|
||||
.remove = td_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(td_driver);
|
||||
|
@ -1190,7 +1190,7 @@ static int __init txx9dmac_chan_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit txx9dmac_chan_remove(struct platform_device *pdev)
|
||||
static int txx9dmac_chan_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct txx9dmac_chan *dc = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1252,7 +1252,7 @@ static int __init txx9dmac_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit txx9dmac_remove(struct platform_device *pdev)
|
||||
static int txx9dmac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct txx9dmac_dev *ddev = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1299,14 +1299,14 @@ static const struct dev_pm_ops txx9dmac_dev_pm_ops = {
|
||||
};
|
||||
|
||||
static struct platform_driver txx9dmac_chan_driver = {
|
||||
.remove = __exit_p(txx9dmac_chan_remove),
|
||||
.remove = txx9dmac_chan_remove,
|
||||
.driver = {
|
||||
.name = "txx9dmac-chan",
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver txx9dmac_driver = {
|
||||
.remove = __exit_p(txx9dmac_remove),
|
||||
.remove = txx9dmac_remove,
|
||||
.shutdown = txx9dmac_shutdown,
|
||||
.driver = {
|
||||
.name = "txx9dmac",
|
||||
|
116
include/linux/acpi_dma.h
Normal file
116
include/linux/acpi_dma.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* ACPI helpers for DMA request / controller
|
||||
*
|
||||
* Based on of_dma.h
|
||||
*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_ACPI_DMA_H
|
||||
#define __LINUX_ACPI_DMA_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
/**
|
||||
* struct acpi_dma_spec - slave device DMA resources
|
||||
* @chan_id: channel unique id
|
||||
* @slave_id: request line unique id
|
||||
* @dev: struct device of the DMA controller to be used in the filter
|
||||
* function
|
||||
*/
|
||||
struct acpi_dma_spec {
|
||||
int chan_id;
|
||||
int slave_id;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_dma - representation of the registered DMAC
|
||||
* @dma_controllers: linked list node
|
||||
* @dev: struct device of this controller
|
||||
* @acpi_dma_xlate: callback function to find a suitable channel
|
||||
* @data: private data used by a callback function
|
||||
*/
|
||||
struct acpi_dma {
|
||||
struct list_head dma_controllers;
|
||||
struct device *dev;
|
||||
struct dma_chan *(*acpi_dma_xlate)
|
||||
(struct acpi_dma_spec *, struct acpi_dma *);
|
||||
void *data;
|
||||
};
|
||||
|
||||
/* Used with acpi_dma_simple_xlate() */
|
||||
struct acpi_dma_filter_info {
|
||||
dma_cap_mask_t dma_cap;
|
||||
dma_filter_fn filter_fn;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DMA_ACPI
|
||||
|
||||
int acpi_dma_controller_register(struct device *dev,
|
||||
struct dma_chan *(*acpi_dma_xlate)
|
||||
(struct acpi_dma_spec *, struct acpi_dma *),
|
||||
void *data);
|
||||
int acpi_dma_controller_free(struct device *dev);
|
||||
int devm_acpi_dma_controller_register(struct device *dev,
|
||||
struct dma_chan *(*acpi_dma_xlate)
|
||||
(struct acpi_dma_spec *, struct acpi_dma *),
|
||||
void *data);
|
||||
void devm_acpi_dma_controller_free(struct device *dev);
|
||||
|
||||
struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,
|
||||
size_t index);
|
||||
struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev,
|
||||
const char *name);
|
||||
|
||||
struct dma_chan *acpi_dma_simple_xlate(struct acpi_dma_spec *dma_spec,
|
||||
struct acpi_dma *adma);
|
||||
#else
|
||||
|
||||
static inline int acpi_dma_controller_register(struct device *dev,
|
||||
struct dma_chan *(*acpi_dma_xlate)
|
||||
(struct acpi_dma_spec *, struct acpi_dma *),
|
||||
void *data)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int acpi_dma_controller_free(struct device *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int devm_acpi_dma_controller_register(struct device *dev,
|
||||
struct dma_chan *(*acpi_dma_xlate)
|
||||
(struct acpi_dma_spec *, struct acpi_dma *),
|
||||
void *data)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void devm_acpi_dma_controller_free(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct dma_chan *acpi_dma_request_slave_chan_by_index(
|
||||
struct device *dev, size_t index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline struct dma_chan *acpi_dma_request_slave_chan_by_name(
|
||||
struct device *dev, const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define acpi_dma_simple_xlate NULL
|
||||
|
||||
#endif
|
||||
|
||||
#define acpi_dma_request_slave_channel acpi_dma_request_slave_chan_by_index
|
||||
|
||||
#endif /* __LINUX_ACPI_DMA_H */
|
@ -967,8 +967,9 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie);
|
||||
#ifdef CONFIG_DMA_ENGINE
|
||||
enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx);
|
||||
void dma_issue_pending_all(void);
|
||||
struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param);
|
||||
struct dma_chan *dma_request_slave_channel(struct device *dev, char *name);
|
||||
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
|
||||
dma_filter_fn fn, void *fn_param);
|
||||
struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name);
|
||||
void dma_release_channel(struct dma_chan *chan);
|
||||
#else
|
||||
static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx)
|
||||
@ -978,13 +979,13 @@ static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descript
|
||||
static inline void dma_issue_pending_all(void)
|
||||
{
|
||||
}
|
||||
static inline struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask,
|
||||
static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
|
||||
dma_filter_fn fn, void *fn_param)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline struct dma_chan *dma_request_slave_channel(struct device *dev,
|
||||
char *name)
|
||||
const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@ -1005,9 +1006,9 @@ struct dma_chan *net_dma_find_channel(void);
|
||||
__dma_request_slave_channel_compat(&(mask), x, y, dev, name)
|
||||
|
||||
static inline struct dma_chan
|
||||
*__dma_request_slave_channel_compat(dma_cap_mask_t *mask, dma_filter_fn fn,
|
||||
void *fn_param, struct device *dev,
|
||||
char *name)
|
||||
*__dma_request_slave_channel_compat(const dma_cap_mask_t *mask,
|
||||
dma_filter_fn fn, void *fn_param,
|
||||
struct device *dev, char *name)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
|
||||
|
@ -25,7 +25,6 @@ struct of_dma {
|
||||
struct dma_chan *(*of_dma_xlate)
|
||||
(struct of_phandle_args *, struct of_dma *);
|
||||
void *of_dma_data;
|
||||
int use_count;
|
||||
};
|
||||
|
||||
struct of_dma_filter_info {
|
||||
@ -38,9 +37,9 @@ extern int of_dma_controller_register(struct device_node *np,
|
||||
struct dma_chan *(*of_dma_xlate)
|
||||
(struct of_phandle_args *, struct of_dma *),
|
||||
void *data);
|
||||
extern int of_dma_controller_free(struct device_node *np);
|
||||
extern void of_dma_controller_free(struct device_node *np);
|
||||
extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
|
||||
char *name);
|
||||
const char *name);
|
||||
extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec,
|
||||
struct of_dma *ofdma);
|
||||
#else
|
||||
@ -52,13 +51,12 @@ static inline int of_dma_controller_register(struct device_node *np,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int of_dma_controller_free(struct device_node *np)
|
||||
static inline void of_dma_controller_free(struct device_node *np)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
|
||||
char *name)
|
||||
const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
52
include/linux/sudmac.h
Normal file
52
include/linux/sudmac.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Header for the SUDMAC driver
|
||||
*
|
||||
* Copyright (C) 2013 Renesas Solutions Corp.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef SUDMAC_H
|
||||
#define SUDMAC_H
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/shdma-base.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Used by slave DMA clients to request DMA to/from a specific peripheral */
|
||||
struct sudmac_slave {
|
||||
struct shdma_slave shdma_slave; /* Set by the platform */
|
||||
};
|
||||
|
||||
/*
|
||||
* Supplied by platforms to specify, how a DMA channel has to be configured for
|
||||
* a certain peripheral
|
||||
*/
|
||||
struct sudmac_slave_config {
|
||||
int slave_id;
|
||||
};
|
||||
|
||||
struct sudmac_channel {
|
||||
unsigned long offset;
|
||||
unsigned long config;
|
||||
unsigned long wait; /* The configuable range is 0 to 3 */
|
||||
unsigned long dint_end_bit;
|
||||
};
|
||||
|
||||
struct sudmac_pdata {
|
||||
const struct sudmac_slave_config *slave;
|
||||
int slave_num;
|
||||
const struct sudmac_channel *channel;
|
||||
int channel_num;
|
||||
};
|
||||
|
||||
/* Definitions for the sudmac_channel.config */
|
||||
#define SUDMAC_TX_BUFFER_MODE BIT(0)
|
||||
#define SUDMAC_RX_END_MODE BIT(1)
|
||||
|
||||
/* Definitions for the sudmac_channel.dint_end_bit */
|
||||
#define SUDMAC_DMA_BIT_CH0 BIT(0)
|
||||
#define SUDMAC_DMA_BIT_CH1 BIT(1)
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user