* [GIT PULL] arm64 revert for 3.13
From: Catalin Marinas @ 2014-01-16 18:55 UTC (permalink / raw)
To: linux-arm-kernel
Hi Linus,
If there's still time for 3.13, could you please pull the one-line
revert below? We noticed that it breaks ioremap (and earlyprintk) with
64K page configuration. Thanks.
The following changes since commit cdc27c27843248ae7eb0df5fc261dd004eaa5670:
arm64: ptrace: avoid using HW_BREAKPOINT_EMPTY for disabled events (2013-12-19 17:41:25 +0000)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux tags/arm64-fixes
for you to fetch changes up to 4ce00dfcf19c473f3dbf23d5b1372639f0c334f6:
Revert "arm64: Fix memory shareability attribute for ioremap_wc/cache" (2014-01-16 18:32:25 +0000)
----------------------------------------------------------------
Revert "arm64: Fix memory shareability attribute for ioremap_wc/cache"
----------------------------------------------------------------
Catalin Marinas (1):
Revert "arm64: Fix memory shareability attribute for ioremap_wc/cache"
arch/arm64/include/asm/io.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
^ permalink raw reply
* [BUG] FL1009: xHCI host not responding to stop endpoint command.
From: Sarah Sharp @ 2014-01-16 18:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87mwix5anm.fsf@natisbad.org>
On Wed, Jan 15, 2014 at 08:04:45PM +0100, Arnaud Ebalard wrote:
> Hi David,
>
> David Laight <David.Laight@ACULAB.COM> writes:
>
> >> I tried current 3.13.0-rc8 w/ 35773dac5f86 reverted and the result is
> >> the same:
> >
> > That patch only affects an error code and stops the fs code retrying
> > for ever.
>
> Are you sure? ...
>
> > Does everything work if you comment out the code in xhci-ring.c that adds
> > NOP TRBs to the ring end in order to stop the LINK TRB appearing in the middle
> > of a TB.
> > The ethernet code needs it, but the disk transfers are (probably) aligned
> > such that they don't.
>
> ... AFAICT, this is exactly what commit 35773dac5f86 does and reverting
> it does not help. If I am mistaken, can you point which part you want me
> to remove in the code to test?
>
> I am slowly starting to see a bisect session coming ;-)
Try reverting commit 60e102ac73cd40069d077014c93c86dc7205cb68. That was
causing issues with another Fresco Logic host. If that doesn't help,
then yes, please git bisect.
Sarah Sharp
^ permalink raw reply
* [PATCH] ARM: orion: provide C-style interrupt handler for MULTI_IRQ_HANDLER
From: Jason Cooper @ 2014-01-16 18:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52D82802.6090104@gmail.com>
On Thu, Jan 16, 2014 at 07:42:10PM +0100, Sebastian Hesselbarth wrote:
...
> Out of curiosity, does that adding the Cc tag to the commit queue
> it automatically as soon as it will be pulled by Linus?
Yes. The -stable team scans commits as they are added to Linus' tree.
If it has that tag, then they cherrypick it for the appropriate trees.
As do many vendors and distros.
There's no need to actually "email Cc" stable, they won't take it until
it's landed in mainline. iirc, this generates a form letter from Greg
:)
> @Ian: Thanks for hunting this down, please keep bothering us with
> bug reports :)
Yes, please!
thx,
Jason.
^ permalink raw reply
* [PATCH v2] ACPI: introduce CONFIG_ACPI_REDUCED_HARDWARE_ONLY to enforce this ACPI mode
From: al.stone at linaro.org @ 2014-01-16 18:44 UTC (permalink / raw)
To: linux-arm-kernel
From: Al Stone <al.stone@linaro.org>
Hardware reduced mode, despite the name, exists primarily to allow
newer platforms to use a much simpler form of ACPI that does not
require supporting the legacy of previous versions of the specification.
This mode was first introduced in the ACPI 5.0 specification, but because
it is so much simpler and reduces the size of the object code needed to
support ACPI, it is likely to be used more often in the near future.
To enable the hardware reduced mode of ACPI on some platforms (such as
ARM), we need to modify the kernel code and set ACPI_REDUCED_HARDWARE
to TRUE in the ACPICA source. For ARM/ARM64, hardware reduced ACPI
should be the only mode used; legacy mode would require modifications
to SoCs in order to provide several x86-specific hardware features (e.g.,
an NMI and SMI support).
We set ACPI_REDUCED_HARDWARE to TRUE in the ACPICA source by introducing
a kernel config item to enable/disable ACPI_REDUCED_HARDWARE. We can then
change the kernel config instead of having to modify the kernel source
directly to enable the reduced hardware mode of ACPI.
Lv Zheng suggested that this configuration item does not belong in ACPICA,
the upstream source for much of the ACPI internals, but rather to the
Linux kernel itself. Hence, we introduce this flag so that we can make
ACPI_REDUCED_HARDWARE configurable. For the details of the discussion,
please refer to: http://www.spinics.net/lists/linux-acpi/msg46369.html
Even though support for X86 in hardware reduced mode is possible, it
is NOT enabled. Extensive effort has gone into the Linux kernel so that
there is a single kernel image than can run on all x86 hardware; the kernel
changes run-time behavior to adapt to the hardware being used. This is not
currently possible with the existing ACPICA infrastructure but only presents
a problem on achitectures supporting both hardware-reduced and legacy modes
of ACPI -- i.e., on x86 only.
The problem with the current ACPICA code base is that if one builds legacy
ACPI (a proper superset of hardware-reduced), the kernel can run in hardware-
reduced with the proper ACPI tables, but there is still ACPICA code that could
be executed even though it is not allowed by the specification. If one builds
a hardware-reduced only ACPI, the kernel cannot run with ACPI tables that are
for legacy mode. To ensure compliance with ACPI, one must therefore build
two separate kernels. Once this problem has been properly fixed, we can then
enable x86 hardware-reduced mode and use a single kernel.
Changes for v2:
-- Changed test for EXPERT to avoid reported Kconfig warning
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Al Stone <al.stone@linaro.org>
---
drivers/acpi/Kconfig | 13 +++++++++++++
include/acpi/platform/aclinux.h | 6 ++++++
2 files changed, 19 insertions(+)
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 4770de5..9fd6a7a 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -343,6 +343,19 @@ config ACPI_BGRT
data from the firmware boot splash. It will appear under
/sys/firmware/acpi/bgrt/ .
+config ACPI_REDUCED_HARDWARE_ONLY
+ bool "Hardware-reduced ACPI support only" if EXPERT
+ def_bool n
+ depends on ACPI
+ help
+ This config item changes the way the ACPI code is built. When this
+ option is selected, the kernel will use a specialized version of
+ ACPICA that ONLY supports the ACPI "reduced hardware" mode. The
+ resulting kernel will be smaller but it will also be restricted to
+ running in ACPI reduced hardware mode ONLY.
+
+ If you are unsure what to do, do not enable this option.
+
source "drivers/acpi/apei/Kconfig"
config ACPI_EXTLOG
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 28f4f4d..7d71f08 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -52,6 +52,12 @@
#ifdef __KERNEL__
+/* Compile for reduced hardware mode only with this kernel config */
+
+#ifdef CONFIG_ACPI_REDUCED_HARDWARE_ONLY
+#define ACPI_REDUCED_HARDWARE 1
+#endif
+
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/ctype.h>
--
1.8.4.2
^ permalink raw reply related
* [PATCH] ARM: orion: provide C-style interrupt handler for MULTI_IRQ_HANDLER
From: Sebastian Hesselbarth @ 2014-01-16 18:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116165347.GI29184@titan.lakedaemon.net>
On 01/16/2014 05:53 PM, Jason Cooper wrote:
> On Thu, Jan 16, 2014 at 04:51:27PM +0000, Ian Campbell wrote:
>> On Thu, 2014-01-16 at 11:48 -0500, Jason Cooper wrote:
>>> Sebastian,
>>>
>>> On Thu, Jan 16, 2014 at 09:10:31AM +0100, Sebastian Hesselbarth wrote:
>>>> DT-enabled Marvell Kirkwood and Dove SoCs make use of an irqchip
>>>> driver. As expected for irqchip drivers, it uses a C-style
>>>> interrupt handler and therefore selects MULTI_IRQ_HANDLER.
>>>>
>>>> Now, compiling a kernel with both non-DT and DT support enabled,
>>>> selecting MULTI_IRQ_HANDLER will break ASM irq handler used by
>>>> non-DT boards.
>>>>
>>>> Therefore, we provide a C-style irq handler even for non-DT boards,
>>>> if MULTI_IRQ_HANDLER is set. By installing the C-style irq handler
>>>> in orion_irq_init this is transparent to all non-DT board files.
>>>>
>>>> While the regression report was filed on Marvell Kirkwood, also
>>>> Marvell Dove non-DT boards are affected and fixed by this patch.
>>>>
>>>> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>>>> Tested-by: Ian Campbell <ijc@hellion.org.uk>
>>>> Reported-by: Ian Campbell <ijc@hellion.org.uk>
>>>> Fixes: 2326f04321a9 ("ARM: kirkwood: convert to DT irqchip and clocksource")
>>>> Fixes: f07d73e33d0e ("ARM: dove: convert to DT irqchip and clocksource")
>>>> ---
>>>> Compared to the two patch version sent on Ian's regression report,
>>>> I cooked this down to a single patch only touching plat-orion/irq.c.
>>>> I also dropped Orion5x and MV78x00, as they not yet select MULTI_IRQ_HANDLER
>>>> at all. Instead I added a comment about taking care of them, as soon as
>>>> they move over to an irqchip driver themselves.
>>>>
>>>> I have not yet Cc'ed this patch to -stable to get some discussion and
>>>> an Acked-by from Jason or Andrew, too.
>>>>
>>>> Cc: Jason Cooper <jason@lakedaemon.net>
>>>> Cc: Andrew Lunn <andrew@lunn.ch>
>>>> Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>>>> Cc: Russell King <linux@arm.linux.org.uk>
>>>> Cc: Ian Campbell <ijc@hellion.org.uk>
>>>> Cc: linux-arm-kernel at lists.infradead.org
>>>> Cc: linux-kernel at vger.kernel.org
>>>> ---
>>>> arch/arm/plat-orion/irq.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
>>>> 1 file changed, 47 insertions(+)
>>>
>>> Applied to mvebu/fixes with Andrew's Ack. It's too late for v3.13, so
>>> it'll be the first thing sent for v3.14.
>>
>> Please can it also be tagged for stable backport to 3.12+3.13.
>
> Yes, I forgot to mention it, but that is already done. ;-)
Ok, thanks!
Out of curiosity, does that adding the Cc tag to the commit queue
it automatically as soon as it will be pulled by Linus?
@Ian: Thanks for hunting this down, please keep bothering us with
bug reports :)
Sebastian
^ permalink raw reply
* Multiple CCR definitions (was: Re: mmc: omap_hsmmc: use the generic config for omap2plus devices)
From: Nishanth Menon @ 2014-01-16 18:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAMuHMdVLat9-EMnCX=usthZLrP2YhCCT7-a3nhSQmgLvn_ftBg@mail.gmail.com>
+linux-omap as well.
On Thu, Jan 16, 2014 at 7:07 AM, Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
> On Tue, Sep 10, 2013 at 11:05 PM, Linux Kernel Mailing List
> <linux-kernel@vger.kernel.org> wrote:
>> mmc: omap_hsmmc: use the generic config for omap2plus devices
>>
>> OMAP's hs_mmc driver is used for MMC controller operation on many
>> omap2plus SoCs (OMAP2430, OMAP3, 4, 5 and AM335x).
>>
>> Considering that the device tree entries are already present for these,
>> allow the driver to be built using the config ARCH_OMAP2PLUS rather
>> than individually adding a config for each SoC to enable the
>> support.
>>
>> Use COMPILE_TEST to enable the build for other platforms.
>>
>> Signed-off-by: Amarinder Bindra <a-bindra@ti.com>
>> Cc: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
>> Cc: Nishanth Menon <nm@ti.com>
>> Acked-by: Felipe Balbi <balbi@ti.com>
>> Acked-by: Balaji T K <balajitk@ti.com>
>> Signed-off-by: Chris Ball <cjb@laptop.org>
>> ---
>> drivers/mmc/host/Kconfig | 6 +++---
>> 1 files changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>> index 84102bc..b8ce939 100644
>> --- a/drivers/mmc/host/Kconfig
>> +++ b/drivers/mmc/host/Kconfig
>> @@ -284,11 +284,11 @@ config MMC_OMAP
>>
>> config MMC_OMAP_HS
>> tristate "TI OMAP High Speed Multimedia Card Interface support"
>> - depends on SOC_OMAP2430 || ARCH_OMAP3 || ARCH_OMAP4
>> + depends on ARCH_OMAP2PLUS || COMPILE_TEST
>
> sh-allmodconfig (http://kisskb.ellerman.id.au/kisskb/buildresult/10477672/):
> include/linux/omap-dma.h:171:8: error: expected identifier before
> numeric constant
> make[4]: *** [drivers/mmc/host/omap_hsmmc.o] Error 1
>
> It fails because sh #defines "CCR", which is one of the enum values in
> include/linux/omap-dma.h.
>
> I expect a similar failure on blackfin, which also has #define CCR.
>
> Possible solutions:
> 1. Rename CCR in sh/bfin to e.g. SH_CCR resp. BF_CCR.
> 2. Use "depends on ARCH_OMAP2PLUS || (COMPILE_TEST && ARM)
> 3. Stop using include/linux/omap-dma.h, as it says:
>
> * Legacy OMAP DMA handling defines and functions
> *
> * NOTE: Do not use these any longer.
> *
> * Use the generic dmaengine functions as defined in
> * include/linux/dmaengine.h.
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
^ permalink raw reply
* [PATCH] ACPI: introduce CONFIG_ACPI_REDUCED_HARDWARE_ONLY to enforce this ACPI mode
From: Al Stone @ 2014-01-16 18:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201401161155.27592.arnd@arndb.de>
On 01/16/2014 03:55 AM, Arnd Bergmann wrote:
> On Thursday 16 January 2014, Al Stone wrote:
>> On 01/14/2014 02:26 PM, Arnd Bergmann wrote:
>>> On Tuesday 14 January 2014 13:37:02 al.stone at linaro.org wrote:
>>>> +config ACPI_REDUCED_HARDWARE_ONLY
>>>> + bool "Hardware-reduced ACPI support only"
>>>> + def_bool n
>>>> + depends on ACPI && EXPERT
>>>
>>> I think this will cause a Kconfig warning if you try to select this
>>> on ARM64 without turning on EXPERT as well.
>>>
>>> It should be ok if you express it as
>>>
>>> config ACPI_REDUCED_HARDWARE_ONLY
>>> bool "Hardware-reduced ACPI support only" if EXPERT
>>> def_bool n
>>> depends on ACPI
>>>
>>>
>>> Arnd
>>>
>>
>> I am very puzzled. I went back and re-did my configs from
>> before, tried a few new ones, and tried with both expressions
>> above, but I could not get a Kconfig warning. Do you have an
>> example .config that shows the warning?
>>
>
> I don't have a source tree that allows turning on ACPI on arm64
> to easily test this. To clarify: the warning will only happen if
> you also add this piece to arch/arm64/Kconfig:
>
> select ACPI_REDUCED_HARDWARE_ONLY if ACPI
>
> Arnd
>
Right; I assumed I had to have that SELECT. My apologies -- I
still cannot reproduce the warning. I'll resubmit the patch
incorporating the suggestion, though, since the result ends up
being the same -- you must have ACPI and EXPERT selected in order
to enable ACPI_REDUCED_HARDWARE_ONLY.
Thanks.
--
ciao,
al
-----------------------------------
Al Stone
Software Engineer
Linaro Enterprise Group
al.stone at linaro.org
-----------------------------------
^ permalink raw reply
* [PATCH v5 2/4] devicetree: bindings: Document Krait CPU/L1 EDAC
From: Lorenzo Pieralisi @ 2014-01-16 18:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116180505.GA30925@codeaurora.org>
On Thu, Jan 16, 2014 at 06:05:05PM +0000, Stephen Boyd wrote:
> On 01/16, Lorenzo Pieralisi wrote:
> > On Thu, Jan 16, 2014 at 01:38:40AM +0000, Stephen Boyd wrote:
> > > On 01/15, Stephen Boyd wrote:
> > > >
> > > > Ah sorry, I forgot to put the compatible property here like in
> > > > the dts change. I'll do that in the next revision. Yes we need a
> > > > compatible property here to match the platform driver.
> > > >
> > >
> > > This is the replacement patch
> > >
> > > -----8<------
> > > From: Stephen Boyd <sboyd@codeaurora.org>
> > > Subject: [PATCH v9] devicetree: bindings: Document Krait CPU/L1 EDAC
> > >
> > > The Krait CPU/L1 error reporting device is made up a per-CPU
> > > interrupt. While we're here, document the next-level-cache
> > > property that's used by the Krait EDAC driver.
> > >
> > > Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> > > Cc: Mark Rutland <mark.rutland@arm.com>
> > > Cc: Kumar Gala <galak@codeaurora.org>
> > > Cc: <devicetree@vger.kernel.org>
> > > Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> > > ---
> > > Documentation/devicetree/bindings/arm/cpus.txt | 58 ++++++++++++++++++++++++++
> > > 1 file changed, 58 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
> > > index 91304353eea4..03a529e791c4 100644
> > > --- a/Documentation/devicetree/bindings/arm/cpus.txt
> > > +++ b/Documentation/devicetree/bindings/arm/cpus.txt
> > > @@ -62,6 +62,20 @@ nodes to be present and contain the properties described below.
> > > Value type: <u32>
> > > Definition: must be set to 0
> > >
> > > + - compatible
> > > + Usage: optional
> > > + Value type: <string>
> > > + Definition: should be one of the compatible strings listed
> > > + in the cpu node compatible property. This property
> > > + shall only be present if all the cpu nodes have the
> > > + same compatible property.
> >
> > Do we really want to do that ? I am not sure. A cpus node is supposed to
> > be a container node, we should not define this binding just because we
> > know the kernel creates a platform device for it then.
>
> This is just copying more of the ePAPR spec into this document.
> It just so happens that having a compatible field here allows a
> platform device to be created. I don't see why that's a problem.
I do not see why you cannot define a node like pmu or arch-timer and stick
a compatible property in there. cpus node does not represent a device, and
must not be created as a platform device, that's my opinion.
What would you do for big.LITTLE systems ? We are going to create two
cpus node because we need two platform devices ? I really think there
must be a better way to implement this, but I will let DT maintainers
make a decision.
> > interrupts is a cpu node property and I think it should be kept as such.
> >
> > I know it will be duplicated and I know you can't rely on a platform
> > device for probing (since if I am not mistaken, removing a compatible
> > string from cpus prevents its platform device creation), but that's an issue
> > related to how the kernel works, you should not define DT bindings to solve
> > that IMHO.
>
> The interrupts property is also common for all cpus so it seems
> fine to collapse the value down into a PPI specifier indicating
> that all CPUs get the interrupt, similar to how we compress the
> information about the compatible string.
I think it is nicer to create a device node (as I said, like a pmu or an
arch-timer) and define interrupts there along with a proper compatible
property. This would serve the same purpose without adding properties in
the cpus node.
cpu-edac {
compatible = "qcom,cpu-edac";
interrupts = <...>;
};
Thanks,
Lorenzo
^ permalink raw reply
* [PATCH] mmc: dw_mmc: fix dw_mci_get_cd
From: Kevin Hilman @ 2014-01-16 18:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389876527-13498-1-git-send-email-zhangfei.gao@linaro.org>
Zhangfei Gao <zhangfei.gao@linaro.org> writes:
> Introduced from commit bf626e5550f24aec24975a0e85ad8e572ca76a6b
> CDETECT is ignored since negated return value of mmc_gpio_get_cd(mmc)
> can not be checked by IS_ERR_VALUE.
> Add spin_lock_bh(&host->lock) for atomic accessing DW_MMC_CARD_PRESENT,
> otherwise sd detect may occasionally fail.
>
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> Reported-by: Kevin Hilman <khilman@linaro.org>
> Reviewed-by: Sachin Kamat <sachin.kamat@linaro.org>
> Tested-by: Sachin Kamat <sachin.kamat@linaro.org>
I didn't look at the patch in detail, but can at least confirm that
Arndale boots again in -next with this patch applied.
Tested-by: Kevin Hilman <khilman@linaro.org>
Kevin
^ permalink raw reply
* [PATCH] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Levente Kurusa @ 2014-01-16 18:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389894803-4147-2-git-send-email-sthokal@xilinx.com>
Hello,
On 01/16/2014 06:53 PM, Srikanth Thokala wrote:
> This is the driver for the AXI Video Direct Memory Access (AXI
> VDMA) core, which is a soft Xilinx IP core that provides high-
> bandwidth direct memory access between memory and AXI4-Stream
> type video target peripherals. The core provides efficient two
> dimensional DMA operations with independent asynchronous read
> and write channel operation.
>
> This module works on Zynq (ARM Based SoC) and Microblaze platforms.
>
> Signed-off-by: Srikanth Thokala <sthokal@xilinx.com>
> ---
Just a few suggestions and fixes.
> NOTE:
> 1. Created a separate directory 'dma/xilinx' as Xilinx has two more
> DMA IPs and we are also planning to upstream these drivers soon.
> 2. Rebased on v3.13.0-rc8
> ---
> .../devicetree/bindings/dma/xilinx/xilinx_vdma.txt | 71 +
> .../bindings/dma/xilinx/xilinx_vdma_test.txt | 39 +
> drivers/dma/Kconfig | 23 +
> drivers/dma/Makefile | 1 +
> drivers/dma/xilinx/Makefile | 2 +
> drivers/dma/xilinx/xilinx_vdma.c | 1497 ++++++++++++++++++++
> drivers/dma/xilinx/xilinx_vdma_test.c | 629 ++++++++
> 7 files changed, 2262 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
> create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
> create mode 100644 drivers/dma/xilinx/Makefile
> create mode 100644 drivers/dma/xilinx/xilinx_vdma.c
> create mode 100644 drivers/dma/xilinx/xilinx_vdma_test.c
>
> diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
> new file mode 100644
> index 0000000..3f5c428
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
> @@ -0,0 +1,71 @@
> +Xilinx AXI VDMA engine, it does transfers between memory and video devices.
> +It can be configured to have one channel or two channels. If configured
> +as two channels, one is to transmit to the video device and another is
> +to receive from the video device.
> +
> +Required properties:
> +- compatible: Should be "xlnx,axi-vdma-1.00.a"
> +- #dma-cells: Should be <1>, see "dmas" property below
> +- reg: Should contain VDMA registers location and length.
> +- interrupts: Should contain per channel VDMA interrupts.
> +- compatible (child node): It should be either "xlnx,axi-vdma-mm2s-channel" or
> + "xlnx,axi-vdma-s2mm-channel". It depends on the hardware design and it
> + can also have both channels.
> +- xlnx,device-id: Should contain device number in each channel. It should be
> + {0,1,2...so on} to the number of VDMA devices configured in hardware.
> +- xlnx,num-fstores: Should be the number of framebuffers as configured in h/w.
> +- xlnx,data-width: Should contain the stream data width, takes {32,64...so on}.
> +- xlnx,flush-fsync: (Optional) Tells whether which channel to Flush on Fsync.
> + It takes following values:
> + {1}, flush both channels
> + {2}, flush mm2s channel
> + {3}, flush s2mm channel
> +- xlnx,include-sg: (Optional) Tells whether configured for Scatter-mode in
> + the hardware.
> +- xlnx,include-dre: (Optional) Tells whether hardware is configured for Data
> + Realignment Engine.
> +- xlnx,genlock-mode: (Optional) Tells whether Genlock synchornisation is
s/synchornisation/synchronization
> + enabled/disabled in hardware.
> +
> +Example:
> +++++++++
> +
> +axi_vdma_0: axivdma at 40030000 {
> + compatible = "xlnx,axi-vdma-1.00.a";
> + #dma_cells = <1>;
> + reg = < 0x40030000 0x10000 >;
> + xlnx,flush-fsync = <0x1>;
> + dma-channel at 40030000 {
> + compatible = "xlnx,axi-vdma-mm2s-channel";
> + interrupts = < 0 54 4 >;
> + xlnx,num-fstores = <0x8>;
> + xlnx,device-id = <0x0>;
> + xlnx,datawidth = <0x40>;
> + } ;
> + dma-channel at 40030030 {
> + compatible = "xlnx,axi-vdma-s2mm-channel";
> + interrupts = < 0 53 4 >;
> + xlnx,num-fstores = <0x8>;
> + xlnx,device-id = <0x0>;
> + xlnx,datawidth = <0x40>;
> + } ;
> +} ;
> +
> +
> +* Xilinx Video DMA client
> +
> +Required properties:
> +- dmas: a list of <[Video DMA device phandle] [Channel ID]> pairs,
> + where Channel ID is '0' for write/tx and '1' for read/rx
> + channel.
> +- dma-names: a list of DMA channel names, one per "dmas" entry
> +
> +VDMA Test Client Example:
> ++++++++++++++++++++++++++
> +
> +vdmatest_0: vdmatest at 0 {
> + compatible ="xlnx,axi-vdma-test-1.00.a";
> + dmas = <&axi_vdma_0 0
> + &axi_vdma_0 1>;
> + dma-names = "vdma0", "vdma1";
> +} ;
> diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
> new file mode 100644
> index 0000000..5821fdc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
> @@ -0,0 +1,39 @@
> +* Xilinx Video DMA Test client
> +
> +Required properties:
> +- compatible: Should be "xlnx,axi-vdma-test-1.00.a"
> +- dmas: a list of <[Video DMA device phandle] [Channel ID]> pairs,
> + where Channel ID is '0' for write/tx and '1' for read/rx
> + channel.
> +- dma-names: a list of DMA channel names, one per "dmas" entry
> +- xlnx,num-fstores: Should be the number of framebuffers as configured in
> + VDMA device node.
> +
> +Example:
> +++++++++
> +
> +vdmatest_0: vdmatest at 0 {
> + compatible ="xlnx,axi-vdma-test-1.00.a";
> + dmas = <&axi_vdma_0 0
> + &axi_vdma_0 1>;
> + dma-names = "vdma0", "vdma1";
> + xlnx,num-fstores = <0x8>;
> +} ;
> +
> +
> +Xilinx Video DMA Device Node Example
> +++++++++++++++++++++++++++++++++++++
> +axi_vdma_0: axivdma at 44A40000 {
> + compatible = "xlnx,axi-vdma-1.00.a";
> + ...
> + dma-channel at 44A40000 {
> + ...
> + xlnx,num-fstores = <0x8>;
> + ...
> + } ;
> + dma-channel at 44A40030 {
> + ...
> + xlnx,num-fstores = <0x8>;
> + ...
> + } ;
> +} ;
> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> index c823daa..675719f 100644
> --- a/drivers/dma/Kconfig
> +++ b/drivers/dma/Kconfig
> @@ -334,6 +334,20 @@ config K3_DMA
> Support the DMA engine for Hisilicon K3 platform
> devices.
>
> +config XILINX_VDMA
> + tristate "Xilinx AXI VDMA Engine"
> + depends on (ARCH_ZYNQ || MICROBLAZE)
> + select DMA_ENGINE
> + help
> + Enable support for Xilinx AXI VDMA Soft IP.
> +
> + This engine provides high-bandwidth direct memory access
> + between memory and AXI4-Stream video type target
> + peripherals including peripherals which support AXI4-
> + Stream Video Protocol. It has two stream interfaces/
> + channels, Memory Mapped to Stream (MM2S) and Stream to
> + Memory Mapped (S2MM) for the data transfers.
> +
> config DMA_ENGINE
> bool
>
> @@ -384,4 +398,13 @@ config DMATEST
> config DMA_ENGINE_RAID
> bool
>
> +config XILINX_VDMA_TEST
> + tristate "DMA Test client for Xilinx AXI VDMA"
> + depends on XILINX_VDMA
> + help
> + Simple VDMA driver test client. This test assumes both
> + the stream interfaces of VDMA engine, MM2S and S2MM are
> + connected back-to-back in the hardware design.
> +
> + Say N unless you're debugging a DMA Device driver.
> endif
> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> index 0ce2da9..d84130b 100644
> --- a/drivers/dma/Makefile
> +++ b/drivers/dma/Makefile
> @@ -42,3 +42,4 @@ obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
> obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
> obj-$(CONFIG_TI_CPPI41) += cppi41.o
> obj-$(CONFIG_K3_DMA) += k3dma.o
> +obj-y += xilinx/
> diff --git a/drivers/dma/xilinx/Makefile b/drivers/dma/xilinx/Makefile
> new file mode 100644
> index 0000000..cef1e88
> --- /dev/null
> +++ b/drivers/dma/xilinx/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_XILINX_VDMA) += xilinx_vdma.o
> +obj-$(CONFIG_XILINX_VDMA_TEST) += xilinx_vdma_test.o
> diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c
> new file mode 100644
> index 0000000..66a12de
> --- /dev/null
> +++ b/drivers/dma/xilinx/xilinx_vdma.c
> @@ -0,0 +1,1497 @@
> +/*
> + * DMA driver for Xilinx Video DMA Engine
> + *
> + * Copyright (C) 2010-2013 Xilinx, Inc. All rights reserved.
Happy new year! ;-)
> + *
> + * Based on the Freescale DMA driver.
> + *
> + * Description:
> + * The AXI Video Direct Memory Access (AXI VDMA) core is a soft Xilinx IP
> + * core that provides high-bandwidth direct memory access between memory
> + * and AXI4-Stream type video target peripherals. The core provides efficient
> + * two dimensional DMA operations with independent asynchronous read (S2MM)
> + * and write (MM2S) channel operation. It can be configured to have either
> + * one channel or two channels. If configured as two channels, one is to
> + * transmit to the video device (MM2S) and another is to receive from the
> + * video device (S2MM). Initialization, status, interrupt and management
> + * registers are accessed through an AXI4-Lite slave interface.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/amba/xilinx_dma.h>
> +#include <linux/bitops.h>
> +#include <linux/dmapool.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_dma.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_irq.h>
> +#include <linux/slab.h>
> +
> +/* Register/Descriptor Offsets */
> +#define XILINX_VDMA_MM2S_CTRL_OFFSET 0x0000
> +#define XILINX_VDMA_S2MM_CTRL_OFFSET 0x0030
> +#define XILINX_VDMA_MM2S_DESC_OFFSET 0x0050
> +#define XILINX_VDMA_S2MM_DESC_OFFSET 0x00a0
> +
> +/* Control Registers */
> +#define XILINX_VDMA_REG_DMACR 0x0000
> +#define XILINX_VDMA_DMACR_DELAY_MAX 0xff
> +#define XILINX_VDMA_DMACR_DELAY_SHIFT 24
> +#define XILINX_VDMA_DMACR_FRAME_COUNT_MAX 0xff
> +#define XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT 16
> +#define XILINX_VDMA_DMACR_ERR_IRQ (1 << 14)
> +#define XILINX_VDMA_DMACR_DLY_CNT_IRQ (1 << 13)
> +#define XILINX_VDMA_DMACR_FRM_CNT_IRQ (1 << 12)
> +#define XILINX_VDMA_DMACR_MASTER_SHIFT 8
> +#define XILINX_VDMA_DMACR_FSYNCSRC_SHIFT 5
> +#define XILINX_VDMA_DMACR_FRAMECNT_EN (1 << 4)
> +#define XILINX_VDMA_DMACR_GENLOCK_EN (1 << 3)
> +#define XILINX_VDMA_DMACR_RESET (1 << 2)
> +#define XILINX_VDMA_DMACR_CIRC_EN (1 << 1)
> +#define XILINX_VDMA_DMACR_RUNSTOP (1 << 0)
You could use the BIT(n) field from <linux/bitops.h>
> +#define XILINX_VDMA_DMACR_DELAY_MASK \
> + (XILINX_VDMA_DMACR_DELAY_MAX << \
> + XILINX_VDMA_DMACR_DELAY_SHIFT)
> +#define XILINX_VDMA_DMACR_FRAME_COUNT_MASK \
> + (XILINX_VDMA_DMACR_FRAME_COUNT_MAX << \
> + XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT)
> +#define XILINX_VDMA_DMACR_MASTER_MASK \
> + (0xf << XILINX_VDMA_DMACR_MASTER_SHIFT)
> +#define XILINX_VDMA_DMACR_FSYNCSRC_MASK \
> + (3 << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT)
> +
> +#define XILINX_VDMA_REG_DMASR 0x0004
> +#define XILINX_VDMA_DMASR_DELAY_SHIFT 24
> +#define XILINX_VDMA_DMASR_FRAME_COUNT_SHIFT 16
> +#define XILINX_VDMA_DMASR_EOL_LATE_ERR (1 << 15)
> +#define XILINX_VDMA_DMASR_ERR_IRQ (1 << 14)
> +#define XILINX_VDMA_DMASR_DLY_CNT_IRQ (1 << 13)
> +#define XILINX_VDMA_DMASR_FRM_CNT_IRQ (1 << 12)
> +#define XILINX_VDMA_DMASR_SOF_LATE_ERR (1 << 11)
> +#define XILINX_VDMA_DMASR_SG_DEC_ERR (1 << 10)
> +#define XILINX_VDMA_DMASR_SG_SLV_ERR (1 << 9)
> +#define XILINX_VDMA_DMASR_EOF_EARLY_ERR (1 << 8)
> +#define XILINX_VDMA_DMASR_SOF_EARLY_ERR (1 << 7)
> +#define XILINX_VDMA_DMASR_DMA_DEC_ERR (1 << 6)
> +#define XILINX_VDMA_DMASR_DMA_SLAVE_ERR (1 << 5)
> +#define XILINX_VDMA_DMASR_DMA_INT_ERR (1 << 4)
> +#define XILINX_VDMA_DMASR_IDLE (1 << 1)
> +#define XILINX_VDMA_DMASR_HALTED (1 << 0)
Here as well.
> +#define XILINX_VDMA_DMASR_DELAY_MASK \
> + (0xff << XILINX_VDMA_DMASR_DELAY_SHIFT)
> +#define XILINX_VDMA_DMASR_FRAME_COUNT_MASK \
> + (0xff << XILINX_VDMA_DMASR_FRAME_COUNT_SHIFT)
> +
> +#define XILINX_VDMA_REG_CURDESC 0x0008
> +#define XILINX_VDMA_REG_TAILDESC 0x0010
> +#define XILINX_VDMA_REG_REG_INDEX 0x0014
> +#define XILINX_VDMA_REG_FRMSTORE 0x0018
> +#define XILINX_VDMA_REG_THRESHOLD 0x001c
> +#define XILINX_VDMA_REG_FRMPTR_STS 0x0024
> +#define XILINX_VDMA_REG_PARK_PTR 0x0028
> +#define XILINX_VDMA_PARK_PTR_WR_REF_SHIFT 8
> +#define XILINX_VDMA_PARK_PTR_RD_REF_SHIFT 0
> +#define XILINX_VDMA_REG_VDMA_VERSION 0x002c
> +
> [...]
> +
> +/**
> + * xilinx_vdma_slave_config - Configure VDMA channel
> + * Run-time configuration for Axi VDMA, supports:
> + * . halt the channel
> + * . configure interrupt coalescing and inter-packet delay threshold
> + * . start/stop parking
> + * . enable genlock
> + * . set transfer information using config struct
> + *
> + * @chan: Driver specific VDMA Channel pointer
> + * @cfg: Channel configuration pointer
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_slave_config(struct xilinx_vdma_chan *chan,
> + struct xilinx_vdma_config *cfg)
> +{
> + u32 dmacr;
> +
> + if (cfg->reset)
> + return xilinx_vdma_chan_reset(chan);
> +
> + dmacr = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR);
> +
> + /* If vsize is -1, it is park-related operations */
> + if (cfg->vsize == -1) {
> + if (cfg->park)
> + dmacr &= ~XILINX_VDMA_DMACR_CIRC_EN;
> + else
> + dmacr |= XILINX_VDMA_DMACR_CIRC_EN;
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
> + return 0;
> + }
> +
> + /* If hsize is -1, it is interrupt threshold settings */
> + if (cfg->hsize == -1) {
> + if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
> + dmacr &= ~XILINX_VDMA_DMACR_FRAME_COUNT_MASK;
> + dmacr |= cfg->coalesc <<
> + XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
> + chan->config.coalesc = cfg->coalesc;
> + }
> +
> + if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
> + dmacr &= ~XILINX_VDMA_DMACR_DELAY_MASK;
> + dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
> + chan->config.delay = cfg->delay;
> + }
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
> + return 0;
> + }
> +
> + /* Transfer information */
> + chan->config.vsize = cfg->vsize;
> + chan->config.hsize = cfg->hsize;
> + chan->config.stride = cfg->stride;
> + chan->config.frm_dly = cfg->frm_dly;
> + chan->config.park = cfg->park;
Can't this be done with a memcpy? Not sure though.
> +
> + /* genlock settings */
> + chan->config.gen_lock = cfg->gen_lock;
> + chan->config.master = cfg->master;
This as well maybe.
> +
> + if (cfg->gen_lock && chan->genlock) {
> + dmacr |= XILINX_VDMA_DMACR_GENLOCK_EN;
> + dmacr |= cfg->master << XILINX_VDMA_DMACR_MASTER_SHIFT;
> + }
> +
> + chan->config.frm_cnt_en = cfg->frm_cnt_en;
> + if (cfg->park)
> + chan->config.park_frm = cfg->park_frm;
> + else
> + chan->config.park_frm = -1;
> +
> + chan->config.coalesc = cfg->coalesc;
> + chan->config.delay = cfg->delay;
> + if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
> + dmacr |= cfg->coalesc << XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
> + chan->config.coalesc = cfg->coalesc;
> + }
> +
> + if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
> + dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
> + chan->config.delay = cfg->delay;
> + }
> +
> + /* FSync Source selection */
> + dmacr &= ~XILINX_VDMA_DMACR_FSYNCSRC_MASK;
> + dmacr |= cfg->ext_fsync << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT;
> +
> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
> + return 0;
> +}
> +
> [...]
> +
> +/**
> + * xilinx_vdma_probe - Driver probe function
> + * @pdev: Pointer to the platform_device structure
> + *
> + * Return: '0' on success and failure value on error
> + */
> +static int xilinx_vdma_probe(struct platform_device *pdev)
> +{
> + struct device_node *node = pdev->dev.of_node;
> + struct xilinx_vdma_device *xdev;
> + struct device_node *child;
> + struct resource *io;
> + u32 num_frames;
> + int i, err;
> +
> + dev_info(&pdev->dev, "Probing xilinx axi vdma engine\n");
> +
> + /* Allocate and initialize the DMA engine structure */
> + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
> + if (!xdev)
> + return -ENOMEM;
> +
> + xdev->dev = &pdev->dev;
> +
> + /* Request and map I/O memory */
> + io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + xdev->regs = devm_ioremap_resource(&pdev->dev, io);
> + if (IS_ERR(xdev->regs))
> + return PTR_ERR(xdev->regs);
> +
> + /* Retrieve the DMA engine properties from the device tree */
> + xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg");
> +
> + err = of_property_read_u32(node, "xlnx,num-fstores", &num_frames);
> + if (err < 0) {
> + dev_err(xdev->dev, "missing xlnx,num-fstores property\n");
> + return err;
> + }
> +
> + of_property_read_u32(node, "xlnx,flush-fsync", &xdev->flush_on_fsync);
> +
> + /* Initialize the DMA engine */
> + xdev->common.dev = &pdev->dev;
> +
> + INIT_LIST_HEAD(&xdev->common.channels);
> + dma_cap_set(DMA_SLAVE, xdev->common.cap_mask);
> + dma_cap_set(DMA_PRIVATE, xdev->common.cap_mask);
> +
> + xdev->common.device_alloc_chan_resources =
> + xilinx_vdma_alloc_chan_resources;
> + xdev->common.device_free_chan_resources =
> + xilinx_vdma_free_chan_resources;
> + xdev->common.device_prep_slave_sg = xilinx_vdma_prep_slave_sg;
> + xdev->common.device_control = xilinx_vdma_device_control;
> + xdev->common.device_tx_status = xilinx_vdma_tx_status;
> + xdev->common.device_issue_pending = xilinx_vdma_issue_pending;
> +
> + platform_set_drvdata(pdev, xdev);
> +
> + /* Initialize the channels */
> + for_each_child_of_node(node, child) {
> + err = xilinx_vdma_chan_probe(xdev, child);
> + if (err < 0)
> + goto error;
> + }
> +
> + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
> + if (xdev->chan[i])
> + xdev->chan[i]->num_frms = num_frames;
> + }
> +
> + /* Register the DMA engine with the core */
> + dma_async_device_register(&xdev->common);
> +
> + err = of_dma_controller_register(node, of_dma_xilinx_xlate,
> + &xdev->common);
> + if (err < 0)
> + dev_err(&pdev->dev, "Unable to register DMA to DT\n");
Shouldn't this return 'err'? Like when you can't register the DMA to the node,
you might want to bail out, don't you?
> +
> + return 0;
> +
> +error:
> + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
> + if (xdev->chan[i])
> + xilinx_vdma_chan_remove(xdev->chan[i]);
> + }
> +
> + return err;
> +}
> [...]
> +static int xilinx_vdmatest_add_slave_channels(struct dma_chan *tx_chan,
> + struct dma_chan *rx_chan)
> +{
> + struct xilinx_vdmatest_chan *tx_dtc, *rx_dtc;
> + unsigned int thread_count = 0;
> +
> + tx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
> + if (!tx_dtc)
> + return -ENOMEM;
> +
> + rx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
> + if (!rx_dtc)
> + return -ENOMEM;
That's a memory leak. You gotta kfree tx_dtc.
> [...]
>
--
Regards,
Levente Kurusa
^ permalink raw reply
* [PATCH 2/2] ARM: add DT support for cubox-i platforms
From: Russell King @ 2014-01-16 18:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116182211.GX15937@n2100.arm.linux.org.uk>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/imx6dl-cubox-i.dts | 12 ++++++
arch/arm/boot/dts/imx6q-cubox-i.dts | 16 +++++++
arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 78 ++++++++++++++++++++++++++++++++++
4 files changed, 108 insertions(+)
create mode 100644 arch/arm/boot/dts/imx6dl-cubox-i.dts
create mode 100644 arch/arm/boot/dts/imx6q-cubox-i.dts
create mode 100644 arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 875af333c6dc..1eeae00d9d09 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -136,11 +136,13 @@ dtb-$(CONFIG_ARCH_MXC) += \
imx53-mba53.dtb \
imx53-qsb.dtb \
imx53-smd.dtb \
+ imx6dl-cubox-i.dtb \
imx6dl-hummingboard.dtb \
imx6dl-sabreauto.dtb \
imx6dl-sabresd.dtb \
imx6dl-wandboard.dtb \
imx6q-arm2.dtb \
+ imx6q-cubox-i.dtb \
imx6q-phytec-pbab01.dtb \
imx6q-sabreauto.dtb \
imx6q-sabrelite.dtb \
diff --git a/arch/arm/boot/dts/imx6dl-cubox-i.dts b/arch/arm/boot/dts/imx6dl-cubox-i.dts
new file mode 100644
index 000000000000..58aa8f2b0f26
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-cubox-i.dts
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2014 Russell King
+ */
+/dts-v1/;
+
+#include "imx6dl.dtsi"
+#include "imx6qdl-cubox-i.dtsi"
+
+/ {
+ model = "SolidRun Cubox-i Solo/DualLite";
+ compatible = "solidrun,cubox-i/dl", "fsl,imx6dl";
+};
diff --git a/arch/arm/boot/dts/imx6q-cubox-i.dts b/arch/arm/boot/dts/imx6q-cubox-i.dts
new file mode 100644
index 000000000000..bc5f31e3e892
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-cubox-i.dts
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2014 Russell King
+ */
+/dts-v1/;
+
+#include "imx6q.dtsi"
+#include "imx6qdl-cubox-i.dtsi"
+
+/ {
+ model = "SolidRun Cubox-i Dual/Quad";
+ compatible = "solidrun,cubox-i/q", "fsl,imx6q";
+};
+
+&sata {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
new file mode 100644
index 000000000000..6fbca9063b10
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Russell King
+ */
+#include "imx6qdl-microsom.dtsi"
+#include "imx6qdl-microsom-ar8035.dtsi"
+
+/ {
+ ir_recv: ir-receiver {
+ compatible = "gpio-ir-receiver";
+ gpios = <&gpio3 9 1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cubox_i_ir>;
+ };
+
+ codec: spdif-transmitter {
+ compatible = "linux,spdif-dit";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cubox_i_spdif>;
+ };
+
+ sound-spdif {
+ compatible = "fsl,imx-audio-spdif";
+ model = "imx-spdif";
+ /* IMX6 doesn't implement this yet */
+ spdif-controller = <&spdif>;
+ spdif-out;
+ };
+};
+
+&i2c3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3_1>;
+
+ status = "okay";
+
+ rtc: pcf8523 at 68 {
+ compatible = "nxp,pcf8523";
+ reg = <0x68>;
+ };
+};
+
+&iomuxc {
+ cubox_i {
+ pinctrl_cubox_i_ir: cubox_i-ir {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x80000000
+ >;
+ };
+
+ pinctrl_cubox_i_spdif: cubox_i-spdif {
+ fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x1b0b0>;
+ };
+
+ pinctrl_cubox_i_usdhc2: cubox_i-usdhc2 {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059
+ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059
+ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
+ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
+ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
+ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059
+ MX6QDL_PAD_GPIO_4__SD2_CD_B 0x1f071
+ >;
+ };
+ };
+};
+
+&spdif {
+ status = "okay";
+};
+
+&usdhc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cubox_i_usdhc2>;
+ vmmc-supply = <®_3p3v>;
+ fsl,cd-controller;
+ status = "okay";
+};
--
1.8.3.1
^ permalink raw reply related
* [PATCH 1/2] ARM: move flexcan1 configuration to hummingboard
From: Russell King @ 2014-01-16 18:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116182211.GX15937@n2100.arm.linux.org.uk>
Flexcan1 is not used on the cubox-i, so move it out of the microsom.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/boot/dts/imx6dl-hummingboard.dts | 13 +++++++++++++
arch/arm/boot/dts/imx6qdl-microsom.dtsi | 13 -------------
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/arch/arm/boot/dts/imx6dl-hummingboard.dts b/arch/arm/boot/dts/imx6dl-hummingboard.dts
index 6184b0366926..e22f3d855c02 100644
--- a/arch/arm/boot/dts/imx6dl-hummingboard.dts
+++ b/arch/arm/boot/dts/imx6dl-hummingboard.dts
@@ -36,6 +36,12 @@
};
};
+&can1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hummingboard_flexcan1>;
+ status = "okay";
+};
+
&i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1_1>;
@@ -53,6 +59,13 @@
&iomuxc {
hummingboard {
+ pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 {
+ fsl,pins = <
+ MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000
+ MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000
+ >;
+ };
+
pinctrl_hummingboard_gpio1_2: hummingboard-gpio1_2 {
fsl,pins = <
MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000
diff --git a/arch/arm/boot/dts/imx6qdl-microsom.dtsi b/arch/arm/boot/dts/imx6qdl-microsom.dtsi
index 8aa31a842236..965ac4c1f509 100644
--- a/arch/arm/boot/dts/imx6qdl-microsom.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-microsom.dtsi
@@ -36,21 +36,8 @@
};
};
-&can1 {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_microsom_flexcan1>;
- status = "okay";
-};
-
&iomuxc {
microsom {
- pinctrl_microsom_flexcan1: microsom-flexcan1 {
- fsl,pins = <
- MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000
- MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000
- >;
- };
-
pinctrl_microsom_usbotg: microsom-usbotg {
/*
* Similar to pinctrl_usbotg_2, but we want it
--
1.8.3.1
^ permalink raw reply related
* [PATCH 0/2] DT updates for Hummingboard and new Cubox-i
From: Russell King - ARM Linux @ 2014-01-16 18:22 UTC (permalink / raw)
To: linux-arm-kernel
Now that the Cubox-i has been released, and I have one, I've been able to
update things a little. Out of the following two patches, the first one
I think is critical to be merged before the microsom support hits
mainline, since it drops the flexcan support from the microsom level,
moving it to the Hummingboard level.
That doesn't have _too_ much visible effect, but it's something which
should not be there (since as far as the microsom is concerned, there's
nothing special about these pads.)
The second adds basic support for the Cubox-i - as I have only the i4,
this is the platform which this has been tested against, but I've
generated the solo/dual-lite version as well since the only difference
at this basic level is the eSATA.
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/imx6dl-cubox-i.dts | 12 +++++
arch/arm/boot/dts/imx6dl-hummingboard.dts | 13 ++++++
arch/arm/boot/dts/imx6q-cubox-i.dts | 16 +++++++
arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 78 +++++++++++++++++++++++++++++++
arch/arm/boot/dts/imx6qdl-microsom.dtsi | 13 ------
6 files changed, 121 insertions(+), 13 deletions(-)
create mode 100644 arch/arm/boot/dts/imx6dl-cubox-i.dts
create mode 100644 arch/arm/boot/dts/imx6q-cubox-i.dts
create mode 100644 arch/arm/boot/dts/imx6qdl-cubox-i.dtsi
--
FTTC broadband for 0.8mile line: 5.8Mbps down 500kbps up. Estimation
in database were 13.1 to 19Mbit for a good line, about 7.5+ for a bad.
Estimate before purchase was "up to 13.2Mbit".
^ permalink raw reply
* [PATCH 2/4] ARM: sun6i: dt: Add PLL6 and SPI module clocks
From: Josh Cartwright @ 2014-01-16 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389892285-11745-3-git-send-email-maxime.ripard@free-electrons.com>
On Thu, Jan 16, 2014 at 06:11:23PM +0100, Maxime Ripard wrote:
> The module clocks in the A31 are still compatible with the A10 one. Add the SPI
> module clocks and the PLL6 in the device tree to allow their use by the SPI
> controllers.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> arch/arm/boot/dts/sun6i-a31.dtsi | 48 +++++++++++++++++++++++++++++++---------
> 1 file changed, 38 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
> index 5256ad9..ae058eb 100644
> --- a/arch/arm/boot/dts/sun6i-a31.dtsi
> +++ b/arch/arm/boot/dts/sun6i-a31.dtsi
> @@ -73,16 +73,12 @@
> clocks = <&osc24M>;
> };
>
> - /*
> - * This is a dummy clock, to be used as placeholder on
> - * other mux clocks when a specific parent clock is not
> - * yet implemented. It should be dropped when the driver
> - * is complete.
> - */
> - pll6: pll6 {
> - #clock-cells = <0>;
> - compatible = "fixed-clock";
> - clock-frequency = <0>;
> + pll6: clk at 01c20028 {
> + #clock-cells = <1>;
> + compatible = "allwinner,sun6i-a31-pll6-clk";
> + reg = <0x01c20028 0x4>;
> + clocks = <&osc24M>;
> + clock-output-names = "pll6";
> };
>
> cpu: cpu at 01c20050 {
> @@ -182,6 +178,38 @@
> "apb2_uart1", "apb2_uart2", "apb2_uart3",
> "apb2_uart4", "apb2_uart5";
> };
> +
> + spi0_clk: clk at 01c200a0 {
> + #clock-cells = <0>;
> + compatible = "allwinner,sun4i-mod0-clk";
> + reg = <0x01c200a0 0x4>;
> + clocks = <&osc24M>, <&pll6>;
This looks weird. You've set the pll6 #clock-cells = <1>, but you
aren't using a specifier here. Same below, as well. The binding
documentation indicates that #clock-cells should be 0 for the pll6 node.
Josh
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* [PATCH v4 13/16] ARM: Add an emulate flag to the kprobes/uprobes instruction decode functions
From: David Long @ 2014-01-16 18:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389863932.3530.12.camel@linaro1.home>
On 01/16/14 04:18, Jon Medhurst (Tixy) wrote:
> On Wed, 2014-01-15 at 14:31 -0500, David Long wrote:
>> On 12/20/13 09:58, Jon Medhurst (Tixy) wrote:
>>> On Sun, 2013-12-15 at 23:08 -0500, David Long wrote:
> [...]
>>>> {
>>>> #ifdef CONFIG_THUMB2_KERNEL
>>>> if (thumb) {
>>>> @@ -253,7 +253,7 @@ set_emulated_insn(probes_opcode_t insn, struct arch_specific_insn *asi,
>>>> * non-zero value, the corresponding nibble in pinsn is validated and modified
>>>> * according to the type.
>>>> */
>>>> -static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs)
>>>> +static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify)
>>>> {
>>>> probes_opcode_t insn = *pinsn;
>>>> probes_opcode_t mask = 0xf; /* Start at least significant nibble */
>>>> @@ -317,9 +317,16 @@ static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs)
>>>> /* Replace value of nibble with new register number... */
>>>> insn &= ~mask;
>>>> insn |= new_bits & mask;
>>>> + if (modify) {
>>>> + /* Replace value of nibble with new register number */
>>>> + insn &= ~mask;
>>>> + insn |= new_bits & mask;
>>>> + }
>>>
>>> Huh? As is, the above addition doesn't do anything because insn has
>>> already been modified. I guess you played with the idea that you needed
>>> to avoid changing insn (you don't) and then didn't undo the experiment
>>> quite right. :-)
>>>
>>
>> The conditional modification of the instruction was part of Rabin's
>> original work for uprobes, but I messed up the merge from an earlier
>> working version of my patches. My intention was/is to delete the old
>> unconditional code. Sounds like maybe you disagree though. The intent
>> is to only modify the instruction in the kprobes case.
>
> 'insn' is the local variable containing the instruction value we're
> processing. It doesn't matter if we change that, we just need to avoid
> updating the instruction in memory, which the code in the next chunk
> already correctly checks for...
>
>>>> }
>>>>
>>>> - *pinsn = insn;
>>>> + if (modify)
>>>> + *pinsn = insn;
>>>> +
>>>> return true;
>>>>
>
> So only one of these 'if (modify)' checks is required for code
> correctness, and I suggest keeping the second one as it's more explicit
> and defensive.
>
>
OK, I see your point. I shall simplify the code as you have suggested.
-dl
^ permalink raw reply
* [PATCH v5 2/4] devicetree: bindings: Document Krait CPU/L1 EDAC
From: Stephen Boyd @ 2014-01-16 18:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116113332.GC25540@e102568-lin.cambridge.arm.com>
On 01/16, Lorenzo Pieralisi wrote:
> On Thu, Jan 16, 2014 at 01:38:40AM +0000, Stephen Boyd wrote:
> > On 01/15, Stephen Boyd wrote:
> > >
> > > Ah sorry, I forgot to put the compatible property here like in
> > > the dts change. I'll do that in the next revision. Yes we need a
> > > compatible property here to match the platform driver.
> > >
> >
> > This is the replacement patch
> >
> > -----8<------
> > From: Stephen Boyd <sboyd@codeaurora.org>
> > Subject: [PATCH v9] devicetree: bindings: Document Krait CPU/L1 EDAC
> >
> > The Krait CPU/L1 error reporting device is made up a per-CPU
> > interrupt. While we're here, document the next-level-cache
> > property that's used by the Krait EDAC driver.
> >
> > Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> > Cc: Kumar Gala <galak@codeaurora.org>
> > Cc: <devicetree@vger.kernel.org>
> > Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> > ---
> > Documentation/devicetree/bindings/arm/cpus.txt | 58 ++++++++++++++++++++++++++
> > 1 file changed, 58 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
> > index 91304353eea4..03a529e791c4 100644
> > --- a/Documentation/devicetree/bindings/arm/cpus.txt
> > +++ b/Documentation/devicetree/bindings/arm/cpus.txt
> > @@ -62,6 +62,20 @@ nodes to be present and contain the properties described below.
> > Value type: <u32>
> > Definition: must be set to 0
> >
> > + - compatible
> > + Usage: optional
> > + Value type: <string>
> > + Definition: should be one of the compatible strings listed
> > + in the cpu node compatible property. This property
> > + shall only be present if all the cpu nodes have the
> > + same compatible property.
>
> Do we really want to do that ? I am not sure. A cpus node is supposed to
> be a container node, we should not define this binding just because we
> know the kernel creates a platform device for it then.
This is just copying more of the ePAPR spec into this document.
It just so happens that having a compatible field here allows a
platform device to be created. I don't see why that's a problem.
>
> interrupts is a cpu node property and I think it should be kept as such.
>
> I know it will be duplicated and I know you can't rely on a platform
> device for probing (since if I am not mistaken, removing a compatible
> string from cpus prevents its platform device creation), but that's an issue
> related to how the kernel works, you should not define DT bindings to solve
> that IMHO.
The interrupts property is also common for all cpus so it seems
fine to collapse the value down into a PPI specifier indicating
that all CPUs get the interrupt, similar to how we compress the
information about the compatible string.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* [PATCH] of: add function to count number of u32 elements in a property
From: Heiko Stübner @ 2014-01-16 18:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116124527.GD19578@e106331-lin.cambridge.arm.com>
The need to know the number of array elements in a property is
a common pattern. To prevent duplication of open-coded implementations
add a helper function that also centralises strict sanity checking
and DTB format details.
Suggested-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
Hi Mark,
did you mean it like this? I've tested it with the sram-reserve change and
it made the part of the determining the number elements a lot nicer :-)
drivers/of/base.c | 32 ++++++++++++++++++++++++++++++++
include/linux/of.h | 8 ++++++++
2 files changed, 40 insertions(+)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index f807d0e..0f40ea5 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -920,6 +920,38 @@ int of_property_read_u32_index(const struct device_node *np,
EXPORT_SYMBOL_GPL(of_property_read_u32_index);
/**
+ * of_property_count_u32_elems - Count the number of u32 values in a property
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device node and count the number of u32 elements
+ * in it. Returns number of elements on sucess, -EINVAL if the property does
+ * not exist or its length does not match a multiple of u32 and -ENODATA if the
+ * property does not have a value.
+ */
+int of_property_count_u32_elems(const struct device_node *np,
+ const char *propname)
+{
+ int elem_size = sizeof(u32);
+ int len;
+ struct property *prop = of_find_property(np, propname, &len);
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ if (prop->length % elem_size != 0) {
+ pr_err("size of %s is not a multiple of u32\n", propname);
+ return -EINVAL;
+ }
+
+ return len / elem_size;
+}
+EXPORT_SYMBOL_GPL(of_property_count_u32_elems);
+
+/**
* of_property_read_u8_array - Find and read an array of u8 from a property.
*
* @np: device node from which the property value is to be read.
diff --git a/include/linux/of.h b/include/linux/of.h
index 276c546..5794942 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -253,6 +253,8 @@ extern struct property *of_find_property(const struct device_node *np,
extern int of_property_read_u32_index(const struct device_node *np,
const char *propname,
u32 index, u32 *out_value);
+extern int of_property_count_u32_elems(const struct device_node *np,
+ const char *propname);
extern int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz);
extern int of_property_read_u16_array(const struct device_node *np,
@@ -432,6 +434,12 @@ static inline int of_property_read_u32_index(const struct device_node *np,
return -ENOSYS;
}
+static inline int of_property_count_u32_elems(const struct device_node *np,
+ const char *propname)
+{
+ return -ENOSYS;
+}
+
static inline int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz)
{
--
1.7.10.4
^ permalink raw reply related
* [PATCH] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Srikanth Thokala @ 2014-01-16 17:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389894803-4147-1-git-send-email-sthokal@xilinx.com>
This is the driver for the AXI Video Direct Memory Access (AXI
VDMA) core, which is a soft Xilinx IP core that provides high-
bandwidth direct memory access between memory and AXI4-Stream
type video target peripherals. The core provides efficient two
dimensional DMA operations with independent asynchronous read
and write channel operation.
This module works on Zynq (ARM Based SoC) and Microblaze platforms.
Signed-off-by: Srikanth Thokala <sthokal@xilinx.com>
---
NOTE:
1. Created a separate directory 'dma/xilinx' as Xilinx has two more
DMA IPs and we are also planning to upstream these drivers soon.
2. Rebased on v3.13.0-rc8
---
.../devicetree/bindings/dma/xilinx/xilinx_vdma.txt | 71 +
.../bindings/dma/xilinx/xilinx_vdma_test.txt | 39 +
drivers/dma/Kconfig | 23 +
drivers/dma/Makefile | 1 +
drivers/dma/xilinx/Makefile | 2 +
drivers/dma/xilinx/xilinx_vdma.c | 1497 ++++++++++++++++++++
drivers/dma/xilinx/xilinx_vdma_test.c | 629 ++++++++
7 files changed, 2262 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
create mode 100644 drivers/dma/xilinx/Makefile
create mode 100644 drivers/dma/xilinx/xilinx_vdma.c
create mode 100644 drivers/dma/xilinx/xilinx_vdma_test.c
diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
new file mode 100644
index 0000000..3f5c428
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
@@ -0,0 +1,71 @@
+Xilinx AXI VDMA engine, it does transfers between memory and video devices.
+It can be configured to have one channel or two channels. If configured
+as two channels, one is to transmit to the video device and another is
+to receive from the video device.
+
+Required properties:
+- compatible: Should be "xlnx,axi-vdma-1.00.a"
+- #dma-cells: Should be <1>, see "dmas" property below
+- reg: Should contain VDMA registers location and length.
+- interrupts: Should contain per channel VDMA interrupts.
+- compatible (child node): It should be either "xlnx,axi-vdma-mm2s-channel" or
+ "xlnx,axi-vdma-s2mm-channel". It depends on the hardware design and it
+ can also have both channels.
+- xlnx,device-id: Should contain device number in each channel. It should be
+ {0,1,2...so on} to the number of VDMA devices configured in hardware.
+- xlnx,num-fstores: Should be the number of framebuffers as configured in h/w.
+- xlnx,data-width: Should contain the stream data width, takes {32,64...so on}.
+- xlnx,flush-fsync: (Optional) Tells whether which channel to Flush on Fsync.
+ It takes following values:
+ {1}, flush both channels
+ {2}, flush mm2s channel
+ {3}, flush s2mm channel
+- xlnx,include-sg: (Optional) Tells whether configured for Scatter-mode in
+ the hardware.
+- xlnx,include-dre: (Optional) Tells whether hardware is configured for Data
+ Realignment Engine.
+- xlnx,genlock-mode: (Optional) Tells whether Genlock synchornisation is
+ enabled/disabled in hardware.
+
+Example:
+++++++++
+
+axi_vdma_0: axivdma at 40030000 {
+ compatible = "xlnx,axi-vdma-1.00.a";
+ #dma_cells = <1>;
+ reg = < 0x40030000 0x10000 >;
+ xlnx,flush-fsync = <0x1>;
+ dma-channel at 40030000 {
+ compatible = "xlnx,axi-vdma-mm2s-channel";
+ interrupts = < 0 54 4 >;
+ xlnx,num-fstores = <0x8>;
+ xlnx,device-id = <0x0>;
+ xlnx,datawidth = <0x40>;
+ } ;
+ dma-channel at 40030030 {
+ compatible = "xlnx,axi-vdma-s2mm-channel";
+ interrupts = < 0 53 4 >;
+ xlnx,num-fstores = <0x8>;
+ xlnx,device-id = <0x0>;
+ xlnx,datawidth = <0x40>;
+ } ;
+} ;
+
+
+* Xilinx Video DMA client
+
+Required properties:
+- dmas: a list of <[Video DMA device phandle] [Channel ID]> pairs,
+ where Channel ID is '0' for write/tx and '1' for read/rx
+ channel.
+- dma-names: a list of DMA channel names, one per "dmas" entry
+
+VDMA Test Client Example:
++++++++++++++++++++++++++
+
+vdmatest_0: vdmatest at 0 {
+ compatible ="xlnx,axi-vdma-test-1.00.a";
+ dmas = <&axi_vdma_0 0
+ &axi_vdma_0 1>;
+ dma-names = "vdma0", "vdma1";
+} ;
diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
new file mode 100644
index 0000000..5821fdc
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
@@ -0,0 +1,39 @@
+* Xilinx Video DMA Test client
+
+Required properties:
+- compatible: Should be "xlnx,axi-vdma-test-1.00.a"
+- dmas: a list of <[Video DMA device phandle] [Channel ID]> pairs,
+ where Channel ID is '0' for write/tx and '1' for read/rx
+ channel.
+- dma-names: a list of DMA channel names, one per "dmas" entry
+- xlnx,num-fstores: Should be the number of framebuffers as configured in
+ VDMA device node.
+
+Example:
+++++++++
+
+vdmatest_0: vdmatest at 0 {
+ compatible ="xlnx,axi-vdma-test-1.00.a";
+ dmas = <&axi_vdma_0 0
+ &axi_vdma_0 1>;
+ dma-names = "vdma0", "vdma1";
+ xlnx,num-fstores = <0x8>;
+} ;
+
+
+Xilinx Video DMA Device Node Example
+++++++++++++++++++++++++++++++++++++
+axi_vdma_0: axivdma at 44A40000 {
+ compatible = "xlnx,axi-vdma-1.00.a";
+ ...
+ dma-channel at 44A40000 {
+ ...
+ xlnx,num-fstores = <0x8>;
+ ...
+ } ;
+ dma-channel at 44A40030 {
+ ...
+ xlnx,num-fstores = <0x8>;
+ ...
+ } ;
+} ;
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index c823daa..675719f 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -334,6 +334,20 @@ config K3_DMA
Support the DMA engine for Hisilicon K3 platform
devices.
+config XILINX_VDMA
+ tristate "Xilinx AXI VDMA Engine"
+ depends on (ARCH_ZYNQ || MICROBLAZE)
+ select DMA_ENGINE
+ help
+ Enable support for Xilinx AXI VDMA Soft IP.
+
+ This engine provides high-bandwidth direct memory access
+ between memory and AXI4-Stream video type target
+ peripherals including peripherals which support AXI4-
+ Stream Video Protocol. It has two stream interfaces/
+ channels, Memory Mapped to Stream (MM2S) and Stream to
+ Memory Mapped (S2MM) for the data transfers.
+
config DMA_ENGINE
bool
@@ -384,4 +398,13 @@ config DMATEST
config DMA_ENGINE_RAID
bool
+config XILINX_VDMA_TEST
+ tristate "DMA Test client for Xilinx AXI VDMA"
+ depends on XILINX_VDMA
+ help
+ Simple VDMA driver test client. This test assumes both
+ the stream interfaces of VDMA engine, MM2S and S2MM are
+ connected back-to-back in the hardware design.
+
+ Say N unless you're debugging a DMA Device driver.
endif
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 0ce2da9..d84130b 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_K3_DMA) += k3dma.o
+obj-y += xilinx/
diff --git a/drivers/dma/xilinx/Makefile b/drivers/dma/xilinx/Makefile
new file mode 100644
index 0000000..cef1e88
--- /dev/null
+++ b/drivers/dma/xilinx/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_XILINX_VDMA) += xilinx_vdma.o
+obj-$(CONFIG_XILINX_VDMA_TEST) += xilinx_vdma_test.o
diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c
new file mode 100644
index 0000000..66a12de
--- /dev/null
+++ b/drivers/dma/xilinx/xilinx_vdma.c
@@ -0,0 +1,1497 @@
+/*
+ * DMA driver for Xilinx Video DMA Engine
+ *
+ * Copyright (C) 2010-2013 Xilinx, Inc. All rights reserved.
+ *
+ * Based on the Freescale DMA driver.
+ *
+ * Description:
+ * The AXI Video Direct Memory Access (AXI VDMA) core is a soft Xilinx IP
+ * core that provides high-bandwidth direct memory access between memory
+ * and AXI4-Stream type video target peripherals. The core provides efficient
+ * two dimensional DMA operations with independent asynchronous read (S2MM)
+ * and write (MM2S) channel operation. It can be configured to have either
+ * one channel or two channels. If configured as two channels, one is to
+ * transmit to the video device (MM2S) and another is to receive from the
+ * video device (S2MM). Initialization, status, interrupt and management
+ * registers are accessed through an AXI4-Lite slave interface.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/amba/xilinx_dma.h>
+#include <linux/bitops.h>
+#include <linux/dmapool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_dma.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+
+/* Register/Descriptor Offsets */
+#define XILINX_VDMA_MM2S_CTRL_OFFSET 0x0000
+#define XILINX_VDMA_S2MM_CTRL_OFFSET 0x0030
+#define XILINX_VDMA_MM2S_DESC_OFFSET 0x0050
+#define XILINX_VDMA_S2MM_DESC_OFFSET 0x00a0
+
+/* Control Registers */
+#define XILINX_VDMA_REG_DMACR 0x0000
+#define XILINX_VDMA_DMACR_DELAY_MAX 0xff
+#define XILINX_VDMA_DMACR_DELAY_SHIFT 24
+#define XILINX_VDMA_DMACR_FRAME_COUNT_MAX 0xff
+#define XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT 16
+#define XILINX_VDMA_DMACR_ERR_IRQ (1 << 14)
+#define XILINX_VDMA_DMACR_DLY_CNT_IRQ (1 << 13)
+#define XILINX_VDMA_DMACR_FRM_CNT_IRQ (1 << 12)
+#define XILINX_VDMA_DMACR_MASTER_SHIFT 8
+#define XILINX_VDMA_DMACR_FSYNCSRC_SHIFT 5
+#define XILINX_VDMA_DMACR_FRAMECNT_EN (1 << 4)
+#define XILINX_VDMA_DMACR_GENLOCK_EN (1 << 3)
+#define XILINX_VDMA_DMACR_RESET (1 << 2)
+#define XILINX_VDMA_DMACR_CIRC_EN (1 << 1)
+#define XILINX_VDMA_DMACR_RUNSTOP (1 << 0)
+#define XILINX_VDMA_DMACR_DELAY_MASK \
+ (XILINX_VDMA_DMACR_DELAY_MAX << \
+ XILINX_VDMA_DMACR_DELAY_SHIFT)
+#define XILINX_VDMA_DMACR_FRAME_COUNT_MASK \
+ (XILINX_VDMA_DMACR_FRAME_COUNT_MAX << \
+ XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT)
+#define XILINX_VDMA_DMACR_MASTER_MASK \
+ (0xf << XILINX_VDMA_DMACR_MASTER_SHIFT)
+#define XILINX_VDMA_DMACR_FSYNCSRC_MASK \
+ (3 << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT)
+
+#define XILINX_VDMA_REG_DMASR 0x0004
+#define XILINX_VDMA_DMASR_DELAY_SHIFT 24
+#define XILINX_VDMA_DMASR_FRAME_COUNT_SHIFT 16
+#define XILINX_VDMA_DMASR_EOL_LATE_ERR (1 << 15)
+#define XILINX_VDMA_DMASR_ERR_IRQ (1 << 14)
+#define XILINX_VDMA_DMASR_DLY_CNT_IRQ (1 << 13)
+#define XILINX_VDMA_DMASR_FRM_CNT_IRQ (1 << 12)
+#define XILINX_VDMA_DMASR_SOF_LATE_ERR (1 << 11)
+#define XILINX_VDMA_DMASR_SG_DEC_ERR (1 << 10)
+#define XILINX_VDMA_DMASR_SG_SLV_ERR (1 << 9)
+#define XILINX_VDMA_DMASR_EOF_EARLY_ERR (1 << 8)
+#define XILINX_VDMA_DMASR_SOF_EARLY_ERR (1 << 7)
+#define XILINX_VDMA_DMASR_DMA_DEC_ERR (1 << 6)
+#define XILINX_VDMA_DMASR_DMA_SLAVE_ERR (1 << 5)
+#define XILINX_VDMA_DMASR_DMA_INT_ERR (1 << 4)
+#define XILINX_VDMA_DMASR_IDLE (1 << 1)
+#define XILINX_VDMA_DMASR_HALTED (1 << 0)
+#define XILINX_VDMA_DMASR_DELAY_MASK \
+ (0xff << XILINX_VDMA_DMASR_DELAY_SHIFT)
+#define XILINX_VDMA_DMASR_FRAME_COUNT_MASK \
+ (0xff << XILINX_VDMA_DMASR_FRAME_COUNT_SHIFT)
+
+#define XILINX_VDMA_REG_CURDESC 0x0008
+#define XILINX_VDMA_REG_TAILDESC 0x0010
+#define XILINX_VDMA_REG_REG_INDEX 0x0014
+#define XILINX_VDMA_REG_FRMSTORE 0x0018
+#define XILINX_VDMA_REG_THRESHOLD 0x001c
+#define XILINX_VDMA_REG_FRMPTR_STS 0x0024
+#define XILINX_VDMA_REG_PARK_PTR 0x0028
+#define XILINX_VDMA_PARK_PTR_WR_REF_SHIFT 8
+#define XILINX_VDMA_PARK_PTR_RD_REF_SHIFT 0
+#define XILINX_VDMA_REG_VDMA_VERSION 0x002c
+
+/* Register Direct Mode Registers */
+#define XILINX_VDMA_REG_VSIZE 0x0000
+#define XILINX_VDMA_REG_HSIZE 0x0004
+
+#define XILINX_VDMA_REG_FRMDLY_STRIDE 0x0008
+#define XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT 24
+#define XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT 0
+#define XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_MASK \
+ (0x1f << \
+ XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT)
+#define XILINX_VDMA_FRMDLY_STRIDE_STRIDE_MASK \
+ (0xffff << \
+ XILINX_VDMA_FRMDLY_STRIDE_STRIDE_MASK)
+
+#define XILINX_VDMA_REG_START_ADDRESS(n) (0x000c + 4 * (n))
+
+/* Hw specific definitions */
+#define XILINX_VDMA_MAX_CHANS_PER_DEVICE 0x2
+
+#define XILINX_VDMA_DMAXR_ALL_IRQ_MASK (XILINX_VDMA_DMASR_FRM_CNT_IRQ | \
+ XILINX_VDMA_DMASR_DLY_CNT_IRQ | \
+ XILINX_VDMA_DMASR_ERR_IRQ)
+
+#define XILINX_VDMA_DMASR_ALL_ERR_MASK (XILINX_VDMA_DMASR_EOL_LATE_ERR | \
+ XILINX_VDMA_DMASR_SOF_LATE_ERR | \
+ XILINX_VDMA_DMASR_SG_DEC_ERR | \
+ XILINX_VDMA_DMASR_SG_SLV_ERR | \
+ XILINX_VDMA_DMASR_EOF_EARLY_ERR | \
+ XILINX_VDMA_DMASR_SOF_EARLY_ERR | \
+ XILINX_VDMA_DMASR_DMA_DEC_ERR | \
+ XILINX_VDMA_DMASR_DMA_SLAVE_ERR | \
+ XILINX_VDMA_DMASR_DMA_INT_ERR)
+
+/*
+ * Recoverable errors are DMA Internal error, SOF Early, EOF Early and SOF Late.
+ * They are only recoverable when C_FLUSH_ON_FSYNC is enabled in the h/w system.
+ */
+#define XILINX_VDMA_DMASR_ERR_RECOVER_MASK \
+ (XILINX_VDMA_DMASR_SOF_LATE_ERR | \
+ XILINX_VDMA_DMASR_EOF_EARLY_ERR | \
+ XILINX_VDMA_DMASR_SOF_EARLY_ERR | \
+ XILINX_VDMA_DMASR_DMA_INT_ERR)
+
+/* Axi VDMA Flush on Fsync bits */
+#define XILINX_VDMA_FLUSH_S2MM 3
+#define XILINX_VDMA_FLUSH_MM2S 2
+#define XILINX_VDMA_FLUSH_BOTH 1
+
+/* Delay loop counter to prevent hardware failure */
+#define XILINX_VDMA_LOOP_COUNT 1000000
+
+/**
+ * struct xilinx_vdma_desc_hw - Hardware Descriptor
+ * @next_desc: Next Descriptor Pointer @0x00
+ * @pad1: Reserved @0x04
+ * @buf_addr: Buffer address @0x08
+ * @pad2: Reserved @0x0C
+ * @vsize: Vertical Size @0x10
+ * @hsize: Horizontal Size @0x14
+ * @stride: Number of bytes between the first
+ * pixels of each horizontal line @0x18
+ */
+struct xilinx_vdma_desc_hw {
+ u32 next_desc;
+ u32 pad1;
+ u32 buf_addr;
+ u32 pad2;
+ u32 vsize;
+ u32 hsize;
+ u32 stride;
+} __aligned(64);
+
+/**
+ * struct xilinx_vdma_tx_segment - Descriptor segment
+ * @hw: Hardware descriptor
+ * @node: Node in the descriptor segments list
+ * @cookie: Segment cookie
+ * @phys: Physical address of segment
+ */
+struct xilinx_vdma_tx_segment {
+ struct xilinx_vdma_desc_hw hw;
+ struct list_head node;
+ dma_cookie_t cookie;
+ dma_addr_t phys;
+} __aligned(64);
+
+/**
+ * struct xilinx_vdma_tx_descriptor - Per Transaction structure
+ * @async_tx: Async transaction descriptor
+ * @segments: TX segments list
+ * @node: Node in the channel descriptors list
+ */
+struct xilinx_vdma_tx_descriptor {
+ struct dma_async_tx_descriptor async_tx;
+ struct list_head segments;
+ struct list_head node;
+};
+
+#define to_vdma_tx_descriptor(tx) \
+ container_of(tx, struct xilinx_vdma_tx_descriptor, async_tx)
+
+/**
+ * struct xilinx_vdma_chan - Driver specific VDMA channel structure
+ * @xdev: Driver specific device structure
+ * @ctrl_offset: Control registers offset
+ * @desc_offset: TX descriptor registers offset
+ * @completed_cookie: Maximum cookie completed
+ * @cookie: The current cookie
+ * @lock: Descriptor operation lock
+ * @pending_list: Descriptors waiting
+ * @active_desc: Active descriptor
+ * @done_list: Complete descriptors
+ * @common: DMA common channel
+ * @desc_pool: Descriptors pool
+ * @dev: The dma device
+ * @irq: Channel IRQ
+ * @id: Channel ID
+ * @direction: Transfer direction
+ * @num_frms: Number of frames
+ * @has_sg: Support scatter transfers
+ * @genlock: Support genlock mode
+ * @err: Channel has errors
+ * @tasklet: Cleanup work after irq
+ * @private: Match info for channel request
+ * @config: Device configuration info
+ * @flush_on_fsync: Flush on Frame sync
+ */
+struct xilinx_vdma_chan {
+ struct xilinx_vdma_device *xdev;
+ u32 ctrl_offset;
+ u32 desc_offset;
+ dma_cookie_t completed_cookie;
+ dma_cookie_t cookie;
+ spinlock_t lock;
+ struct list_head pending_list;
+ struct xilinx_vdma_tx_descriptor *active_desc;
+ struct list_head done_list;
+ struct dma_chan common;
+ struct dma_pool *desc_pool;
+ struct device *dev;
+ int irq;
+ int id;
+ enum dma_transfer_direction direction;
+ int num_frms;
+ bool has_sg;
+ bool genlock;
+ bool err;
+ struct tasklet_struct tasklet;
+ u32 private;
+ struct xilinx_vdma_config config;
+ bool flush_on_fsync;
+};
+
+/**
+ * struct xilinx_vdma_device - VDMA device structure
+ * @regs: I/O mapped base address
+ * @dev: Device Structure
+ * @common: DMA device structure
+ * @chan: Driver specific VDMA channel
+ * @has_sg: Specifies whether Scatter-Gather is present or not
+ * @flush_on_fsync: Flush on frame sync
+ */
+struct xilinx_vdma_device {
+ void __iomem *regs;
+ struct device *dev;
+ struct dma_device common;
+ struct xilinx_vdma_chan *chan[XILINX_VDMA_MAX_CHANS_PER_DEVICE];
+ bool has_sg;
+ u32 flush_on_fsync;
+};
+
+#define to_xilinx_chan(chan) \
+ container_of(chan, struct xilinx_vdma_chan, common)
+
+/* IO accessors */
+static inline u32 vdma_read(struct xilinx_vdma_chan *chan, u32 reg)
+{
+ return ioread32(chan->xdev->regs + reg);
+}
+
+static inline void vdma_write(struct xilinx_vdma_chan *chan, u32 reg, u32 value)
+{
+ iowrite32(value, chan->xdev->regs + reg);
+}
+
+static inline void vdma_desc_write(struct xilinx_vdma_chan *chan, u32 reg,
+ u32 value)
+{
+ vdma_write(chan, chan->desc_offset + reg, value);
+}
+
+static inline u32 vdma_ctrl_read(struct xilinx_vdma_chan *chan, u32 reg)
+{
+ return vdma_read(chan, chan->ctrl_offset + reg);
+}
+
+static inline void vdma_ctrl_write(struct xilinx_vdma_chan *chan, u32 reg,
+ u32 value)
+{
+ vdma_write(chan, chan->ctrl_offset + reg, value);
+}
+
+static inline void vdma_ctrl_clr(struct xilinx_vdma_chan *chan, u32 reg,
+ u32 clr)
+{
+ vdma_ctrl_write(chan, reg, vdma_ctrl_read(chan, reg) & ~clr);
+}
+
+static inline void vdma_ctrl_set(struct xilinx_vdma_chan *chan, u32 reg,
+ u32 set)
+{
+ vdma_ctrl_write(chan, reg, vdma_ctrl_read(chan, reg) | set);
+}
+
+/* -----------------------------------------------------------------------------
+ * Descriptors and segments alloc and free
+ */
+
+/**
+ * xilinx_vdma_alloc_tx_segment - Allocate transaction segment
+ * @chan: Driver specific VDMA channel
+ *
+ * Return: The allocated segment on success and NULL on failure.
+ */
+static struct xilinx_vdma_tx_segment *
+xilinx_vdma_alloc_tx_segment(struct xilinx_vdma_chan *chan)
+{
+ struct xilinx_vdma_tx_segment *segment;
+ dma_addr_t phys;
+
+ segment = dma_pool_alloc(chan->desc_pool, GFP_ATOMIC, &phys);
+ if (!segment)
+ return NULL;
+
+ memset(segment, 0, sizeof(*segment));
+ segment->phys = phys;
+
+ return segment;
+}
+
+/**
+ * xilinx_vdma_free_tx_segment - Free transaction segment
+ * @chan: Driver specific VDMA channel
+ * @segment: VDMA transaction segment
+ */
+static void xilinx_vdma_free_tx_segment(struct xilinx_vdma_chan *chan,
+ struct xilinx_vdma_tx_segment *segment)
+{
+ dma_pool_free(chan->desc_pool, segment, segment->phys);
+}
+
+/**
+ * xilinx_vdma_tx_descriptor - Allocate transaction descriptor
+ * @chan: Driver specific VDMA channel
+ *
+ * Return: The allocated descriptor on success and NULL on failure.
+ */
+static struct xilinx_vdma_tx_descriptor *
+xilinx_vdma_alloc_tx_descriptor(struct xilinx_vdma_chan *chan)
+{
+ struct xilinx_vdma_tx_descriptor *desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return NULL;
+
+ INIT_LIST_HEAD(&desc->segments);
+
+ return desc;
+}
+
+/**
+ * xilinx_vdma_free_tx_descriptor - Free transaction descriptor
+ * @chan: Driver specific VDMA channel
+ * @desc: VDMA transaction descriptor
+ */
+static void
+xilinx_vdma_free_tx_descriptor(struct xilinx_vdma_chan *chan,
+ struct xilinx_vdma_tx_descriptor *desc)
+{
+ struct xilinx_vdma_tx_segment *segment, *next;
+
+ if (!desc)
+ return;
+
+ list_for_each_entry_safe(segment, next, &desc->segments, node) {
+ list_del(&segment->node);
+ xilinx_vdma_free_tx_segment(chan, segment);
+ }
+
+ kfree(desc);
+}
+
+/* Required functions */
+
+/**
+ * xilinx_vdma_free_descriptors - Free descriptors list
+ * @chan: Driver specific VDMA channel
+ * @list: List to parse and delete the descriptor
+ */
+static void xilinx_vdma_free_desc_list(struct xilinx_vdma_chan *chan,
+ struct list_head *list)
+{
+ struct xilinx_vdma_tx_descriptor *desc, *next;
+
+ list_for_each_entry_safe(desc, next, list, node) {
+ list_del(&desc->node);
+ xilinx_vdma_free_tx_descriptor(chan, desc);
+ }
+}
+
+/**
+ * xilinx_vdma_free_descriptors - Free channel descriptors
+ * @chan: Driver specific VDMA channel
+ */
+static void xilinx_vdma_free_descriptors(struct xilinx_vdma_chan *chan)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ xilinx_vdma_free_desc_list(chan, &chan->pending_list);
+ xilinx_vdma_free_desc_list(chan, &chan->done_list);
+
+ xilinx_vdma_free_tx_descriptor(chan, chan->active_desc);
+ chan->active_desc = NULL;
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/**
+ * xilinx_vdma_free_chan_resources - Free channel resources
+ * @dchan: DMA channel
+ */
+static void xilinx_vdma_free_chan_resources(struct dma_chan *dchan)
+{
+ struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
+
+ dev_dbg(chan->dev, "Free all channel resources.\n");
+
+ tasklet_kill(&chan->tasklet);
+ xilinx_vdma_free_descriptors(chan);
+ dma_pool_destroy(chan->desc_pool);
+ chan->desc_pool = NULL;
+}
+
+/**
+ * xilinx_vdma_chan_desc_cleanup - Clean channel descriptors
+ * @chan: Driver specific VDMA channel
+ */
+static void xilinx_vdma_chan_desc_cleanup(struct xilinx_vdma_chan *chan)
+{
+ struct xilinx_vdma_tx_descriptor *desc, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ list_for_each_entry_safe(desc, next, &chan->done_list, node) {
+ dma_async_tx_callback callback;
+ void *callback_param;
+
+ /* Remove from the list of running transactions */
+ list_del(&desc->node);
+
+ /* Run the link descriptor callback function */
+ callback = desc->async_tx.callback;
+ callback_param = desc->async_tx.callback_param;
+ if (callback) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ callback(callback_param);
+ spin_lock_irqsave(&chan->lock, flags);
+ }
+
+ /* Run any dependencies, then free the descriptor */
+ dma_run_dependencies(&desc->async_tx);
+ xilinx_vdma_free_tx_descriptor(chan, desc);
+ }
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/**
+ * xilinx_vdma_do_tasklet - Schedule completion tasklet
+ * @data: Pointer to the Xilinx VDMA channel structure
+ */
+static void xilinx_vdma_do_tasklet(unsigned long data)
+{
+ struct xilinx_vdma_chan *chan = (struct xilinx_vdma_chan *)data;
+
+ xilinx_vdma_chan_desc_cleanup(chan);
+}
+
+/**
+ * xilinx_vdma_alloc_chan_resources - Allocate channel resources
+ * @dchan: DMA channel
+ *
+ * Return: '1' on success and failure value on error
+ */
+static int xilinx_vdma_alloc_chan_resources(struct dma_chan *dchan)
+{
+ struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
+
+ /* Has this channel already been allocated? */
+ if (chan->desc_pool)
+ return 1;
+
+ /*
+ * We need the descriptor to be aligned to 64bytes
+ * for meeting Xilinx VDMA specification requirement.
+ */
+ chan->desc_pool = dma_pool_create("xilinx_vdma_desc_pool",
+ chan->dev,
+ sizeof(struct xilinx_vdma_tx_segment),
+ __alignof__(struct xilinx_vdma_tx_segment), 0);
+ if (!chan->desc_pool) {
+ dev_err(chan->dev,
+ "unable to allocate channel %d descriptor pool\n",
+ chan->id);
+ return -ENOMEM;
+ }
+
+ tasklet_init(&chan->tasklet, xilinx_vdma_do_tasklet,
+ (unsigned long)chan);
+
+ chan->completed_cookie = DMA_MIN_COOKIE;
+ chan->cookie = DMA_MIN_COOKIE;
+
+ /* There is at least one descriptor free to be allocated */
+ return 1;
+}
+
+/**
+ * xilinx_vdma_tx_status - Get VDMA transaction status
+ * @dchan: DMA channel
+ * @cookie: Transaction identifier
+ * @txstate: Transaction state
+ *
+ * Return: DMA transaction status
+ */
+static enum dma_status xilinx_vdma_tx_status(struct dma_chan *dchan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
+ dma_cookie_t last_used;
+ dma_cookie_t last_complete;
+
+ xilinx_vdma_chan_desc_cleanup(chan);
+
+ last_used = dchan->cookie;
+ last_complete = chan->completed_cookie;
+
+ dma_set_tx_state(txstate, last_complete, last_used, 0);
+
+ return dma_async_is_complete(cookie, last_complete, last_used);
+}
+
+/**
+ * xilinx_vdma_is_running - Check if VDMA channel is running
+ * @chan: Driver specific VDMA channel
+ *
+ * Return: '1' if running, '0' if not.
+ */
+static int xilinx_vdma_is_running(struct xilinx_vdma_chan *chan)
+{
+ return !(vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) &
+ XILINX_VDMA_DMASR_HALTED) &&
+ (vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) &
+ XILINX_VDMA_DMACR_RUNSTOP);
+}
+
+/**
+ * xilinx_vdma_is_idle - Check if VDMA channel is idle
+ * @chan: Driver specific VDMA channel
+ *
+ * Return: '1' if idle, '0' if not.
+ */
+static int xilinx_vdma_is_idle(struct xilinx_vdma_chan *chan)
+{
+ return vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) &
+ XILINX_VDMA_DMASR_IDLE;
+}
+
+/**
+ * xilinx_vdma_halt - Halt VDMA channel
+ * @chan: Driver specific VDMA channel
+ */
+static void xilinx_vdma_halt(struct xilinx_vdma_chan *chan)
+{
+ int loop = XILINX_VDMA_LOOP_COUNT + 1;
+
+ vdma_ctrl_clr(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP);
+
+ /* Wait for the hardware to halt */
+ while (loop--)
+ if (vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) &
+ XILINX_VDMA_DMASR_HALTED)
+ break;
+
+ if (!loop) {
+ dev_err(chan->dev, "Cannot stop channel %p: %x\n",
+ chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
+ chan->err = true;
+ }
+
+ return;
+}
+
+/**
+ * xilinx_vdma_start - Start VDMA channel
+ * @chan: Driver specific VDMA channel
+ */
+static void xilinx_vdma_start(struct xilinx_vdma_chan *chan)
+{
+ int loop = XILINX_VDMA_LOOP_COUNT + 1;
+
+ vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP);
+
+ /* Wait for the hardware to start */
+ while (loop)
+ if (!(vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) &
+ XILINX_VDMA_DMASR_HALTED))
+ break;
+
+ if (!loop) {
+ dev_err(chan->dev, "Cannot start channel %p: %x\n",
+ chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
+
+ chan->err = true;
+ }
+
+ return;
+}
+
+/**
+ * xilinx_vdma_start_transfer - Starts VDMA transfer
+ * @chan: Driver specific channel struct pointer
+ */
+static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan)
+{
+ struct xilinx_vdma_config *config = &chan->config;
+ struct xilinx_vdma_tx_descriptor *desc;
+ unsigned long flags;
+ u32 reg;
+ struct xilinx_vdma_tx_segment *head, *tail = NULL;
+
+ if (chan->err)
+ return;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ /* There's already an active descriptor, bail out. */
+ if (chan->active_desc)
+ goto out_unlock;
+
+ if (list_empty(&chan->pending_list))
+ goto out_unlock;
+
+ desc = list_first_entry(&chan->pending_list,
+ struct xilinx_vdma_tx_descriptor, node);
+
+ /* If it is SG mode and hardware is busy, cannot submit */
+ if (chan->has_sg && xilinx_vdma_is_running(chan) &&
+ !xilinx_vdma_is_idle(chan)) {
+ dev_dbg(chan->dev, "DMA controller still busy\n");
+ goto out_unlock;
+ }
+
+ if (chan->err)
+ goto out_unlock;
+
+ /*
+ * If hardware is idle, then all descriptors on the running lists are
+ * done, start new transfers
+ */
+ if (chan->has_sg) {
+ head = list_first_entry(&desc->segments,
+ struct xilinx_vdma_tx_segment, node);
+ tail = list_entry(desc->segments.prev,
+ struct xilinx_vdma_tx_segment, node);
+
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_CURDESC, head->phys);
+ }
+
+ /* Configure the hardware using info in the config structure */
+ reg = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR);
+
+ if (config->frm_cnt_en)
+ reg |= XILINX_VDMA_DMACR_FRAMECNT_EN;
+ else
+ reg &= ~XILINX_VDMA_DMACR_FRAMECNT_EN;
+
+ /*
+ * With SG, start with circular mode, so that BDs can be fetched.
+ * In direct register mode, if not parking, enable circular mode
+ */
+ if (chan->has_sg || !config->park)
+ reg |= XILINX_VDMA_DMACR_CIRC_EN;
+
+ if (config->park)
+ reg &= ~XILINX_VDMA_DMACR_CIRC_EN;
+
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, reg);
+
+ if (config->park && (config->park_frm >= 0) &&
+ (config->park_frm < chan->num_frms)) {
+ if (chan->direction == DMA_MEM_TO_DEV)
+ vdma_write(chan, XILINX_VDMA_REG_PARK_PTR,
+ config->park_frm <<
+ XILINX_VDMA_PARK_PTR_RD_REF_SHIFT);
+ else
+ vdma_write(chan, XILINX_VDMA_REG_PARK_PTR,
+ config->park_frm <<
+ XILINX_VDMA_PARK_PTR_WR_REF_SHIFT);
+ }
+
+ /* Start the hardware */
+ xilinx_vdma_start(chan);
+
+ if (chan->err)
+ goto out_unlock;
+
+ /* Start the transfer */
+ if (chan->has_sg) {
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_TAILDESC, tail->phys);
+ } else {
+ struct xilinx_vdma_tx_segment *segment;
+ int i = 0;
+
+ list_for_each_entry(segment, &desc->segments, node)
+ vdma_desc_write(chan,
+ XILINX_VDMA_REG_START_ADDRESS(i++),
+ segment->hw.buf_addr);
+
+ vdma_desc_write(chan, XILINX_VDMA_REG_HSIZE, config->hsize);
+ vdma_desc_write(chan, XILINX_VDMA_REG_FRMDLY_STRIDE,
+ (config->frm_dly <<
+ XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT) |
+ (config->stride <<
+ XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT));
+ vdma_desc_write(chan, XILINX_VDMA_REG_VSIZE, config->vsize);
+ }
+
+ list_del(&desc->node);
+ chan->active_desc = desc;
+
+out_unlock:
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/**
+ * xilinx_vdma_issue_pending - Issue pending transactions
+ * @dchan: DMA channel
+ */
+static void xilinx_vdma_issue_pending(struct dma_chan *dchan)
+{
+ struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
+
+ xilinx_vdma_start_transfer(chan);
+}
+
+/**
+ * xilinx_vdma_complete_descriptor - Mark the active descriptor as complete
+ * @chan : xilinx DMA channel
+ *
+ * CONTEXT: hardirq
+ */
+static void xilinx_vdma_complete_descriptor(struct xilinx_vdma_chan *chan)
+{
+ struct xilinx_vdma_tx_descriptor *desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ desc = chan->active_desc;
+ if (!desc) {
+ dev_dbg(chan->dev, "no running descriptors\n");
+ goto out_unlock;
+ }
+
+ list_add_tail(&desc->node, &chan->done_list);
+
+ /* Update the completed cookie and reset the active descriptor. */
+ chan->completed_cookie = desc->async_tx.cookie;
+ chan->active_desc = NULL;
+
+out_unlock:
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/**
+ * xilinx_vdma_reset - Reset VDMA channel
+ * @chan: Driver specific VDMA channel
+ *
+ * Return: '0' on success and failure value on error
+ */
+static int xilinx_vdma_reset(struct xilinx_vdma_chan *chan)
+{
+ int loop = XILINX_VDMA_LOOP_COUNT + 1;
+ u32 tmp;
+
+ vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RESET);
+
+ tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) &
+ XILINX_VDMA_DMACR_RESET;
+
+ /* Wait for the hardware to finish reset */
+ while (loop-- && tmp)
+ tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) &
+ XILINX_VDMA_DMACR_RESET;
+
+ if (!loop) {
+ dev_err(chan->dev, "reset timeout, cr %x, sr %x\n",
+ vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR),
+ vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR));
+ return -ETIMEDOUT;
+ }
+
+ chan->err = false;
+
+ return 0;
+}
+
+/**
+ * xilinx_vdma_chan_reset - Reset VDMA channel and enable interrupts
+ * @chan: Driver specific VDMA channel
+ *
+ * Return: '0' on success and failure value on error
+ */
+static int xilinx_vdma_chan_reset(struct xilinx_vdma_chan *chan)
+{
+ int err;
+
+ /* Reset VDMA */
+ err = xilinx_vdma_reset(chan);
+ if (err)
+ return err;
+
+ /* Enable interrupts */
+ vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR,
+ XILINX_VDMA_DMAXR_ALL_IRQ_MASK);
+
+ return 0;
+}
+
+/**
+ * xilinx_vdma_irq_handler - VDMA Interrupt handler
+ * @irq: IRQ number
+ * @data: Pointer to the Xilinx VDMA channel structure
+ *
+ * Return: IRQ_HANDLED/IRQ_NONE
+ */
+static irqreturn_t xilinx_vdma_irq_handler(int irq, void *data)
+{
+ struct xilinx_vdma_chan *chan = data;
+ u32 status;
+
+ /* Read the status and ack the interrupts. */
+ status = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR);
+ if (!(status & XILINX_VDMA_DMAXR_ALL_IRQ_MASK))
+ return IRQ_NONE;
+
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_DMASR,
+ status & XILINX_VDMA_DMAXR_ALL_IRQ_MASK);
+
+ if (status & XILINX_VDMA_DMASR_ERR_IRQ) {
+ /*
+ * An error occurred. If C_FLUSH_ON_FSYNC is enabled and the
+ * error is recoverable, ignore it. Otherwise flag the error.
+ *
+ * Only recoverable errors can be cleared in the DMASR register,
+ * make sure not to write to other error bits to 1.
+ */
+ u32 errors = status & XILINX_VDMA_DMASR_ALL_ERR_MASK;
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_DMASR,
+ errors & XILINX_VDMA_DMASR_ERR_RECOVER_MASK);
+
+ if (!chan->flush_on_fsync ||
+ (errors & ~XILINX_VDMA_DMASR_ERR_RECOVER_MASK)) {
+ dev_err(chan->dev,
+ "Channel %p has errors %x, cdr %x tdr %x\n",
+ chan, errors,
+ vdma_ctrl_read(chan, XILINX_VDMA_REG_CURDESC),
+ vdma_ctrl_read(chan, XILINX_VDMA_REG_TAILDESC));
+ chan->err = true;
+ }
+ }
+
+ if (status & XILINX_VDMA_DMASR_DLY_CNT_IRQ) {
+ /*
+ * Device takes too long to do the transfer when user requires
+ * responsiveness.
+ */
+ dev_dbg(chan->dev, "Inter-packet latency too long\n");
+ }
+
+ if (status & XILINX_VDMA_DMASR_FRM_CNT_IRQ) {
+ xilinx_vdma_complete_descriptor(chan);
+ xilinx_vdma_start_transfer(chan);
+ }
+
+ tasklet_schedule(&chan->tasklet);
+ return IRQ_HANDLED;
+}
+
+/**
+ * xilinx_vdma_tx_submit - Submit DMA transaction
+ * @tx: Async transaction descriptor
+ *
+ * Return: cookie value on success and failure value on error
+ */
+static dma_cookie_t xilinx_vdma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct xilinx_vdma_tx_descriptor *desc = to_vdma_tx_descriptor(tx);
+ struct xilinx_vdma_chan *chan = to_xilinx_chan(tx->chan);
+ struct xilinx_vdma_tx_segment *segment;
+ dma_cookie_t cookie;
+ unsigned long flags;
+ int err;
+
+ if (chan->err) {
+ /*
+ * If reset fails, need to hard reset the system.
+ * Channel is no longer functional
+ */
+ err = xilinx_vdma_chan_reset(chan);
+ if (err < 0)
+ return err;
+ }
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ /* Assign cookies to all of the segments that make up this transaction.
+ * Use the cookie of the last segment as the transaction cookie.
+ */
+ cookie = chan->cookie;
+
+ list_for_each_entry(segment, &desc->segments, node) {
+ if (cookie < DMA_MAX_COOKIE)
+ cookie++;
+ else
+ cookie = DMA_MIN_COOKIE;
+
+ segment->cookie = cookie;
+ }
+
+ tx->cookie = cookie;
+ chan->cookie = cookie;
+
+ /* Append the transaction to the pending transactions queue. */
+ list_add_tail(&desc->node, &chan->pending_list);
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return cookie;
+}
+
+/**
+ * xilinx_vdma_prep_slave_sg - prepare a descriptor for a DMA_SLAVE transaction
+ * @dchan: DMA channel
+ * @sgl: scatterlist to transfer to/from
+ * @sg_len: number of entries in @sgl
+ * @dir: DMA direction
+ * @flags: transfer ack flags
+ * @context: unused
+ *
+ * Return: Async transaction descriptor on success and NULL on failure
+ */
+static struct dma_async_tx_descriptor *
+xilinx_vdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction dir,
+ unsigned long flags, void *context)
+{
+ struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
+ struct xilinx_vdma_tx_descriptor *desc;
+ struct xilinx_vdma_tx_segment *segment;
+ struct xilinx_vdma_tx_segment *prev = NULL;
+ struct scatterlist *sg;
+ int i;
+
+ if (chan->direction != dir || sg_len == 0)
+ return NULL;
+
+ /* Enforce one sg entry for one frame. */
+ if (sg_len != chan->num_frms) {
+ dev_err(chan->dev,
+ "number of entries %d not the same as num stores %d\n",
+ sg_len, chan->num_frms);
+ return NULL;
+ }
+
+ /* Allocate a transaction descriptor. */
+ desc = xilinx_vdma_alloc_tx_descriptor(chan);
+ if (!desc)
+ return NULL;
+
+ dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
+ desc->async_tx.tx_submit = xilinx_vdma_tx_submit;
+ desc->async_tx.cookie = 0;
+ async_tx_ack(&desc->async_tx);
+
+ /* Build the list of transaction segments. */
+ for_each_sg(sgl, sg, sg_len, i) {
+ struct xilinx_vdma_desc_hw *hw;
+
+ /* Allocate the link descriptor from DMA pool */
+ segment = xilinx_vdma_alloc_tx_segment(chan);
+ if (!segment)
+ goto error;
+
+ /* Fill in the hardware descriptor */
+ hw = &segment->hw;
+ hw->buf_addr = sg_dma_address(sg);
+ hw->vsize = chan->config.vsize;
+ hw->hsize = chan->config.hsize;
+ hw->stride = (chan->config.frm_dly <<
+ XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT) |
+ (chan->config.stride <<
+ XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT);
+ if (prev)
+ prev->hw.next_desc = segment->phys;
+
+ /* Insert the segment into the descriptor segments list. */
+ list_add_tail(&segment->node, &desc->segments);
+
+ prev = segment;
+ }
+
+ /* Link the last hardware descriptor with the first. */
+ segment = list_first_entry(&desc->segments,
+ struct xilinx_vdma_tx_segment, node);
+ prev->hw.next_desc = segment->phys;
+
+ return &desc->async_tx;
+
+error:
+ xilinx_vdma_free_tx_descriptor(chan, desc);
+ return NULL;
+}
+
+/**
+ * xilinx_vdma_terminate_all - Halt the channel and free descriptors
+ * @chan: Driver specific VDMA Channel pointer
+ */
+static void xilinx_vdma_terminate_all(struct xilinx_vdma_chan *chan)
+{
+ /* Halt the DMA engine */
+ xilinx_vdma_halt(chan);
+
+ /* Remove and free all of the descriptors in the lists */
+ xilinx_vdma_free_descriptors(chan);
+}
+
+/**
+ * xilinx_vdma_slave_config - Configure VDMA channel
+ * Run-time configuration for Axi VDMA, supports:
+ * . halt the channel
+ * . configure interrupt coalescing and inter-packet delay threshold
+ * . start/stop parking
+ * . enable genlock
+ * . set transfer information using config struct
+ *
+ * @chan: Driver specific VDMA Channel pointer
+ * @cfg: Channel configuration pointer
+ *
+ * Return: '0' on success and failure value on error
+ */
+static int xilinx_vdma_slave_config(struct xilinx_vdma_chan *chan,
+ struct xilinx_vdma_config *cfg)
+{
+ u32 dmacr;
+
+ if (cfg->reset)
+ return xilinx_vdma_chan_reset(chan);
+
+ dmacr = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR);
+
+ /* If vsize is -1, it is park-related operations */
+ if (cfg->vsize == -1) {
+ if (cfg->park)
+ dmacr &= ~XILINX_VDMA_DMACR_CIRC_EN;
+ else
+ dmacr |= XILINX_VDMA_DMACR_CIRC_EN;
+
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
+ return 0;
+ }
+
+ /* If hsize is -1, it is interrupt threshold settings */
+ if (cfg->hsize == -1) {
+ if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
+ dmacr &= ~XILINX_VDMA_DMACR_FRAME_COUNT_MASK;
+ dmacr |= cfg->coalesc <<
+ XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
+ chan->config.coalesc = cfg->coalesc;
+ }
+
+ if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
+ dmacr &= ~XILINX_VDMA_DMACR_DELAY_MASK;
+ dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
+ chan->config.delay = cfg->delay;
+ }
+
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
+ return 0;
+ }
+
+ /* Transfer information */
+ chan->config.vsize = cfg->vsize;
+ chan->config.hsize = cfg->hsize;
+ chan->config.stride = cfg->stride;
+ chan->config.frm_dly = cfg->frm_dly;
+ chan->config.park = cfg->park;
+
+ /* genlock settings */
+ chan->config.gen_lock = cfg->gen_lock;
+ chan->config.master = cfg->master;
+
+ if (cfg->gen_lock && chan->genlock) {
+ dmacr |= XILINX_VDMA_DMACR_GENLOCK_EN;
+ dmacr |= cfg->master << XILINX_VDMA_DMACR_MASTER_SHIFT;
+ }
+
+ chan->config.frm_cnt_en = cfg->frm_cnt_en;
+ if (cfg->park)
+ chan->config.park_frm = cfg->park_frm;
+ else
+ chan->config.park_frm = -1;
+
+ chan->config.coalesc = cfg->coalesc;
+ chan->config.delay = cfg->delay;
+ if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
+ dmacr |= cfg->coalesc << XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
+ chan->config.coalesc = cfg->coalesc;
+ }
+
+ if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
+ dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
+ chan->config.delay = cfg->delay;
+ }
+
+ /* FSync Source selection */
+ dmacr &= ~XILINX_VDMA_DMACR_FSYNCSRC_MASK;
+ dmacr |= cfg->ext_fsync << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT;
+
+ vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
+ return 0;
+}
+
+/**
+ * xilinx_vdma_device_control - Configure DMA channel of the device
+ * @dchan: DMA Channel pointer
+ * @cmd: DMA control command
+ * @arg: Channel configuration
+ *
+ * Return: '0' on success and failure value on error
+ */
+static int xilinx_vdma_device_control(struct dma_chan *dchan,
+ enum dma_ctrl_cmd cmd, unsigned long arg)
+{
+ struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan);
+
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ xilinx_vdma_terminate_all(chan);
+ return 0;
+ case DMA_SLAVE_CONFIG:
+ return xilinx_vdma_slave_config(chan,
+ (struct xilinx_vdma_config *)arg);
+ default:
+ return -ENXIO;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Probe and remove
+ */
+
+/**
+ * xilinx_vdma_chan_remove - Per Channel remove function
+ * @chan: Driver specific VDMA channel
+ */
+static void xilinx_vdma_chan_remove(struct xilinx_vdma_chan *chan)
+{
+ /* Disable all interrupts */
+ vdma_ctrl_clr(chan, XILINX_VDMA_REG_DMACR,
+ XILINX_VDMA_DMAXR_ALL_IRQ_MASK);
+
+ list_del(&chan->common.device_node);
+}
+
+/**
+ * xilinx_vdma_chan_probe - Per Channel Probing
+ * It get channel features from the device tree entry and
+ * initialize special channel handling routines
+ *
+ * @xdev: Driver specific device structure
+ * @node: Device node
+ *
+ * Return: '0' on success and failure value on error
+ */
+static int xilinx_vdma_chan_probe(struct xilinx_vdma_device *xdev,
+ struct device_node *node)
+{
+ struct xilinx_vdma_chan *chan;
+ bool has_dre = false;
+ u32 device_id;
+ u32 value;
+ int err;
+
+ /* Allocate and initialize the channel structure */
+ chan = devm_kzalloc(xdev->dev, sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ chan->dev = xdev->dev;
+ chan->xdev = xdev;
+ chan->has_sg = xdev->has_sg;
+
+ spin_lock_init(&chan->lock);
+ INIT_LIST_HEAD(&chan->pending_list);
+ INIT_LIST_HEAD(&chan->done_list);
+
+ /* Retrieve the channel properties from the device tree */
+ has_dre = of_property_read_bool(node, "xlnx,include-dre");
+
+ chan->genlock = of_property_read_bool(node, "xlnx,genlock-mode");
+
+ err = of_property_read_u32(node, "xlnx,datawidth", &value);
+ if (!err) {
+ u32 width = value >> 3; /* Convert bits to bytes */
+
+ /* If data width is greater than 8 bytes, DRE is not in hw */
+ if (width > 8)
+ has_dre = false;
+
+ if (!has_dre)
+ xdev->common.copy_align = fls(width - 1);
+ }
+
+ err = of_property_read_u32(node, "xlnx,device-id", &device_id);
+ if (err < 0) {
+ dev_err(xdev->dev, "missing xlnx,device-id property\n");
+ return err;
+ }
+
+ if (of_device_is_compatible(node, "xlnx,axi-vdma-mm2s-channel")) {
+ chan->direction = DMA_MEM_TO_DEV;
+ chan->id = 0;
+
+ chan->ctrl_offset = XILINX_VDMA_MM2S_CTRL_OFFSET;
+ chan->desc_offset = XILINX_VDMA_MM2S_DESC_OFFSET;
+
+ if (xdev->flush_on_fsync == XILINX_VDMA_FLUSH_BOTH ||
+ xdev->flush_on_fsync == XILINX_VDMA_FLUSH_MM2S)
+ chan->flush_on_fsync = true;
+ } else if (of_device_is_compatible(node,
+ "xlnx,axi-vdma-s2mm-channel")) {
+ chan->direction = DMA_DEV_TO_MEM;
+ chan->id = 1;
+
+ chan->ctrl_offset = XILINX_VDMA_S2MM_CTRL_OFFSET;
+ chan->desc_offset = XILINX_VDMA_S2MM_DESC_OFFSET;
+
+ if (xdev->flush_on_fsync == XILINX_VDMA_FLUSH_BOTH ||
+ xdev->flush_on_fsync == XILINX_VDMA_FLUSH_S2MM)
+ chan->flush_on_fsync = true;
+ } else {
+ dev_err(xdev->dev, "Invalid channel compatible node\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Used by DMA clients who doesnt have a device node and can request
+ * the channel by passing this as a filter to 'dma_request_channel()'.
+ */
+ chan->private = (chan->direction & 0xff) |
+ XILINX_DMA_IP_VDMA |
+ (device_id << XILINX_DMA_DEVICE_ID_SHIFT);
+
+ /* Request the interrupt */
+ chan->irq = irq_of_parse_and_map(node, 0);
+ err = devm_request_irq(xdev->dev, chan->irq, xilinx_vdma_irq_handler,
+ IRQF_SHARED, "xilinx-vdma-controller", chan);
+ if (err) {
+ dev_err(xdev->dev, "unable to request IRQ\n");
+ return err;
+ }
+
+ /* Initialize the DMA channel and add it to the DMA engine channels
+ * list.
+ */
+ chan->common.device = &xdev->common;
+ chan->common.private = (void *)&(chan->private);
+
+ list_add_tail(&chan->common.device_node, &xdev->common.channels);
+ xdev->chan[chan->id] = chan;
+
+ /* Reset the channel */
+ err = xilinx_vdma_chan_reset(chan);
+ if (err < 0) {
+ dev_err(xdev->dev, "Reset channel failed\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * struct of_dma_filter_xilinx_args - Channel filter args
+ * @dev: DMA device structure
+ * @chan_id: Channel id
+ */
+struct of_dma_filter_xilinx_args {
+ struct dma_device *dev;
+ u32 chan_id;
+};
+
+/**
+ * xilinx_vdma_dt_filter - VDMA channel filter function
+ * @chan: DMA channel pointer
+ * @param: Filter match value
+ *
+ * Return: true/false based on the result
+ */
+static bool xilinx_vdma_dt_filter(struct dma_chan *chan, void *param)
+{
+ struct of_dma_filter_xilinx_args *args = param;
+
+ return chan->device == args->dev && chan->chan_id == args->chan_id;
+}
+
+/**
+ * of_dma_xilinx_xlate - Translation function
+ * @dma_spec: Pointer to DMA specifier as found in the device tree
+ * @ofdma: Pointer to DMA controller data
+ *
+ * Return: DMA channel pointer on success and NULL on error
+ */
+static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct of_dma_filter_xilinx_args args;
+ dma_cap_mask_t cap;
+
+ args.dev = ofdma->of_dma_data;
+ if (!args.dev)
+ return NULL;
+
+ if (dma_spec->args_count != 1)
+ return NULL;
+
+ dma_cap_zero(cap);
+ dma_cap_set(DMA_SLAVE, cap);
+
+ args.chan_id = dma_spec->args[0];
+
+ return dma_request_channel(cap, xilinx_vdma_dt_filter, &args);
+}
+
+/**
+ * xilinx_vdma_probe - Driver probe function
+ * @pdev: Pointer to the platform_device structure
+ *
+ * Return: '0' on success and failure value on error
+ */
+static int xilinx_vdma_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct xilinx_vdma_device *xdev;
+ struct device_node *child;
+ struct resource *io;
+ u32 num_frames;
+ int i, err;
+
+ dev_info(&pdev->dev, "Probing xilinx axi vdma engine\n");
+
+ /* Allocate and initialize the DMA engine structure */
+ xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
+ if (!xdev)
+ return -ENOMEM;
+
+ xdev->dev = &pdev->dev;
+
+ /* Request and map I/O memory */
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xdev->regs = devm_ioremap_resource(&pdev->dev, io);
+ if (IS_ERR(xdev->regs))
+ return PTR_ERR(xdev->regs);
+
+ /* Retrieve the DMA engine properties from the device tree */
+ xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg");
+
+ err = of_property_read_u32(node, "xlnx,num-fstores", &num_frames);
+ if (err < 0) {
+ dev_err(xdev->dev, "missing xlnx,num-fstores property\n");
+ return err;
+ }
+
+ of_property_read_u32(node, "xlnx,flush-fsync", &xdev->flush_on_fsync);
+
+ /* Initialize the DMA engine */
+ xdev->common.dev = &pdev->dev;
+
+ INIT_LIST_HEAD(&xdev->common.channels);
+ dma_cap_set(DMA_SLAVE, xdev->common.cap_mask);
+ dma_cap_set(DMA_PRIVATE, xdev->common.cap_mask);
+
+ xdev->common.device_alloc_chan_resources =
+ xilinx_vdma_alloc_chan_resources;
+ xdev->common.device_free_chan_resources =
+ xilinx_vdma_free_chan_resources;
+ xdev->common.device_prep_slave_sg = xilinx_vdma_prep_slave_sg;
+ xdev->common.device_control = xilinx_vdma_device_control;
+ xdev->common.device_tx_status = xilinx_vdma_tx_status;
+ xdev->common.device_issue_pending = xilinx_vdma_issue_pending;
+
+ platform_set_drvdata(pdev, xdev);
+
+ /* Initialize the channels */
+ for_each_child_of_node(node, child) {
+ err = xilinx_vdma_chan_probe(xdev, child);
+ if (err < 0)
+ goto error;
+ }
+
+ for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
+ if (xdev->chan[i])
+ xdev->chan[i]->num_frms = num_frames;
+ }
+
+ /* Register the DMA engine with the core */
+ dma_async_device_register(&xdev->common);
+
+ err = of_dma_controller_register(node, of_dma_xilinx_xlate,
+ &xdev->common);
+ if (err < 0)
+ dev_err(&pdev->dev, "Unable to register DMA to DT\n");
+
+ return 0;
+
+error:
+ for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
+ if (xdev->chan[i])
+ xilinx_vdma_chan_remove(xdev->chan[i]);
+ }
+
+ return err;
+}
+
+/**
+ * xilinx_vdma_remove - Driver remove function
+ * @pdev: Pointer to the platform_device structure
+ *
+ * Return: Always '0'
+ */
+static int xilinx_vdma_remove(struct platform_device *pdev)
+{
+ struct xilinx_vdma_device *xdev;
+ int i;
+
+ of_dma_controller_free(pdev->dev.of_node);
+
+ xdev = platform_get_drvdata(pdev);
+ dma_async_device_unregister(&xdev->common);
+
+ for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
+ if (xdev->chan[i])
+ xilinx_vdma_chan_remove(xdev->chan[i]);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id xilinx_vdma_of_ids[] = {
+ { .compatible = "xlnx,axi-vdma-1.00.a",},
+ {}
+};
+
+static struct platform_driver xilinx_vdma_driver = {
+ .driver = {
+ .name = "xilinx-vdma",
+ .owner = THIS_MODULE,
+ .of_match_table = xilinx_vdma_of_ids,
+ },
+ .probe = xilinx_vdma_probe,
+ .remove = xilinx_vdma_remove,
+};
+
+module_platform_driver(xilinx_vdma_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx VDMA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/xilinx/xilinx_vdma_test.c b/drivers/dma/xilinx/xilinx_vdma_test.c
new file mode 100644
index 0000000..813b67c
--- /dev/null
+++ b/drivers/dma/xilinx/xilinx_vdma_test.c
@@ -0,0 +1,629 @@
+/*
+ * XILINX VDMA Engine test client driver
+ *
+ * Copyright (C) 2010-2013 Xilinx, Inc. All rights reserved.
+ *
+ * Based on Atmel DMA Test Client
+ *
+ * Description:
+ * This is a simple Xilinx VDMA test client for AXI VDMA driver.
+ * This test assumes both the channels of VDMA are enabled in the
+ * hardware design and configured in back-to-back connection. Test
+ * starts by pumping the data onto one channel (MM2S) and then
+ * compares the data that is received on the other channel (S2MM).
+ *
+ * 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/amba/xilinx_dma.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+static unsigned int test_buf_size = 64;
+module_param(test_buf_size, uint, S_IRUGO);
+MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer");
+
+static unsigned int iterations;
+module_param(iterations, uint, S_IRUGO);
+MODULE_PARM_DESC(iterations,
+ "Iterations before stopping test (default: infinite)");
+
+/*
+ * Initialization patterns. All bytes in the source buffer has bit 7
+ * set, all bytes in the destination buffer has bit 7 cleared.
+ *
+ * Bit 6 is set for all bytes which are to be copied by the DMA
+ * engine. Bit 5 is set for all bytes which are to be overwritten by
+ * the DMA engine.
+ *
+ * The remaining bits are the inverse of a counter which increments by
+ * one for each byte address.
+ */
+#define PATTERN_SRC 0x80
+#define PATTERN_DST 0x00
+#define PATTERN_COPY 0x40
+#define PATTERN_OVERWRITE 0x20
+#define PATTERN_COUNT_MASK 0x1f
+
+/* Maximum number of frame buffers */
+#define MAX_NUM_FRAMES 32
+
+/**
+ * struct vdmatest_slave_thread - VDMA test thread
+ * @node: Thread node
+ * @task: Task structure pointer
+ * @tx_chan: Tx channel pointer
+ * @rx_chan: Rx Channel pointer
+ * @srcs: Source buffer
+ * @dsts: Destination buffer
+ * @type: DMA transaction type
+ */
+struct xilinx_vdmatest_slave_thread {
+ struct list_head node;
+ struct task_struct *task;
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
+ u8 **srcs;
+ u8 **dsts;
+ enum dma_transaction_type type;
+};
+
+/**
+ * struct vdmatest_chan - VDMA Test channel
+ * @node: Channel node
+ * @chan: DMA channel pointer
+ * @threads: List of VDMA test threads
+ */
+struct xilinx_vdmatest_chan {
+ struct list_head node;
+ struct dma_chan *chan;
+ struct list_head threads;
+};
+
+/* Global variables */
+static LIST_HEAD(xilinx_vdmatest_channels);
+static unsigned int nr_channels;
+static unsigned int frm_cnt;
+static dma_addr_t dma_srcs[MAX_NUM_FRAMES];
+static dma_addr_t dma_dsts[MAX_NUM_FRAMES];
+static struct scatterlist tx_sg[MAX_NUM_FRAMES];
+static struct scatterlist rx_sg[MAX_NUM_FRAMES];
+
+static void xilinx_vdmatest_init_srcs(u8 **bufs, unsigned int start,
+ unsigned int len)
+{
+ unsigned int i;
+ u8 *buf;
+
+ for (; (buf = *bufs); bufs++) {
+ for (i = 0; i < start; i++)
+ buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
+ for (; i < start + len; i++)
+ buf[i] = PATTERN_SRC | PATTERN_COPY
+ | (~i & PATTERN_COUNT_MASK);
+ for (; i < test_buf_size; i++)
+ buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK);
+ buf++;
+ }
+}
+
+static void xilinx_vdmatest_init_dsts(u8 **bufs, unsigned int start,
+ unsigned int len)
+{
+ unsigned int i;
+ u8 *buf;
+
+ for (; (buf = *bufs); bufs++) {
+ for (i = 0; i < start; i++)
+ buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK);
+ for (; i < start + len; i++)
+ buf[i] = PATTERN_DST | PATTERN_OVERWRITE
+ | (~i & PATTERN_COUNT_MASK);
+ for (; i < test_buf_size; i++)
+ buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK);
+ }
+}
+
+static void xilinx_vdmatest_mismatch(u8 actual, u8 pattern, unsigned int index,
+ unsigned int counter, bool is_srcbuf)
+{
+ u8 diff = actual ^ pattern;
+ u8 expected = pattern | (~counter & PATTERN_COUNT_MASK);
+ const char *thread_name = current->comm;
+
+ if (is_srcbuf)
+ pr_warn(
+ "%s: srcbuf[0x%x] overwritten! Expected %02x, got %02x\n",
+ thread_name, index, expected, actual);
+ else if ((pattern & PATTERN_COPY)
+ && (diff & (PATTERN_COPY | PATTERN_OVERWRITE)))
+ pr_warn(
+ "%s: dstbuf[0x%x] not copied! Expected %02x, got %02x\n",
+ thread_name, index, expected, actual);
+ else if (diff & PATTERN_SRC)
+ pr_warn(
+ "%s: dstbuf[0x%x] was copied! Expected %02x, got %02x\n",
+ thread_name, index, expected, actual);
+ else
+ pr_warn(
+ "%s: dstbuf[0x%x] mismatch! Expected %02x, got %02x\n",
+ thread_name, index, expected, actual);
+}
+
+static unsigned int xilinx_vdmatest_verify(u8 **bufs, unsigned int start,
+ unsigned int end, unsigned int counter, u8 pattern,
+ bool is_srcbuf)
+{
+ unsigned int i, error_count = 0;
+ u8 actual, expected, *buf;
+ unsigned int counter_orig = counter;
+
+ for (; (buf = *bufs); bufs++) {
+ counter = counter_orig;
+ for (i = start; i < end; i++) {
+ actual = buf[i];
+ expected = pattern | (~counter & PATTERN_COUNT_MASK);
+ if (actual != expected) {
+ if (error_count < 32)
+ xilinx_vdmatest_mismatch(actual,
+ pattern, i,
+ counter, is_srcbuf);
+ error_count++;
+ }
+ counter++;
+ }
+ }
+
+ if (error_count > 32)
+ pr_warn("%s: %u errors suppressed\n",
+ current->comm, error_count - 32);
+
+ return error_count;
+}
+
+static void xilinx_vdmatest_slave_tx_callback(void *completion)
+{
+ pr_debug("Got tx callback\n");
+ complete(completion);
+}
+
+static void xilinx_vdmatest_slave_rx_callback(void *completion)
+{
+ pr_debug("Got rx callback\n");
+ complete(completion);
+}
+
+/*
+ * Function for slave transfers
+ * Each thread requires 2 channels, one for transmit, and one for receive
+ */
+static int xilinx_vdmatest_slave_func(void *data)
+{
+ struct xilinx_vdmatest_slave_thread *thread = data;
+ struct dma_chan *tx_chan, *rx_chan;
+ const char *thread_name;
+ unsigned int len, error_count;
+ unsigned int failed_tests = 0, total_tests = 0;
+ dma_cookie_t tx_cookie, rx_cookie;
+ enum dma_status status;
+ enum dma_ctrl_flags flags;
+ int ret = -ENOMEM, i;
+ int hsize = 64, vsize = 32;
+ struct xilinx_vdma_config config;
+
+ thread_name = current->comm;
+
+ /* Limit testing scope here */
+ iterations = 1;
+ test_buf_size = hsize * vsize;
+
+ /* This barrier ensures 'thread' is initialized and
+ * we get valid DMA channels
+ */
+ smp_rmb();
+ tx_chan = thread->tx_chan;
+ rx_chan = thread->rx_chan;
+
+ thread->srcs = kcalloc(frm_cnt+1, sizeof(u8 *), GFP_KERNEL);
+ if (!thread->srcs)
+ goto err_srcs;
+ for (i = 0; i < frm_cnt; i++) {
+ thread->srcs[i] = kmalloc(test_buf_size, GFP_KERNEL);
+ if (!thread->srcs[i])
+ goto err_srcbuf;
+ }
+
+ thread->dsts = kcalloc(frm_cnt+1, sizeof(u8 *), GFP_KERNEL);
+ if (!thread->dsts)
+ goto err_dsts;
+ for (i = 0; i < frm_cnt; i++) {
+ thread->dsts[i] = kmalloc(test_buf_size, GFP_KERNEL);
+ if (!thread->dsts[i])
+ goto err_dstbuf;
+ }
+
+ set_user_nice(current, 10);
+
+ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+
+ while (!kthread_should_stop()
+ && !(iterations && total_tests >= iterations)) {
+ struct dma_device *tx_dev = tx_chan->device;
+ struct dma_device *rx_dev = rx_chan->device;
+ struct dma_async_tx_descriptor *txd = NULL;
+ struct dma_async_tx_descriptor *rxd = NULL;
+ struct completion rx_cmp, tx_cmp;
+ unsigned long rx_tmo =
+ msecs_to_jiffies(30000); /* RX takes longer */
+ unsigned long tx_tmo = msecs_to_jiffies(30000);
+ u8 align = 0;
+
+ total_tests++;
+
+ /* honor larger alignment restrictions */
+ align = tx_dev->copy_align;
+ if (rx_dev->copy_align > align)
+ align = rx_dev->copy_align;
+
+ if (1 << align > test_buf_size) {
+ pr_err("%u-byte buffer too small for %d-byte alignment\n",
+ test_buf_size, 1 << align);
+ break;
+ }
+
+ len = test_buf_size;
+ xilinx_vdmatest_init_srcs(thread->srcs, 0, len);
+ xilinx_vdmatest_init_dsts(thread->dsts, 0, len);
+
+ sg_init_table(tx_sg, frm_cnt);
+ sg_init_table(rx_sg, frm_cnt);
+
+ for (i = 0; i < frm_cnt; i++) {
+ u8 *buf = thread->srcs[i];
+
+ dma_srcs[i] = dma_map_single(tx_dev->dev, buf, len,
+ DMA_MEM_TO_DEV);
+ pr_debug("src buf %x dma %x\n", (unsigned int)buf,
+ (unsigned int)dma_srcs[i]);
+ sg_dma_address(&tx_sg[i]) = dma_srcs[i];
+ sg_dma_len(&tx_sg[i]) = len;
+ }
+
+ for (i = 0; i < frm_cnt; i++) {
+ dma_dsts[i] = dma_map_single(rx_dev->dev,
+ thread->dsts[i],
+ test_buf_size,
+ DMA_DEV_TO_MEM);
+ pr_debug("dst %x dma %x\n",
+ (unsigned int)thread->dsts[i],
+ (unsigned int)dma_dsts[i]);
+ sg_dma_address(&rx_sg[i]) = dma_dsts[i];
+ sg_dma_len(&rx_sg[i]) = len;
+ }
+
+ /* Zero out configuration */
+ memset(&config, 0, sizeof(struct xilinx_vdma_config));
+
+ /* Set up hardware configuration information */
+ config.vsize = vsize;
+ config.hsize = hsize;
+ config.stride = hsize;
+ config.frm_cnt_en = 1;
+ config.coalesc = frm_cnt * 10;
+ config.park = 1;
+ tx_dev->device_control(tx_chan, DMA_SLAVE_CONFIG,
+ (unsigned long)&config);
+
+ config.park = 0;
+ rx_dev->device_control(rx_chan, DMA_SLAVE_CONFIG,
+ (unsigned long)&config);
+
+ rxd = rx_dev->device_prep_slave_sg(rx_chan, rx_sg, frm_cnt,
+ DMA_DEV_TO_MEM, flags, NULL);
+
+ txd = tx_dev->device_prep_slave_sg(tx_chan, tx_sg, frm_cnt,
+ DMA_MEM_TO_DEV, flags, NULL);
+
+ if (!rxd || !txd) {
+ for (i = 0; i < frm_cnt; i++)
+ dma_unmap_single(tx_dev->dev, dma_srcs[i], len,
+ DMA_MEM_TO_DEV);
+ for (i = 0; i < frm_cnt; i++)
+ dma_unmap_single(rx_dev->dev, dma_dsts[i],
+ test_buf_size,
+ DMA_DEV_TO_MEM);
+ pr_warn("%s: #%u: prep error with len=0x%x ",
+ thread_name, total_tests - 1, len);
+ msleep(100);
+ failed_tests++;
+ continue;
+ }
+
+ init_completion(&rx_cmp);
+ rxd->callback = xilinx_vdmatest_slave_rx_callback;
+ rxd->callback_param = &rx_cmp;
+ rx_cookie = rxd->tx_submit(rxd);
+
+ init_completion(&tx_cmp);
+ txd->callback = xilinx_vdmatest_slave_tx_callback;
+ txd->callback_param = &tx_cmp;
+ tx_cookie = txd->tx_submit(txd);
+
+ if (dma_submit_error(rx_cookie) ||
+ dma_submit_error(tx_cookie)) {
+ pr_warn("%s: #%u: submit error %d/%d with len=0x%x ",
+ thread_name, total_tests - 1,
+ rx_cookie, tx_cookie, len);
+ msleep(100);
+ failed_tests++;
+ continue;
+ }
+ dma_async_issue_pending(tx_chan);
+ dma_async_issue_pending(rx_chan);
+
+ tx_tmo = wait_for_completion_timeout(&tx_cmp, tx_tmo);
+
+ status = dma_async_is_tx_complete(tx_chan, tx_cookie,
+ NULL, NULL);
+
+ if (tx_tmo == 0) {
+ pr_warn("%s: #%u: tx test timed out\n",
+ thread_name, total_tests - 1);
+ failed_tests++;
+ continue;
+ } else if (status != DMA_COMPLETE) {
+ pr_warn(
+ "%s: #%u: tx got completion callback, ",
+ thread_name, total_tests - 1);
+ pr_warn("but status is \'%s\'\n",
+ status == DMA_ERROR ? "error" :
+ "in progress");
+ failed_tests++;
+ continue;
+ }
+
+ rx_tmo = wait_for_completion_timeout(&rx_cmp, rx_tmo);
+ status = dma_async_is_tx_complete(rx_chan, rx_cookie,
+ NULL, NULL);
+
+ if (rx_tmo == 0) {
+ pr_warn("%s: #%u: rx test timed out\n",
+ thread_name, total_tests - 1);
+ failed_tests++;
+ continue;
+ } else if (status != DMA_COMPLETE) {
+ pr_warn(
+ "%s: #%u: rx got completion callback, ",
+ thread_name, total_tests - 1);
+ pr_warn("but status is \'%s\'\n",
+ status == DMA_ERROR ? "error" :
+ "in progress");
+ failed_tests++;
+ continue;
+ }
+
+ for (i = 0; i < frm_cnt; i++)
+ dma_unmap_single(rx_dev->dev, dma_dsts[i],
+ test_buf_size, DMA_DEV_TO_MEM);
+
+ error_count = 0;
+
+ pr_debug("%s: verifying source buffer...\n", thread_name);
+ error_count += xilinx_vdmatest_verify(thread->srcs, 0, 0,
+ 0, PATTERN_SRC, true);
+ error_count += xilinx_vdmatest_verify(thread->srcs, 0,
+ len, 0, PATTERN_SRC | PATTERN_COPY, true);
+ error_count += xilinx_vdmatest_verify(thread->srcs, len,
+ test_buf_size, len, PATTERN_SRC, true);
+
+ pr_debug("%s: verifying dest buffer...\n",
+ thread->task->comm);
+ error_count += xilinx_vdmatest_verify(thread->dsts, 0, 0,
+ 0, PATTERN_DST, false);
+ error_count += xilinx_vdmatest_verify(thread->dsts, 0,
+ len, 0, PATTERN_SRC | PATTERN_COPY, false);
+ error_count += xilinx_vdmatest_verify(thread->dsts, len,
+ test_buf_size, len, PATTERN_DST, false);
+
+ if (error_count) {
+ pr_warn("%s: #%u: %u errors with len=0x%x\n",
+ thread_name, total_tests - 1, error_count, len);
+ failed_tests++;
+ } else {
+ pr_debug("%s: #%u: No errors with len=0x%x\n",
+ thread_name, total_tests - 1, len);
+ }
+ }
+
+ ret = 0;
+ for (i = 0; thread->dsts[i]; i++)
+ kfree(thread->dsts[i]);
+err_dstbuf:
+ kfree(thread->dsts);
+err_dsts:
+ for (i = 0; thread->srcs[i]; i++)
+ kfree(thread->srcs[i]);
+err_srcbuf:
+ kfree(thread->srcs);
+err_srcs:
+ pr_notice("%s: terminating after %u tests, %u failures (status %d)\n",
+ thread_name, total_tests, failed_tests, ret);
+
+ if (iterations > 0)
+ while (!kthread_should_stop()) {
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_vdmatest_exit);
+ interruptible_sleep_on(&wait_vdmatest_exit);
+ }
+
+ return ret;
+}
+
+static void xilinx_vdmatest_cleanup_channel(struct xilinx_vdmatest_chan *dtc)
+{
+ struct xilinx_vdmatest_slave_thread *thread, *_thread;
+ int ret;
+
+ list_for_each_entry_safe(thread, _thread,
+ &dtc->threads, node) {
+ ret = kthread_stop(thread->task);
+ pr_info("xilinx_vdmatest: thread %s exited with status %d\n",
+ thread->task->comm, ret);
+ list_del(&thread->node);
+ kfree(thread);
+ }
+ kfree(dtc);
+}
+
+static int
+xilinx_vdmatest_add_slave_threads(struct xilinx_vdmatest_chan *tx_dtc,
+ struct xilinx_vdmatest_chan *rx_dtc)
+{
+ struct xilinx_vdmatest_slave_thread *thread;
+ struct dma_chan *tx_chan = tx_dtc->chan;
+ struct dma_chan *rx_chan = rx_dtc->chan;
+
+ thread = kzalloc(sizeof(struct xilinx_vdmatest_slave_thread),
+ GFP_KERNEL);
+ if (!thread)
+ pr_warn("xilinx_vdmatest: No memory for slave thread %s-%s\n",
+ dma_chan_name(tx_chan), dma_chan_name(rx_chan));
+
+ thread->tx_chan = tx_chan;
+ thread->rx_chan = rx_chan;
+ thread->type = (enum dma_transaction_type)DMA_SLAVE;
+
+ /* This barrier ensures the DMA channels in the 'thread'
+ * are initialized
+ */
+ smp_wmb();
+ thread->task = kthread_run(xilinx_vdmatest_slave_func, thread, "%s-%s",
+ dma_chan_name(tx_chan), dma_chan_name(rx_chan));
+ if (IS_ERR(thread->task)) {
+ pr_warn("xilinx_vdmatest: Failed to run thread %s-%s\n",
+ dma_chan_name(tx_chan), dma_chan_name(rx_chan));
+ kfree(thread);
+ }
+
+ list_add_tail(&thread->node, &tx_dtc->threads);
+
+ /* Added one thread with 2 channels */
+ return 1;
+}
+
+static int xilinx_vdmatest_add_slave_channels(struct dma_chan *tx_chan,
+ struct dma_chan *rx_chan)
+{
+ struct xilinx_vdmatest_chan *tx_dtc, *rx_dtc;
+ unsigned int thread_count = 0;
+
+ tx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
+ if (!tx_dtc)
+ return -ENOMEM;
+
+ rx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
+ if (!rx_dtc)
+ return -ENOMEM;
+
+ tx_dtc->chan = tx_chan;
+ rx_dtc->chan = rx_chan;
+ INIT_LIST_HEAD(&tx_dtc->threads);
+ INIT_LIST_HEAD(&rx_dtc->threads);
+
+ xilinx_vdmatest_add_slave_threads(tx_dtc, rx_dtc);
+ thread_count += 1;
+
+ pr_info("xilinx_vdmatest: Started %u threads using %s %s\n",
+ thread_count, dma_chan_name(tx_chan), dma_chan_name(rx_chan));
+
+ list_add_tail(&tx_dtc->node, &xilinx_vdmatest_channels);
+ list_add_tail(&rx_dtc->node, &xilinx_vdmatest_channels);
+ nr_channels += 2;
+
+ return 0;
+}
+
+static int xilinx_vdmatest_probe(struct platform_device *pdev)
+{
+ struct dma_chan *chan, *rx_chan;
+ int err;
+
+ err = of_property_read_u32(pdev->dev.of_node,
+ "xlnx,num-fstores", &frm_cnt);
+ if (err < 0) {
+ pr_err("xilinx_vdmatest: missing xlnx,num-fstores property\n");
+ return err;
+ }
+
+ chan = dma_request_slave_channel(&pdev->dev, "vdma0");
+ if (IS_ERR(chan)) {
+ pr_err("xilinx_vdmatest: No Tx channel\n");
+ return PTR_ERR(chan);
+ }
+
+ rx_chan = dma_request_slave_channel(&pdev->dev, "vdma1");
+ if (IS_ERR(rx_chan)) {
+ err = PTR_ERR(rx_chan);
+ pr_err("xilinx_vdmatest: No Rx channel\n");
+ goto free_tx;
+ }
+
+ err = xilinx_vdmatest_add_slave_channels(chan, rx_chan);
+ if (err) {
+ pr_err("xilinx_vdmatest: Unable to add channels\n");
+ goto free_rx;
+ }
+ return 0;
+
+free_rx:
+ dma_release_channel(rx_chan);
+free_tx:
+ dma_release_channel(chan);
+
+ return err;
+}
+
+static int xilinx_vdmatest_remove(struct platform_device *pdev)
+{
+ struct xilinx_vdmatest_chan *dtc, *_dtc;
+ struct dma_chan *chan;
+
+ list_for_each_entry_safe(dtc, _dtc, &xilinx_vdmatest_channels, node) {
+ list_del(&dtc->node);
+ chan = dtc->chan;
+ xilinx_vdmatest_cleanup_channel(dtc);
+ pr_info("xilinx_vdmatest: dropped channel %s\n",
+ dma_chan_name(chan));
+ dma_release_channel(chan);
+ }
+ return 0;
+}
+
+static const struct of_device_id xilinx_vdmatest_of_ids[] = {
+ { .compatible = "xlnx,axi-vdma-test-1.00.a",},
+ {}
+};
+
+static struct platform_driver xilinx_vdmatest_driver = {
+ .driver = {
+ .name = "xilinx_vdmatest",
+ .owner = THIS_MODULE,
+ .of_match_table = xilinx_vdmatest_of_ids,
+ },
+ .probe = xilinx_vdmatest_probe,
+ .remove = xilinx_vdmatest_remove,
+};
+
+module_platform_driver(xilinx_vdmatest_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx AXI VDMA Test Client");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related
* [PATCH] Add Xilinx AXI Video DMA Engine driver
From: Srikanth Thokala @ 2014-01-16 17:53 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
This is the driver for Xilinx AXI Video Direct Memory Access Engine.
It is a soft IP core, which provides high-bandwidth direct memory
access between memory and AXI4-Stream video type target peripherals
including peripherals which support AXI4-Stream Video Protocol. The
core provides efficient two dimensional DMA operations with independent
asynchronous read and write channel operation.
For more information on the IP, please refer to
http://www.xilinx.com/support/documentation/ip_documentation/axi_vdma/v6_1/pg020_axi_vdma.pdf
This patch also provides a test client, which assumes read and write channels
of the core are configured in a back-to-back connection. It transfers
data on the write channel, read and verify the data on the read channel.
Use cases:
++++++++++
1. Xilinx Video Targeted Reference design
http://www.wiki.xilinx.com/Zynq+Base+TRD+14.5
2. Common Display Framework
http://events.linuxfoundation.org/sites/events/files/slides/20131024-elce.pdf
Regards,
Srikanth
Srikanth Thokala (1):
dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
.../devicetree/bindings/dma/xilinx/xilinx_vdma.txt | 71 +
.../bindings/dma/xilinx/xilinx_vdma_test.txt | 39 +
drivers/dma/Kconfig | 23 +
drivers/dma/Makefile | 1 +
drivers/dma/xilinx/Makefile | 2 +
drivers/dma/xilinx/xilinx_vdma.c | 1497 ++++++++++++++++++++
drivers/dma/xilinx/xilinx_vdma_test.c | 629 ++++++++
7 files changed, 2262 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
create mode 100644 drivers/dma/xilinx/Makefile
create mode 100644 drivers/dma/xilinx/xilinx_vdma.c
create mode 100644 drivers/dma/xilinx/xilinx_vdma_test.c
--
1.7.9.5
^ permalink raw reply
* [PATCH] aarch64: always map VDSO at worst case alignment
From: Kyle McMartin @ 2014-01-16 17:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116174344.GJ30257@mudshark.cambridge.arm.com>
On Thu, Jan 16, 2014 at 05:43:44PM +0000, Will Deacon wrote:
> > + struct vm_unmapped_area_info info =
> > + {
> > + .flags = 0,
> > + .length = vdso_mapping_len,
> > + .low_limit = mm->mmap_base,
> > + .high_limit = TASK_SIZE,
> > + .align_mask = (1 << 16) - 1,
> > + };
> > + vdso_base = vm_unmapped_area(&info);
> > + }
>
> I don't like this fix. The kernel is perfectly alright mapping the vdso at
> the actual page size, as opposed to the maximum. Since the vdso isn't
> demand-paged, we can actually just tell the linker not to bother forcing 64k
> (worst case) alignment for PT_LOAD segments. Please can you try the patch
> below?
>
Me either, tbh. ;-)
I was testing out using -Wl,-z,max-page-size for the
!CONFIG_ARM64_64K_PAGES case, and that seemed to work. A quick compile
check shows the segment alignment being 0x10 with -Wl,-n:
kmcmarti ~/linux $ eu-readelf -l
arch/arm64/kernel/vdso/vdso.so
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz
MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000700
0x000700 R E 0x10
so I think that should work nicely!
I'll reboot a machine with this fix to test it as soon as possible.
Acked-by: Kyle McMartin <kyle@redhat.com>
> --- a/arch/arm64/kernel/vdso/Makefile
> +++ b/arch/arm64/kernel/vdso/Makefile
> @@ -48,7 +48,7 @@ $(obj-vdso): %.o: %.S
>
> # Actual build commands
> quiet_cmd_vdsold = VDSOL $@
> - cmd_vdsold = $(CC) $(c_flags) -Wl,-T $^ -o $@
> + cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@
> quiet_cmd_vdsoas = VDSOA $@
> cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<
>
^ permalink raw reply
* [PATCH] aarch64: always map VDSO at worst case alignment
From: Will Deacon @ 2014-01-16 17:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140115214144.GF18705@redacted.bos.redhat.com>
Hi Kyle,
On Wed, Jan 15, 2014 at 09:41:44PM +0000, Kyle McMartin wrote:
> Currently on ARM64 with 4K pages set, GDB fails to load the VDSO with
> the error "Failed to read a valid object file image from memory" as it
> is applying the phdr alignment to the vma, and attempting to read below
> where the VDSO is mapped. This is because our segment alignment is 64K
> in the ELF headers, but the VDSO only has PAGE_SIZE alignment from
> get_unmapped_area.
>
> Work around this by calling vm_unmapped_area directly, and specifying
> the worst case alignment (64K) directly.
>
> With this patch applied, I no longer have issues loading the VDSO in
> gdb (and no more error message every time I run a program inside it.)
>
> Signed-off-by: Kyle McMartin <kyle@redhat.com>
>
> --- a/arch/arm64/kernel/vdso.c
> +++ b/arch/arm64/kernel/vdso.c
> @@ -163,7 +163,18 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
> vdso_mapping_len = (vdso_pages + 1) << PAGE_SHIFT;
>
> down_write(&mm->mmap_sem);
> - vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
> + {
> + /* the VDSO must be worst-case aligned to 64K */
> + struct vm_unmapped_area_info info =
> + {
> + .flags = 0,
> + .length = vdso_mapping_len,
> + .low_limit = mm->mmap_base,
> + .high_limit = TASK_SIZE,
> + .align_mask = (1 << 16) - 1,
> + };
> + vdso_base = vm_unmapped_area(&info);
> + }
I don't like this fix. The kernel is perfectly alright mapping the vdso at
the actual page size, as opposed to the maximum. Since the vdso isn't
demand-paged, we can actually just tell the linker not to bother forcing 64k
(worst case) alignment for PT_LOAD segments. Please can you try the patch
below?
Will
--->8
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
index d8064af42e62..6d20b7d162d8 100644
--- a/arch/arm64/kernel/vdso/Makefile
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -48,7 +48,7 @@ $(obj-vdso): %.o: %.S
# Actual build commands
quiet_cmd_vdsold = VDSOL $@
- cmd_vdsold = $(CC) $(c_flags) -Wl,-T $^ -o $@
+ cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@
quiet_cmd_vdsoas = VDSOA $@
cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<
^ permalink raw reply related
* [PATCH] ARM: imx: clk-imx6sl: Suppress duplicate const sparse warning
From: Bill Pringlemeir @ 2014-01-16 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87fvon6elf.fsf@nbsps.com>
On 16 Jan 2014, bpringlemeir at nbsps.com wrote:
>> It was added into checkpatch.pl by commit cb710ec (scripts/checkpatch.pl:
>> add warnings for static char that could be static const char). I'm not
>> sure which warning we should ignore, the sparse or the checkpatch one.
> I think both scripts/programs are right. There is a difference.
> static const char const * step_sels[] = { "osc", "pll2_pfd2", }; /* dup */
> static const char * const step_sels[] = { "osc", "pll2_pfd2", }; /* ok */
> static char const * const step_sels[] = { "osc", "pll2_pfd2", }; /* ok */
> I think that 'type const * const' is a const pointer to const data, but
> 'const type const *' is just a const pointer (with duplicate). The
> patches have made the data non-const?
Sorry, that patch is correct. It just removed the duplicate 'const',
but checkpatch is right to recommend the 'const * const' as the strings
could be put in a read-only section.
^ permalink raw reply
* [RFC 06/18] regmap: Formalise use of non-bus context
From: Pawel Moll @ 2014-01-16 17:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140116170954.C3CBCC40973@trevor.secretlab.ca>
On Thu, 2014-01-16 at 17:09 +0000, Grant Likely wrote:
> > First of all, it's just a generalization of the free_context already
> > existing in regmap_bus (and used by regmap-mmio). And in case of this
> > series it is being used to release extra resource added allocated for a
> > "busless" regmap_config. Briefly, I'm using devm_regmap_init() to
> > "attach" a custom regmap configuration to a device when it is being
> > created (which is then dev_get_regmap()-ed in the driver, as you saw in
> > the regulator patch) and its context is a pointer to kzallocated data.
> > free_context is used to release it when devm resource is being removed.
>
> Have you thought through all the implications here? What you've
> described effectively changes the devm model. devm operates under the
> assumption that devm data only exists between probe() and remove() time.
> If you 'preload' devm data then the preloaded data will get discarded at
> remove() time which breaks if the driver is remove and probed again at
> runtime.
Uh. Right, you're correct, I've missed that (obvious) fact :-(
I will get the drivers back to "vexpress_regmap_init()" model or try to
find a way of "attaching" a regmap pointer(s?) to a struct device. If it
makes sense at all...
Pawe?
^ permalink raw reply
* How to support SDIO wifi/bt in DT
From: Olof Johansson @ 2014-01-16 17:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3215037.8HVCjAeS8g@wuerfel>
[adding devicetree and mmc lists]
On Thu, Jan 16, 2014 at 6:02 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Thursday 16 January 2014 13:36:49 Russell King - ARM Linux wrote:
>
>>
>> The Wifi/BT chip can only be detected via probing the SDIO connection, and
>> only when the device has been powered up and released from reset - so we
>> have to power up and reset the bcrm device before we probe via the SDIO
>> bus.
>
> This is indeed a common problem, and while we've talked about solving
> it in the past, nothing has happened code-wise. It indeed needs an
> implementation in the sdio framework.
Yeah, this is what's keeping wifi from working on the ARM Chromebook
upstream as well, and it's been on my list to deal with for a long
time. So now is as good a time as any.
Generic SDIO framework solution/binding would be great, since these
constructs are quite common on systems these days (even x86 is
starting to get peripherals on SDIO).
>> While it's possible to attach the power supply for the Wifi/BT chip to the
>> vmmc-supply property of the host, it's not possible to do that with the
>> oscillator supply. Neither is there any provision for manipulating the
>> GPIOs to deal with the resets.
>>
>> I can't find any examples of anything similar in our existing set of DT
>> files, so I suspect either this is a device which no one supports on any
>> DT platform, or there's some clever way to handle this.
>
> The former.
We've dealt with it with local code in our tree for Chrome OS, and I
suspect everyone else has too. It's definitely time to solve
generically.
>> How do other people support this in DT? Do they hack up some platform
>> specific code (which isn't nice)? What other solutions are there to get
>> around this problem? How does this kind of thing get represented in DT?
>>
>> (Don't suggest adding DT support to the bcrmfmac driver - this is a
>> chicken-and-egg problem. The driver isn't being probed at the moment
>> because the device is powered down and/or held in reset, so is
>> undetectable. The kernel needs to power it up and release the reset
>> so it becomes detectable.)
>
> The problem is two-fold:
>
> a) we need to add a generic mechanism to the SDIO probe code to
> allow specifying clocks, regulators and resets (possibly more)
> for an SDIO endpoint that get enabled before the probe starts.
> This parts needs a DT binding for SDIO slots as well as code.
"for SDIO slots" is somewhat misleading; nearly all controllers only
do one slot/device per controller. The designware controller can do
multiple slots, and that adds a bit of driver and binding complexity
for something that seemingly not a single vendor has actually
implemented.
So we can likely assume to keep these reset/power/clock lines per host
controller, no need to add a slot construct.
> b) We need to add a way to attach a device_node to an sdio_func,
> so that a function driver can find additional DT properties.
> This part should be relatively simple once (a) is done and
> should only need a little code but no new binding. The code
> would be similar to what we do for amba, i2c or spi devices.
This isn't actually needed for this functionality, but might be needed
for other things...
> Note that the same problem exists for a number of other discoverable
> buses that get used in nondiscoverable ways, e.g. USB, PCI,
> or even SCSI. It's normally a spec violation if those devices
> are wired up like this, but of course we still want to support
> the hardware.
... such as these. :)
-Olof
^ permalink raw reply
* [PATCH 4/4] ARM: sun6i: dt: Add SPI controllers to the A31 DTSI
From: Maxime Ripard @ 2014-01-16 17:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389892285-11745-1-git-send-email-maxime.ripard@free-electrons.com>
The A31 has 4 SPI controllers. Add them in the DTSI.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm/boot/dts/sun6i-a31.dtsi | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index ae058eb..288cc8e 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -340,6 +340,46 @@
status = "disabled";
};
+ spi0: spi at 01c68000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c68000 0x1000>;
+ interrupts = <0 65 4>;
+ clocks = <&ahb1_gates 20>, <&spi0_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 20>;
+ status = "disabled";
+ };
+
+ spi1: spi at 01c69000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c69000 0x1000>;
+ interrupts = <0 66 4>;
+ clocks = <&ahb1_gates 21>, <&spi1_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 21>;
+ status = "disabled";
+ };
+
+ spi2: spi at 01c6a000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c6a000 0x1000>;
+ interrupts = <0 67 4>;
+ clocks = <&ahb1_gates 22>, <&spi2_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 22>;
+ status = "disabled";
+ };
+
+ spi3: spi at 01c6b000 {
+ compatible = "allwinner,sun6i-a31-spi";
+ reg = <0x01c6b000 0x1000>;
+ interrupts = <0 68 4>;
+ clocks = <&ahb1_gates 23>, <&spi3_clk>;
+ clock-names = "ahb", "mod";
+ resets = <&ahb1_rst 23>;
+ status = "disabled";
+ };
+
gic: interrupt-controller at 01c81000 {
compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic";
reg = <0x01c81000 0x1000>,
--
1.8.4.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox